diff --git a/lib/.gitignore b/lib/.gitignore index 5e7fa54c45..e5e217b830 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -4,3 +4,5 @@ /gen_crc32table /gen_crc64table /oid_registry_data.c +/test_fortify.log +/test_fortify/*.log diff --git a/lib/Kconfig b/lib/Kconfig index fa4b10322e..9b5a692ce0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -64,9 +64,6 @@ config GENERIC_STRNLEN_USER config GENERIC_NET_UTILS bool -config GENERIC_FIND_FIRST_BIT - bool - source "lib/math/Kconfig" config NO_GENERIC_PCI_IOPORT_MAP @@ -121,6 +118,8 @@ config INDIRECT_IOMEM_FALLBACK mmio accesses when the IO memory address is not a registered emulated region. +source "lib/crypto/Kconfig" + config CRC_CCITT tristate "CRC-CCITT functions" help @@ -670,6 +669,10 @@ config STACKDEPOT bool select STACKTRACE +config STACKDEPOT_ALWAYS_INIT + bool + select STACKDEPOT + config STACK_HASH_ORDER int "stack depot hash size (12 => 4KB, 20 => 1024KB)" range 12 20 @@ -679,6 +682,11 @@ config STACK_HASH_ORDER Select the hash size as a power of 2 for the stackdepot hash table. Choose a lower value to reduce the memory impact. +config REF_TRACKER + bool + depends on STACKTRACE_SUPPORT + select STACKDEPOT + config SBITMAP bool diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 2a9b6dcdac..14b89aa37c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -316,6 +316,7 @@ config DEBUG_INFO_BTF bool "Generate BTF typeinfo" depends on !DEBUG_INFO_SPLIT && !DEBUG_INFO_REDUCED depends on !GCC_PLUGIN_RANDSTRUCT || COMPILE_TEST + depends on BPF_SYSCALL help Generate deduplicated BTF type information from DWARF debug info. Turning this on expects presence of pahole tool, which will convert @@ -346,8 +347,9 @@ config FRAME_WARN int "Warn for stack frames larger than" range 0 8192 default 2048 if GCC_PLUGIN_LATENT_ENTROPY - default 1536 if (!64BIT && (PARISC || XTENSA)) - default 1024 if (!64BIT && !PARISC) + default 2048 if PARISC + default 1536 if (!64BIT && XTENSA) + default 1024 if !64BIT default 2048 if 64BIT help Tell gcc to warn at build time for stack frames larger than this. @@ -458,7 +460,7 @@ config STACK_VALIDATION config VMLINUX_VALIDATION bool - depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT + depends on STACK_VALIDATION && DEBUG_ENTRY default y config VMLINUX_MAP @@ -597,6 +599,11 @@ config DEBUG_MISC Say Y here if you need to enable miscellaneous debug code that should be under a more specific debug option but isn't. +menu "Networking Debugging" + +source "net/Kconfig.debug" + +endmenu # "Networking Debugging" menu "Memory Debugging" @@ -877,7 +884,7 @@ config DEBUG_MEMORY_INIT config MEMORY_NOTIFIER_ERROR_INJECT tristate "Memory hotplug notifier error injection module" - depends on MEMORY_HOTPLUG_SPARSE && NOTIFIER_ERROR_INJECTION + depends on MEMORY_HOTPLUG && NOTIFIER_ERROR_INJECTION help This option provides the ability to inject artificial errors to memory hotplug notifier chain callbacks. It is controlled through @@ -1977,6 +1984,8 @@ config KCOV bool "Code coverage for fuzzing" depends on ARCH_HAS_KCOV depends on CC_HAS_SANCOV_TRACE_PC || GCC_PLUGINS + depends on !ARCH_WANTS_NO_INSTR || STACK_VALIDATION || \ + GCC_VERSION >= 120000 || CLANG_VERSION >= 130000 select DEBUG_FS select GCC_PLUGIN_SANCOV if !CC_HAS_SANCOV_TRACE_PC help @@ -2080,9 +2089,10 @@ config TEST_DIV64 If unsure, say N. config KPROBES_SANITY_TEST - bool "Kprobes sanity tests" + tristate "Kprobes sanity tests" depends on DEBUG_KERNEL depends on KPROBES + depends on KUNIT help This option provides for testing basic kprobes functionality on boot. Samples of kprobe and kretprobe are inserted and @@ -2104,6 +2114,16 @@ config BACKTRACE_SELF_TEST Say N if you are unsure. +config TEST_REF_TRACKER + tristate "Self test for reference tracker" + depends on DEBUG_KERNEL && STACKTRACE_SUPPORT + select REF_TRACKER + help + This option provides a kernel module performing tests + using reference tracker infrastructure. + + Say N if you are unsure. + config RBTREE_TEST tristate "Red-Black tree test" depends on DEBUG_KERNEL @@ -2204,12 +2224,11 @@ config TEST_RHASHTABLE If unsure, say N. -config TEST_HASH - tristate "Perform selftest on hash functions" +config TEST_SIPHASH + tristate "Perform selftest on siphash functions" help - Enable this option to test the kernel's integer (), - string (), and siphash () - hash functions on boot (or module load). + Enable this option to test the kernel's siphash () hash + functions on boot (or module load). This is intended to help people writing architecture-specific optimized versions. If unsure, say N. @@ -2353,6 +2372,25 @@ config BITFIELD_KUNIT If unsure, say N. +config HASH_KUNIT_TEST + tristate "KUnit Test for integer hash functions" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this option to test the kernel's string (), and + integer () hash functions on boot. + + KUnit tests run during boot and output the results to the debug log + in TAP format (https://testanything.org/). Only useful for kernel devs + running the KUnit test harness, and not intended for inclusion into a + production build. + + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + config RESOURCE_KUNIT_TEST tristate "KUnit test for resource API" depends on KUNIT @@ -2452,6 +2490,17 @@ config RATIONAL_KUNIT_TEST If unsure, say N. +config MEMCPY_KUNIT_TEST + tristate "Test memcpy(), memmove(), and memset() functions at runtime" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Builds unit tests for memcpy(), memmove(), and memset() functions. + For more information on KUnit and unit tests in general please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config TEST_UDELAY tristate "udelay test driver" help @@ -2473,6 +2522,7 @@ config TEST_KMOD depends on m depends on NETDEVICES && NET_CORE && INET # for TUN depends on BLOCK + depends on PAGE_SIZE_LESS_THAN_256KB # for BTRFS select TEST_LKM select XFS_FS select TUN diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index cdc842d090..879757b6dd 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -38,7 +38,7 @@ menuconfig KASAN CC_HAS_WORKING_NOSANITIZE_ADDRESS) || \ HAVE_ARCH_KASAN_HW_TAGS depends on (SLUB && SYSFS) || (SLAB && !DEBUG_SLAB) - select STACKDEPOT + select STACKDEPOT_ALWAYS_INIT help Enables KASAN (KernelAddressSANitizer) - runtime memory debugger, designed to find out-of-bounds accesses and use-after-free bugs. diff --git a/lib/Kconfig.kcsan b/lib/Kconfig.kcsan index e0a93ffdef..63b70b8c55 100644 --- a/lib/Kconfig.kcsan +++ b/lib/Kconfig.kcsan @@ -191,6 +191,26 @@ config KCSAN_STRICT closely aligns with the rules defined by the Linux-kernel memory consistency model (LKMM). +config KCSAN_WEAK_MEMORY + bool "Enable weak memory modeling to detect missing memory barriers" + default y + depends on KCSAN_STRICT + # We can either let objtool nop __tsan_func_{entry,exit}() and builtin + # atomics instrumentation in .noinstr.text, or use a compiler that can + # implement __no_kcsan to really remove all instrumentation. + depends on STACK_VALIDATION || CC_IS_GCC || CLANG_VERSION >= 140000 + help + Enable support for modeling a subset of weak memory, which allows + detecting a subset of data races due to missing memory barriers. + + Depends on KCSAN_STRICT, because the options strenghtening certain + plain accesses by default (depending on !KCSAN_STRICT) reduce the + ability to detect any data races invoving reordered accesses, in + particular reordered writes. + + Weak memory modeling relies on additional instrumentation and may + affect performance. + config KCSAN_REPORT_VALUE_CHANGE_ONLY bool "Only report races where watcher observed a data value change" default y diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan index e5372a1351..236c5cefc4 100644 --- a/lib/Kconfig.ubsan +++ b/lib/Kconfig.ubsan @@ -112,19 +112,6 @@ config UBSAN_UNREACHABLE This option enables -fsanitize=unreachable which checks for control flow reaching an expected-to-be-unreachable position. -config UBSAN_OBJECT_SIZE - bool "Perform checking for accesses beyond the end of objects" - default UBSAN - # gcc hugely expands stack usage with -fsanitize=object-size - # https://lore.kernel.org/lkml/CAHk-=wjPasyJrDuwDnpHJS2TuQfExwe=px-SzLeN8GFMAQJPmQ@mail.gmail.com/ - depends on !CC_IS_GCC - depends on $(cc-option,-fsanitize=object-size) - help - This option enables -fsanitize=object-size which checks for accesses - beyond the end of objects where the optimizer can determine both the - object being operated on and its size, usually seen with bad downcasts, - or access to struct members from NULL pointers. - config UBSAN_BOOL bool "Perform checking for non-boolean values used as boolean" default UBSAN diff --git a/lib/Makefile b/lib/Makefile index a841be5244..300f569c62 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -61,7 +61,8 @@ obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o obj-$(CONFIG_TEST_BITOPS) += test_bitops.o CFLAGS_test_bitops.o += -Werror obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o -obj-$(CONFIG_TEST_HASH) += test_hash.o test_siphash.o +obj-$(CONFIG_TEST_SIPHASH) += test_siphash.o +obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_KASAN_KUNIT_TEST) += test_kasan.o CFLAGS_test_kasan.o += -fno-builtin @@ -100,7 +101,8 @@ obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o obj-$(CONFIG_TEST_HMM) += test_hmm.o obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o - +obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o +obj-$(CONFIG_TEST_REF_TRACKER) += test_ref_tracker.o # # CFLAGS for compiling floating point code inside the kernel. x86/Makefile turns # off the generation of FPU/SSE* instructions for kernel proper but FPU_FLAGS @@ -269,6 +271,8 @@ obj-$(CONFIG_STACKDEPOT) += stackdepot.o KASAN_SANITIZE_stackdepot.o := n KCOV_INSTRUMENT_stackdepot.o := n +obj-$(CONFIG_REF_TRACKER) += ref_tracker.o + libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \ fdt_empty_tree.o fdt_addresses.o $(foreach file, $(libfdt_files), \ @@ -358,5 +362,39 @@ obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o obj-$(CONFIG_BITS_TEST) += test_bits.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o +obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o + +# FORTIFY_SOURCE compile-time behavior tests +TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c) +TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS)) +TEST_FORTIFY_LOG = test_fortify.log + +quiet_cmd_test_fortify = TEST $@ + cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \ + $< $@ "$(NM)" $(CC) $(c_flags) \ + $(call cc-disable-warning,fortify-source) + +targets += $(TEST_FORTIFY_LOGS) +clean-files += $(TEST_FORTIFY_LOGS) +clean-files += $(addsuffix .o, $(TEST_FORTIFY_LOGS)) +$(obj)/test_fortify/%.log: $(src)/test_fortify/%.c \ + $(src)/test_fortify/test_fortify.h \ + $(srctree)/include/linux/fortify-string.h \ + $(srctree)/scripts/test_fortify.sh \ + FORCE + $(call if_changed,test_fortify) + +quiet_cmd_gen_fortify_log = GEN $@ + cmd_gen_fortify_log = cat /dev/null > $@ || true + +targets += $(TEST_FORTIFY_LOG) +clean-files += $(TEST_FORTIFY_LOG) +$(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE + $(call if_changed,gen_fortify_log) + +# Fake dependency to trigger the fortify tests. +ifeq ($(CONFIG_FORTIFY_SOURCE),y) +$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG) +endif diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c index 27bbe89171..0fd3c454a4 100644 --- a/lib/asn1_encoder.c +++ b/lib/asn1_encoder.c @@ -164,8 +164,6 @@ asn1_encode_oid(unsigned char *data, const unsigned char *end_data, data_len -= 3; - ret = 0; - for (i = 2; i < oid_len; i++) { ret = asn1_encode_oid_digit(&d, &data_len, oid[i]); if (ret < 0) diff --git a/lib/assoc_array.c b/lib/assoc_array.c index 04c98799c3..079c72e264 100644 --- a/lib/assoc_array.c +++ b/lib/assoc_array.c @@ -741,8 +741,7 @@ static bool assoc_array_insert_into_terminal_node(struct assoc_array_edit *edit, keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE); keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; - new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) + - keylen * sizeof(unsigned long), GFP_KERNEL); + new_s0 = kzalloc(struct_size(new_s0, index_key, keylen), GFP_KERNEL); if (!new_s0) return false; edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s0); @@ -849,8 +848,8 @@ static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit, keylen = round_up(diff, ASSOC_ARRAY_KEY_CHUNK_SIZE); keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; - new_s0 = kzalloc(sizeof(struct assoc_array_shortcut) + - keylen * sizeof(unsigned long), GFP_KERNEL); + new_s0 = kzalloc(struct_size(new_s0, index_key, keylen), + GFP_KERNEL); if (!new_s0) return false; edit->new_meta[1] = assoc_array_shortcut_to_ptr(new_s0); @@ -864,7 +863,7 @@ static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit, new_n0->parent_slot = 0; memcpy(new_s0->index_key, shortcut->index_key, - keylen * sizeof(unsigned long)); + flex_array_size(new_s0, index_key, keylen)); blank = ULONG_MAX << (diff & ASSOC_ARRAY_KEY_CHUNK_MASK); pr_devel("blank off [%zu] %d: %lx\n", keylen - 1, diff, blank); @@ -899,8 +898,8 @@ static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit, keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE); keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; - new_s1 = kzalloc(sizeof(struct assoc_array_shortcut) + - keylen * sizeof(unsigned long), GFP_KERNEL); + new_s1 = kzalloc(struct_size(new_s1, index_key, keylen), + GFP_KERNEL); if (!new_s1) return false; edit->new_meta[2] = assoc_array_shortcut_to_ptr(new_s1); @@ -913,7 +912,7 @@ static bool assoc_array_insert_mid_shortcut(struct assoc_array_edit *edit, new_n0->slots[sc_slot] = assoc_array_shortcut_to_ptr(new_s1); memcpy(new_s1->index_key, shortcut->index_key, - keylen * sizeof(unsigned long)); + flex_array_size(new_s1, index_key, keylen)); edit->set[1].ptr = &side->back_pointer; edit->set[1].to = assoc_array_shortcut_to_ptr(new_s1); @@ -1490,13 +1489,12 @@ int assoc_array_gc(struct assoc_array *array, shortcut = assoc_array_ptr_to_shortcut(cursor); keylen = round_up(shortcut->skip_to_level, ASSOC_ARRAY_KEY_CHUNK_SIZE); keylen >>= ASSOC_ARRAY_KEY_CHUNK_SHIFT; - new_s = kmalloc(sizeof(struct assoc_array_shortcut) + - keylen * sizeof(unsigned long), GFP_KERNEL); + new_s = kmalloc(struct_size(new_s, index_key, keylen), + GFP_KERNEL); if (!new_s) goto enomem; pr_devel("dup shortcut %p -> %p\n", shortcut, new_s); - memcpy(new_s, shortcut, (sizeof(struct assoc_array_shortcut) + - keylen * sizeof(unsigned long))); + memcpy(new_s, shortcut, struct_size(new_s, index_key, keylen)); new_s->back_pointer = new_parent; new_s->parent_slot = shortcut->parent_slot; *new_ptr_pp = new_parent = assoc_array_shortcut_to_ptr(new_s); diff --git a/lib/atomic64.c b/lib/atomic64.c index 3df6539941..caf895789a 100644 --- a/lib/atomic64.c +++ b/lib/atomic64.c @@ -118,7 +118,6 @@ ATOMIC64_OPS(sub, -=) #undef ATOMIC64_OPS #define ATOMIC64_OPS(op, c_op) \ ATOMIC64_OP(op, c_op) \ - ATOMIC64_OP_RETURN(op, c_op) \ ATOMIC64_FETCH_OP(op, c_op) ATOMIC64_OPS(and, &=) @@ -127,7 +126,6 @@ ATOMIC64_OPS(xor, ^=) #undef ATOMIC64_OPS #undef ATOMIC64_FETCH_OP -#undef ATOMIC64_OP_RETURN #undef ATOMIC64_OP s64 generic_atomic64_dec_if_positive(atomic64_t *v) diff --git a/lib/audit.c b/lib/audit.c index 5004bff928..738bda22dd 100644 --- a/lib/audit.c +++ b/lib/audit.c @@ -45,23 +45,27 @@ int audit_classify_syscall(int abi, unsigned syscall) switch(syscall) { #ifdef __NR_open case __NR_open: - return 2; + return AUDITSC_OPEN; #endif #ifdef __NR_openat case __NR_openat: - return 3; + return AUDITSC_OPENAT; #endif #ifdef __NR_socketcall case __NR_socketcall: - return 4; + return AUDITSC_SOCKETCALL; #endif #ifdef __NR_execveat case __NR_execveat: #endif case __NR_execve: - return 5; + return AUDITSC_EXECVE; +#ifdef __NR_openat2 + case __NR_openat2: + return AUDITSC_OPENAT2; +#endif default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/lib/bitmap.c b/lib/bitmap.c index 663dd81967..9264088834 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -1398,6 +1398,19 @@ unsigned long *bitmap_zalloc(unsigned int nbits, gfp_t flags) } EXPORT_SYMBOL(bitmap_zalloc); +unsigned long *bitmap_alloc_node(unsigned int nbits, gfp_t flags, int node) +{ + return kmalloc_array_node(BITS_TO_LONGS(nbits), sizeof(unsigned long), + flags, node); +} +EXPORT_SYMBOL(bitmap_alloc_node); + +unsigned long *bitmap_zalloc_node(unsigned int nbits, gfp_t flags, int node) +{ + return bitmap_alloc_node(nbits, flags | __GFP_ZERO, node); +} +EXPORT_SYMBOL(bitmap_zalloc_node); + void bitmap_free(const unsigned long *bitmap) { kfree(bitmap); diff --git a/lib/bootconfig.c b/lib/bootconfig.c index 5ae248b293..74f3201ab8 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -4,16 +4,24 @@ * Masami Hiramatsu */ -#define pr_fmt(fmt) "bootconfig: " fmt - +#ifdef __KERNEL__ #include #include #include #include #include #include -#include #include +#else /* !__KERNEL__ */ +/* + * NOTE: This is only for tools/bootconfig, because tools/bootconfig will + * run the parser sanity test. + * This does NOT mean lib/bootconfig.c is available in the user space. + * However, if you change this file, please make sure the tools/bootconfig + * has no issue on building and running. + */ +#include +#endif /* * Extra Boot Config (XBC) is given as tree-structured ascii text of @@ -34,6 +42,50 @@ static int xbc_err_pos __initdata; static int open_brace[XBC_DEPTH_MAX] __initdata; static int brace_index __initdata; +#ifdef __KERNEL__ +static inline void * __init xbc_alloc_mem(size_t size) +{ + return memblock_alloc(size, SMP_CACHE_BYTES); +} + +static inline void __init xbc_free_mem(void *addr, size_t size) +{ + memblock_free(addr, size); +} + +#else /* !__KERNEL__ */ + +static inline void *xbc_alloc_mem(size_t size) +{ + return malloc(size); +} + +static inline void xbc_free_mem(void *addr, size_t size) +{ + free(addr); +} +#endif +/** + * xbc_get_info() - Get the information of loaded boot config + * @node_size: A pointer to store the number of nodes. + * @data_size: A pointer to store the size of bootconfig data. + * + * Get the number of used nodes in @node_size if it is not NULL, + * and the size of bootconfig data in @data_size if it is not NULL. + * Return 0 if the boot config is initialized, or return -ENODEV. + */ +int __init xbc_get_info(int *node_size, size_t *data_size) +{ + if (!xbc_data) + return -ENODEV; + + if (node_size) + *node_size = xbc_node_num; + if (data_size) + *data_size = xbc_data_size; + return 0; +} + static int __init xbc_parse_error(const char *msg, const char *p) { xbc_err_msg = msg; @@ -226,7 +278,7 @@ int __init xbc_node_compose_key_after(struct xbc_node *root, struct xbc_node *node, char *buf, size_t size) { - u16 keys[XBC_DEPTH_MAX]; + uint16_t keys[XBC_DEPTH_MAX]; int depth = 0, ret = 0, total = 0; if (!node || node == root) @@ -341,21 +393,21 @@ const char * __init xbc_node_find_next_key_value(struct xbc_node *root, /* XBC parse and tree build */ -static int __init xbc_init_node(struct xbc_node *node, char *data, u32 flag) +static int __init xbc_init_node(struct xbc_node *node, char *data, uint32_t flag) { unsigned long offset = data - xbc_data; if (WARN_ON(offset >= XBC_DATA_MAX)) return -EINVAL; - node->data = (u16)offset | flag; + node->data = (uint16_t)offset | flag; node->child = 0; node->next = 0; return 0; } -static struct xbc_node * __init xbc_add_node(char *data, u32 flag) +static struct xbc_node * __init xbc_add_node(char *data, uint32_t flag) { struct xbc_node *node; @@ -385,7 +437,7 @@ static inline __init struct xbc_node *xbc_last_child(struct xbc_node *node) return node; } -static struct xbc_node * __init __xbc_add_sibling(char *data, u32 flag, bool head) +static struct xbc_node * __init __xbc_add_sibling(char *data, uint32_t flag, bool head) { struct xbc_node *sib, *node = xbc_add_node(data, flag); @@ -412,17 +464,17 @@ static struct xbc_node * __init __xbc_add_sibling(char *data, u32 flag, bool hea return node; } -static inline struct xbc_node * __init xbc_add_sibling(char *data, u32 flag) +static inline struct xbc_node * __init xbc_add_sibling(char *data, uint32_t flag) { return __xbc_add_sibling(data, flag, false); } -static inline struct xbc_node * __init xbc_add_head_sibling(char *data, u32 flag) +static inline struct xbc_node * __init xbc_add_head_sibling(char *data, uint32_t flag) { return __xbc_add_sibling(data, flag, true); } -static inline __init struct xbc_node *xbc_add_child(char *data, u32 flag) +static inline __init struct xbc_node *xbc_add_child(char *data, uint32_t flag) { struct xbc_node *node = xbc_add_sibling(data, flag); @@ -780,72 +832,14 @@ static int __init xbc_verify_tree(void) return 0; } -/** - * xbc_destroy_all() - Clean up all parsed bootconfig - * - * This clears all data structures of parsed bootconfig on memory. - * If you need to reuse xbc_init() with new boot config, you can - * use this. - */ -void __init xbc_destroy_all(void) -{ - xbc_data = NULL; - xbc_data_size = 0; - xbc_node_num = 0; - memblock_free_ptr(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX); - xbc_nodes = NULL; - brace_index = 0; -} - -/** - * xbc_init() - Parse given XBC file and build XBC internal tree - * @buf: boot config text - * @emsg: A pointer of const char * to store the error message - * @epos: A pointer of int to store the error position - * - * This parses the boot config text in @buf. @buf must be a - * null terminated string and smaller than XBC_DATA_MAX. - * Return the number of stored nodes (>0) if succeeded, or -errno - * if there is any error. - * In error cases, @emsg will be updated with an error message and - * @epos will be updated with the error position which is the byte offset - * of @buf. If the error is not a parser error, @epos will be -1. - */ -int __init xbc_init(char *buf, const char **emsg, int *epos) +/* Need to setup xbc_data and xbc_nodes before call this. */ +static int __init xbc_parse_tree(void) { char *p, *q; - int ret, c; + int ret = 0, c; - if (epos) - *epos = -1; - - if (xbc_data) { - if (emsg) - *emsg = "Bootconfig is already initialized"; - return -EBUSY; - } - - ret = strlen(buf); - if (ret > XBC_DATA_MAX - 1 || ret == 0) { - if (emsg) - *emsg = ret ? "Config data is too big" : - "Config data is empty"; - return -ERANGE; - } - - xbc_nodes = memblock_alloc(sizeof(struct xbc_node) * XBC_NODE_MAX, - SMP_CACHE_BYTES); - if (!xbc_nodes) { - if (emsg) - *emsg = "Failed to allocate bootconfig nodes"; - return -ENOMEM; - } - memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); - xbc_data = buf; - xbc_data_size = ret + 1; last_parent = NULL; - - p = buf; + p = xbc_data; do { q = strpbrk(p, "{}=+;:\n#"); if (!q) { @@ -887,6 +881,81 @@ int __init xbc_init(char *buf, const char **emsg, int *epos) } } while (!ret); + return ret; +} + +/** + * xbc_exit() - Clean up all parsed bootconfig + * + * This clears all data structures of parsed bootconfig on memory. + * If you need to reuse xbc_init() with new boot config, you can + * use this. + */ +void __init xbc_exit(void) +{ + xbc_free_mem(xbc_data, xbc_data_size); + xbc_data = NULL; + xbc_data_size = 0; + xbc_node_num = 0; + xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX); + xbc_nodes = NULL; + brace_index = 0; +} + +/** + * xbc_init() - Parse given XBC file and build XBC internal tree + * @data: The boot config text original data + * @size: The size of @data + * @emsg: A pointer of const char * to store the error message + * @epos: A pointer of int to store the error position + * + * This parses the boot config text in @data. @size must be smaller + * than XBC_DATA_MAX. + * Return the number of stored nodes (>0) if succeeded, or -errno + * if there is any error. + * In error cases, @emsg will be updated with an error message and + * @epos will be updated with the error position which is the byte offset + * of @buf. If the error is not a parser error, @epos will be -1. + */ +int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) +{ + int ret; + + if (epos) + *epos = -1; + + if (xbc_data) { + if (emsg) + *emsg = "Bootconfig is already initialized"; + return -EBUSY; + } + if (size > XBC_DATA_MAX || size == 0) { + if (emsg) + *emsg = size ? "Config data is too big" : + "Config data is empty"; + return -ERANGE; + } + + xbc_data = xbc_alloc_mem(size + 1); + if (!xbc_data) { + if (emsg) + *emsg = "Failed to allocate bootconfig data"; + return -ENOMEM; + } + memcpy(xbc_data, data, size); + xbc_data[size] = '\0'; + xbc_data_size = size + 1; + + xbc_nodes = xbc_alloc_mem(sizeof(struct xbc_node) * XBC_NODE_MAX); + if (!xbc_nodes) { + if (emsg) + *emsg = "Failed to allocate bootconfig nodes"; + xbc_exit(); + return -ENOMEM; + } + memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); + + ret = xbc_parse_tree(); if (!ret) ret = xbc_verify_tree(); @@ -895,27 +964,9 @@ int __init xbc_init(char *buf, const char **emsg, int *epos) *epos = xbc_err_pos; if (emsg) *emsg = xbc_err_msg; - xbc_destroy_all(); + xbc_exit(); } else ret = xbc_node_num; return ret; } - -/** - * xbc_debug_dump() - Dump current XBC node list - * - * Dump the current XBC node list on printk buffer for debug. - */ -void __init xbc_debug_dump(void) -{ - int i; - - for (i = 0; i < xbc_node_num; i++) { - pr_debug("[%d] %s (%s) .next=%d, .child=%d .parent=%d\n", i, - xbc_node_get_data(xbc_nodes + i), - xbc_node_is_value(xbc_nodes + i) ? "value" : "key", - xbc_nodes[i].next, xbc_nodes[i].child, - xbc_nodes[i].parent); - } -} diff --git a/lib/compat_audit.c b/lib/compat_audit.c index 77eabad69b..3d6b8996f0 100644 --- a/lib/compat_audit.c +++ b/lib/compat_audit.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include unsigned compat_dir_class[] = { @@ -33,19 +34,23 @@ int audit_classify_compat_syscall(int abi, unsigned syscall) switch (syscall) { #ifdef __NR_open case __NR_open: - return 2; + return AUDITSC_OPEN; #endif #ifdef __NR_openat case __NR_openat: - return 3; + return AUDITSC_OPENAT; #endif #ifdef __NR_socketcall case __NR_socketcall: - return 4; + return AUDITSC_SOCKETCALL; #endif case __NR_execve: - return 5; + return AUDITSC_EXECVE; +#ifdef __NR_openat2 + case __NR_openat2: + return AUDITSC_OPENAT2; +#endif default: - return 1; + return AUDITSC_COMPAT; } } diff --git a/lib/cpumask.c b/lib/cpumask.c index c3c76b8333..a971a82d2f 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -188,7 +188,7 @@ EXPORT_SYMBOL(free_cpumask_var); */ void __init free_bootmem_cpumask_var(cpumask_var_t mask) { - memblock_free_early(__pa(mask), cpumask_size()); + memblock_free(mask, cpumask_size()); } #endif diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 545ccbddf6..e8e525650c 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -comment "Crypto library routines" +menu "Crypto library routines" config CRYPTO_LIB_AES tristate @@ -9,14 +9,14 @@ config CRYPTO_LIB_ARC4 tristate config CRYPTO_ARCH_HAVE_LIB_BLAKE2S - tristate + bool help Declares whether the architecture provides an arch-specific accelerated implementation of the Blake2s library interface, either builtin or as a module. config CRYPTO_LIB_BLAKE2S_GENERIC - tristate + def_bool !CRYPTO_ARCH_HAVE_LIB_BLAKE2S help This symbol can be depended upon by arch implementations of the Blake2s library interface that require the generic code as a @@ -24,15 +24,6 @@ config CRYPTO_LIB_BLAKE2S_GENERIC implementation is enabled, this implementation serves the users of CRYPTO_LIB_BLAKE2S. -config CRYPTO_LIB_BLAKE2S - tristate "BLAKE2s hash function library" - depends on CRYPTO_ARCH_HAVE_LIB_BLAKE2S || !CRYPTO_ARCH_HAVE_LIB_BLAKE2S - select CRYPTO_LIB_BLAKE2S_GENERIC if CRYPTO_ARCH_HAVE_LIB_BLAKE2S=n - help - Enable the Blake2s library interface. This interface may be fulfilled - by either the generic implementation or an arch-specific one, if one - is available and enabled. - config CRYPTO_ARCH_HAVE_LIB_CHACHA tristate help @@ -42,7 +33,7 @@ config CRYPTO_ARCH_HAVE_LIB_CHACHA config CRYPTO_LIB_CHACHA_GENERIC tristate - select CRYPTO_ALGAPI + select XOR_BLOCKS help This symbol can be depended upon by arch implementations of the ChaCha library interface that require the generic code as a @@ -52,6 +43,7 @@ config CRYPTO_LIB_CHACHA_GENERIC config CRYPTO_LIB_CHACHA tristate "ChaCha library interface" + depends on CRYPTO depends on CRYPTO_ARCH_HAVE_LIB_CHACHA || !CRYPTO_ARCH_HAVE_LIB_CHACHA select CRYPTO_LIB_CHACHA_GENERIC if CRYPTO_ARCH_HAVE_LIB_CHACHA=n help @@ -123,11 +115,15 @@ config CRYPTO_LIB_CHACHA20POLY1305 tristate "ChaCha20-Poly1305 AEAD support (8-byte nonce library version)" depends on CRYPTO_ARCH_HAVE_LIB_CHACHA || !CRYPTO_ARCH_HAVE_LIB_CHACHA depends on CRYPTO_ARCH_HAVE_LIB_POLY1305 || !CRYPTO_ARCH_HAVE_LIB_POLY1305 + depends on CRYPTO select CRYPTO_LIB_CHACHA select CRYPTO_LIB_POLY1305 + select CRYPTO_ALGAPI config CRYPTO_LIB_SHA256 tristate config CRYPTO_LIB_SM4 tristate + +endmenu diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index 73205ed269..ed43a41f2d 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -10,11 +10,10 @@ libaes-y := aes.o obj-$(CONFIG_CRYPTO_LIB_ARC4) += libarc4.o libarc4-y := arc4.o -obj-$(CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC) += libblake2s-generic.o -libblake2s-generic-y += blake2s-generic.o - -obj-$(CONFIG_CRYPTO_LIB_BLAKE2S) += libblake2s.o -libblake2s-y += blake2s.o +# blake2s is used by the /dev/random driver which is always builtin +obj-y += libblake2s.o +libblake2s-y := blake2s.o +libblake2s-$(CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC) += blake2s-generic.o obj-$(CONFIG_CRYPTO_LIB_CHACHA20POLY1305) += libchacha20poly1305.o libchacha20poly1305-y += chacha20poly1305.o diff --git a/lib/crypto/blake2s-generic.c b/lib/crypto/blake2s-generic.c index 04ff8df245..75ccb3e633 100644 --- a/lib/crypto/blake2s-generic.c +++ b/lib/crypto/blake2s-generic.c @@ -37,7 +37,11 @@ static inline void blake2s_increment_counter(struct blake2s_state *state, state->t[1] += (state->t[0] < inc); } -void blake2s_compress_generic(struct blake2s_state *state,const u8 *block, +void blake2s_compress(struct blake2s_state *state, const u8 *block, + size_t nblocks, const u32 inc) + __weak __alias(blake2s_compress_generic); + +void blake2s_compress_generic(struct blake2s_state *state, const u8 *block, size_t nblocks, const u32 inc) { u32 m[16]; diff --git a/lib/crypto/blake2s-selftest.c b/lib/crypto/blake2s-selftest.c index 5d9ea53be9..409e4b7287 100644 --- a/lib/crypto/blake2s-selftest.c +++ b/lib/crypto/blake2s-selftest.c @@ -15,7 +15,6 @@ * #include * * #include - * #include * * #define BLAKE2S_TESTVEC_COUNT 256 * @@ -58,16 +57,6 @@ * } * printf("};\n\n"); * - * printf("static const u8 blake2s_hmac_testvecs[][BLAKE2S_HASH_SIZE] __initconst = {\n"); - * - * HMAC(EVP_blake2s256(), key, sizeof(key), buf, sizeof(buf), hash, NULL); - * print_vec(hash, BLAKE2S_OUTBYTES); - * - * HMAC(EVP_blake2s256(), buf, sizeof(buf), key, sizeof(key), hash, NULL); - * print_vec(hash, BLAKE2S_OUTBYTES); - * - * printf("};\n"); - * * return 0; *} */ @@ -554,15 +543,6 @@ static const u8 blake2s_testvecs[][BLAKE2S_HASH_SIZE] __initconst = { 0xd6, 0x98, 0x6b, 0x07, 0x10, 0x65, 0x52, 0x65, }, }; -static const u8 blake2s_hmac_testvecs[][BLAKE2S_HASH_SIZE] __initconst = { - { 0xce, 0xe1, 0x57, 0x69, 0x82, 0xdc, 0xbf, 0x43, 0xad, 0x56, 0x4c, 0x70, - 0xed, 0x68, 0x16, 0x96, 0xcf, 0xa4, 0x73, 0xe8, 0xe8, 0xfc, 0x32, 0x79, - 0x08, 0x0a, 0x75, 0x82, 0xda, 0x3f, 0x05, 0x11, }, - { 0x77, 0x2f, 0x0c, 0x71, 0x41, 0xf4, 0x4b, 0x2b, 0xb3, 0xc6, 0xb6, 0xf9, - 0x60, 0xde, 0xe4, 0x52, 0x38, 0x66, 0xe8, 0xbf, 0x9b, 0x96, 0xc4, 0x9f, - 0x60, 0xd9, 0x24, 0x37, 0x99, 0xd6, 0xec, 0x31, }, -}; - bool __init blake2s_selftest(void) { u8 key[BLAKE2S_KEY_SIZE]; @@ -607,16 +587,5 @@ bool __init blake2s_selftest(void) } } - if (success) { - blake2s256_hmac(hash, buf, key, sizeof(buf), sizeof(key)); - success &= !memcmp(hash, blake2s_hmac_testvecs[0], BLAKE2S_HASH_SIZE); - - blake2s256_hmac(hash, key, buf, sizeof(key), sizeof(buf)); - success &= !memcmp(hash, blake2s_hmac_testvecs[1], BLAKE2S_HASH_SIZE); - - if (!success) - pr_err("blake2s256_hmac self-test: FAIL\n"); - } - return success; } diff --git a/lib/crypto/blake2s.c b/lib/crypto/blake2s.c index 4055aa593e..c71c09621c 100644 --- a/lib/crypto/blake2s.c +++ b/lib/crypto/blake2s.c @@ -16,63 +16,20 @@ #include #include -#if IS_ENABLED(CONFIG_CRYPTO_ARCH_HAVE_LIB_BLAKE2S) -# define blake2s_compress blake2s_compress_arch -#else -# define blake2s_compress blake2s_compress_generic -#endif - void blake2s_update(struct blake2s_state *state, const u8 *in, size_t inlen) { - __blake2s_update(state, in, inlen, blake2s_compress); + __blake2s_update(state, in, inlen, false); } EXPORT_SYMBOL(blake2s_update); void blake2s_final(struct blake2s_state *state, u8 *out) { WARN_ON(IS_ENABLED(DEBUG) && !out); - __blake2s_final(state, out, blake2s_compress); + __blake2s_final(state, out, false); memzero_explicit(state, sizeof(*state)); } EXPORT_SYMBOL(blake2s_final); -void blake2s256_hmac(u8 *out, const u8 *in, const u8 *key, const size_t inlen, - const size_t keylen) -{ - struct blake2s_state state; - u8 x_key[BLAKE2S_BLOCK_SIZE] __aligned(__alignof__(u32)) = { 0 }; - u8 i_hash[BLAKE2S_HASH_SIZE] __aligned(__alignof__(u32)); - int i; - - if (keylen > BLAKE2S_BLOCK_SIZE) { - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, key, keylen); - blake2s_final(&state, x_key); - } else - memcpy(x_key, key, keylen); - - for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i) - x_key[i] ^= 0x36; - - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE); - blake2s_update(&state, in, inlen); - blake2s_final(&state, i_hash); - - for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i) - x_key[i] ^= 0x5c ^ 0x36; - - blake2s_init(&state, BLAKE2S_HASH_SIZE); - blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE); - blake2s_update(&state, i_hash, BLAKE2S_HASH_SIZE); - blake2s_final(&state, i_hash); - - memcpy(out, i_hash, BLAKE2S_HASH_SIZE); - memzero_explicit(x_key, BLAKE2S_BLOCK_SIZE); - memzero_explicit(i_hash, BLAKE2S_HASH_SIZE); -} -EXPORT_SYMBOL(blake2s256_hmac); - static int __init blake2s_mod_init(void) { if (!IS_ENABLED(CONFIG_CRYPTO_MANAGER_DISABLE_TESTS) && diff --git a/lib/decompress_unzstd.c b/lib/decompress_unzstd.c index 6b629ab31c..a512b99ae1 100644 --- a/lib/decompress_unzstd.c +++ b/lib/decompress_unzstd.c @@ -68,11 +68,7 @@ #ifdef STATIC # define UNZSTD_PREBOOT # include "xxhash.c" -# include "zstd/entropy_common.c" -# include "zstd/fse_decompress.c" -# include "zstd/huf_decompress.c" -# include "zstd/zstd_common.c" -# include "zstd/decompress.c" +# include "zstd/decompress_sources.h" #endif #include @@ -91,11 +87,15 @@ static int INIT handle_zstd_error(size_t ret, void (*error)(char *x)) { - const int err = ZSTD_getErrorCode(ret); + const zstd_error_code err = zstd_get_error_code(ret); - if (!ZSTD_isError(ret)) + if (!zstd_is_error(ret)) return 0; + /* + * zstd_get_error_name() cannot be used because error takes a char * + * not a const char * + */ switch (err) { case ZSTD_error_memory_allocation: error("ZSTD decompressor ran out of memory"); @@ -124,28 +124,28 @@ static int INIT decompress_single(const u8 *in_buf, long in_len, u8 *out_buf, long out_len, long *in_pos, void (*error)(char *x)) { - const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); + const size_t wksp_size = zstd_dctx_workspace_bound(); void *wksp = large_malloc(wksp_size); - ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size); + zstd_dctx *dctx = zstd_init_dctx(wksp, wksp_size); int err; size_t ret; if (dctx == NULL) { - error("Out of memory while allocating ZSTD_DCtx"); + error("Out of memory while allocating zstd_dctx"); err = -1; goto out; } /* * Find out how large the frame actually is, there may be junk at - * the end of the frame that ZSTD_decompressDCtx() can't handle. + * the end of the frame that zstd_decompress_dctx() can't handle. */ - ret = ZSTD_findFrameCompressedSize(in_buf, in_len); + ret = zstd_find_frame_compressed_size(in_buf, in_len); err = handle_zstd_error(ret, error); if (err) goto out; in_len = (long)ret; - ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len); + ret = zstd_decompress_dctx(dctx, out_buf, out_len, in_buf, in_len); err = handle_zstd_error(ret, error); if (err) goto out; @@ -167,14 +167,14 @@ static int INIT __unzstd(unsigned char *in_buf, long in_len, long *in_pos, void (*error)(char *x)) { - ZSTD_inBuffer in; - ZSTD_outBuffer out; - ZSTD_frameParams params; + zstd_in_buffer in; + zstd_out_buffer out; + zstd_frame_header header; void *in_allocated = NULL; void *out_allocated = NULL; void *wksp = NULL; size_t wksp_size; - ZSTD_DStream *dstream; + zstd_dstream *dstream; int err; size_t ret; @@ -238,13 +238,13 @@ static int INIT __unzstd(unsigned char *in_buf, long in_len, out.size = out_len; /* - * We need to know the window size to allocate the ZSTD_DStream. + * We need to know the window size to allocate the zstd_dstream. * Since we are streaming, we need to allocate a buffer for the sliding * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX * (8 MB), so it is important to use the actual value so as not to * waste memory when it is smaller. */ - ret = ZSTD_getFrameParams(¶ms, in.src, in.size); + ret = zstd_get_frame_header(&header, in.src, in.size); err = handle_zstd_error(ret, error); if (err) goto out; @@ -253,19 +253,19 @@ static int INIT __unzstd(unsigned char *in_buf, long in_len, err = -1; goto out; } - if (params.windowSize > ZSTD_WINDOWSIZE_MAX) { + if (header.windowSize > ZSTD_WINDOWSIZE_MAX) { error("ZSTD-compressed data has too large a window size"); err = -1; goto out; } /* - * Allocate the ZSTD_DStream now that we know how much memory is + * Allocate the zstd_dstream now that we know how much memory is * required. */ - wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize); + wksp_size = zstd_dstream_workspace_bound(header.windowSize); wksp = large_malloc(wksp_size); - dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size); + dstream = zstd_init_dstream(header.windowSize, wksp, wksp_size); if (dstream == NULL) { error("Out of memory while allocating ZSTD_DStream"); err = -1; @@ -298,7 +298,7 @@ static int INIT __unzstd(unsigned char *in_buf, long in_len, in.size = in_len; } /* Returns zero when the frame is complete. */ - ret = ZSTD_decompressStream(dstream, &out, &in); + ret = zstd_decompress_stream(dstream, &out, &in); err = handle_zstd_error(ret, error); if (err) goto out; diff --git a/lib/devres.c b/lib/devres.c index b0e1c6702c..14664bbb48 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -528,3 +528,85 @@ void pcim_iounmap_regions(struct pci_dev *pdev, int mask) } EXPORT_SYMBOL(pcim_iounmap_regions); #endif /* CONFIG_PCI */ + +static void devm_arch_phys_ac_add_release(struct device *dev, void *res) +{ + arch_phys_wc_del(*((int *)res)); +} + +/** + * devm_arch_phys_wc_add - Managed arch_phys_wc_add() + * @dev: Managed device + * @base: Memory base address + * @size: Size of memory range + * + * Adds a WC MTRR using arch_phys_wc_add() and sets up a release callback. + * See arch_phys_wc_add() for more information. + */ +int devm_arch_phys_wc_add(struct device *dev, unsigned long base, unsigned long size) +{ + int *mtrr; + int ret; + + mtrr = devres_alloc(devm_arch_phys_ac_add_release, sizeof(*mtrr), GFP_KERNEL); + if (!mtrr) + return -ENOMEM; + + ret = arch_phys_wc_add(base, size); + if (ret < 0) { + devres_free(mtrr); + return ret; + } + + *mtrr = ret; + devres_add(dev, mtrr); + + return ret; +} +EXPORT_SYMBOL(devm_arch_phys_wc_add); + +struct arch_io_reserve_memtype_wc_devres { + resource_size_t start; + resource_size_t size; +}; + +static void devm_arch_io_free_memtype_wc_release(struct device *dev, void *res) +{ + const struct arch_io_reserve_memtype_wc_devres *this = res; + + arch_io_free_memtype_wc(this->start, this->size); +} + +/** + * devm_arch_io_reserve_memtype_wc - Managed arch_io_reserve_memtype_wc() + * @dev: Managed device + * @start: Memory base address + * @size: Size of memory range + * + * Reserves a memory range with WC caching using arch_io_reserve_memtype_wc() + * and sets up a release callback See arch_io_reserve_memtype_wc() for more + * information. + */ +int devm_arch_io_reserve_memtype_wc(struct device *dev, resource_size_t start, + resource_size_t size) +{ + struct arch_io_reserve_memtype_wc_devres *dr; + int ret; + + dr = devres_alloc(devm_arch_io_free_memtype_wc_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = arch_io_reserve_memtype_wc(start, size); + if (ret < 0) { + devres_free(dr); + return ret; + } + + dr->start = start; + dr->size = size; + devres_add(dev, dr); + + return ret; +} +EXPORT_SYMBOL(devm_arch_io_reserve_memtype_wc); diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 84c16309cc..dd7f56af9a 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -71,6 +71,8 @@ static DEFINE_MUTEX(ddebug_lock); static LIST_HEAD(ddebug_tables); static int verbose; module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, " dynamic_debug/control processing " + "( 0 = off (default), 1 = module add/rm, 2 = >control summary, 3 = parsing, 4 = per-site changes)"); /* Return the path relative to source root */ static inline const char *trim_prefix(const char *path) @@ -118,6 +120,8 @@ do { \ #define vpr_info(fmt, ...) vnpr_info(1, fmt, ##__VA_ARGS__) #define v2pr_info(fmt, ...) vnpr_info(2, fmt, ##__VA_ARGS__) +#define v3pr_info(fmt, ...) vnpr_info(3, fmt, ##__VA_ARGS__) +#define v4pr_info(fmt, ...) vnpr_info(4, fmt, ##__VA_ARGS__) static void vpr_info_dq(const struct ddebug_query *query, const char *msg) { @@ -130,7 +134,7 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg) fmtlen--; } - vpr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u\n", + v3pr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u\n", msg, query->function ?: "", query->filename ?: "", @@ -213,7 +217,7 @@ static int ddebug_change(const struct ddebug_query *query, static_branch_enable(&dp->key.dd_key_true); #endif dp->flags = newflags; - v2pr_info("changed %s:%d [%s]%s =%s\n", + v4pr_info("changed %s:%d [%s]%s =%s\n", trim_prefix(dp->filename), dp->lineno, dt->mod_name, dp->function, ddebug_describe_flags(dp->flags, &fbuf)); @@ -273,7 +277,7 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords) buf = end; } - if (verbose) { + if (verbose >= 3) { int i; pr_info("split into words:"); for (i = 0; i < nwords; i++) @@ -333,7 +337,7 @@ static int parse_linerange(struct ddebug_query *query, const char *first) } else { query->last_lineno = query->first_lineno; } - vpr_info("parsed line %d-%d\n", query->first_lineno, + v3pr_info("parsed line %d-%d\n", query->first_lineno, query->last_lineno); return 0; } @@ -447,7 +451,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers) pr_err("bad flag-op %c, at start of %s\n", *str, str); return -EINVAL; } - vpr_info("op='%c'\n", op); + v3pr_info("op='%c'\n", op); for (; *str ; ++str) { for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) { @@ -461,7 +465,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers) return -EINVAL; } } - vpr_info("flags=0x%x\n", modifiers->flags); + v3pr_info("flags=0x%x\n", modifiers->flags); /* calculate final flags, mask based upon op */ switch (op) { @@ -477,7 +481,7 @@ static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers) modifiers->flags = 0; break; } - vpr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); + v3pr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); return 0; } @@ -529,7 +533,7 @@ static int ddebug_exec_queries(char *query, const char *modname) if (!query || !*query || *query == '#') continue; - vpr_info("query %d: \"%s\"\n", i, query); + vpr_info("query %d: \"%s\" mod:%s\n", i, query, modname ?: "*"); rc = ddebug_exec_query(query, modname); if (rc < 0) { @@ -540,8 +544,9 @@ static int ddebug_exec_queries(char *query, const char *modname) } i++; } - vpr_info("processed %d queries, with %d matches, %d errs\n", - i, nfound, errs); + if (i) + v2pr_info("processed %d queries, with %d matches, %d errs\n", + i, nfound, errs); if (exitcode) return exitcode; @@ -746,21 +751,6 @@ EXPORT_SYMBOL(__dynamic_ibdev_dbg); #endif -#define DDEBUG_STRING_SIZE 1024 -static __initdata char ddebug_setup_string[DDEBUG_STRING_SIZE]; - -static __init int ddebug_setup_query(char *str) -{ - if (strlen(str) >= DDEBUG_STRING_SIZE) { - pr_warn("ddebug boot param string too large\n"); - return 0; - } - strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE); - return 1; -} - -__setup("ddebug_query=", ddebug_setup_query); - /* * Install a noop handler to make dyndbg look like a normal kernel cli param. * This avoids warnings about dyndbg being an unknown cli param when supplied @@ -793,7 +783,7 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, tmpbuf = memdup_user_nul(ubuf, len); if (IS_ERR(tmpbuf)) return PTR_ERR(tmpbuf); - vpr_info("read %d bytes from userspace\n", (int)len); + v2pr_info("read %zu bytes from userspace\n", len); ret = ddebug_exec_queries(tmpbuf, NULL); kfree(tmpbuf); @@ -981,7 +971,7 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n, list_add(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); - v2pr_info("%3u debug prints in module %s\n", n, dt->mod_name); + vpr_info("%3u debug prints in module %s\n", n, dt->mod_name); return 0; } @@ -1040,8 +1030,6 @@ int ddebug_remove_module(const char *mod_name) struct ddebug_table *dt, *nextdt; int ret = -ENOENT; - v2pr_info("removing module \"%s\"\n", mod_name); - mutex_lock(&ddebug_lock); list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { if (dt->mod_name == mod_name) { @@ -1051,6 +1039,8 @@ int ddebug_remove_module(const char *mod_name) } } mutex_unlock(&ddebug_lock); + if (!ret) + v2pr_info("removed module \"%s\"\n", mod_name); return ret; } @@ -1133,16 +1123,6 @@ static int __init dynamic_debug_init(void) entries, modct, (int)((modct * sizeof(struct ddebug_table)) >> 10), (int)((entries * sizeof(struct _ddebug)) >> 10)); - /* apply ddebug_query boot param, dont unload tables on err */ - if (ddebug_setup_string[0] != '\0') { - pr_warn("ddebug_query param name is deprecated, change it to dyndbg\n"); - ret = ddebug_exec_queries(ddebug_setup_string, NULL); - if (ret < 0) - pr_warn("Invalid ddebug boot param %s\n", - ddebug_setup_string); - else - pr_info("%d changes by ddebug_query\n", ret); - } /* now that ddebug tables are loaded, process all boot args * again to find and activate queries given in dyndbg params. * While this has already been done for known boot params, it diff --git a/lib/error-inject.c b/lib/error-inject.c index c73651b15b..2ff5ef689d 100644 --- a/lib/error-inject.c +++ b/lib/error-inject.c @@ -8,6 +8,7 @@ #include #include #include +#include /* Whitelist of symbols that can be overridden for error injection. */ static LIST_HEAD(error_injection_list); @@ -64,7 +65,7 @@ static void populate_error_injection_list(struct error_injection_entry *start, mutex_lock(&ei_mutex); for (iter = start; iter < end; iter++) { - entry = arch_deref_entry_point((void *)iter->addr); + entry = (unsigned long)dereference_symbol_descriptor((void *)iter->addr); if (!kernel_text_address(entry) || !kallsyms_lookup_size_offset(entry, &size, &offset)) { diff --git a/lib/find_bit.c b/lib/find_bit.c index 0f8e2e369b..1b8e4b2a9c 100644 --- a/lib/find_bit.c +++ b/lib/find_bit.c @@ -89,6 +89,27 @@ unsigned long _find_first_bit(const unsigned long *addr, unsigned long size) EXPORT_SYMBOL(_find_first_bit); #endif +#ifndef find_first_and_bit +/* + * Find the first set bit in two memory regions. + */ +unsigned long _find_first_and_bit(const unsigned long *addr1, + const unsigned long *addr2, + unsigned long size) +{ + unsigned long idx, val; + + for (idx = 0; idx * BITS_PER_LONG < size; idx++) { + val = addr1[idx] & addr2[idx]; + if (val) + return min(idx * BITS_PER_LONG + __ffs(val), size); + } + + return size; +} +EXPORT_SYMBOL(_find_first_and_bit); +#endif + #ifndef find_first_zero_bit /* * Find the first cleared bit in a memory region. diff --git a/lib/find_bit_benchmark.c b/lib/find_bit_benchmark.c index 5637c5711d..db904b57d4 100644 --- a/lib/find_bit_benchmark.c +++ b/lib/find_bit_benchmark.c @@ -49,6 +49,25 @@ static int __init test_find_first_bit(void *bitmap, unsigned long len) return 0; } +static int __init test_find_first_and_bit(void *bitmap, const void *bitmap2, unsigned long len) +{ + static DECLARE_BITMAP(cp, BITMAP_LEN) __initdata; + unsigned long i, cnt; + ktime_t time; + + bitmap_copy(cp, bitmap, BITMAP_LEN); + + time = ktime_get(); + for (cnt = i = 0; i < len; cnt++) { + i = find_first_and_bit(cp, bitmap2, len); + __clear_bit(i, cp); + } + time = ktime_get() - time; + pr_err("find_first_and_bit: %18llu ns, %6ld iterations\n", time, cnt); + + return 0; +} + static int __init test_find_next_bit(const void *bitmap, unsigned long len) { unsigned long i, cnt; @@ -129,6 +148,7 @@ static int __init find_bit_test(void) * traverse only part of bitmap to avoid soft lockup. */ test_find_first_bit(bitmap, BITMAP_LEN / 10); + test_find_first_and_bit(bitmap, bitmap2, BITMAP_LEN / 2); test_find_next_and_bit(bitmap, bitmap2, BITMAP_LEN); pr_err("\nStart testing find_bit() with sparse bitmap\n"); @@ -145,6 +165,7 @@ static int __init find_bit_test(void) test_find_next_zero_bit(bitmap, BITMAP_LEN); test_find_last_bit(bitmap, BITMAP_LEN); test_find_first_bit(bitmap, BITMAP_LEN); + test_find_first_and_bit(bitmap, bitmap2, BITMAP_LEN); test_find_next_and_bit(bitmap, bitmap2, BITMAP_LEN); /* diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c index 4515439375..53e7eb1dd7 100644 --- a/lib/flex_proportions.c +++ b/lib/flex_proportions.c @@ -217,11 +217,12 @@ static void fprop_reflect_period_percpu(struct fprop_global *p, } /* Event of type pl happened */ -void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl) +void __fprop_add_percpu(struct fprop_global *p, struct fprop_local_percpu *pl, + long nr) { fprop_reflect_period_percpu(p, pl); - percpu_counter_add_batch(&pl->events, 1, PROP_BATCH); - percpu_counter_add(&p->events, 1); + percpu_counter_add_batch(&pl->events, nr, PROP_BATCH); + percpu_counter_add(&p->events, nr); } void fprop_fraction_percpu(struct fprop_global *p, @@ -253,20 +254,29 @@ void fprop_fraction_percpu(struct fprop_global *p, } /* - * Like __fprop_inc_percpu() except that event is counted only if the given + * Like __fprop_add_percpu() except that event is counted only if the given * type has fraction smaller than @max_frac/FPROP_FRAC_BASE */ -void __fprop_inc_percpu_max(struct fprop_global *p, - struct fprop_local_percpu *pl, int max_frac) +void __fprop_add_percpu_max(struct fprop_global *p, + struct fprop_local_percpu *pl, int max_frac, long nr) { if (unlikely(max_frac < FPROP_FRAC_BASE)) { unsigned long numerator, denominator; + s64 tmp; fprop_fraction_percpu(p, pl, &numerator, &denominator); - if (numerator > - (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT) + /* Adding 'nr' to fraction exceeds max_frac/FPROP_FRAC_BASE? */ + tmp = (u64)denominator * max_frac - + ((u64)numerator << FPROP_FRAC_SHIFT); + if (tmp < 0) { + /* Maximum fraction already exceeded? */ return; + } else if (tmp < nr * (FPROP_FRAC_BASE - max_frac)) { + /* Add just enough for the fraction to saturate */ + nr = div_u64(tmp + FPROP_FRAC_BASE - max_frac - 1, + FPROP_FRAC_BASE - max_frac); + } } - __fprop_inc_percpu(p, pl); + __fprop_add_percpu(p, pl, nr); } diff --git a/lib/genalloc.c b/lib/genalloc.c index 9a57257988..00fc50d0a6 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -251,7 +251,7 @@ void gen_pool_destroy(struct gen_pool *pool) list_del(&chunk->next_chunk); end_bit = chunk_size(chunk) >> order; - bit = find_next_bit(chunk->bits, end_bit, 0); + bit = find_first_bit(chunk->bits, end_bit); BUG_ON(bit < end_bit); vfree(chunk); diff --git a/lib/iov_iter.c b/lib/iov_iter.c index c5b2f0f4b8..6dd5330f7a 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -69,42 +69,40 @@ #define iterate_xarray(i, n, base, len, __off, STEP) { \ __label__ __out; \ size_t __off = 0; \ - struct page *head = NULL; \ + struct folio *folio; \ loff_t start = i->xarray_start + i->iov_offset; \ - unsigned offset = start % PAGE_SIZE; \ pgoff_t index = start / PAGE_SIZE; \ - int j; \ - \ XA_STATE(xas, i->xarray, index); \ \ + len = PAGE_SIZE - offset_in_page(start); \ rcu_read_lock(); \ - xas_for_each(&xas, head, ULONG_MAX) { \ + xas_for_each(&xas, folio, ULONG_MAX) { \ unsigned left; \ - if (xas_retry(&xas, head)) \ + size_t offset; \ + if (xas_retry(&xas, folio)) \ continue; \ - if (WARN_ON(xa_is_value(head))) \ + if (WARN_ON(xa_is_value(folio))) \ break; \ - if (WARN_ON(PageHuge(head))) \ + if (WARN_ON(folio_test_hugetlb(folio))) \ break; \ - for (j = (head->index < index) ? index - head->index : 0; \ - j < thp_nr_pages(head); j++) { \ - void *kaddr = kmap_local_page(head + j); \ - base = kaddr + offset; \ - len = PAGE_SIZE - offset; \ + offset = offset_in_folio(folio, start + __off); \ + while (offset < folio_size(folio)) { \ + base = kmap_local_folio(folio, offset); \ len = min(n, len); \ left = (STEP); \ - kunmap_local(kaddr); \ + kunmap_local(base); \ len -= left; \ __off += len; \ n -= len; \ if (left || n == 0) \ goto __out; \ - offset = 0; \ + offset += len; \ + len = PAGE_SIZE; \ } \ } \ __out: \ rcu_read_unlock(); \ - i->iov_offset += __off; \ + i->iov_offset += __off; \ n = __off; \ } @@ -191,7 +189,7 @@ static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t b buf = iov->iov_base + skip; copy = min(bytes, iov->iov_len - skip); - if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_writeable(buf, copy)) { + if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_writeable(buf, copy)) { kaddr = kmap_atomic(page); from = kaddr + offset; @@ -275,7 +273,7 @@ static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t buf = iov->iov_base + skip; copy = min(bytes, iov->iov_len - skip); - if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_pages_readable(buf, copy)) { + if (IS_ENABLED(CONFIG_HIGHMEM) && !fault_in_readable(buf, copy)) { kaddr = kmap_atomic(page); to = kaddr + offset; @@ -431,35 +429,81 @@ static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t by } /* - * Fault in one or more iovecs of the given iov_iter, to a maximum length of - * bytes. For each iovec, fault in each page that constitutes the iovec. + * fault_in_iov_iter_readable - fault in iov iterator for reading + * @i: iterator + * @size: maximum length * - * Return 0 on success, or non-zero if the memory could not be accessed (i.e. - * because it is an invalid address). + * Fault in one or more iovecs of the given iov_iter, to a maximum length of + * @size. For each iovec, fault in each page that constitutes the iovec. + * + * Returns the number of bytes not faulted in (like copy_to_user() and + * copy_from_user()). + * + * Always returns 0 for non-userspace iterators. */ -int iov_iter_fault_in_readable(const struct iov_iter *i, size_t bytes) +size_t fault_in_iov_iter_readable(const struct iov_iter *i, size_t size) { if (iter_is_iovec(i)) { + size_t count = min(size, iov_iter_count(i)); const struct iovec *p; size_t skip; - if (bytes > i->count) - bytes = i->count; - for (p = i->iov, skip = i->iov_offset; bytes; p++, skip = 0) { - size_t len = min(bytes, p->iov_len - skip); - int err; + size -= count; + for (p = i->iov, skip = i->iov_offset; count; p++, skip = 0) { + size_t len = min(count, p->iov_len - skip); + size_t ret; if (unlikely(!len)) continue; - err = fault_in_pages_readable(p->iov_base + skip, len); - if (unlikely(err)) - return err; - bytes -= len; + ret = fault_in_readable(p->iov_base + skip, len); + count -= len - ret; + if (ret) + break; } + return count + size; } return 0; } -EXPORT_SYMBOL(iov_iter_fault_in_readable); +EXPORT_SYMBOL(fault_in_iov_iter_readable); + +/* + * fault_in_iov_iter_writeable - fault in iov iterator for writing + * @i: iterator + * @size: maximum length + * + * Faults in the iterator using get_user_pages(), i.e., without triggering + * hardware page faults. This is primarily useful when we already know that + * some or all of the pages in @i aren't in memory. + * + * Returns the number of bytes not faulted in, like copy_to_user() and + * copy_from_user(). + * + * Always returns 0 for non-user-space iterators. + */ +size_t fault_in_iov_iter_writeable(const struct iov_iter *i, size_t size) +{ + if (iter_is_iovec(i)) { + size_t count = min(size, iov_iter_count(i)); + const struct iovec *p; + size_t skip; + + size -= count; + for (p = i->iov, skip = i->iov_offset; count; p++, skip = 0) { + size_t len = min(count, p->iov_len - skip); + size_t ret; + + if (unlikely(!len)) + continue; + ret = fault_in_safe_writeable(p->iov_base + skip, len); + count -= len - ret; + if (ret) + break; + } + return count + size; + } + return 0; +} +EXPORT_SYMBOL(fault_in_iov_iter_writeable); void iov_iter_init(struct iov_iter *i, unsigned int direction, const struct iovec *iov, unsigned long nr_segs, @@ -468,6 +512,7 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction, WARN_ON(direction & ~(READ | WRITE)); *i = (struct iov_iter) { .iter_type = ITER_IOVEC, + .nofault = false, .data_source = direction, .iov = iov, .nr_segs = nr_segs, @@ -1483,13 +1528,17 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, return 0; if (likely(iter_is_iovec(i))) { + unsigned int gup_flags = 0; unsigned long addr; + if (iov_iter_rw(i) != WRITE) + gup_flags |= FOLL_WRITE; + if (i->nofault) + gup_flags |= FOLL_NOFAULT; + addr = first_iovec_segment(i, &len, start, maxsize, maxpages); n = DIV_ROUND_UP(len, PAGE_SIZE); - res = get_user_pages_fast(addr, n, - iov_iter_rw(i) != WRITE ? FOLL_WRITE : 0, - pages); + res = get_user_pages_fast(addr, n, gup_flags, pages); if (unlikely(res <= 0)) return res; return (res == n ? len : res * PAGE_SIZE) - *start; @@ -1605,15 +1654,20 @@ ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, return 0; if (likely(iter_is_iovec(i))) { + unsigned int gup_flags = 0; unsigned long addr; + if (iov_iter_rw(i) != WRITE) + gup_flags |= FOLL_WRITE; + if (i->nofault) + gup_flags |= FOLL_NOFAULT; + addr = first_iovec_segment(i, &len, start, maxsize, ~0U); n = DIV_ROUND_UP(len, PAGE_SIZE); p = get_pages_array(n); if (!p) return -ENOMEM; - res = get_user_pages_fast(addr, n, - iov_iter_rw(i) != WRITE ? FOLL_WRITE : 0, p); + res = get_user_pages_fast(addr, n, gup_flags, p); if (unlikely(res <= 0)) { kvfree(p); *pages = NULL; diff --git a/lib/kobject.c b/lib/kobject.c index ea53b30cf4..56fa037501 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -65,7 +65,7 @@ void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid) */ static int populate_dir(struct kobject *kobj) { - struct kobj_type *t = get_ktype(kobj); + const struct kobj_type *t = get_ktype(kobj); struct attribute *attr; int error = 0; int i; @@ -346,7 +346,7 @@ EXPORT_SYMBOL(kobject_set_name); * to kobject_put(), not by a call to kfree directly to ensure that all of * the memory is cleaned up properly. */ -void kobject_init(struct kobject *kobj, struct kobj_type *ktype) +void kobject_init(struct kobject *kobj, const struct kobj_type *ktype) { char *err_str; @@ -461,7 +461,7 @@ EXPORT_SYMBOL(kobject_add); * same type of error handling after a call to kobject_add() and kobject * lifetime rules are the same here. */ -int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, +int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; @@ -679,7 +679,7 @@ EXPORT_SYMBOL(kobject_get_unless_zero); static void kobject_cleanup(struct kobject *kobj) { struct kobject *parent = kobj->parent; - struct kobj_type *t = get_ktype(kobj); + const struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; pr_debug("kobject: '%s' (%p): %s, parent %p\n", @@ -777,7 +777,7 @@ static struct kobj_type dynamic_kobj_ktype = { * call to kobject_put() and not kfree(), as kobject_init() has * already been called on this structure. */ -struct kobject *kobject_create(void) +static struct kobject *kobject_create(void) { struct kobject *kobj; diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index c87d5b6a8a..7c44b7ae4c 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -501,7 +501,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, } /* skip the event, if the filter returns zero. */ if (uevent_ops && uevent_ops->filter) - if (!uevent_ops->filter(kset, kobj)) { + if (!uevent_ops->filter(kobj)) { pr_debug("kobject: '%s' (%p): %s: filter function " "caused the event to drop!\n", kobject_name(kobj), kobj, __func__); @@ -510,7 +510,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* originating subsystem */ if (uevent_ops && uevent_ops->name) - subsystem = uevent_ops->name(kset, kobj); + subsystem = uevent_ops->name(kobj); else subsystem = kobject_name(&kset->kobj); if (!subsystem) { @@ -554,7 +554,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* let the kset specific function add its stuff */ if (uevent_ops && uevent_ops->uevent) { - retval = uevent_ops->uevent(kset, kobj, env); + retval = uevent_ops->uevent(kobj, env); if (retval) { pr_debug("kobject: '%s' (%p): %s: uevent() returned " "%d\n", kobject_name(kobj), kobj, diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 059b8b00dc..886510d248 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -22,6 +22,7 @@ #include "kstrtox.h" +noinline const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) { if (*base == 0) { @@ -47,6 +48,7 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) * * Don't you dare use this function. */ +noinline unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p, size_t max_chars) { @@ -85,6 +87,7 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon return rv; } +noinline unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) { return _parse_integer_limit(s, base, p, INT_MAX); @@ -125,6 +128,7 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. * Preferred over simple_strtoull(). Return code must be checked. */ +noinline int kstrtoull(const char *s, unsigned int base, unsigned long long *res) { if (s[0] == '+') @@ -148,6 +152,7 @@ EXPORT_SYMBOL(kstrtoull); * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. * Preferred over simple_strtoll(). Return code must be checked. */ +noinline int kstrtoll(const char *s, unsigned int base, long long *res) { unsigned long long tmp; @@ -219,6 +224,7 @@ EXPORT_SYMBOL(_kstrtol); * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. * Preferred over simple_strtoul(). Return code must be checked. */ +noinline int kstrtouint(const char *s, unsigned int base, unsigned int *res) { unsigned long long tmp; @@ -249,6 +255,7 @@ EXPORT_SYMBOL(kstrtouint); * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. * Preferred over simple_strtol(). Return code must be checked. */ +noinline int kstrtoint(const char *s, unsigned int base, int *res) { long long tmp; @@ -264,6 +271,7 @@ int kstrtoint(const char *s, unsigned int base, int *res) } EXPORT_SYMBOL(kstrtoint); +noinline int kstrtou16(const char *s, unsigned int base, u16 *res) { unsigned long long tmp; @@ -279,6 +287,7 @@ int kstrtou16(const char *s, unsigned int base, u16 *res) } EXPORT_SYMBOL(kstrtou16); +noinline int kstrtos16(const char *s, unsigned int base, s16 *res) { long long tmp; @@ -294,6 +303,7 @@ int kstrtos16(const char *s, unsigned int base, s16 *res) } EXPORT_SYMBOL(kstrtos16); +noinline int kstrtou8(const char *s, unsigned int base, u8 *res) { unsigned long long tmp; @@ -309,6 +319,7 @@ int kstrtou8(const char *s, unsigned int base, u8 *res) } EXPORT_SYMBOL(kstrtou8); +noinline int kstrtos8(const char *s, unsigned int base, s8 *res) { long long tmp; @@ -333,6 +344,7 @@ EXPORT_SYMBOL(kstrtos8); * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value * pointed to by res is updated upon finding a match. */ +noinline int kstrtobool(const char *s, bool *res) { if (!s) diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index acd1de436f..22640c9ee8 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -15,23 +15,89 @@ extern struct kunit_suite * const * const __kunit_suites_end[]; #if IS_BUILTIN(CONFIG_KUNIT) static char *filter_glob_param; +static char *action_param; + module_param_named(filter_glob, filter_glob_param, charp, 0); MODULE_PARM_DESC(filter_glob, - "Filter which KUnit test suites run at boot-time, e.g. list*"); + "Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test"); +module_param_named(action, action_param, charp, 0); +MODULE_PARM_DESC(action, + "Changes KUnit executor behavior, valid values are:\n" + ": run the tests like normal\n" + "'list' to list test names instead of running them.\n"); + +/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */ +struct kunit_test_filter { + char *suite_glob; + char *test_glob; +}; + +/* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ +static void kunit_parse_filter_glob(struct kunit_test_filter *parsed, + const char *filter_glob) +{ + const int len = strlen(filter_glob); + const char *period = strchr(filter_glob, '.'); + + if (!period) { + parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL); + parsed->test_glob = NULL; + strcpy(parsed->suite_glob, filter_glob); + return; + } + + parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL); + parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL); + + strncpy(parsed->suite_glob, filter_glob, period - filter_glob); + strncpy(parsed->test_glob, period + 1, len - (period - filter_glob)); +} + +/* Create a copy of suite with only tests that match test_glob. */ +static struct kunit_suite * +kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob) +{ + int n = 0; + struct kunit_case *filtered, *test_case; + struct kunit_suite *copy; + + kunit_suite_for_each_test_case(suite, test_case) { + if (!test_glob || glob_match(test_glob, test_case->name)) + ++n; + } + + if (n == 0) + return NULL; + + /* Use memcpy to workaround copy->name being const. */ + copy = kmalloc(sizeof(*copy), GFP_KERNEL); + memcpy(copy, suite, sizeof(*copy)); + + filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL); + + n = 0; + kunit_suite_for_each_test_case(suite, test_case) { + if (!test_glob || glob_match(test_glob, test_case->name)) + filtered[n++] = *test_case; + } + + copy->test_cases = filtered; + return copy; +} static char *kunit_shutdown; core_param(kunit_shutdown, kunit_shutdown, charp, 0644); static struct kunit_suite * const * kunit_filter_subsuite(struct kunit_suite * const * const subsuite, - const char *filter_glob) + struct kunit_test_filter *filter) { int i, n = 0; - struct kunit_suite **filtered; + struct kunit_suite **filtered, *filtered_suite; n = 0; - for (i = 0; subsuite[i] != NULL; ++i) { - if (glob_match(filter_glob, subsuite[i]->name)) + for (i = 0; subsuite[i]; ++i) { + if (glob_match(filter->suite_glob, subsuite[i]->name)) ++n; } @@ -44,8 +110,11 @@ kunit_filter_subsuite(struct kunit_suite * const * const subsuite, n = 0; for (i = 0; subsuite[i] != NULL; ++i) { - if (glob_match(filter_glob, subsuite[i]->name)) - filtered[n++] = subsuite[i]; + if (!glob_match(filter->suite_glob, subsuite[i]->name)) + continue; + filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob); + if (filtered_suite) + filtered[n++] = filtered_suite; } filtered[n] = NULL; @@ -57,12 +126,32 @@ struct suite_set { struct kunit_suite * const * const *end; }; +static void kunit_free_subsuite(struct kunit_suite * const *subsuite) +{ + unsigned int i; + + for (i = 0; subsuite[i]; i++) + kfree(subsuite[i]); + + kfree(subsuite); +} + +static void kunit_free_suite_set(struct suite_set suite_set) +{ + struct kunit_suite * const * const *suites; + + for (suites = suite_set.start; suites < suite_set.end; suites++) + kunit_free_subsuite(*suites); + kfree(suite_set.start); +} + static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, const char *filter_glob) { int i; struct kunit_suite * const **copy, * const *filtered_subsuite; struct suite_set filtered; + struct kunit_test_filter filter; const size_t max = suite_set->end - suite_set->start; @@ -73,12 +162,17 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, return filtered; } + kunit_parse_filter_glob(&filter, filter_glob); + for (i = 0; i < max; ++i) { - filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], filter_glob); + filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter); if (filtered_subsuite) *copy++ = filtered_subsuite; } filtered.end = copy; + + kfree(filter.suite_glob); + kfree(filter.test_glob); return filtered; } @@ -109,9 +203,35 @@ static void kunit_print_tap_header(struct suite_set *suite_set) pr_info("1..%d\n", num_of_suites); } -int kunit_run_all_tests(void) +static void kunit_exec_run_tests(struct suite_set *suite_set) { struct kunit_suite * const * const *suites; + + kunit_print_tap_header(suite_set); + + for (suites = suite_set->start; suites < suite_set->end; suites++) + __kunit_test_suites_init(*suites); +} + +static void kunit_exec_list_tests(struct suite_set *suite_set) +{ + unsigned int i; + struct kunit_suite * const * const *suites; + struct kunit_case *test_case; + + /* Hack: print a tap header so kunit.py can find the start of KUnit output. */ + pr_info("TAP version 14\n"); + + for (suites = suite_set->start; suites < suite_set->end; suites++) + for (i = 0; (*suites)[i] != NULL; i++) { + kunit_suite_for_each_test_case((*suites)[i], test_case) { + pr_info("%s.%s\n", (*suites)[i]->name, test_case->name); + } + } +} + +int kunit_run_all_tests(void) +{ struct suite_set suite_set = { .start = __kunit_suites_start, .end = __kunit_suites_end, @@ -120,15 +240,15 @@ int kunit_run_all_tests(void) if (filter_glob_param) suite_set = kunit_filter_suites(&suite_set, filter_glob_param); - kunit_print_tap_header(&suite_set); - - for (suites = suite_set.start; suites < suite_set.end; suites++) - __kunit_test_suites_init(*suites); + if (!action_param) + kunit_exec_run_tests(&suite_set); + else if (strcmp(action_param, "list") == 0) + kunit_exec_list_tests(&suite_set); + else + pr_err("kunit executor: unknown action '%s'\n", action_param); if (filter_glob_param) { /* a copy was made of each array */ - for (suites = suite_set.start; suites < suite_set.end; suites++) - kfree(*suites); - kfree(suite_set.start); + kunit_free_suite_set(suite_set); } kunit_handle_shutdown(); diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c index e14a18af57..4ed57fd94e 100644 --- a/lib/kunit/executor_test.c +++ b/lib/kunit/executor_test.c @@ -9,38 +9,103 @@ #include static void kfree_at_end(struct kunit *test, const void *to_free); +static void free_subsuite_at_end(struct kunit *test, + struct kunit_suite *const *to_free); static struct kunit_suite *alloc_fake_suite(struct kunit *test, - const char *suite_name); + const char *suite_name, + struct kunit_case *test_cases); + +static void dummy_test(struct kunit *test) {} + +static struct kunit_case dummy_test_cases[] = { + /* .run_case is not important, just needs to be non-NULL */ + { .name = "test1", .run_case = dummy_test }, + { .name = "test2", .run_case = dummy_test }, + {}, +}; + +static void parse_filter_test(struct kunit *test) +{ + struct kunit_test_filter filter = {NULL, NULL}; + + kunit_parse_filter_glob(&filter, "suite"); + KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite"); + KUNIT_EXPECT_FALSE(test, filter.test_glob); + kfree(filter.suite_glob); + kfree(filter.test_glob); + + kunit_parse_filter_glob(&filter, "suite.test"); + KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite"); + KUNIT_EXPECT_STREQ(test, filter.test_glob, "test"); + kfree(filter.suite_glob); + kfree(filter.test_glob); +} static void filter_subsuite_test(struct kunit *test) { struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite * const *filtered; + struct kunit_test_filter filter = { + .suite_glob = "suite2", + .test_glob = NULL, + }; - subsuite[0] = alloc_fake_suite(test, "suite1"); - subsuite[1] = alloc_fake_suite(test, "suite2"); + subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); + subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); /* Want: suite1, suite2, NULL -> suite2, NULL */ - filtered = kunit_filter_subsuite(subsuite, "suite2*"); + filtered = kunit_filter_subsuite(subsuite, &filter); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); - kfree_at_end(test, filtered); + free_subsuite_at_end(test, filtered); + /* Validate we just have suite2 */ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); - KUNIT_EXPECT_FALSE(test, filtered[1]); } +static void filter_subsuite_test_glob_test(struct kunit *test) +{ + struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; + struct kunit_suite * const *filtered; + struct kunit_test_filter filter = { + .suite_glob = "suite2", + .test_glob = "test2", + }; + + subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); + subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); + + /* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */ + filtered = kunit_filter_subsuite(subsuite, &filter); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); + free_subsuite_at_end(test, filtered); + + /* Validate we just have suite2 */ + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); + KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); + KUNIT_EXPECT_FALSE(test, filtered[1]); + + /* Now validate we just have test2 */ + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]->test_cases); + KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->test_cases[0].name, "test2"); + KUNIT_EXPECT_FALSE(test, filtered[0]->test_cases[1].name); +} + static void filter_subsuite_to_empty_test(struct kunit *test) { struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite * const *filtered; + struct kunit_test_filter filter = { + .suite_glob = "not_found", + .test_glob = NULL, + }; - subsuite[0] = alloc_fake_suite(test, "suite1"); - subsuite[1] = alloc_fake_suite(test, "suite2"); + subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); + subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); - filtered = kunit_filter_subsuite(subsuite, "not_found"); - kfree_at_end(test, filtered); /* just in case */ + filtered = kunit_filter_subsuite(subsuite, &filter); + free_subsuite_at_end(test, filtered); /* just in case */ KUNIT_EXPECT_FALSE_MSG(test, filtered, "should be NULL to indicate no match"); @@ -52,7 +117,7 @@ static void kfree_subsuites_at_end(struct kunit *test, struct suite_set *suite_s kfree_at_end(test, suite_set->start); for (suites = suite_set->start; suites < suite_set->end; suites++) - kfree_at_end(test, *suites); + free_subsuite_at_end(test, *suites); } static void filter_suites_test(struct kunit *test) @@ -74,8 +139,8 @@ static void filter_suites_test(struct kunit *test) struct suite_set filtered = {.start = NULL, .end = NULL}; /* Emulate two files, each having one suite */ - subsuites[0][0] = alloc_fake_suite(test, "suite0"); - subsuites[1][0] = alloc_fake_suite(test, "suite1"); + subsuites[0][0] = alloc_fake_suite(test, "suite0", dummy_test_cases); + subsuites[1][0] = alloc_fake_suite(test, "suite1", dummy_test_cases); /* Filter out suite1 */ filtered = kunit_filter_suites(&suite_set, "suite0"); @@ -84,11 +149,14 @@ static void filter_suites_test(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0]); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0][0]); KUNIT_EXPECT_STREQ(test, (const char *)filtered.start[0][0]->name, "suite0"); } static struct kunit_case executor_test_cases[] = { + KUNIT_CASE(parse_filter_test), KUNIT_CASE(filter_subsuite_test), + KUNIT_CASE(filter_subsuite_test_glob_test), KUNIT_CASE(filter_subsuite_to_empty_test), KUNIT_CASE(filter_suites_test), {} @@ -120,14 +188,30 @@ static void kfree_at_end(struct kunit *test, const void *to_free) (void *)to_free); } +static void free_subsuite_res_free(struct kunit_resource *res) +{ + kunit_free_subsuite(res->data); +} + +static void free_subsuite_at_end(struct kunit *test, + struct kunit_suite *const *to_free) +{ + if (IS_ERR_OR_NULL(to_free)) + return; + kunit_alloc_resource(test, NULL, free_subsuite_res_free, + GFP_KERNEL, (void *)to_free); +} + static struct kunit_suite *alloc_fake_suite(struct kunit *test, - const char *suite_name) + const char *suite_name, + struct kunit_case *test_cases) { struct kunit_suite *suite; /* We normally never expect to allocate suites, hence the non-const cast. */ suite = kunit_kzalloc(test, sizeof(*suite), GFP_KERNEL); strncpy((char *)suite->name, suite_name, sizeof(suite->name) - 1); + suite->test_cases = test_cases; return suite; } diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c index d69efcbed6..555601d17f 100644 --- a/lib/kunit/kunit-test.c +++ b/lib/kunit/kunit-test.c @@ -415,12 +415,15 @@ static struct kunit_suite kunit_log_test_suite = { static void kunit_log_test(struct kunit *test) { - struct kunit_suite *suite = &kunit_log_test_suite; + struct kunit_suite suite; + + suite.log = kunit_kzalloc(test, KUNIT_LOG_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, suite.log); kunit_log(KERN_INFO, test, "put this in log."); kunit_log(KERN_INFO, test, "this too."); - kunit_log(KERN_INFO, suite, "add to suite log."); - kunit_log(KERN_INFO, suite, "along with this."); + kunit_log(KERN_INFO, &suite, "add to suite log."); + kunit_log(KERN_INFO, &suite, "along with this."); #ifdef CONFIG_KUNIT_DEBUGFS KUNIT_EXPECT_NOT_ERR_OR_NULL(test, @@ -428,12 +431,11 @@ static void kunit_log_test(struct kunit *test) KUNIT_EXPECT_NOT_ERR_OR_NULL(test, strstr(test->log, "this too.")); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, - strstr(suite->log, "add to suite log.")); + strstr(suite.log, "add to suite log.")); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, - strstr(suite->log, "along with this.")); + strstr(suite.log, "along with this.")); #else KUNIT_EXPECT_PTR_EQ(test, test->log, (char *)NULL); - KUNIT_EXPECT_PTR_EQ(test, suite->log, (char *)NULL); #endif } diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 9aef816e57..c7ed4aabec 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -190,10 +190,10 @@ enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite) } EXPORT_SYMBOL_GPL(kunit_suite_has_succeeded); +static size_t kunit_suite_counter = 1; + static void kunit_print_subtest_end(struct kunit_suite *suite) { - static size_t kunit_suite_counter = 1; - kunit_print_ok_not_ok((void *)suite, false, kunit_suite_has_succeeded(suite), kunit_suite_counter++, @@ -512,6 +512,8 @@ int kunit_run_tests(struct kunit_suite *suite) /* Get initial param. */ param_desc[0] = '\0'; test.param_value = test_case->generate_params(NULL, param_desc); + kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT + "# Subtest: %s", test_case->name); while (test.param_value) { kunit_run_case_catch_errors(suite, test_case, &test); @@ -522,9 +524,8 @@ int kunit_run_tests(struct kunit_suite *suite) } kunit_log(KERN_INFO, &test, - KUNIT_SUBTEST_INDENT - "# %s: %s %d - %s", - test_case->name, + KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT + "%s %d - %s", kunit_status_to_ok_not_ok(test.status), test.param_index + 1, param_desc); @@ -585,6 +586,8 @@ void __kunit_test_suites_exit(struct kunit_suite **suites) for (i = 0; suites[i] != NULL; i++) kunit_exit_suite(suites[i]); + + kunit_suite_counter = 1; } EXPORT_SYMBOL_GPL(__kunit_test_suites_exit); diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c index 0dd434e404..be38a2c5ec 100644 --- a/lib/kunit/try-catch.c +++ b/lib/kunit/try-catch.c @@ -17,7 +17,7 @@ void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch) { try_catch->try_result = -EFAULT; - complete_and_exit(try_catch->try_completion, -EFAULT); + kthread_complete_and_exit(try_catch->try_completion, -EFAULT); } EXPORT_SYMBOL_GPL(kunit_try_catch_throw); @@ -27,7 +27,7 @@ static int kunit_generic_run_threadfn_adapter(void *data) try_catch->try(try_catch->context); - complete_and_exit(try_catch->try_completion, 0); + kthread_complete_and_exit(try_catch->try_completion, 0); } static unsigned long kunit_test_timeout(void) diff --git a/lib/list_debug.c b/lib/list_debug.c index 5d5424b51b..9daa3fb9d1 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -49,11 +49,11 @@ bool __list_del_entry_valid(struct list_head *entry) "list_del corruption, %px->prev is LIST_POISON2 (%px)\n", entry, LIST_POISON2) || CHECK_DATA_CORRUPTION(prev->next != entry, - "list_del corruption. prev->next should be %px, but was %px\n", - entry, prev->next) || + "list_del corruption. prev->next should be %px, but was %px. (prev=%px)\n", + entry, prev->next, prev) || CHECK_DATA_CORRUPTION(next->prev != entry, - "list_del corruption. next->prev should be %px, but was %px\n", - entry, next->prev)) + "list_del corruption. next->prev should be %px, but was %px. (next=%px)\n", + entry, next->prev, next)) return false; return true; diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 161108e5d2..8d24279fad 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -26,6 +26,12 @@ #include #include +#ifdef CONFIG_PREEMPT_RT +# define NON_RT(...) +#else +# define NON_RT(...) __VA_ARGS__ +#endif + /* * Change this to 1 if you want to see the failure printouts: */ @@ -139,7 +145,7 @@ static DEFINE_RT_MUTEX(rtmutex_Z2); #endif -static local_lock_t local_A = INIT_LOCAL_LOCK(local_A); +static DEFINE_PER_CPU(local_lock_t, local_A); /* * non-inlined runtime initializers, to let separate locks share @@ -258,7 +264,7 @@ static void init_shared_classes(void) #define WWAF(x) ww_acquire_fini(x) #define WWL(x, c) ww_mutex_lock(x, c) -#define WWT(x) ww_mutex_trylock(x) +#define WWT(x) ww_mutex_trylock(x, NULL) #define WWL1(x) ww_mutex_lock(x, NULL) #define WWU(x) ww_mutex_unlock(x) @@ -712,12 +718,18 @@ GENERATE_TESTCASE(ABCDBCDA_rtmutex); #undef E +#ifdef CONFIG_PREEMPT_RT +# define RT_PREPARE_DBL_UNLOCK() { migrate_disable(); rcu_read_lock(); } +#else +# define RT_PREPARE_DBL_UNLOCK() +#endif /* * Double unlock: */ #define E() \ \ LOCK(A); \ + RT_PREPARE_DBL_UNLOCK(); \ UNLOCK(A); \ UNLOCK(A); /* fail */ @@ -802,6 +814,7 @@ GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_rlock) #include "locking-selftest-wlock-hardirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-spin-softirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_spin) @@ -810,10 +823,12 @@ GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_rlock) #include "locking-selftest-wlock-softirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_wlock) +#endif #undef E1 #undef E2 +#ifndef CONFIG_PREEMPT_RT /* * Enabling hardirqs with a softirq-safe lock held: */ @@ -846,6 +861,8 @@ GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2A_rlock) #undef E1 #undef E2 +#endif + /* * Enabling irqs with an irq-safe lock held: */ @@ -875,6 +892,7 @@ GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_rlock) #include "locking-selftest-wlock-hardirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-spin-softirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_spin) @@ -883,6 +901,7 @@ GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_rlock) #include "locking-selftest-wlock-softirq.h" GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_wlock) +#endif #undef E1 #undef E2 @@ -921,6 +940,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_rlock) #include "locking-selftest-wlock-hardirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-spin-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_spin) @@ -929,6 +949,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_rlock) #include "locking-selftest-wlock-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_wlock) +#endif #undef E1 #undef E2 @@ -969,6 +990,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_rlock) #include "locking-selftest-wlock-hardirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-spin-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_spin) @@ -977,6 +999,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_rlock) #include "locking-selftest-wlock-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_wlock) +#endif #undef E1 #undef E2 @@ -1031,6 +1054,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_hard_rlock) #include "locking-selftest-wlock-hardirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-spin-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_spin) @@ -1039,6 +1063,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_rlock) #include "locking-selftest-wlock-softirq.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_wlock) +#endif #undef E1 #undef E2 @@ -1206,12 +1231,14 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_hard_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-softirq.h" #include "locking-selftest-rlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft_wlock) +#endif #undef E1 #undef E2 @@ -1252,12 +1279,14 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_hard_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-softirq.h" #include "locking-selftest-rlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_soft_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_soft_wlock) +#endif #undef E1 #undef E2 @@ -1306,12 +1335,14 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_hard_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_hard_wlock) +#ifndef CONFIG_PREEMPT_RT #include "locking-selftest-softirq.h" #include "locking-selftest-rlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_rlock) #include "locking-selftest-wlock.h" GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) +#endif #ifdef CONFIG_DEBUG_LOCK_ALLOC # define I_SPINLOCK(x) lockdep_reset_lock(&lock_##x.dep_map) @@ -1320,7 +1351,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion3_soft_wlock) # define I_MUTEX(x) lockdep_reset_lock(&mutex_##x.dep_map) # define I_RWSEM(x) lockdep_reset_lock(&rwsem_##x.dep_map) # define I_WW(x) lockdep_reset_lock(&x.dep_map) -# define I_LOCAL_LOCK(x) lockdep_reset_lock(&local_##x.dep_map) +# define I_LOCAL_LOCK(x) lockdep_reset_lock(this_cpu_ptr(&local_##x.dep_map)) #ifdef CONFIG_RT_MUTEXES # define I_RTMUTEX(x) lockdep_reset_lock(&rtmutex_##x.dep_map) #endif @@ -1380,7 +1411,7 @@ static void reset_locks(void) init_shared_classes(); raw_spin_lock_init(&raw_lock_A); raw_spin_lock_init(&raw_lock_B); - local_lock_init(&local_A); + local_lock_init(this_cpu_ptr(&local_A)); ww_mutex_init(&o, &ww_lockdep); ww_mutex_init(&o2, &ww_lockdep); ww_mutex_init(&o3, &ww_lockdep); memset(&t, 0, sizeof(t)); memset(&t2, 0, sizeof(t2)); @@ -1398,7 +1429,13 @@ static int unexpected_testcase_failures; static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) { - unsigned long saved_preempt_count = preempt_count(); + int saved_preempt_count = preempt_count(); +#ifdef CONFIG_PREEMPT_RT +#ifdef CONFIG_SMP + int saved_mgd_count = current->migration_disabled; +#endif + int saved_rcu_count = current->rcu_read_lock_nesting; +#endif WARN_ON(irqs_disabled()); @@ -1432,6 +1469,18 @@ static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) * count, so restore it: */ preempt_count_set(saved_preempt_count); + +#ifdef CONFIG_PREEMPT_RT +#ifdef CONFIG_SMP + while (current->migration_disabled > saved_mgd_count) + migrate_enable(); +#endif + + while (current->rcu_read_lock_nesting > saved_rcu_count) + rcu_read_unlock(); + WARN_ON_ONCE(current->rcu_read_lock_nesting < saved_rcu_count); +#endif + #ifdef CONFIG_TRACE_IRQFLAGS if (softirq_count()) current->softirqs_enabled = 0; @@ -1499,7 +1548,7 @@ static inline void print_testname(const char *testname) #define DO_TESTCASE_2x2RW(desc, name, nr) \ DO_TESTCASE_2RW("hard-"desc, name##_hard, nr) \ - DO_TESTCASE_2RW("soft-"desc, name##_soft, nr) \ + NON_RT(DO_TESTCASE_2RW("soft-"desc, name##_soft, nr)) \ #define DO_TESTCASE_6x2x2RW(desc, name) \ DO_TESTCASE_2x2RW(desc, name, 123); \ @@ -1547,19 +1596,19 @@ static inline void print_testname(const char *testname) #define DO_TESTCASE_2I(desc, name, nr) \ DO_TESTCASE_1("hard-"desc, name##_hard, nr); \ - DO_TESTCASE_1("soft-"desc, name##_soft, nr); + NON_RT(DO_TESTCASE_1("soft-"desc, name##_soft, nr)); #define DO_TESTCASE_2IB(desc, name, nr) \ DO_TESTCASE_1B("hard-"desc, name##_hard, nr); \ - DO_TESTCASE_1B("soft-"desc, name##_soft, nr); + NON_RT(DO_TESTCASE_1B("soft-"desc, name##_soft, nr)); #define DO_TESTCASE_6I(desc, name, nr) \ DO_TESTCASE_3("hard-"desc, name##_hard, nr); \ - DO_TESTCASE_3("soft-"desc, name##_soft, nr); + NON_RT(DO_TESTCASE_3("soft-"desc, name##_soft, nr)); #define DO_TESTCASE_6IRW(desc, name, nr) \ DO_TESTCASE_3RW("hard-"desc, name##_hard, nr); \ - DO_TESTCASE_3RW("soft-"desc, name##_soft, nr); + NON_RT(DO_TESTCASE_3RW("soft-"desc, name##_soft, nr)); #define DO_TESTCASE_2x3(desc, name) \ DO_TESTCASE_3(desc, name, 12); \ @@ -1651,6 +1700,22 @@ static void ww_test_fail_acquire(void) #endif } +#ifdef CONFIG_PREEMPT_RT +#define ww_mutex_base_lock(b) rt_mutex_lock(b) +#define ww_mutex_base_trylock(b) rt_mutex_trylock(b) +#define ww_mutex_base_lock_nest_lock(b, b2) rt_mutex_lock_nest_lock(b, b2) +#define ww_mutex_base_lock_interruptible(b) rt_mutex_lock_interruptible(b) +#define ww_mutex_base_lock_killable(b) rt_mutex_lock_killable(b) +#define ww_mutex_base_unlock(b) rt_mutex_unlock(b) +#else +#define ww_mutex_base_lock(b) mutex_lock(b) +#define ww_mutex_base_trylock(b) mutex_trylock(b) +#define ww_mutex_base_lock_nest_lock(b, b2) mutex_lock_nest_lock(b, b2) +#define ww_mutex_base_lock_interruptible(b) mutex_lock_interruptible(b) +#define ww_mutex_base_lock_killable(b) mutex_lock_killable(b) +#define ww_mutex_base_unlock(b) mutex_unlock(b) +#endif + static void ww_test_normal(void) { int ret; @@ -1665,50 +1730,50 @@ static void ww_test_normal(void) /* mutex_lock (and indirectly, mutex_lock_nested) */ o.ctx = (void *)~0UL; - mutex_lock(&o.base); - mutex_unlock(&o.base); + ww_mutex_base_lock(&o.base); + ww_mutex_base_unlock(&o.base); WARN_ON(o.ctx != (void *)~0UL); /* mutex_lock_interruptible (and *_nested) */ o.ctx = (void *)~0UL; - ret = mutex_lock_interruptible(&o.base); + ret = ww_mutex_base_lock_interruptible(&o.base); if (!ret) - mutex_unlock(&o.base); + ww_mutex_base_unlock(&o.base); else WARN_ON(1); WARN_ON(o.ctx != (void *)~0UL); /* mutex_lock_killable (and *_nested) */ o.ctx = (void *)~0UL; - ret = mutex_lock_killable(&o.base); + ret = ww_mutex_base_lock_killable(&o.base); if (!ret) - mutex_unlock(&o.base); + ww_mutex_base_unlock(&o.base); else WARN_ON(1); WARN_ON(o.ctx != (void *)~0UL); /* trylock, succeeding */ o.ctx = (void *)~0UL; - ret = mutex_trylock(&o.base); + ret = ww_mutex_base_trylock(&o.base); WARN_ON(!ret); if (ret) - mutex_unlock(&o.base); + ww_mutex_base_unlock(&o.base); else WARN_ON(1); WARN_ON(o.ctx != (void *)~0UL); /* trylock, failing */ o.ctx = (void *)~0UL; - mutex_lock(&o.base); - ret = mutex_trylock(&o.base); + ww_mutex_base_lock(&o.base); + ret = ww_mutex_base_trylock(&o.base); WARN_ON(ret); - mutex_unlock(&o.base); + ww_mutex_base_unlock(&o.base); WARN_ON(o.ctx != (void *)~0UL); /* nest_lock */ o.ctx = (void *)~0UL; - mutex_lock_nest_lock(&o.base, &t); - mutex_unlock(&o.base); + ww_mutex_base_lock_nest_lock(&o.base, &t); + ww_mutex_base_unlock(&o.base); WARN_ON(o.ctx != (void *)~0UL); } @@ -1721,7 +1786,7 @@ static void ww_test_two_contexts(void) static void ww_test_diff_class(void) { WWAI(&t); -#ifdef CONFIG_DEBUG_MUTEXES +#ifdef DEBUG_WW_MUTEXES t.ww_class = NULL; #endif WWL(&o, &t); @@ -1785,7 +1850,7 @@ static void ww_test_edeadlk_normal(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); o2.ctx = &t2; mutex_release(&o2.base.dep_map, _THIS_IP_); @@ -1801,7 +1866,7 @@ static void ww_test_edeadlk_normal(void) o2.ctx = NULL; mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); - mutex_unlock(&o2.base); + ww_mutex_base_unlock(&o2.base); WWU(&o); WWL(&o2, &t); @@ -1811,7 +1876,7 @@ static void ww_test_edeadlk_normal_slow(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -1827,7 +1892,7 @@ static void ww_test_edeadlk_normal_slow(void) o2.ctx = NULL; mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); - mutex_unlock(&o2.base); + ww_mutex_base_unlock(&o2.base); WWU(&o); ww_mutex_lock_slow(&o2, &t); @@ -1837,7 +1902,7 @@ static void ww_test_edeadlk_no_unlock(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); o2.ctx = &t2; mutex_release(&o2.base.dep_map, _THIS_IP_); @@ -1853,7 +1918,7 @@ static void ww_test_edeadlk_no_unlock(void) o2.ctx = NULL; mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); - mutex_unlock(&o2.base); + ww_mutex_base_unlock(&o2.base); WWL(&o2, &t); } @@ -1862,7 +1927,7 @@ static void ww_test_edeadlk_no_unlock_slow(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -1878,7 +1943,7 @@ static void ww_test_edeadlk_no_unlock_slow(void) o2.ctx = NULL; mutex_acquire(&o2.base.dep_map, 0, 1, _THIS_IP_); - mutex_unlock(&o2.base); + ww_mutex_base_unlock(&o2.base); ww_mutex_lock_slow(&o2, &t); } @@ -1887,7 +1952,7 @@ static void ww_test_edeadlk_acquire_more(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -1908,7 +1973,7 @@ static void ww_test_edeadlk_acquire_more_slow(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -1929,11 +1994,11 @@ static void ww_test_edeadlk_acquire_more_edeadlk(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; - mutex_lock(&o3.base); + ww_mutex_base_lock(&o3.base); mutex_release(&o3.base.dep_map, _THIS_IP_); o3.ctx = &t2; @@ -1955,11 +2020,11 @@ static void ww_test_edeadlk_acquire_more_edeadlk_slow(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; - mutex_lock(&o3.base); + ww_mutex_base_lock(&o3.base); mutex_release(&o3.base.dep_map, _THIS_IP_); o3.ctx = &t2; @@ -1980,7 +2045,7 @@ static void ww_test_edeadlk_acquire_wrong(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -2005,7 +2070,7 @@ static void ww_test_edeadlk_acquire_wrong_slow(void) { int ret; - mutex_lock(&o2.base); + ww_mutex_base_lock(&o2.base); mutex_release(&o2.base.dep_map, _THIS_IP_); o2.ctx = &t2; @@ -2646,8 +2711,8 @@ static void wait_context_tests(void) static void local_lock_2(void) { - local_lock_acquire(&local_A); /* IRQ-ON */ - local_lock_release(&local_A); + local_lock(&local_A); /* IRQ-ON */ + local_unlock(&local_A); HARDIRQ_ENTER(); spin_lock(&lock_A); /* IN-IRQ */ @@ -2656,18 +2721,18 @@ static void local_lock_2(void) HARDIRQ_DISABLE(); spin_lock(&lock_A); - local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle, false */ - local_lock_release(&local_A); + local_lock(&local_A); /* IN-IRQ <-> IRQ-ON cycle, false */ + local_unlock(&local_A); spin_unlock(&lock_A); HARDIRQ_ENABLE(); } static void local_lock_3A(void) { - local_lock_acquire(&local_A); /* IRQ-ON */ + local_lock(&local_A); /* IRQ-ON */ spin_lock(&lock_B); /* IRQ-ON */ spin_unlock(&lock_B); - local_lock_release(&local_A); + local_unlock(&local_A); HARDIRQ_ENTER(); spin_lock(&lock_A); /* IN-IRQ */ @@ -2676,18 +2741,18 @@ static void local_lock_3A(void) HARDIRQ_DISABLE(); spin_lock(&lock_A); - local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ - local_lock_release(&local_A); + local_lock(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ + local_unlock(&local_A); spin_unlock(&lock_A); HARDIRQ_ENABLE(); } static void local_lock_3B(void) { - local_lock_acquire(&local_A); /* IRQ-ON */ + local_lock(&local_A); /* IRQ-ON */ spin_lock(&lock_B); /* IRQ-ON */ spin_unlock(&lock_B); - local_lock_release(&local_A); + local_unlock(&local_A); HARDIRQ_ENTER(); spin_lock(&lock_A); /* IN-IRQ */ @@ -2696,8 +2761,8 @@ static void local_lock_3B(void) HARDIRQ_DISABLE(); spin_lock(&lock_A); - local_lock_acquire(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ - local_lock_release(&local_A); + local_lock(&local_A); /* IN-IRQ <-> IRQ-ON cycle only if we count local_lock(), false */ + local_unlock(&local_A); spin_unlock(&lock_A); HARDIRQ_ENABLE(); @@ -2812,7 +2877,7 @@ void locking_selftest(void) printk("------------------------\n"); printk("| Locking API testsuite:\n"); printk("----------------------------------------------------------------------------\n"); - printk(" | spin |wlock |rlock |mutex | wsem | rsem |\n"); + printk(" | spin |wlock |rlock |mutex | wsem | rsem |rtmutex\n"); printk(" --------------------------------------------------------------------------\n"); init_shared_classes(); @@ -2885,12 +2950,11 @@ void locking_selftest(void) DO_TESTCASE_6x1RR("rlock W1R2/R2R3/W3W1", W1R2_R2R3_W3W1); printk(" --------------------------------------------------------------------------\n"); - /* * irq-context testcases: */ DO_TESTCASE_2x6("irqs-on + irq-safe-A", irqsafe1); - DO_TESTCASE_2x3("sirq-safe-A => hirqs-on", irqsafe2A); + NON_RT(DO_TESTCASE_2x3("sirq-safe-A => hirqs-on", irqsafe2A)); DO_TESTCASE_2x6("safe-A + irqs-on", irqsafe2B); DO_TESTCASE_6x6("safe-A + unsafe-B #1", irqsafe3); DO_TESTCASE_6x6("safe-A + unsafe-B #2", irqsafe4); diff --git a/lib/logic_iomem.c b/lib/logic_iomem.c index 549b22d4bc..8c3365f26e 100644 --- a/lib/logic_iomem.c +++ b/lib/logic_iomem.c @@ -76,7 +76,7 @@ static void __iomem *real_ioremap(phys_addr_t offset, size_t size) return NULL; } -static void real_iounmap(void __iomem *addr) +static void real_iounmap(volatile void __iomem *addr) { WARN(1, "invalid iounmap for addr 0x%llx\n", (unsigned long long)(uintptr_t __force)addr); @@ -149,7 +149,7 @@ get_area(const volatile void __iomem *addr) return NULL; } -void iounmap(void __iomem *addr) +void iounmap(volatile void __iomem *addr) { struct logic_iomem_area *area = get_area(addr); diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h index 673bd206aa..330aa539b4 100644 --- a/lib/lz4/lz4defs.h +++ b/lib/lz4/lz4defs.h @@ -36,6 +36,8 @@ */ #include + +#include #include /* memset, memcpy */ #define FORCE_INLINE __always_inline diff --git a/lib/objagg.c b/lib/objagg.c index 5e1676ccda..1e248629ed 100644 --- a/lib/objagg.c +++ b/lib/objagg.c @@ -781,7 +781,6 @@ static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) struct objagg_tmp_node *node; struct objagg_tmp_node *pnode; struct objagg_obj *objagg_obj; - size_t alloc_size; int i, j; graph = kzalloc(sizeof(*graph), GFP_KERNEL); @@ -793,9 +792,7 @@ static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) goto err_nodes_alloc; graph->nodes_count = nodes_count; - alloc_size = BITS_TO_LONGS(nodes_count * nodes_count) * - sizeof(unsigned long); - graph->edges = kzalloc(alloc_size, GFP_KERNEL); + graph->edges = bitmap_zalloc(nodes_count * nodes_count, GFP_KERNEL); if (!graph->edges) goto err_edges_alloc; @@ -833,7 +830,7 @@ static struct objagg_tmp_graph *objagg_tmp_graph_create(struct objagg *objagg) static void objagg_tmp_graph_destroy(struct objagg_tmp_graph *graph) { - kfree(graph->edges); + bitmap_free(graph->edges); kfree(graph->nodes); kfree(graph); } diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 6d5e5000fd..39b74221f4 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -145,13 +145,13 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void) static inline const struct raid6_calls *raid6_choose_gen( void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) { - unsigned long perf, bestgenperf, bestxorperf, j0, j1; + unsigned long perf, bestgenperf, j0, j1; int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */ const struct raid6_calls *const *algo; const struct raid6_calls *best; - for (bestgenperf = 0, bestxorperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { - if (!best || (*algo)->prefer >= best->prefer) { + for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { + if (!best || (*algo)->priority >= best->priority) { if ((*algo)->valid && !(*algo)->valid()) continue; @@ -180,50 +180,48 @@ static inline const struct raid6_calls *raid6_choose_gen( pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name, (perf * HZ * (disks-2)) >> (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); - - if (!(*algo)->xor_syndrome) - continue; - - perf = 0; - - preempt_disable(); - j0 = jiffies; - while ((j1 = jiffies) == j0) - cpu_relax(); - while (time_before(jiffies, - j1 + (1<xor_syndrome(disks, start, stop, - PAGE_SIZE, *dptrs); - perf++; - } - preempt_enable(); - - if (best == *algo) - bestxorperf = perf; - - pr_info("raid6: %-8s xor() %5ld MB/s\n", (*algo)->name, - (perf * HZ * (disks-2)) >> - (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); } } - if (best) { - if (IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { - pr_info("raid6: using algorithm %s gen() %ld MB/s\n", - best->name, - (bestgenperf * HZ * (disks-2)) >> - (20 - PAGE_SHIFT+RAID6_TIME_JIFFIES_LG2)); - if (best->xor_syndrome) - pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n", - (bestxorperf * HZ * (disks-2)) >> - (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); - } else - pr_info("raid6: skip pq benchmark and using algorithm %s\n", - best->name); - raid6_call = *best; - } else - pr_err("raid6: Yikes! No algorithm found!\n"); + if (!best) { + pr_err("raid6: Yikes! No algorithm found!\n"); + goto out; + } + raid6_call = *best; + + if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { + pr_info("raid6: skipped pq benchmark and selected %s\n", + best->name); + goto out; + } + + pr_info("raid6: using algorithm %s gen() %ld MB/s\n", + best->name, + (bestgenperf * HZ * (disks - 2)) >> + (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); + + if (best->xor_syndrome) { + perf = 0; + + preempt_disable(); + j0 = jiffies; + while ((j1 = jiffies) == j0) + cpu_relax(); + while (time_before(jiffies, + j1 + (1 << RAID6_TIME_JIFFIES_LG2))) { + best->xor_syndrome(disks, start, stop, + PAGE_SIZE, *dptrs); + perf++; + } + preempt_enable(); + + pr_info("raid6: .... xor() %ld MB/s, rmw enabled\n", + (perf * HZ * (disks - 2)) >> + (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); + } + +out: return best; } diff --git a/lib/raid6/avx2.c b/lib/raid6/avx2.c index f299476e1d..059024234d 100644 --- a/lib/raid6/avx2.c +++ b/lib/raid6/avx2.c @@ -132,7 +132,7 @@ const struct raid6_calls raid6_avx2x1 = { raid6_avx21_xor_syndrome, raid6_have_avx2, "avx2x1", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ }; /* @@ -262,7 +262,7 @@ const struct raid6_calls raid6_avx2x2 = { raid6_avx22_xor_syndrome, raid6_have_avx2, "avx2x2", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ }; #ifdef CONFIG_X86_64 @@ -465,6 +465,6 @@ const struct raid6_calls raid6_avx2x4 = { raid6_avx24_xor_syndrome, raid6_have_avx2, "avx2x4", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ }; -#endif +#endif /* CONFIG_X86_64 */ diff --git a/lib/raid6/avx512.c b/lib/raid6/avx512.c index bb684d144e..9c3e822e1a 100644 --- a/lib/raid6/avx512.c +++ b/lib/raid6/avx512.c @@ -162,7 +162,7 @@ const struct raid6_calls raid6_avx512x1 = { raid6_avx5121_xor_syndrome, raid6_have_avx512, "avx512x1", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ }; /* @@ -319,7 +319,7 @@ const struct raid6_calls raid6_avx512x2 = { raid6_avx5122_xor_syndrome, raid6_have_avx512, "avx512x2", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ }; #ifdef CONFIG_X86_64 @@ -557,7 +557,7 @@ const struct raid6_calls raid6_avx512x4 = { raid6_avx5124_xor_syndrome, raid6_have_avx512, "avx512x4", - 1 /* Has cache hints */ + .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ }; #endif diff --git a/lib/random32.c b/lib/random32.c index 4d0e05e471..a57a0e1881 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/lib/sbitmap.c b/lib/sbitmap.c index b25db9be93..09d293c30f 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -457,10 +457,9 @@ int sbitmap_queue_init_node(struct sbitmap_queue *sbq, unsigned int depth, } EXPORT_SYMBOL_GPL(sbitmap_queue_init_node); -static void sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq, - unsigned int depth) +static inline void __sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq, + unsigned int wake_batch) { - unsigned int wake_batch = sbq_calc_wake_batch(sbq, depth); int i; if (sbq->wake_batch != wake_batch) { @@ -476,6 +475,30 @@ static void sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq, } } +static void sbitmap_queue_update_wake_batch(struct sbitmap_queue *sbq, + unsigned int depth) +{ + unsigned int wake_batch; + + wake_batch = sbq_calc_wake_batch(sbq, depth); + __sbitmap_queue_update_wake_batch(sbq, wake_batch); +} + +void sbitmap_queue_recalculate_wake_batch(struct sbitmap_queue *sbq, + unsigned int users) +{ + unsigned int wake_batch; + unsigned int min_batch; + unsigned int depth = (sbq->sb.depth + users - 1) / users; + + min_batch = sbq->sb.depth >= (4 * SBQ_WAIT_QUEUES) ? 4 : 1; + + wake_batch = clamp_val(depth / SBQ_WAIT_QUEUES, + min_batch, SBQ_WAKE_BATCH); + __sbitmap_queue_update_wake_batch(sbq, wake_batch); +} +EXPORT_SYMBOL_GPL(sbitmap_queue_recalculate_wake_batch); + void sbitmap_queue_resize(struct sbitmap_queue *sbq, unsigned int depth) { sbitmap_queue_update_wake_batch(sbq, depth); @@ -489,6 +512,57 @@ int __sbitmap_queue_get(struct sbitmap_queue *sbq) } EXPORT_SYMBOL_GPL(__sbitmap_queue_get); +unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags, + unsigned int *offset) +{ + struct sbitmap *sb = &sbq->sb; + unsigned int hint, depth; + unsigned long index, nr; + int i; + + if (unlikely(sb->round_robin)) + return 0; + + depth = READ_ONCE(sb->depth); + hint = update_alloc_hint_before_get(sb, depth); + + index = SB_NR_TO_INDEX(sb, hint); + + for (i = 0; i < sb->map_nr; i++) { + struct sbitmap_word *map = &sb->map[index]; + unsigned long get_mask; + + sbitmap_deferred_clear(map); + if (map->word == (1UL << (map->depth - 1)) - 1) + continue; + + nr = find_first_zero_bit(&map->word, map->depth); + if (nr + nr_tags <= map->depth) { + atomic_long_t *ptr = (atomic_long_t *) &map->word; + int map_tags = min_t(int, nr_tags, map->depth); + unsigned long val, ret; + + get_mask = ((1UL << map_tags) - 1) << nr; + do { + val = READ_ONCE(map->word); + ret = atomic_long_cmpxchg(ptr, val, get_mask | val); + } while (ret != val); + get_mask = (get_mask & ~ret) >> nr; + if (get_mask) { + *offset = nr + (index << sb->shift); + update_alloc_hint_after_get(sb, depth, hint, + *offset + map_tags - 1); + return get_mask; + } + } + /* Jump to next index. */ + if (++index >= sb->map_nr) + index = 0; + } + + return 0; +} + int __sbitmap_queue_get_shallow(struct sbitmap_queue *sbq, unsigned int shallow_depth) { @@ -577,6 +651,46 @@ void sbitmap_queue_wake_up(struct sbitmap_queue *sbq) } EXPORT_SYMBOL_GPL(sbitmap_queue_wake_up); +static inline void sbitmap_update_cpu_hint(struct sbitmap *sb, int cpu, int tag) +{ + if (likely(!sb->round_robin && tag < sb->depth)) + data_race(*per_cpu_ptr(sb->alloc_hint, cpu) = tag); +} + +void sbitmap_queue_clear_batch(struct sbitmap_queue *sbq, int offset, + int *tags, int nr_tags) +{ + struct sbitmap *sb = &sbq->sb; + unsigned long *addr = NULL; + unsigned long mask = 0; + int i; + + smp_mb__before_atomic(); + for (i = 0; i < nr_tags; i++) { + const int tag = tags[i] - offset; + unsigned long *this_addr; + + /* since we're clearing a batch, skip the deferred map */ + this_addr = &sb->map[SB_NR_TO_INDEX(sb, tag)].word; + if (!addr) { + addr = this_addr; + } else if (addr != this_addr) { + atomic_long_andnot(mask, (atomic_long_t *) addr); + mask = 0; + addr = this_addr; + } + mask |= (1UL << SB_NR_TO_BIT(sb, tag)); + } + + if (mask) + atomic_long_andnot(mask, (atomic_long_t *) addr); + + smp_mb__after_atomic(); + sbitmap_queue_wake_up(sbq); + sbitmap_update_cpu_hint(&sbq->sb, raw_smp_processor_id(), + tags[nr_tags - 1] - offset); +} + void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr, unsigned int cpu) { @@ -601,9 +715,7 @@ void sbitmap_queue_clear(struct sbitmap_queue *sbq, unsigned int nr, */ smp_mb__after_atomic(); sbitmap_queue_wake_up(sbq); - - if (likely(!sbq->sb.round_robin && nr < sbq->sb.depth)) - *per_cpu_ptr(sbq->sb.alloc_hint, cpu) = nr; + sbitmap_update_cpu_hint(&sbq->sb, cpu, nr); } EXPORT_SYMBOL_GPL(sbitmap_queue_clear); diff --git a/lib/scatterlist.c b/lib/scatterlist.c index abb3432ed7..d5e82e4a57 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -828,8 +828,7 @@ static bool sg_miter_get_next_page(struct sg_mapping_iter *miter) * stops @miter. * * Context: - * Don't care if @miter is stopped, or not proceeded yet. - * Otherwise, preemption disabled if the SG_MITER_ATOMIC is set. + * Don't care. * * Returns: * true if @miter contains the valid mapping. false if end of sg @@ -865,8 +864,7 @@ EXPORT_SYMBOL(sg_miter_skip); * @miter->addr and @miter->length point to the current mapping. * * Context: - * Preemption disabled if SG_MITER_ATOMIC. Preemption must stay disabled - * till @miter is stopped. May sleep if !SG_MITER_ATOMIC. + * May sleep if !SG_MITER_ATOMIC. * * Returns: * true if @miter contains the next mapping. false if end of sg @@ -906,8 +904,7 @@ EXPORT_SYMBOL(sg_miter_next); * need to be released during iteration. * * Context: - * Preemption disabled if the SG_MITER_ATOMIC is set. Don't care - * otherwise. + * Don't care otherwise. */ void sg_miter_stop(struct sg_mapping_iter *miter) { @@ -922,7 +919,7 @@ void sg_miter_stop(struct sg_mapping_iter *miter) flush_dcache_page(miter->page); if (miter->__flags & SG_MITER_ATOMIC) { - WARN_ON_ONCE(preemptible()); + WARN_ON_ONCE(!pagefault_disabled()); kunmap_atomic(miter->addr); } else kunmap(miter->page); diff --git a/lib/sha1.c b/lib/sha1.c index 9bd1935a14..0494766fc5 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,8 @@ #define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ __u32 TEMP = input(t); setW(t, TEMP); \ E += TEMP + rol32(A,5) + (fn) + (constant); \ - B = ror32(B, 2); } while (0) + B = ror32(B, 2); \ + TEMP = E; E = D; D = C; C = B; B = A; A = TEMP; } while (0) #define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) #define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) @@ -84,6 +86,7 @@ void sha1_transform(__u32 *digest, const char *data, __u32 *array) { __u32 A, B, C, D, E; + unsigned int i = 0; A = digest[0]; B = digest[1]; @@ -92,94 +95,24 @@ void sha1_transform(__u32 *digest, const char *data, __u32 *array) E = digest[4]; /* Round 1 - iterations 0-16 take their input from 'data' */ - T_0_15( 0, A, B, C, D, E); - T_0_15( 1, E, A, B, C, D); - T_0_15( 2, D, E, A, B, C); - T_0_15( 3, C, D, E, A, B); - T_0_15( 4, B, C, D, E, A); - T_0_15( 5, A, B, C, D, E); - T_0_15( 6, E, A, B, C, D); - T_0_15( 7, D, E, A, B, C); - T_0_15( 8, C, D, E, A, B); - T_0_15( 9, B, C, D, E, A); - T_0_15(10, A, B, C, D, E); - T_0_15(11, E, A, B, C, D); - T_0_15(12, D, E, A, B, C); - T_0_15(13, C, D, E, A, B); - T_0_15(14, B, C, D, E, A); - T_0_15(15, A, B, C, D, E); + for (; i < 16; ++i) + T_0_15(i, A, B, C, D, E); /* Round 1 - tail. Input from 512-bit mixing array */ - T_16_19(16, E, A, B, C, D); - T_16_19(17, D, E, A, B, C); - T_16_19(18, C, D, E, A, B); - T_16_19(19, B, C, D, E, A); + for (; i < 20; ++i) + T_16_19(i, A, B, C, D, E); /* Round 2 */ - T_20_39(20, A, B, C, D, E); - T_20_39(21, E, A, B, C, D); - T_20_39(22, D, E, A, B, C); - T_20_39(23, C, D, E, A, B); - T_20_39(24, B, C, D, E, A); - T_20_39(25, A, B, C, D, E); - T_20_39(26, E, A, B, C, D); - T_20_39(27, D, E, A, B, C); - T_20_39(28, C, D, E, A, B); - T_20_39(29, B, C, D, E, A); - T_20_39(30, A, B, C, D, E); - T_20_39(31, E, A, B, C, D); - T_20_39(32, D, E, A, B, C); - T_20_39(33, C, D, E, A, B); - T_20_39(34, B, C, D, E, A); - T_20_39(35, A, B, C, D, E); - T_20_39(36, E, A, B, C, D); - T_20_39(37, D, E, A, B, C); - T_20_39(38, C, D, E, A, B); - T_20_39(39, B, C, D, E, A); + for (; i < 40; ++i) + T_20_39(i, A, B, C, D, E); /* Round 3 */ - T_40_59(40, A, B, C, D, E); - T_40_59(41, E, A, B, C, D); - T_40_59(42, D, E, A, B, C); - T_40_59(43, C, D, E, A, B); - T_40_59(44, B, C, D, E, A); - T_40_59(45, A, B, C, D, E); - T_40_59(46, E, A, B, C, D); - T_40_59(47, D, E, A, B, C); - T_40_59(48, C, D, E, A, B); - T_40_59(49, B, C, D, E, A); - T_40_59(50, A, B, C, D, E); - T_40_59(51, E, A, B, C, D); - T_40_59(52, D, E, A, B, C); - T_40_59(53, C, D, E, A, B); - T_40_59(54, B, C, D, E, A); - T_40_59(55, A, B, C, D, E); - T_40_59(56, E, A, B, C, D); - T_40_59(57, D, E, A, B, C); - T_40_59(58, C, D, E, A, B); - T_40_59(59, B, C, D, E, A); + for (; i < 60; ++i) + T_40_59(i, A, B, C, D, E); /* Round 4 */ - T_60_79(60, A, B, C, D, E); - T_60_79(61, E, A, B, C, D); - T_60_79(62, D, E, A, B, C); - T_60_79(63, C, D, E, A, B); - T_60_79(64, B, C, D, E, A); - T_60_79(65, A, B, C, D, E); - T_60_79(66, E, A, B, C, D); - T_60_79(67, D, E, A, B, C); - T_60_79(68, C, D, E, A, B); - T_60_79(69, B, C, D, E, A); - T_60_79(70, A, B, C, D, E); - T_60_79(71, E, A, B, C, D); - T_60_79(72, D, E, A, B, C); - T_60_79(73, C, D, E, A, B); - T_60_79(74, B, C, D, E, A); - T_60_79(75, A, B, C, D, E); - T_60_79(76, E, A, B, C, D); - T_60_79(77, D, E, A, B, C); - T_60_79(78, C, D, E, A, B); - T_60_79(79, B, C, D, E, A); + for (; i < 80; ++i) + T_60_79(i, A, B, C, D, E); digest[0] += A; digest[1] += B; diff --git a/lib/stackdepot.c b/lib/stackdepot.c index 0a2e417f83..bf5ba9af05 100644 --- a/lib/stackdepot.c +++ b/lib/stackdepot.c @@ -20,10 +20,10 @@ */ #include -#include #include #include #include +#include #include #include #include @@ -102,8 +102,8 @@ static bool init_stack_slab(void **prealloc) } /* Allocation of a new stack in raw storage */ -static struct stack_record *depot_alloc_stack(unsigned long *entries, int size, - u32 hash, void **prealloc, gfp_t alloc_flags) +static struct stack_record * +depot_alloc_stack(unsigned long *entries, int size, u32 hash, void **prealloc) { struct stack_record *stack; size_t required_size = struct_size(stack, entries, size); @@ -162,18 +162,40 @@ static int __init is_stack_depot_disabled(char *str) } early_param("stack_depot_disable", is_stack_depot_disabled); -int __init stack_depot_init(void) +/* + * __ref because of memblock_alloc(), which will not be actually called after + * the __init code is gone, because at that point slab_is_available() is true + */ +__ref int stack_depot_init(void) { - if (!stack_depot_disable) { + static DEFINE_MUTEX(stack_depot_init_mutex); + + mutex_lock(&stack_depot_init_mutex); + if (!stack_depot_disable && !stack_table) { size_t size = (STACK_HASH_SIZE * sizeof(struct stack_record *)); int i; - stack_table = memblock_alloc(size, size); - for (i = 0; i < STACK_HASH_SIZE; i++) - stack_table[i] = NULL; + if (slab_is_available()) { + pr_info("Stack Depot allocating hash table with kvmalloc\n"); + stack_table = kvmalloc(size, GFP_KERNEL); + } else { + pr_info("Stack Depot allocating hash table with memblock_alloc\n"); + stack_table = memblock_alloc(size, SMP_CACHE_BYTES); + } + if (stack_table) { + for (i = 0; i < STACK_HASH_SIZE; i++) + stack_table[i] = NULL; + } else { + pr_err("Stack Depot hash table allocation failed, disabling\n"); + stack_depot_disable = true; + mutex_unlock(&stack_depot_init_mutex); + return -ENOMEM; + } } + mutex_unlock(&stack_depot_init_mutex); return 0; } +EXPORT_SYMBOL_GPL(stack_depot_init); /* Calculate hash for a stack */ static inline u32 hash_stack(unsigned long *entries, unsigned int size) @@ -214,6 +236,49 @@ static inline struct stack_record *find_stack(struct stack_record *bucket, return NULL; } +/** + * stack_depot_snprint - print stack entries from a depot into a buffer + * + * @handle: Stack depot handle which was returned from + * stack_depot_save(). + * @buf: Pointer to the print buffer + * + * @size: Size of the print buffer + * + * @spaces: Number of leading spaces to print + * + * Return: Number of bytes printed. + */ +int stack_depot_snprint(depot_stack_handle_t handle, char *buf, size_t size, + int spaces) +{ + unsigned long *entries; + unsigned int nr_entries; + + nr_entries = stack_depot_fetch(handle, &entries); + return nr_entries ? stack_trace_snprint(buf, size, entries, nr_entries, + spaces) : 0; +} +EXPORT_SYMBOL_GPL(stack_depot_snprint); + +/** + * stack_depot_print - print stack entries from a depot + * + * @stack: Stack depot handle which was returned from + * stack_depot_save(). + * + */ +void stack_depot_print(depot_stack_handle_t stack) +{ + unsigned long *entries; + unsigned int nr_entries; + + nr_entries = stack_depot_fetch(stack, &entries); + if (nr_entries > 0) + stack_trace_print(entries, nr_entries, 0); +} +EXPORT_SYMBOL_GPL(stack_depot_print); + /** * stack_depot_fetch - Fetch stack entries from a depot * @@ -232,6 +297,9 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle, struct stack_record *stack; *entries = NULL; + if (!handle) + return 0; + if (parts.slabindex > depot_index) { WARN(1, "slab index %d out of bounds (%d) for stack id %08x\n", parts.slabindex, depot_index, handle); @@ -248,17 +316,31 @@ unsigned int stack_depot_fetch(depot_stack_handle_t handle, EXPORT_SYMBOL_GPL(stack_depot_fetch); /** - * stack_depot_save - Save a stack trace from an array + * __stack_depot_save - Save a stack trace from an array * * @entries: Pointer to storage array * @nr_entries: Size of the storage array * @alloc_flags: Allocation gfp flags + * @can_alloc: Allocate stack slabs (increased chance of failure if false) * - * Return: The handle of the stack struct stored in depot + * Saves a stack trace from @entries array of size @nr_entries. If @can_alloc is + * %true, is allowed to replenish the stack slab pool in case no space is left + * (allocates using GFP flags of @alloc_flags). If @can_alloc is %false, avoids + * any allocations and will fail if no space is left to store the stack trace. + * + * If the stack trace in @entries is from an interrupt, only the portion up to + * interrupt entry is saved. + * + * Context: Any context, but setting @can_alloc to %false is required if + * alloc_pages() cannot be used from the current context. Currently + * this is the case from contexts where neither %GFP_ATOMIC nor + * %GFP_NOWAIT can be used (NMI, raw_spin_lock). + * + * Return: The handle of the stack struct stored in depot, 0 on failure. */ -depot_stack_handle_t stack_depot_save(unsigned long *entries, - unsigned int nr_entries, - gfp_t alloc_flags) +depot_stack_handle_t __stack_depot_save(unsigned long *entries, + unsigned int nr_entries, + gfp_t alloc_flags, bool can_alloc) { struct stack_record *found = NULL, **bucket; depot_stack_handle_t retval = 0; @@ -267,6 +349,16 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries, unsigned long flags; u32 hash; + /* + * If this stack trace is from an interrupt, including anything before + * interrupt entry usually leads to unbounded stackdepot growth. + * + * Because use of filter_irq_stacks() is a requirement to ensure + * stackdepot can efficiently deduplicate interrupt stacks, always + * filter_irq_stacks() to simplify all callers' use of stackdepot. + */ + nr_entries = filter_irq_stacks(entries, nr_entries); + if (unlikely(nr_entries == 0) || stack_depot_disable) goto fast_exit; @@ -291,7 +383,7 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries, * The smp_load_acquire() here pairs with smp_store_release() to * |next_slab_inited| in depot_alloc_stack() and init_stack_slab(). */ - if (unlikely(!smp_load_acquire(&next_slab_inited))) { + if (unlikely(can_alloc && !smp_load_acquire(&next_slab_inited))) { /* * Zero out zone modifiers, as we don't have specific zone * requirements. Keep the flags related to allocation in atomic @@ -309,9 +401,8 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries, found = find_stack(*bucket, entries, nr_entries, hash); if (!found) { - struct stack_record *new = - depot_alloc_stack(entries, nr_entries, - hash, &prealloc, alloc_flags); + struct stack_record *new = depot_alloc_stack(entries, nr_entries, hash, &prealloc); + if (new) { new->next = *bucket; /* @@ -340,27 +431,24 @@ depot_stack_handle_t stack_depot_save(unsigned long *entries, fast_exit: return retval; } +EXPORT_SYMBOL_GPL(__stack_depot_save); + +/** + * stack_depot_save - Save a stack trace from an array + * + * @entries: Pointer to storage array + * @nr_entries: Size of the storage array + * @alloc_flags: Allocation gfp flags + * + * Context: Contexts where allocations via alloc_pages() are allowed. + * See __stack_depot_save() for more details. + * + * Return: The handle of the stack struct stored in depot, 0 on failure. + */ +depot_stack_handle_t stack_depot_save(unsigned long *entries, + unsigned int nr_entries, + gfp_t alloc_flags) +{ + return __stack_depot_save(entries, nr_entries, alloc_flags, true); +} EXPORT_SYMBOL_GPL(stack_depot_save); - -static inline int in_irqentry_text(unsigned long ptr) -{ - return (ptr >= (unsigned long)&__irqentry_text_start && - ptr < (unsigned long)&__irqentry_text_end) || - (ptr >= (unsigned long)&__softirqentry_text_start && - ptr < (unsigned long)&__softirqentry_text_end); -} - -unsigned int filter_irq_stacks(unsigned long *entries, - unsigned int nr_entries) -{ - unsigned int i; - - for (i = 0; i < nr_entries; i++) { - if (in_irqentry_text(entries[i])) { - /* Include the irqentry function into the stack. */ - return i + 1; - } - } - return nr_entries; -} -EXPORT_SYMBOL_GPL(filter_irq_stacks); diff --git a/lib/string.c b/lib/string.c index b2de45a581..485777c9da 100644 --- a/lib/string.c +++ b/lib/string.c @@ -6,20 +6,15 @@ */ /* - * stupid library routines.. The optimized versions should generally be found - * as inline code in + * This file should be used only for "library" routines that may have + * alternative implementations on specific architectures (generally + * found in ), or get overloaded by FORTIFY_SOURCE. + * (Specifically, this file is built with __NO_FORTIFY.) * - * These are buggy as well.. - * - * * Fri Jun 25 1999, Ingo Oeser - * - Added strsep() which will replace strtok() soon (because strsep() is - * reentrant and should be faster). Use only strsep() in new code, please. - * - * * Sat Feb 09 2002, Jason Thomas , - * Matthew Hawkins - * - Kissed strtok() goodbye + * Other helper functions should live in string_helpers.c. */ +#define __NO_FORTIFY #include #include #include @@ -238,40 +233,6 @@ ssize_t strscpy(char *dest, const char *src, size_t count) EXPORT_SYMBOL(strscpy); #endif -/** - * strscpy_pad() - Copy a C-string into a sized buffer - * @dest: Where to copy the string to - * @src: Where to copy the string from - * @count: Size of destination buffer - * - * Copy the string, or as much of it as fits, into the dest buffer. The - * behavior is undefined if the string buffers overlap. The destination - * buffer is always %NUL terminated, unless it's zero-sized. - * - * If the source string is shorter than the destination buffer, zeros - * the tail of the destination buffer. - * - * For full explanation of why you may want to consider using the - * 'strscpy' functions please see the function docstring for strscpy(). - * - * Returns: - * * The number of characters copied (not including the trailing %NUL) - * * -E2BIG if count is 0 or @src was truncated. - */ -ssize_t strscpy_pad(char *dest, const char *src, size_t count) -{ - ssize_t written; - - written = strscpy(dest, src, count); - if (written < 0 || written == count - 1) - return written; - - memset(dest + written + 1, 0, count - written - 1); - - return written; -} -EXPORT_SYMBOL(strscpy_pad); - /** * stpcpy - copy a string from src to dest returning a pointer to the new end * of dest, including src's %NUL-terminator. May overrun dest. @@ -514,46 +475,6 @@ char *strnchr(const char *s, size_t count, int c) EXPORT_SYMBOL(strnchr); #endif -/** - * skip_spaces - Removes leading whitespace from @str. - * @str: The string to be stripped. - * - * Returns a pointer to the first non-whitespace character in @str. - */ -char *skip_spaces(const char *str) -{ - while (isspace(*str)) - ++str; - return (char *)str; -} -EXPORT_SYMBOL(skip_spaces); - -/** - * strim - Removes leading and trailing whitespace from @s. - * @s: The string to be stripped. - * - * Note that the first trailing whitespace is replaced with a %NUL-terminator - * in the given string @s. Returns a pointer to the first non-whitespace - * character in @s. - */ -char *strim(char *s) -{ - size_t size; - char *end; - - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - return skip_spaces(s); -} -EXPORT_SYMBOL(strim); - #ifndef __HAVE_ARCH_STRLEN /** * strlen - Find the length of a string @@ -688,101 +609,6 @@ char *strsep(char **s, const char *ct) EXPORT_SYMBOL(strsep); #endif -/** - * sysfs_streq - return true if strings are equal, modulo trailing newline - * @s1: one string - * @s2: another string - * - * This routine returns true iff two strings are equal, treating both - * NUL and newline-then-NUL as equivalent string terminations. It's - * geared for use with sysfs input strings, which generally terminate - * with newlines but are compared against values without newlines. - */ -bool sysfs_streq(const char *s1, const char *s2) -{ - while (*s1 && *s1 == *s2) { - s1++; - s2++; - } - - if (*s1 == *s2) - return true; - if (!*s1 && *s2 == '\n' && !s2[1]) - return true; - if (*s1 == '\n' && !s1[1] && !*s2) - return true; - return false; -} -EXPORT_SYMBOL(sysfs_streq); - -/** - * match_string - matches given string in an array - * @array: array of strings - * @n: number of strings in the array or -1 for NULL terminated arrays - * @string: string to match with - * - * This routine will look for a string in an array of strings up to the - * n-th element in the array or until the first NULL element. - * - * Historically the value of -1 for @n, was used to search in arrays that - * are NULL terminated. However, the function does not make a distinction - * when finishing the search: either @n elements have been compared OR - * the first NULL element was found. - * - * Return: - * index of a @string in the @array if matches, or %-EINVAL otherwise. - */ -int match_string(const char * const *array, size_t n, const char *string) -{ - int index; - const char *item; - - for (index = 0; index < n; index++) { - item = array[index]; - if (!item) - break; - if (!strcmp(item, string)) - return index; - } - - return -EINVAL; -} -EXPORT_SYMBOL(match_string); - -/** - * __sysfs_match_string - matches given string in an array - * @array: array of strings - * @n: number of strings in the array or -1 for NULL terminated arrays - * @str: string to match with - * - * Returns index of @str in the @array or -EINVAL, just like match_string(). - * Uses sysfs_streq instead of strcmp for matching. - * - * This routine will look for a string in an array of strings up to the - * n-th element in the array or until the first NULL element. - * - * Historically the value of -1 for @n, was used to search in arrays that - * are NULL terminated. However, the function does not make a distinction - * when finishing the search: either @n elements have been compared OR - * the first NULL element was found. - */ -int __sysfs_match_string(const char * const *array, size_t n, const char *str) -{ - const char *item; - int index; - - for (index = 0; index < n; index++) { - item = array[index]; - if (!item) - break; - if (sysfs_streq(item, str)) - return index; - } - - return -EINVAL; -} -EXPORT_SYMBOL(__sysfs_match_string); - #ifndef __HAVE_ARCH_MEMSET /** * memset - Fill a region of memory with the given value @@ -1141,27 +967,3 @@ void *memchr_inv(const void *start, int c, size_t bytes) return check_bytes8(start, value, bytes % 8); } EXPORT_SYMBOL(memchr_inv); - -/** - * strreplace - Replace all occurrences of character in string. - * @s: The string to operate on. - * @old: The character being replaced. - * @new: The character @old is replaced with. - * - * Returns pointer to the nul byte at the end of @s. - */ -char *strreplace(char *s, char old, char new) -{ - for (; *s; ++s) - if (*s == old) - *s = new; - return s; -} -EXPORT_SYMBOL(strreplace); - -void fortify_panic(const char *name) -{ - pr_emerg("detected buffer overflow in %s\n", name); - BUG(); -} -EXPORT_SYMBOL(fortify_panic); diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 2ddc10bd9a..90f9f1b7af 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -674,6 +675,39 @@ char *kstrdup_quotable_file(struct file *file, gfp_t gfp) } EXPORT_SYMBOL_GPL(kstrdup_quotable_file); +/** + * kasprintf_strarray - allocate and fill array of sequential strings + * @gfp: flags for the slab allocator + * @prefix: prefix to be used + * @n: amount of lines to be allocated and filled + * + * Allocates and fills @n strings using pattern "%s-%zu", where prefix + * is provided by caller. The caller is responsible to free them with + * kfree_strarray() after use. + * + * Returns array of strings or NULL when memory can't be allocated. + */ +char **kasprintf_strarray(gfp_t gfp, const char *prefix, size_t n) +{ + char **names; + size_t i; + + names = kcalloc(n + 1, sizeof(char *), gfp); + if (!names) + return NULL; + + for (i = 0; i < n; i++) { + names[i] = kasprintf(gfp, "%s-%zu", prefix, i); + if (!names[i]) { + kfree_strarray(names, i); + return NULL; + } + } + + return names; +} +EXPORT_SYMBOL_GPL(kasprintf_strarray); + /** * kfree_strarray - free a number of dynamically allocated strings contained * in an array and the array itself @@ -697,6 +731,222 @@ void kfree_strarray(char **array, size_t n) } EXPORT_SYMBOL_GPL(kfree_strarray); +struct strarray { + char **array; + size_t n; +}; + +static void devm_kfree_strarray(struct device *dev, void *res) +{ + struct strarray *array = res; + + kfree_strarray(array->array, array->n); +} + +char **devm_kasprintf_strarray(struct device *dev, const char *prefix, size_t n) +{ + struct strarray *ptr; + + ptr = devres_alloc(devm_kfree_strarray, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + ptr->array = kasprintf_strarray(GFP_KERNEL, prefix, n); + if (!ptr->array) { + devres_free(ptr); + return ERR_PTR(-ENOMEM); + } + + return ptr->array; +} +EXPORT_SYMBOL_GPL(devm_kasprintf_strarray); + +/** + * strscpy_pad() - Copy a C-string into a sized buffer + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @count: Size of destination buffer + * + * Copy the string, or as much of it as fits, into the dest buffer. The + * behavior is undefined if the string buffers overlap. The destination + * buffer is always %NUL terminated, unless it's zero-sized. + * + * If the source string is shorter than the destination buffer, zeros + * the tail of the destination buffer. + * + * For full explanation of why you may want to consider using the + * 'strscpy' functions please see the function docstring for strscpy(). + * + * Returns: + * * The number of characters copied (not including the trailing %NUL) + * * -E2BIG if count is 0 or @src was truncated. + */ +ssize_t strscpy_pad(char *dest, const char *src, size_t count) +{ + ssize_t written; + + written = strscpy(dest, src, count); + if (written < 0 || written == count - 1) + return written; + + memset(dest + written + 1, 0, count - written - 1); + + return written; +} +EXPORT_SYMBOL(strscpy_pad); + +/** + * skip_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + */ +char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} +EXPORT_SYMBOL(skip_spaces); + +/** + * strim - Removes leading and trailing whitespace from @s. + * @s: The string to be stripped. + * + * Note that the first trailing whitespace is replaced with a %NUL-terminator + * in the given string @s. Returns a pointer to the first non-whitespace + * character in @s. + */ +char *strim(char *s) +{ + size_t size; + char *end; + + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + return skip_spaces(s); +} +EXPORT_SYMBOL(strim); + +/** + * sysfs_streq - return true if strings are equal, modulo trailing newline + * @s1: one string + * @s2: another string + * + * This routine returns true iff two strings are equal, treating both + * NUL and newline-then-NUL as equivalent string terminations. It's + * geared for use with sysfs input strings, which generally terminate + * with newlines but are compared against values without newlines. + */ +bool sysfs_streq(const char *s1, const char *s2) +{ + while (*s1 && *s1 == *s2) { + s1++; + s2++; + } + + if (*s1 == *s2) + return true; + if (!*s1 && *s2 == '\n' && !s2[1]) + return true; + if (*s1 == '\n' && !s1[1] && !*s2) + return true; + return false; +} +EXPORT_SYMBOL(sysfs_streq); + +/** + * match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @string: string to match with + * + * This routine will look for a string in an array of strings up to the + * n-th element in the array or until the first NULL element. + * + * Historically the value of -1 for @n, was used to search in arrays that + * are NULL terminated. However, the function does not make a distinction + * when finishing the search: either @n elements have been compared OR + * the first NULL element was found. + * + * Return: + * index of a @string in the @array if matches, or %-EINVAL otherwise. + */ +int match_string(const char * const *array, size_t n, const char *string) +{ + int index; + const char *item; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (!strcmp(item, string)) + return index; + } + + return -EINVAL; +} +EXPORT_SYMBOL(match_string); + +/** + * __sysfs_match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @str: string to match with + * + * Returns index of @str in the @array or -EINVAL, just like match_string(). + * Uses sysfs_streq instead of strcmp for matching. + * + * This routine will look for a string in an array of strings up to the + * n-th element in the array or until the first NULL element. + * + * Historically the value of -1 for @n, was used to search in arrays that + * are NULL terminated. However, the function does not make a distinction + * when finishing the search: either @n elements have been compared OR + * the first NULL element was found. + */ +int __sysfs_match_string(const char * const *array, size_t n, const char *str) +{ + const char *item; + int index; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (sysfs_streq(item, str)) + return index; + } + + return -EINVAL; +} +EXPORT_SYMBOL(__sysfs_match_string); + +/** + * strreplace - Replace all occurrences of character in string. + * @s: The string to operate on. + * @old: The character being replaced. + * @new: The character @old is replaced with. + * + * Returns pointer to the nul byte at the end of @s. + */ +char *strreplace(char *s, char old, char new) +{ + for (; *s; ++s) + if (*s == old) + *s = new; + return s; +} +EXPORT_SYMBOL(strreplace); + /** * memcpy_and_pad - Copy one buffer to another with padding * @dest: Where to copy to @@ -716,3 +966,12 @@ void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count, } } EXPORT_SYMBOL(memcpy_and_pad); + +#ifdef CONFIG_FORTIFY_SOURCE +void fortify_panic(const char *name) +{ + pr_emerg("detected buffer overflow in %s\n", name); + BUG(); +} +EXPORT_SYMBOL(fortify_panic); +#endif /* CONFIG_FORTIFY_SOURCE */ diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index d33fa5a61b..0c82f07f74 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -446,6 +446,42 @@ static void __init test_bitmap_parselist(void) } } +static void __init test_bitmap_printlist(void) +{ + unsigned long *bmap = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char expected[256]; + int ret, slen; + ktime_t time; + + if (!buf || !bmap) + goto out; + + memset(bmap, -1, PAGE_SIZE); + slen = snprintf(expected, 256, "0-%ld", PAGE_SIZE * 8 - 1); + if (slen < 0) + goto out; + + time = ktime_get(); + ret = bitmap_print_to_pagebuf(true, buf, bmap, PAGE_SIZE * 8); + time = ktime_get() - time; + + if (ret != slen + 1) { + pr_err("bitmap_print_to_pagebuf: result is %d, expected %d\n", ret, slen); + goto out; + } + + if (strncmp(buf, expected, slen)) { + pr_err("bitmap_print_to_pagebuf: result is %s, expected %s\n", buf, expected); + goto out; + } + + pr_err("bitmap_print_to_pagebuf: input is '%s', Time: %llu\n", buf, time); +out: + kfree(buf); + kfree(bmap); +} + static const unsigned long parse_test[] __initconst = { BITMAP_FROM_U64(0), BITMAP_FROM_U64(1), @@ -818,6 +854,7 @@ static void __init selftest(void) test_bitmap_arr32(); test_bitmap_parse(); test_bitmap_parselist(); + test_bitmap_printlist(); test_mem_optimisations(); test_for_each_set_clump8(); test_bitmap_cut(); diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 68d125b409..0c5cb2d643 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -52,6 +52,7 @@ #define FLAG_NO_DATA BIT(0) #define FLAG_EXPECTED_FAIL BIT(1) #define FLAG_SKB_FRAG BIT(2) +#define FLAG_VERIFIER_ZEXT BIT(3) enum { CLASSIC = BIT(6), /* Old BPF instructions only. */ @@ -80,6 +81,7 @@ struct bpf_test { int expected_errcode; /* used when FLAG_EXPECTED_FAIL is set in the aux */ __u8 frag_data[MAX_DATA]; int stack_depth; /* for eBPF only, since tests don't call verifier */ + int nr_testruns; /* Custom run count, defaults to MAX_TESTRUNS if 0 */ }; /* Large test cases need separate allocation and fill handler. */ @@ -461,41 +463,2602 @@ static int bpf_fill_stxdw(struct bpf_test *self) return __bpf_fill_stxdw(self, BPF_DW); } -static int bpf_fill_long_jmp(struct bpf_test *self) +static int __bpf_ld_imm64(struct bpf_insn insns[2], u8 reg, s64 imm64) { - unsigned int len = BPF_MAXINSNS; - struct bpf_insn *insn; + struct bpf_insn tmp[] = {BPF_LD_IMM64(reg, imm64)}; + + memcpy(insns, tmp, sizeof(tmp)); + return 2; +} + +/* + * Branch conversion tests. Complex operations can expand to a lot + * of instructions when JITed. This in turn may cause jump offsets + * to overflow the field size of the native instruction, triggering + * a branch conversion mechanism in some JITs. + */ +static int __bpf_fill_max_jmp(struct bpf_test *self, int jmp, int imm) +{ + struct bpf_insn *insns; + int len = S16_MAX + 5; int i; + insns = kmalloc_array(len, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; + + i = __bpf_ld_imm64(insns, R1, 0x0123456789abcdefULL); + insns[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insns[i++] = BPF_JMP_IMM(jmp, R0, imm, S16_MAX); + insns[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 2); + insns[i++] = BPF_EXIT_INSN(); + + while (i < len - 1) { + static const int ops[] = { + BPF_LSH, BPF_RSH, BPF_ARSH, BPF_ADD, + BPF_SUB, BPF_MUL, BPF_DIV, BPF_MOD, + }; + int op = ops[(i >> 1) % ARRAY_SIZE(ops)]; + + if (i & 1) + insns[i++] = BPF_ALU32_REG(op, R0, R1); + else + insns[i++] = BPF_ALU64_REG(op, R0, R1); + } + + insns[i++] = BPF_EXIT_INSN(); + self->u.ptr.insns = insns; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +/* Branch taken by runtime decision */ +static int bpf_fill_max_jmp_taken(struct bpf_test *self) +{ + return __bpf_fill_max_jmp(self, BPF_JEQ, 1); +} + +/* Branch not taken by runtime decision */ +static int bpf_fill_max_jmp_not_taken(struct bpf_test *self) +{ + return __bpf_fill_max_jmp(self, BPF_JEQ, 0); +} + +/* Branch always taken, known at JIT time */ +static int bpf_fill_max_jmp_always_taken(struct bpf_test *self) +{ + return __bpf_fill_max_jmp(self, BPF_JGE, 0); +} + +/* Branch never taken, known at JIT time */ +static int bpf_fill_max_jmp_never_taken(struct bpf_test *self) +{ + return __bpf_fill_max_jmp(self, BPF_JLT, 0); +} + +/* ALU result computation used in tests */ +static bool __bpf_alu_result(u64 *res, u64 v1, u64 v2, u8 op) +{ + *res = 0; + switch (op) { + case BPF_MOV: + *res = v2; + break; + case BPF_AND: + *res = v1 & v2; + break; + case BPF_OR: + *res = v1 | v2; + break; + case BPF_XOR: + *res = v1 ^ v2; + break; + case BPF_LSH: + *res = v1 << v2; + break; + case BPF_RSH: + *res = v1 >> v2; + break; + case BPF_ARSH: + *res = v1 >> v2; + if (v2 > 0 && v1 > S64_MAX) + *res |= ~0ULL << (64 - v2); + break; + case BPF_ADD: + *res = v1 + v2; + break; + case BPF_SUB: + *res = v1 - v2; + break; + case BPF_MUL: + *res = v1 * v2; + break; + case BPF_DIV: + if (v2 == 0) + return false; + *res = div64_u64(v1, v2); + break; + case BPF_MOD: + if (v2 == 0) + return false; + div64_u64_rem(v1, v2, res); + break; + } + return true; +} + +/* Test an ALU shift operation for all valid shift values */ +static int __bpf_fill_alu_shift(struct bpf_test *self, u8 op, + u8 mode, bool alu32) +{ + static const s64 regs[] = { + 0x0123456789abcdefLL, /* dword > 0, word < 0 */ + 0xfedcba9876543210LL, /* dowrd < 0, word > 0 */ + 0xfedcba0198765432LL, /* dowrd < 0, word < 0 */ + 0x0123458967abcdefLL, /* dword > 0, word > 0 */ + }; + int bits = alu32 ? 32 : 64; + int len = (2 + 7 * bits) * ARRAY_SIZE(regs) + 3; + struct bpf_insn *insn; + int imm, k; + int i = 0; + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); if (!insn) return -ENOMEM; - insn[0] = BPF_ALU64_IMM(BPF_MOV, R0, 1); - insn[1] = BPF_JMP_IMM(BPF_JEQ, R0, 1, len - 2 - 1); + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 0); - /* - * Fill with a complex 64-bit operation that expands to a lot of - * instructions on 32-bit JITs. The large jump offset can then - * overflow the conditional branch field size, triggering a branch - * conversion mechanism in some JITs. - * - * Note: BPF_MAXINSNS of ALU64 MUL is enough to trigger such branch - * conversion on the 32-bit MIPS JIT. For other JITs, the instruction - * count and/or operation may need to be modified to trigger the - * branch conversion. - */ - for (i = 2; i < len - 1; i++) - insn[i] = BPF_ALU64_IMM(BPF_MUL, R0, (i << 16) + i); + for (k = 0; k < ARRAY_SIZE(regs); k++) { + s64 reg = regs[k]; - insn[len - 1] = BPF_EXIT_INSN(); + i += __bpf_ld_imm64(&insn[i], R3, reg); + + for (imm = 0; imm < bits; imm++) { + u64 val; + + /* Perform operation */ + insn[i++] = BPF_ALU64_REG(BPF_MOV, R1, R3); + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R2, imm); + if (alu32) { + if (mode == BPF_K) + insn[i++] = BPF_ALU32_IMM(op, R1, imm); + else + insn[i++] = BPF_ALU32_REG(op, R1, R2); + + if (op == BPF_ARSH) + reg = (s32)reg; + else + reg = (u32)reg; + __bpf_alu_result(&val, reg, imm, op); + val = (u32)val; + } else { + if (mode == BPF_K) + insn[i++] = BPF_ALU64_IMM(op, R1, imm); + else + insn[i++] = BPF_ALU64_REG(op, R1, R2); + __bpf_alu_result(&val, reg, imm, op); + } + + /* + * When debugging a JIT that fails this test, one + * can write the immediate value to R0 here to find + * out which operand values that fail. + */ + + /* Load reference and check the result */ + i += __bpf_ld_imm64(&insn[i], R4, val); + insn[i++] = BPF_JMP_REG(BPF_JEQ, R1, R4, 1); + insn[i++] = BPF_EXIT_INSN(); + } + } + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insn[i++] = BPF_EXIT_INSN(); self->u.ptr.insns = insn; self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +static int bpf_fill_alu64_lsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, false); +} + +static int bpf_fill_alu64_rsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, false); +} + +static int bpf_fill_alu64_arsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, false); +} + +static int bpf_fill_alu64_lsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, false); +} + +static int bpf_fill_alu64_rsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, false); +} + +static int bpf_fill_alu64_arsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, false); +} + +static int bpf_fill_alu32_lsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_LSH, BPF_K, true); +} + +static int bpf_fill_alu32_rsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_RSH, BPF_K, true); +} + +static int bpf_fill_alu32_arsh_imm(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_K, true); +} + +static int bpf_fill_alu32_lsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_LSH, BPF_X, true); +} + +static int bpf_fill_alu32_rsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_RSH, BPF_X, true); +} + +static int bpf_fill_alu32_arsh_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift(self, BPF_ARSH, BPF_X, true); +} + +/* + * Test an ALU register shift operation for all valid shift values + * for the case when the source and destination are the same. + */ +static int __bpf_fill_alu_shift_same_reg(struct bpf_test *self, u8 op, + bool alu32) +{ + int bits = alu32 ? 32 : 64; + int len = 3 + 6 * bits; + struct bpf_insn *insn; + int i = 0; + u64 val; + + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); + if (!insn) + return -ENOMEM; + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 0); + + for (val = 0; val < bits; val++) { + u64 res; + + /* Perform operation */ + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R1, val); + if (alu32) + insn[i++] = BPF_ALU32_REG(op, R1, R1); + else + insn[i++] = BPF_ALU64_REG(op, R1, R1); + + /* Compute the reference result */ + __bpf_alu_result(&res, val, val, op); + if (alu32) + res = (u32)res; + i += __bpf_ld_imm64(&insn[i], R2, res); + + /* Check the actual result */ + insn[i++] = BPF_JMP_REG(BPF_JEQ, R1, R2, 1); + insn[i++] = BPF_EXIT_INSN(); + } + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insn[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insn; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +static int bpf_fill_alu64_lsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, false); +} + +static int bpf_fill_alu64_rsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, false); +} + +static int bpf_fill_alu64_arsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, false); +} + +static int bpf_fill_alu32_lsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_LSH, true); +} + +static int bpf_fill_alu32_rsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_RSH, true); +} + +static int bpf_fill_alu32_arsh_same_reg(struct bpf_test *self) +{ + return __bpf_fill_alu_shift_same_reg(self, BPF_ARSH, true); +} + +/* + * Common operand pattern generator for exhaustive power-of-two magnitudes + * tests. The block size parameters can be adjusted to increase/reduce the + * number of combinatons tested and thereby execution speed and memory + * footprint. + */ + +static inline s64 value(int msb, int delta, int sign) +{ + return sign * (1LL << msb) + delta; +} + +static int __bpf_fill_pattern(struct bpf_test *self, void *arg, + int dbits, int sbits, int block1, int block2, + int (*emit)(struct bpf_test*, void*, + struct bpf_insn*, s64, s64)) +{ + static const int sgn[][2] = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; + struct bpf_insn *insns; + int di, si, bt, db, sb; + int count, len, k; + int extra = 1 + 2; + int i = 0; + + /* Total number of iterations for the two pattern */ + count = (dbits - 1) * (sbits - 1) * block1 * block1 * ARRAY_SIZE(sgn); + count += (max(dbits, sbits) - 1) * block2 * block2 * ARRAY_SIZE(sgn); + + /* Compute the maximum number of insns and allocate the buffer */ + len = extra + count * (*emit)(self, arg, NULL, 0, 0); + insns = kmalloc_array(len, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; + + /* Add head instruction(s) */ + insns[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 0); + + /* + * Pattern 1: all combinations of power-of-two magnitudes and sign, + * and with a block of contiguous values around each magnitude. + */ + for (di = 0; di < dbits - 1; di++) /* Dst magnitudes */ + for (si = 0; si < sbits - 1; si++) /* Src magnitudes */ + for (k = 0; k < ARRAY_SIZE(sgn); k++) /* Sign combos */ + for (db = -(block1 / 2); + db < (block1 + 1) / 2; db++) + for (sb = -(block1 / 2); + sb < (block1 + 1) / 2; sb++) { + s64 dst, src; + + dst = value(di, db, sgn[k][0]); + src = value(si, sb, sgn[k][1]); + i += (*emit)(self, arg, + &insns[i], + dst, src); + } + /* + * Pattern 2: all combinations for a larger block of values + * for each power-of-two magnitude and sign, where the magnitude is + * the same for both operands. + */ + for (bt = 0; bt < max(dbits, sbits) - 1; bt++) /* Magnitude */ + for (k = 0; k < ARRAY_SIZE(sgn); k++) /* Sign combos */ + for (db = -(block2 / 2); db < (block2 + 1) / 2; db++) + for (sb = -(block2 / 2); + sb < (block2 + 1) / 2; sb++) { + s64 dst, src; + + dst = value(bt % dbits, db, sgn[k][0]); + src = value(bt % sbits, sb, sgn[k][1]); + i += (*emit)(self, arg, &insns[i], + dst, src); + } + + /* Append tail instructions */ + insns[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insns[i++] = BPF_EXIT_INSN(); + BUG_ON(i > len); + + self->u.ptr.insns = insns; + self->u.ptr.len = i; + + return 0; +} + +/* + * Block size parameters used in pattern tests below. une as needed to + * increase/reduce the number combinations tested, see following examples. + * block values per operand MSB + * ---------------------------------------- + * 0 none + * 1 (1 << MSB) + * 2 (1 << MSB) + [-1, 0] + * 3 (1 << MSB) + [-1, 0, 1] + */ +#define PATTERN_BLOCK1 1 +#define PATTERN_BLOCK2 5 + +/* Number of test runs for a pattern test */ +#define NR_PATTERN_RUNS 1 + +/* + * Exhaustive tests of ALU operations for all combinations of power-of-two + * magnitudes of the operands, both for positive and negative values. The + * test is designed to verify e.g. the ALU and ALU64 operations for JITs that + * emit different code depending on the magnitude of the immediate value. + */ +static int __bpf_emit_alu64_imm(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 imm) +{ + int op = *(int *)arg; + int i = 0; + u64 res; + + if (!insns) + return 7; + + if (__bpf_alu_result(&res, dst, (s32)imm, op)) { + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R3, res); + insns[i++] = BPF_ALU64_IMM(op, R1, imm); + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + } + + return i; +} + +static int __bpf_emit_alu32_imm(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 imm) +{ + int op = *(int *)arg; + int i = 0; + u64 res; + + if (!insns) + return 7; + + if (__bpf_alu_result(&res, (u32)dst, (u32)imm, op)) { + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R3, (u32)res); + insns[i++] = BPF_ALU32_IMM(op, R1, imm); + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + } + + return i; +} + +static int __bpf_emit_alu64_reg(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + int i = 0; + u64 res; + + if (!insns) + return 9; + + if (__bpf_alu_result(&res, dst, src, op)) { + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + i += __bpf_ld_imm64(&insns[i], R3, res); + insns[i++] = BPF_ALU64_REG(op, R1, R2); + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + } + + return i; +} + +static int __bpf_emit_alu32_reg(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + int i = 0; + u64 res; + + if (!insns) + return 9; + + if (__bpf_alu_result(&res, (u32)dst, (u32)src, op)) { + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + i += __bpf_ld_imm64(&insns[i], R3, (u32)res); + insns[i++] = BPF_ALU32_REG(op, R1, R2); + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + } + + return i; +} + +static int __bpf_fill_alu64_imm(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 32, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_alu64_imm); +} + +static int __bpf_fill_alu32_imm(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 32, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_alu32_imm); +} + +static int __bpf_fill_alu64_reg(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_alu64_reg); +} + +static int __bpf_fill_alu32_reg(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_alu32_reg); +} + +/* ALU64 immediate operations */ +static int bpf_fill_alu64_mov_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_MOV); +} + +static int bpf_fill_alu64_and_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_AND); +} + +static int bpf_fill_alu64_or_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_OR); +} + +static int bpf_fill_alu64_xor_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_XOR); +} + +static int bpf_fill_alu64_add_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_ADD); +} + +static int bpf_fill_alu64_sub_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_SUB); +} + +static int bpf_fill_alu64_mul_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_MUL); +} + +static int bpf_fill_alu64_div_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_DIV); +} + +static int bpf_fill_alu64_mod_imm(struct bpf_test *self) +{ + return __bpf_fill_alu64_imm(self, BPF_MOD); +} + +/* ALU32 immediate operations */ +static int bpf_fill_alu32_mov_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_MOV); +} + +static int bpf_fill_alu32_and_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_AND); +} + +static int bpf_fill_alu32_or_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_OR); +} + +static int bpf_fill_alu32_xor_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_XOR); +} + +static int bpf_fill_alu32_add_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_ADD); +} + +static int bpf_fill_alu32_sub_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_SUB); +} + +static int bpf_fill_alu32_mul_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_MUL); +} + +static int bpf_fill_alu32_div_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_DIV); +} + +static int bpf_fill_alu32_mod_imm(struct bpf_test *self) +{ + return __bpf_fill_alu32_imm(self, BPF_MOD); +} + +/* ALU64 register operations */ +static int bpf_fill_alu64_mov_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_MOV); +} + +static int bpf_fill_alu64_and_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_AND); +} + +static int bpf_fill_alu64_or_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_OR); +} + +static int bpf_fill_alu64_xor_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_XOR); +} + +static int bpf_fill_alu64_add_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_ADD); +} + +static int bpf_fill_alu64_sub_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_SUB); +} + +static int bpf_fill_alu64_mul_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_MUL); +} + +static int bpf_fill_alu64_div_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_DIV); +} + +static int bpf_fill_alu64_mod_reg(struct bpf_test *self) +{ + return __bpf_fill_alu64_reg(self, BPF_MOD); +} + +/* ALU32 register operations */ +static int bpf_fill_alu32_mov_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_MOV); +} + +static int bpf_fill_alu32_and_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_AND); +} + +static int bpf_fill_alu32_or_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_OR); +} + +static int bpf_fill_alu32_xor_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_XOR); +} + +static int bpf_fill_alu32_add_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_ADD); +} + +static int bpf_fill_alu32_sub_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_SUB); +} + +static int bpf_fill_alu32_mul_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_MUL); +} + +static int bpf_fill_alu32_div_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_DIV); +} + +static int bpf_fill_alu32_mod_reg(struct bpf_test *self) +{ + return __bpf_fill_alu32_reg(self, BPF_MOD); +} + +/* + * Test JITs that implement complex ALU operations as function + * calls, and must re-arrange operands for argument passing. + */ +static int __bpf_fill_alu_imm_regs(struct bpf_test *self, u8 op, bool alu32) +{ + int len = 2 + 10 * 10; + struct bpf_insn *insns; + u64 dst, res; + int i = 0; + u32 imm; + int rd; + + insns = kmalloc_array(len, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; + + /* Operand and result values according to operation */ + if (alu32) + dst = 0x76543210U; + else + dst = 0x7edcba9876543210ULL; + imm = 0x01234567U; + + if (op == BPF_LSH || op == BPF_RSH || op == BPF_ARSH) + imm &= 31; + + __bpf_alu_result(&res, dst, imm, op); + + if (alu32) + res = (u32)res; + + /* Check all operand registers */ + for (rd = R0; rd <= R9; rd++) { + i += __bpf_ld_imm64(&insns[i], rd, dst); + + if (alu32) + insns[i++] = BPF_ALU32_IMM(op, rd, imm); + else + insns[i++] = BPF_ALU64_IMM(op, rd, imm); + + insns[i++] = BPF_JMP32_IMM(BPF_JEQ, rd, res, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_ALU64_IMM(BPF_RSH, rd, 32); + insns[i++] = BPF_JMP32_IMM(BPF_JEQ, rd, res >> 32, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + } + + insns[i++] = BPF_MOV64_IMM(R0, 1); + insns[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insns; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +/* ALU64 K registers */ +static int bpf_fill_alu64_mov_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MOV, false); +} + +static int bpf_fill_alu64_and_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_AND, false); +} + +static int bpf_fill_alu64_or_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_OR, false); +} + +static int bpf_fill_alu64_xor_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_XOR, false); +} + +static int bpf_fill_alu64_lsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_LSH, false); +} + +static int bpf_fill_alu64_rsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_RSH, false); +} + +static int bpf_fill_alu64_arsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_ARSH, false); +} + +static int bpf_fill_alu64_add_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_ADD, false); +} + +static int bpf_fill_alu64_sub_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_SUB, false); +} + +static int bpf_fill_alu64_mul_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MUL, false); +} + +static int bpf_fill_alu64_div_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_DIV, false); +} + +static int bpf_fill_alu64_mod_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MOD, false); +} + +/* ALU32 K registers */ +static int bpf_fill_alu32_mov_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MOV, true); +} + +static int bpf_fill_alu32_and_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_AND, true); +} + +static int bpf_fill_alu32_or_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_OR, true); +} + +static int bpf_fill_alu32_xor_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_XOR, true); +} + +static int bpf_fill_alu32_lsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_LSH, true); +} + +static int bpf_fill_alu32_rsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_RSH, true); +} + +static int bpf_fill_alu32_arsh_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_ARSH, true); +} + +static int bpf_fill_alu32_add_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_ADD, true); +} + +static int bpf_fill_alu32_sub_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_SUB, true); +} + +static int bpf_fill_alu32_mul_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MUL, true); +} + +static int bpf_fill_alu32_div_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_DIV, true); +} + +static int bpf_fill_alu32_mod_imm_regs(struct bpf_test *self) +{ + return __bpf_fill_alu_imm_regs(self, BPF_MOD, true); +} + +/* + * Test JITs that implement complex ALU operations as function + * calls, and must re-arrange operands for argument passing. + */ +static int __bpf_fill_alu_reg_pairs(struct bpf_test *self, u8 op, bool alu32) +{ + int len = 2 + 10 * 10 * 12; + u64 dst, src, res, same; + struct bpf_insn *insns; + int rd, rs; + int i = 0; + + insns = kmalloc_array(len, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; + + /* Operand and result values according to operation */ + if (alu32) { + dst = 0x76543210U; + src = 0x01234567U; + } else { + dst = 0x7edcba9876543210ULL; + src = 0x0123456789abcdefULL; + } + + if (op == BPF_LSH || op == BPF_RSH || op == BPF_ARSH) + src &= 31; + + __bpf_alu_result(&res, dst, src, op); + __bpf_alu_result(&same, src, src, op); + + if (alu32) { + res = (u32)res; + same = (u32)same; + } + + /* Check all combinations of operand registers */ + for (rd = R0; rd <= R9; rd++) { + for (rs = R0; rs <= R9; rs++) { + u64 val = rd == rs ? same : res; + + i += __bpf_ld_imm64(&insns[i], rd, dst); + i += __bpf_ld_imm64(&insns[i], rs, src); + + if (alu32) + insns[i++] = BPF_ALU32_REG(op, rd, rs); + else + insns[i++] = BPF_ALU64_REG(op, rd, rs); + + insns[i++] = BPF_JMP32_IMM(BPF_JEQ, rd, val, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_ALU64_IMM(BPF_RSH, rd, 32); + insns[i++] = BPF_JMP32_IMM(BPF_JEQ, rd, val >> 32, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + } + } + + insns[i++] = BPF_MOV64_IMM(R0, 1); + insns[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insns; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +/* ALU64 X register combinations */ +static int bpf_fill_alu64_mov_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MOV, false); +} + +static int bpf_fill_alu64_and_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_AND, false); +} + +static int bpf_fill_alu64_or_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_OR, false); +} + +static int bpf_fill_alu64_xor_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_XOR, false); +} + +static int bpf_fill_alu64_lsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_LSH, false); +} + +static int bpf_fill_alu64_rsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_RSH, false); +} + +static int bpf_fill_alu64_arsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_ARSH, false); +} + +static int bpf_fill_alu64_add_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_ADD, false); +} + +static int bpf_fill_alu64_sub_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_SUB, false); +} + +static int bpf_fill_alu64_mul_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MUL, false); +} + +static int bpf_fill_alu64_div_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_DIV, false); +} + +static int bpf_fill_alu64_mod_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MOD, false); +} + +/* ALU32 X register combinations */ +static int bpf_fill_alu32_mov_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MOV, true); +} + +static int bpf_fill_alu32_and_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_AND, true); +} + +static int bpf_fill_alu32_or_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_OR, true); +} + +static int bpf_fill_alu32_xor_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_XOR, true); +} + +static int bpf_fill_alu32_lsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_LSH, true); +} + +static int bpf_fill_alu32_rsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_RSH, true); +} + +static int bpf_fill_alu32_arsh_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_ARSH, true); +} + +static int bpf_fill_alu32_add_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_ADD, true); +} + +static int bpf_fill_alu32_sub_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_SUB, true); +} + +static int bpf_fill_alu32_mul_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MUL, true); +} + +static int bpf_fill_alu32_div_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_DIV, true); +} + +static int bpf_fill_alu32_mod_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_alu_reg_pairs(self, BPF_MOD, true); +} + +/* + * Exhaustive tests of atomic operations for all power-of-two operand + * magnitudes, both for positive and negative values. + */ + +static int __bpf_emit_atomic64(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + u64 keep, fetch, res; + int i = 0; + + if (!insns) + return 21; + + switch (op) { + case BPF_XCHG: + res = src; + break; + default: + __bpf_alu_result(&res, dst, src, BPF_OP(op)); + } + + keep = 0x0123456789abcdefULL; + if (op & BPF_FETCH) + fetch = dst; + else + fetch = src; + + i += __bpf_ld_imm64(&insns[i], R0, keep); + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + i += __bpf_ld_imm64(&insns[i], R3, res); + i += __bpf_ld_imm64(&insns[i], R4, fetch); + i += __bpf_ld_imm64(&insns[i], R5, keep); + + insns[i++] = BPF_STX_MEM(BPF_DW, R10, R1, -8); + insns[i++] = BPF_ATOMIC_OP(BPF_DW, op, R10, R2, -8); + insns[i++] = BPF_LDX_MEM(BPF_DW, R1, R10, -8); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R2, R4, 1); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R5, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; +} + +static int __bpf_emit_atomic32(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + u64 keep, fetch, res; + int i = 0; + + if (!insns) + return 21; + + switch (op) { + case BPF_XCHG: + res = src; + break; + default: + __bpf_alu_result(&res, (u32)dst, (u32)src, BPF_OP(op)); + } + + keep = 0x0123456789abcdefULL; + if (op & BPF_FETCH) + fetch = (u32)dst; + else + fetch = src; + + i += __bpf_ld_imm64(&insns[i], R0, keep); + i += __bpf_ld_imm64(&insns[i], R1, (u32)dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + i += __bpf_ld_imm64(&insns[i], R3, (u32)res); + i += __bpf_ld_imm64(&insns[i], R4, fetch); + i += __bpf_ld_imm64(&insns[i], R5, keep); + + insns[i++] = BPF_STX_MEM(BPF_W, R10, R1, -4); + insns[i++] = BPF_ATOMIC_OP(BPF_W, op, R10, R2, -4); + insns[i++] = BPF_LDX_MEM(BPF_W, R1, R10, -4); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 1); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R2, R4, 1); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R5, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; +} + +static int __bpf_emit_cmpxchg64(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int i = 0; + + if (!insns) + return 23; + + i += __bpf_ld_imm64(&insns[i], R0, ~dst); + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + + /* Result unsuccessful */ + insns[i++] = BPF_STX_MEM(BPF_DW, R10, R1, -8); + insns[i++] = BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, R10, R2, -8); + insns[i++] = BPF_LDX_MEM(BPF_DW, R3, R10, -8); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R1, R3, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R3, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + /* Result successful */ + insns[i++] = BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, R10, R2, -8); + insns[i++] = BPF_LDX_MEM(BPF_DW, R3, R10, -8); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R2, R3, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R1, 2); + insns[i++] = BPF_MOV64_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + return i; +} + +static int __bpf_emit_cmpxchg32(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int i = 0; + + if (!insns) + return 27; + + i += __bpf_ld_imm64(&insns[i], R0, ~dst); + i += __bpf_ld_imm64(&insns[i], R1, (u32)dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + + /* Result unsuccessful */ + insns[i++] = BPF_STX_MEM(BPF_W, R10, R1, -4); + insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4); + insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */ + insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4); + + insns[i++] = BPF_JMP32_REG(BPF_JEQ, R1, R3, 2); + insns[i++] = BPF_MOV32_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R3, 2); + insns[i++] = BPF_MOV32_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + /* Result successful */ + i += __bpf_ld_imm64(&insns[i], R0, dst); + insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4); + insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */ + insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4); + + insns[i++] = BPF_JMP32_REG(BPF_JEQ, R2, R3, 2); + insns[i++] = BPF_MOV32_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + insns[i++] = BPF_JMP_REG(BPF_JEQ, R0, R1, 2); + insns[i++] = BPF_MOV32_IMM(R0, __LINE__); + insns[i++] = BPF_EXIT_INSN(); + + return i; +} + +static int __bpf_fill_atomic64(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + 0, PATTERN_BLOCK2, + &__bpf_emit_atomic64); +} + +static int __bpf_fill_atomic32(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + 0, PATTERN_BLOCK2, + &__bpf_emit_atomic32); +} + +/* 64-bit atomic operations */ +static int bpf_fill_atomic64_add(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_ADD); +} + +static int bpf_fill_atomic64_and(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_AND); +} + +static int bpf_fill_atomic64_or(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_OR); +} + +static int bpf_fill_atomic64_xor(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_XOR); +} + +static int bpf_fill_atomic64_add_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_ADD | BPF_FETCH); +} + +static int bpf_fill_atomic64_and_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_AND | BPF_FETCH); +} + +static int bpf_fill_atomic64_or_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_OR | BPF_FETCH); +} + +static int bpf_fill_atomic64_xor_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_XOR | BPF_FETCH); +} + +static int bpf_fill_atomic64_xchg(struct bpf_test *self) +{ + return __bpf_fill_atomic64(self, BPF_XCHG); +} + +static int bpf_fill_cmpxchg64(struct bpf_test *self) +{ + return __bpf_fill_pattern(self, NULL, 64, 64, 0, PATTERN_BLOCK2, + &__bpf_emit_cmpxchg64); +} + +/* 32-bit atomic operations */ +static int bpf_fill_atomic32_add(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_ADD); +} + +static int bpf_fill_atomic32_and(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_AND); +} + +static int bpf_fill_atomic32_or(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_OR); +} + +static int bpf_fill_atomic32_xor(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_XOR); +} + +static int bpf_fill_atomic32_add_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_ADD | BPF_FETCH); +} + +static int bpf_fill_atomic32_and_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_AND | BPF_FETCH); +} + +static int bpf_fill_atomic32_or_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_OR | BPF_FETCH); +} + +static int bpf_fill_atomic32_xor_fetch(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_XOR | BPF_FETCH); +} + +static int bpf_fill_atomic32_xchg(struct bpf_test *self) +{ + return __bpf_fill_atomic32(self, BPF_XCHG); +} + +static int bpf_fill_cmpxchg32(struct bpf_test *self) +{ + return __bpf_fill_pattern(self, NULL, 64, 64, 0, PATTERN_BLOCK2, + &__bpf_emit_cmpxchg32); +} + +/* + * Test JITs that implement ATOMIC operations as function calls or + * other primitives, and must re-arrange operands for argument passing. + */ +static int __bpf_fill_atomic_reg_pairs(struct bpf_test *self, u8 width, u8 op) +{ + struct bpf_insn *insn; + int len = 2 + 34 * 10 * 10; + u64 mem, upd, res; + int rd, rs, i = 0; + + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); + if (!insn) + return -ENOMEM; + + /* Operand and memory values */ + if (width == BPF_DW) { + mem = 0x0123456789abcdefULL; + upd = 0xfedcba9876543210ULL; + } else { /* BPF_W */ + mem = 0x01234567U; + upd = 0x76543210U; + } + + /* Memory updated according to operation */ + switch (op) { + case BPF_XCHG: + res = upd; + break; + case BPF_CMPXCHG: + res = mem; + break; + default: + __bpf_alu_result(&res, mem, upd, BPF_OP(op)); + } + + /* Test all operand registers */ + for (rd = R0; rd <= R9; rd++) { + for (rs = R0; rs <= R9; rs++) { + u64 cmp, src; + + /* Initialize value in memory */ + i += __bpf_ld_imm64(&insn[i], R0, mem); + insn[i++] = BPF_STX_MEM(width, R10, R0, -8); + + /* Initialize registers in order */ + i += __bpf_ld_imm64(&insn[i], R0, ~mem); + i += __bpf_ld_imm64(&insn[i], rs, upd); + insn[i++] = BPF_MOV64_REG(rd, R10); + + /* Perform atomic operation */ + insn[i++] = BPF_ATOMIC_OP(width, op, rd, rs, -8); + if (op == BPF_CMPXCHG && width == BPF_W) + insn[i++] = BPF_ZEXT_REG(R0); + + /* Check R0 register value */ + if (op == BPF_CMPXCHG) + cmp = mem; /* Expect value from memory */ + else if (R0 == rd || R0 == rs) + cmp = 0; /* Aliased, checked below */ + else + cmp = ~mem; /* Expect value to be preserved */ + if (cmp) { + insn[i++] = BPF_JMP32_IMM(BPF_JEQ, R0, + (u32)cmp, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + insn[i++] = BPF_ALU64_IMM(BPF_RSH, R0, 32); + insn[i++] = BPF_JMP32_IMM(BPF_JEQ, R0, + cmp >> 32, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + } + + /* Check source register value */ + if (rs == R0 && op == BPF_CMPXCHG) + src = 0; /* Aliased with R0, checked above */ + else if (rs == rd && (op == BPF_CMPXCHG || + !(op & BPF_FETCH))) + src = 0; /* Aliased with rd, checked below */ + else if (op == BPF_CMPXCHG) + src = upd; /* Expect value to be preserved */ + else if (op & BPF_FETCH) + src = mem; /* Expect fetched value from mem */ + else /* no fetch */ + src = upd; /* Expect value to be preserved */ + if (src) { + insn[i++] = BPF_JMP32_IMM(BPF_JEQ, rs, + (u32)src, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + insn[i++] = BPF_ALU64_IMM(BPF_RSH, rs, 32); + insn[i++] = BPF_JMP32_IMM(BPF_JEQ, rs, + src >> 32, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + } + + /* Check destination register value */ + if (!(rd == R0 && op == BPF_CMPXCHG) && + !(rd == rs && (op & BPF_FETCH))) { + insn[i++] = BPF_JMP_REG(BPF_JEQ, rd, R10, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + } + + /* Check value in memory */ + if (rs != rd) { /* No aliasing */ + i += __bpf_ld_imm64(&insn[i], R1, res); + } else if (op == BPF_XCHG) { /* Aliased, XCHG */ + insn[i++] = BPF_MOV64_REG(R1, R10); + } else if (op == BPF_CMPXCHG) { /* Aliased, CMPXCHG */ + i += __bpf_ld_imm64(&insn[i], R1, mem); + } else { /* Aliased, ALU oper */ + i += __bpf_ld_imm64(&insn[i], R1, mem); + insn[i++] = BPF_ALU64_REG(BPF_OP(op), R1, R10); + } + + insn[i++] = BPF_LDX_MEM(width, R0, R10, -8); + if (width == BPF_DW) + insn[i++] = BPF_JMP_REG(BPF_JEQ, R0, R1, 2); + else /* width == BPF_W */ + insn[i++] = BPF_JMP32_REG(BPF_JEQ, R0, R1, 2); + insn[i++] = BPF_MOV32_IMM(R0, __LINE__); + insn[i++] = BPF_EXIT_INSN(); + } + } + + insn[i++] = BPF_MOV64_IMM(R0, 1); + insn[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insn; + self->u.ptr.len = i; + BUG_ON(i > len); + + return 0; +} + +/* 64-bit atomic register tests */ +static int bpf_fill_atomic64_add_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_ADD); +} + +static int bpf_fill_atomic64_and_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_AND); +} + +static int bpf_fill_atomic64_or_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_OR); +} + +static int bpf_fill_atomic64_xor_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_XOR); +} + +static int bpf_fill_atomic64_add_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_ADD | BPF_FETCH); +} + +static int bpf_fill_atomic64_and_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_AND | BPF_FETCH); +} + +static int bpf_fill_atomic64_or_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_OR | BPF_FETCH); +} + +static int bpf_fill_atomic64_xor_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_XOR | BPF_FETCH); +} + +static int bpf_fill_atomic64_xchg_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_XCHG); +} + +static int bpf_fill_atomic64_cmpxchg_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_DW, BPF_CMPXCHG); +} + +/* 32-bit atomic register tests */ +static int bpf_fill_atomic32_add_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_ADD); +} + +static int bpf_fill_atomic32_and_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_AND); +} + +static int bpf_fill_atomic32_or_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_OR); +} + +static int bpf_fill_atomic32_xor_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_XOR); +} + +static int bpf_fill_atomic32_add_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_ADD | BPF_FETCH); +} + +static int bpf_fill_atomic32_and_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_AND | BPF_FETCH); +} + +static int bpf_fill_atomic32_or_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_OR | BPF_FETCH); +} + +static int bpf_fill_atomic32_xor_fetch_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_XOR | BPF_FETCH); +} + +static int bpf_fill_atomic32_xchg_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_XCHG); +} + +static int bpf_fill_atomic32_cmpxchg_reg_pairs(struct bpf_test *self) +{ + return __bpf_fill_atomic_reg_pairs(self, BPF_W, BPF_CMPXCHG); +} + +/* + * Test the two-instruction 64-bit immediate load operation for all + * power-of-two magnitudes of the immediate operand. For each MSB, a block + * of immediate values centered around the power-of-two MSB are tested, + * both for positive and negative values. The test is designed to verify + * the operation for JITs that emit different code depending on the magnitude + * of the immediate value. This is often the case if the native instruction + * immediate field width is narrower than 32 bits. + */ +static int bpf_fill_ld_imm64_magn(struct bpf_test *self) +{ + int block = 64; /* Increase for more tests per MSB position */ + int len = 3 + 8 * 63 * block * 2; + struct bpf_insn *insn; + int bit, adj, sign; + int i = 0; + + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); + if (!insn) + return -ENOMEM; + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 0); + + for (bit = 0; bit <= 62; bit++) { + for (adj = -block / 2; adj < block / 2; adj++) { + for (sign = -1; sign <= 1; sign += 2) { + s64 imm = sign * ((1LL << bit) + adj); + + /* Perform operation */ + i += __bpf_ld_imm64(&insn[i], R1, imm); + + /* Load reference */ + insn[i++] = BPF_ALU32_IMM(BPF_MOV, R2, imm); + insn[i++] = BPF_ALU32_IMM(BPF_MOV, R3, + (u32)(imm >> 32)); + insn[i++] = BPF_ALU64_IMM(BPF_LSH, R3, 32); + insn[i++] = BPF_ALU64_REG(BPF_OR, R2, R3); + + /* Check result */ + insn[i++] = BPF_JMP_REG(BPF_JEQ, R1, R2, 1); + insn[i++] = BPF_EXIT_INSN(); + } + } + } + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insn[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insn; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +/* + * Test the two-instruction 64-bit immediate load operation for different + * combinations of bytes. Each byte in the 64-bit word is constructed as + * (base & mask) | (rand() & ~mask), where rand() is a deterministic LCG. + * All patterns (base1, mask1) and (base2, mask2) bytes are tested. + */ +static int __bpf_fill_ld_imm64_bytes(struct bpf_test *self, + u8 base1, u8 mask1, + u8 base2, u8 mask2) +{ + struct bpf_insn *insn; + int len = 3 + 8 * BIT(8); + int pattern, index; + u32 rand = 1; + int i = 0; + + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); + if (!insn) + return -ENOMEM; + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 0); + + for (pattern = 0; pattern < BIT(8); pattern++) { + u64 imm = 0; + + for (index = 0; index < 8; index++) { + int byte; + + if (pattern & BIT(index)) + byte = (base1 & mask1) | (rand & ~mask1); + else + byte = (base2 & mask2) | (rand & ~mask2); + imm = (imm << 8) | byte; + } + + /* Update our LCG */ + rand = rand * 1664525 + 1013904223; + + /* Perform operation */ + i += __bpf_ld_imm64(&insn[i], R1, imm); + + /* Load reference */ + insn[i++] = BPF_ALU32_IMM(BPF_MOV, R2, imm); + insn[i++] = BPF_ALU32_IMM(BPF_MOV, R3, (u32)(imm >> 32)); + insn[i++] = BPF_ALU64_IMM(BPF_LSH, R3, 32); + insn[i++] = BPF_ALU64_REG(BPF_OR, R2, R3); + + /* Check result */ + insn[i++] = BPF_JMP_REG(BPF_JEQ, R1, R2, 1); + insn[i++] = BPF_EXIT_INSN(); + } + + insn[i++] = BPF_ALU64_IMM(BPF_MOV, R0, 1); + insn[i++] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insn; + self->u.ptr.len = len; + BUG_ON(i != len); + + return 0; +} + +static int bpf_fill_ld_imm64_checker(struct bpf_test *self) +{ + return __bpf_fill_ld_imm64_bytes(self, 0, 0xff, 0xff, 0xff); +} + +static int bpf_fill_ld_imm64_pos_neg(struct bpf_test *self) +{ + return __bpf_fill_ld_imm64_bytes(self, 1, 0x81, 0x80, 0x80); +} + +static int bpf_fill_ld_imm64_pos_zero(struct bpf_test *self) +{ + return __bpf_fill_ld_imm64_bytes(self, 1, 0x81, 0, 0xff); +} + +static int bpf_fill_ld_imm64_neg_zero(struct bpf_test *self) +{ + return __bpf_fill_ld_imm64_bytes(self, 0x80, 0x80, 0, 0xff); +} + +/* + * Exhaustive tests of JMP operations for all combinations of power-of-two + * magnitudes of the operands, both for positive and negative values. The + * test is designed to verify e.g. the JMP and JMP32 operations for JITs that + * emit different code depending on the magnitude of the immediate value. + */ + +static bool __bpf_match_jmp_cond(s64 v1, s64 v2, u8 op) +{ + switch (op) { + case BPF_JSET: + return !!(v1 & v2); + case BPF_JEQ: + return v1 == v2; + case BPF_JNE: + return v1 != v2; + case BPF_JGT: + return (u64)v1 > (u64)v2; + case BPF_JGE: + return (u64)v1 >= (u64)v2; + case BPF_JLT: + return (u64)v1 < (u64)v2; + case BPF_JLE: + return (u64)v1 <= (u64)v2; + case BPF_JSGT: + return v1 > v2; + case BPF_JSGE: + return v1 >= v2; + case BPF_JSLT: + return v1 < v2; + case BPF_JSLE: + return v1 <= v2; + } + return false; +} + +static int __bpf_emit_jmp_imm(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 imm) +{ + int op = *(int *)arg; + + if (insns) { + bool match = __bpf_match_jmp_cond(dst, (s32)imm, op); + int i = 0; + + insns[i++] = BPF_ALU32_IMM(BPF_MOV, R0, match); + + i += __bpf_ld_imm64(&insns[i], R1, dst); + insns[i++] = BPF_JMP_IMM(op, R1, imm, 1); + if (!match) + insns[i++] = BPF_JMP_IMM(BPF_JA, 0, 0, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; + } + + return 5 + 1; +} + +static int __bpf_emit_jmp32_imm(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 imm) +{ + int op = *(int *)arg; + + if (insns) { + bool match = __bpf_match_jmp_cond((s32)dst, (s32)imm, op); + int i = 0; + + i += __bpf_ld_imm64(&insns[i], R1, dst); + insns[i++] = BPF_JMP32_IMM(op, R1, imm, 1); + if (!match) + insns[i++] = BPF_JMP_IMM(BPF_JA, 0, 0, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; + } + + return 5; +} + +static int __bpf_emit_jmp_reg(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + + if (insns) { + bool match = __bpf_match_jmp_cond(dst, src, op); + int i = 0; + + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + insns[i++] = BPF_JMP_REG(op, R1, R2, 1); + if (!match) + insns[i++] = BPF_JMP_IMM(BPF_JA, 0, 0, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; + } + + return 7; +} + +static int __bpf_emit_jmp32_reg(struct bpf_test *self, void *arg, + struct bpf_insn *insns, s64 dst, s64 src) +{ + int op = *(int *)arg; + + if (insns) { + bool match = __bpf_match_jmp_cond((s32)dst, (s32)src, op); + int i = 0; + + i += __bpf_ld_imm64(&insns[i], R1, dst); + i += __bpf_ld_imm64(&insns[i], R2, src); + insns[i++] = BPF_JMP32_REG(op, R1, R2, 1); + if (!match) + insns[i++] = BPF_JMP_IMM(BPF_JA, 0, 0, 1); + insns[i++] = BPF_EXIT_INSN(); + + return i; + } + + return 7; +} + +static int __bpf_fill_jmp_imm(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 32, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_jmp_imm); +} + +static int __bpf_fill_jmp32_imm(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 32, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_jmp32_imm); +} + +static int __bpf_fill_jmp_reg(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_jmp_reg); +} + +static int __bpf_fill_jmp32_reg(struct bpf_test *self, int op) +{ + return __bpf_fill_pattern(self, &op, 64, 64, + PATTERN_BLOCK1, PATTERN_BLOCK2, + &__bpf_emit_jmp32_reg); +} + +/* JMP immediate tests */ +static int bpf_fill_jmp_jset_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JSET); +} + +static int bpf_fill_jmp_jeq_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JEQ); +} + +static int bpf_fill_jmp_jne_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JNE); +} + +static int bpf_fill_jmp_jgt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JGT); +} + +static int bpf_fill_jmp_jge_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JGE); +} + +static int bpf_fill_jmp_jlt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JLT); +} + +static int bpf_fill_jmp_jle_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JLE); +} + +static int bpf_fill_jmp_jsgt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JSGT); +} + +static int bpf_fill_jmp_jsge_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JSGE); +} + +static int bpf_fill_jmp_jslt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JSLT); +} + +static int bpf_fill_jmp_jsle_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp_imm(self, BPF_JSLE); +} + +/* JMP32 immediate tests */ +static int bpf_fill_jmp32_jset_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JSET); +} + +static int bpf_fill_jmp32_jeq_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JEQ); +} + +static int bpf_fill_jmp32_jne_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JNE); +} + +static int bpf_fill_jmp32_jgt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JGT); +} + +static int bpf_fill_jmp32_jge_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JGE); +} + +static int bpf_fill_jmp32_jlt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JLT); +} + +static int bpf_fill_jmp32_jle_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JLE); +} + +static int bpf_fill_jmp32_jsgt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JSGT); +} + +static int bpf_fill_jmp32_jsge_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JSGE); +} + +static int bpf_fill_jmp32_jslt_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JSLT); +} + +static int bpf_fill_jmp32_jsle_imm(struct bpf_test *self) +{ + return __bpf_fill_jmp32_imm(self, BPF_JSLE); +} + +/* JMP register tests */ +static int bpf_fill_jmp_jset_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JSET); +} + +static int bpf_fill_jmp_jeq_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JEQ); +} + +static int bpf_fill_jmp_jne_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JNE); +} + +static int bpf_fill_jmp_jgt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JGT); +} + +static int bpf_fill_jmp_jge_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JGE); +} + +static int bpf_fill_jmp_jlt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JLT); +} + +static int bpf_fill_jmp_jle_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JLE); +} + +static int bpf_fill_jmp_jsgt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JSGT); +} + +static int bpf_fill_jmp_jsge_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JSGE); +} + +static int bpf_fill_jmp_jslt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JSLT); +} + +static int bpf_fill_jmp_jsle_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp_reg(self, BPF_JSLE); +} + +/* JMP32 register tests */ +static int bpf_fill_jmp32_jset_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JSET); +} + +static int bpf_fill_jmp32_jeq_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JEQ); +} + +static int bpf_fill_jmp32_jne_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JNE); +} + +static int bpf_fill_jmp32_jgt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JGT); +} + +static int bpf_fill_jmp32_jge_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JGE); +} + +static int bpf_fill_jmp32_jlt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JLT); +} + +static int bpf_fill_jmp32_jle_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JLE); +} + +static int bpf_fill_jmp32_jsgt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JSGT); +} + +static int bpf_fill_jmp32_jsge_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JSGE); +} + +static int bpf_fill_jmp32_jslt_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JSLT); +} + +static int bpf_fill_jmp32_jsle_reg(struct bpf_test *self) +{ + return __bpf_fill_jmp32_reg(self, BPF_JSLE); +} + +/* + * Set up a sequence of staggered jumps, forwards and backwards with + * increasing offset. This tests the conversion of relative jumps to + * JITed native jumps. On some architectures, for example MIPS, a large + * PC-relative jump offset may overflow the immediate field of the native + * conditional branch instruction, triggering a conversion to use an + * absolute jump instead. Since this changes the jump offsets, another + * offset computation pass is necessary, and that may in turn trigger + * another branch conversion. This jump sequence is particularly nasty + * in that regard. + * + * The sequence generation is parameterized by size and jump type. + * The size must be even, and the expected result is always size + 1. + * Below is an example with size=8 and result=9. + * + * ________________________Start + * R0 = 0 + * R1 = r1 + * R2 = r2 + * ,------- JMP +4 * 3______________Preamble: 4 insns + * ,----------|-ind 0- if R0 != 7 JMP 8 * 3 + 1 <--------------------. + * | | R0 = 8 | + * | | JMP +7 * 3 ------------------------. + * | ,--------|-----1- if R0 != 5 JMP 7 * 3 + 1 <--------------. | | + * | | | R0 = 6 | | | + * | | | JMP +5 * 3 ------------------. | | + * | | ,------|-----2- if R0 != 3 JMP 6 * 3 + 1 <--------. | | | | + * | | | | R0 = 4 | | | | | + * | | | | JMP +3 * 3 ------------. | | | | + * | | | ,----|-----3- if R0 != 1 JMP 5 * 3 + 1 <--. | | | | | | + * | | | | | R0 = 2 | | | | | | | + * | | | | | JMP +1 * 3 ------. | | | | | | + * | | | | ,--t=====4> if R0 != 0 JMP 4 * 3 + 1 1 2 3 4 5 6 7 8 loc + * | | | | | R0 = 1 -1 +2 -3 +4 -5 +6 -7 +8 off + * | | | | | JMP -2 * 3 ---' | | | | | | | + * | | | | | ,------5- if R0 != 2 JMP 3 * 3 + 1 <-----' | | | | | | + * | | | | | | R0 = 3 | | | | | | + * | | | | | | JMP -4 * 3 ---------' | | | | | + * | | | | | | ,----6- if R0 != 4 JMP 2 * 3 + 1 <-----------' | | | | + * | | | | | | | R0 = 5 | | | | + * | | | | | | | JMP -6 * 3 ---------------' | | | + * | | | | | | | ,--7- if R0 != 6 JMP 1 * 3 + 1 <-----------------' | | + * | | | | | | | | R0 = 7 | | + * | | Error | | | JMP -8 * 3 ---------------------' | + * | | paths | | | ,8- if R0 != 8 JMP 0 * 3 + 1 <-----------------------' + * | | | | | | | | | R0 = 9__________________Sequence: 3 * size - 1 insns + * `-+-+-+-+-+-+-+-+-> EXIT____________________Return: 1 insn + * + */ + +/* The maximum size parameter */ +#define MAX_STAGGERED_JMP_SIZE ((0x7fff / 3) & ~1) + +/* We use a reduced number of iterations to get a reasonable execution time */ +#define NR_STAGGERED_JMP_RUNS 10 + +static int __bpf_fill_staggered_jumps(struct bpf_test *self, + const struct bpf_insn *jmp, + u64 r1, u64 r2) +{ + int size = self->test[0].result - 1; + int len = 4 + 3 * (size + 1); + struct bpf_insn *insns; + int off, ind; + + insns = kmalloc_array(len, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; + + /* Preamble */ + insns[0] = BPF_ALU64_IMM(BPF_MOV, R0, 0); + insns[1] = BPF_ALU64_IMM(BPF_MOV, R1, r1); + insns[2] = BPF_ALU64_IMM(BPF_MOV, R2, r2); + insns[3] = BPF_JMP_IMM(BPF_JA, 0, 0, 3 * size / 2); + + /* Sequence */ + for (ind = 0, off = size; ind <= size; ind++, off -= 2) { + struct bpf_insn *ins = &insns[4 + 3 * ind]; + int loc; + + if (off == 0) + off--; + + loc = abs(off); + ins[0] = BPF_JMP_IMM(BPF_JNE, R0, loc - 1, + 3 * (size - ind) + 1); + ins[1] = BPF_ALU64_IMM(BPF_MOV, R0, loc); + ins[2] = *jmp; + ins[2].off = 3 * (off - 1); + } + + /* Return */ + insns[len - 1] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insns; + self->u.ptr.len = len; return 0; } +/* 64-bit unconditional jump */ +static int bpf_fill_staggered_ja(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JA, 0, 0, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0, 0); +} + +/* 64-bit immediate jumps */ +static int bpf_fill_staggered_jeq_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JEQ, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jne_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JNE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 4321, 0); +} + +static int bpf_fill_staggered_jset_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JSET, R1, 0x82, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x86, 0); +} + +static int bpf_fill_staggered_jgt_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JGT, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x80000000, 0); +} + +static int bpf_fill_staggered_jge_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JGE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jlt_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JLT, R1, 0x80000000, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jle_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JLE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jsgt_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JSGT, R1, -2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, 0); +} + +static int bpf_fill_staggered_jsge_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JSGE, R1, -2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, 0); +} + +static int bpf_fill_staggered_jslt_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JSLT, R1, -1, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, 0); +} + +static int bpf_fill_staggered_jsle_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_IMM(BPF_JSLE, R1, -1, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, 0); +} + +/* 64-bit register jumps */ +static int bpf_fill_staggered_jeq_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JEQ, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jne_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JNE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 4321, 1234); +} + +static int bpf_fill_staggered_jset_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JSET, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x86, 0x82); +} + +static int bpf_fill_staggered_jgt_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JGT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x80000000, 1234); +} + +static int bpf_fill_staggered_jge_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JGE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jlt_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JLT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0x80000000); +} + +static int bpf_fill_staggered_jle_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JLE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jsgt_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JSGT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, -2); +} + +static int bpf_fill_staggered_jsge_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JSGE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, -2); +} + +static int bpf_fill_staggered_jslt_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JSLT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, -1); +} + +static int bpf_fill_staggered_jsle_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP_REG(BPF_JSLE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, -1); +} + +/* 32-bit immediate jumps */ +static int bpf_fill_staggered_jeq32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JEQ, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jne32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JNE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 4321, 0); +} + +static int bpf_fill_staggered_jset32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JSET, R1, 0x82, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x86, 0); +} + +static int bpf_fill_staggered_jgt32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JGT, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x80000000, 0); +} + +static int bpf_fill_staggered_jge32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JGE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jlt32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JLT, R1, 0x80000000, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jle32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JLE, R1, 1234, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0); +} + +static int bpf_fill_staggered_jsgt32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JSGT, R1, -2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, 0); +} + +static int bpf_fill_staggered_jsge32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JSGE, R1, -2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, 0); +} + +static int bpf_fill_staggered_jslt32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JSLT, R1, -1, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, 0); +} + +static int bpf_fill_staggered_jsle32_imm(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_IMM(BPF_JSLE, R1, -1, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, 0); +} + +/* 32-bit register jumps */ +static int bpf_fill_staggered_jeq32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JEQ, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jne32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JNE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 4321, 1234); +} + +static int bpf_fill_staggered_jset32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JSET, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x86, 0x82); +} + +static int bpf_fill_staggered_jgt32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JGT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 0x80000000, 1234); +} + +static int bpf_fill_staggered_jge32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JGE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jlt32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JLT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 0x80000000); +} + +static int bpf_fill_staggered_jle32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JLE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, 1234, 1234); +} + +static int bpf_fill_staggered_jsgt32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JSGT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, -2); +} + +static int bpf_fill_staggered_jsge32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JSGE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, -2); +} + +static int bpf_fill_staggered_jslt32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JSLT, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -2, -1); +} + +static int bpf_fill_staggered_jsle32_reg(struct bpf_test *self) +{ + struct bpf_insn jmp = BPF_JMP32_REG(BPF_JSLE, R1, R2, 0); + + return __bpf_fill_staggered_jumps(self, &jmp, -1, -1); +} + + static struct bpf_test tests[] = { { "TAX", @@ -1951,147 +4514,6 @@ static struct bpf_test tests[] = { { }, { { 0, -1 } } }, - { - /* - * Register (non-)clobbering test, in the case where a 32-bit - * JIT implements complex ALU64 operations via function calls. - * If so, the function call must be invisible in the eBPF - * registers. The JIT must then save and restore relevant - * registers during the call. The following tests check that - * the eBPF registers retain their values after such a call. - */ - "INT: Register clobbering, R1 updated", - .u.insns_int = { - BPF_ALU32_IMM(BPF_MOV, R0, 0), - BPF_ALU32_IMM(BPF_MOV, R1, 123456789), - BPF_ALU32_IMM(BPF_MOV, R2, 2), - BPF_ALU32_IMM(BPF_MOV, R3, 3), - BPF_ALU32_IMM(BPF_MOV, R4, 4), - BPF_ALU32_IMM(BPF_MOV, R5, 5), - BPF_ALU32_IMM(BPF_MOV, R6, 6), - BPF_ALU32_IMM(BPF_MOV, R7, 7), - BPF_ALU32_IMM(BPF_MOV, R8, 8), - BPF_ALU32_IMM(BPF_MOV, R9, 9), - BPF_ALU64_IMM(BPF_DIV, R1, 123456789), - BPF_JMP_IMM(BPF_JNE, R0, 0, 10), - BPF_JMP_IMM(BPF_JNE, R1, 1, 9), - BPF_JMP_IMM(BPF_JNE, R2, 2, 8), - BPF_JMP_IMM(BPF_JNE, R3, 3, 7), - BPF_JMP_IMM(BPF_JNE, R4, 4, 6), - BPF_JMP_IMM(BPF_JNE, R5, 5, 5), - BPF_JMP_IMM(BPF_JNE, R6, 6, 4), - BPF_JMP_IMM(BPF_JNE, R7, 7, 3), - BPF_JMP_IMM(BPF_JNE, R8, 8, 2), - BPF_JMP_IMM(BPF_JNE, R9, 9, 1), - BPF_ALU32_IMM(BPF_MOV, R0, 1), - BPF_EXIT_INSN(), - }, - INTERNAL, - { }, - { { 0, 1 } } - }, - { - "INT: Register clobbering, R2 updated", - .u.insns_int = { - BPF_ALU32_IMM(BPF_MOV, R0, 0), - BPF_ALU32_IMM(BPF_MOV, R1, 1), - BPF_ALU32_IMM(BPF_MOV, R2, 2 * 123456789), - BPF_ALU32_IMM(BPF_MOV, R3, 3), - BPF_ALU32_IMM(BPF_MOV, R4, 4), - BPF_ALU32_IMM(BPF_MOV, R5, 5), - BPF_ALU32_IMM(BPF_MOV, R6, 6), - BPF_ALU32_IMM(BPF_MOV, R7, 7), - BPF_ALU32_IMM(BPF_MOV, R8, 8), - BPF_ALU32_IMM(BPF_MOV, R9, 9), - BPF_ALU64_IMM(BPF_DIV, R2, 123456789), - BPF_JMP_IMM(BPF_JNE, R0, 0, 10), - BPF_JMP_IMM(BPF_JNE, R1, 1, 9), - BPF_JMP_IMM(BPF_JNE, R2, 2, 8), - BPF_JMP_IMM(BPF_JNE, R3, 3, 7), - BPF_JMP_IMM(BPF_JNE, R4, 4, 6), - BPF_JMP_IMM(BPF_JNE, R5, 5, 5), - BPF_JMP_IMM(BPF_JNE, R6, 6, 4), - BPF_JMP_IMM(BPF_JNE, R7, 7, 3), - BPF_JMP_IMM(BPF_JNE, R8, 8, 2), - BPF_JMP_IMM(BPF_JNE, R9, 9, 1), - BPF_ALU32_IMM(BPF_MOV, R0, 1), - BPF_EXIT_INSN(), - }, - INTERNAL, - { }, - { { 0, 1 } } - }, - { - /* - * Test 32-bit JITs that implement complex ALU64 operations as - * function calls R0 = f(R1, R2), and must re-arrange operands. - */ -#define NUMER 0xfedcba9876543210ULL -#define DENOM 0x0123456789abcdefULL - "ALU64_DIV X: Operand register permutations", - .u.insns_int = { - /* R0 / R2 */ - BPF_LD_IMM64(R0, NUMER), - BPF_LD_IMM64(R2, DENOM), - BPF_ALU64_REG(BPF_DIV, R0, R2), - BPF_JMP_IMM(BPF_JEQ, R0, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R1 / R0 */ - BPF_LD_IMM64(R1, NUMER), - BPF_LD_IMM64(R0, DENOM), - BPF_ALU64_REG(BPF_DIV, R1, R0), - BPF_JMP_IMM(BPF_JEQ, R1, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R0 / R1 */ - BPF_LD_IMM64(R0, NUMER), - BPF_LD_IMM64(R1, DENOM), - BPF_ALU64_REG(BPF_DIV, R0, R1), - BPF_JMP_IMM(BPF_JEQ, R0, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R2 / R0 */ - BPF_LD_IMM64(R2, NUMER), - BPF_LD_IMM64(R0, DENOM), - BPF_ALU64_REG(BPF_DIV, R2, R0), - BPF_JMP_IMM(BPF_JEQ, R2, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R2 / R1 */ - BPF_LD_IMM64(R2, NUMER), - BPF_LD_IMM64(R1, DENOM), - BPF_ALU64_REG(BPF_DIV, R2, R1), - BPF_JMP_IMM(BPF_JEQ, R2, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R1 / R2 */ - BPF_LD_IMM64(R1, NUMER), - BPF_LD_IMM64(R2, DENOM), - BPF_ALU64_REG(BPF_DIV, R1, R2), - BPF_JMP_IMM(BPF_JEQ, R1, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* R1 / R1 */ - BPF_LD_IMM64(R1, NUMER), - BPF_ALU64_REG(BPF_DIV, R1, R1), - BPF_JMP_IMM(BPF_JEQ, R1, 1, 1), - BPF_EXIT_INSN(), - /* R2 / R2 */ - BPF_LD_IMM64(R2, DENOM), - BPF_ALU64_REG(BPF_DIV, R2, R2), - BPF_JMP_IMM(BPF_JEQ, R2, 1, 1), - BPF_EXIT_INSN(), - /* R3 / R4 */ - BPF_LD_IMM64(R3, NUMER), - BPF_LD_IMM64(R4, DENOM), - BPF_ALU64_REG(BPF_DIV, R3, R4), - BPF_JMP_IMM(BPF_JEQ, R3, NUMER / DENOM, 1), - BPF_EXIT_INSN(), - /* Successful return */ - BPF_LD_IMM64(R0, 1), - BPF_EXIT_INSN(), - }, - INTERNAL, - { }, - { { 0, 1 } }, -#undef NUMER -#undef DENOM - }, #ifdef CONFIG_32BIT { "INT: 32-bit context pointer word order and zero-extension", @@ -5255,6 +7677,67 @@ static struct bpf_test tests[] = { { }, { { 0, (u32) cpu_to_be64(0x0123456789abcdefLL) } }, }, + { + "ALU_END_FROM_BE 64: 0x0123456789abcdef >> 32 -> 0x01234567", + .u.insns_int = { + BPF_LD_IMM64(R0, 0x0123456789abcdefLL), + BPF_ENDIAN(BPF_FROM_BE, R0, 64), + BPF_ALU64_IMM(BPF_RSH, R0, 32), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) (cpu_to_be64(0x0123456789abcdefLL) >> 32) } }, + }, + /* BPF_ALU | BPF_END | BPF_FROM_BE, reversed */ + { + "ALU_END_FROM_BE 16: 0xfedcba9876543210 -> 0x3210", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_BE, R0, 16), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, cpu_to_be16(0x3210) } }, + }, + { + "ALU_END_FROM_BE 32: 0xfedcba9876543210 -> 0x76543210", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_BE, R0, 32), + BPF_ALU64_REG(BPF_MOV, R1, R0), + BPF_ALU64_IMM(BPF_RSH, R1, 32), + BPF_ALU32_REG(BPF_ADD, R0, R1), /* R1 = 0 */ + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, cpu_to_be32(0x76543210) } }, + }, + { + "ALU_END_FROM_BE 64: 0xfedcba9876543210 -> 0x76543210", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_BE, R0, 64), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) cpu_to_be64(0xfedcba9876543210ULL) } }, + }, + { + "ALU_END_FROM_BE 64: 0xfedcba9876543210 >> 32 -> 0xfedcba98", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_BE, R0, 64), + BPF_ALU64_IMM(BPF_RSH, R0, 32), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) (cpu_to_be64(0xfedcba9876543210ULL) >> 32) } }, + }, /* BPF_ALU | BPF_END | BPF_FROM_LE */ { "ALU_END_FROM_LE 16: 0x0123456789abcdef -> 0xefcd", @@ -5292,6 +7775,321 @@ static struct bpf_test tests[] = { { }, { { 0, (u32) cpu_to_le64(0x0123456789abcdefLL) } }, }, + { + "ALU_END_FROM_LE 64: 0x0123456789abcdef >> 32 -> 0xefcdab89", + .u.insns_int = { + BPF_LD_IMM64(R0, 0x0123456789abcdefLL), + BPF_ENDIAN(BPF_FROM_LE, R0, 64), + BPF_ALU64_IMM(BPF_RSH, R0, 32), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) (cpu_to_le64(0x0123456789abcdefLL) >> 32) } }, + }, + /* BPF_ALU | BPF_END | BPF_FROM_LE, reversed */ + { + "ALU_END_FROM_LE 16: 0xfedcba9876543210 -> 0x1032", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_LE, R0, 16), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, cpu_to_le16(0x3210) } }, + }, + { + "ALU_END_FROM_LE 32: 0xfedcba9876543210 -> 0x10325476", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_LE, R0, 32), + BPF_ALU64_REG(BPF_MOV, R1, R0), + BPF_ALU64_IMM(BPF_RSH, R1, 32), + BPF_ALU32_REG(BPF_ADD, R0, R1), /* R1 = 0 */ + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, cpu_to_le32(0x76543210) } }, + }, + { + "ALU_END_FROM_LE 64: 0xfedcba9876543210 -> 0x10325476", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_LE, R0, 64), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) cpu_to_le64(0xfedcba9876543210ULL) } }, + }, + { + "ALU_END_FROM_LE 64: 0xfedcba9876543210 >> 32 -> 0x98badcfe", + .u.insns_int = { + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), + BPF_ENDIAN(BPF_FROM_LE, R0, 64), + BPF_ALU64_IMM(BPF_RSH, R0, 32), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, (u32) (cpu_to_le64(0xfedcba9876543210ULL) >> 32) } }, + }, + /* BPF_LDX_MEM B/H/W/DW */ + { + "BPF_LDX_MEM | BPF_B", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x0102030405060708ULL), + BPF_LD_IMM64(R2, 0x0000000000000008ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_B, R0, R10, -1), +#else + BPF_LDX_MEM(BPF_B, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_LDX_MEM | BPF_B, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8182838485868788ULL), + BPF_LD_IMM64(R2, 0x0000000000000088ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_B, R0, R10, -1), +#else + BPF_LDX_MEM(BPF_B, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_LDX_MEM | BPF_H", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x0102030405060708ULL), + BPF_LD_IMM64(R2, 0x0000000000000708ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_H, R0, R10, -2), +#else + BPF_LDX_MEM(BPF_H, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_LDX_MEM | BPF_H, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8182838485868788ULL), + BPF_LD_IMM64(R2, 0x0000000000008788ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_H, R0, R10, -2), +#else + BPF_LDX_MEM(BPF_H, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_LDX_MEM | BPF_W", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x0102030405060708ULL), + BPF_LD_IMM64(R2, 0x0000000005060708ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_W, R0, R10, -4), +#else + BPF_LDX_MEM(BPF_W, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_LDX_MEM | BPF_W, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8182838485868788ULL), + BPF_LD_IMM64(R2, 0x0000000085868788ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_LDX_MEM(BPF_W, R0, R10, -4), +#else + BPF_LDX_MEM(BPF_W, R0, R10, -8), +#endif + BPF_JMP_REG(BPF_JNE, R0, R2, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + /* BPF_STX_MEM B/H/W/DW */ + { + "BPF_STX_MEM | BPF_B", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x0102030405060708ULL), + BPF_LD_IMM64(R3, 0x8090a0b0c0d0e008ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_B, R10, R2, -1), +#else + BPF_STX_MEM(BPF_B, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_STX_MEM | BPF_B, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x8182838485868788ULL), + BPF_LD_IMM64(R3, 0x8090a0b0c0d0e088ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_B, R10, R2, -1), +#else + BPF_STX_MEM(BPF_B, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_STX_MEM | BPF_H", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x0102030405060708ULL), + BPF_LD_IMM64(R3, 0x8090a0b0c0d00708ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_H, R10, R2, -2), +#else + BPF_STX_MEM(BPF_H, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_STX_MEM | BPF_H, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x8182838485868788ULL), + BPF_LD_IMM64(R3, 0x8090a0b0c0d08788ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_H, R10, R2, -2), +#else + BPF_STX_MEM(BPF_H, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_STX_MEM | BPF_W", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x0102030405060708ULL), + BPF_LD_IMM64(R3, 0x8090a0b005060708ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_W, R10, R2, -4), +#else + BPF_STX_MEM(BPF_W, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + { + "BPF_STX_MEM | BPF_W, MSB set", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x8090a0b0c0d0e0f0ULL), + BPF_LD_IMM64(R2, 0x8182838485868788ULL), + BPF_LD_IMM64(R3, 0x8090a0b085868788ULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), +#ifdef __BIG_ENDIAN + BPF_STX_MEM(BPF_W, R10, R2, -4), +#else + BPF_STX_MEM(BPF_W, R10, R2, -8), +#endif + BPF_LDX_MEM(BPF_DW, R0, R10, -8), + BPF_JMP_REG(BPF_JNE, R0, R3, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, /* BPF_ST(X) | BPF_MEM | BPF_B/H/W/DW */ { "ST_MEM_B: Store/Load byte: max negative", @@ -5529,15 +8327,20 @@ static struct bpf_test tests[] = { * Individual tests are expanded from template macros for all * combinations of ALU operation, word size and fetching. */ +#define BPF_ATOMIC_POISON(width) ((width) == BPF_W ? (0xbaadf00dULL << 32) : 0) + #define BPF_ATOMIC_OP_TEST1(width, op, logic, old, update, result) \ { \ "BPF_ATOMIC | " #width ", " #op ": Test: " \ #old " " #logic " " #update " = " #result, \ .u.insns_int = { \ - BPF_ALU32_IMM(BPF_MOV, R5, update), \ + BPF_LD_IMM64(R5, (update) | BPF_ATOMIC_POISON(width)), \ BPF_ST_MEM(width, R10, -40, old), \ BPF_ATOMIC_OP(width, op, R10, R5, -40), \ BPF_LDX_MEM(width, R0, R10, -40), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ BPF_EXIT_INSN(), \ }, \ INTERNAL, \ @@ -5551,11 +8354,14 @@ static struct bpf_test tests[] = { #old " " #logic " " #update " = " #result, \ .u.insns_int = { \ BPF_ALU64_REG(BPF_MOV, R1, R10), \ - BPF_ALU32_IMM(BPF_MOV, R0, update), \ + BPF_LD_IMM64(R0, (update) | BPF_ATOMIC_POISON(width)), \ BPF_ST_MEM(BPF_W, R10, -40, old), \ BPF_ATOMIC_OP(width, op, R10, R0, -40), \ BPF_ALU64_REG(BPF_MOV, R0, R10), \ BPF_ALU64_REG(BPF_SUB, R0, R1), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ BPF_EXIT_INSN(), \ }, \ INTERNAL, \ @@ -5569,10 +8375,13 @@ static struct bpf_test tests[] = { #old " " #logic " " #update " = " #result, \ .u.insns_int = { \ BPF_ALU64_REG(BPF_MOV, R0, R10), \ - BPF_ALU32_IMM(BPF_MOV, R1, update), \ + BPF_LD_IMM64(R1, (update) | BPF_ATOMIC_POISON(width)), \ BPF_ST_MEM(width, R10, -40, old), \ BPF_ATOMIC_OP(width, op, R10, R1, -40), \ BPF_ALU64_REG(BPF_SUB, R0, R10), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ BPF_EXIT_INSN(), \ }, \ INTERNAL, \ @@ -5585,10 +8394,10 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | " #width ", " #op ": Test fetch: " \ #old " " #logic " " #update " = " #result, \ .u.insns_int = { \ - BPF_ALU32_IMM(BPF_MOV, R3, update), \ + BPF_LD_IMM64(R3, (update) | BPF_ATOMIC_POISON(width)), \ BPF_ST_MEM(width, R10, -40, old), \ BPF_ATOMIC_OP(width, op, R10, R3, -40), \ - BPF_ALU64_REG(BPF_MOV, R0, R3), \ + BPF_ALU32_REG(BPF_MOV, R0, R3), \ BPF_EXIT_INSN(), \ }, \ INTERNAL, \ @@ -5686,6 +8495,7 @@ static struct bpf_test tests[] = { BPF_ATOMIC_OP_TEST2(BPF_DW, BPF_XCHG, xchg, 0x12, 0xab, 0xab), BPF_ATOMIC_OP_TEST3(BPF_DW, BPF_XCHG, xchg, 0x12, 0xab, 0xab), BPF_ATOMIC_OP_TEST4(BPF_DW, BPF_XCHG, xchg, 0x12, 0xab, 0xab), +#undef BPF_ATOMIC_POISON #undef BPF_ATOMIC_OP_TEST1 #undef BPF_ATOMIC_OP_TEST2 #undef BPF_ATOMIC_OP_TEST3 @@ -5770,7 +8580,7 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | BPF_DW, BPF_CMPXCHG: Test successful return", .u.insns_int = { BPF_LD_IMM64(R1, 0x0123456789abcdefULL), - BPF_LD_IMM64(R2, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL), BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_STX_MEM(BPF_DW, R10, R1, -40), BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, R10, R2, -40), @@ -5787,7 +8597,7 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | BPF_DW, BPF_CMPXCHG: Test successful store", .u.insns_int = { BPF_LD_IMM64(R1, 0x0123456789abcdefULL), - BPF_LD_IMM64(R2, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL), BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_STX_MEM(BPF_DW, R10, R0, -40), BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, R10, R2, -40), @@ -5805,7 +8615,7 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | BPF_DW, BPF_CMPXCHG: Test failure return", .u.insns_int = { BPF_LD_IMM64(R1, 0x0123456789abcdefULL), - BPF_LD_IMM64(R2, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL), BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_ALU64_IMM(BPF_ADD, R0, 1), BPF_STX_MEM(BPF_DW, R10, R1, -40), @@ -5823,7 +8633,7 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | BPF_DW, BPF_CMPXCHG: Test failure store", .u.insns_int = { BPF_LD_IMM64(R1, 0x0123456789abcdefULL), - BPF_LD_IMM64(R2, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL), BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_ALU64_IMM(BPF_ADD, R0, 1), BPF_STX_MEM(BPF_DW, R10, R1, -40), @@ -5842,11 +8652,11 @@ static struct bpf_test tests[] = { "BPF_ATOMIC | BPF_DW, BPF_CMPXCHG: Test side effects", .u.insns_int = { BPF_LD_IMM64(R1, 0x0123456789abcdefULL), - BPF_LD_IMM64(R2, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL), BPF_ALU64_REG(BPF_MOV, R0, R1), BPF_STX_MEM(BPF_DW, R10, R1, -40), BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, R10, R2, -40), - BPF_LD_IMM64(R0, 0xfecdba9876543210ULL), + BPF_LD_IMM64(R0, 0xfedcba9876543210ULL), BPF_JMP_REG(BPF_JNE, R0, R2, 1), BPF_ALU64_REG(BPF_SUB, R0, R2), BPF_EXIT_INSN(), @@ -7192,14 +10002,6 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, - { /* Mainly checking JIT here. */ - "BPF_MAXINSNS: Very long conditional jump", - { }, - INTERNAL | FLAG_NO_DATA, - { }, - { { 0, 1 } }, - .fill_helper = bpf_fill_long_jmp, - }, { "JMP_JA: Jump, gap, jump, ...", { }, @@ -8413,6 +11215,2841 @@ static struct bpf_test tests[] = { {}, { { 0, 2 } }, }, + /* BPF_LDX_MEM with operand aliasing */ + { + "LDX_MEM_B: operand register aliasing", + .u.insns_int = { + BPF_ST_MEM(BPF_B, R10, -8, 123), + BPF_MOV64_REG(R0, R10), + BPF_LDX_MEM(BPF_B, R0, R0, -8), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 123 } }, + .stack_depth = 8, + }, + { + "LDX_MEM_H: operand register aliasing", + .u.insns_int = { + BPF_ST_MEM(BPF_H, R10, -8, 12345), + BPF_MOV64_REG(R0, R10), + BPF_LDX_MEM(BPF_H, R0, R0, -8), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 12345 } }, + .stack_depth = 8, + }, + { + "LDX_MEM_W: operand register aliasing", + .u.insns_int = { + BPF_ST_MEM(BPF_W, R10, -8, 123456789), + BPF_MOV64_REG(R0, R10), + BPF_LDX_MEM(BPF_W, R0, R0, -8), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 123456789 } }, + .stack_depth = 8, + }, + { + "LDX_MEM_DW: operand register aliasing", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x123456789abcdefULL), + BPF_STX_MEM(BPF_DW, R10, R1, -8), + BPF_MOV64_REG(R0, R10), + BPF_LDX_MEM(BPF_DW, R0, R0, -8), + BPF_ALU64_REG(BPF_SUB, R0, R1), + BPF_MOV64_REG(R1, R0), + BPF_ALU64_IMM(BPF_RSH, R1, 32), + BPF_ALU64_REG(BPF_OR, R0, R1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + /* + * Register (non-)clobbering tests for the case where a JIT implements + * complex ALU or ATOMIC operations via function calls. If so, the + * function call must be transparent to the eBPF registers. The JIT + * must therefore save and restore relevant registers across the call. + * The following tests check that the eBPF registers retain their + * values after such an operation. Mainly intended for complex ALU + * and atomic operation, but we run it for all. You never know... + * + * Note that each operations should be tested twice with different + * destinations, to check preservation for all registers. + */ +#define BPF_TEST_CLOBBER_ALU(alu, op, dst, src) \ + { \ + #alu "_" #op " to " #dst ": no clobbering", \ + .u.insns_int = { \ + BPF_ALU64_IMM(BPF_MOV, R0, R0), \ + BPF_ALU64_IMM(BPF_MOV, R1, R1), \ + BPF_ALU64_IMM(BPF_MOV, R2, R2), \ + BPF_ALU64_IMM(BPF_MOV, R3, R3), \ + BPF_ALU64_IMM(BPF_MOV, R4, R4), \ + BPF_ALU64_IMM(BPF_MOV, R5, R5), \ + BPF_ALU64_IMM(BPF_MOV, R6, R6), \ + BPF_ALU64_IMM(BPF_MOV, R7, R7), \ + BPF_ALU64_IMM(BPF_MOV, R8, R8), \ + BPF_ALU64_IMM(BPF_MOV, R9, R9), \ + BPF_##alu(BPF_ ##op, dst, src), \ + BPF_ALU32_IMM(BPF_MOV, dst, dst), \ + BPF_JMP_IMM(BPF_JNE, R0, R0, 10), \ + BPF_JMP_IMM(BPF_JNE, R1, R1, 9), \ + BPF_JMP_IMM(BPF_JNE, R2, R2, 8), \ + BPF_JMP_IMM(BPF_JNE, R3, R3, 7), \ + BPF_JMP_IMM(BPF_JNE, R4, R4, 6), \ + BPF_JMP_IMM(BPF_JNE, R5, R5, 5), \ + BPF_JMP_IMM(BPF_JNE, R6, R6, 4), \ + BPF_JMP_IMM(BPF_JNE, R7, R7, 3), \ + BPF_JMP_IMM(BPF_JNE, R8, R8, 2), \ + BPF_JMP_IMM(BPF_JNE, R9, R9, 1), \ + BPF_ALU64_IMM(BPF_MOV, R0, 1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 1 } } \ + } + /* ALU64 operations, register clobbering */ + BPF_TEST_CLOBBER_ALU(ALU64_IMM, AND, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, AND, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, OR, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, OR, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, XOR, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, XOR, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, LSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, LSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, RSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, RSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, ARSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, ARSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, ADD, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, ADD, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, SUB, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, SUB, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, MUL, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, MUL, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, DIV, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, DIV, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, MOD, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU64_IMM, MOD, R9, 123456789), + /* ALU32 immediate operations, register clobbering */ + BPF_TEST_CLOBBER_ALU(ALU32_IMM, AND, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, AND, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, OR, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, OR, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, XOR, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, XOR, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, LSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, LSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, RSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, RSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, ARSH, R8, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, ARSH, R9, 12), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, ADD, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, ADD, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, SUB, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, SUB, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, MUL, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, MUL, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, DIV, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, DIV, R9, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, MOD, R8, 123456789), + BPF_TEST_CLOBBER_ALU(ALU32_IMM, MOD, R9, 123456789), + /* ALU64 register operations, register clobbering */ + BPF_TEST_CLOBBER_ALU(ALU64_REG, AND, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, AND, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, OR, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, OR, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, XOR, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, XOR, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, LSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, LSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, RSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, RSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, ARSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, ARSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, ADD, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, ADD, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, SUB, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, SUB, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, MUL, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, MUL, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, DIV, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, DIV, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, MOD, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU64_REG, MOD, R9, R1), + /* ALU32 register operations, register clobbering */ + BPF_TEST_CLOBBER_ALU(ALU32_REG, AND, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, AND, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, OR, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, OR, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, XOR, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, XOR, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, LSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, LSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, RSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, RSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, ARSH, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, ARSH, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, ADD, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, ADD, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, SUB, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, SUB, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, MUL, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, MUL, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, DIV, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, DIV, R9, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, MOD, R8, R1), + BPF_TEST_CLOBBER_ALU(ALU32_REG, MOD, R9, R1), +#undef BPF_TEST_CLOBBER_ALU +#define BPF_TEST_CLOBBER_ATOMIC(width, op) \ + { \ + "Atomic_" #width " " #op ": no clobbering", \ + .u.insns_int = { \ + BPF_ALU64_IMM(BPF_MOV, R0, 0), \ + BPF_ALU64_IMM(BPF_MOV, R1, 1), \ + BPF_ALU64_IMM(BPF_MOV, R2, 2), \ + BPF_ALU64_IMM(BPF_MOV, R3, 3), \ + BPF_ALU64_IMM(BPF_MOV, R4, 4), \ + BPF_ALU64_IMM(BPF_MOV, R5, 5), \ + BPF_ALU64_IMM(BPF_MOV, R6, 6), \ + BPF_ALU64_IMM(BPF_MOV, R7, 7), \ + BPF_ALU64_IMM(BPF_MOV, R8, 8), \ + BPF_ALU64_IMM(BPF_MOV, R9, 9), \ + BPF_ST_MEM(width, R10, -8, \ + (op) == BPF_CMPXCHG ? 0 : \ + (op) & BPF_FETCH ? 1 : 0), \ + BPF_ATOMIC_OP(width, op, R10, R1, -8), \ + BPF_JMP_IMM(BPF_JNE, R0, 0, 10), \ + BPF_JMP_IMM(BPF_JNE, R1, 1, 9), \ + BPF_JMP_IMM(BPF_JNE, R2, 2, 8), \ + BPF_JMP_IMM(BPF_JNE, R3, 3, 7), \ + BPF_JMP_IMM(BPF_JNE, R4, 4, 6), \ + BPF_JMP_IMM(BPF_JNE, R5, 5, 5), \ + BPF_JMP_IMM(BPF_JNE, R6, 6, 4), \ + BPF_JMP_IMM(BPF_JNE, R7, 7, 3), \ + BPF_JMP_IMM(BPF_JNE, R8, 8, 2), \ + BPF_JMP_IMM(BPF_JNE, R9, 9, 1), \ + BPF_ALU64_IMM(BPF_MOV, R0, 1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 1 } }, \ + .stack_depth = 8, \ + } + /* 64-bit atomic operations, register clobbering */ + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_ADD), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_AND), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_OR), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_XOR), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_ADD | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_AND | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_OR | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_XOR | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_XCHG), + BPF_TEST_CLOBBER_ATOMIC(BPF_DW, BPF_CMPXCHG), + /* 32-bit atomic operations, register clobbering */ + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_ADD), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_AND), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_OR), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_XOR), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_ADD | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_AND | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_OR | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_XOR | BPF_FETCH), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_XCHG), + BPF_TEST_CLOBBER_ATOMIC(BPF_W, BPF_CMPXCHG), +#undef BPF_TEST_CLOBBER_ATOMIC + /* Checking that ALU32 src is not zero extended in place */ +#define BPF_ALU32_SRC_ZEXT(op) \ + { \ + "ALU32_" #op "_X: src preserved in zext", \ + .u.insns_int = { \ + BPF_LD_IMM64(R1, 0x0123456789acbdefULL),\ + BPF_LD_IMM64(R2, 0xfedcba9876543210ULL),\ + BPF_ALU64_REG(BPF_MOV, R0, R1), \ + BPF_ALU32_REG(BPF_##op, R2, R1), \ + BPF_ALU64_REG(BPF_SUB, R0, R1), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 0 } }, \ + } + BPF_ALU32_SRC_ZEXT(MOV), + BPF_ALU32_SRC_ZEXT(AND), + BPF_ALU32_SRC_ZEXT(OR), + BPF_ALU32_SRC_ZEXT(XOR), + BPF_ALU32_SRC_ZEXT(ADD), + BPF_ALU32_SRC_ZEXT(SUB), + BPF_ALU32_SRC_ZEXT(MUL), + BPF_ALU32_SRC_ZEXT(DIV), + BPF_ALU32_SRC_ZEXT(MOD), +#undef BPF_ALU32_SRC_ZEXT + /* Checking that ATOMIC32 src is not zero extended in place */ +#define BPF_ATOMIC32_SRC_ZEXT(op) \ + { \ + "ATOMIC_W_" #op ": src preserved in zext", \ + .u.insns_int = { \ + BPF_LD_IMM64(R0, 0x0123456789acbdefULL), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ST_MEM(BPF_W, R10, -4, 0), \ + BPF_ATOMIC_OP(BPF_W, BPF_##op, R10, R1, -4), \ + BPF_ALU64_REG(BPF_SUB, R0, R1), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 0 } }, \ + .stack_depth = 8, \ + } + BPF_ATOMIC32_SRC_ZEXT(ADD), + BPF_ATOMIC32_SRC_ZEXT(AND), + BPF_ATOMIC32_SRC_ZEXT(OR), + BPF_ATOMIC32_SRC_ZEXT(XOR), +#undef BPF_ATOMIC32_SRC_ZEXT + /* Checking that CMPXCHG32 src is not zero extended in place */ + { + "ATOMIC_W_CMPXCHG: src preserved in zext", + .u.insns_int = { + BPF_LD_IMM64(R1, 0x0123456789acbdefULL), + BPF_ALU64_REG(BPF_MOV, R2, R1), + BPF_ALU64_REG(BPF_MOV, R0, 0), + BPF_ST_MEM(BPF_W, R10, -4, 0), + BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R1, -4), + BPF_ALU64_REG(BPF_SUB, R1, R2), + BPF_ALU64_REG(BPF_MOV, R2, R1), + BPF_ALU64_IMM(BPF_RSH, R2, 32), + BPF_ALU64_REG(BPF_OR, R1, R2), + BPF_ALU64_REG(BPF_MOV, R0, R1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + .stack_depth = 8, + }, + /* Checking that JMP32 immediate src is not zero extended in place */ +#define BPF_JMP32_IMM_ZEXT(op) \ + { \ + "JMP32_" #op "_K: operand preserved in zext", \ + .u.insns_int = { \ + BPF_LD_IMM64(R0, 0x0123456789acbdefULL),\ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_JMP32_IMM(BPF_##op, R0, 1234, 1), \ + BPF_JMP_A(0), /* Nop */ \ + BPF_ALU64_REG(BPF_SUB, R0, R1), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 0 } }, \ + } + BPF_JMP32_IMM_ZEXT(JEQ), + BPF_JMP32_IMM_ZEXT(JNE), + BPF_JMP32_IMM_ZEXT(JSET), + BPF_JMP32_IMM_ZEXT(JGT), + BPF_JMP32_IMM_ZEXT(JGE), + BPF_JMP32_IMM_ZEXT(JLT), + BPF_JMP32_IMM_ZEXT(JLE), + BPF_JMP32_IMM_ZEXT(JSGT), + BPF_JMP32_IMM_ZEXT(JSGE), + BPF_JMP32_IMM_ZEXT(JSGT), + BPF_JMP32_IMM_ZEXT(JSLT), + BPF_JMP32_IMM_ZEXT(JSLE), +#undef BPF_JMP2_IMM_ZEXT + /* Checking that JMP32 dst & src are not zero extended in place */ +#define BPF_JMP32_REG_ZEXT(op) \ + { \ + "JMP32_" #op "_X: operands preserved in zext", \ + .u.insns_int = { \ + BPF_LD_IMM64(R0, 0x0123456789acbdefULL),\ + BPF_LD_IMM64(R1, 0xfedcba9876543210ULL),\ + BPF_ALU64_REG(BPF_MOV, R2, R0), \ + BPF_ALU64_REG(BPF_MOV, R3, R1), \ + BPF_JMP32_IMM(BPF_##op, R0, R1, 1), \ + BPF_JMP_A(0), /* Nop */ \ + BPF_ALU64_REG(BPF_SUB, R0, R2), \ + BPF_ALU64_REG(BPF_SUB, R1, R3), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ + BPF_ALU64_REG(BPF_MOV, R1, R0), \ + BPF_ALU64_IMM(BPF_RSH, R1, 32), \ + BPF_ALU64_REG(BPF_OR, R0, R1), \ + BPF_EXIT_INSN(), \ + }, \ + INTERNAL, \ + { }, \ + { { 0, 0 } }, \ + } + BPF_JMP32_REG_ZEXT(JEQ), + BPF_JMP32_REG_ZEXT(JNE), + BPF_JMP32_REG_ZEXT(JSET), + BPF_JMP32_REG_ZEXT(JGT), + BPF_JMP32_REG_ZEXT(JGE), + BPF_JMP32_REG_ZEXT(JLT), + BPF_JMP32_REG_ZEXT(JLE), + BPF_JMP32_REG_ZEXT(JSGT), + BPF_JMP32_REG_ZEXT(JSGE), + BPF_JMP32_REG_ZEXT(JSGT), + BPF_JMP32_REG_ZEXT(JSLT), + BPF_JMP32_REG_ZEXT(JSLE), +#undef BPF_JMP2_REG_ZEXT + /* ALU64 K register combinations */ + { + "ALU64_MOV_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mov_imm_regs, + }, + { + "ALU64_AND_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_and_imm_regs, + }, + { + "ALU64_OR_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_or_imm_regs, + }, + { + "ALU64_XOR_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_xor_imm_regs, + }, + { + "ALU64_LSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_lsh_imm_regs, + }, + { + "ALU64_RSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_rsh_imm_regs, + }, + { + "ALU64_ARSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_arsh_imm_regs, + }, + { + "ALU64_ADD_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_add_imm_regs, + }, + { + "ALU64_SUB_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_sub_imm_regs, + }, + { + "ALU64_MUL_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mul_imm_regs, + }, + { + "ALU64_DIV_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_div_imm_regs, + }, + { + "ALU64_MOD_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mod_imm_regs, + }, + /* ALU32 K registers */ + { + "ALU32_MOV_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mov_imm_regs, + }, + { + "ALU32_AND_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_and_imm_regs, + }, + { + "ALU32_OR_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_or_imm_regs, + }, + { + "ALU32_XOR_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_xor_imm_regs, + }, + { + "ALU32_LSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_lsh_imm_regs, + }, + { + "ALU32_RSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_rsh_imm_regs, + }, + { + "ALU32_ARSH_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_arsh_imm_regs, + }, + { + "ALU32_ADD_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_add_imm_regs, + }, + { + "ALU32_SUB_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_sub_imm_regs, + }, + { + "ALU32_MUL_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mul_imm_regs, + }, + { + "ALU32_DIV_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_div_imm_regs, + }, + { + "ALU32_MOD_K: registers", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mod_imm_regs, + }, + /* ALU64 X register combinations */ + { + "ALU64_MOV_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mov_reg_pairs, + }, + { + "ALU64_AND_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_and_reg_pairs, + }, + { + "ALU64_OR_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_or_reg_pairs, + }, + { + "ALU64_XOR_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_xor_reg_pairs, + }, + { + "ALU64_LSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_lsh_reg_pairs, + }, + { + "ALU64_RSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_rsh_reg_pairs, + }, + { + "ALU64_ARSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_arsh_reg_pairs, + }, + { + "ALU64_ADD_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_add_reg_pairs, + }, + { + "ALU64_SUB_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_sub_reg_pairs, + }, + { + "ALU64_MUL_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mul_reg_pairs, + }, + { + "ALU64_DIV_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_div_reg_pairs, + }, + { + "ALU64_MOD_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mod_reg_pairs, + }, + /* ALU32 X register combinations */ + { + "ALU32_MOV_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mov_reg_pairs, + }, + { + "ALU32_AND_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_and_reg_pairs, + }, + { + "ALU32_OR_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_or_reg_pairs, + }, + { + "ALU32_XOR_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_xor_reg_pairs, + }, + { + "ALU32_LSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_lsh_reg_pairs, + }, + { + "ALU32_RSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_rsh_reg_pairs, + }, + { + "ALU32_ARSH_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_arsh_reg_pairs, + }, + { + "ALU32_ADD_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_add_reg_pairs, + }, + { + "ALU32_SUB_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_sub_reg_pairs, + }, + { + "ALU32_MUL_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mul_reg_pairs, + }, + { + "ALU32_DIV_X: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_div_reg_pairs, + }, + { + "ALU32_MOD_X register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mod_reg_pairs, + }, + /* Exhaustive test of ALU64 shift operations */ + { + "ALU64_LSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_lsh_imm, + }, + { + "ALU64_RSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_rsh_imm, + }, + { + "ALU64_ARSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_arsh_imm, + }, + { + "ALU64_LSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_lsh_reg, + }, + { + "ALU64_RSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_rsh_reg, + }, + { + "ALU64_ARSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_arsh_reg, + }, + /* Exhaustive test of ALU32 shift operations */ + { + "ALU32_LSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_lsh_imm, + }, + { + "ALU32_RSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_rsh_imm, + }, + { + "ALU32_ARSH_K: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_arsh_imm, + }, + { + "ALU32_LSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_lsh_reg, + }, + { + "ALU32_RSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_rsh_reg, + }, + { + "ALU32_ARSH_X: all shift values", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_arsh_reg, + }, + /* + * Exhaustive test of ALU64 shift operations when + * source and destination register are the same. + */ + { + "ALU64_LSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_lsh_same_reg, + }, + { + "ALU64_RSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_rsh_same_reg, + }, + { + "ALU64_ARSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_arsh_same_reg, + }, + /* + * Exhaustive test of ALU32 shift operations when + * source and destination register are the same. + */ + { + "ALU32_LSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_lsh_same_reg, + }, + { + "ALU32_RSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_rsh_same_reg, + }, + { + "ALU32_ARSH_X: all shift values with the same register", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_arsh_same_reg, + }, + /* ALU64 immediate magnitudes */ + { + "ALU64_MOV_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mov_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_AND_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_and_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_OR_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_or_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_XOR_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_xor_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_ADD_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_add_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_SUB_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_sub_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_MUL_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mul_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_DIV_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_div_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_MOD_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mod_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* ALU32 immediate magnitudes */ + { + "ALU32_MOV_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mov_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_AND_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_and_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_OR_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_or_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_XOR_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_xor_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_ADD_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_add_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_SUB_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_sub_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_MUL_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mul_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_DIV_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_div_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_MOD_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mod_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* ALU64 register magnitudes */ + { + "ALU64_MOV_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mov_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_AND_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_and_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_OR_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_or_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_XOR_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_xor_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_ADD_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_add_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_SUB_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_sub_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_MUL_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mul_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_DIV_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_div_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU64_MOD_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu64_mod_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* ALU32 register magnitudes */ + { + "ALU32_MOV_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mov_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_AND_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_and_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_OR_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_or_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_XOR_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_xor_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_ADD_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_add_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_SUB_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_sub_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_MUL_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mul_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_DIV_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_div_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ALU32_MOD_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_alu32_mod_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* LD_IMM64 immediate magnitudes and byte patterns */ + { + "LD_IMM64: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_ld_imm64_magn, + }, + { + "LD_IMM64: checker byte patterns", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_ld_imm64_checker, + }, + { + "LD_IMM64: random positive and zero byte patterns", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_ld_imm64_pos_zero, + }, + { + "LD_IMM64: random negative and zero byte patterns", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_ld_imm64_neg_zero, + }, + { + "LD_IMM64: random positive and negative byte patterns", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_ld_imm64_pos_neg, + }, + /* 64-bit ATOMIC register combinations */ + { + "ATOMIC_DW_ADD: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_add_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_AND: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_and_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_OR: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_or_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_XOR: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xor_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_ADD_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_add_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_AND_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_and_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_OR_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_or_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_XOR_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xor_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_XCHG: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xchg_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_DW_CMPXCHG: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_cmpxchg_reg_pairs, + .stack_depth = 8, + }, + /* 32-bit ATOMIC register combinations */ + { + "ATOMIC_W_ADD: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_add_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_AND: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_and_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_OR: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_or_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_XOR: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xor_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_ADD_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_add_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_AND_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_and_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_OR_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_or_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_XOR_FETCH: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xor_fetch_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_XCHG: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xchg_reg_pairs, + .stack_depth = 8, + }, + { + "ATOMIC_W_CMPXCHG: register combinations", + { }, + INTERNAL, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_cmpxchg_reg_pairs, + .stack_depth = 8, + }, + /* 64-bit ATOMIC magnitudes */ + { + "ATOMIC_DW_ADD: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_add, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_AND: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_and, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_OR: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_or, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_XOR: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xor, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_ADD_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_add_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_AND_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_and_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_OR_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_or_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_XOR_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xor_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_XCHG: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic64_xchg, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_DW_CMPXCHG: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_cmpxchg64, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* 64-bit atomic magnitudes */ + { + "ATOMIC_W_ADD: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_add, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_AND: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_and, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_OR: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_or, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_XOR: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xor, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_ADD_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_add_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_AND_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_and_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_OR_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_or_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_XOR_FETCH: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xor_fetch, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_XCHG: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_atomic32_xchg, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "ATOMIC_W_CMPXCHG: all operand magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_cmpxchg32, + .stack_depth = 8, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* JMP immediate magnitudes */ + { + "JMP_JSET_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jset_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JEQ_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jeq_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JNE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jne_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JGT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jgt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JGE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jge_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JLT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jlt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JLE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jle_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSGT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsgt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSGE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsge_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSLT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jslt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSLE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsle_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* JMP register magnitudes */ + { + "JMP_JSET_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jset_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JEQ_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jeq_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JNE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jne_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JGT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jgt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JGE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jge_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JLT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jlt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JLE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jle_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSGT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsgt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSGE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsge_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSLT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jslt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP_JSLE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp_jsle_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* JMP32 immediate magnitudes */ + { + "JMP32_JSET_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jset_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JEQ_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jeq_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JNE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jne_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JGT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jgt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JGE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jge_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JLT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jlt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JLE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jle_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSGT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsgt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSGE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsge_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSLT_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jslt_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSLE_K: all immediate value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsle_imm, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* JMP32 register magnitudes */ + { + "JMP32_JSET_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jset_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JEQ_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jeq_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JNE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jne_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JGT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jgt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JGE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jge_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JLT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jlt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JLE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jle_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSGT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsgt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSGE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsge_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSLT_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jslt_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + { + "JMP32_JSLE_X: all register value magnitudes", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_jmp32_jsle_reg, + .nr_testruns = NR_PATTERN_RUNS, + }, + /* Conditional jumps with constant decision */ + { + "JMP_JSET_K: imm = 0 -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_IMM(BPF_JSET, R1, 0, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JLT_K: imm = 0 -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_IMM(BPF_JLT, R1, 0, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JGE_K: imm = 0 -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_IMM(BPF_JGE, R1, 0, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JGT_K: imm = 0xffffffff -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_IMM(BPF_JGT, R1, U32_MAX, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JLE_K: imm = 0xffffffff -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_IMM(BPF_JLE, R1, U32_MAX, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP32_JSGT_K: imm = 0x7fffffff -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP32_IMM(BPF_JSGT, R1, S32_MAX, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP32_JSGE_K: imm = -0x80000000 -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP32_IMM(BPF_JSGE, R1, S32_MIN, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP32_JSLT_K: imm = -0x80000000 -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP32_IMM(BPF_JSLT, R1, S32_MIN, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP32_JSLE_K: imm = 0x7fffffff -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP32_IMM(BPF_JSLE, R1, S32_MAX, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JEQ_X: dst = src -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JEQ, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JGE_X: dst = src -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JGE, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JLE_X: dst = src -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JLE, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSGE_X: dst = src -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JSGE, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLE_X: dst = src -> always taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JSLE, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + }, + { + "JMP_JNE_X: dst = src -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JNE, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JGT_X: dst = src -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JGT, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JLT_X: dst = src -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JLT, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JSGT_X: dst = src -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JSGT, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + { + "JMP_JSLT_X: dst = src -> never taken", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 1), + BPF_JMP_REG(BPF_JSLT, R1, R1, 1), + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 0 } }, + }, + /* Short relative jumps */ + { + "Short relative jump: offset=0", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_JMP_IMM(BPF_JEQ, R0, 0, 0), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, -1), + }, + INTERNAL | FLAG_NO_DATA | FLAG_VERIFIER_ZEXT, + { }, + { { 0, 0 } }, + }, + { + "Short relative jump: offset=1", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_JMP_IMM(BPF_JEQ, R0, 0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, -1), + }, + INTERNAL | FLAG_NO_DATA | FLAG_VERIFIER_ZEXT, + { }, + { { 0, 0 } }, + }, + { + "Short relative jump: offset=2", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_JMP_IMM(BPF_JEQ, R0, 0, 2), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, -1), + }, + INTERNAL | FLAG_NO_DATA | FLAG_VERIFIER_ZEXT, + { }, + { { 0, 0 } }, + }, + { + "Short relative jump: offset=3", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_JMP_IMM(BPF_JEQ, R0, 0, 3), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, -1), + }, + INTERNAL | FLAG_NO_DATA | FLAG_VERIFIER_ZEXT, + { }, + { { 0, 0 } }, + }, + { + "Short relative jump: offset=4", + .u.insns_int = { + BPF_ALU64_IMM(BPF_MOV, R0, 0), + BPF_JMP_IMM(BPF_JEQ, R0, 0, 4), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_ALU32_IMM(BPF_ADD, R0, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, -1), + }, + INTERNAL | FLAG_NO_DATA | FLAG_VERIFIER_ZEXT, + { }, + { { 0, 0 } }, + }, + /* Conditional branch conversions */ + { + "Long conditional jump: taken at runtime", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_max_jmp_taken, + }, + { + "Long conditional jump: not taken at runtime", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 2 } }, + .fill_helper = bpf_fill_max_jmp_not_taken, + }, + { + "Long conditional jump: always taken, known at JIT time", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 1 } }, + .fill_helper = bpf_fill_max_jmp_always_taken, + }, + { + "Long conditional jump: never taken, known at JIT time", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, 2 } }, + .fill_helper = bpf_fill_max_jmp_never_taken, + }, + /* Staggered jump sequences, immediate */ + { + "Staggered jumps: JMP_JA", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_ja, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JEQ_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jeq_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JNE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jne_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSET_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jset_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JGT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jgt_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JGE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jge_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JLT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jlt_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JLE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jle_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSGT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsgt_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSGE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsge_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSLT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jslt_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSLE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsle_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + /* Staggered jump sequences, register */ + { + "Staggered jumps: JMP_JEQ_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jeq_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JNE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jne_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSET_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jset_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JGT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jgt_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JGE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jge_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JLT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jlt_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JLE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jle_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSGT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsgt_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSGE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsge_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSLT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jslt_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP_JSLE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsle_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + /* Staggered jump sequences, JMP32 immediate */ + { + "Staggered jumps: JMP32_JEQ_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jeq32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JNE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jne32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSET_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jset32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JGT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jgt32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JGE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jge32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JLT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jlt32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JLE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jle32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSGT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsgt32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSGE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsge32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSLT_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jslt32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSLE_K", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsle32_imm, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + /* Staggered jump sequences, JMP32 register */ + { + "Staggered jumps: JMP32_JEQ_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jeq32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JNE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jne32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSET_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jset32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JGT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jgt32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JGE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jge32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JLT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jlt32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JLE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jle32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSGT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsgt32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSGE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsge32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSLT_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jslt32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, + { + "Staggered jumps: JMP32_JSLE_X", + { }, + INTERNAL | FLAG_NO_DATA, + { }, + { { 0, MAX_STAGGERED_JMP_SIZE + 1 } }, + .fill_helper = bpf_fill_staggered_jsle32_reg, + .nr_testruns = NR_STAGGERED_JMP_RUNS, + }, }; static struct net_device dev; @@ -8576,6 +14213,8 @@ static struct bpf_prog *generate_filter(int which, int *err) fp->type = BPF_PROG_TYPE_SOCKET_FILTER; memcpy(fp->insnsi, fptr, fp->len * sizeof(struct bpf_insn)); fp->aux->stack_depth = tests[which].stack_depth; + fp->aux->verifier_zext = !!(tests[which].aux & + FLAG_VERIFIER_ZEXT); /* We cannot error here as we don't need type compatibility * checks. @@ -8631,6 +14270,9 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test) { int err_cnt = 0, i, runs = MAX_TESTRUNS; + if (test->nr_testruns) + runs = min(test->nr_testruns, MAX_TESTRUNS); + for (i = 0; i < MAX_SUBTESTS; i++) { void *data; u64 duration; @@ -8674,86 +14316,9 @@ module_param_string(test_name, test_name, sizeof(test_name), 0); static int test_id = -1; module_param(test_id, int, 0); -static int test_range[2] = { 0, ARRAY_SIZE(tests) - 1 }; +static int test_range[2] = { 0, INT_MAX }; module_param_array(test_range, int, NULL, 0); -static __init int find_test_index(const char *test_name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - if (!strcmp(tests[i].descr, test_name)) - return i; - } - return -1; -} - -static __init int prepare_bpf_tests(void) -{ - int i; - - if (test_id >= 0) { - /* - * if a test_id was specified, use test_range to - * cover only that test. - */ - if (test_id >= ARRAY_SIZE(tests)) { - pr_err("test_bpf: invalid test_id specified.\n"); - return -EINVAL; - } - - test_range[0] = test_id; - test_range[1] = test_id; - } else if (*test_name) { - /* - * if a test_name was specified, find it and setup - * test_range to cover only that test. - */ - int idx = find_test_index(test_name); - - if (idx < 0) { - pr_err("test_bpf: no test named '%s' found.\n", - test_name); - return -EINVAL; - } - test_range[0] = idx; - test_range[1] = idx; - } else { - /* - * check that the supplied test_range is valid. - */ - if (test_range[0] >= ARRAY_SIZE(tests) || - test_range[1] >= ARRAY_SIZE(tests) || - test_range[0] < 0 || test_range[1] < 0) { - pr_err("test_bpf: test_range is out of bound.\n"); - return -EINVAL; - } - - if (test_range[1] < test_range[0]) { - pr_err("test_bpf: test_range is ending before it starts.\n"); - return -EINVAL; - } - } - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - if (tests[i].fill_helper && - tests[i].fill_helper(&tests[i]) < 0) - return -ENOMEM; - } - - return 0; -} - -static __init void destroy_bpf_tests(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - if (tests[i].fill_helper) - kfree(tests[i].u.ptr.insns); - } -} - static bool exclude_test(int test_id) { return test_id < test_range[0] || test_id > test_range[1]; @@ -8800,6 +14365,7 @@ static __init struct sk_buff *build_test_skb(void) skb_shinfo(skb[0])->gso_type |= SKB_GSO_DODGY; skb_shinfo(skb[0])->gso_segs = 0; skb_shinfo(skb[0])->frag_list = skb[1]; + skb_shinfo(skb[0])->hwtstamps.hwtstamp = 1000; /* adjust skb[0]'s len */ skb[0]->len += skb[1]->len; @@ -8924,6 +14490,10 @@ static __init int test_skb_segment(void) for (i = 0; i < ARRAY_SIZE(skb_segment_tests); i++) { const struct skb_segment_test *test = &skb_segment_tests[i]; + cond_resched(); + if (exclude_test(i)) + continue; + pr_info("#%d %s ", i, test->descr); if (test_skb_segment_single(test)) { @@ -8955,7 +14525,19 @@ static __init int test_bpf(void) pr_info("#%d %s ", i, tests[i].descr); + if (tests[i].fill_helper && + tests[i].fill_helper(&tests[i]) < 0) { + pr_cont("FAIL to prog_fill\n"); + continue; + } + fp = generate_filter(i, &err); + + if (tests[i].fill_helper) { + kfree(tests[i].u.ptr.insns); + tests[i].u.ptr.insns = NULL; + } + if (fp == NULL) { if (err == 0) { pass_cnt++; @@ -9020,6 +14602,30 @@ struct tail_call_test { offset, TAIL_CALL_MARKER), \ BPF_JMP_IMM(BPF_TAIL_CALL, 0, 0, 0) +/* + * A test function to be called from a BPF program, clobbering a lot of + * CPU registers in the process. A JITed BPF program calling this function + * must save and restore any caller-saved registers it uses for internal + * state, for example the current tail call count. + */ +BPF_CALL_1(bpf_test_func, u64, arg) +{ + char buf[64]; + long a = 0; + long b = 1; + long c = 2; + long d = 3; + long e = 4; + long f = 5; + long g = 6; + long h = 7; + + return snprintf(buf, sizeof(buf), + "%ld %lu %lx %ld %lu %lx %ld %lu %x", + a, b, c, d, e, f, g, h, (int)arg); +} +#define BPF_FUNC_test_func __BPF_FUNC_MAX_ID + /* * Tail call tests. Each test case may call any other test in the table, * including itself, specified as a relative index offset from the calling @@ -9077,7 +14683,29 @@ static struct tail_call_test tail_call_tests[] = { BPF_EXIT_INSN(), }, .flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE, - .result = (MAX_TAIL_CALL_CNT + 1 + 1) * MAX_TESTRUNS, + .result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS, + }, + { + "Tail call count preserved across function calls", + .insns = { + BPF_LDX_MEM(BPF_W, R2, R1, 0), + BPF_ALU64_IMM(BPF_ADD, R2, 1), + BPF_STX_MEM(BPF_W, R1, R2, 0), + BPF_STX_MEM(BPF_DW, R10, R1, -8), + BPF_CALL_REL(BPF_FUNC_get_numa_node_id), + BPF_CALL_REL(BPF_FUNC_ktime_get_ns), + BPF_CALL_REL(BPF_FUNC_ktime_get_boot_ns), + BPF_CALL_REL(BPF_FUNC_ktime_get_coarse_ns), + BPF_CALL_REL(BPF_FUNC_jiffies64), + BPF_CALL_REL(BPF_FUNC_test_func), + BPF_LDX_MEM(BPF_DW, R1, R10, -8), + BPF_ALU32_REG(BPF_MOV, R0, R1), + TAIL_CALL(0), + BPF_EXIT_INSN(), + }, + .stack_depth = 8, + .flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE, + .result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS, }, { "Tail call error path, NULL target", @@ -9157,17 +14785,19 @@ static __init int prepare_tail_call_tests(struct bpf_array **pprogs) /* Relocate runtime tail call offsets and addresses */ for (i = 0; i < len; i++) { struct bpf_insn *insn = &fp->insnsi[i]; - - if (insn->imm != TAIL_CALL_MARKER) - continue; + long addr = 0; switch (insn->code) { case BPF_LD | BPF_DW | BPF_IMM: + if (insn->imm != TAIL_CALL_MARKER) + break; insn[0].imm = (u32)(long)progs; insn[1].imm = ((u64)(long)progs) >> 32; break; case BPF_ALU | BPF_MOV | BPF_K: + if (insn->imm != TAIL_CALL_MARKER) + break; if (insn->off == TAIL_CALL_NULL) insn->imm = ntests; else if (insn->off == TAIL_CALL_INVALID) @@ -9175,6 +14805,38 @@ static __init int prepare_tail_call_tests(struct bpf_array **pprogs) else insn->imm = which + insn->off; insn->off = 0; + break; + + case BPF_JMP | BPF_CALL: + if (insn->src_reg != BPF_PSEUDO_CALL) + break; + switch (insn->imm) { + case BPF_FUNC_get_numa_node_id: + addr = (long)&numa_node_id; + break; + case BPF_FUNC_ktime_get_ns: + addr = (long)&ktime_get_ns; + break; + case BPF_FUNC_ktime_get_boot_ns: + addr = (long)&ktime_get_boot_fast_ns; + break; + case BPF_FUNC_ktime_get_coarse_ns: + addr = (long)&ktime_get_coarse_ns; + break; + case BPF_FUNC_jiffies64: + addr = (long)&get_jiffies_64; + break; + case BPF_FUNC_test_func: + addr = (long)&bpf_test_func; + break; + default: + err = -EFAULT; + goto out_err; + } + *insn = BPF_EMIT_CALL(addr); + if ((long)__bpf_call_base + insn->imm != addr) + *insn = BPF_JMP_A(0); /* Skip: NOP */ + break; } } @@ -9213,6 +14875,8 @@ static __init int test_tail_calls(struct bpf_array *progs) int ret; cond_resched(); + if (exclude_test(i)) + continue; pr_info("#%d %s ", i, test->descr); if (!fp) { @@ -9245,29 +14909,144 @@ static __init int test_tail_calls(struct bpf_array *progs) return err_cnt ? -EINVAL : 0; } +static char test_suite[32]; +module_param_string(test_suite, test_suite, sizeof(test_suite), 0); + +static __init int find_test_index(const char *test_name) +{ + int i; + + if (!strcmp(test_suite, "test_bpf")) { + for (i = 0; i < ARRAY_SIZE(tests); i++) { + if (!strcmp(tests[i].descr, test_name)) + return i; + } + } + + if (!strcmp(test_suite, "test_tail_calls")) { + for (i = 0; i < ARRAY_SIZE(tail_call_tests); i++) { + if (!strcmp(tail_call_tests[i].descr, test_name)) + return i; + } + } + + if (!strcmp(test_suite, "test_skb_segment")) { + for (i = 0; i < ARRAY_SIZE(skb_segment_tests); i++) { + if (!strcmp(skb_segment_tests[i].descr, test_name)) + return i; + } + } + + return -1; +} + +static __init int prepare_test_range(void) +{ + int valid_range; + + if (!strcmp(test_suite, "test_bpf")) + valid_range = ARRAY_SIZE(tests); + else if (!strcmp(test_suite, "test_tail_calls")) + valid_range = ARRAY_SIZE(tail_call_tests); + else if (!strcmp(test_suite, "test_skb_segment")) + valid_range = ARRAY_SIZE(skb_segment_tests); + else + return 0; + + if (test_id >= 0) { + /* + * if a test_id was specified, use test_range to + * cover only that test. + */ + if (test_id >= valid_range) { + pr_err("test_bpf: invalid test_id specified for '%s' suite.\n", + test_suite); + return -EINVAL; + } + + test_range[0] = test_id; + test_range[1] = test_id; + } else if (*test_name) { + /* + * if a test_name was specified, find it and setup + * test_range to cover only that test. + */ + int idx = find_test_index(test_name); + + if (idx < 0) { + pr_err("test_bpf: no test named '%s' found for '%s' suite.\n", + test_name, test_suite); + return -EINVAL; + } + test_range[0] = idx; + test_range[1] = idx; + } else if (test_range[0] != 0 || test_range[1] != INT_MAX) { + /* + * check that the supplied test_range is valid. + */ + if (test_range[0] < 0 || test_range[1] >= valid_range) { + pr_err("test_bpf: test_range is out of bound for '%s' suite.\n", + test_suite); + return -EINVAL; + } + + if (test_range[1] < test_range[0]) { + pr_err("test_bpf: test_range is ending before it starts.\n"); + return -EINVAL; + } + } + + return 0; +} + static int __init test_bpf_init(void) { struct bpf_array *progs = NULL; int ret; - ret = prepare_bpf_tests(); + if (strlen(test_suite) && + strcmp(test_suite, "test_bpf") && + strcmp(test_suite, "test_tail_calls") && + strcmp(test_suite, "test_skb_segment")) { + pr_err("test_bpf: invalid test_suite '%s' specified.\n", test_suite); + return -EINVAL; + } + + /* + * if test_suite is not specified, but test_id, test_name or test_range + * is specified, set 'test_bpf' as the default test suite. + */ + if (!strlen(test_suite) && + (test_id != -1 || strlen(test_name) || + (test_range[0] != 0 || test_range[1] != INT_MAX))) { + pr_info("test_bpf: set 'test_bpf' as the default test_suite.\n"); + strscpy(test_suite, "test_bpf", sizeof(test_suite)); + } + + ret = prepare_test_range(); if (ret < 0) return ret; - ret = test_bpf(); - destroy_bpf_tests(); - if (ret) - return ret; + if (!strlen(test_suite) || !strcmp(test_suite, "test_bpf")) { + ret = test_bpf(); + if (ret) + return ret; + } - ret = prepare_tail_call_tests(&progs); - if (ret) - return ret; - ret = test_tail_calls(progs); - destroy_tail_call_tests(progs); - if (ret) - return ret; + if (!strlen(test_suite) || !strcmp(test_suite, "test_tail_calls")) { + ret = prepare_tail_call_tests(&progs); + if (ret) + return ret; + ret = test_tail_calls(progs); + destroy_tail_call_tests(progs); + if (ret) + return ret; + } - return test_skb_segment(); + if (!strlen(test_suite) || !strcmp(test_suite, "test_skb_segment")) + return test_skb_segment(); + + return 0; } static void __exit test_bpf_exit(void) diff --git a/lib/test_hash.c b/lib/test_hash.c index 0ee40b4a56..bb25fda347 100644 --- a/lib/test_hash.c +++ b/lib/test_hash.c @@ -14,17 +14,15 @@ * and hash_64(). */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt "\n" - #include #include #include #include #include -#include +#include /* 32-bit XORSHIFT generator. Seed must not be zero. */ -static u32 __init __attribute_const__ +static u32 __attribute_const__ xorshift(u32 seed) { seed ^= seed << 13; @@ -34,7 +32,7 @@ xorshift(u32 seed) } /* Given a non-zero x, returns a non-zero byte. */ -static u8 __init __attribute_const__ +static u8 __attribute_const__ mod255(u32 x) { x = (x & 0xffff) + (x >> 16); /* 1 <= x <= 0x1fffe */ @@ -45,8 +43,7 @@ mod255(u32 x) } /* Fill the buffer with non-zero bytes. */ -static void __init -fill_buf(char *buf, size_t len, u32 seed) +static void fill_buf(char *buf, size_t len, u32 seed) { size_t i; @@ -56,6 +53,50 @@ fill_buf(char *buf, size_t len, u32 seed) } } +/* Holds most testing variables for the int test. */ +struct test_hash_params { + /* Pointer to integer to be hashed. */ + unsigned long long *h64; + /* Low 32-bits of integer to be hashed. */ + u32 h0; + /* Arch-specific hash result. */ + u32 h1; + /* Generic hash result. */ + u32 h2; + /* ORed hashes of given size (in bits). */ + u32 (*hash_or)[33]; +}; + +#ifdef HAVE_ARCH__HASH_32 +static void +test_int__hash_32(struct kunit *test, struct test_hash_params *params) +{ + params->hash_or[1][0] |= params->h2 = __hash_32_generic(params->h0); +#if HAVE_ARCH__HASH_32 == 1 + KUNIT_EXPECT_EQ_MSG(test, params->h1, params->h2, + "__hash_32(%#x) = %#x != __hash_32_generic() = %#x", + params->h0, params->h1, params->h2); +#endif +} +#endif + +#ifdef HAVE_ARCH_HASH_64 +static void +test_int_hash_64(struct kunit *test, struct test_hash_params *params, u32 const *m, int *k) +{ + params->h2 = hash_64_generic(*params->h64, *k); +#if HAVE_ARCH_HASH_64 == 1 + KUNIT_EXPECT_EQ_MSG(test, params->h1, params->h2, + "hash_64(%#llx, %d) = %#x != hash_64_generic() = %#x", + *params->h64, *k, params->h1, params->h2); +#else + KUNIT_EXPECT_LE_MSG(test, params->h1, params->h2, + "hash_64_generic(%#llx, %d) = %#x > %#x", + *params->h64, *k, params->h1, *m); +#endif +} +#endif + /* * Test the various integer hash functions. h64 (or its low-order bits) * is the integer to hash. hash_or accumulates the OR of the hash values, @@ -65,23 +106,16 @@ fill_buf(char *buf, size_t len, u32 seed) * inline, the code being tested is actually in the module, and you can * recompile and re-test the module without rebooting. */ -static bool __init -test_int_hash(unsigned long long h64, u32 hash_or[2][33]) +static void +test_int_hash(struct kunit *test, unsigned long long h64, u32 hash_or[2][33]) { int k; - u32 h0 = (u32)h64, h1, h2; + struct test_hash_params params = { &h64, (u32)h64, 0, 0, hash_or }; /* Test __hash32 */ - hash_or[0][0] |= h1 = __hash_32(h0); + hash_or[0][0] |= params.h1 = __hash_32(params.h0); #ifdef HAVE_ARCH__HASH_32 - hash_or[1][0] |= h2 = __hash_32_generic(h0); -#if HAVE_ARCH__HASH_32 == 1 - if (h1 != h2) { - pr_err("__hash_32(%#x) = %#x != __hash_32_generic() = %#x", - h0, h1, h2); - return false; - } -#endif + test_int__hash_32(test, ¶ms); #endif /* Test k = 1..32 bits */ @@ -89,63 +123,53 @@ test_int_hash(unsigned long long h64, u32 hash_or[2][33]) u32 const m = ((u32)2 << (k-1)) - 1; /* Low k bits set */ /* Test hash_32 */ - hash_or[0][k] |= h1 = hash_32(h0, k); - if (h1 > m) { - pr_err("hash_32(%#x, %d) = %#x > %#x", h0, k, h1, m); - return false; - } -#ifdef HAVE_ARCH_HASH_32 - h2 = hash_32_generic(h0, k); -#if HAVE_ARCH_HASH_32 == 1 - if (h1 != h2) { - pr_err("hash_32(%#x, %d) = %#x != hash_32_generic() " - " = %#x", h0, k, h1, h2); - return false; - } -#else - if (h2 > m) { - pr_err("hash_32_generic(%#x, %d) = %#x > %#x", - h0, k, h1, m); - return false; - } -#endif -#endif + hash_or[0][k] |= params.h1 = hash_32(params.h0, k); + KUNIT_EXPECT_LE_MSG(test, params.h1, m, + "hash_32(%#x, %d) = %#x > %#x", + params.h0, k, params.h1, m); + /* Test hash_64 */ - hash_or[1][k] |= h1 = hash_64(h64, k); - if (h1 > m) { - pr_err("hash_64(%#llx, %d) = %#x > %#x", h64, k, h1, m); - return false; - } + hash_or[1][k] |= params.h1 = hash_64(h64, k); + KUNIT_EXPECT_LE_MSG(test, params.h1, m, + "hash_64(%#llx, %d) = %#x > %#x", + h64, k, params.h1, m); #ifdef HAVE_ARCH_HASH_64 - h2 = hash_64_generic(h64, k); -#if HAVE_ARCH_HASH_64 == 1 - if (h1 != h2) { - pr_err("hash_64(%#llx, %d) = %#x != hash_64_generic() " - "= %#x", h64, k, h1, h2); - return false; - } -#else - if (h2 > m) { - pr_err("hash_64_generic(%#llx, %d) = %#x > %#x", - h64, k, h1, m); - return false; - } -#endif + test_int_hash_64(test, ¶ms, &m, &k); #endif } - - (void)h2; /* Suppress unused variable warning */ - return true; } #define SIZE 256 /* Run time is cubic in SIZE */ -static int __init -test_hash_init(void) +static void test_string_or(struct kunit *test) { char buf[SIZE+1]; - u32 string_or = 0, hash_or[2][33] = { { 0, } }; - unsigned tests = 0; + u32 string_or = 0; + int i, j; + + fill_buf(buf, SIZE, 1); + + /* Test every possible non-empty substring in the buffer. */ + for (j = SIZE; j > 0; --j) { + buf[j] = '\0'; + + for (i = 0; i <= j; i++) { + u32 h0 = full_name_hash(buf+i, buf+i, j-i); + + string_or |= h0; + } /* i */ + } /* j */ + + /* The OR of all the hash values should cover all the bits */ + KUNIT_EXPECT_EQ_MSG(test, string_or, -1u, + "OR of all string hash results = %#x != %#x", + string_or, -1u); +} + +static void test_hash_or(struct kunit *test) +{ + char buf[SIZE+1]; + u32 hash_or[2][33] = { { 0, } }; unsigned long long h64 = 0; int i, j; @@ -160,46 +184,27 @@ test_hash_init(void) u32 h0 = full_name_hash(buf+i, buf+i, j-i); /* Check that hashlen_string gets the length right */ - if (hashlen_len(hashlen) != j-i) { - pr_err("hashlen_string(%d..%d) returned length" - " %u, expected %d", - i, j, hashlen_len(hashlen), j-i); - return -EINVAL; - } + KUNIT_EXPECT_EQ_MSG(test, hashlen_len(hashlen), j-i, + "hashlen_string(%d..%d) returned length %u, expected %d", + i, j, hashlen_len(hashlen), j-i); /* Check that the hashes match */ - if (hashlen_hash(hashlen) != h0) { - pr_err("hashlen_string(%d..%d) = %08x != " - "full_name_hash() = %08x", - i, j, hashlen_hash(hashlen), h0); - return -EINVAL; - } + KUNIT_EXPECT_EQ_MSG(test, hashlen_hash(hashlen), h0, + "hashlen_string(%d..%d) = %08x != full_name_hash() = %08x", + i, j, hashlen_hash(hashlen), h0); - string_or |= h0; h64 = h64 << 32 | h0; /* For use with hash_64 */ - if (!test_int_hash(h64, hash_or)) - return -EINVAL; - tests++; + test_int_hash(test, h64, hash_or); } /* i */ } /* j */ - /* The OR of all the hash values should cover all the bits */ - if (~string_or) { - pr_err("OR of all string hash results = %#x != %#x", - string_or, -1u); - return -EINVAL; - } - if (~hash_or[0][0]) { - pr_err("OR of all __hash_32 results = %#x != %#x", - hash_or[0][0], -1u); - return -EINVAL; - } + KUNIT_EXPECT_EQ_MSG(test, hash_or[0][0], -1u, + "OR of all __hash_32 results = %#x != %#x", + hash_or[0][0], -1u); #ifdef HAVE_ARCH__HASH_32 #if HAVE_ARCH__HASH_32 != 1 /* Test is pointless if results match */ - if (~hash_or[1][0]) { - pr_err("OR of all __hash_32_generic results = %#x != %#x", - hash_or[1][0], -1u); - return -EINVAL; - } + KUNIT_EXPECT_EQ_MSG(test, hash_or[1][0], -1u, + "OR of all __hash_32_generic results = %#x != %#x", + hash_or[1][0], -1u); #endif #endif @@ -207,51 +212,27 @@ test_hash_init(void) for (i = 1; i <= 32; i++) { u32 const m = ((u32)2 << (i-1)) - 1; /* Low i bits set */ - if (hash_or[0][i] != m) { - pr_err("OR of all hash_32(%d) results = %#x " - "(%#x expected)", i, hash_or[0][i], m); - return -EINVAL; - } - if (hash_or[1][i] != m) { - pr_err("OR of all hash_64(%d) results = %#x " - "(%#x expected)", i, hash_or[1][i], m); - return -EINVAL; - } + KUNIT_EXPECT_EQ_MSG(test, hash_or[0][i], m, + "OR of all hash_32(%d) results = %#x (%#x expected)", + i, hash_or[0][i], m); + KUNIT_EXPECT_EQ_MSG(test, hash_or[1][i], m, + "OR of all hash_64(%d) results = %#x (%#x expected)", + i, hash_or[1][i], m); } - - /* Issue notices about skipped tests. */ -#ifdef HAVE_ARCH__HASH_32 -#if HAVE_ARCH__HASH_32 != 1 - pr_info("__hash_32() is arch-specific; not compared to generic."); -#endif -#else - pr_info("__hash_32() has no arch implementation to test."); -#endif -#ifdef HAVE_ARCH_HASH_32 -#if HAVE_ARCH_HASH_32 != 1 - pr_info("hash_32() is arch-specific; not compared to generic."); -#endif -#else - pr_info("hash_32() has no arch implementation to test."); -#endif -#ifdef HAVE_ARCH_HASH_64 -#if HAVE_ARCH_HASH_64 != 1 - pr_info("hash_64() is arch-specific; not compared to generic."); -#endif -#else - pr_info("hash_64() has no arch implementation to test."); -#endif - - pr_notice("%u tests passed.", tests); - - return 0; } -static void __exit test_hash_exit(void) -{ -} +static struct kunit_case hash_test_cases[] __refdata = { + KUNIT_CASE(test_string_or), + KUNIT_CASE(test_hash_or), + {} +}; -module_init(test_hash_init); /* Does everything */ -module_exit(test_hash_exit); /* Does nothing */ +static struct kunit_suite hash_test_suite = { + .name = "hash", + .test_cases = hash_test_cases, +}; + + +kunit_test_suite(hash_test_suite); MODULE_LICENSE("GPL"); diff --git a/lib/test_hmm.c b/lib/test_hmm.c index ac794e3540..767538089a 100644 --- a/lib/test_hmm.c +++ b/lib/test_hmm.c @@ -613,8 +613,7 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args, */ rpage->zone_device_data = dmirror; - *dst = migrate_pfn(page_to_pfn(dpage)) | - MIGRATE_PFN_LOCKED; + *dst = migrate_pfn(page_to_pfn(dpage)); if ((*src & MIGRATE_PFN_WRITE) || (!spage && args->vma->vm_flags & VM_WRITE)) *dst |= MIGRATE_PFN_WRITE; @@ -1161,7 +1160,7 @@ static vm_fault_t dmirror_devmem_fault_alloc_and_copy(struct migrate_vma *args, lock_page(dpage); xa_erase(&dmirror->pt, addr >> PAGE_SHIFT); copy_highpage(dpage, spage); - *dst = migrate_pfn(page_to_pfn(dpage)) | MIGRATE_PFN_LOCKED; + *dst = migrate_pfn(page_to_pfn(dpage)); if (*src & MIGRATE_PFN_WRITE) *dst |= MIGRATE_PFN_WRITE; } diff --git a/lib/test_kasan.c b/lib/test_kasan.c index 8835e07845..3b413f8c8a 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -88,7 +88,7 @@ static void kasan_test_exit(struct kunit *test) */ #define KUNIT_EXPECT_KASAN_FAIL(test, expression) do { \ if (IS_ENABLED(CONFIG_KASAN_HW_TAGS) && \ - !kasan_async_mode_enabled()) \ + kasan_sync_fault_possible()) \ migrate_disable(); \ KUNIT_EXPECT_FALSE(test, READ_ONCE(fail_data.report_found)); \ barrier(); \ @@ -440,6 +440,7 @@ static void kmalloc_oob_memset_2(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, memset(ptr + size - 1, 0, 2)); kfree(ptr); } @@ -452,6 +453,7 @@ static void kmalloc_oob_memset_4(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, memset(ptr + size - 3, 0, 4)); kfree(ptr); } @@ -464,6 +466,7 @@ static void kmalloc_oob_memset_8(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, memset(ptr + size - 7, 0, 8)); kfree(ptr); } @@ -476,6 +479,7 @@ static void kmalloc_oob_memset_16(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, memset(ptr + size - 15, 0, 16)); kfree(ptr); } @@ -488,16 +492,18 @@ static void kmalloc_oob_in_memset(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, memset(ptr, 0, size + KASAN_GRANULE_SIZE)); kfree(ptr); } -static void kmalloc_memmove_invalid_size(struct kunit *test) +static void kmalloc_memmove_negative_size(struct kunit *test) { char *ptr; size_t size = 64; - volatile size_t invalid_size = -2; + size_t invalid_size = -2; /* * Hardware tag-based mode doesn't check memmove for negative size. @@ -510,6 +516,24 @@ static void kmalloc_memmove_invalid_size(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); memset((char *)ptr, 0, 64); + OPTIMIZER_HIDE_VAR(ptr); + OPTIMIZER_HIDE_VAR(invalid_size); + KUNIT_EXPECT_KASAN_FAIL(test, + memmove((char *)ptr, (char *)ptr + 4, invalid_size)); + kfree(ptr); +} + +static void kmalloc_memmove_invalid_size(struct kunit *test) +{ + char *ptr; + size_t size = 64; + volatile size_t invalid_size = size; + + ptr = kmalloc(size, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + + memset((char *)ptr, 0, 64); + OPTIMIZER_HIDE_VAR(ptr); KUNIT_EXPECT_KASAN_FAIL(test, memmove((char *)ptr, (char *)ptr + 4, invalid_size)); kfree(ptr); @@ -679,7 +703,7 @@ static void kmem_cache_bulk(struct kunit *test) static char global_array[10]; -static void kasan_global_oob(struct kunit *test) +static void kasan_global_oob_right(struct kunit *test) { /* * Deliberate out-of-bounds access. To prevent CONFIG_UBSAN_LOCAL_BOUNDS @@ -702,6 +726,20 @@ static void kasan_global_oob(struct kunit *test) KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p); } +static void kasan_global_oob_left(struct kunit *test) +{ + char *volatile array = global_array; + char *p = array - 3; + + /* + * GCC is known to fail this test, skip it. + * See https://bugzilla.kernel.org/show_bug.cgi?id=215051. + */ + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_CC_IS_CLANG); + KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_KASAN_GENERIC); + KUNIT_EXPECT_KASAN_FAIL(test, *(volatile char *)p); +} + /* Check that ksize() makes the whole object accessible. */ static void ksize_unpoisons_memory(struct kunit *test) { @@ -831,6 +869,19 @@ static void kmem_cache_invalid_free(struct kunit *test) kmem_cache_destroy(cache); } +static void empty_cache_ctor(void *object) { } + +static void kmem_cache_double_destroy(struct kunit *test) +{ + struct kmem_cache *cache; + + /* Provide a constructor to prevent cache merging. */ + cache = kmem_cache_create("test_cache", 200, 0, 0, empty_cache_ctor); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cache); + kmem_cache_destroy(cache); + KUNIT_EXPECT_KASAN_FAIL(test, kmem_cache_destroy(cache)); +} + static void kasan_memchr(struct kunit *test) { char *ptr; @@ -848,6 +899,8 @@ static void kasan_memchr(struct kunit *test) ptr = kmalloc(size, GFP_KERNEL | __GFP_ZERO); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); + OPTIMIZER_HIDE_VAR(ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, kasan_ptr_result = memchr(ptr, '1', size + 1)); @@ -873,6 +926,8 @@ static void kasan_memcmp(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ptr); memset(arr, 0, sizeof(arr)); + OPTIMIZER_HIDE_VAR(ptr); + OPTIMIZER_HIDE_VAR(size); KUNIT_EXPECT_KASAN_FAIL(test, kasan_int_result = memcmp(ptr, arr, size+1)); kfree(ptr); @@ -1129,6 +1184,7 @@ static struct kunit_case kasan_kunit_test_cases[] = { KUNIT_CASE(kmalloc_oob_memset_4), KUNIT_CASE(kmalloc_oob_memset_8), KUNIT_CASE(kmalloc_oob_memset_16), + KUNIT_CASE(kmalloc_memmove_negative_size), KUNIT_CASE(kmalloc_memmove_invalid_size), KUNIT_CASE(kmalloc_uaf), KUNIT_CASE(kmalloc_uaf_memset), @@ -1138,7 +1194,8 @@ static struct kunit_case kasan_kunit_test_cases[] = { KUNIT_CASE(kmem_cache_oob), KUNIT_CASE(kmem_cache_accounted), KUNIT_CASE(kmem_cache_bulk), - KUNIT_CASE(kasan_global_oob), + KUNIT_CASE(kasan_global_oob_right), + KUNIT_CASE(kasan_global_oob_left), KUNIT_CASE(kasan_stack_oob), KUNIT_CASE(kasan_alloca_oob_left), KUNIT_CASE(kasan_alloca_oob_right), @@ -1146,6 +1203,7 @@ static struct kunit_case kasan_kunit_test_cases[] = { KUNIT_CASE(ksize_uaf), KUNIT_CASE(kmem_cache_double_free), KUNIT_CASE(kmem_cache_invalid_free), + KUNIT_CASE(kmem_cache_double_destroy), KUNIT_CASE(kasan_memchr), KUNIT_CASE(kasan_memcmp), KUNIT_CASE(kasan_strings), diff --git a/lib/test_kasan_module.c b/lib/test_kasan_module.c index 7ebf433ede..b112cbc835 100644 --- a/lib/test_kasan_module.c +++ b/lib/test_kasan_module.c @@ -35,6 +35,8 @@ static noinline void __init copy_user_test(void) return; } + OPTIMIZER_HIDE_VAR(size); + pr_info("out-of-bounds in copy_from_user()\n"); unused = copy_from_user(kmem, usermem, size + 1); diff --git a/lib/test_printf.c b/lib/test_printf.c index 55082432f3..07309c45f3 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -586,70 +586,59 @@ struct page_flags_test { int width; int shift; int mask; - unsigned long value; const char *fmt; const char *name; }; -static struct page_flags_test pft[] = { +static const struct page_flags_test pft[] = { {SECTIONS_WIDTH, SECTIONS_PGSHIFT, SECTIONS_MASK, - 0, "%d", "section"}, + "%d", "section"}, {NODES_WIDTH, NODES_PGSHIFT, NODES_MASK, - 0, "%d", "node"}, + "%d", "node"}, {ZONES_WIDTH, ZONES_PGSHIFT, ZONES_MASK, - 0, "%d", "zone"}, + "%d", "zone"}, {LAST_CPUPID_WIDTH, LAST_CPUPID_PGSHIFT, LAST_CPUPID_MASK, - 0, "%#x", "lastcpupid"}, + "%#x", "lastcpupid"}, {KASAN_TAG_WIDTH, KASAN_TAG_PGSHIFT, KASAN_TAG_MASK, - 0, "%#x", "kasantag"}, + "%#x", "kasantag"}, }; static void __init page_flags_test(int section, int node, int zone, int last_cpupid, - int kasan_tag, int flags, const char *name, char *cmp_buf) + int kasan_tag, unsigned long flags, const char *name, + char *cmp_buf) { unsigned long values[] = {section, node, zone, last_cpupid, kasan_tag}; - unsigned long page_flags = 0; - unsigned long size = 0; + unsigned long size; bool append = false; int i; - flags &= PAGEFLAGS_MASK; - if (flags) { - page_flags |= flags; - snprintf(cmp_buf + size, BUF_SIZE - size, "%s", name); - size = strlen(cmp_buf); -#if SECTIONS_WIDTH || NODES_WIDTH || ZONES_WIDTH || \ - LAST_CPUPID_WIDTH || KASAN_TAG_WIDTH - /* Other information also included in page flags */ - snprintf(cmp_buf + size, BUF_SIZE - size, "|"); - size = strlen(cmp_buf); -#endif - } + for (i = 0; i < ARRAY_SIZE(values); i++) + flags |= (values[i] & pft[i].mask) << pft[i].shift; - /* Set the test value */ - for (i = 0; i < ARRAY_SIZE(pft); i++) - pft[i].value = values[i]; + size = scnprintf(cmp_buf, BUF_SIZE, "%#lx(", flags); + if (flags & PAGEFLAGS_MASK) { + size += scnprintf(cmp_buf + size, BUF_SIZE - size, "%s", name); + append = true; + } for (i = 0; i < ARRAY_SIZE(pft); i++) { if (!pft[i].width) continue; - if (append) { - snprintf(cmp_buf + size, BUF_SIZE - size, "|"); - size = strlen(cmp_buf); - } + if (append) + size += scnprintf(cmp_buf + size, BUF_SIZE - size, "|"); - page_flags |= (pft[i].value & pft[i].mask) << pft[i].shift; - snprintf(cmp_buf + size, BUF_SIZE - size, "%s=", pft[i].name); - size = strlen(cmp_buf); - snprintf(cmp_buf + size, BUF_SIZE - size, pft[i].fmt, - pft[i].value & pft[i].mask); - size = strlen(cmp_buf); + size += scnprintf(cmp_buf + size, BUF_SIZE - size, "%s=", + pft[i].name); + size += scnprintf(cmp_buf + size, BUF_SIZE - size, pft[i].fmt, + values[i] & pft[i].mask); append = true; } - test(cmp_buf, "%pGp", &page_flags); + snprintf(cmp_buf + size, BUF_SIZE - size, ")"); + + test(cmp_buf, "%pGp", &flags); } static void __init diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c index 3750323973..a5a3d6c27e 100644 --- a/lib/test_sysctl.c +++ b/lib/test_sysctl.c @@ -128,26 +128,6 @@ static struct ctl_table test_table[] = { { } }; -static struct ctl_table test_sysctl_table[] = { - { - .procname = "test_sysctl", - .maxlen = 0, - .mode = 0555, - .child = test_table, - }, - { } -}; - -static struct ctl_table test_sysctl_root_table[] = { - { - .procname = "debug", - .maxlen = 0, - .mode = 0555, - .child = test_sysctl_table, - }, - { } -}; - static struct ctl_table_header *test_sysctl_header; static int __init test_sysctl_init(void) @@ -155,7 +135,7 @@ static int __init test_sysctl_init(void) test_data.bitmap_0001 = kzalloc(SYSCTL_TEST_BITMAP_SIZE/8, GFP_KERNEL); if (!test_data.bitmap_0001) return -ENOMEM; - test_sysctl_header = register_sysctl_table(test_sysctl_root_table); + test_sysctl_header = register_sysctl("debug/test_sysctl", test_table); if (!test_sysctl_header) { kfree(test_data.bitmap_0001); return -ENOMEM; diff --git a/lib/test_ubsan.c b/lib/test_ubsan.c index 7e7bbd0f3f..2062be1f2e 100644 --- a/lib/test_ubsan.c +++ b/lib/test_ubsan.c @@ -79,15 +79,6 @@ static void test_ubsan_load_invalid_value(void) eval2 = eval; } -static void test_ubsan_null_ptr_deref(void) -{ - volatile int *ptr = NULL; - int val; - - UBSAN_TEST(CONFIG_UBSAN_OBJECT_SIZE); - val = *ptr; -} - static void test_ubsan_misaligned_access(void) { volatile char arr[5] __aligned(4) = {1, 2, 3, 4, 5}; @@ -98,29 +89,16 @@ static void test_ubsan_misaligned_access(void) *ptr = val; } -static void test_ubsan_object_size_mismatch(void) -{ - /* "((aligned(8)))" helps this not into be misaligned for ptr-access. */ - volatile int val __aligned(8) = 4; - volatile long long *ptr, val2; - - UBSAN_TEST(CONFIG_UBSAN_OBJECT_SIZE); - ptr = (long long *)&val; - val2 = *ptr; -} - static const test_ubsan_fp test_ubsan_array[] = { test_ubsan_shift_out_of_bounds, test_ubsan_out_of_bounds, test_ubsan_load_invalid_value, test_ubsan_misaligned_access, - test_ubsan_object_size_mismatch, }; /* Excluded because they Oops the module. */ static const test_ubsan_fp skip_ubsan_array[] = { test_ubsan_divrem_overflow, - test_ubsan_null_ptr_deref, }; static int __init test_ubsan_init(void) diff --git a/lib/test_vmalloc.c b/lib/test_vmalloc.c index e14993bc84..cf41fd6df4 100644 --- a/lib/test_vmalloc.c +++ b/lib/test_vmalloc.c @@ -393,7 +393,7 @@ static struct test_driver { static void shuffle_array(int *arr, int n) { unsigned int rnd; - int i, j, x; + int i, j; for (i = n - 1; i > 0; i--) { get_random_bytes(&rnd, sizeof(rnd)); @@ -402,9 +402,7 @@ static void shuffle_array(int *arr, int n) j = rnd % i; /* Swap indexes. */ - x = arr[i]; - arr[i] = arr[j]; - arr[j] = x; + swap(arr[i], arr[j]); } } diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d7ad44f2c8..3b8129dd37 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -53,8 +53,7 @@ #include #include "kstrtox.h" -static unsigned long long simple_strntoull(const char *startp, size_t max_chars, - char **endp, unsigned int base) +static noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base) { const char *cp; unsigned long long result = 0ULL; @@ -408,8 +407,9 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width) #define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */ #define SPECIAL 64 /* prefix hex with "0x", octal with "0" */ +static_assert(SIGN == 1); static_assert(ZEROPAD == ('0' - ' ')); -static_assert(SMALL == ' '); +static_assert(SMALL == ('a' ^ 'A')); enum format_type { FORMAT_TYPE_NONE, /* Just a string part */ @@ -1241,20 +1241,13 @@ char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap, struct printf_spec spec, const char *fmt) { int nr_bits = max_t(int, spec.field_width, 0); - /* current bit is 'cur', most recently seen range is [rbot, rtop] */ - int cur, rbot, rtop; bool first = true; + int rbot, rtop; if (check_pointer(&buf, end, bitmap, spec)) return buf; - rbot = cur = find_first_bit(bitmap, nr_bits); - while (cur < nr_bits) { - rtop = cur; - cur = find_next_bit(bitmap, nr_bits, cur + 1); - if (cur < nr_bits && cur <= rtop + 1) - continue; - + for_each_set_bitrange(rbot, rtop, bitmap, nr_bits) { if (!first) { if (buf < end) *buf = ','; @@ -1263,15 +1256,12 @@ char *bitmap_list_string(char *buf, char *end, unsigned long *bitmap, first = false; buf = number(buf, end, rbot, default_dec_spec); - if (rbot < rtop) { - if (buf < end) - *buf = '-'; - buf++; + if (rtop == rbot + 1) + continue; - buf = number(buf, end, rtop, default_dec_spec); - } - - rbot = cur; + if (buf < end) + *buf = '-'; + buf = number(++buf, end, rtop - 1, default_dec_spec); } return buf; } @@ -2023,6 +2013,11 @@ char *format_page_flags(char *buf, char *end, unsigned long flags) bool append = false; int i; + buf = number(buf, end, flags, default_flag_spec); + if (buf < end) + *buf = '('; + buf++; + /* Page flags from the main area. */ if (main_flags) { buf = format_flags(buf, end, main_flags, pageflag_names); @@ -2051,6 +2046,9 @@ char *format_page_flags(char *buf, char *end, unsigned long flags) append = true; } + if (buf < end) + *buf = ')'; + buf++; return buf; } @@ -3556,7 +3554,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args) ++fmt; for ( ; *fmt && *fmt != ']'; ++fmt, ++len) - set_bit((u8)*fmt, set); + __set_bit((u8)*fmt, set); /* no ']' or no character set found */ if (!*fmt || !len) @@ -3566,7 +3564,7 @@ int vsscanf(const char *buf, const char *fmt, va_list args) if (negate) { bitmap_complement(set, set, 256); /* exclude null '\0' byte */ - clear_bit(0, set); + __clear_bit(0, set); } /* match must be non-empty */ diff --git a/lib/xarray.c b/lib/xarray.c index f5d8f54907..6f47f63758 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -157,7 +157,7 @@ static void xas_move_index(struct xa_state *xas, unsigned long offset) xas->xa_index += offset << shift; } -static void xas_advance(struct xa_state *xas) +static void xas_next_offset(struct xa_state *xas) { xas->xa_offset++; xas_move_index(xas, xas->xa_offset); @@ -1250,7 +1250,7 @@ void *xas_find(struct xa_state *xas, unsigned long max) xas->xa_offset = ((xas->xa_index - 1) & XA_CHUNK_MASK) + 1; } - xas_advance(xas); + xas_next_offset(xas); while (xas->xa_node && (xas->xa_index <= max)) { if (unlikely(xas->xa_offset == XA_CHUNK_SIZE)) { @@ -1268,7 +1268,7 @@ void *xas_find(struct xa_state *xas, unsigned long max) if (entry && !xa_is_sibling(entry)) return entry; - xas_advance(xas); + xas_next_offset(xas); } if (!xas->xa_node) diff --git a/lib/xz/Kconfig b/lib/xz/Kconfig index 5cb50245a8..adce22ac18 100644 --- a/lib/xz/Kconfig +++ b/lib/xz/Kconfig @@ -39,6 +39,19 @@ config XZ_DEC_SPARC default y select XZ_DEC_BCJ +config XZ_DEC_MICROLZMA + bool "MicroLZMA decoder" + default n + help + MicroLZMA is a header format variant where the first byte + of a raw LZMA stream (without the end of stream marker) has + been replaced with a bitwise-negation of the lc/lp/pb + properties byte. MicroLZMA was created to be used in EROFS + but can be used by other things too where wasting minimal + amount of space for headers is important. + + Unless you know that you need this, say N. + endif config XZ_DEC_BCJ diff --git a/lib/xz/xz_dec_lzma2.c b/lib/xz/xz_dec_lzma2.c index d548cf0e59..27ce34520e 100644 --- a/lib/xz/xz_dec_lzma2.c +++ b/lib/xz/xz_dec_lzma2.c @@ -248,6 +248,10 @@ struct lzma2_dec { * before the first LZMA chunk. */ bool need_props; + +#ifdef XZ_DEC_MICROLZMA + bool pedantic_microlzma; +#endif }; struct xz_dec_lzma2 { @@ -419,6 +423,12 @@ static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, } } +#ifdef XZ_DEC_MICROLZMA +# define DICT_FLUSH_SUPPORTS_SKIPPING true +#else +# define DICT_FLUSH_SUPPORTS_SKIPPING false +#endif + /* * Flush pending data from dictionary to b->out. It is assumed that there is * enough space in b->out. This is guaranteed because caller uses dict_limit() @@ -437,9 +447,14 @@ static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b) * decompression because in multi-call mode dict->buf * has been allocated by us in this file; it's not * provided by the caller like in single-call mode. + * + * With MicroLZMA, b->out can be NULL to skip bytes that + * the caller doesn't need. This cannot be done with XZ + * because it would break BCJ filters. */ - memcpy(b->out + b->out_pos, dict->buf + dict->start, - copy_size); + if (!DICT_FLUSH_SUPPORTS_SKIPPING || b->out != NULL) + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); } dict->start = dict->pos; @@ -505,7 +520,7 @@ static __always_inline void rc_normalize(struct rc_dec *rc) * functions so that the compiler is supposed to be able to more easily avoid * an extra branch. In this particular version of the LZMA decoder, this * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 - * on x86). Using a non-splitted version results in nicer looking code too. + * on x86). Using a non-split version results in nicer looking code too. * * NOTE: This must return an int. Do not make it return a bool or the speed * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, @@ -791,6 +806,7 @@ static void lzma_reset(struct xz_dec_lzma2 *s) s->lzma.rep1 = 0; s->lzma.rep2 = 0; s->lzma.rep3 = 0; + s->lzma.len = 0; /* * All probabilities are initialized to the same value. This hack @@ -1174,8 +1190,6 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props) } } - s->lzma.len = 0; - s->lzma2.sequence = SEQ_CONTROL; s->lzma2.need_dict_reset = true; @@ -1191,3 +1205,140 @@ XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s) kfree(s); } + +#ifdef XZ_DEC_MICROLZMA +/* This is a wrapper struct to have a nice struct name in the public API. */ +struct xz_dec_microlzma { + struct xz_dec_lzma2 s; +}; + +enum xz_ret xz_dec_microlzma_run(struct xz_dec_microlzma *s_ptr, + struct xz_buf *b) +{ + struct xz_dec_lzma2 *s = &s_ptr->s; + + /* + * sequence is SEQ_PROPERTIES before the first input byte, + * SEQ_LZMA_PREPARE until a total of five bytes have been read, + * and SEQ_LZMA_RUN for the rest of the input stream. + */ + if (s->lzma2.sequence != SEQ_LZMA_RUN) { + if (s->lzma2.sequence == SEQ_PROPERTIES) { + /* One byte is needed for the props. */ + if (b->in_pos >= b->in_size) + return XZ_OK; + + /* + * Don't increment b->in_pos here. The same byte is + * also passed to rc_read_init() which will ignore it. + */ + if (!lzma_props(s, ~b->in[b->in_pos])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + } + + /* + * xz_dec_microlzma_reset() doesn't validate the compressed + * size so we do it here. We have to limit the maximum size + * to avoid integer overflows in lzma2_lzma(). 3 GiB is a nice + * round number and much more than users of this code should + * ever need. + */ + if (s->lzma2.compressed < RC_INIT_BYTES + || s->lzma2.compressed > (3U << 30)) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + dict_reset(&s->dict, b); + } + + /* This is to allow increasing b->out_size between calls. */ + if (DEC_IS_SINGLE(s->dict.mode)) + s->dict.end = b->out_size - b->out_pos; + + while (true) { + dict_limit(&s->dict, min_t(size_t, b->out_size - b->out_pos, + s->lzma2.uncompressed)); + + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) { + if (s->lzma2.pedantic_microlzma) { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + } + + return XZ_STREAM_END; + } + + if (b->out_pos == b->out_size) + return XZ_OK; + + if (b->in_pos == b->in_size + && s->temp.size < s->lzma2.compressed) + return XZ_OK; + } +} + +struct xz_dec_microlzma *xz_dec_microlzma_alloc(enum xz_mode mode, + uint32_t dict_size) +{ + struct xz_dec_microlzma *s; + + /* Restrict dict_size to the same range as in the LZMA2 code. */ + if (dict_size < 4096 || dict_size > (3U << 30)) + return NULL; + + s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->s.dict.mode = mode; + s->s.dict.size = dict_size; + + if (DEC_IS_MULTI(mode)) { + s->s.dict.end = dict_size; + + s->s.dict.buf = vmalloc(dict_size); + if (s->s.dict.buf == NULL) { + kfree(s); + return NULL; + } + } + + return s; +} + +void xz_dec_microlzma_reset(struct xz_dec_microlzma *s, uint32_t comp_size, + uint32_t uncomp_size, int uncomp_size_is_exact) +{ + /* + * comp_size is validated in xz_dec_microlzma_run(). + * uncomp_size can safely be anything. + */ + s->s.lzma2.compressed = comp_size; + s->s.lzma2.uncompressed = uncomp_size; + s->s.lzma2.pedantic_microlzma = uncomp_size_is_exact; + + s->s.lzma2.sequence = SEQ_PROPERTIES; + s->s.temp.size = 0; +} + +void xz_dec_microlzma_end(struct xz_dec_microlzma *s) +{ + if (DEC_IS_MULTI(s->s.dict.mode)) + vfree(s->s.dict.buf); + + kfree(s); +} +#endif diff --git a/lib/xz/xz_dec_syms.c b/lib/xz/xz_dec_syms.c index 32eb3c03ae..61098c67a4 100644 --- a/lib/xz/xz_dec_syms.c +++ b/lib/xz/xz_dec_syms.c @@ -15,8 +15,15 @@ EXPORT_SYMBOL(xz_dec_reset); EXPORT_SYMBOL(xz_dec_run); EXPORT_SYMBOL(xz_dec_end); +#ifdef CONFIG_XZ_DEC_MICROLZMA +EXPORT_SYMBOL(xz_dec_microlzma_alloc); +EXPORT_SYMBOL(xz_dec_microlzma_reset); +EXPORT_SYMBOL(xz_dec_microlzma_run); +EXPORT_SYMBOL(xz_dec_microlzma_end); +#endif + MODULE_DESCRIPTION("XZ decompressor"); -MODULE_VERSION("1.0"); +MODULE_VERSION("1.1"); MODULE_AUTHOR("Lasse Collin and Igor Pavlov"); /* diff --git a/lib/xz/xz_private.h b/lib/xz/xz_private.h index 09360ebb51..bf1e94ec78 100644 --- a/lib/xz/xz_private.h +++ b/lib/xz/xz_private.h @@ -37,6 +37,9 @@ # ifdef CONFIG_XZ_DEC_SPARC # define XZ_DEC_SPARC # endif +# ifdef CONFIG_XZ_DEC_MICROLZMA +# define XZ_DEC_MICROLZMA +# endif # define memeq(a, b, size) (memcmp(a, b, size) == 0) # define memzero(buf, size) memset(buf, 0, size) # endif diff --git a/lib/zstd/Makefile b/lib/zstd/Makefile index f5d778e7e5..fc45339fc3 100644 --- a/lib/zstd/Makefile +++ b/lib/zstd/Makefile @@ -1,10 +1,44 @@ -# SPDX-License-Identifier: GPL-2.0-only +# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +# ################################################################ +# Copyright (c) Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under both the BSD-style license (found in the +# LICENSE file in the root directory of this source tree) and the GPLv2 (found +# in the COPYING file in the root directory of this source tree). +# You may select, at your option, one of the above-listed licenses. +# ################################################################ obj-$(CONFIG_ZSTD_COMPRESS) += zstd_compress.o obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd_decompress.o -ccflags-y += -O3 +zstd_compress-y := \ + zstd_compress_module.o \ + common/debug.o \ + common/entropy_common.o \ + common/error_private.o \ + common/fse_decompress.o \ + common/zstd_common.o \ + compress/fse_compress.o \ + compress/hist.o \ + compress/huf_compress.o \ + compress/zstd_compress.o \ + compress/zstd_compress_literals.o \ + compress/zstd_compress_sequences.o \ + compress/zstd_compress_superblock.o \ + compress/zstd_double_fast.o \ + compress/zstd_fast.o \ + compress/zstd_lazy.o \ + compress/zstd_ldm.o \ + compress/zstd_opt.o \ -zstd_compress-y := fse_compress.o huf_compress.o compress.o \ - entropy_common.o fse_decompress.o zstd_common.o -zstd_decompress-y := huf_decompress.o decompress.o \ - entropy_common.o fse_decompress.o zstd_common.o +zstd_decompress-y := \ + zstd_decompress_module.o \ + common/debug.o \ + common/entropy_common.o \ + common/error_private.o \ + common/fse_decompress.o \ + common/zstd_common.o \ + decompress/huf_decompress.o \ + decompress/zstd_ddict.o \ + decompress/zstd_decompress.o \ + decompress/zstd_decompress_block.o \ diff --git a/scripts/.gitignore b/scripts/.gitignore index e83c620ef5..eed308bef6 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only /asn1_compiler /bin2c -/extract-cert /insert-sys-cert /kallsyms /module.lds diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index cdec220884..3514c2149e 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -195,53 +195,6 @@ why = \ echo-why = $(call escsq, $(strip $(why))) endif -############################################################################### -# -# When a Kconfig string contains a filename, it is suitable for -# passing to shell commands. It is surrounded by double-quotes, and -# any double-quotes or backslashes within it are escaped by -# backslashes. -# -# This is no use for dependencies or $(wildcard). We need to strip the -# surrounding quotes and the escaping from quotes and backslashes, and -# we *do* need to escape any spaces in the string. So, for example: -# -# Usage: $(eval $(call config_filename,FOO)) -# -# Defines FOO_FILENAME based on the contents of the CONFIG_FOO option, -# transformed as described above to be suitable for use within the -# makefile. -# -# Also, if the filename is a relative filename and exists in the source -# tree but not the build tree, define FOO_SRCPREFIX as $(srctree)/ to -# be prefixed to *both* command invocation and dependencies. -# -# Note: We also print the filenames in the quiet_cmd_foo text, and -# perhaps ought to have a version specially escaped for that purpose. -# But it's only cosmetic, and $(patsubst "%",%,$(CONFIG_FOO)) is good -# enough. It'll strip the quotes in the common case where there's no -# space and it's a simple filename, and it'll retain the quotes when -# there's a space. There are some esoteric cases in which it'll print -# the wrong thing, but we don't really care. The actual dependencies -# and commands *do* get it right, with various combinations of single -# and double quotes, backslashes and spaces in the filenames. -# -############################################################################### -# -define config_filename -ifneq ($$(CONFIG_$(1)),"") -$(1)_FILENAME := $$(subst \\,\,$$(subst \$$(quote),$$(quote),$$(subst $$(space_escape),\$$(space),$$(patsubst "%",%,$$(subst $$(space),$$(space_escape),$$(CONFIG_$(1))))))) -ifneq ($$(patsubst /%,%,$$(firstword $$($(1)_FILENAME))),$$(firstword $$($(1)_FILENAME))) -else -ifeq ($$(wildcard $$($(1)_FILENAME)),) -ifneq ($$(wildcard $$(srctree)/$$($(1)_FILENAME)),) -$(1)_SRCPREFIX := $(srctree)/ -endif -endif -endif -endif -endef -# ############################################################################### # delete partially updated (i.e. corrupted) files on error diff --git a/scripts/Makefile b/scripts/Makefile index 9adb6d2478..ce5aa9030b 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -3,25 +3,19 @@ # scripts contains sources for various helper programs used throughout # the kernel for the build process. -CRYPTO_LIBS = $(shell pkg-config --libs libcrypto 2> /dev/null || echo -lcrypto) -CRYPTO_CFLAGS = $(shell pkg-config --cflags libcrypto 2> /dev/null) - hostprogs-always-$(CONFIG_BUILD_BIN2C) += bin2c hostprogs-always-$(CONFIG_KALLSYMS) += kallsyms hostprogs-always-$(BUILD_C_RECORDMCOUNT) += recordmcount hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable hostprogs-always-$(CONFIG_ASN1) += asn1_compiler hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file -hostprogs-always-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert -hostprogs-always-$(CONFIG_SYSTEM_REVOCATION_LIST) += extract-cert HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include +HOSTLDLIBS_sorttable = -lpthread HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include -HOSTCFLAGS_sign-file.o = $(CRYPTO_CFLAGS) -HOSTLDLIBS_sign-file = $(CRYPTO_LIBS) -HOSTCFLAGS_extract-cert.o = $(CRYPTO_CFLAGS) -HOSTLDLIBS_extract-cert = $(CRYPTO_LIBS) +HOSTCFLAGS_sign-file.o = $(shell pkg-config --cflags libcrypto 2> /dev/null) +HOSTLDLIBS_sign-file = $(shell pkg-config --libs libcrypto 2> /dev/null || echo -lcrypto) ifdef CONFIG_UNWINDER_ORC ifeq ($(ARCH),x86_64) @@ -29,7 +23,10 @@ ARCH := x86 endif HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED -HOSTLDLIBS_sorttable = -lpthread +endif + +ifdef CONFIG_BUILDTIME_MCOUNT_SORT +HOSTCFLAGS_sorttable.o += -DMCOUNT_SORT_ENABLED endif # The following programs are only built on demand diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 3efc984d4c..a4b89b7572 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -155,7 +155,7 @@ $(obj)/%.ll: $(src)/%.c FORCE # (See cmd_cc_o_c + relevant part of rule_cc_o_c) quiet_cmd_cc_o_c = CC $(quiet_modtag) $@ - cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< + cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< $(cmd_objtool) ifdef CONFIG_MODVERSIONS # When module versioning is enabled the following steps are executed: @@ -224,27 +224,39 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT ifdef CONFIG_STACK_VALIDATION -ifndef CONFIG_LTO_CLANG -__objtool_obj := $(objtree)/tools/objtool/objtool +objtool := $(objtree)/tools/objtool/objtool + +objtool_args = \ + $(if $(CONFIG_UNWINDER_ORC),orc generate,check) \ + $(if $(part-of-module), --module) \ + $(if $(CONFIG_FRAME_POINTER),, --no-fp) \ + $(if $(CONFIG_GCOV_KERNEL)$(CONFIG_LTO_CLANG), --no-unreachable)\ + $(if $(CONFIG_RETPOLINE), --retpoline) \ + $(if $(CONFIG_X86_SMAP), --uaccess) \ + $(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) \ + $(if $(CONFIG_SLS), --sls) + +cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool_args) $@) +cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd) + +endif # CONFIG_STACK_VALIDATION + +ifdef CONFIG_LTO_CLANG + +# Skip objtool for LLVM bitcode +$(obj)/%.o: objtool-enabled := + +else # 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory # 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file # 'OBJECT_FILES_NON_STANDARD_foo.o := 'n': override directory skip for a file -cmd_objtool = $(if $(patsubst y%,, \ - $(OBJECT_FILES_NON_STANDARD_$(basetarget).o)$(OBJECT_FILES_NON_STANDARD)n), \ - $(__objtool_obj) $(objtool_args) $@) -objtool_obj = $(if $(patsubst y%,, \ - $(OBJECT_FILES_NON_STANDARD_$(basetarget).o)$(OBJECT_FILES_NON_STANDARD)n), \ - $(__objtool_obj)) -endif # CONFIG_LTO_CLANG -endif # CONFIG_STACK_VALIDATION +$(obj)/%.o: objtool-enabled = $(if $(filter-out y%, \ + $(OBJECT_FILES_NON_STANDARD_$(basetarget).o)$(OBJECT_FILES_NON_STANDARD)n),y) -# Rebuild all objects when objtool changes, or is enabled/disabled. -objtool_dep = $(objtool_obj) \ - $(wildcard include/config/ORC_UNWINDER \ - include/config/STACK_VALIDATION) +endif ifdef CONFIG_TRIM_UNUSED_KSYMS cmd_gen_ksymdeps = \ @@ -259,7 +271,7 @@ define rule_cc_o_c $(call cmd,gen_ksymdeps) $(call cmd,checksrc) $(call cmd,checkdoc) - $(call cmd,objtool) + $(call cmd,gen_objtooldep) $(call cmd,modversions_c) $(call cmd,record_mcount) endef @@ -267,13 +279,12 @@ endef define rule_as_o_S $(call cmd_and_fixdep,as_o_S) $(call cmd,gen_ksymdeps) - $(call cmd,objtool) + $(call cmd,gen_objtooldep) $(call cmd,modversions_S) endef # Built-in and composite module parts -.SECONDEXPANSION: -$(obj)/%.o: $(src)/%.c $(recordmcount_source) $$(objtool_dep) FORCE +$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE $(call if_changed_rule,cc_o_c) $(call cmd,force_checksrc) @@ -285,14 +296,13 @@ cmd_cc_lto_link_modules = \ $(LD) $(ld_flags) -r -o $@ \ $(shell [ -s $(@:.lto.o=.o.symversions) ] && \ echo -T $(@:.lto.o=.o.symversions)) \ - --whole-archive $(filter-out FORCE,$^) + --whole-archive $(filter-out FORCE,$^) \ + $(cmd_objtool) -ifdef CONFIG_STACK_VALIDATION # objtool was skipped for LLVM bitcode, run it now that we have compiled # modules into native code -cmd_cc_lto_link_modules += ; \ - $(objtree)/tools/objtool/objtool $(objtool_args) --module $@ -endif +$(obj)/%.lto.o: objtool-enabled = y +$(obj)/%.lto.o: part-of-module := y $(obj)/%.lto.o: $(obj)/%.o FORCE $(call if_changed,cc_lto_link_modules) @@ -356,7 +366,7 @@ $(obj)/%.s: $(src)/%.S FORCE $(call if_changed_dep,cpp_s_S) quiet_cmd_as_o_S = AS $(quiet_modtag) $@ - cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< + cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool) ifdef CONFIG_ASM_MODVERSIONS @@ -375,7 +385,7 @@ cmd_modversions_S = \ fi endif -$(obj)/%.o: $(src)/%.S $$(objtool_dep) FORCE +$(obj)/%.o: $(src)/%.S FORCE $(call if_changed_rule,as_o_S) targets += $(filter-out $(subdir-builtin), $(real-obj-y)) diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 4aad284800..1d16ca1b78 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so - gcc-plugin-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += latent_entropy_plugin.so gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) \ += -DLATENT_ENTROPY_PLUGIN diff --git a/scripts/Makefile.kcsan b/scripts/Makefile.kcsan index 37cb504c77..19f693b68a 100644 --- a/scripts/Makefile.kcsan +++ b/scripts/Makefile.kcsan @@ -9,7 +9,18 @@ endif # Keep most options here optional, to allow enabling more compilers if absence # of some options does not break KCSAN nor causes false positive reports. -export CFLAGS_KCSAN := -fsanitize=thread \ - $(call cc-option,$(call cc-param,tsan-instrument-func-entry-exit=0) -fno-optimize-sibling-calls) \ +kcsan-cflags := -fsanitize=thread -fno-optimize-sibling-calls \ $(call cc-option,$(call cc-param,tsan-compound-read-before-write=1),$(call cc-option,$(call cc-param,tsan-instrument-read-before-write=1))) \ $(call cc-param,tsan-distinguish-volatile=1) + +ifdef CONFIG_CC_IS_GCC +# GCC started warning about operations unsupported by the TSan runtime. But +# KCSAN != TSan, so just ignore these warnings. +kcsan-cflags += -Wno-tsan +endif + +ifndef CONFIG_KCSAN_WEAK_MEMORY +kcsan-cflags += $(call cc-option,$(call cc-param,tsan-instrument-func-entry-exit=0)) +endif + +export CFLAGS_KCSAN := $(kcsan-cflags) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 7e3f156ac5..405163d4a5 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -182,6 +182,11 @@ ifeq ($(CONFIG_KCSAN),y) _c_flags += $(if $(patsubst n%,, \ $(KCSAN_SANITIZE_$(basetarget).o)$(KCSAN_SANITIZE)y), \ $(CFLAGS_KCSAN)) +# Some uninstrumented files provide implied barriers required to avoid false +# positives: set KCSAN_INSTRUMENT_BARRIERS for barrier instrumentation only. +_c_flags += $(if $(patsubst n%,, \ + $(KCSAN_INSTRUMENT_BARRIERS_$(basetarget).o)$(KCSAN_INSTRUMENT_BARRIERS)n), \ + -D__KCSAN_INSTRUMENT_BARRIERS__) endif # $(srctree)/$(src) for including checkin headers from generated source files @@ -232,17 +237,6 @@ ifeq ($(CONFIG_LTO_CLANG),y) mod-prelink-ext := .lto endif -# Objtool arguments are also needed for modfinal with LTO, so we define -# then here to avoid duplication. -objtool_args = \ - $(if $(CONFIG_UNWINDER_ORC),orc generate,check) \ - $(if $(part-of-module), --module) \ - $(if $(CONFIG_FRAME_POINTER),, --no-fp) \ - $(if $(CONFIG_GCOV_KERNEL)$(CONFIG_LTO_CLANG), --no-unreachable)\ - $(if $(CONFIG_RETPOLINE), --retpoline) \ - $(if $(CONFIG_X86_SMAP), --uaccess) \ - $(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) - # Useful for describing the dependency of composite objects # Usage: # $(call multi_depend, multi_used_targets, suffix_to_remove, suffix_to_add) @@ -310,7 +304,6 @@ DTC_FLAGS += -Wno-interrupt_provider # Disable noisy checks by default ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),) DTC_FLAGS += -Wno-unit_address_vs_reg \ - -Wno-unit_address_format \ -Wno-gpios_property \ -Wno-avoid_unnecessary_addr_size \ -Wno-alias_paths \ @@ -425,20 +418,35 @@ printf "%08x\n" $$dec_size | \ } \ ) +quiet_cmd_file_size = GEN $@ + cmd_file_size = $(size_append) > $@ + quiet_cmd_bzip2 = BZIP2 $@ - cmd_bzip2 = { cat $(real-prereqs) | $(KBZIP2) -9; $(size_append); } > $@ + cmd_bzip2 = cat $(real-prereqs) | $(KBZIP2) -9 > $@ + +quiet_cmd_bzip2_with_size = BZIP2 $@ + cmd_bzip2_with_size = { cat $(real-prereqs) | $(KBZIP2) -9; $(size_append); } > $@ # Lzma # --------------------------------------------------------------------------- quiet_cmd_lzma = LZMA $@ - cmd_lzma = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@ + cmd_lzma = cat $(real-prereqs) | $(LZMA) -9 > $@ + +quiet_cmd_lzma_with_size = LZMA $@ + cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@ quiet_cmd_lzo = LZO $@ - cmd_lzo = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@ + cmd_lzo = cat $(real-prereqs) | $(KLZOP) -9 > $@ + +quiet_cmd_lzo_with_size = LZO $@ + cmd_lzo_with_size = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@ quiet_cmd_lz4 = LZ4 $@ - cmd_lz4 = { cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout; \ + cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout > $@ + +quiet_cmd_lz4_with_size = LZ4 $@ + cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout; \ $(size_append); } > $@ # U-Boot mkimage @@ -481,7 +489,10 @@ quiet_cmd_uimage = UIMAGE $@ # big dictionary would increase the memory usage too much in the multi-call # decompression mode. A BCJ filter isn't used either. quiet_cmd_xzkern = XZKERN $@ - cmd_xzkern = { cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh; \ + cmd_xzkern = cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh > $@ + +quiet_cmd_xzkern_with_size = XZKERN $@ + cmd_xzkern_with_size = { cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh; \ $(size_append); } > $@ quiet_cmd_xzmisc = XZMISC $@ @@ -504,10 +515,13 @@ quiet_cmd_xzmisc = XZMISC $@ # be used because it would require zstd to allocate a 128 MB buffer. quiet_cmd_zstd = ZSTD $@ - cmd_zstd = { cat $(real-prereqs) | $(ZSTD) -19; $(size_append); } > $@ + cmd_zstd = cat $(real-prereqs) | $(ZSTD) -19 > $@ quiet_cmd_zstd22 = ZSTD22 $@ - cmd_zstd22 = { cat $(real-prereqs) | $(ZSTD) -22 --ultra; $(size_append); } > $@ + cmd_zstd22 = cat $(real-prereqs) | $(ZSTD) -22 --ultra > $@ + +quiet_cmd_zstd22_with_size = ZSTD22 $@ + cmd_zstd22_with_size = { cat $(real-prereqs) | $(ZSTD) -22 --ultra; $(size_append); } > $@ # ASM offsets # --------------------------------------------------------------------------- diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal index ff80577743..7f39599e9f 100644 --- a/scripts/Makefile.modfinal +++ b/scripts/Makefile.modfinal @@ -40,7 +40,8 @@ quiet_cmd_ld_ko_o = LD [M] $@ quiet_cmd_btf_ko = BTF [M] $@ cmd_btf_ko = \ if [ -f vmlinux ]; then \ - LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J --btf_base vmlinux $@; \ + LLVM_OBJCOPY="$(OBJCOPY)" $(PAHOLE) -J $(PAHOLE_FLAGS) --btf_base vmlinux $@; \ + $(RESOLVE_BTFIDS) -b vmlinux $@; \ else \ printf "Skipping BTF generation for %s due to unavailability of vmlinux\n" $@ 1>&2; \ fi; diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index ff9b09e4cf..c2c43a0ecf 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -66,9 +66,9 @@ endif # Don't stop modules_install even if we can't sign external modules. # ifeq ($(CONFIG_MODULE_SIG_ALL),y) +sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(srctree)/)$(CONFIG_MODULE_SIG_KEY) quiet_cmd_sign = SIGN $@ -$(eval $(call config_filename,MODULE_SIG_KEY)) - cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY) certs/signing_key.x509 $@ \ + cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(sig-key) certs/signing_key.x509 $@ \ $(if $(KBUILD_EXTMOD),|| true) else quiet_cmd_sign := diff --git a/scripts/Makefile.package b/scripts/Makefile.package index b74c65284f..77b612183c 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -103,7 +103,7 @@ snap-pkg: # tarball targets # --------------------------------------------------------------------------- -tar-pkgs := dir-pkg tar-pkg targz-pkg tarbz2-pkg tarxz-pkg +tar-pkgs := dir-pkg tar-pkg targz-pkg tarbz2-pkg tarxz-pkg tarzst-pkg PHONY += $(tar-pkgs) $(tar-pkgs): $(MAKE) -f $(srctree)/Makefile @@ -130,10 +130,12 @@ $(if $(findstring tar-src,$@),, \ $(if $(findstring bz2,$@),$(KBZIP2), \ $(if $(findstring gz,$@),$(KGZIP), \ $(if $(findstring xz,$@),$(XZ), \ -$(error unknown target $@)))) \ +$(if $(findstring zst,$@),$(ZSTD), \ +$(error unknown target $@))))) \ -f -9 $(perf-tar).tar) -perf-tar-pkgs := perf-tar-src-pkg perf-targz-src-pkg perf-tarbz2-src-pkg perf-tarxz-src-pkg +perf-tar-pkgs := perf-tar-src-pkg perf-targz-src-pkg perf-tarbz2-src-pkg \ + perf-tarxz-src-pkg perf-tarzst-src-pkg PHONY += $(perf-tar-pkgs) $(perf-tar-pkgs): $(call cmd,perf_tar) @@ -153,9 +155,11 @@ help: @echo ' targz-pkg - Build the kernel as a gzip compressed tarball' @echo ' tarbz2-pkg - Build the kernel as a bzip2 compressed tarball' @echo ' tarxz-pkg - Build the kernel as a xz compressed tarball' + @echo ' tarzst-pkg - Build the kernel as a zstd compressed tarball' @echo ' perf-tar-src-pkg - Build $(perf-tar).tar source tarball' @echo ' perf-targz-src-pkg - Build $(perf-tar).tar.gz source tarball' @echo ' perf-tarbz2-src-pkg - Build $(perf-tar).tar.bz2 source tarball' @echo ' perf-tarxz-src-pkg - Build $(perf-tar).tar.xz source tarball' + @echo ' perf-tarzst-src-pkg - Build $(perf-tar).tar.zst source tarball' .PHONY: $(PHONY) diff --git a/scripts/Makefile.ubsan b/scripts/Makefile.ubsan index 9e2092fd52..7099c603ff 100644 --- a/scripts/Makefile.ubsan +++ b/scripts/Makefile.ubsan @@ -8,7 +8,6 @@ ubsan-cflags-$(CONFIG_UBSAN_LOCAL_BOUNDS) += -fsanitize=local-bounds ubsan-cflags-$(CONFIG_UBSAN_SHIFT) += -fsanitize=shift ubsan-cflags-$(CONFIG_UBSAN_DIV_ZERO) += -fsanitize=integer-divide-by-zero ubsan-cflags-$(CONFIG_UBSAN_UNREACHABLE) += -fsanitize=unreachable -ubsan-cflags-$(CONFIG_UBSAN_OBJECT_SIZE) += -fsanitize=object-size ubsan-cflags-$(CONFIG_UBSAN_BOOL) += -fsanitize=bool ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum ubsan-cflags-$(CONFIG_UBSAN_TRAP) += -fsanitize-undefined-trap-on-error diff --git a/scripts/atomic/gen-atomic-instrumented.sh b/scripts/atomic/gen-atomic-instrumented.sh index 035ceb4ee8..68f902731d 100644 --- a/scripts/atomic/gen-atomic-instrumented.sh +++ b/scripts/atomic/gen-atomic-instrumented.sh @@ -34,6 +34,14 @@ gen_param_check() gen_params_checks() { local meta="$1"; shift + local order="$1"; shift + + if [ "${order}" = "_release" ]; then + printf "\tkcsan_release();\n" + elif [ -z "${order}" ] && ! meta_in "$meta" "slv"; then + # RMW with return value is fully ordered + printf "\tkcsan_mb();\n" + fi while [ "$#" -gt 0 ]; do gen_param_check "$meta" "$1" @@ -56,7 +64,7 @@ gen_proto_order_variant() local ret="$(gen_ret_type "${meta}" "${int}")" local params="$(gen_params "${int}" "${atomic}" "$@")" - local checks="$(gen_params_checks "${meta}" "$@")" + local checks="$(gen_params_checks "${meta}" "${order}" "$@")" local args="$(gen_args "$@")" local retstmt="$(gen_ret_stmt "${meta}")" @@ -75,29 +83,44 @@ EOF gen_xchg() { local xchg="$1"; shift + local order="$1"; shift local mult="$1"; shift + kcsan_barrier="" + if [ "${xchg%_local}" = "${xchg}" ]; then + case "$order" in + _release) kcsan_barrier="kcsan_release()" ;; + "") kcsan_barrier="kcsan_mb()" ;; + esac + fi + if [ "${xchg%${xchg#try_cmpxchg}}" = "try_cmpxchg" ] ; then cat < \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, - 'codespellfile=s' => \$codespellfile, + 'codespellfile=s' => \$user_codespellfile, 'typedefsfile=s' => \$typedefsfile, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor @@ -325,9 +326,32 @@ GetOptions( 'kconfig-prefix=s' => \${CONFIG_}, 'h|help' => \$help, 'version' => \$help -) or help(1); +) or $help = 2; -help(0) if ($help); +if ($user_codespellfile) { + # Use the user provided codespell file unconditionally + $codespellfile = $user_codespellfile; +} elsif (!(-f $codespellfile)) { + # If /usr/share/codespell/dictionary.txt is not present, try to find it + # under codespell's install directory: /data/dictionary.txt + if (($codespell || $help) && which("codespell") ne "" && which("python") ne "") { + my $python_codespell_dict = << "EOF"; + +import os.path as op +import codespell_lib +codespell_dir = op.dirname(codespell_lib.__file__) +codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt') +print(codespell_file, end='') +EOF + + my $codespell_dict = `python -c "$python_codespell_dict" 2> /dev/null`; + $codespellfile = $codespell_dict if (-f $codespell_dict); + } +} + +# $help is 1 if either -h, --help or --version is passed as option - exitcode: 0 +# $help is 2 if invalid option is passed - exitcode: 1 +help($help - 1) if ($help); die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix)); die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse); @@ -489,7 +513,8 @@ our $Attribute = qr{ ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| - __weak + __weak| + __alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; @@ -3147,7 +3172,7 @@ sub process { length($line) > 75 && !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || # file delta changes - $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || + $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || # filename then : $line =~ /^\s*(?:Fixes:|Link:|$signature_tags)/i || # A Fixes: or Link: line or signature tag line @@ -3454,47 +3479,47 @@ sub process { # Kconfig supports named choices), so use a word boundary # (\b) rather than a whitespace character (\s) $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { - my $length = 0; - my $cnt = $realcnt; - my $ln = $linenr + 1; - my $f; - my $is_start = 0; - my $is_end = 0; - for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { - $f = $lines[$ln - 1]; - $cnt-- if ($lines[$ln - 1] !~ /^-/); - $is_end = $lines[$ln - 1] =~ /^\+/; + my $ln = $linenr; + my $needs_help = 0; + my $has_help = 0; + my $help_length = 0; + while (defined $lines[$ln]) { + my $f = $lines[$ln++]; next if ($f =~ /^-/); - last if (!$file && $f =~ /^\@\@/); + last if ($f !~ /^[\+ ]/); # !patch context - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { - $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { - $length = -1; + if ($f =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { + $needs_help = 1; + next; + } + if ($f =~ /^\+\s*help\s*$/) { + $has_help = 1; + next; } - $f =~ s/^.//; - $f =~ s/#.*//; - $f =~ s/^\s+//; - next if ($f =~ /^$/); + $f =~ s/^.//; # strip patch context [+ ] + $f =~ s/#.*//; # strip # directives + $f =~ s/^\s+//; # strip leading blanks + next if ($f =~ /^$/); # skip blank lines + # At the end of this Kconfig block: # This only checks context lines in the patch # and so hopefully shouldn't trigger false # positives, even though some of these are # common words in help texts - if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| - if|endif|menu|endmenu|source)\b/x) { - $is_end = 1; + if ($f =~ /^(?:config|menuconfig|choice|endchoice| + if|endif|menu|endmenu|source)\b/x) { last; } - $length++; + $help_length++ if ($has_help); } - if ($is_start && $is_end && $length < $min_conf_desc_length) { + if ($needs_help && + $help_length < $min_conf_desc_length) { + my $stat_real = get_stat_real($linenr, $ln - 1); WARN("CONFIG_DESCRIPTION", - "please write a paragraph that describes the config symbol fully\n" . $herecurr); + "please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n"); } - #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } # check MAINTAINERS entries @@ -4448,6 +4473,7 @@ sub process { # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; + $name =~ s/^\s*($Ident).*/$1/; if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index 1aae4f4fda..1eeb7b42c5 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -12,19 +12,27 @@ driver_info drm_connector_funcs drm_encoder_funcs drm_encoder_helper_funcs +dvb_frontend_ops +dvb_tuner_ops ethtool_ops extent_io_ops +fb_ops file_lock_operations file_operations hv_ops +hwmon_ops +ib_device_ops ide_dma_ops ide_port_ops +ieee80211_ops +iio_buffer_setup_ops inode_operations intel_dvo_dev_ops irq_domain_ops item_operations iwl_cfg iwl_ops +kernel_param_ops kgdb_arch kgdb_io kset_uevent_ops @@ -32,29 +40,41 @@ lock_manager_operations machine_desc microcode_ops mlxsw_reg_info +mtd_ooblayout_ops mtrr_ops +nand_controller_ops neigh_ops net_device_ops +nft_expr_ops nlmsvc_binding nvkm_device_chip of_device_id pci_raw_ops phy_ops +pinconf_ops pinctrl_ops pinmux_ops pipe_buf_operations platform_hibernation_ops platform_suspend_ops +proc_ops proto_ops +pwm_ops regmap_access_table regulator_ops +reset_control_ops rpc_pipe_ops rtc_class_ops sd_desc +sdhci_ops seq_operations sirfsoc_padmux snd_ac97_build_ops +snd_pcm_ops +snd_rawmidi_ops snd_soc_component_driver +snd_soc_dai_ops +snd_soc_ops soc_pcmcia_socket_ops stacktrace_ops sysfs_ops @@ -63,6 +83,13 @@ uart_ops usb_mon_operations v4l2_ctrl_ops v4l2_ioctl_ops +v4l2_subdev_core_ops +v4l2_subdev_internal_ops +v4l2_subdev_ops +v4l2_subdev_pad_ops +v4l2_subdev_video_ops +vb2_ops vm_operations_struct wacom_features +watchdog_ops wd_ops diff --git a/scripts/decodecode b/scripts/decodecode index 31d884e35f..c711a19651 100644 --- a/scripts/decodecode +++ b/scripts/decodecode @@ -126,7 +126,7 @@ if [ $marker -ne 0 ]; then fi echo Code starting with the faulting instruction > $T.aa echo =========================================== >> $T.aa -code=`echo $code | sed -e 's/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` +code=`echo $code | sed -e 's/\r//;s/ [<(]/ /;s/[>)] / /;s/ /,0x/g; s/[>)]$//'` echo -n " .$type 0x" > $T.s echo $code >> $T.s disas $T 0 diff --git a/scripts/documentation-file-ref-check b/scripts/documentation-file-ref-check index 7187ea5e51..68083f2f11 100644 --- a/scripts/documentation-file-ref-check +++ b/scripts/documentation-file-ref-check @@ -94,6 +94,9 @@ while () { # Makefiles and scripts contain nasty expressions to parse docs next if ($f =~ m/Makefile/ || $f =~ m/\.sh$/); + # It doesn't make sense to parse hidden files + next if ($f =~ m#/\.#); + # Skip this script next if ($f eq $scriptname); @@ -144,6 +147,7 @@ while () { if ($f =~ m/tools/) { my $path = $f; $path =~ s,(.*)/.*,$1,; + $path =~ s,testing/selftests/bpf,bpf/bpftool,; next if (grep -e, glob("$path/$ref $path/../$ref $path/$fulref")); } diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 17cb6890d4..781ba1129a 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -143,6 +143,14 @@ static void check_nodes_props(struct check *c, struct dt_info *dti, struct node check_nodes_props(c, dti, child); } +static bool is_multiple_of(int multiple, int divisor) +{ + if (divisor == 0) + return multiple == 0; + else + return (multiple % divisor) == 0; +} + static bool run_check(struct check *c, struct dt_info *dti) { struct node *dt = dti->dt; @@ -297,19 +305,20 @@ ERROR(duplicate_property_names, check_duplicate_property_names, NULL); #define LOWERCASE "abcdefghijklmnopqrstuvwxyz" #define UPPERCASE "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define DIGITS "0123456789" -#define PROPNODECHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" +#define NODECHARS LOWERCASE UPPERCASE DIGITS ",._+-@" +#define PROPCHARS LOWERCASE UPPERCASE DIGITS ",._+*#?-" #define PROPNODECHARSSTRICT LOWERCASE UPPERCASE DIGITS ",-" static void check_node_name_chars(struct check *c, struct dt_info *dti, struct node *node) { - int n = strspn(node->name, c->data); + size_t n = strspn(node->name, c->data); if (n < strlen(node->name)) FAIL(c, dti, node, "Bad character '%c' in node name", node->name[n]); } -ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@"); +ERROR(node_name_chars, check_node_name_chars, NODECHARS); static void check_node_name_chars_strict(struct check *c, struct dt_info *dti, struct node *node) @@ -330,6 +339,20 @@ static void check_node_name_format(struct check *c, struct dt_info *dti, } ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars); +static void check_node_name_vs_property_name(struct check *c, + struct dt_info *dti, + struct node *node) +{ + if (!node->parent) + return; + + if (get_property(node->parent, node->name)) { + FAIL(c, dti, node, "node name and property name conflict"); + } +} +WARNING(node_name_vs_property_name, check_node_name_vs_property_name, + NULL, &node_name_chars); + static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti, struct node *node) { @@ -363,14 +386,14 @@ static void check_property_name_chars(struct check *c, struct dt_info *dti, struct property *prop; for_each_property(node, prop) { - int n = strspn(prop->name, c->data); + size_t n = strspn(prop->name, c->data); if (n < strlen(prop->name)) FAIL_PROP(c, dti, node, prop, "Bad character '%c' in property name", prop->name[n]); } } -ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS); +ERROR(property_name_chars, check_property_name_chars, PROPCHARS); static void check_property_name_chars_strict(struct check *c, struct dt_info *dti, @@ -380,7 +403,7 @@ static void check_property_name_chars_strict(struct check *c, for_each_property(node, prop) { const char *name = prop->name; - int n = strspn(name, c->data); + size_t n = strspn(name, c->data); if (n == strlen(prop->name)) continue; @@ -497,7 +520,7 @@ static cell_t check_phandle_prop(struct check *c, struct dt_info *dti, phandle = propval_cell(prop); - if ((phandle == 0) || (phandle == -1)) { + if (!phandle_is_valid(phandle)) { FAIL_PROP(c, dti, node, prop, "bad value (0x%x) in %s property", phandle, prop->name); return 0; @@ -556,7 +579,7 @@ static void check_name_properties(struct check *c, struct dt_info *dti, if (!prop) return; /* No name property, that's fine */ - if ((prop->val.len != node->basenamelen+1) + if ((prop->val.len != node->basenamelen + 1U) || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) { FAIL(c, dti, node, "\"name\" property is incorrect (\"%s\" instead" " of base node name)", prop->val.val); @@ -657,7 +680,6 @@ ERROR(omit_unused_nodes, fixup_omit_unused_nodes, NULL, &phandle_references, &pa */ WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells"); WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells"); -WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells"); WARNING_IF_NOT_STRING(device_type_is_string, "device_type"); WARNING_IF_NOT_STRING(model_is_string, "model"); @@ -672,8 +694,7 @@ static void check_names_is_string_list(struct check *c, struct dt_info *dti, struct property *prop; for_each_property(node, prop) { - const char *s = strrchr(prop->name, '-'); - if (!s || !streq(s, "-names")) + if (!strends(prop->name, "-names")) continue; c->data = prop->name; @@ -753,7 +774,7 @@ static void check_reg_format(struct check *c, struct dt_info *dti, size_cells = node_size_cells(node->parent); entrylen = (addr_cells + size_cells) * sizeof(cell_t); - if (!entrylen || (prop->val.len % entrylen) != 0) + if (!is_multiple_of(prop->val.len, entrylen)) FAIL_PROP(c, dti, node, prop, "property has invalid length (%d bytes) " "(#address-cells == %d, #size-cells == %d)", prop->val.len, addr_cells, size_cells); @@ -794,7 +815,7 @@ static void check_ranges_format(struct check *c, struct dt_info *dti, "#size-cells (%d) differs from %s (%d)", ranges, c_size_cells, node->parent->fullpath, p_size_cells); - } else if ((prop->val.len % entrylen) != 0) { + } else if (!is_multiple_of(prop->val.len, entrylen)) { FAIL_PROP(c, dti, node, prop, "\"%s\" property has invalid length (%d bytes) " "(parent #address-cells == %d, child #address-cells == %d, " "#size-cells == %d)", ranges, prop->val.len, @@ -871,7 +892,7 @@ static void check_pci_device_bus_num(struct check *c, struct dt_info *dti, struc } else { cells = (cell_t *)prop->val.val; min_bus = fdt32_to_cpu(cells[0]); - max_bus = fdt32_to_cpu(cells[0]); + max_bus = fdt32_to_cpu(cells[1]); } if ((bus_num < min_bus) || (bus_num > max_bus)) FAIL_PROP(c, dti, node, prop, "PCI bus number %d out of range, expected (%d - %d)", @@ -1367,9 +1388,9 @@ static void check_property_phandle_args(struct check *c, const struct provider *provider) { struct node *root = dti->dt; - int cell, cellsize = 0; + unsigned int cell, cellsize = 0; - if (prop->val.len % sizeof(cell_t)) { + if (!is_multiple_of(prop->val.len, sizeof(cell_t))) { FAIL_PROP(c, dti, node, prop, "property size (%d) is invalid, expected multiple of %zu", prop->val.len, sizeof(cell_t)); @@ -1379,14 +1400,14 @@ static void check_property_phandle_args(struct check *c, for (cell = 0; cell < prop->val.len / sizeof(cell_t); cell += cellsize + 1) { struct node *provider_node; struct property *cellprop; - int phandle; + cell_t phandle; phandle = propval_cell_n(prop, cell); /* * Some bindings use a cell value 0 or -1 to skip over optional * entries when each index position has a specific definition. */ - if (phandle == 0 || phandle == -1) { + if (!phandle_is_valid(phandle)) { /* Give up if this is an overlay with external references */ if (dti->dtsflags & DTSF_PLUGIN) break; @@ -1452,7 +1473,8 @@ static void check_provider_cells_property(struct check *c, } #define WARNING_PROPERTY_PHANDLE_CELLS(nm, propname, cells_name, ...) \ static struct provider nm##_provider = { (propname), (cells_name), __VA_ARGS__ }; \ - WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &phandle_references); + WARNING_IF_NOT_CELL(nm##_is_cell, cells_name); \ + WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &nm##_is_cell, &phandle_references); WARNING_PROPERTY_PHANDLE_CELLS(clocks, "clocks", "#clock-cells"); WARNING_PROPERTY_PHANDLE_CELLS(cooling_device, "cooling-device", "#cooling-cells"); @@ -1473,24 +1495,17 @@ WARNING_PROPERTY_PHANDLE_CELLS(thermal_sensors, "thermal-sensors", "#thermal-sen static bool prop_is_gpio(struct property *prop) { - char *str; - /* * *-gpios and *-gpio can appear in property names, * so skip over any false matches (only one known ATM) */ - if (strstr(prop->name, "nr-gpio")) + if (strends(prop->name, ",nr-gpios")) return false; - str = strrchr(prop->name, '-'); - if (str) - str++; - else - str = prop->name; - if (!(streq(str, "gpios") || streq(str, "gpio"))) - return false; - - return true; + return strends(prop->name, "-gpios") || + streq(prop->name, "gpios") || + strends(prop->name, "-gpio") || + streq(prop->name, "gpio"); } static void check_gpios_property(struct check *c, @@ -1525,13 +1540,10 @@ static void check_deprecated_gpio_property(struct check *c, struct property *prop; for_each_property(node, prop) { - char *str; - if (!prop_is_gpio(prop)) continue; - str = strstr(prop->name, "gpio"); - if (!streq(str, "gpio")) + if (!strends(prop->name, "gpio")) continue; FAIL_PROP(c, dti, node, prop, @@ -1561,21 +1573,106 @@ static void check_interrupt_provider(struct check *c, struct node *node) { struct property *prop; - - if (!node_is_interrupt_provider(node)) - return; + bool irq_provider = node_is_interrupt_provider(node); prop = get_property(node, "#interrupt-cells"); - if (!prop) + if (irq_provider && !prop) { FAIL(c, dti, node, - "Missing #interrupt-cells in interrupt provider"); + "Missing '#interrupt-cells' in interrupt provider"); + return; + } - prop = get_property(node, "#address-cells"); - if (!prop) + if (!irq_provider && prop) { FAIL(c, dti, node, - "Missing #address-cells in interrupt provider"); + "'#interrupt-cells' found, but node is not an interrupt provider"); + return; + } } -WARNING(interrupt_provider, check_interrupt_provider, NULL); +WARNING(interrupt_provider, check_interrupt_provider, NULL, &interrupts_extended_is_cell); + +static void check_interrupt_map(struct check *c, + struct dt_info *dti, + struct node *node) +{ + struct node *root = dti->dt; + struct property *prop, *irq_map_prop; + size_t cellsize, cell, map_cells; + + irq_map_prop = get_property(node, "interrupt-map"); + if (!irq_map_prop) + return; + + if (node->addr_cells < 0) { + FAIL(c, dti, node, + "Missing '#address-cells' in interrupt-map provider"); + return; + } + cellsize = node_addr_cells(node); + cellsize += propval_cell(get_property(node, "#interrupt-cells")); + + prop = get_property(node, "interrupt-map-mask"); + if (prop && (prop->val.len != (cellsize * sizeof(cell_t)))) + FAIL_PROP(c, dti, node, prop, + "property size (%d) is invalid, expected %zu", + prop->val.len, cellsize * sizeof(cell_t)); + + if (!is_multiple_of(irq_map_prop->val.len, sizeof(cell_t))) { + FAIL_PROP(c, dti, node, irq_map_prop, + "property size (%d) is invalid, expected multiple of %zu", + irq_map_prop->val.len, sizeof(cell_t)); + return; + } + + map_cells = irq_map_prop->val.len / sizeof(cell_t); + for (cell = 0; cell < map_cells; ) { + struct node *provider_node; + struct property *cellprop; + int phandle; + size_t parent_cellsize; + + if ((cell + cellsize) >= map_cells) { + FAIL_PROP(c, dti, node, irq_map_prop, + "property size (%d) too small, expected > %zu", + irq_map_prop->val.len, (cell + cellsize) * sizeof(cell_t)); + break; + } + cell += cellsize; + + phandle = propval_cell_n(irq_map_prop, cell); + if (!phandle_is_valid(phandle)) { + /* Give up if this is an overlay with external references */ + if (!(dti->dtsflags & DTSF_PLUGIN)) + FAIL_PROP(c, dti, node, irq_map_prop, + "Cell %zu is not a phandle(%d)", + cell, phandle); + break; + } + + provider_node = get_node_by_phandle(root, phandle); + if (!provider_node) { + FAIL_PROP(c, dti, node, irq_map_prop, + "Could not get phandle(%d) node for (cell %zu)", + phandle, cell); + break; + } + + cellprop = get_property(provider_node, "#interrupt-cells"); + if (cellprop) { + parent_cellsize = propval_cell(cellprop); + } else { + FAIL(c, dti, node, "Missing property '#interrupt-cells' in node %s or bad phandle (referred from interrupt-map[%zu])", + provider_node->fullpath, cell); + break; + } + + cellprop = get_property(provider_node, "#address-cells"); + if (cellprop) + parent_cellsize += propval_cell(cellprop); + + cell += 1 + parent_cellsize; + } +} +WARNING(interrupt_map, check_interrupt_map, NULL, &phandle_references, &addr_size_cells, &interrupt_provider); static void check_interrupts_property(struct check *c, struct dt_info *dti, @@ -1584,13 +1681,13 @@ static void check_interrupts_property(struct check *c, struct node *root = dti->dt; struct node *irq_node = NULL, *parent = node; struct property *irq_prop, *prop = NULL; - int irq_cells, phandle; + cell_t irq_cells, phandle; irq_prop = get_property(node, "interrupts"); if (!irq_prop) return; - if (irq_prop->val.len % sizeof(cell_t)) + if (!is_multiple_of(irq_prop->val.len, sizeof(cell_t))) FAIL_PROP(c, dti, node, irq_prop, "size (%d) is invalid, expected multiple of %zu", irq_prop->val.len, sizeof(cell_t)); @@ -1603,7 +1700,7 @@ static void check_interrupts_property(struct check *c, prop = get_property(parent, "interrupt-parent"); if (prop) { phandle = propval_cell(prop); - if ((phandle == 0) || (phandle == -1)) { + if (!phandle_is_valid(phandle)) { /* Give up if this is an overlay with * external references */ if (dti->dtsflags & DTSF_PLUGIN) @@ -1639,7 +1736,7 @@ static void check_interrupts_property(struct check *c, } irq_cells = propval_cell(prop); - if (irq_prop->val.len % (irq_cells * sizeof(cell_t))) { + if (!is_multiple_of(irq_prop->val.len, irq_cells * sizeof(cell_t))) { FAIL_PROP(c, dti, node, prop, "size is (%d), expected multiple of %d", irq_prop->val.len, (int)(irq_cells * sizeof(cell_t))); @@ -1750,7 +1847,7 @@ WARNING(graph_port, check_graph_port, NULL, &graph_nodes); static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti, struct node *endpoint) { - int phandle; + cell_t phandle; struct node *node; struct property *prop; @@ -1760,7 +1857,7 @@ static struct node *get_remote_endpoint(struct check *c, struct dt_info *dti, phandle = propval_cell(prop); /* Give up if this is an overlay with external references */ - if (phandle == 0 || phandle == -1) + if (!phandle_is_valid(phandle)) return NULL; node = get_node_by_phandle(dti->dt, phandle); @@ -1796,7 +1893,7 @@ WARNING(graph_endpoint, check_graph_endpoint, NULL, &graph_nodes); static struct check *check_table[] = { &duplicate_node_names, &duplicate_property_names, &node_name_chars, &node_name_format, &property_name_chars, - &name_is_string, &name_properties, + &name_is_string, &name_properties, &node_name_vs_property_name, &duplicate_label, @@ -1804,7 +1901,7 @@ static struct check *check_table[] = { &phandle_references, &path_references, &omit_unused_nodes, - &address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, + &address_cells_is_cell, &size_cells_is_cell, &device_type_is_string, &model_is_string, &status_is_string, &label_is_string, @@ -1839,26 +1936,43 @@ static struct check *check_table[] = { &chosen_node_is_root, &chosen_node_bootargs, &chosen_node_stdout_path, &clocks_property, + &clocks_is_cell, &cooling_device_property, + &cooling_device_is_cell, &dmas_property, + &dmas_is_cell, &hwlocks_property, + &hwlocks_is_cell, &interrupts_extended_property, + &interrupts_extended_is_cell, &io_channels_property, + &io_channels_is_cell, &iommus_property, + &iommus_is_cell, &mboxes_property, + &mboxes_is_cell, &msi_parent_property, + &msi_parent_is_cell, &mux_controls_property, + &mux_controls_is_cell, &phys_property, + &phys_is_cell, &power_domains_property, + &power_domains_is_cell, &pwms_property, + &pwms_is_cell, &resets_property, + &resets_is_cell, &sound_dai_property, + &sound_dai_is_cell, &thermal_sensors_property, + &thermal_sensors_is_cell, &deprecated_gpio_property, &gpios_property, &interrupts_property, &interrupt_provider, + &interrupt_map, &alias_paths, @@ -1882,7 +1996,7 @@ static void enable_warning_error(struct check *c, bool warn, bool error) static void disable_warning_error(struct check *c, bool warn, bool error) { - int i; + unsigned int i; /* Lowering level, also lower it for things this is the prereq * for */ @@ -1903,7 +2017,7 @@ static void disable_warning_error(struct check *c, bool warn, bool error) void parse_checks_option(bool warn, bool error, const char *arg) { - int i; + unsigned int i; const char *name = arg; bool enable = true; @@ -1930,7 +2044,7 @@ void parse_checks_option(bool warn, bool error, const char *arg) void process_checks(bool force, struct dt_info *dti) { - int i; + unsigned int i; int error = 0; for (i = 0; i < ARRAY_SIZE(check_table); i++) { diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c index 838c5df96c..bc786c543b 100644 --- a/scripts/dtc/dtc.c +++ b/scripts/dtc/dtc.c @@ -12,7 +12,7 @@ * Command line options */ int quiet; /* Level of quietness */ -int reservenum; /* Number of memory reservation slots */ +unsigned int reservenum;/* Number of memory reservation slots */ int minsize; /* Minimum blob size */ int padsize; /* Additional padding to blob */ int alignsize; /* Additional padding to blob accroding to the alignsize */ @@ -197,7 +197,7 @@ int main(int argc, char *argv[]) depname = optarg; break; case 'R': - reservenum = strtol(optarg, NULL, 0); + reservenum = strtoul(optarg, NULL, 0); break; case 'S': minsize = strtol(optarg, NULL, 0); @@ -359,8 +359,6 @@ int main(int argc, char *argv[]) #endif } else if (streq(outform, "dtb")) { dt_to_blob(outf, dti, outversion); - } else if (streq(outform, "dtbo")) { - dt_to_blob(outf, dti, outversion); } else if (streq(outform, "asm")) { dt_to_asm(outf, dti, outversion); } else if (streq(outform, "null")) { diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index d3e82fb8e3..0a1f549910 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -35,7 +35,7 @@ * Command line options */ extern int quiet; /* Level of quietness */ -extern int reservenum; /* Number of memory reservation slots */ +extern unsigned int reservenum; /* Number of memory reservation slots */ extern int minsize; /* Minimum blob size */ extern int padsize; /* Additional padding to blob */ extern int alignsize; /* Additional padding to blob accroding to the alignsize */ @@ -51,6 +51,11 @@ extern int annotate; /* annotate .dts with input source location */ typedef uint32_t cell_t; +static inline bool phandle_is_valid(cell_t phandle) +{ + return phandle != 0 && phandle != ~0U; +} + static inline uint16_t dtb_ld16(const void *p) { const uint8_t *bp = (const uint8_t *)p; @@ -86,6 +91,16 @@ static inline uint64_t dtb_ld64(const void *p) #define streq(a, b) (strcmp((a), (b)) == 0) #define strstarts(s, prefix) (strncmp((s), (prefix), strlen(prefix)) == 0) #define strprefixeq(a, n, b) (strlen(b) == (n) && (memcmp(a, b, n) == 0)) +static inline bool strends(const char *str, const char *suffix) +{ + unsigned int len, suffix_len; + + len = strlen(str); + suffix_len = strlen(suffix); + if (len < suffix_len) + return false; + return streq(str + len - suffix_len, suffix); +} #define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) @@ -101,6 +116,12 @@ enum markertype { TYPE_UINT64, TYPE_STRING, }; + +static inline bool is_type_marker(enum markertype type) +{ + return type >= TYPE_UINT8; +} + extern const char *markername(enum markertype markertype); struct marker { @@ -125,7 +146,22 @@ struct data { for_each_marker(m) \ if ((m)->type == (t)) -size_t type_marker_length(struct marker *m); +static inline struct marker *next_type_marker(struct marker *m) +{ + for_each_marker(m) + if (is_type_marker(m->type)) + break; + return m; +} + +static inline size_t type_marker_length(struct marker *m) +{ + struct marker *next = next_type_marker(m->next); + + if (next) + return next->offset - m->offset; + return 0; +} void data_free(struct data d); diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c index 4659afbfcb..95e43d32c3 100644 --- a/scripts/dtc/flattree.c +++ b/scripts/dtc/flattree.c @@ -124,7 +124,8 @@ static void asm_emit_cell(void *e, cell_t val) { FILE *f = e; - fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n", + fprintf(f, "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n" + "\t.byte\t0x%02x\n" "\t.byte\t0x%02x\n", (val >> 24) & 0xff, (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff); } @@ -134,9 +135,9 @@ static void asm_emit_string(void *e, const char *str, int len) FILE *f = e; if (len != 0) - fprintf(f, "\t.string\t\"%.*s\"\n", len, str); + fprintf(f, "\t.asciz\t\"%.*s\"\n", len, str); else - fprintf(f, "\t.string\t\"%s\"\n", str); + fprintf(f, "\t.asciz\t\"%s\"\n", str); } static void asm_emit_align(void *e, int a) @@ -295,7 +296,7 @@ static struct data flatten_reserve_list(struct reserve_info *reservelist, { struct reserve_info *re; struct data d = empty_data; - int j; + unsigned int j; for (re = reservelist; re; re = re->next) { d = data_append_re(d, re->address, re->size); @@ -438,7 +439,7 @@ static void dump_stringtable_asm(FILE *f, struct data strbuf) while (p < (strbuf.val + strbuf.len)) { len = strlen(p); - fprintf(f, "\t.string \"%s\"\n", p); + fprintf(f, "\t.asciz \"%s\"\n", p); p += len+1; } } diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c index 3e893073da..9fe7cf4b74 100644 --- a/scripts/dtc/libfdt/fdt.c +++ b/scripts/dtc/libfdt/fdt.c @@ -90,6 +90,10 @@ int fdt_check_header(const void *fdt) { size_t hdrsize; + /* The device tree must be at an 8-byte aligned address */ + if ((uintptr_t)fdt & 7) + return -FDT_ERR_ALIGNMENT; + if (fdt_magic(fdt) != FDT_MAGIC) return -FDT_ERR_BADMAGIC; if (!can_assume(LATEST)) { diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index f13458d165..3621d3651d 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -349,7 +349,10 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset, return offset; /* Try to place the new node after the parent's properties */ - fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + tag = fdt_next_tag(fdt, parentoffset, &nextoffset); + /* the fdt_subnode_offset_namelen() should ensure this never hits */ + if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE)) + return -FDT_ERR_INTERNAL; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); @@ -391,7 +394,9 @@ int fdt_del_node(void *fdt, int nodeoffset) } static void fdt_packblocks_(const char *old, char *new, - int mem_rsv_size, int struct_size) + int mem_rsv_size, + int struct_size, + int strings_size) { int mem_rsv_off, struct_off, strings_off; @@ -406,8 +411,7 @@ static void fdt_packblocks_(const char *old, char *new, fdt_set_off_dt_struct(new, struct_off); fdt_set_size_dt_struct(new, struct_size); - memmove(new + strings_off, old + fdt_off_dt_strings(old), - fdt_size_dt_strings(old)); + memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size); fdt_set_off_dt_strings(new, strings_off); fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); } @@ -467,7 +471,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize) return -FDT_ERR_NOSPACE; } - fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); + fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size, + fdt_size_dt_strings(fdt)); memmove(buf, tmp, newsize); fdt_set_magic(buf, FDT_MAGIC); @@ -487,7 +492,8 @@ int fdt_pack(void *fdt) mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); - fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt), + fdt_size_dt_strings(fdt)); fdt_set_totalsize(fdt, fdt_data_size_(fdt)); return 0; diff --git a/scripts/dtc/libfdt/fdt_strerror.c b/scripts/dtc/libfdt/fdt_strerror.c index b4356931b0..d852b77e81 100644 --- a/scripts/dtc/libfdt/fdt_strerror.c +++ b/scripts/dtc/libfdt/fdt_strerror.c @@ -39,6 +39,7 @@ static struct fdt_errtabent fdt_errtable[] = { FDT_ERRTABENT(FDT_ERR_BADOVERLAY), FDT_ERRTABENT(FDT_ERR_NOPHANDLES), FDT_ERRTABENT(FDT_ERR_BADFLAGS), + FDT_ERRTABENT(FDT_ERR_ALIGNMENT), }; #define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h index c42807a766..ce31e84485 100644 --- a/scripts/dtc/libfdt/libfdt.h +++ b/scripts/dtc/libfdt/libfdt.h @@ -131,6 +131,13 @@ uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); * to work even with unaligned pointers on platforms (such as ARMv5) that don't * like unaligned loads and stores. */ +static inline uint16_t fdt16_ld(const fdt16_t *p) +{ + const uint8_t *bp = (const uint8_t *)p; + + return ((uint16_t)bp[0] << 8) | bp[1]; +} + static inline uint32_t fdt32_ld(const fdt32_t *p) { const uint8_t *bp = (const uint8_t *)p; diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c index 7eacd02486..cc612370ec 100644 --- a/scripts/dtc/livetree.c +++ b/scripts/dtc/livetree.c @@ -526,7 +526,7 @@ struct node *get_node_by_path(struct node *tree, const char *path) p = strchr(path, '/'); for_each_child(tree, child) { - if (p && strprefixeq(path, p - path, child->name)) + if (p && strprefixeq(path, (size_t)(p - path), child->name)) return get_node_by_path(child, p+1); else if (!p && streq(path, child->name)) return child; @@ -559,7 +559,7 @@ struct node *get_node_by_phandle(struct node *tree, cell_t phandle) { struct node *child, *node; - if ((phandle == 0) || (phandle == -1)) { + if (!phandle_is_valid(phandle)) { assert(generate_fixups); return NULL; } @@ -594,7 +594,7 @@ cell_t get_node_phandle(struct node *root, struct node *node) static cell_t phandle = 1; /* FIXME: ick, static local */ struct data d = empty_data; - if ((node->phandle != 0) && (node->phandle != -1)) + if (phandle_is_valid(node->phandle)) return node->phandle; while (get_node_by_phandle(root, phandle)) diff --git a/scripts/dtc/treesource.c b/scripts/dtc/treesource.c index 061ba8c9c5..33fedee82d 100644 --- a/scripts/dtc/treesource.c +++ b/scripts/dtc/treesource.c @@ -124,27 +124,6 @@ static void write_propval_int(FILE *f, const char *p, size_t len, size_t width) } } -static bool has_data_type_information(struct marker *m) -{ - return m->type >= TYPE_UINT8; -} - -static struct marker *next_type_marker(struct marker *m) -{ - while (m && !has_data_type_information(m)) - m = m->next; - return m; -} - -size_t type_marker_length(struct marker *m) -{ - struct marker *next = next_type_marker(m->next); - - if (next) - return next->offset - m->offset; - return 0; -} - static const char *delim_start[] = { [TYPE_UINT8] = "[", [TYPE_UINT16] = "/bits/ 16 <", @@ -229,26 +208,39 @@ static void write_propval(FILE *f, struct property *prop) size_t chunk_len = (m->next ? m->next->offset : len) - m->offset; size_t data_len = type_marker_length(m) ? : len - m->offset; const char *p = &prop->val.val[m->offset]; + struct marker *m_phandle; - if (has_data_type_information(m)) { + if (is_type_marker(m->type)) { emit_type = m->type; fprintf(f, " %s", delim_start[emit_type]); } else if (m->type == LABEL) fprintf(f, " %s:", m->ref); - else if (m->offset) - fputc(' ', f); - if (emit_type == TYPE_NONE) { - assert(chunk_len == 0); + if (emit_type == TYPE_NONE || chunk_len == 0) continue; - } switch(emit_type) { case TYPE_UINT16: write_propval_int(f, p, chunk_len, 2); break; case TYPE_UINT32: - write_propval_int(f, p, chunk_len, 4); + m_phandle = prop->val.markers; + for_each_marker_of_type(m_phandle, REF_PHANDLE) + if (m->offset == m_phandle->offset) + break; + + if (m_phandle) { + if (m_phandle->ref[0] == '/') + fprintf(f, "&{%s}", m_phandle->ref); + else + fprintf(f, "&%s", m_phandle->ref); + if (chunk_len > 4) { + fputc(' ', f); + write_propval_int(f, p + 4, chunk_len - 4, 4); + } + } else { + write_propval_int(f, p, chunk_len, 4); + } break; case TYPE_UINT64: write_propval_int(f, p, chunk_len, 8); diff --git a/scripts/dtc/util.h b/scripts/dtc/util.h index a771b4654c..c45b2c295a 100644 --- a/scripts/dtc/util.h +++ b/scripts/dtc/util.h @@ -13,10 +13,10 @@ */ #ifdef __GNUC__ -#ifdef __clang__ -#define PRINTF(i, j) __attribute__((format (printf, i, j))) -#else +#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) #define PRINTF(i, j) __attribute__((format (gnu_printf, i, j))) +#else +#define PRINTF(i, j) __attribute__((format (printf, i, j))) #endif #define NORETURN __attribute__((noreturn)) #else diff --git a/scripts/dtc/yamltree.c b/scripts/dtc/yamltree.c index e63d32fe14..55908c829c 100644 --- a/scripts/dtc/yamltree.c +++ b/scripts/dtc/yamltree.c @@ -29,11 +29,12 @@ char *yaml_error_name[] = { (emitter)->problem, __func__, __LINE__); \ }) -static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, char *data, unsigned int len, int width) +static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, + char *data, unsigned int seq_offset, unsigned int len, int width) { yaml_event_t event; void *tag; - unsigned int off, start_offset = markers->offset; + unsigned int off; switch(width) { case 1: tag = "!u8"; break; @@ -66,7 +67,7 @@ static void yaml_propval_int(yaml_emitter_t *emitter, struct marker *markers, ch m = markers; is_phandle = false; for_each_marker_of_type(m, REF_PHANDLE) { - if (m->offset == (start_offset + off)) { + if (m->offset == (seq_offset + off)) { is_phandle = true; break; } @@ -114,6 +115,7 @@ static void yaml_propval(yaml_emitter_t *emitter, struct property *prop) yaml_event_t event; unsigned int len = prop->val.len; struct marker *m = prop->val.markers; + struct marker *markers = prop->val.markers; /* Emit the property name */ yaml_scalar_event_initialize(&event, NULL, @@ -151,19 +153,19 @@ static void yaml_propval(yaml_emitter_t *emitter, struct property *prop) switch(m->type) { case TYPE_UINT16: - yaml_propval_int(emitter, m, data, chunk_len, 2); + yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 2); break; case TYPE_UINT32: - yaml_propval_int(emitter, m, data, chunk_len, 4); + yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 4); break; case TYPE_UINT64: - yaml_propval_int(emitter, m, data, chunk_len, 8); + yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 8); break; case TYPE_STRING: yaml_propval_string(emitter, data, chunk_len); break; default: - yaml_propval_int(emitter, m, data, chunk_len, 1); + yaml_propval_int(emitter, markers, data, m->offset, chunk_len, 1); break; } } diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index ab9eb4cbe3..51d81c3f03 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig @@ -19,24 +19,10 @@ menuconfig GCC_PLUGINS if GCC_PLUGINS -config GCC_PLUGIN_CYC_COMPLEXITY - bool "Compute the cyclomatic complexity of a function" if EXPERT - depends on !COMPILE_TEST # too noisy - help - The complexity M of a function's control flow graph is defined as: - M = E - N + 2P - where - - E = the number of edges - N = the number of nodes - P = the number of connected components (exit nodes). - - Enabling this plugin reports the complexity to stderr during the - build. It mainly serves as a simple example of how to create a - gcc plugin for the kernel. - config GCC_PLUGIN_SANCOV bool + # Plugin can be removed once the kernel only supports GCC 6+ + depends on !CC_HAS_SANCOV_TRACE_PC help This plugin inserts a __sanitizer_cov_trace_pc() call at the start of basic blocks. It supports all gcc versions with plugin support (from @@ -83,8 +69,6 @@ config GCC_PLUGIN_RANDSTRUCT the existing seed and will be removed by a make mrproper or make distclean. - Note that the implementation requires gcc 4.7 or newer. - This plugin was ported from grsecurity/PaX. More information at: * https://grsecurity.net/ * https://pax.grsecurity.net/ diff --git a/scripts/gcc-plugins/arm_ssp_per_task_plugin.c b/scripts/gcc-plugins/arm_ssp_per_task_plugin.c index 8c1af9bdcb..7328d037f9 100644 --- a/scripts/gcc-plugins/arm_ssp_per_task_plugin.c +++ b/scripts/gcc-plugins/arm_ssp_per_task_plugin.c @@ -4,7 +4,7 @@ __visible int plugin_is_GPL_compatible; -static unsigned int sp_mask, canary_offset; +static unsigned int canary_offset; static unsigned int arm_pertask_ssp_rtl_execute(void) { @@ -13,7 +13,7 @@ static unsigned int arm_pertask_ssp_rtl_execute(void) for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) { const char *sym; rtx body; - rtx mask, masked_sp; + rtx current; /* * Find a SET insn involving a SYMBOL_REF to __stack_chk_guard @@ -30,19 +30,13 @@ static unsigned int arm_pertask_ssp_rtl_execute(void) /* * Replace the source of the SET insn with an expression that - * produces the address of the copy of the stack canary value - * stored in struct thread_info + * produces the address of the current task's stack canary value */ - mask = GEN_INT(sext_hwi(sp_mask, GET_MODE_PRECISION(Pmode))); - masked_sp = gen_reg_rtx(Pmode); + current = gen_reg_rtx(Pmode); - emit_insn_before(gen_rtx_set(masked_sp, - gen_rtx_AND(Pmode, - stack_pointer_rtx, - mask)), - insn); + emit_insn_before(gen_load_tp_hard(current), insn); - SET_SRC(body) = gen_rtx_PLUS(Pmode, masked_sp, + SET_SRC(body) = gen_rtx_PLUS(Pmode, current, GEN_INT(canary_offset)); } return 0; @@ -72,7 +66,6 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, const char * const plugin_name = plugin_info->base_name; const int argc = plugin_info->argc; const struct plugin_argument *argv = plugin_info->argv; - int tso = 0; int i; if (!plugin_default_version_check(version, &gcc_version)) { @@ -91,11 +84,6 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, return 1; } - if (!strcmp(argv[i].key, "tso")) { - tso = atoi(argv[i].value); - continue; - } - if (!strcmp(argv[i].key, "offset")) { canary_offset = atoi(argv[i].value); continue; @@ -105,9 +93,6 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, return 1; } - /* create the mask that produces the base of the stack */ - sp_mask = ~((1U << (12 + tso)) - 1); - PASS_INFO(arm_pertask_ssp_rtl, "expand", 1, PASS_POS_INSERT_AFTER); register_callback(plugin_info->base_name, PLUGIN_PASS_MANAGER_SETUP, diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h index 0c087614fc..9a1895747b 100644 --- a/scripts/gcc-plugins/gcc-common.h +++ b/scripts/gcc-plugins/gcc-common.h @@ -27,9 +27,7 @@ #include "except.h" #include "function.h" #include "toplev.h" -#if BUILDING_GCC_VERSION >= 5000 #include "expr.h" -#endif #include "basic-block.h" #include "intl.h" #include "ggc.h" @@ -39,11 +37,7 @@ #include "params.h" #endif -#if BUILDING_GCC_VERSION <= 4009 -#include "pointer-set.h" -#else #include "hash-map.h" -#endif #if BUILDING_GCC_VERSION >= 7000 #include "memmodel.h" @@ -92,16 +86,13 @@ #include "stmt.h" #include "gimplify.h" #include "gimple.h" -#include "tree-ssa-operands.h" #include "tree-phinodes.h" #include "tree-cfg.h" #include "gimple-iterator.h" #include "gimple-ssa.h" #include "ssa-iterators.h" -#if BUILDING_GCC_VERSION >= 5000 #include "builtins.h" -#endif /* missing from basic_block.h... */ void debug_dominance_info(enum cdi_direction dir); @@ -152,125 +143,6 @@ struct register_pass_info NAME##_pass_info = { \ #define TODO_dump_func 0 #define TODO_dump_cgraph 0 -#if BUILDING_GCC_VERSION <= 4009 -#define TODO_verify_il 0 -#define AVAIL_INTERPOSABLE AVAIL_OVERWRITABLE - -#define section_name_prefix LTO_SECTION_NAME_PREFIX -#define fatal_error(loc, gmsgid, ...) fatal_error((gmsgid), __VA_ARGS__) - -rtx emit_move_insn(rtx x, rtx y); - -typedef struct rtx_def rtx_insn; - -static inline const char *get_decl_section_name(const_tree decl) -{ - if (DECL_SECTION_NAME(decl) == NULL_TREE) - return NULL; - - return TREE_STRING_POINTER(DECL_SECTION_NAME(decl)); -} - -static inline void set_decl_section_name(tree node, const char *value) -{ - if (value) - DECL_SECTION_NAME(node) = build_string(strlen(value) + 1, value); - else - DECL_SECTION_NAME(node) = NULL; -} -#endif - -#if BUILDING_GCC_VERSION == 4009 -typedef struct gimple_statement_asm gasm; -typedef struct gimple_statement_base gassign; -typedef struct gimple_statement_call gcall; -typedef struct gimple_statement_base gcond; -typedef struct gimple_statement_base gdebug; -typedef struct gimple_statement_base ggoto; -typedef struct gimple_statement_phi gphi; -typedef struct gimple_statement_base greturn; - -static inline gasm *as_a_gasm(gimple stmt) -{ - return as_a(stmt); -} - -static inline const gasm *as_a_const_gasm(const_gimple stmt) -{ - return as_a(stmt); -} - -static inline gassign *as_a_gassign(gimple stmt) -{ - return stmt; -} - -static inline const gassign *as_a_const_gassign(const_gimple stmt) -{ - return stmt; -} - -static inline gcall *as_a_gcall(gimple stmt) -{ - return as_a(stmt); -} - -static inline const gcall *as_a_const_gcall(const_gimple stmt) -{ - return as_a(stmt); -} - -static inline gcond *as_a_gcond(gimple stmt) -{ - return stmt; -} - -static inline const gcond *as_a_const_gcond(const_gimple stmt) -{ - return stmt; -} - -static inline gdebug *as_a_gdebug(gimple stmt) -{ - return stmt; -} - -static inline const gdebug *as_a_const_gdebug(const_gimple stmt) -{ - return stmt; -} - -static inline ggoto *as_a_ggoto(gimple stmt) -{ - return stmt; -} - -static inline const ggoto *as_a_const_ggoto(const_gimple stmt) -{ - return stmt; -} - -static inline gphi *as_a_gphi(gimple stmt) -{ - return as_a(stmt); -} - -static inline const gphi *as_a_const_gphi(const_gimple stmt) -{ - return as_a(stmt); -} - -static inline greturn *as_a_greturn(gimple stmt) -{ - return stmt; -} - -static inline const greturn *as_a_const_greturn(const_gimple stmt) -{ - return stmt; -} -#endif - #define TODO_ggc_collect 0 #define NODE_SYMBOL(node) (node) #define NODE_DECL(node) (node)->decl @@ -282,7 +154,7 @@ static inline opt_pass *get_pass_for_id(int id) return g->get_passes()->get_pass_for_id(id); } -#if BUILDING_GCC_VERSION >= 5000 && BUILDING_GCC_VERSION < 6000 +#if BUILDING_GCC_VERSION < 6000 /* gimple related */ template <> template <> @@ -292,7 +164,6 @@ inline bool is_a_helper::test(const_gimple gs) } #endif -#if BUILDING_GCC_VERSION >= 5000 #define TODO_verify_ssa TODO_verify_il #define TODO_verify_flow TODO_verify_il #define TODO_verify_stmts TODO_verify_il @@ -533,7 +404,6 @@ static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimpl { referring_node->remove_stmt_references(stmt); } -#endif #if BUILDING_GCC_VERSION < 6000 #define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \ diff --git a/scripts/gcc-plugins/gcc-generate-gimple-pass.h b/scripts/gcc-plugins/gcc-generate-gimple-pass.h index 5178082873..503c074963 100644 --- a/scripts/gcc-plugins/gcc-generate-gimple-pass.h +++ b/scripts/gcc-plugins/gcc-generate-gimple-pass.h @@ -78,17 +78,6 @@ static const pass_data _PASS_NAME_PASS_DATA = { .type = GIMPLE_PASS, .name = _PASS_NAME_NAME, .optinfo_flags = OPTGROUP_NONE, -#if BUILDING_GCC_VERSION >= 5000 -#elif BUILDING_GCC_VERSION == 4009 - .has_gate = _HAS_GATE, - .has_execute = _HAS_EXECUTE, -#else - .gate = _GATE, - .execute = _EXECUTE, - .sub = NULL, - .next = NULL, - .static_pass_number = 0, -#endif .tv_id = TV_NONE, .properties_required = PROPERTIES_REQUIRED, .properties_provided = PROPERTIES_PROVIDED, @@ -102,21 +91,13 @@ class _PASS_NAME_PASS : public gimple_opt_pass { _PASS_NAME_PASS() : gimple_opt_pass(_PASS_NAME_PASS_DATA, g) {} #ifndef NO_GATE -#if BUILDING_GCC_VERSION >= 5000 virtual bool gate(function *) { return _GATE(); } -#else - virtual bool gate(void) { return _GATE(); } -#endif #endif virtual opt_pass * clone () { return new _PASS_NAME_PASS(); } #ifndef NO_EXECUTE -#if BUILDING_GCC_VERSION >= 5000 virtual unsigned int execute(function *) { return _EXECUTE(); } -#else - virtual unsigned int execute(void) { return _EXECUTE(); } -#endif }; } diff --git a/scripts/gcc-plugins/gcc-generate-ipa-pass.h b/scripts/gcc-plugins/gcc-generate-ipa-pass.h index c34ffec035..1e7f064e8f 100644 --- a/scripts/gcc-plugins/gcc-generate-ipa-pass.h +++ b/scripts/gcc-plugins/gcc-generate-ipa-pass.h @@ -146,17 +146,6 @@ static const pass_data _PASS_NAME_PASS_DATA = { .type = IPA_PASS, .name = _PASS_NAME_NAME, .optinfo_flags = OPTGROUP_NONE, -#if BUILDING_GCC_VERSION >= 5000 -#elif BUILDING_GCC_VERSION == 4009 - .has_gate = _HAS_GATE, - .has_execute = _HAS_EXECUTE, -#else - .gate = _GATE, - .execute = _EXECUTE, - .sub = NULL, - .next = NULL, - .static_pass_number = 0, -#endif .tv_id = TV_NONE, .properties_required = PROPERTIES_REQUIRED, .properties_provided = PROPERTIES_PROVIDED, @@ -180,20 +169,12 @@ class _PASS_NAME_PASS : public ipa_opt_pass_d { _VARIABLE_TRANSFORM) {} #ifndef NO_GATE -#if BUILDING_GCC_VERSION >= 5000 virtual bool gate(function *) { return _GATE(); } -#else - virtual bool gate(void) { return _GATE(); } -#endif virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } #ifndef NO_EXECUTE -#if BUILDING_GCC_VERSION >= 5000 virtual unsigned int execute(function *) { return _EXECUTE(); } -#else - virtual unsigned int execute(void) { return _EXECUTE(); } -#endif #endif }; } diff --git a/scripts/gcc-plugins/gcc-generate-rtl-pass.h b/scripts/gcc-plugins/gcc-generate-rtl-pass.h index d14614f4b1..7cd46e8d50 100644 --- a/scripts/gcc-plugins/gcc-generate-rtl-pass.h +++ b/scripts/gcc-plugins/gcc-generate-rtl-pass.h @@ -78,17 +78,6 @@ static const pass_data _PASS_NAME_PASS_DATA = { .type = RTL_PASS, .name = _PASS_NAME_NAME, .optinfo_flags = OPTGROUP_NONE, -#if BUILDING_GCC_VERSION >= 5000 -#elif BUILDING_GCC_VERSION == 4009 - .has_gate = _HAS_GATE, - .has_execute = _HAS_EXECUTE, -#else - .gate = _GATE, - .execute = _EXECUTE, - .sub = NULL, - .next = NULL, - .static_pass_number = 0, -#endif .tv_id = TV_NONE, .properties_required = PROPERTIES_REQUIRED, .properties_provided = PROPERTIES_PROVIDED, @@ -102,21 +91,13 @@ class _PASS_NAME_PASS : public rtl_opt_pass { _PASS_NAME_PASS() : rtl_opt_pass(_PASS_NAME_PASS_DATA, g) {} #ifndef NO_GATE -#if BUILDING_GCC_VERSION >= 5000 virtual bool gate(function *) { return _GATE(); } -#else - virtual bool gate(void) { return _GATE(); } -#endif #endif virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } #ifndef NO_EXECUTE -#if BUILDING_GCC_VERSION >= 5000 virtual unsigned int execute(function *) { return _EXECUTE(); } -#else - virtual unsigned int execute(void) { return _EXECUTE(); } -#endif #endif }; } diff --git a/scripts/gcc-plugins/gcc-generate-simple_ipa-pass.h b/scripts/gcc-plugins/gcc-generate-simple_ipa-pass.h index ef6f4c2cb6..33093ccc94 100644 --- a/scripts/gcc-plugins/gcc-generate-simple_ipa-pass.h +++ b/scripts/gcc-plugins/gcc-generate-simple_ipa-pass.h @@ -78,17 +78,6 @@ static const pass_data _PASS_NAME_PASS_DATA = { .type = SIMPLE_IPA_PASS, .name = _PASS_NAME_NAME, .optinfo_flags = OPTGROUP_NONE, -#if BUILDING_GCC_VERSION >= 5000 -#elif BUILDING_GCC_VERSION == 4009 - .has_gate = _HAS_GATE, - .has_execute = _HAS_EXECUTE, -#else - .gate = _GATE, - .execute = _EXECUTE, - .sub = NULL, - .next = NULL, - .static_pass_number = 0, -#endif .tv_id = TV_NONE, .properties_required = PROPERTIES_REQUIRED, .properties_provided = PROPERTIES_PROVIDED, @@ -102,21 +91,13 @@ class _PASS_NAME_PASS : public simple_ipa_opt_pass { _PASS_NAME_PASS() : simple_ipa_opt_pass(_PASS_NAME_PASS_DATA, g) {} #ifndef NO_GATE -#if BUILDING_GCC_VERSION >= 5000 virtual bool gate(function *) { return _GATE(); } -#else - virtual bool gate(void) { return _GATE(); } -#endif #endif virtual opt_pass *clone() { return new _PASS_NAME_PASS(); } #ifndef NO_EXECUTE -#if BUILDING_GCC_VERSION >= 5000 virtual unsigned int execute(function *) { return _EXECUTE(); } -#else - virtual unsigned int execute(void) { return _EXECUTE(); } -#endif #endif }; } diff --git a/scripts/gcc-plugins/structleak_plugin.c b/scripts/gcc-plugins/structleak_plugin.c index d7190e443a..74e3192883 100644 --- a/scripts/gcc-plugins/structleak_plugin.c +++ b/scripts/gcc-plugins/structleak_plugin.c @@ -103,10 +103,8 @@ static void finish_type(void *event_data, void *data) if (type == NULL_TREE || type == error_mark_node) return; -#if BUILDING_GCC_VERSION >= 5000 if (TREE_CODE(type) == ENUMERAL_TYPE) return; -#endif if (TYPE_USERSPACE(type)) return; diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py index a92c55bd8d..d5983cf3db 100644 --- a/scripts/gdb/linux/dmesg.py +++ b/scripts/gdb/linux/dmesg.py @@ -44,19 +44,17 @@ class LxDmesg(gdb.Command): sz = prb_desc_ring_type.get_type().sizeof desc_ring = utils.read_memoryview(inf, addr, sz).tobytes() - # read in descriptor array + # read in descriptor count, size, and address off = prb_desc_ring_type.get_type()['count_bits'].bitpos // 8 desc_ring_count = 1 << utils.read_u32(desc_ring, off) desc_sz = prb_desc_type.get_type().sizeof off = prb_desc_ring_type.get_type()['descs'].bitpos // 8 - addr = utils.read_ulong(desc_ring, off) - descs = utils.read_memoryview(inf, addr, desc_sz * desc_ring_count).tobytes() + desc_addr = utils.read_ulong(desc_ring, off) - # read in info array + # read in info size and address info_sz = printk_info_type.get_type().sizeof off = prb_desc_ring_type.get_type()['infos'].bitpos // 8 - addr = utils.read_ulong(desc_ring, off) - infos = utils.read_memoryview(inf, addr, info_sz * desc_ring_count).tobytes() + info_addr = utils.read_ulong(desc_ring, off) # read in text data ring structure off = printk_ringbuffer_type.get_type()['text_data_ring'].bitpos // 8 @@ -64,12 +62,11 @@ class LxDmesg(gdb.Command): sz = prb_data_ring_type.get_type().sizeof text_data_ring = utils.read_memoryview(inf, addr, sz).tobytes() - # read in text data + # read in text data size and address off = prb_data_ring_type.get_type()['size_bits'].bitpos // 8 text_data_sz = 1 << utils.read_u32(text_data_ring, off) off = prb_data_ring_type.get_type()['data'].bitpos // 8 - addr = utils.read_ulong(text_data_ring, off) - text_data = utils.read_memoryview(inf, addr, text_data_sz).tobytes() + text_data_addr = utils.read_ulong(text_data_ring, off) counter_off = atomic_long_type.get_type()['counter'].bitpos // 8 @@ -102,17 +99,20 @@ class LxDmesg(gdb.Command): desc_off = desc_sz * ind info_off = info_sz * ind + desc = utils.read_memoryview(inf, desc_addr + desc_off, desc_sz).tobytes() + # skip non-committed record - state = 3 & (utils.read_u64(descs, desc_off + sv_off + - counter_off) >> desc_flags_shift) + state = 3 & (utils.read_u64(desc, sv_off + counter_off) >> desc_flags_shift) if state != desc_committed and state != desc_finalized: if did == head_id: break did = (did + 1) & desc_id_mask continue - begin = utils.read_ulong(descs, desc_off + begin_off) % text_data_sz - end = utils.read_ulong(descs, desc_off + next_off) % text_data_sz + begin = utils.read_ulong(desc, begin_off) % text_data_sz + end = utils.read_ulong(desc, next_off) % text_data_sz + + info = utils.read_memoryview(inf, info_addr + info_off, info_sz).tobytes() # handle data-less record if begin & 1 == 1: @@ -125,16 +125,17 @@ class LxDmesg(gdb.Command): # skip over descriptor id text_start = begin + utils.get_long_type().sizeof - text_len = utils.read_u16(infos, info_off + len_off) + text_len = utils.read_u16(info, len_off) # handle truncated message if end - text_start < text_len: text_len = end - text_start - text = text_data[text_start:text_start + text_len].decode( - encoding='utf8', errors='replace') + text_data = utils.read_memoryview(inf, text_data_addr + text_start, + text_len).tobytes() + text = text_data[0:text_len].decode(encoding='utf8', errors='replace') - time_stamp = utils.read_u64(infos, info_off + ts_off) + time_stamp = utils.read_u64(info, ts_off) for line in text.splitlines(): msg = u"[{time:12.6f}] {line}\n".format( diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 08d264ac32..46f7542db0 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -148,7 +148,8 @@ lx-symbols command.""" # drop all current symbols and reload vmlinux orig_vmlinux = 'vmlinux' for obj in gdb.objfiles(): - if obj.filename.endswith('vmlinux'): + if (obj.filename.endswith('vmlinux') or + obj.filename.endswith('vmlinux.debug')): orig_vmlinux = obj.filename gdb.execute("symbol-file", to_string=True) gdb.execute("symbol-file {0}".format(orig_vmlinux)) diff --git a/scripts/gen_autoksyms.sh b/scripts/gen_autoksyms.sh index 6ed0d225c8..120225c541 100644 --- a/scripts/gen_autoksyms.sh +++ b/scripts/gen_autoksyms.sh @@ -16,20 +16,15 @@ case "$KBUILD_VERBOSE" in ;; esac -# We need access to CONFIG_ symbols -. include/config/auto.conf - needed_symbols= # Special case for modversions (see modpost.c) -if [ -n "$CONFIG_MODVERSIONS" ]; then +if grep -q "^CONFIG_MODVERSIONS=y$" include/config/auto.conf; then needed_symbols="$needed_symbols module_layout" fi -ksym_wl= -if [ -n "$CONFIG_UNUSED_KSYMS_WHITELIST" ]; then - # Use 'eval' to expand the whitelist path and check if it is relative - eval ksym_wl="$CONFIG_UNUSED_KSYMS_WHITELIST" +ksym_wl=$(sed -n 's/^CONFIG_UNUSED_KSYMS_WHITELIST=\(.*\)$/\1/p' include/config/auto.conf) +if [ -n "$ksym_wl" ]; then [ "${ksym_wl}" != "${ksym_wl#/}" ] || ksym_wl="$abs_srctree/$ksym_wl" if [ ! -f "$ksym_wl" ] || [ ! -r "$ksym_wl" ]; then echo "ERROR: '$ksym_wl' whitelist file not found" >&2 diff --git a/scripts/get_abi.pl b/scripts/get_abi.pl index d7aa820942..6212f58b69 100644 --- a/scripts/get_abi.pl +++ b/scripts/get_abi.pl @@ -1,19 +1,37 @@ #!/usr/bin/env perl # SPDX-License-Identifier: GPL-2.0 +BEGIN { $Pod::Usage::Formatter = 'Pod::Text::Termcap'; } + use strict; use warnings; use utf8; -use Pod::Usage; +use Pod::Usage qw(pod2usage); use Getopt::Long; use File::Find; +use IO::Handle; use Fcntl ':mode'; +use Cwd 'abs_path'; +use Data::Dumper; my $help = 0; +my $hint = 0; my $man = 0; my $debug = 0; my $enable_lineno = 0; +my $show_warnings = 1; my $prefix="Documentation/ABI"; +my $sysfs_prefix="/sys"; +my $search_string; + +# Debug options +my $dbg_what_parsing = 1; +my $dbg_what_open = 2; +my $dbg_dump_abi_structs = 4; +my $dbg_undefined = 8; + +$Data::Dumper::Indent = 1; +$Data::Dumper::Terse = 1; # # If true, assumes that the description is formatted with ReST @@ -21,25 +39,27 @@ my $prefix="Documentation/ABI"; my $description_is_rst = 1; GetOptions( - "debug|d+" => \$debug, + "debug=i" => \$debug, "enable-lineno" => \$enable_lineno, "rst-source!" => \$description_is_rst, "dir=s" => \$prefix, 'help|?' => \$help, + "show-hints" => \$hint, + "search-string=s" => \$search_string, man => \$man ) or pod2usage(2); pod2usage(1) if $help; -pod2usage(-exitstatus => 0, -verbose => 2) if $man; +pod2usage(-exitstatus => 0, -noperldoc, -verbose => 2) if $man; pod2usage(2) if (scalar @ARGV < 1 || @ARGV > 2); my ($cmd, $arg) = @ARGV; -pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate"); +pod2usage(2) if ($cmd ne "search" && $cmd ne "rest" && $cmd ne "validate" && $cmd ne "undefined"); pod2usage(2) if ($cmd eq "search" && !$arg); -require Data::Dumper if ($debug); +require Data::Dumper if ($debug & $dbg_dump_abi_structs); my %data; my %symbols; @@ -50,6 +70,8 @@ my %symbols; sub parse_error($$$$) { my ($file, $ln, $msg, $data) = @_; + return if (!$show_warnings); + $data =~ s/\s+$/\n/; print STDERR "Warning: file $file#$ln:\n\t$msg"; @@ -97,7 +119,7 @@ sub parse_abi { my @labels; my $label = ""; - print STDERR "Opening $file\n" if ($debug > 1); + print STDERR "Opening $file\n" if ($debug & $dbg_what_open); open IN, $file; while() { $ln++; @@ -129,12 +151,12 @@ sub parse_abi { push @{$symbols{$content}->{file}}, " $file:" . ($ln - 1); if ($tag =~ m/what/) { - $what .= ", " . $content; + $what .= "\xac" . $content; } else { if ($what) { parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description}); - foreach my $w(split /, /, $what) { + foreach my $w(split /\xac/, $what) { $symbols{$w}->{xref} = $what; }; } @@ -164,12 +186,13 @@ sub parse_abi { $data{$what}->{file} = $name; $data{$what}->{filepath} = $file; } else { + $data{$what}->{description} .= "\n\n" if (defined($data{$what}->{description})); if ($name ne $data{$what}->{file}) { $data{$what}->{file} .= " " . $name; $data{$what}->{filepath} .= " " . $file; } } - print STDERR "\twhat: $what\n" if ($debug > 1); + print STDERR "\twhat: $what\n" if ($debug & $dbg_what_parsing); $data{$what}->{line_no} = $ln; } else { $data{$what}->{line_no} = $ln if (!defined($data{$what}->{line_no})); @@ -239,7 +262,7 @@ sub parse_abi { if ($what) { parse_error($file, $ln, "What '$what' doesn't have a description", "") if (!$data{$what}->{description}); - foreach my $w(split /, /,$what) { + foreach my $w(split /\xac/,$what) { $symbols{$w}->{xref} = $what; }; } @@ -328,7 +351,7 @@ sub output_rest { printf ".. _%s:\n\n", $data{$what}->{label}; - my @names = split /, /,$w; + my @names = split /\xac/,$w; my $len = 0; foreach my $name (@names) { @@ -492,6 +515,7 @@ sub search_symbols { my $file = $data{$what}->{filepath}; + $what =~ s/\xac/, /g; my $bar = $what; $bar =~ s/./-/g; @@ -521,22 +545,420 @@ sub search_symbols { } } +# Exclude /sys/kernel/debug and /sys/kernel/tracing from the search path +sub dont_parse_special_attributes { + if (($File::Find::dir =~ m,^/sys/kernel,)) { + return grep {!/(debug|tracing)/ } @_; + } + + if (($File::Find::dir =~ m,^/sys/fs,)) { + return grep {!/(pstore|bpf|fuse)/ } @_; + } + + return @_ +} + +my %leaf; +my %aliases; +my @files; +my %root; + +sub graph_add_file { + my $file = shift; + my $type = shift; + + my $dir = $file; + $dir =~ s,^(.*/).*,$1,; + $file =~ s,.*/,,; + + my $name; + my $file_ref = \%root; + foreach my $edge(split "/", $dir) { + $name .= "$edge/"; + if (!defined ${$file_ref}{$edge}) { + ${$file_ref}{$edge} = { }; + } + $file_ref = \%{$$file_ref{$edge}}; + ${$file_ref}{"__name"} = [ $name ]; + } + $name .= "$file"; + ${$file_ref}{$file} = { + "__name" => [ $name ] + }; + + return \%{$$file_ref{$file}}; +} + +sub graph_add_link { + my $file = shift; + my $link = shift; + + # Traverse graph to find the reference + my $file_ref = \%root; + foreach my $edge(split "/", $file) { + $file_ref = \%{$$file_ref{$edge}} || die "Missing node!"; + } + + # do a BFS + + my @queue; + my %seen; + my $st; + + push @queue, $file_ref; + $seen{$start}++; + + while (@queue) { + my $v = shift @queue; + my @child = keys(%{$v}); + + foreach my $c(@child) { + next if $seen{$$v{$c}}; + next if ($c eq "__name"); + + if (!defined($$v{$c}{"__name"})) { + printf STDERR "Error: Couldn't find a non-empty name on a children of $file/.*: "; + print STDERR Dumper(%{$v}); + exit; + } + + # Add new name + my $name = @{$$v{$c}{"__name"}}[0]; + if ($name =~ s#^$file/#$link/#) { + push @{$$v{$c}{"__name"}}, $name; + } + # Add child to the queue and mark as seen + push @queue, $$v{$c}; + $seen{$c}++; + } + } +} + +my $escape_symbols = qr { ([\x01-\x08\x0e-\x1f\x21-\x29\x2b-\x2d\x3a-\x40\x7b-\xfe]) }x; +sub parse_existing_sysfs { + my $file = $File::Find::name; + + my $mode = (lstat($file))[2]; + my $abs_file = abs_path($file); + + my @tmp; + push @tmp, $file; + push @tmp, $abs_file if ($abs_file ne $file); + + foreach my $f(@tmp) { + # Ignore cgroup, as this is big and has zero docs under ABI + return if ($f =~ m#^/sys/fs/cgroup/#); + + # Ignore firmware as it is documented elsewhere + # Either ACPI or under Documentation/devicetree/bindings/ + return if ($f =~ m#^/sys/firmware/#); + + # Ignore some sysfs nodes that aren't actually part of ABI + return if ($f =~ m#/sections|notes/#); + + # Would need to check at + # Documentation/admin-guide/kernel-parameters.txt, but this + # is not easily parseable. + return if ($f =~ m#/parameters/#); + } + + if (S_ISLNK($mode)) { + $aliases{$file} = $abs_file; + return; + } + + return if (S_ISDIR($mode)); + + # Trivial: file is defined exactly the same way at ABI What: + return if (defined($data{$file})); + return if (defined($data{$abs_file})); + + push @files, graph_add_file($abs_file, "file"); +} + +sub get_leave($) +{ + my $what = shift; + my $leave; + + my $l = $what; + my $stop = 1; + + $leave = $l; + $leave =~ s,/$,,; + $leave =~ s,.*/,,; + $leave =~ s/[\(\)]//g; + + # $leave is used to improve search performance at + # check_undefined_symbols, as the algorithm there can seek + # for a small number of "what". It also allows giving a + # hint about a leave with the same name somewhere else. + # However, there are a few occurences where the leave is + # either a wildcard or a number. Just group such cases + # altogether. + if ($leave =~ m/\.\*/ || $leave eq "" || $leave =~ /\\d/) { + $leave = "others"; + } + + return $leave; +} + +my @not_found; + +sub check_file($$) +{ + my $file_ref = shift; + my $names_ref = shift; + my @names = @{$names_ref}; + my $file = $names[0]; + + my $found_string; + + my $leave = get_leave($file); + if (!defined($leaf{$leave})) { + $leave = "others"; + } + my @expr = @{$leaf{$leave}->{expr}}; + die ("\rmissing rules for $leave") if (!defined($leaf{$leave})); + + my $path = $file; + $path =~ s,(.*/).*,$1,; + + if ($search_string) { + return if (!($file =~ m#$search_string#)); + $found_string = 1; + } + + for (my $i = 0; $i < @names; $i++) { + if ($found_string && $hint) { + if (!$i) { + print STDERR "--> $names[$i]\n"; + } else { + print STDERR " $names[$i]\n"; + } + } + foreach my $re (@expr) { + print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined); + if ($names[$i] =~ $re) { + return; + } + } + } + + if ($leave ne "others") { + my @expr = @{$leaf{"others"}->{expr}}; + for (my $i = 0; $i < @names; $i++) { + foreach my $re (@expr) { + print STDERR "$names[$i] =~ /^$re\$/\n" if ($debug && $dbg_undefined); + if ($names[$i] =~ $re) { + return; + } + } + } + } + + push @not_found, $file if (!$search_string || $found_string); + + if ($hint && (!$search_string || $found_string)) { + my $what = $leaf{$leave}->{what}; + $what =~ s/\xac/\n\t/g; + if ($leave ne "others") { + print STDERR "\r more likely regexes:\n\t$what\n"; + } else { + print STDERR "\r tested regexes:\n\t$what\n"; + } + } +} + +sub check_undefined_symbols { + my $num_files = scalar @files; + my $next_i = 0; + my $start_time = times; + + @files = sort @files; + + my $last_time = $start_time; + + # When either debug or hint is enabled, there's no sense showing + # progress, as the progress will be overriden. + if ($hint || ($debug && $dbg_undefined)) { + $next_i = $num_files; + } + + my $is_console; + $is_console = 1 if (-t STDERR); + + for (my $i = 0; $i < $num_files; $i++) { + my $file_ref = $files[$i]; + my @names = @{$$file_ref{"__name"}}; + + check_file($file_ref, \@names); + + my $cur_time = times; + + if ($i == $next_i || $cur_time > $last_time + 1) { + my $percent = $i * 100 / $num_files; + + my $tm = $cur_time - $start_time; + my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm)); + + printf STDERR "\33[2K\r", if ($is_console); + printf STDERR "%s: processing sysfs files... %i%%: $names[0]", $time, $percent; + printf STDERR "\n", if (!$is_console); + STDERR->flush(); + + $next_i = int (($percent + 1) * $num_files / 100); + $last_time = $cur_time; + } + } + + my $cur_time = times; + my $tm = $cur_time - $start_time; + my $time = sprintf "%d:%02d", int($tm), 60 * ($tm - int($tm)); + + printf STDERR "\33[2K\r", if ($is_console); + printf STDERR "%s: processing sysfs files... done\n", $time; + + foreach my $file (@not_found) { + print "$file not found.\n"; + } +} + +sub undefined_symbols { + print STDERR "Reading $sysfs_prefix directory contents..."; + find({ + wanted =>\&parse_existing_sysfs, + preprocess =>\&dont_parse_special_attributes, + no_chdir => 1 + }, $sysfs_prefix); + print STDERR "done.\n"; + + $leaf{"others"}->{what} = ""; + + print STDERR "Converting ABI What fields into regexes..."; + foreach my $w (sort keys %data) { + foreach my $what (split /\xac/,$w) { + next if (!($what =~ m/^$sysfs_prefix/)); + + # Convert what into regular expressions + + # Escape dot characters + $what =~ s/\./\xf6/g; + + # Temporarily change [0-9]+ type of patterns + $what =~ s/\[0\-9\]\+/\xff/g; + + # Temporarily change [\d+-\d+] type of patterns + $what =~ s/\[0\-\d+\]/\xff/g; + $what =~ s/\[(\d+)\]/\xf4$1\xf5/g; + + # Temporarily change [0-9] type of patterns + $what =~ s/\[(\d)\-(\d)\]/\xf4$1-$2\xf5/g; + + # Handle multiple option patterns + $what =~ s/[\{\<\[]([\w_]+)(?:[,|]+([\w_]+)){1,}[\}\>\]]/($1|$2)/g; + + # Handle wildcards + $what =~ s,\*,.*,g; + $what =~ s,/\xf6..,/.*,g; + $what =~ s/\<[^\>]+\>/.*/g; + $what =~ s/\{[^\}]+\}/.*/g; + $what =~ s/\[[^\]]+\]/.*/g; + + $what =~ s/[XYZ]/.*/g; + + # Recover [0-9] type of patterns + $what =~ s/\xf4/[/g; + $what =~ s/\xf5/]/g; + + # Remove duplicated spaces + $what =~ s/\s+/ /g; + + # Special case: this ABI has a parenthesis on it + $what =~ s/sqrt\(x^2\+y^2\+z^2\)/sqrt\(x^2\+y^2\+z^2\)/; + + # Special case: drop comparition as in: + # What: foo = + # (this happens on a few IIO definitions) + $what =~ s,\s*\=.*$,,; + + # Escape all other symbols + $what =~ s/$escape_symbols/\\$1/g; + $what =~ s/\\\\/\\/g; + $what =~ s/\\([\[\]\(\)\|])/$1/g; + $what =~ s/(\d+)\\(-\d+)/$1$2/g; + + $what =~ s/\xff/\\d+/g; + + # Special case: IIO ABI which a parenthesis. + $what =~ s/sqrt(.*)/sqrt\(.*\)/; + + # Simplify regexes with multiple .* + $what =~ s#(?:\.\*){2,}##g; +# $what =~ s#\.\*/\.\*#.*#g; + + # Recover dot characters + $what =~ s/\xf6/\./g; + + my $leave = get_leave($what); + + my $added = 0; + foreach my $l (split /\|/, $leave) { + if (defined($leaf{$l})) { + next if ($leaf{$l}->{what} =~ m/\b$what\b/); + $leaf{$l}->{what} .= "\xac" . $what; + $added = 1; + } else { + $leaf{$l}->{what} = $what; + $added = 1; + } + } + if ($search_string && $added) { + print STDERR "What: $what\n" if ($what =~ m#$search_string#); + } + + } + } + # Compile regexes + foreach my $l (sort keys %leaf) { + my @expr; + foreach my $w(sort split /\xac/, $leaf{$l}->{what}) { + push @expr, qr /^$w$/; + } + $leaf{$l}->{expr} = \@expr; + } + + # Take links into account + foreach my $link (sort keys %aliases) { + my $abs_file = $aliases{$link}; + graph_add_link($abs_file, $link); + } + print STDERR "done.\n"; + + check_undefined_symbols; +} + # Ensure that the prefix will always end with a slash # While this is not needed for find, it makes the patch nicer # with --enable-lineno $prefix =~ s,/?$,/,; +if ($cmd eq "undefined" || $cmd eq "search") { + $show_warnings = 0; +} # # Parses all ABI files located at $prefix dir # find({wanted =>\&parse_abi, no_chdir => 1}, $prefix); -print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug); +print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug & $dbg_dump_abi_structs); # # Handles the command # -if ($cmd eq "search") { +if ($cmd eq "undefined") { + undefined_symbols; +} elsif ($cmd eq "search") { search_symbols; } else { if ($cmd eq "rest") { @@ -562,18 +984,23 @@ abi_book.pl - parse the Linux ABI files and produce a ReST book. =head1 SYNOPSIS -B [--debug] [--enable-lineno] [--man] [--help] - [--(no-)rst-source] [--dir=] [] +B [--debug ] [--enable-lineno] [--man] [--help] + [--(no-)rst-source] [--dir=] [--show-hints] + [--search-string ] + [] -Where can be: +Where B can be: =over 8 -B [SEARCH_REGEX] - search for [SEARCH_REGEX] inside ABI +B I - search for I inside ABI -B - output the ABI in ReST markup language +B - output the ABI in ReST markup language -B - validate the ABI contents +B - validate the ABI contents + +B - existing symbols at the system that aren't + defined at Documentation/ABI =back @@ -589,18 +1016,32 @@ the Documentation/ABI directory. =item B<--rst-source> and B<--no-rst-source> The input file may be using ReST syntax or not. Those two options allow -selecting between a rst-compliant source ABI (--rst-source), or a +selecting between a rst-compliant source ABI (B<--rst-source>), or a plain text that may be violating ReST spec, so it requres some escaping -logic (--no-rst-source). +logic (B<--no-rst-source>). =item B<--enable-lineno> Enable output of #define LINENO lines. -=item B<--debug> +=item B<--debug> I -Put the script in verbose mode, useful for debugging. Can be called multiple -times, to increase verbosity. +Print debug information according with the level, which is given by the +following bitmask: + + - 1: Debug parsing What entries from ABI files; + - 2: Shows what files are opened from ABI files; + - 4: Dump the structs used to store the contents of the ABI files. + +=item B<--show-hints> + +Show hints about possible definitions for the missing ABI symbols. +Used only when B. + +=item B<--search-string> I + +Show only occurences that match a search string. +Used only when B. =item B<--help> @@ -646,11 +1087,11 @@ $ scripts/get_abi.pl rest --dir Documentation/ABI/obsolete =head1 BUGS -Report bugs to Mauro Carvalho Chehab +Report bugs to Mauro Carvalho Chehab =head1 COPYRIGHT -Copyright (c) 2016-2019 by Mauro Carvalho Chehab . +Copyright (c) 2016-2021 by Mauro Carvalho Chehab . License GPLv2: GNU GPL version 2 . diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 2075db0c08..6bd5221d37 100644 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -1718,7 +1718,7 @@ sub vcs_exists { %VCS_cmds = %VCS_cmds_hg; return 2 if eval $VCS_cmds{"available"}; %VCS_cmds = (); - if (!$printed_novcs) { + if (!$printed_novcs && $email_git) { warn("$P: No supported VCS found. Add --nogit to options?\n"); warn("Using a git repository produces better results.\n"); warn("Try Linus Torvalds' latest git repository using:\n"); diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 5a215880b2..b8ef0fb4bb 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -69,7 +69,7 @@ localyesconfig localmodconfig: $(obj)/conf # deprecated for external use simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \ alldefconfig randconfig listnewconfig olddefconfig syncconfig \ - helpnewconfig yes2modconfig mod2yesconfig + helpnewconfig yes2modconfig mod2yesconfig mod2noconfig PHONY += $(simple-targets) @@ -134,6 +134,7 @@ help: @echo ' randconfig - New config with random answer to all options' @echo ' yes2modconfig - Change answers from yes to mod if possible' @echo ' mod2yesconfig - Change answers from mod to yes if possible' + @echo ' mod2noconfig - Change answers from mod to no if possible' @echo ' listnewconfig - List new options' @echo ' helpnewconfig - List new options and help text' @echo ' olddefconfig - Same as oldconfig but sets new symbols to their' diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 5d84b44a2a..4178065ca2 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -35,6 +35,7 @@ enum input_mode { olddefconfig, yes2modconfig, mod2yesconfig, + mod2noconfig, }; static enum input_mode input_mode = oldaskconfig; static int input_mode_opt; @@ -163,8 +164,6 @@ enum conf_def_mode { def_default, def_yes, def_mod, - def_y2m, - def_m2y, def_no, def_random }; @@ -302,12 +301,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) return has_changed; } -static void conf_rewrite_mod_or_yes(enum conf_def_mode mode) +static void conf_rewrite_tristates(tristate old_val, tristate new_val) { struct symbol *sym; int i; - tristate old_val = (mode == def_y2m) ? yes : mod; - tristate new_val = (mode == def_y2m) ? mod : yes; for_all_symbols(i, sym) { if (sym_get_type(sym) == S_TRISTATE && @@ -646,19 +643,8 @@ static void check_conf(struct menu *menu) switch (input_mode) { case listnewconfig: - if (sym->name) { - const char *str; - - if (sym->type == S_STRING) { - str = sym_get_string_value(sym); - str = sym_escape_string_value(str); - printf("%s%s=%s\n", CONFIG_, sym->name, str); - free((void *)str); - } else { - str = sym_get_string_value(sym); - printf("%s%s=%s\n", CONFIG_, sym->name, str); - } - } + if (sym->name) + print_symbol_for_listconfig(sym); break; case helpnewconfig: printf("-----\n"); @@ -696,6 +682,7 @@ static const struct option long_opts[] = { {"olddefconfig", no_argument, &input_mode_opt, olddefconfig}, {"yes2modconfig", no_argument, &input_mode_opt, yes2modconfig}, {"mod2yesconfig", no_argument, &input_mode_opt, mod2yesconfig}, + {"mod2noconfig", no_argument, &input_mode_opt, mod2noconfig}, {NULL, 0, NULL, 0} }; @@ -724,6 +711,7 @@ static void conf_usage(const char *progname) printf(" --randconfig New config with random answer to all options\n"); printf(" --yes2modconfig Change answers from yes to mod if possible\n"); printf(" --mod2yesconfig Change answers from mod to yes if possible\n"); + printf(" --mod2noconfig Change answers from mod to no if possible\n"); printf(" (If none of the above is given, --oldaskconfig is the default)\n"); } @@ -799,6 +787,7 @@ int main(int ac, char **av) case olddefconfig: case yes2modconfig: case mod2yesconfig: + case mod2noconfig: conf_read(NULL); break; case allnoconfig: @@ -873,10 +862,13 @@ int main(int ac, char **av) case savedefconfig: break; case yes2modconfig: - conf_rewrite_mod_or_yes(def_y2m); + conf_rewrite_tristates(yes, mod); break; case mod2yesconfig: - conf_rewrite_mod_or_yes(def_m2y); + conf_rewrite_tristates(mod, yes); + break; + case mod2noconfig: + conf_rewrite_tristates(mod, no); break; case oldaskconfig: rootEntry = &rootmenu; diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 4a828bca07..d3c3a61308 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -129,41 +130,22 @@ static size_t depfile_prefix_len; /* touch depfile for symbol 'name' */ static int conf_touch_dep(const char *name) { - int fd, ret; - char *d; + int fd; /* check overflow: prefix + name + '\0' must fit in buffer. */ if (depfile_prefix_len + strlen(name) + 1 > sizeof(depfile_path)) return -1; - d = depfile_path + depfile_prefix_len; - strcpy(d, name); + strcpy(depfile_path + depfile_prefix_len, name); - /* Assume directory path already exists. */ fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd == -1) { - if (errno != ENOENT) - return -1; - - ret = make_parent_dir(depfile_path); - if (ret) - return ret; - - /* Try it again. */ - fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd == -1) - return -1; - } + if (fd == -1) + return -1; close(fd); return 0; } -struct conf_printer { - void (*print_symbol)(FILE *, struct symbol *, const char *, void *); - void (*print_comment)(FILE *, const char *, void *); -}; - static void conf_warning(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); @@ -227,6 +209,13 @@ static const char *conf_get_autoconfig_name(void) return name ? name : "include/config/auto.conf"; } +static const char *conf_get_autoheader_name(void) +{ + char *name = getenv("KCONFIG_AUTOHEADER"); + + return name ? name : "include/generated/autoconf.h"; +} + static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) { char *p2; @@ -255,19 +244,21 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) p, sym->name); return 1; case S_STRING: - if (*p++ != '"') - break; - for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { - if (*p2 == '"') { - *p2 = 0; + /* No escaping for S_DEF_AUTO (include/config/auto.conf) */ + if (def != S_DEF_AUTO) { + if (*p++ != '"') break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); } - memmove(p2, p2 + 1, strlen(p2)); - } - if (!p2) { - if (def != S_DEF_AUTO) + if (!p2) { conf_warning("invalid string found"); - return 1; + return 1; + } } /* fall through */ case S_INT: @@ -594,169 +585,171 @@ int conf_read(const char *name) return 0; } +struct comment_style { + const char *decoration; + const char *prefix; + const char *postfix; +}; + +static const struct comment_style comment_style_pound = { + .decoration = "#", + .prefix = "#", + .postfix = "#", +}; + +static const struct comment_style comment_style_c = { + .decoration = " *", + .prefix = "/*", + .postfix = " */", +}; + +static void conf_write_heading(FILE *fp, const struct comment_style *cs) +{ + fprintf(fp, "%s\n", cs->prefix); + + fprintf(fp, "%s Automatically generated file; DO NOT EDIT.\n", + cs->decoration); + + fprintf(fp, "%s %s\n", cs->decoration, rootmenu.prompt->text); + + fprintf(fp, "%s\n", cs->postfix); +} + +/* The returned pointer must be freed on the caller side */ +static char *escape_string_value(const char *in) +{ + const char *p; + char *out; + size_t len; + + len = strlen(in) + strlen("\"\"") + 1; + + p = in; + while (1) { + p += strcspn(p, "\"\\"); + + if (p[0] == '\0') + break; + + len++; + p++; + } + + out = xmalloc(len); + out[0] = '\0'; + + strcat(out, "\""); + + p = in; + while (1) { + len = strcspn(p, "\"\\"); + strncat(out, p, len); + p += len; + + if (p[0] == '\0') + break; + + strcat(out, "\\"); + strncat(out, p++, 1); + } + + strcat(out, "\""); + + return out; +} + /* * Kconfig configuration printer * * This printer is used when generating the resulting configuration after * kconfig invocation and `defconfig' files. Unset symbol might be omitted by * passing a non-NULL argument to the printer. - * */ -static void -kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE }; + +static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n, + bool escape_string) { + const char *val; + char *escaped = NULL; + + if (sym->type == S_UNKNOWN) + return; + + val = sym_get_string_value(sym); + + if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) && + output_n != OUTPUT_N && *val == 'n') { + if (output_n == OUTPUT_N_AS_UNSET) + fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name); + return; + } + + if (sym->type == S_STRING && escape_string) { + escaped = escape_string_value(val); + val = escaped; + } + + fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, val); + + free(escaped); +} + +static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym) +{ + __print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true); +} + +static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym) +{ + __print_symbol(fp, sym, OUTPUT_N_NONE, false); +} + +void print_symbol_for_listconfig(struct symbol *sym) +{ + __print_symbol(stdout, sym, OUTPUT_N, true); +} + +static void print_symbol_for_c(FILE *fp, struct symbol *sym) +{ + const char *val; + const char *sym_suffix = ""; + const char *val_prefix = ""; + char *escaped = NULL; + + if (sym->type == S_UNKNOWN) + return; + + val = sym_get_string_value(sym); switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: - if (*value == 'n') { - bool skip_unset = (arg != NULL); - - if (!skip_unset) - fprintf(fp, "# %s%s is not set\n", - CONFIG_, sym->name); - return; - } - break; - default: - break; - } - - fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value); -} - -static void -kconfig_print_comment(FILE *fp, const char *value, void *arg) -{ - const char *p = value; - size_t l; - - for (;;) { - l = strcspn(p, "\n"); - fprintf(fp, "#"); - if (l) { - fprintf(fp, " "); - xfwrite(p, l, 1, fp); - p += l; - } - fprintf(fp, "\n"); - if (*p++ == '\0') - break; - } -} - -static struct conf_printer kconfig_printer_cb = -{ - .print_symbol = kconfig_print_symbol, - .print_comment = kconfig_print_comment, -}; - -/* - * Header printer - * - * This printer is used when generating the `include/generated/autoconf.h' file. - */ -static void -header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) -{ - - switch (sym->type) { - case S_BOOLEAN: - case S_TRISTATE: { - const char *suffix = ""; - - switch (*value) { + switch (*val) { case 'n': - break; + return; case 'm': - suffix = "_MODULE"; + sym_suffix = "_MODULE"; /* fall through */ default: - fprintf(fp, "#define %s%s%s 1\n", - CONFIG_, sym->name, suffix); + val = "1"; } break; - } - case S_HEX: { - const char *prefix = ""; - - if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) - prefix = "0x"; - fprintf(fp, "#define %s%s %s%s\n", - CONFIG_, sym->name, prefix, value); + case S_HEX: + if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X')) + val_prefix = "0x"; break; - } case S_STRING: - case S_INT: - fprintf(fp, "#define %s%s %s\n", - CONFIG_, sym->name, value); - break; + escaped = escape_string_value(val); + val = escaped; default: break; } -} + fprintf(fp, "#define %s%s%s %s%s\n", CONFIG_, sym->name, sym_suffix, + val_prefix, val); -static void -header_print_comment(FILE *fp, const char *value, void *arg) -{ - const char *p = value; - size_t l; - - fprintf(fp, "/*\n"); - for (;;) { - l = strcspn(p, "\n"); - fprintf(fp, " *"); - if (l) { - fprintf(fp, " "); - xfwrite(p, l, 1, fp); - p += l; - } - fprintf(fp, "\n"); - if (*p++ == '\0') - break; - } - fprintf(fp, " */\n"); -} - -static struct conf_printer header_printer_cb = -{ - .print_symbol = header_print_symbol, - .print_comment = header_print_comment, -}; - -static void conf_write_symbol(FILE *fp, struct symbol *sym, - struct conf_printer *printer, void *printer_arg) -{ - const char *str; - - switch (sym->type) { - case S_UNKNOWN: - break; - case S_STRING: - str = sym_get_string_value(sym); - str = sym_escape_string_value(str); - printer->print_symbol(fp, sym, str, printer_arg); - free((void *)str); - break; - default: - str = sym_get_string_value(sym); - printer->print_symbol(fp, sym, str, printer_arg); - } -} - -static void -conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), - "\n" - "Automatically generated file; DO NOT EDIT.\n" - "%s\n", - rootmenu.prompt->text); - - printer->print_comment(fp, buf, printer_arg); + free(escaped); } /* @@ -815,7 +808,7 @@ int conf_write_defconfig(const char *filename) goto next_menu; } } - conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + print_symbol_for_dotconfig(out, sym); } next_menu: if (menu->list != NULL) { @@ -875,7 +868,7 @@ int conf_write(const char *name) if (!out) return 1; - conf_write_heading(out, &kconfig_printer_cb, NULL); + conf_write_heading(out, &comment_style_pound); if (!conf_get_changed()) sym_clear_all_valid(); @@ -902,7 +895,7 @@ int conf_write(const char *name) need_newline = false; } sym->flags |= SYMBOL_WRITTEN; - conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + print_symbol_for_dotconfig(out, sym); } next: @@ -952,32 +945,50 @@ int conf_write(const char *name) } /* write a dependency file as used by kbuild to track dependencies */ -static int conf_write_dep(const char *name) +static int conf_write_autoconf_cmd(const char *autoconf_name) { + char name[PATH_MAX], tmp[PATH_MAX]; struct file *file; FILE *out; + int ret; - out = fopen("..config.tmp", "w"); - if (!out) - return 1; - fprintf(out, "deps_config := \\\n"); - for (file = file_list; file; file = file->next) { - if (file->next) - fprintf(out, "\t%s \\\n", file->name); - else - fprintf(out, "\t%s\n", file->name); - } - fprintf(out, "\n%s: \\\n" - "\t$(deps_config)\n\n", conf_get_autoconfig_name()); - - env_write_dep(out, conf_get_autoconfig_name()); - - fprintf(out, "\n$(deps_config): ;\n"); - fclose(out); + ret = snprintf(name, sizeof(name), "%s.cmd", autoconf_name); + if (ret >= sizeof(name)) /* check truncation */ + return -1; if (make_parent_dir(name)) - return 1; - rename("..config.tmp", name); + return -1; + + ret = snprintf(tmp, sizeof(tmp), "%s.cmd.tmp", autoconf_name); + if (ret >= sizeof(tmp)) /* check truncation */ + return -1; + + out = fopen(tmp, "w"); + if (!out) { + perror("fopen"); + return -1; + } + + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) + fprintf(out, "\t%s \\\n", file->name); + + fprintf(out, "\n%s: $(deps_config)\n\n", autoconf_name); + + env_write_dep(out, autoconf_name); + + fprintf(out, "\n$(deps_config): ;\n"); + + ret = ferror(out); /* error check for all fprintf() calls */ + fclose(out); + if (ret) + return -1; + + if (rename(tmp, name)) { + perror("rename"); + return -1; + } + return 0; } @@ -1058,63 +1069,83 @@ static int conf_touch_deps(void) return 0; } +static int __conf_write_autoconf(const char *filename, + void (*print_symbol)(FILE *, struct symbol *), + const struct comment_style *comment_style) +{ + char tmp[PATH_MAX]; + FILE *file; + struct symbol *sym; + int ret, i; + + if (make_parent_dir(filename)) + return -1; + + ret = snprintf(tmp, sizeof(tmp), "%s.tmp", filename); + if (ret >= sizeof(tmp)) /* check truncation */ + return -1; + + file = fopen(tmp, "w"); + if (!file) { + perror("fopen"); + return -1; + } + + conf_write_heading(file, comment_style); + + for_all_symbols(i, sym) + if ((sym->flags & SYMBOL_WRITE) && sym->name) + print_symbol(file, sym); + + /* check possible errors in conf_write_heading() and print_symbol() */ + ret = ferror(file); + fclose(file); + if (ret) + return -1; + + if (rename(tmp, filename)) { + perror("rename"); + return -1; + } + + return 0; +} + int conf_write_autoconf(int overwrite) { struct symbol *sym; - const char *name; const char *autoconf_name = conf_get_autoconfig_name(); - FILE *out, *out_h; - int i; + int ret, i; if (!overwrite && is_present(autoconf_name)) return 0; - conf_write_dep("include/config/auto.conf.cmd"); + ret = conf_write_autoconf_cmd(autoconf_name); + if (ret) + return -1; if (conf_touch_deps()) return 1; - out = fopen(".tmpconfig", "w"); - if (!out) - return 1; - - out_h = fopen(".tmpconfig.h", "w"); - if (!out_h) { - fclose(out); - return 1; - } - - conf_write_heading(out, &kconfig_printer_cb, NULL); - conf_write_heading(out_h, &header_printer_cb, NULL); - - for_all_symbols(i, sym) { + for_all_symbols(i, sym) sym_calc_value(sym); - if (!(sym->flags & SYMBOL_WRITE) || !sym->name) - continue; - /* write symbols to auto.conf and autoconf.h */ - conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); - conf_write_symbol(out_h, sym, &header_printer_cb, NULL); - } - fclose(out); - fclose(out_h); + ret = __conf_write_autoconf(conf_get_autoheader_name(), + print_symbol_for_c, + &comment_style_c); + if (ret) + return ret; - name = getenv("KCONFIG_AUTOHEADER"); - if (!name) - name = "include/generated/autoconf.h"; - if (make_parent_dir(name)) - return 1; - if (rename(".tmpconfig.h", name)) - return 1; - - if (make_parent_dir(autoconf_name)) - return 1; /* - * This must be the last step, kbuild has a dependency on auto.conf - * and this marks the successful completion of the previous steps. + * Create include/config/auto.conf. This must be the last step because + * Kbuild has a dependency on auto.conf and this marks the successful + * completion of the previous steps. */ - if (rename(".tmpconfig", autoconf_name)) - return 1; + ret = __conf_write_autoconf(conf_get_autoconfig_name(), + print_symbol_for_autoconf, + &comment_style_pound); + if (ret) + return ret; return 0; } diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l index 312cbad2d3..cc386e4436 100644 --- a/scripts/kconfig/lexer.l +++ b/scripts/kconfig/lexer.l @@ -84,8 +84,7 @@ static void warn_ignored_character(char chr) n [A-Za-z0-9_-] %% - int str = 0; - int ts, i; + char open_quote = 0; #.* /* ignore comment */ [ \t]* /* whitespaces */ @@ -134,7 +133,7 @@ n [A-Za-z0-9_-] ":=" return T_COLON_EQUAL; "+=" return T_PLUS_EQUAL; \"|\' { - str = yytext[0]; + open_quote = yytext[0]; new_string(); BEGIN(STRING); } @@ -171,7 +170,7 @@ n [A-Za-z0-9_-] append_string(yytext + 1, yyleng - 1); } \'|\" { - if (str == yytext[0]) { + if (open_quote == yytext[0]) { BEGIN(INITIAL); yylval.string = text; return T_WORD_QUOTE; @@ -196,6 +195,8 @@ n [A-Za-z0-9_-] { [ \t]+ { + int ts, i; + ts = 0; for (i = 0; i < yyleng; i++) { if (yytext[i] == '\t') diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 606ba8a63c..3d6f7cba88 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -728,7 +728,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop, get_dep_str(r, prop->visible.expr, " Visible if: "); menu = prop->menu->parent; - for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { + for (i = 0; menu && i < 8; menu = menu->parent) { bool accessible = menu_is_visible(menu); submenu[i++] = menu; @@ -758,21 +758,24 @@ static void get_prompt_str(struct gstr *r, struct property *prop, list_add_tail(&jump->entries, head); } - if (i > 0) { - str_printf(r, " Location:\n"); - for (j = 4; --i >= 0; j += 2) { - menu = submenu[i]; - if (jump && menu == location) - jump->offset = strlen(r->s); - str_printf(r, "%*c-> %s", j, ' ', - menu_get_prompt(menu)); - if (menu->sym) { - str_printf(r, " (%s [=%s])", menu->sym->name ? - menu->sym->name : "", - sym_get_string_value(menu->sym)); - } - str_append(r, "\n"); + str_printf(r, " Location:\n"); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + if (jump && menu == location) + jump->offset = strlen(r->s); + + if (menu == &rootmenu) + /* The real rootmenu prompt is ugly */ + str_printf(r, "%*cMain menu", j, ' '); + else + str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu)); + + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : "", + sym_get_string_value(menu->sym)); } + str_append(r, "\n"); } } diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index 1a5fea0519..3387ad7508 100644 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -170,7 +170,7 @@ sub read_kconfig { $source =~ s/\$\($env\)/$ENV{$env}/; } - open(my $kinfile, '<', $source) || die "Can't open $kconfig"; + open(my $kinfile, '<', $source) || die "Can't open $source"; while (<$kinfile>) { chomp; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 5844d636d3..0572330bf8 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -871,49 +871,6 @@ struct symbol *sym_find(const char *name) return symbol; } -const char *sym_escape_string_value(const char *in) -{ - const char *p; - size_t reslen; - char *res; - size_t l; - - reslen = strlen(in) + strlen("\"\"") + 1; - - p = in; - for (;;) { - l = strcspn(p, "\"\\"); - p += l; - - if (p[0] == '\0') - break; - - reslen++; - p++; - } - - res = xmalloc(reslen); - res[0] = '\0'; - - strcat(res, "\""); - - p = in; - for (;;) { - l = strcspn(p, "\"\\"); - strncat(res, p, l); - p += l; - - if (p[0] == '\0') - break; - - strcat(res, "\\"); - strncat(res, p++, 1); - } - - strcat(res, "\""); - return res; -} - struct sym_match { struct symbol *sym; off_t so, eo; diff --git a/scripts/kernel-doc b/scripts/kernel-doc index cfcb607379..3106b7536b 100644 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1245,10 +1245,18 @@ sub dump_struct($$) { $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos; $members =~ s/\s*____cacheline_aligned_in_smp/ /gos; $members =~ s/\s*____cacheline_aligned/ /gos; + # unwrap struct_group(): + # - first eat non-declaration parameters and rewrite for final match + # - then remove macro, outer parens, and trailing semicolon + $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos; + $members =~ s/\bstruct_group_(attr|tagged)\s*\(([^,]*,){2}/STRUCT_GROUP(/gos; + $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos; + $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos; my $args = qr{([^,)]+)}; # replace DECLARE_BITMAP $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos; + $members =~ s/DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, PHY_INTERFACE_MODE_MAX)/gos; $members =~ s/DECLARE_BITMAP\s*\($args,\s*$args\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; # replace DECLARE_HASHTABLE $members =~ s/DECLARE_HASHTABLE\s*\($args,\s*$args\)/unsigned long $1\[1 << (($2) - 1)\]/gos; @@ -1256,6 +1264,8 @@ sub dump_struct($$) { $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos; # replace DECLARE_KFIFO_PTR $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos; + # replace DECLARE_FLEX_ARRAY + $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos; my $declaration = $members; # Split nested struct/union elements as newer ones @@ -1789,6 +1799,7 @@ sub dump_function($$) { $prototype =~ s/__weak +//; $prototype =~ s/__sched +//; $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; + $prototype =~ s/__alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +//; my $define = $prototype =~ s/^#\s*define\s+//; #ak added $prototype =~ s/__attribute_const__ +//; $prototype =~ s/__attribute__\s*\(\( diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index d74cee5c43..666f7bbc13 100644 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -34,6 +34,10 @@ LD="$1" KBUILD_LDFLAGS="$2" LDFLAGS_vmlinux="$3" +is_enabled() { + grep -q "^$1=y" include/config/auto.conf +} + # Nice output in kbuild format # Will be supressed by "make -s" info() @@ -80,11 +84,11 @@ modpost_link() ${KBUILD_VMLINUX_LIBS} \ --end-group" - if [ -n "${CONFIG_LTO_CLANG}" ]; then + if is_enabled CONFIG_LTO_CLANG; then gen_initcalls lds="-T .tmp_initcalls.lds" - if [ -n "${CONFIG_MODVERSIONS}" ]; then + if is_enabled CONFIG_MODVERSIONS; then gen_symversions lds="${lds} -T .tmp_symversions.lds" fi @@ -104,21 +108,21 @@ objtool_link() local objtoolcmd; local objtoolopt; - if [ "${CONFIG_LTO_CLANG} ${CONFIG_STACK_VALIDATION}" = "y y" ]; then + if is_enabled CONFIG_LTO_CLANG && is_enabled CONFIG_STACK_VALIDATION; then # Don't perform vmlinux validation unless explicitly requested, # but run objtool on vmlinux.o now that we have an object file. - if [ -n "${CONFIG_UNWINDER_ORC}" ]; then + if is_enabled CONFIG_UNWINDER_ORC; then objtoolcmd="orc generate" fi objtoolopt="${objtoolopt} --duplicate" - if [ -n "${CONFIG_FTRACE_MCOUNT_USE_OBJTOOL}" ]; then + if is_enabled CONFIG_FTRACE_MCOUNT_USE_OBJTOOL; then objtoolopt="${objtoolopt} --mcount" fi fi - if [ -n "${CONFIG_VMLINUX_VALIDATION}" ]; then + if is_enabled CONFIG_VMLINUX_VALIDATION; then objtoolopt="${objtoolopt} --noinstr" fi @@ -127,18 +131,21 @@ objtool_link() objtoolcmd="check" fi objtoolopt="${objtoolopt} --vmlinux" - if [ -z "${CONFIG_FRAME_POINTER}" ]; then + if ! is_enabled CONFIG_FRAME_POINTER; then objtoolopt="${objtoolopt} --no-fp" fi - if [ -n "${CONFIG_GCOV_KERNEL}" ] || [ -n "${CONFIG_LTO_CLANG}" ]; then + if is_enabled CONFIG_GCOV_KERNEL || is_enabled CONFIG_LTO_CLANG; then objtoolopt="${objtoolopt} --no-unreachable" fi - if [ -n "${CONFIG_RETPOLINE}" ]; then + if is_enabled CONFIG_RETPOLINE; then objtoolopt="${objtoolopt} --retpoline" fi - if [ -n "${CONFIG_X86_SMAP}" ]; then + if is_enabled CONFIG_X86_SMAP; then objtoolopt="${objtoolopt} --uaccess" fi + if is_enabled CONFIG_SLS; then + objtoolopt="${objtoolopt} --sls" + fi info OBJTOOL ${1} tools/objtool/objtool ${objtoolcmd} ${objtoolopt} ${1} fi @@ -161,7 +168,7 @@ vmlinux_link() # skip output file argument shift - if [ -n "${CONFIG_LTO_CLANG}" ]; then + if is_enabled CONFIG_LTO_CLANG; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs= @@ -189,7 +196,7 @@ vmlinux_link() ldflags="${ldflags} ${wl}--strip-debug" fi - if [ -n "${CONFIG_VMLINUX_MAP}" ]; then + if is_enabled CONFIG_VMLINUX_MAP; then ldflags="${ldflags} ${wl}-Map=${output}.map" fi @@ -205,7 +212,6 @@ vmlinux_link() gen_btf() { local pahole_ver - local extra_paholeopt= if ! [ -x "$(command -v ${PAHOLE})" ]; then echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" @@ -220,16 +226,8 @@ gen_btf() vmlinux_link ${1} - if [ "${pahole_ver}" -ge "118" ] && [ "${pahole_ver}" -le "121" ]; then - # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars - extra_paholeopt="${extra_paholeopt} --skip_encoding_btf_vars" - fi - if [ "${pahole_ver}" -ge "121" ]; then - extra_paholeopt="${extra_paholeopt} --btf_gen_floats" - fi - info "BTF" ${2} - LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${extra_paholeopt} ${1} + LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} # Create ${2} which contains just .BTF section but no symbols. Add # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all @@ -248,15 +246,15 @@ kallsyms() { local kallsymopt; - if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then + if is_enabled CONFIG_KALLSYMS_ALL; then kallsymopt="${kallsymopt} --all-symbols" fi - if [ -n "${CONFIG_KALLSYMS_ABSOLUTE_PERCPU}" ]; then + if is_enabled CONFIG_KALLSYMS_ABSOLUTE_PERCPU; then kallsymopt="${kallsymopt} --absolute-percpu" fi - if [ -n "${CONFIG_KALLSYMS_BASE_RELATIVE}" ]; then + if is_enabled CONFIG_KALLSYMS_BASE_RELATIVE; then kallsymopt="${kallsymopt} --base-relative" fi @@ -321,9 +319,6 @@ if [ "$1" = "clean" ]; then exit 0 fi -# We need access to CONFIG_ symbols -. include/config/auto.conf - # Update version info GEN .version if [ -r .version ]; then @@ -352,7 +347,7 @@ tr '\0' '\n' < modules.builtin.modinfo | sed -n 's/^[[:alnum:]:_]*\.file=//p' | tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$/.ko/' > modules.builtin btf_vmlinux_bin_o="" -if [ -n "${CONFIG_DEBUG_INFO_BTF}" ]; then +if is_enabled CONFIG_DEBUG_INFO_BTF; then btf_vmlinux_bin_o=.btf.vmlinux.bin.o if ! gen_btf .tmp_vmlinux.btf $btf_vmlinux_bin_o ; then echo >&2 "Failed to generate BTF for vmlinux" @@ -364,19 +359,19 @@ fi kallsymso="" kallsymso_prev="" kallsyms_vmlinux="" -if [ -n "${CONFIG_KALLSYMS}" ]; then +if is_enabled CONFIG_KALLSYMS; then # kallsyms support # Generate section listing all symbols and add it into vmlinux # It's a three step process: - # 1) Link .tmp_vmlinux1 so it has all symbols and sections, + # 1) Link .tmp_vmlinux.kallsyms1 so it has all symbols and sections, # but __kallsyms is empty. # Running kallsyms on that gives us .tmp_kallsyms1.o with # the right size - # 2) Link .tmp_vmlinux2 so it now has a __kallsyms section of + # 2) Link .tmp_vmlinux.kallsyms2 so it now has a __kallsyms section of # the right size, but due to the added section, some # addresses have shifted. - # From here, we generate a correct .tmp_kallsyms2.o + # From here, we generate a correct .tmp_vmlinux.kallsyms2.o # 3) That link may have expanded the kernel image enough that # more linker branch stubs / trampolines had to be added, which # introduces new names, which further expands kallsyms. Do another @@ -404,12 +399,15 @@ fi vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} # fill in BTF IDs -if [ -n "${CONFIG_DEBUG_INFO_BTF}" -a -n "${CONFIG_BPF}" ]; then +if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then info BTFIDS vmlinux ${RESOLVE_BTFIDS} vmlinux fi -if [ -n "${CONFIG_BUILDTIME_TABLE_SORT}" ]; then +info SYSMAP System.map +mksysmap vmlinux System.map + +if is_enabled CONFIG_BUILDTIME_TABLE_SORT; then info SORTTAB vmlinux if ! sorttable vmlinux; then echo >&2 Failed to sort kernel tables @@ -417,11 +415,8 @@ if [ -n "${CONFIG_BUILDTIME_TABLE_SORT}" ]; then fi fi -info SYSMAP System.map -mksysmap vmlinux System.map - # step a (see comment above) -if [ -n "${CONFIG_KALLSYMS}" ]; then +if is_enabled CONFIG_KALLSYMS; then mksysmap ${kallsyms_vmlinux} .tmp_System.map if ! cmp -s System.map .tmp_System.map; then diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index cc3625617a..c0d3bcb991 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -259,5 +259,8 @@ int main(void) DEVID_FIELD(dfl_device_id, type); DEVID_FIELD(dfl_device_id, feature_id); + DEVID(ishtp_device_id); + DEVID_FIELD(ishtp_device_id, guid); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 49aba86207..5258247d78 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -115,6 +115,17 @@ static inline void add_uuid(char *str, uuid_le uuid) uuid.b[12], uuid.b[13], uuid.b[14], uuid.b[15]); } +static inline void add_guid(char *str, guid_t guid) +{ + int len = strlen(str); + + sprintf(str + len, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + guid.b[3], guid.b[2], guid.b[1], guid.b[0], + guid.b[5], guid.b[4], guid.b[7], guid.b[6], + guid.b[8], guid.b[9], guid.b[10], guid.b[11], + guid.b[12], guid.b[13], guid.b[14], guid.b[15]); +} + /** * Check that sizeof(device_id type) are consistent with size of section * in .o file. If in-consistent then userspace and kernel does not agree @@ -1380,6 +1391,18 @@ static int do_mhi_entry(const char *filename, void *symval, char *alias) return 1; } +/* Looks like: ishtp:{guid} */ +static int do_ishtp_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, ishtp_device_id, guid); + + strcpy(alias, ISHTP_MODULE_PREFIX "{"); + add_guid(alias, guid); + strcat(alias, "}"); + + return 1; +} + static int do_auxiliary_entry(const char *filename, void *symval, char *alias) { DEF_FIELD_ADDR(symval, auxiliary_device_id, name); @@ -1499,6 +1522,7 @@ static const struct devtable devtable[] = { {"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry}, {"ssam", SIZE_ssam_device_id, do_ssam_entry}, {"dfl", SIZE_dfl_device_id, do_dfl_entry}, + {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry}, }; /* Create MODULE_ALIAS() statements. diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index cb8ab7d91d..6bfa332179 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1830,6 +1830,14 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) return 0; } +#ifndef EM_RISCV +#define EM_RISCV 243 +#endif + +#ifndef R_RISCV_SUB32 +#define R_RISCV_SUB32 39 +#endif + static void section_rela(const char *modname, struct elf_info *elf, Elf_Shdr *sechdr) { @@ -1866,6 +1874,13 @@ static void section_rela(const char *modname, struct elf_info *elf, r_sym = ELF_R_SYM(r.r_info); #endif r.r_addend = TO_NATIVE(rela->r_addend); + switch (elf->hdr->e_machine) { + case EM_RISCV: + if (!strcmp("__ex_table", fromsec) && + ELF_R_TYPE(r.r_info) == R_RISCV_SUB32) + continue; + break; + } sym = elf->symtab_start + r_sym; /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) diff --git a/scripts/package/buildtar b/scripts/package/buildtar index 221aa7df00..cb54c7f1aa 100644 --- a/scripts/package/buildtar +++ b/scripts/package/buildtar @@ -39,6 +39,10 @@ case "${1}" in opts="-I ${XZ}" tarball=${tarball}.xz ;; + tarzst-pkg) + opts="-I ${ZSTD}" + tarball=${tarball}.zst + ;; *) echo "Unknown tarball target \"${1}\" requested, please add it to ${0}." >&2 exit 1 diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files index c3eb81c3f7..7adab46180 100644 --- a/scripts/remove-stale-files +++ b/scripts/remove-stale-files @@ -24,8 +24,20 @@ set -e # with O=, make sure to remove the stale files in the output tree. Otherwise, # the build system wrongly compiles the stale ones. if [ -n "${building_out_of_srctree}" ]; then - for f in fdt_rw.c fdt_ro.c fdt_wip.c fdt.c + for f in fdt_rw.c fdt_ro.c fdt_wip.c fdt.c ashldi3.S bswapsdi2.S font.c lib1funcs.S hyp-stub.S do rm -f arch/arm/boot/compressed/${f} done + + for f in uart-ath79.c ashldi3.c bswapdi.c bswapsi.c + do + rm -f arch/mips/boot/compressed/${f} + done + + for f in firmware.c real2.S + do + rm -f arch/parisc/boot/compressed/${f} + done fi + +rm -f scripts/extract-cert diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 6b54e46a0f..af4754a35e 100644 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -111,9 +111,7 @@ if $scm_only; then exit fi -if test -e include/config/auto.conf; then - . include/config/auto.conf -else +if ! test -e include/config/auto.conf; then echo "Error: kernelrelease not valid - run 'make prepare' to update it" >&2 exit 1 fi @@ -125,10 +123,11 @@ if test ! "$srctree" -ef .; then fi # CONFIG_LOCALVERSION and LOCALVERSION (if set) -res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}" +config_localversion=$(sed -n 's/^CONFIG_LOCALVERSION=\(.*\)$/\1/p' include/config/auto.conf) +res="${res}${config_localversion}${LOCALVERSION}" # scm version string if not at a tagged commit -if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then +if grep -q "^CONFIG_LOCALVERSION_AUTO=y$" include/config/auto.conf; then # full scm version string res="$res$(scm_version)" elif [ "${LOCALVERSION+set}" != "set" ]; then diff --git a/scripts/sorttable.c b/scripts/sorttable.c index 6ee4fa8829..3a8ea5ed55 100644 --- a/scripts/sorttable.c +++ b/scripts/sorttable.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -231,7 +233,7 @@ static void sort_relative_table(char *extab_image, int image_size) } } -static void x86_sort_relative_table(char *extab_image, int image_size) +static void sort_relative_table_with_data(char *extab_image, int image_size) { int i = 0; @@ -240,7 +242,7 @@ static void x86_sort_relative_table(char *extab_image, int image_size) w(r(loc) + i, loc); w(r(loc + 1) + i + 4, loc + 1); - w(r(loc + 2) + i + 8, loc + 2); + /* Don't touch the fixup type or data */ i += sizeof(uint32_t) * 3; } @@ -253,7 +255,7 @@ static void x86_sort_relative_table(char *extab_image, int image_size) w(r(loc) - i, loc); w(r(loc + 1) - (i + 4), loc + 1); - w(r(loc + 2) - (i + 8), loc + 2); + /* Don't touch the fixup type or data */ i += sizeof(uint32_t) * 3; } @@ -336,13 +338,14 @@ static int do_file(char const *const fname, void *addr) switch (r2(&ehdr->e_machine)) { case EM_386: + case EM_AARCH64: + case EM_RISCV: case EM_X86_64: - custom_sort = x86_sort_relative_table; + custom_sort = sort_relative_table_with_data; break; case EM_S390: custom_sort = s390_sort_relative_table; break; - case EM_AARCH64: case EM_PARISC: case EM_PPC: case EM_PPC64: @@ -353,7 +356,6 @@ static int do_file(char const *const fname, void *addr) case EM_ARM: case EM_MICROBLAZE: case EM_MIPS: - case EM_RISCV: case EM_XTENSA: break; default: diff --git a/scripts/sorttable.h b/scripts/sorttable.h index a2baa2fefb..deb7c1d3e9 100644 --- a/scripts/sorttable.h +++ b/scripts/sorttable.h @@ -19,6 +19,9 @@ #undef extable_ent_size #undef compare_extable +#undef get_mcount_loc +#undef sort_mcount_loc +#undef elf_mcount_loc #undef do_sort #undef Elf_Addr #undef Elf_Ehdr @@ -41,6 +44,9 @@ #ifdef SORTTABLE_64 # define extable_ent_size 16 # define compare_extable compare_extable_64 +# define get_mcount_loc get_mcount_loc_64 +# define sort_mcount_loc sort_mcount_loc_64 +# define elf_mcount_loc elf_mcount_loc_64 # define do_sort do_sort_64 # define Elf_Addr Elf64_Addr # define Elf_Ehdr Elf64_Ehdr @@ -62,6 +68,9 @@ #else # define extable_ent_size 8 # define compare_extable compare_extable_32 +# define get_mcount_loc get_mcount_loc_32 +# define sort_mcount_loc sort_mcount_loc_32 +# define elf_mcount_loc elf_mcount_loc_32 # define do_sort do_sort_32 # define Elf_Addr Elf32_Addr # define Elf_Ehdr Elf32_Ehdr @@ -84,8 +93,6 @@ #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) /* ORC unwinder only support X86_64 */ -#include -#include #include #define ERRSTR_MAXSZ 256 @@ -191,7 +198,66 @@ static int compare_extable(const void *a, const void *b) return 1; return 0; } +#ifdef MCOUNT_SORT_ENABLED +pthread_t mcount_sort_thread; +struct elf_mcount_loc { + Elf_Ehdr *ehdr; + Elf_Shdr *init_data_sec; + uint_t start_mcount_loc; + uint_t stop_mcount_loc; +}; + +/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */ +static void *sort_mcount_loc(void *arg) +{ + struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg; + uint_t offset = emloc->start_mcount_loc - _r(&(emloc->init_data_sec)->sh_addr) + + _r(&(emloc->init_data_sec)->sh_offset); + uint_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc; + unsigned char *start_loc = (void *)emloc->ehdr + offset; + + qsort(start_loc, count/sizeof(uint_t), sizeof(uint_t), compare_extable); + return NULL; +} + +/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */ +static void get_mcount_loc(uint_t *_start, uint_t *_stop) +{ + FILE *file_start, *file_stop; + char start_buff[20]; + char stop_buff[20]; + int len = 0; + + file_start = popen(" grep start_mcount System.map | awk '{print $1}' ", "r"); + if (!file_start) { + fprintf(stderr, "get start_mcount_loc error!"); + return; + } + + file_stop = popen(" grep stop_mcount System.map | awk '{print $1}' ", "r"); + if (!file_stop) { + fprintf(stderr, "get stop_mcount_loc error!"); + pclose(file_start); + return; + } + + while (fgets(start_buff, sizeof(start_buff), file_start) != NULL) { + len = strlen(start_buff); + start_buff[len - 1] = '\0'; + } + *_start = strtoul(start_buff, NULL, 16); + + while (fgets(stop_buff, sizeof(stop_buff), file_stop) != NULL) { + len = strlen(stop_buff); + stop_buff[len - 1] = '\0'; + } + *_stop = strtoul(stop_buff, NULL, 16); + + pclose(file_start); + pclose(file_stop); +} +#endif static int do_sort(Elf_Ehdr *ehdr, char const *const fname, table_sort_t custom_sort) @@ -217,6 +283,11 @@ static int do_sort(Elf_Ehdr *ehdr, int idx; unsigned int shnum; unsigned int shstrndx; +#ifdef MCOUNT_SORT_ENABLED + struct elf_mcount_loc mstruct = {0}; + uint_t _start_mcount_loc = 0; + uint_t _stop_mcount_loc = 0; +#endif #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) unsigned int orc_ip_size = 0; unsigned int orc_size = 0; @@ -253,6 +324,17 @@ static int do_sort(Elf_Ehdr *ehdr, symtab_shndx = (Elf32_Word *)((const char *)ehdr + _r(&s->sh_offset)); +#ifdef MCOUNT_SORT_ENABLED + /* locate the .init.data section in vmlinux */ + if (!strcmp(secstrings + idx, ".init.data")) { + get_mcount_loc(&_start_mcount_loc, &_stop_mcount_loc); + mstruct.ehdr = ehdr; + mstruct.init_data_sec = s; + mstruct.start_mcount_loc = _start_mcount_loc; + mstruct.stop_mcount_loc = _stop_mcount_loc; + } +#endif + #if defined(SORTTABLE_64) && defined(UNWINDER_ORC_ENABLED) /* locate the ORC unwind tables */ if (!strcmp(secstrings + idx, ".orc_unwind_ip")) { @@ -294,6 +376,23 @@ static int do_sort(Elf_Ehdr *ehdr, goto out; } #endif + +#ifdef MCOUNT_SORT_ENABLED + if (!mstruct.init_data_sec || !_start_mcount_loc || !_stop_mcount_loc) { + fprintf(stderr, + "incomplete mcount's sort in file: %s\n", + fname); + goto out; + } + + /* create thread to sort mcount_loc concurrently */ + if (pthread_create(&mcount_sort_thread, NULL, &sort_mcount_loc, &mstruct)) { + fprintf(stderr, + "pthread_create mcount_sort_thread failed '%s': %s\n", + strerror(errno), fname); + goto out; + } +#endif if (!extab_sec) { fprintf(stderr, "no __ex_table in file: %s\n", fname); goto out; @@ -364,11 +463,11 @@ static int do_sort(Elf_Ehdr *ehdr, void *retval = NULL; /* wait for ORC tables sort done */ rc = pthread_join(orc_sort_thread, &retval); - if (rc) + if (rc) { fprintf(stderr, "pthread_join failed '%s': %s\n", strerror(errno), fname); - else if (retval) { + } else if (retval) { rc = -1; fprintf(stderr, "failed to sort ORC tables '%s': %s\n", @@ -376,5 +475,23 @@ static int do_sort(Elf_Ehdr *ehdr, } } #endif + +#ifdef MCOUNT_SORT_ENABLED + if (mcount_sort_thread) { + void *retval = NULL; + /* wait for mcount sort done */ + rc = pthread_join(mcount_sort_thread, &retval); + if (rc) { + fprintf(stderr, + "pthread_join failed '%s': %s\n", + strerror(errno), fname); + } else if (retval) { + rc = -1; + fprintf(stderr, + "failed to sort mcount '%s': %s\n", + (char *)retval, fname); + } + } +#endif return rc; } diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 17fdc620d5..0c8b79cfb1 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -178,6 +178,7 @@ assum||assume assumtpion||assumption asuming||assuming asycronous||asynchronous +asychronous||asynchronous asynchnous||asynchronous asynchromous||asynchronous asymetric||asymmetric @@ -241,6 +242,7 @@ beter||better betweeen||between bianries||binaries bitmast||bitmask +bitwiedh||bitwidth boardcast||broadcast borad||board boundry||boundary @@ -265,7 +267,10 @@ calucate||calculate calulate||calculate cancelation||cancellation cancle||cancel +cant||can't +cant'||can't canot||cannot +cann't||can't capabilites||capabilities capabilties||capabilities capabilty||capability @@ -501,6 +506,7 @@ disble||disable disgest||digest disired||desired dispalying||displaying +dissable||disable diplay||display directon||direction direcly||directly @@ -595,6 +601,7 @@ exceded||exceeded exceds||exceeds exceeed||exceed excellant||excellent +exchnage||exchange execeeded||exceeded execeeds||exceeds exeed||exceed @@ -938,6 +945,7 @@ migrateable||migratable milliseonds||milliseconds minium||minimum minimam||minimum +minimun||minimum miniumum||minimum minumum||minimum misalinged||misaligned @@ -956,6 +964,7 @@ mmnemonic||mnemonic mnay||many modfiy||modify modifer||modifier +modul||module modulues||modules momery||memory memomry||memory @@ -1037,6 +1046,7 @@ oustanding||outstanding overaall||overall overhread||overhead overlaping||overlapping +oveflow||overflow overflw||overflow overlfow||overflow overide||override @@ -1154,6 +1164,7 @@ programable||programmable programers||programmers programm||program programms||programs +progres||progress progresss||progress prohibitted||prohibited prohibitting||prohibiting @@ -1328,6 +1339,7 @@ servive||service setts||sets settting||setting shapshot||snapshot +shoft||shift shotdown||shutdown shoud||should shouldnt||shouldn't @@ -1439,6 +1451,7 @@ syfs||sysfs symetric||symmetric synax||syntax synchonized||synchronized +sychronization||synchronization synchronuously||synchronously syncronize||synchronize syncronized||synchronized @@ -1521,6 +1534,7 @@ unexpexted||unexpected unfortunatelly||unfortunately unifiy||unify uniterrupted||uninterrupted +uninterruptable||uninterruptible unintialized||uninitialized unitialized||uninitialized unkmown||unknown @@ -1553,6 +1567,7 @@ unuseful||useless unvalid||invalid upate||update upsupported||unsupported +useable||usable usefule||useful usefull||useful usege||usage @@ -1574,6 +1589,7 @@ varient||variant vaule||value verbse||verbose veify||verify +verfication||verification veriosn||version verisons||versions verison||version @@ -1586,6 +1602,7 @@ visiters||visitors vitual||virtual vunerable||vulnerable wakeus||wakeups +was't||wasn't wathdog||watchdog wating||waiting wiat||wait diff --git a/scripts/tags.sh b/scripts/tags.sh index db8ba41186..16d475b3e2 100644 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -140,71 +140,71 @@ dogtags() # - etags regular expressions have to match at the start of a line; # a ^[^#] is prepended by setup_regex unless an anchor is already present regex_asm=( - '/^\(ENTRY\|_GLOBAL\)(\([[:alnum:]_\\]*\)).*/\2/' + '/^\(ENTRY\|_GLOBAL\)([[:space:]]*\([[:alnum:]_\\]*\)).*/\2/' ) regex_c=( - '/^SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/sys_\1/' - '/^BPF_CALL_[0-9](\([[:alnum:]_]*\).*/\1/' - '/^COMPAT_SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/compat_sys_\1/' - '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1/' - '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1_rcuidle/' - '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1/' - '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1_rcuidle/' - '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/get_\1_slot/' - '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/free_\1_slot/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/ClearPage\1/' - '/^TESTSETFLAG(\([[:alnum:]_]*\).*/TestSetPage\1/' - '/^TESTPAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' - '/^SETPAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' - '/\<__SETPAGEFLAG(\([[:alnum:]_]*\).*/__SetPage\1/' - '/\&1 | grep -iq universal; then + CTAGS_EXTRA="extras" + fi setup_regex exuberant asm c all_target_sources | xargs $1 -a \ -I __initdata,__exitdata,__initconst,__ro_after_init \ @@ -261,7 +265,7 @@ exuberant() -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL \ -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \ -I static,const \ - --extra=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \ + --$CTAGS_EXTRA=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \ "${regex[@]}" setup_regex exuberant kconfig diff --git a/security/Kconfig b/security/Kconfig index fe6c0395fa..0b847f435b 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -163,20 +163,6 @@ config HARDENED_USERCOPY or are part of the kernel text. This kills entire classes of heap overflow exploits and similar kernel memory exposures. -config HARDENED_USERCOPY_FALLBACK - bool "Allow usercopy whitelist violations to fallback to object size" - depends on HARDENED_USERCOPY - default y - help - This is a temporary option that allows missing usercopy whitelists - to be discovered via a WARN() to the kernel log, instead of - rejecting the copy, falling back to non-whitelisted hardened - usercopy that checks the slab allocation size instead of the - whitelist size. This option will be removed once it seems like - all missing usercopy whitelists have been identified and fixed. - Booting with "slab_common.usercopy_fallback=Y/N" can change - this setting. - config HARDENED_USERCOPY_PAGESPAN bool "Refuse to copy allocations that span multiple pages" depends on HARDENED_USERCOPY diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index 90cbaff86e..d051f8ceef 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -23,13 +23,16 @@ config CC_HAS_AUTO_VAR_INIT_PATTERN def_bool $(cc-option,-ftrivial-auto-var-init=pattern) config CC_HAS_AUTO_VAR_INIT_ZERO + # GCC ignores the -enable flag, so we can test for the feature with + # a single invocation using the flag, but drop it as appropriate in + # the Makefile, depending on the presence of Clang. def_bool $(cc-option,-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang) choice prompt "Initialize kernel stack variables at function entry" default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL if COMPILE_TEST && GCC_PLUGINS default INIT_STACK_ALL_PATTERN if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT_PATTERN - default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_PATTERN + default INIT_STACK_ALL_ZERO if CC_HAS_AUTO_VAR_INIT_ZERO default INIT_STACK_NONE help This option enables initialization of stack variables at @@ -53,7 +56,8 @@ choice config GCC_PLUGIN_STRUCTLEAK_USER bool "zero-init structs marked for userspace (weak)" - depends on GCC_PLUGINS + # Plugin can be removed once the kernel only supports GCC 12+ + depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO select GCC_PLUGIN_STRUCTLEAK help Zero-initialize any structures on the stack containing @@ -64,7 +68,8 @@ choice config GCC_PLUGIN_STRUCTLEAK_BYREF bool "zero-init structs passed by reference (strong)" - depends on GCC_PLUGINS + # Plugin can be removed once the kernel only supports GCC 12+ + depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO depends on !(KASAN && KASAN_STACK) select GCC_PLUGIN_STRUCTLEAK help @@ -82,7 +87,8 @@ choice config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL bool "zero-init everything passed by reference (very strong)" - depends on GCC_PLUGINS + # Plugin can be removed once the kernel only supports GCC 12+ + depends on GCC_PLUGINS && !CC_HAS_AUTO_VAR_INIT_ZERO depends on !(KASAN && KASAN_STACK) select GCC_PLUGIN_STRUCTLEAK help diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 2ee3b3d29f..0797edb2fb 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -812,8 +812,6 @@ struct multi_transaction { }; #define MULTI_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct multi_transaction)) -/* TODO: replace with per file lock */ -static DEFINE_SPINLOCK(multi_transaction_lock); static void multi_transaction_kref(struct kref *kref) { @@ -847,10 +845,10 @@ static void multi_transaction_set(struct file *file, AA_BUG(n > MULTI_TRANSACTION_LIMIT); new->size = n; - spin_lock(&multi_transaction_lock); + spin_lock(&file->f_lock); old = (struct multi_transaction *) file->private_data; file->private_data = new; - spin_unlock(&multi_transaction_lock); + spin_unlock(&file->f_lock); put_multi_transaction(old); } @@ -879,9 +877,10 @@ static ssize_t multi_transaction_read(struct file *file, char __user *buf, struct multi_transaction *t; ssize_t ret; - spin_lock(&multi_transaction_lock); + spin_lock(&file->f_lock); t = get_multi_transaction(file->private_data); - spin_unlock(&multi_transaction_lock); + spin_unlock(&file->f_lock); + if (!t) return 0; @@ -1358,7 +1357,7 @@ static int rawdata_open(struct inode *inode, struct file *file) struct aa_loaddata *loaddata; struct rawdata_f_data *private; - if (!policy_view_capable(NULL)) + if (!aa_current_policy_view_capable(NULL)) return -EACCES; loaddata = __aa_get_loaddata(inode->i_private); @@ -2114,7 +2113,7 @@ static struct aa_profile *__first_profile(struct aa_ns *root, /** * __next_profile - step to the next profile in a profile tree - * @profile: current profile in tree (NOT NULL) + * @p: current profile in tree (NOT NULL) * * Perform a depth first traversal on the profile tree in a namespace * @@ -2265,7 +2264,7 @@ static const struct seq_operations aa_sfs_profiles_op = { static int profiles_open(struct inode *inode, struct file *file) { - if (!policy_view_capable(NULL)) + if (!aa_current_policy_view_capable(NULL)) return -EACCES; return seq_open(file, &aa_sfs_profiles_op); diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index d4f8948517..7517605a18 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -167,7 +167,7 @@ int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, * @perms: permission table indexed by the matched state accept entry of @dfa * @trans: transition table for indexed by named x transitions * - * File permission are determined by matching a path against @dfa and then + * File permission are determined by matching a path against @dfa and * then using the value of the accept entry for the matching state as * an index into @perms. If a named exec transition is required it is * looked up in the transition table. diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h index 1e90384b15..9101c2c76d 100644 --- a/security/apparmor/include/label.h +++ b/security/apparmor/include/label.h @@ -77,10 +77,6 @@ struct aa_labelset { #define __labelset_for_each(LS, N) \ for ((N) = rb_first(&(LS)->root); (N); (N) = rb_next(N)) -void aa_labelset_destroy(struct aa_labelset *ls); -void aa_labelset_init(struct aa_labelset *ls); - - enum label_flags { FLAG_HAT = 1, /* profile is a hat */ FLAG_UNCONFINED = 2, /* label unconfined only if all */ @@ -148,6 +144,7 @@ do { \ #define __label_make_stale(X) ((X)->flags |= FLAG_STALE) #define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size)) #define labels_set(X) (&labels_ns(X)->labels) +#define labels_view(X) labels_ns(X) #define labels_profile(X) ((X)->vec[(X)->size - 1]) diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 7d27db740b..e2e8df0c6f 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -31,12 +31,17 @@ #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) -#define AA_BUG(X, args...) AA_BUG_FMT((X), "" args) +#define AA_BUG(X, args...) \ + do { \ + _Pragma("GCC diagnostic ignored \"-Wformat-zero-length\""); \ + AA_BUG_FMT((X), "" args); \ + _Pragma("GCC diagnostic warning \"-Wformat-zero-length\""); \ + } while (0) #ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS #define AA_BUG_FMT(X, fmt, args...) \ WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) #else -#define AA_BUG_FMT(X, fmt, args...) +#define AA_BUG_FMT(X, fmt, args...) no_printk(fmt, ##args) #endif #define AA_ERROR(fmt, args...) \ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index b5b4b8190e..cb5ef21991 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -301,9 +301,11 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } -bool policy_view_capable(struct aa_ns *ns); -bool policy_admin_capable(struct aa_ns *ns); +bool aa_policy_view_capable(struct aa_label *label, struct aa_ns *ns); +bool aa_policy_admin_capable(struct aa_label *label, struct aa_ns *ns); int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask); +bool aa_current_policy_view_capable(struct aa_ns *ns); +bool aa_current_policy_admin_capable(struct aa_ns *ns); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/label.c b/security/apparmor/label.c index 6222fdfebe..0b0265da19 100644 --- a/security/apparmor/label.c +++ b/security/apparmor/label.c @@ -425,8 +425,7 @@ struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp) AA_BUG(size < 1); /* + 1 for null terminator entry on vec */ - new = kzalloc(sizeof(*new) + sizeof(struct aa_profile *) * (size + 1), - gfp); + new = kzalloc(struct_size(new, vec, size + 1), gfp); AA_DEBUG("%s (%p)\n", __func__, new); if (!new) goto fail; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index f72406fe1b..4f0eecb67d 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -728,7 +728,14 @@ static void apparmor_bprm_committed_creds(struct linux_binprm *bprm) return; } -static void apparmor_task_getsecid(struct task_struct *p, u32 *secid) +static void apparmor_current_getsecid_subj(u32 *secid) +{ + struct aa_label *label = aa_get_current_label(); + *secid = label->secid; + aa_put_label(label); +} + +static void apparmor_task_getsecid_obj(struct task_struct *p, u32 *secid) { struct aa_label *label = aa_get_task_label(p); *secid = label->secid; @@ -1252,8 +1259,8 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_free, apparmor_task_free), LSM_HOOK_INIT(task_alloc, apparmor_task_alloc), - LSM_HOOK_INIT(task_getsecid_subj, apparmor_task_getsecid), - LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid), + LSM_HOOK_INIT(current_getsecid_subj, apparmor_current_getsecid_subj), + LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_obj), LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), LSM_HOOK_INIT(task_kill, apparmor_task_kill), @@ -1402,7 +1409,7 @@ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_admin_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -1411,7 +1418,7 @@ static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -1420,7 +1427,7 @@ static int param_set_aabool(const char *val, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_admin_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } @@ -1429,7 +1436,7 @@ static int param_get_aabool(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return param_get_bool(buffer, kp); } @@ -1455,7 +1462,7 @@ static int param_get_aauint(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return param_get_uint(buffer, kp); } @@ -1526,7 +1533,7 @@ static int param_get_aacompressionlevel(char *buffer, { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return param_get_int(buffer, kp); } @@ -1535,7 +1542,7 @@ static int param_get_audit(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); } @@ -1548,7 +1555,7 @@ static int param_set_audit(const char *val, const struct kernel_param *kp) return -EINVAL; if (!val) return -EINVAL; - if (apparmor_initialized && !policy_admin_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_admin_capable(NULL)) return -EPERM; i = match_string(audit_mode_names, AUDIT_MAX_INDEX, val); @@ -1563,7 +1570,7 @@ static int param_get_mode(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; - if (apparmor_initialized && !policy_view_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_view_capable(NULL)) return -EPERM; return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); @@ -1577,7 +1584,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp) return -EINVAL; if (!val) return -EINVAL; - if (apparmor_initialized && !policy_admin_capable(NULL)) + if (apparmor_initialized && !aa_current_policy_admin_capable(NULL)) return -EPERM; i = match_string(aa_profile_mode_names, APPARMOR_MODE_NAMES_MAX_INDEX, @@ -1713,7 +1720,7 @@ static int __init alloc_buffers(void) static int apparmor_dointvec(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { - if (!policy_admin_capable(NULL)) + if (!aa_current_policy_admin_capable(NULL)) return -EPERM; if (!apparmor_enabled) return -EINVAL; @@ -1773,32 +1780,16 @@ static unsigned int apparmor_ip_postroute(void *priv, } -static unsigned int apparmor_ipv4_postroute(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return apparmor_ip_postroute(priv, skb, state); -} - -#if IS_ENABLED(CONFIG_IPV6) -static unsigned int apparmor_ipv6_postroute(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return apparmor_ip_postroute(priv, skb, state); -} -#endif - static const struct nf_hook_ops apparmor_nf_ops[] = { { - .hook = apparmor_ipv4_postroute, + .hook = apparmor_ip_postroute, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_SELINUX_FIRST, }, #if IS_ENABLED(CONFIG_IPV6) { - .hook = apparmor_ipv6_postroute, + .hook = apparmor_ip_postroute, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_SELINUX_FIRST, diff --git a/security/apparmor/path.c b/security/apparmor/path.c index b02dfdbff7..45ec994b55 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -83,7 +83,7 @@ static int disconnect(const struct path *path, char *buf, char **name, * * Returns: %0 else error code if path lookup fails * When no error the path name is returned in @name which points to - * to a position in @buf + * a position in @buf */ static int d_namespace_path(const struct path *path, char *buf, char **name, int flags, const char *disconnected) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 4c010c9a6a..b0cbc4906c 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -260,8 +260,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, struct aa_profile *profile; /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(sizeof(*profile) + sizeof(struct aa_profile *) * 2, - gfp); + profile = kzalloc(struct_size(profile, label.vec, 2), gfp); if (!profile) return NULL; @@ -632,18 +631,35 @@ static int audit_policy(struct aa_label *label, const char *op, return error; } +/* don't call out to other LSMs in the stack for apparmor policy admin + * permissions + */ +static int policy_ns_capable(struct aa_label *label, + struct user_namespace *userns, int cap) +{ + int err; + + /* check for MAC_ADMIN cap in cred */ + err = cap_capable(current_cred(), userns, cap, CAP_OPT_NONE); + if (!err) + err = aa_capable(label, cap, CAP_OPT_NONE); + + return err; +} + /** - * policy_view_capable - check if viewing policy in at @ns is allowed - * ns: namespace being viewed by current task (may be NULL) + * aa_policy_view_capable - check if viewing policy in at @ns is allowed + * label: label that is trying to view policy in ns + * ns: namespace being viewed by @label (may be NULL if @label's ns) * Returns: true if viewing policy is allowed * * If @ns is NULL then the namespace being viewed is assumed to be the * tasks current namespace. */ -bool policy_view_capable(struct aa_ns *ns) +bool aa_policy_view_capable(struct aa_label *label, struct aa_ns *ns) { struct user_namespace *user_ns = current_user_ns(); - struct aa_ns *view_ns = aa_get_current_ns(); + struct aa_ns *view_ns = labels_view(label); bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) || in_egroup_p(make_kgid(user_ns, 0)); bool response = false; @@ -655,20 +671,44 @@ bool policy_view_capable(struct aa_ns *ns) (unprivileged_userns_apparmor_policy != 0 && user_ns->level == view_ns->level))) response = true; - aa_put_ns(view_ns); return response; } -bool policy_admin_capable(struct aa_ns *ns) +bool aa_policy_admin_capable(struct aa_label *label, struct aa_ns *ns) { struct user_namespace *user_ns = current_user_ns(); - bool capable = ns_capable(user_ns, CAP_MAC_ADMIN); + bool capable = policy_ns_capable(label, user_ns, CAP_MAC_ADMIN) == 0; AA_DEBUG("cap_mac_admin? %d\n", capable); AA_DEBUG("policy locked? %d\n", aa_g_lock_policy); - return policy_view_capable(ns) && capable && !aa_g_lock_policy; + return aa_policy_view_capable(label, ns) && capable && + !aa_g_lock_policy; +} + +bool aa_current_policy_view_capable(struct aa_ns *ns) +{ + struct aa_label *label; + bool res; + + label = __begin_current_label_crit_section(); + res = aa_policy_view_capable(label, ns); + __end_current_label_crit_section(label); + + return res; +} + +bool aa_current_policy_admin_capable(struct aa_ns *ns) +{ + struct aa_label *label; + bool res; + + label = __begin_current_label_crit_section(); + res = aa_policy_admin_capable(label, ns); + __end_current_label_crit_section(label); + + return res; } /** @@ -694,7 +734,7 @@ int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask) return audit_policy(label, op, NULL, NULL, "policy_locked", -EACCES); - if (!policy_admin_capable(ns)) + if (!aa_policy_admin_capable(label, ns)) return audit_policy(label, op, NULL, NULL, "not policy admin", -EACCES); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 4e1f96b216..0acca6f2a9 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -39,7 +39,7 @@ /* * The AppArmor interface treats data as a type byte followed by the - * actual data. The interface has the notion of a a named entry + * actual data. The interface has the notion of a named entry * which has a name (AA_NAME typecode followed by name string) followed by * the entries typecode and data. Named types allow for optional * elements and extensions to be added and tested for without breaking diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index c929bf4a3d..fde332e0ea 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -21,8 +21,6 @@ * @profile: the profile to print profile info about (NOT NULL) * @string: Returns - string containing the profile info (NOT NULL) * - * Returns: length of @string on success else error on failure - * * Requires: profile != NULL * * Creates a string containing the namespace_name://profile_name for diff --git a/security/commoncap.c b/security/commoncap.c index 3f810d37b7..5fc8986c3c 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -24,6 +24,7 @@ #include #include #include +#include /* * If a non-root user executes a setuid-root binary in @@ -418,7 +419,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, kroot = make_kuid(fs_ns, root); /* If this is an idmapped mount shift the kuid. */ - kroot = kuid_into_mnt(mnt_userns, kroot); + kroot = mapped_kuid_fs(mnt_userns, fs_ns, kroot); /* If the root kuid maps to a valid uid in current ns, then return * this as a nscap. */ @@ -488,6 +489,7 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, * @size: size of @ivalue * @task_ns: user namespace of the caller * @mnt_userns: user namespace of the mount the inode was found from + * @fs_userns: user namespace of the filesystem * * If the inode has been found through an idmapped mount the user namespace of * the vfsmount must be passed through @mnt_userns. This function will then @@ -497,7 +499,8 @@ int cap_inode_getsecurity(struct user_namespace *mnt_userns, */ static kuid_t rootid_from_xattr(const void *value, size_t size, struct user_namespace *task_ns, - struct user_namespace *mnt_userns) + struct user_namespace *mnt_userns, + struct user_namespace *fs_userns) { const struct vfs_ns_cap_data *nscap = value; kuid_t rootkid; @@ -507,7 +510,7 @@ static kuid_t rootid_from_xattr(const void *value, size_t size, rootid = le32_to_cpu(nscap->rootid); rootkid = make_kuid(task_ns, rootid); - return kuid_from_mnt(mnt_userns, rootkid); + return mapped_kuid_user(mnt_userns, fs_userns, rootkid); } static bool validheader(size_t size, const struct vfs_cap_data *cap) @@ -553,12 +556,12 @@ int cap_convert_nscap(struct user_namespace *mnt_userns, struct dentry *dentry, return -EINVAL; if (!capable_wrt_inode_uidgid(mnt_userns, inode, CAP_SETFCAP)) return -EPERM; - if (size == XATTR_CAPS_SZ_2 && (mnt_userns == &init_user_ns)) + if (size == XATTR_CAPS_SZ_2 && (mnt_userns == fs_ns)) if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP)) /* user is privileged, just write the v2 */ return size; - rootid = rootid_from_xattr(*ivalue, size, task_ns, mnt_userns); + rootid = rootid_from_xattr(*ivalue, size, task_ns, mnt_userns, fs_ns); if (!uid_valid(rootid)) return -EINVAL; @@ -699,7 +702,7 @@ int get_vfs_caps_from_disk(struct user_namespace *mnt_userns, /* Limit the caps to the mounter of the filesystem * or the more limited uid specified in the xattr. */ - rootkuid = kuid_into_mnt(mnt_userns, rootkuid); + rootkuid = mapped_kuid_fs(mnt_userns, fs_ns, rootkuid); if (!rootid_owns_currentns(rootkuid)) return -ENODATA; diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 04375df52f..842889f3dc 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -5,6 +5,7 @@ * Copyright 2007 IBM Corp */ +#include #include #include #include diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index dbba51583e..17232bbfb9 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -76,7 +76,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode, if (!ima_appraise) return 0; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); return ima_match_policy(mnt_userns, inode, current_cred(), secid, func, mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL, NULL); diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index f799cc278a..13753136f0 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -61,9 +61,9 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, } memcpy(file.buf, &khdr, sizeof(khdr)); - print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE, - 16, 1, file.buf, - file.count < 100 ? file.count : 100, true); + print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, + file.buf, file.count < 100 ? file.count : 100, + true); *buffer_size = file.count; *buffer = file.buf; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 4658654121..8c6e4514d4 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -408,7 +408,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) u32 secid; if (file && (prot & PROT_EXEC)) { - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_EXEC, MMAP_CHECK); } @@ -446,7 +446,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) return 0; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); inode = file_inode(vma->vm_file); action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode, current_cred(), secid, MAY_EXEC, MMAP_CHECK, @@ -487,7 +487,7 @@ int ima_bprm_check(struct linux_binprm *bprm) int ret; u32 secid; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0, MAY_EXEC, BPRM_CHECK); if (ret) @@ -512,7 +512,7 @@ int ima_file_check(struct file *file, int mask) { u32 secid; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); return process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); @@ -709,7 +709,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, /* Read entire file for all partial reads. */ func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); return process_measurement(file, current_cred(), secid, NULL, 0, MAY_READ, func); } @@ -752,7 +752,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, } func = read_idmap[read_id] ?: FILE_CHECK; - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); return process_measurement(file, current_cred(), secid, buf, size, MAY_READ, func); } @@ -905,7 +905,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns, * buffer measurements. */ if (func) { - security_task_getsecid_subj(current, &secid); + security_current_getsecid_subj(&secid); action = ima_get_action(mnt_userns, inode, current_cred(), secid, 0, func, &pcr, &template, func_data, NULL); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index fa5a93dbe5..2a1f6418b1 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -36,6 +36,9 @@ #define IMA_KEYRINGS 0x0400 #define IMA_LABEL 0x0800 #define IMA_VALIDATE_ALGOS 0x1000 +#define IMA_GID 0x2000 +#define IMA_EGID 0x4000 +#define IMA_FGROUP 0x8000 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -78,9 +81,13 @@ struct ima_rule_entry { unsigned long fsmagic; uuid_t fsuuid; kuid_t uid; + kgid_t gid; kuid_t fowner; - bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ - bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ + kgid_t fgroup; + bool (*uid_op)(kuid_t cred_uid, kuid_t rule_uid); /* Handlers for operators */ + bool (*gid_op)(kgid_t cred_gid, kgid_t rule_gid); + bool (*fowner_op)(kuid_t cred_uid, kuid_t rule_uid); /* uid_eq(), uid_gt(), uid_lt() */ + bool (*fgroup_op)(kgid_t cred_gid, kgid_t rule_gid); /* gid_eq(), gid_gt(), gid_lt() */ int pcr; unsigned int allowed_algos; /* bitfield of allowed hash algorithms */ struct { @@ -104,7 +111,8 @@ static_assert( /* * Without LSM specific knowledge, the default policy can only be - * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner + * written in terms of .action, .func, .mask, .fsmagic, .uid, .gid, + * .fowner, and .fgroup */ /* @@ -582,10 +590,23 @@ static bool ima_match_rules(struct ima_rule_entry *rule, } else if (!rule->uid_op(cred->euid, rule->uid)) return false; } - + if ((rule->flags & IMA_GID) && !rule->gid_op(cred->gid, rule->gid)) + return false; + if (rule->flags & IMA_EGID) { + if (has_capability_noaudit(current, CAP_SETGID)) { + if (!rule->gid_op(cred->egid, rule->gid) + && !rule->gid_op(cred->sgid, rule->gid) + && !rule->gid_op(cred->gid, rule->gid)) + return false; + } else if (!rule->gid_op(cred->egid, rule->gid)) + return false; + } if ((rule->flags & IMA_FOWNER) && !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner)) return false; + if ((rule->flags & IMA_FGROUP) && + !rule->fgroup_op(i_gid_into_mnt(mnt_userns, inode), rule->fgroup)) + return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; @@ -849,7 +870,7 @@ static int __init ima_init_arch_policy(void) char rule[255]; int result; - result = strlcpy(rule, *rules, sizeof(rule)); + result = strscpy(rule, *rules, sizeof(rule)); INIT_LIST_HEAD(&arch_policy_entry[i].list); result = ima_parse_rule(rule, &arch_policy_entry[i]); @@ -868,8 +889,7 @@ static int __init ima_init_arch_policy(void) /** * ima_init_policy - initialize the default measure rules. * - * ima_rules points to either the ima_default_rules or the - * the new ima_policy_rules. + * ima_rules points to either the ima_default_rules or the new ima_policy_rules. */ void __init ima_init_policy(void) { @@ -991,16 +1011,19 @@ void ima_update_policy(void) } /* Keep the enumeration in sync with the policy_tokens! */ -enum { +enum policy_opt { Opt_measure, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, Opt_audit, Opt_hash, Opt_dont_hash, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, - Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, - Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, - Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, - Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, + Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid, + Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq, + Opt_fowner_eq, Opt_fgroup_eq, + Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt, + Opt_fowner_gt, Opt_fgroup_gt, + Opt_uid_lt, Opt_euid_lt, Opt_gid_lt, Opt_egid_lt, + Opt_fowner_lt, Opt_fgroup_lt, Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos, Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, Opt_label, Opt_err @@ -1027,13 +1050,22 @@ static const match_table_t policy_tokens = { {Opt_fsuuid, "fsuuid=%s"}, {Opt_uid_eq, "uid=%s"}, {Opt_euid_eq, "euid=%s"}, + {Opt_gid_eq, "gid=%s"}, + {Opt_egid_eq, "egid=%s"}, {Opt_fowner_eq, "fowner=%s"}, + {Opt_fgroup_eq, "fgroup=%s"}, {Opt_uid_gt, "uid>%s"}, {Opt_euid_gt, "euid>%s"}, + {Opt_gid_gt, "gid>%s"}, + {Opt_egid_gt, "egid>%s"}, {Opt_fowner_gt, "fowner>%s"}, + {Opt_fgroup_gt, "fgroup>%s"}, {Opt_uid_lt, "uid<%s"}, {Opt_euid_lt, "euid<%s"}, + {Opt_gid_lt, "gid<%s"}, + {Opt_egid_lt, "egid<%s"}, {Opt_fowner_lt, "fowner<%s"}, + {Opt_fgroup_lt, "fgroup<%s"}, {Opt_appraise_type, "appraise_type=%s"}, {Opt_appraise_flag, "appraise_flag=%s"}, {Opt_appraise_algos, "appraise_algos=%s"}, @@ -1077,22 +1109,36 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, } static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, - bool (*rule_operator)(kuid_t, kuid_t)) + enum policy_opt rule_operator) { if (!ab) return; - if (rule_operator == &uid_gt) + switch (rule_operator) { + case Opt_uid_gt: + case Opt_euid_gt: + case Opt_gid_gt: + case Opt_egid_gt: + case Opt_fowner_gt: + case Opt_fgroup_gt: audit_log_format(ab, "%s>", key); - else if (rule_operator == &uid_lt) + break; + case Opt_uid_lt: + case Opt_euid_lt: + case Opt_gid_lt: + case Opt_egid_lt: + case Opt_fowner_lt: + case Opt_fgroup_lt: audit_log_format(ab, "%s<", key); - else + break; + default: audit_log_format(ab, "%s=", key); + } audit_log_format(ab, "%s ", value); } static void ima_log_string(struct audit_buffer *ab, char *key, char *value) { - ima_log_string_op(ab, key, value, NULL); + ima_log_string_op(ab, key, value, Opt_err); } /* @@ -1167,7 +1213,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | - IMA_FSNAME | IMA_DIGSIG_REQUIRED | + IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS)) return false; @@ -1178,7 +1225,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | - IMA_FSNAME | IMA_DIGSIG_REQUIRED | + IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS)) return false; @@ -1190,7 +1238,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_EUID | - IMA_PCR | IMA_FSNAME)) + IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID | + IMA_FGROUP)) return false; break; @@ -1198,7 +1247,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action & ~(MEASURE | DONT_MEASURE)) return false; - if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | + if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR | IMA_KEYRINGS)) return false; @@ -1210,7 +1259,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action & ~(MEASURE | DONT_MEASURE)) return false; - if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR | + if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR | IMA_LABEL)) return false; @@ -1280,7 +1329,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) struct audit_buffer *ab; char *from; char *p; - bool uid_token; + bool eid_token; /* either euid or egid */ struct ima_template_desc *template_desc; int result = 0; @@ -1288,9 +1337,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) AUDIT_INTEGRITY_POLICY_RULE); entry->uid = INVALID_UID; + entry->gid = INVALID_GID; entry->fowner = INVALID_UID; + entry->fgroup = INVALID_GID; entry->uid_op = &uid_eq; + entry->gid_op = &gid_eq; entry->fowner_op = &uid_eq; + entry->fgroup_op = &gid_eq; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -1508,12 +1561,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) fallthrough; case Opt_uid_eq: case Opt_euid_eq: - uid_token = (token == Opt_uid_eq) || - (token == Opt_uid_gt) || - (token == Opt_uid_lt); + eid_token = (token == Opt_euid_eq) || + (token == Opt_euid_gt) || + (token == Opt_euid_lt); - ima_log_string_op(ab, uid_token ? "uid" : "euid", - args[0].from, entry->uid_op); + ima_log_string_op(ab, eid_token ? "euid" : "uid", + args[0].from, token); if (uid_valid(entry->uid)) { result = -EINVAL; @@ -1528,8 +1581,43 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) (uid_t)lnum != lnum) result = -EINVAL; else - entry->flags |= uid_token - ? IMA_UID : IMA_EUID; + entry->flags |= eid_token + ? IMA_EUID : IMA_UID; + } + break; + case Opt_gid_gt: + case Opt_egid_gt: + entry->gid_op = &gid_gt; + fallthrough; + case Opt_gid_lt: + case Opt_egid_lt: + if ((token == Opt_gid_lt) || (token == Opt_egid_lt)) + entry->gid_op = &gid_lt; + fallthrough; + case Opt_gid_eq: + case Opt_egid_eq: + eid_token = (token == Opt_egid_eq) || + (token == Opt_egid_gt) || + (token == Opt_egid_lt); + + ima_log_string_op(ab, eid_token ? "egid" : "gid", + args[0].from, token); + + if (gid_valid(entry->gid)) { + result = -EINVAL; + break; + } + + result = kstrtoul(args[0].from, 10, &lnum); + if (!result) { + entry->gid = make_kgid(current_user_ns(), + (gid_t)lnum); + if (!gid_valid(entry->gid) || + (((gid_t)lnum) != lnum)) + result = -EINVAL; + else + entry->flags |= eid_token + ? IMA_EGID : IMA_GID; } break; case Opt_fowner_gt: @@ -1540,8 +1628,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->fowner_op = &uid_lt; fallthrough; case Opt_fowner_eq: - ima_log_string_op(ab, "fowner", args[0].from, - entry->fowner_op); + ima_log_string_op(ab, "fowner", args[0].from, token); if (uid_valid(entry->fowner)) { result = -EINVAL; @@ -1550,13 +1637,41 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = kstrtoul(args[0].from, 10, &lnum); if (!result) { - entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); - if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) + entry->fowner = make_kuid(current_user_ns(), + (uid_t)lnum); + if (!uid_valid(entry->fowner) || + (((uid_t)lnum) != lnum)) result = -EINVAL; else entry->flags |= IMA_FOWNER; } break; + case Opt_fgroup_gt: + entry->fgroup_op = &gid_gt; + fallthrough; + case Opt_fgroup_lt: + if (token == Opt_fgroup_lt) + entry->fgroup_op = &gid_lt; + fallthrough; + case Opt_fgroup_eq: + ima_log_string_op(ab, "fgroup", args[0].from, token); + + if (gid_valid(entry->fgroup)) { + result = -EINVAL; + break; + } + + result = kstrtoul(args[0].from, 10, &lnum); + if (!result) { + entry->fgroup = make_kgid(current_user_ns(), + (gid_t)lnum); + if (!gid_valid(entry->fgroup) || + (((gid_t)lnum) != lnum)) + result = -EINVAL; + else + entry->flags |= IMA_FGROUP; + } + break; case Opt_obj_user: ima_log_string(ab, "obj_user", args[0].from); result = ima_lsm_rule_init(entry, args, @@ -1951,6 +2066,28 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_GID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid)); + if (entry->gid_op == &gid_gt) + seq_printf(m, pt(Opt_gid_gt), tbuf); + else if (entry->gid_op == &gid_lt) + seq_printf(m, pt(Opt_gid_lt), tbuf); + else + seq_printf(m, pt(Opt_gid_eq), tbuf); + seq_puts(m, " "); + } + + if (entry->flags & IMA_EGID) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid)); + if (entry->gid_op == &gid_gt) + seq_printf(m, pt(Opt_egid_gt), tbuf); + else if (entry->gid_op == &gid_lt) + seq_printf(m, pt(Opt_egid_lt), tbuf); + else + seq_printf(m, pt(Opt_egid_eq), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_FOWNER) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); if (entry->fowner_op == &uid_gt) @@ -1962,6 +2099,17 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_FGROUP) { + snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup)); + if (entry->fgroup_op == &gid_gt) + seq_printf(m, pt(Opt_fgroup_gt), tbuf); + else if (entry->fgroup_op == &gid_lt) + seq_printf(m, pt(Opt_fgroup_lt), tbuf); + else + seq_printf(m, pt(Opt_fgroup_eq), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_VALIDATE_ALGOS) { seq_puts(m, "appraise_algos="); ima_policy_show_appraise_algos(m, entry->allowed_algos); diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c index f290f78c3f..08b6d12f99 100644 --- a/security/integrity/platform_certs/load_uefi.c +++ b/security/integrity/platform_certs/load_uefi.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include "../integrity.h" @@ -176,6 +177,10 @@ static int __init load_uefi_certs(void) kfree(dbx); } + /* the MOK/MOKx can not be trusted when secure boot is disabled */ + if (!arch_ima_get_secureboot()) + return 0; + mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status); if (!mokx) { if (status == EFI_NOT_FOUND) diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 64b81abd08..969122c7b9 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -109,7 +109,7 @@ config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" depends on KEYS select CRYPTO - select CRYPTO_HASH + select CRYPTO_KDF800108_CTR select CRYPTO_DH help This option provides support for calculating Diffie-Hellman diff --git a/security/keys/dh.c b/security/keys/dh.c index 1abfa70ed6..4573fc1561 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "internal.h" @@ -79,17 +80,9 @@ static void dh_crypto_done(struct crypto_async_request *req, int err) complete(&compl->completion); } -struct kdf_sdesc { - struct shash_desc shash; - char ctx[]; -}; - -static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) +static int kdf_alloc(struct crypto_shash **hash, char *hashname) { struct crypto_shash *tfm; - struct kdf_sdesc *sdesc; - int size; - int err; /* allocate synchronous hash */ tfm = crypto_alloc_shash(hashname, 0, 0); @@ -98,112 +91,30 @@ static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) return PTR_ERR(tfm); } - err = -EINVAL; - if (crypto_shash_digestsize(tfm) == 0) - goto out_free_tfm; - - err = -ENOMEM; - size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); - sdesc = kmalloc(size, GFP_KERNEL); - if (!sdesc) - goto out_free_tfm; - sdesc->shash.tfm = tfm; - - *sdesc_ret = sdesc; - - return 0; - -out_free_tfm: - crypto_free_shash(tfm); - return err; -} - -static void kdf_dealloc(struct kdf_sdesc *sdesc) -{ - if (!sdesc) - return; - - if (sdesc->shash.tfm) - crypto_free_shash(sdesc->shash.tfm); - - kfree_sensitive(sdesc); -} - -/* - * Implementation of the KDF in counter mode according to SP800-108 section 5.1 - * as well as SP800-56A section 5.8.1 (Single-step KDF). - * - * SP800-56A: - * The src pointer is defined as Z || other info where Z is the shared secret - * from DH and other info is an arbitrary string (see SP800-56A section - * 5.8.1.2). - * - * 'dlen' must be a multiple of the digest size. - */ -static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, - u8 *dst, unsigned int dlen, unsigned int zlen) -{ - struct shash_desc *desc = &sdesc->shash; - unsigned int h = crypto_shash_digestsize(desc->tfm); - int err = 0; - u8 *dst_orig = dst; - __be32 counter = cpu_to_be32(1); - - while (dlen) { - err = crypto_shash_init(desc); - if (err) - goto err; - - err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32)); - if (err) - goto err; - - if (zlen && h) { - u8 tmpbuffer[32]; - size_t chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); - memset(tmpbuffer, 0, chunk); - - do { - err = crypto_shash_update(desc, tmpbuffer, - chunk); - if (err) - goto err; - - zlen -= chunk; - chunk = min_t(size_t, zlen, sizeof(tmpbuffer)); - } while (zlen); - } - - if (src && slen) { - err = crypto_shash_update(desc, src, slen); - if (err) - goto err; - } - - err = crypto_shash_final(desc, dst); - if (err) - goto err; - - dlen -= h; - dst += h; - counter = cpu_to_be32(be32_to_cpu(counter) + 1); + if (crypto_shash_digestsize(tfm) == 0) { + crypto_free_shash(tfm); + return -EINVAL; } - return 0; + *hash = tfm; -err: - memzero_explicit(dst_orig, dlen); - return err; + return 0; } -static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, - char __user *buffer, size_t buflen, - uint8_t *kbuf, size_t kbuflen, size_t lzero) +static void kdf_dealloc(struct crypto_shash *hash) { + if (hash) + crypto_free_shash(hash); +} + +static int keyctl_dh_compute_kdf(struct crypto_shash *hash, + char __user *buffer, size_t buflen, + uint8_t *kbuf, size_t kbuflen) +{ + struct kvec kbuf_iov = { .iov_base = kbuf, .iov_len = kbuflen }; uint8_t *outbuf = NULL; int ret; - size_t outbuf_len = roundup(buflen, - crypto_shash_digestsize(sdesc->shash.tfm)); + size_t outbuf_len = roundup(buflen, crypto_shash_digestsize(hash)); outbuf = kmalloc(outbuf_len, GFP_KERNEL); if (!outbuf) { @@ -211,7 +122,7 @@ static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, goto err; } - ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, outbuf_len, lzero); + ret = crypto_kdf108_ctr_generate(hash, &kbuf_iov, 1, outbuf, outbuf_len); if (ret) goto err; @@ -240,7 +151,7 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user *params, struct kpp_request *req; uint8_t *secret; uint8_t *outbuf; - struct kdf_sdesc *sdesc = NULL; + struct crypto_shash *hash = NULL; if (!params || (!buffer && buflen)) { ret = -EINVAL; @@ -273,7 +184,7 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user *params, } /* allocate KDF from the kernel crypto API */ - ret = kdf_alloc(&sdesc, hashname); + ret = kdf_alloc(&hash, hashname); kfree(hashname); if (ret) goto out1; @@ -383,9 +294,8 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user *params, goto out6; } - ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, outbuf, - req->dst_len + kdfcopy->otherinfolen, - outlen - req->dst_len); + ret = keyctl_dh_compute_kdf(hash, buffer, buflen, outbuf, + req->dst_len + kdfcopy->otherinfolen); } else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) { ret = req->dst_len; } else { @@ -403,7 +313,7 @@ long __keyctl_dh_compute(struct keyctl_dh_params __user *params, out2: dh_free_data(&dh_inputs); out1: - kdf_dealloc(sdesc); + kdf_dealloc(hash); return ret; } diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 5a5016ef43..1897cbf6fc 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -224,7 +224,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, case LSM_AUDIT_DATA_NONE: return; case LSM_AUDIT_DATA_IPC: - audit_log_format(ab, " key=%d ", a->u.ipc_id); + audit_log_format(ab, " ipc_key=%d ", a->u.ipc_id); break; case LSM_AUDIT_DATA_CAP: audit_log_format(ab, " capability=%d ", a->u.cap); diff --git a/security/security.c b/security/security.c index 67264cb08f..22261d79f3 100644 --- a/security/security.c +++ b/security/security.c @@ -706,7 +706,7 @@ static int lsm_superblock_alloc(struct super_block *sb) #define LSM_RET_DEFAULT(NAME) (NAME##_default) #define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME) #define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \ - static const int LSM_RET_DEFAULT(NAME) = (DEFAULT); + static const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT); #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME) @@ -994,14 +994,6 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb, } EXPORT_SYMBOL(security_sb_clone_mnt_opts); -int security_add_mnt_opt(const char *option, const char *val, int len, - void **mnt_opts) -{ - return call_int_hook(sb_add_mnt_opt, -EINVAL, - option, val, len, mnt_opts); -} -EXPORT_SYMBOL(security_add_mnt_opt); - int security_move_mount(const struct path *from_path, const struct path *to_path) { return call_int_hook(move_mount, 0, from_path, to_path); @@ -1052,11 +1044,23 @@ void security_inode_free(struct inode *inode) } int security_dentry_init_security(struct dentry *dentry, int mode, - const struct qstr *name, void **ctx, - u32 *ctxlen) + const struct qstr *name, + const char **xattr_name, void **ctx, + u32 *ctxlen) { - return call_int_hook(dentry_init_security, -EOPNOTSUPP, dentry, mode, - name, ctx, ctxlen); + struct security_hook_list *hp; + int rc; + + /* + * Only one module will provide a security context. + */ + hlist_for_each_entry(hp, &security_hook_heads.dentry_init_security, list) { + rc = hp->hook.dentry_init_security(dentry, mode, name, + xattr_name, ctx, ctxlen); + if (rc != LSM_RET_DEFAULT(dentry_init_security)) + return rc; + } + return LSM_RET_DEFAULT(dentry_init_security); } EXPORT_SYMBOL(security_dentry_init_security); @@ -1807,12 +1811,12 @@ int security_task_getsid(struct task_struct *p) return call_int_hook(task_getsid, 0, p); } -void security_task_getsecid_subj(struct task_struct *p, u32 *secid) +void security_current_getsecid_subj(u32 *secid) { *secid = 0; - call_void_hook(task_getsecid_subj, p, secid); + call_void_hook(current_getsecid_subj, secid); } -EXPORT_SYMBOL(security_task_getsecid_subj); +EXPORT_SYMBOL(security_current_getsecid_subj); void security_task_getsecid_obj(struct task_struct *p, u32 *secid) { @@ -2366,9 +2370,9 @@ int security_tun_dev_open(void *security) } EXPORT_SYMBOL(security_tun_dev_open); -int security_sctp_assoc_request(struct sctp_endpoint *ep, struct sk_buff *skb) +int security_sctp_assoc_request(struct sctp_association *asoc, struct sk_buff *skb) { - return call_int_hook(sctp_assoc_request, 0, ep, skb); + return call_int_hook(sctp_assoc_request, 0, asoc, skb); } EXPORT_SYMBOL(security_sctp_assoc_request); @@ -2380,10 +2384,10 @@ int security_sctp_bind_connect(struct sock *sk, int optname, } EXPORT_SYMBOL(security_sctp_bind_connect); -void security_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk, +void security_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk, struct sock *newsk) { - call_void_hook(sctp_sk_clone, ep, sk, newsk); + call_void_hook(sctp_sk_clone, asoc, sk, newsk); } EXPORT_SYMBOL(security_sctp_sk_clone); @@ -2625,3 +2629,15 @@ int security_perf_event_write(struct perf_event *event) return call_int_hook(perf_event_write, 0, event); } #endif /* CONFIG_PERF_EVENTS */ + +#ifdef CONFIG_IO_URING +int security_uring_override_creds(const struct cred *new) +{ + return call_int_hook(uring_override_creds, 0, new); +} + +int security_uring_sqpoll(void) +{ + return call_int_hook(uring_sqpoll, 0); +} +#endif /* CONFIG_IO_URING */ diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 97f4c944a2..abcd9740d1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -547,6 +547,7 @@ static inline struct avc_node *avc_search_node(struct selinux_avc *avc, /** * avc_lookup - Look up an AVC entry. + * @avc: the access vector cache * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class @@ -597,6 +598,7 @@ static int avc_latest_notif_update(struct selinux_avc *avc, /** * avc_insert - Insert an AVC entry. + * @avc: the access vector cache * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class @@ -825,9 +827,14 @@ int __init avc_add_callback(int (*callback)(u32 event), u32 events) /** * avc_update_node - Update an AVC entry + * @avc: the access vector cache * @event : Updating event * @perms : Permission mask bits - * @ssid,@tsid,@tclass : identifier of an AVC entry + * @driver: xperm driver information + * @xperm: xperm permissions + * @ssid: AVC entry source sid + * @tsid: AVC entry target sid + * @tclass : AVC entry target object class * @seqno : sequence number when decision was made * @xpd: extended_perms_decision to be added to the node * @flags: the AVC_* flags, e.g. AVC_EXTENDED_PERMS, or 0. @@ -928,6 +935,7 @@ static int avc_update_node(struct selinux_avc *avc, /** * avc_flush - Flush the cache + * @avc: the access vector cache */ static void avc_flush(struct selinux_avc *avc) { @@ -956,6 +964,7 @@ static void avc_flush(struct selinux_avc *avc) /** * avc_ss_reset - Flush the cache and revalidate migrated permissions. + * @avc: the access vector cache * @seqno: policy sequence number */ int avc_ss_reset(struct selinux_avc *avc, u32 seqno) @@ -1105,6 +1114,7 @@ int avc_has_extended_perms(struct selinux_state *state, /** * avc_has_perm_noaudit - Check permissions but perform no auditing. + * @state: SELinux state * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class @@ -1156,6 +1166,7 @@ inline int avc_has_perm_noaudit(struct selinux_state *state, /** * avc_has_perm - Check permissions and perform any appropriate auditing. + * @state: SELinux state * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index baa12d1007..5b6895e4fc 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -229,19 +229,6 @@ static inline u32 cred_sid(const struct cred *cred) return tsec->sid; } -/* - * get the subjective security ID of a task - */ -static inline u32 task_sid_subj(const struct task_struct *task) -{ - u32 sid; - - rcu_read_lock(); - sid = cred_sid(rcu_dereference(task->cred)); - rcu_read_unlock(); - return sid; -} - /* * get the objective security ID of a task */ @@ -742,7 +729,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "binder") || !strcmp(sb->s_type->name, "bpf") || - !strcmp(sb->s_type->name, "pstore")) + !strcmp(sb->s_type->name, "pstore") || + !strcmp(sb->s_type->name, "securityfs")) sbsec->flags |= SE_SBGENFS; if (!strcmp(sb->s_type->name, "sysfs") || @@ -989,14 +977,14 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts) struct selinux_mnt_opts *opts = *mnt_opts; bool is_alloc_opts = false; - if (token == Opt_seclabel) /* eaten and completely ignored */ + if (token == Opt_seclabel) + /* eaten and completely ignored */ return 0; - if (!s) return -ENOMEM; if (!opts) { - opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL); + opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return -ENOMEM; *mnt_opts = opts; @@ -1006,27 +994,29 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts) switch (token) { case Opt_context: if (opts->context || opts->defcontext) - goto Einval; + goto err; opts->context = s; break; case Opt_fscontext: if (opts->fscontext) - goto Einval; + goto err; opts->fscontext = s; break; case Opt_rootcontext: if (opts->rootcontext) - goto Einval; + goto err; opts->rootcontext = s; break; case Opt_defcontext: if (opts->context || opts->defcontext) - goto Einval; + goto err; opts->defcontext = s; break; } + return 0; -Einval: + +err: if (is_alloc_opts) { kfree(opts); *mnt_opts = NULL; @@ -1035,44 +1025,6 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts) return -EINVAL; } -static int selinux_add_mnt_opt(const char *option, const char *val, int len, - void **mnt_opts) -{ - int token = Opt_error; - int rc, i; - - for (i = 0; i < ARRAY_SIZE(tokens); i++) { - if (strcmp(option, tokens[i].name) == 0) { - token = tokens[i].opt; - break; - } - } - - if (token == Opt_error) - return -EINVAL; - - if (token != Opt_seclabel) { - val = kmemdup_nul(val, len, GFP_KERNEL); - if (!val) { - rc = -ENOMEM; - goto free_opt; - } - } - rc = selinux_add_opt(token, val, mnt_opts); - if (unlikely(rc)) { - kfree(val); - goto free_opt; - } - return rc; - -free_opt: - if (*mnt_opts) { - selinux_free_mnt_opts(*mnt_opts); - *mnt_opts = NULL; - } - return rc; -} - static int show_sid(struct seq_file *m, u32 sid) { char *context = NULL; @@ -2939,7 +2891,8 @@ static void selinux_inode_free_security(struct inode *inode) } static int selinux_dentry_init_security(struct dentry *dentry, int mode, - const struct qstr *name, void **ctx, + const struct qstr *name, + const char **xattr_name, void **ctx, u32 *ctxlen) { u32 newsid; @@ -2952,6 +2905,9 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, if (rc) return rc; + if (xattr_name) + *xattr_name = XATTR_NAME_SELINUX; + return security_sid_to_context(&selinux_state, newsid, (char **)ctx, ctxlen); } @@ -4213,9 +4169,9 @@ static int selinux_task_getsid(struct task_struct *p) PROCESS__GETSESSION, NULL); } -static void selinux_task_getsecid_subj(struct task_struct *p, u32 *secid) +static void selinux_current_getsecid_subj(u32 *secid) { - *secid = task_sid_subj(p); + *secid = current_sid(); } static void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid) @@ -5347,10 +5303,10 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) * connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association * already present). */ -static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, +static int selinux_sctp_assoc_request(struct sctp_association *asoc, struct sk_buff *skb) { - struct sk_security_struct *sksec = ep->base.sk->sk_security; + struct sk_security_struct *sksec = asoc->base.sk->sk_security; struct common_audit_data ad; struct lsm_network_audit net = {0,}; u8 peerlbl_active; @@ -5367,7 +5323,7 @@ static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, /* This will return peer_sid = SECSID_NULL if there are * no peer labels, see security_net_peersid_resolve(). */ - err = selinux_skb_peerlbl_sid(skb, ep->base.sk->sk_family, + err = selinux_skb_peerlbl_sid(skb, asoc->base.sk->sk_family, &peer_sid); if (err) return err; @@ -5391,7 +5347,7 @@ static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, */ ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->sk = ep->base.sk; + ad.u.net->sk = asoc->base.sk; err = avc_has_perm(&selinux_state, sksec->peer_sid, peer_sid, sksec->sclass, SCTP_SOCKET__ASSOCIATION, &ad); @@ -5400,7 +5356,7 @@ static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, } /* Compute the MLS component for the connection and store - * the information in ep. This will be used by SCTP TCP type + * the information in asoc. This will be used by SCTP TCP type * sockets and peeled off connections as they cause a new * socket to be generated. selinux_sctp_sk_clone() will then * plug this into the new socket. @@ -5409,11 +5365,11 @@ static int selinux_sctp_assoc_request(struct sctp_endpoint *ep, if (err) return err; - ep->secid = conn_sid; - ep->peer_secid = peer_sid; + asoc->secid = conn_sid; + asoc->peer_secid = peer_sid; /* Set any NetLabel labels including CIPSO/CALIPSO options. */ - return selinux_netlbl_sctp_assoc_request(ep, skb); + return selinux_netlbl_sctp_assoc_request(asoc, skb); } /* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting @@ -5498,7 +5454,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, } /* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */ -static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk, +static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk, struct sock *newsk) { struct sk_security_struct *sksec = sk->sk_security; @@ -5510,8 +5466,8 @@ static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk, if (!selinux_policycap_extsockclass()) return selinux_sk_clone_security(sk, newsk); - newsksec->sid = ep->secid; - newsksec->peer_sid = ep->peer_secid; + newsksec->sid = asoc->secid; + newsksec->peer_sid = asoc->peer_secid; newsksec->sclass = sksec->sclass; selinux_netlbl_sctp_sk_clone(sk, newsk); } @@ -5679,40 +5635,41 @@ static int selinux_tun_dev_open(void *security) #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_forward(struct sk_buff *skb, - const struct net_device *indev, - u16 family) +static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) { - int err; + int ifindex; + u16 family; char *addrp; u32 peer_sid; struct common_audit_data ad; struct lsm_network_audit net = {0,}; - u8 secmark_active; - u8 netlbl_active; - u8 peerlbl_active; + int secmark_active, peerlbl_active; if (!selinux_policycap_netpeer()) return NF_ACCEPT; secmark_active = selinux_secmark_enabled(); - netlbl_active = netlbl_enabled(); peerlbl_active = selinux_peerlbl_enabled(); if (!secmark_active && !peerlbl_active) return NF_ACCEPT; + family = state->pf; if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) return NF_DROP; + ifindex = state->in->ifindex; ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->netif = indev->ifindex; + ad.u.net->netif = ifindex; ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; if (peerlbl_active) { - err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, + int err; + + err = selinux_inet_sys_rcv_skb(state->net, ifindex, addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, family, err, 1); @@ -5726,7 +5683,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) return NF_DROP; - if (netlbl_active) + if (netlbl_enabled()) /* we do this in the FORWARD path and not the POST_ROUTING * path because we want to make sure we apply the necessary * labeling before IPsec is applied so we can leverage AH @@ -5737,24 +5694,8 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ipv4_forward(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_forward(skb, state->in, PF_INET); -} - -#if IS_ENABLED(CONFIG_IPV6) -static unsigned int selinux_ipv6_forward(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_forward(skb, state->in, PF_INET6); -} -#endif /* IPV6 */ - -static unsigned int selinux_ip_output(struct sk_buff *skb, - u16 family) +static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) { struct sock *sk; u32 sid; @@ -5789,48 +5730,32 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, sid = sksec->sid; } else sid = SECINITSID_KERNEL; - if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0) + if (selinux_netlbl_skbuff_setsid(skb, state->pf, sid) != 0) return NF_DROP; return NF_ACCEPT; } -static unsigned int selinux_ipv4_output(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_output(skb, PF_INET); -} - -#if IS_ENABLED(CONFIG_IPV6) -static unsigned int selinux_ipv6_output(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_output(skb, PF_INET6); -} -#endif /* IPV6 */ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, - int ifindex, - u16 family) + const struct nf_hook_state *state) { - struct sock *sk = skb_to_full_sk(skb); + struct sock *sk; struct sk_security_struct *sksec; struct common_audit_data ad; struct lsm_network_audit net = {0,}; - char *addrp; u8 proto = 0; + sk = skb_to_full_sk(skb); if (sk == NULL) return NF_ACCEPT; sksec = sk->sk_security; ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->netif = ifindex; - ad.u.net->family = family; - if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) + ad.u.net->netif = state->out->ifindex; + ad.u.net->family = state->pf; + if (selinux_parse_skb(skb, &ad, NULL, 0, &proto)) return NF_DROP; if (selinux_secmark_enabled()) @@ -5845,26 +5770,26 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ip_postroute(struct sk_buff *skb, - const struct net_device *outdev, - u16 family) +static unsigned int selinux_ip_postroute(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) { + u16 family; u32 secmark_perm; u32 peer_sid; - int ifindex = outdev->ifindex; + int ifindex; struct sock *sk; struct common_audit_data ad; struct lsm_network_audit net = {0,}; char *addrp; - u8 secmark_active; - u8 peerlbl_active; + int secmark_active, peerlbl_active; /* If any sort of compatibility mode is enabled then handoff processing * to the selinux_ip_postroute_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ if (!selinux_policycap_netpeer()) - return selinux_ip_postroute_compat(skb, ifindex, family); + return selinux_ip_postroute_compat(skb, state); secmark_active = selinux_secmark_enabled(); peerlbl_active = selinux_peerlbl_enabled(); @@ -5890,6 +5815,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, return NF_ACCEPT; #endif + family = state->pf; if (sk == NULL) { /* Without an associated socket the packet is either coming * from the kernel or it is being forwarded; check the packet @@ -5950,6 +5876,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, secmark_perm = PACKET__SEND; } + ifindex = state->out->ifindex; ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -5967,7 +5894,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, u32 if_sid; u32 node_sid; - if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) + if (sel_netif_sid(state->net, ifindex, &if_sid)) return NF_DROP; if (avc_has_perm(&selinux_state, peer_sid, if_sid, @@ -5984,23 +5911,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, return NF_ACCEPT; } - -static unsigned int selinux_ipv4_postroute(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_postroute(skb, state->out, PF_INET); -} - -#if IS_ENABLED(CONFIG_IPV6) -static unsigned int selinux_ipv6_postroute(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - return selinux_ip_postroute(skb, state->out, PF_INET6); -} -#endif /* IPV6 */ - #endif /* CONFIG_NETFILTER */ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) @@ -7004,34 +6914,6 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux) } #endif -static int selinux_lockdown(enum lockdown_reason what) -{ - struct common_audit_data ad; - u32 sid = current_sid(); - int invalid_reason = (what <= LOCKDOWN_NONE) || - (what == LOCKDOWN_INTEGRITY_MAX) || - (what >= LOCKDOWN_CONFIDENTIALITY_MAX); - - if (WARN(invalid_reason, "Invalid lockdown reason")) { - audit_log(audit_context(), - GFP_ATOMIC, AUDIT_SELINUX_ERR, - "lockdown_reason=invalid"); - return -EINVAL; - } - - ad.type = LSM_AUDIT_DATA_LOCKDOWN; - ad.u.reason = what; - - if (what <= LOCKDOWN_INTEGRITY_MAX) - return avc_has_perm(&selinux_state, - sid, sid, SECCLASS_LOCKDOWN, - LOCKDOWN__INTEGRITY, &ad); - else - return avc_has_perm(&selinux_state, - sid, sid, SECCLASS_LOCKDOWN, - LOCKDOWN__CONFIDENTIALITY, &ad); -} - struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_security_struct), .lbs_file = sizeof(struct file_security_struct), @@ -7102,6 +6984,35 @@ static int selinux_perf_event_write(struct perf_event *event) } #endif +#ifdef CONFIG_IO_URING +/** + * selinux_uring_override_creds - check the requested cred override + * @new: the target creds + * + * Check to see if the current task is allowed to override it's credentials + * to service an io_uring operation. + */ +static int selinux_uring_override_creds(const struct cred *new) +{ + return avc_has_perm(&selinux_state, current_sid(), cred_sid(new), + SECCLASS_IO_URING, IO_URING__OVERRIDE_CREDS, NULL); +} + +/** + * selinux_uring_sqpoll - check if a io_uring polling thread can be created + * + * Check to see if the current task is allowed to create a new io_uring + * kernel polling thread. + */ +static int selinux_uring_sqpoll(void) +{ + int sid = current_sid(); + + return avc_has_perm(&selinux_state, sid, sid, + SECCLASS_IO_URING, IO_URING__SQPOLL, NULL); +} +#endif /* CONFIG_IO_URING */ + /* * IMPORTANT NOTE: When adding new hooks, please be careful to keep this order: * 1. any hooks that don't belong to (2.) or (3.) below, @@ -7212,7 +7123,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), - LSM_HOOK_INIT(task_getsecid_subj, selinux_task_getsecid_subj), + LSM_HOOK_INIT(current_getsecid_subj, selinux_current_getsecid_subj), LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, selinux_task_setnice), LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), @@ -7340,7 +7251,10 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(perf_event_write, selinux_perf_event_write), #endif - LSM_HOOK_INIT(locked_down, selinux_lockdown), +#ifdef CONFIG_IO_URING + LSM_HOOK_INIT(uring_override_creds, selinux_uring_override_creds), + LSM_HOOK_INIT(uring_sqpoll, selinux_uring_sqpoll), +#endif /* * PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE @@ -7348,7 +7262,6 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup), LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param), LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts), - LSM_HOOK_INIT(sb_add_mnt_opt, selinux_add_mnt_opt), #ifdef CONFIG_SECURITY_NETWORK_XFRM LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone), #endif @@ -7461,38 +7374,38 @@ DEFINE_LSM(selinux) = { static const struct nf_hook_ops selinux_nf_ops[] = { { - .hook = selinux_ipv4_postroute, + .hook = selinux_ip_postroute, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP_PRI_SELINUX_LAST, }, { - .hook = selinux_ipv4_forward, + .hook = selinux_ip_forward, .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_SELINUX_FIRST, }, { - .hook = selinux_ipv4_output, + .hook = selinux_ip_output, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, }, #if IS_ENABLED(CONFIG_IPV6) { - .hook = selinux_ipv6_postroute, + .hook = selinux_ip_postroute, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING, .priority = NF_IP6_PRI_SELINUX_LAST, }, { - .hook = selinux_ipv6_forward, + .hook = selinux_ip_forward, .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_SELINUX_FIRST, }, { - .hook = selinux_ipv6_output, + .hook = selinux_ip_output, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_SELINUX_FIRST, diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 084757ff43..35aac62a66 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -250,10 +250,10 @@ struct security_class_mapping secclass_map[] = { { COMMON_SOCK_PERMS, NULL } }, { "perf_event", { "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } }, - { "lockdown", - { "integrity", "confidentiality", NULL } }, { "anon_inode", { COMMON_FILE_PERMS, NULL } }, + { "io_uring", + { "override_creds", "sqpoll", NULL } }, { NULL } }; diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 0c58f62dc6..4d0456d3d4 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -39,7 +39,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, u16 family, u32 sid); -int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep, +int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc, struct sk_buff *skb); int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family); void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family); @@ -98,7 +98,7 @@ static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, return 0; } -static inline int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep, +static inline int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc, struct sk_buff *skb) { return 0; diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index abaab76838..1321f15799 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -29,6 +29,7 @@ /** * selinux_netlbl_sidlookup_cached - Cache a SID lookup * @skb: the packet + * @family: the packet's address family * @secattr: the NetLabel security attributes * @sid: the SID * @@ -128,6 +129,7 @@ void selinux_netlbl_cache_invalidate(void) /** * selinux_netlbl_err - Handle a NetLabel packet error * @skb: the packet + * @family: the packet's address family * @error: the error code * @gateway: true if host is acting as a gateway, false otherwise * @@ -160,7 +162,6 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec) /** * selinux_netlbl_sk_security_reset - Reset the NetLabel fields * @sksec: the sk_security_struct - * @family: the socket family * * Description: * Called when the NetLabel state of a sk_security_struct needs to be reset. @@ -260,30 +261,30 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, /** * selinux_netlbl_sctp_assoc_request - Label an incoming sctp association. - * @ep: incoming association endpoint. + * @asoc: incoming association. * @skb: the packet. * * Description: - * A new incoming connection is represented by @ep, ...... + * A new incoming connection is represented by @asoc, ...... * Returns zero on success, negative values on failure. * */ -int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep, +int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc, struct sk_buff *skb) { int rc; struct netlbl_lsm_secattr secattr; - struct sk_security_struct *sksec = ep->base.sk->sk_security; + struct sk_security_struct *sksec = asoc->base.sk->sk_security; struct sockaddr_in addr4; struct sockaddr_in6 addr6; - if (ep->base.sk->sk_family != PF_INET && - ep->base.sk->sk_family != PF_INET6) + if (asoc->base.sk->sk_family != PF_INET && + asoc->base.sk->sk_family != PF_INET6) return 0; netlbl_secattr_init(&secattr); rc = security_netlbl_sid_to_secattr(&selinux_state, - ep->secid, &secattr); + asoc->secid, &secattr); if (rc != 0) goto assoc_request_return; @@ -293,11 +294,11 @@ int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep, if (ip_hdr(skb)->version == 4) { addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = ip_hdr(skb)->saddr; - rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr4, &secattr); + rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr4, &secattr); } else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) { addr6.sin6_family = AF_INET6; addr6.sin6_addr = ipv6_hdr(skb)->saddr; - rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr6, &secattr); + rc = netlbl_conn_setattr(asoc->base.sk, (void *)&addr6, &secattr); } else { rc = -EAFNOSUPPORT; } @@ -313,6 +314,7 @@ int selinux_netlbl_sctp_assoc_request(struct sctp_endpoint *ep, /** * selinux_netlbl_inet_conn_request - Label an incoming stream connection * @req: incoming connection request socket + * @family: the request socket's address family * * Description: * A new incoming connection request is represented by @req, we need to label @@ -343,6 +345,7 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family) /** * selinux_netlbl_inet_csk_clone - Initialize the newly created sock * @sk: the new sock + * @family: the sock's address family * * Description: * A new connection has been established using @sk, we've already labeled the @@ -378,7 +381,7 @@ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk) /** * selinux_netlbl_socket_post_create - Label a socket using NetLabel - * @sock: the socket to label + * @sk: the sock to label * @family: protocol family * * Description: diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index a91fb0ed00..0ae4e4e57a 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -8,6 +8,7 @@ #include #include #include "hashtab.h" +#include "security.h" static struct kmem_cache *hashtab_node_cachep __ro_after_init; diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index d338962fb0..3f5fd12434 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -553,6 +553,7 @@ int mls_compute_sid(struct policydb *p, #ifdef CONFIG_NETLABEL /** * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel + * @p: the policy * @context: the security context * @secattr: the NetLabel security attributes * @@ -574,6 +575,7 @@ void mls_export_netlbl_lvl(struct policydb *p, /** * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels + * @p: the policy * @context: the security context * @secattr: the NetLabel security attributes * @@ -595,6 +597,7 @@ void mls_import_netlbl_lvl(struct policydb *p, /** * mls_export_netlbl_cat - Export the MLS categories to NetLabel + * @p: the policy * @context: the security context * @secattr: the NetLabel security attributes * @@ -622,6 +625,7 @@ int mls_export_netlbl_cat(struct policydb *p, /** * mls_import_netlbl_cat - Import the MLS categories from NetLabel + * @p: the policy * @context: the security context * @secattr: the NetLabel security attributes * diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index c4931bf6f9..8e92af7dd2 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1102,7 +1102,7 @@ void security_compute_xperms_decision(struct selinux_state *state, * @state: SELinux state * @ssid: source security identifier * @tsid: target security identifier - * @tclass: target security class + * @orig_tclass: target security class * @avd: access vector decisions * @xperms: extended permissions * @@ -1626,6 +1626,7 @@ int security_context_str_to_sid(struct selinux_state *state, * @scontext_len: length in bytes * @sid: security identifier, SID * @def_sid: default SID to assign on error + * @gfp_flags: the allocator get-free-page (GFP) flags * * Obtains a SID associated with the security context that * has the string representation specified by @scontext. @@ -1919,6 +1920,7 @@ static int security_compute_sid(struct selinux_state *state, * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class + * @qstr: object name * @out_sid: security identifier for new subject/object * * Compute a SID to use for labeling a new subject or object in the @@ -1947,6 +1949,7 @@ int security_transition_sid_user(struct selinux_state *state, /** * security_member_sid - Compute the SID for member selection. + * @state: SELinux state * @ssid: source security identifier * @tsid: target security identifier * @tclass: target security class @@ -2273,6 +2276,7 @@ void selinux_policy_commit(struct selinux_state *state, * @state: SELinux state * @data: binary policy data * @len: length of data in bytes + * @load_state: policy load state * * Load a new set of security policy configuration data, * validate it and convert the SID table as necessary. @@ -2525,7 +2529,7 @@ int security_ib_pkey_sid(struct selinux_state *state, * security_ib_endport_sid - Obtain the SID for a subnet management interface. * @state: SELinux state * @dev_name: device name - * @port: port number + * @port_num: port number * @out_sid: security identifier */ int security_ib_endport_sid(struct selinux_state *state, @@ -2856,9 +2860,10 @@ int security_get_user_sids(struct selinux_state *state, /** * __security_genfs_sid - Helper to obtain a SID for a file in a filesystem + * @policy: policy * @fstype: filesystem type * @path: path from root of mount - * @sclass: file security class + * @orig_sclass: file security class * @sid: SID for path * * Obtain a SID to use for a file in a filesystem that @@ -2915,7 +2920,7 @@ static inline int __security_genfs_sid(struct selinux_policy *policy, * @state: SELinux state * @fstype: filesystem type * @path: path from root of mount - * @sclass: file security class + * @orig_sclass: file security class * @sid: SID for path * * Acquire policy_rwlock before calling __security_genfs_sid() and release @@ -3297,6 +3302,7 @@ int security_sid_mls_copy(struct selinux_state *state, * @nlbl_sid: NetLabel SID * @nlbl_type: NetLabel labeling protocol type * @xfrm_sid: XFRM SID + * @peer_sid: network peer sid * * Description: * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 656d50b09f..293ec048af 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -570,7 +570,7 @@ void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry, goto out_unlock; } - cache = kmalloc(sizeof(struct sidtab_str_cache) + str_len, GFP_ATOMIC); + cache = kmalloc(struct_size(cache, str, str_len), GFP_ATOMIC); if (!cache) goto out_unlock; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index be83e5ce44..9069731789 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -89,7 +89,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, if (str_len >= PAGE_SIZE) return -ENOMEM; - ctx = kmalloc(sizeof(*ctx) + str_len + 1, gfp); + ctx = kmalloc(struct_size(ctx, ctx_str, str_len + 1), gfp); if (!ctx) return -ENOMEM; @@ -360,7 +360,7 @@ int selinux_xfrm_state_alloc_acquire(struct xfrm_state *x, if (rc) return rc; - ctx = kmalloc(sizeof(*ctx) + str_len, GFP_ATOMIC); + ctx = kmalloc(struct_size(ctx, ctx_str, str_len), GFP_ATOMIC); if (!ctx) { rc = -ENOMEM; goto out; diff --git a/security/smack/smack.h b/security/smack/smack.h index 99c3422596..fc837dcebf 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -389,22 +389,6 @@ static inline struct smack_known *smk_of_task(const struct task_smack *tsp) return tsp->smk_task; } -static inline struct smack_known *smk_of_task_struct_subj( - const struct task_struct *t) -{ - struct smack_known *skp; - const struct cred *cred; - - rcu_read_lock(); - - cred = rcu_dereference(t->cred); - skp = smk_of_task(smack_cred(cred)); - - rcu_read_unlock(); - - return skp; -} - static inline struct smack_known *smk_of_task_struct_obj( const struct task_struct *t) { diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 21a0e7c3b8..14b279cc75 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -51,8 +51,10 @@ #define SMK_RECEIVING 1 #define SMK_SENDING 2 +#ifdef SMACK_IPV6_PORT_LABELING static DEFINE_MUTEX(smack_ipv6_lock); static LIST_HEAD(smk_ipv6_port_list); +#endif struct kmem_cache *smack_rule_cache; int smack_enabled __initdata; @@ -389,7 +391,7 @@ static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead, /** * smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_* - * @mode - input mode in form of PTRACE_MODE_* + * @mode: input mode in form of PTRACE_MODE_* * * Returns a converted MAY_* mode usable by smack rules */ @@ -1213,6 +1215,7 @@ static int smack_inode_getattr(const struct path *path) /** * smack_inode_setxattr - Smack check for setting xattrs + * @mnt_userns: active user namespace * @dentry: the object * @name: name of the attribute * @value: value of the attribute @@ -1339,6 +1342,7 @@ static int smack_inode_getxattr(struct dentry *dentry, const char *name) /** * smack_inode_removexattr - Smack check on removexattr + * @mnt_userns: active user namespace * @dentry: the object * @name: name of the attribute * @@ -1398,6 +1402,7 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns, /** * smack_inode_getsecurity - get smack xattrs + * @mnt_userns: active user namespace * @inode: the object * @name: attribute name * @buffer: where to put the result @@ -1619,13 +1624,14 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, } /** - * smack_mmap_file : - * Check permissions for a mmap operation. The @file may be NULL, e.g. - * if mapping anonymous memory. - * @file contains the file structure for file to map (may be NULL). - * @reqprot contains the protection requested by the application. - * @prot contains the protection that will be applied by the kernel. - * @flags contains the operational flags. + * smack_mmap_file - Check permissions for a mmap operation. + * @file: contains the file structure for file to map (may be NULL). + * @reqprot: contains the protection requested by the application. + * @prot: contains the protection that will be applied by the kernel. + * @flags: contains the operational flags. + * + * The @file may be NULL, e.g. if mapping anonymous memory. + * * Return 0 if permission is granted. */ static int smack_mmap_file(struct file *file, @@ -2061,15 +2067,14 @@ static int smack_task_getsid(struct task_struct *p) } /** - * smack_task_getsecid_subj - get the subjective secid of the task - * @p: the task + * smack_current_getsecid_subj - get the subjective secid of the current task * @secid: where to put the result * * Sets the secid to contain a u32 version of the task's subjective smack label. */ -static void smack_task_getsecid_subj(struct task_struct *p, u32 *secid) +static void smack_current_getsecid_subj(u32 *secid) { - struct smack_known *skp = smk_of_task_struct_subj(p); + struct smack_known *skp = smk_of_current(); *secid = skp->smk_secid; } @@ -2603,7 +2608,6 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) mutex_unlock(&smack_ipv6_lock); return; } -#endif /** * smk_ipv6_port_check - check Smack port access @@ -2666,6 +2670,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address, return smk_ipv6_check(skp, object, address, act); } +#endif /** * smack_inode_setsecurity - set smack xattrs @@ -2852,8 +2857,9 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, rc = smk_ipv6_check(ssp->smk_out, rsp, sip, SMK_CONNECTING); } - if (__is_defined(SMACK_IPV6_PORT_LABELING)) - rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING); +#ifdef SMACK_IPV6_PORT_LABELING + rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING); +#endif return rc; } @@ -3051,7 +3057,7 @@ static int smack_sem_associate(struct kern_ipc_perm *isp, int semflg) } /** - * smack_sem_shmctl - Smack access check for sem + * smack_sem_semctl - Smack access check for sem * @isp: the object * @cmd: what it wants to do * @@ -3197,7 +3203,7 @@ static int smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg } /** - * smack_msg_queue_msgsnd - Smack access check for msg_queue + * smack_msg_queue_msgrcv - Smack access check for msg_queue * @isp: the object * @msg: unused * @target: unused @@ -3206,8 +3212,10 @@ static int smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg * * Returns 0 if current has read and write access, error code otherwise */ -static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg, - struct task_struct *target, long type, int mode) +static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, + struct msg_msg *msg, + struct task_struct *target, long type, + int mode) { return smk_curacc_msq(isp, MAY_READWRITE); } @@ -4634,7 +4642,7 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new) /* * Get label from overlay inode and set it in create_sid */ - isp = smack_inode(d_inode(dentry->d_parent)); + isp = smack_inode(d_inode(dentry)); skp = isp->smk_inode; tsp->smk_task = skp; *new = new_creds; @@ -4691,6 +4699,48 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, return 0; } +#ifdef CONFIG_IO_URING +/** + * smack_uring_override_creds - Is io_uring cred override allowed? + * @new: the target creds + * + * Check to see if the current task is allowed to override it's credentials + * to service an io_uring operation. + */ +static int smack_uring_override_creds(const struct cred *new) +{ + struct task_smack *tsp = smack_cred(current_cred()); + struct task_smack *nsp = smack_cred(new); + + /* + * Allow the degenerate case where the new Smack value is + * the same as the current Smack value. + */ + if (tsp->smk_task == nsp->smk_task) + return 0; + + if (smack_privileged_cred(CAP_MAC_OVERRIDE, current_cred())) + return 0; + + return -EPERM; +} + +/** + * smack_uring_sqpoll - check if a io_uring polling thread can be created + * + * Check to see if the current task is allowed to create a new io_uring + * kernel polling thread. + */ +static int smack_uring_sqpoll(void) +{ + if (smack_privileged_cred(CAP_MAC_ADMIN, current_cred())) + return 0; + + return -EPERM; +} + +#endif /* CONFIG_IO_URING */ + struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_smack), .lbs_file = sizeof(struct smack_known *), @@ -4756,7 +4806,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(task_setpgid, smack_task_setpgid), LSM_HOOK_INIT(task_getpgid, smack_task_getpgid), LSM_HOOK_INIT(task_getsid, smack_task_getsid), - LSM_HOOK_INIT(task_getsecid_subj, smack_task_getsecid_subj), + LSM_HOOK_INIT(current_getsecid_subj, smack_current_getsecid_subj), LSM_HOOK_INIT(task_getsecid_obj, smack_task_getsecid_obj), LSM_HOOK_INIT(task_setnice, smack_task_setnice), LSM_HOOK_INIT(task_setioprio, smack_task_setioprio), @@ -4843,6 +4893,10 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(inode_copy_up, smack_inode_copy_up), LSM_HOOK_INIT(inode_copy_up_xattr, smack_inode_copy_up_xattr), LSM_HOOK_INIT(dentry_create_files_as, smack_dentry_create_files_as), +#ifdef CONFIG_IO_URING + LSM_HOOK_INIT(uring_override_creds, smack_uring_override_creds), + LSM_HOOK_INIT(uring_sqpoll, smack_uring_sqpoll), +#endif }; diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index fc7399b453..b945c1d3a7 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -18,27 +18,7 @@ #include #include "smack.h" -#if IS_ENABLED(CONFIG_IPV6) - -static unsigned int smack_ipv6_output(void *priv, - struct sk_buff *skb, - const struct nf_hook_state *state) -{ - struct sock *sk = skb_to_full_sk(skb); - struct socket_smack *ssp; - struct smack_known *skp; - - if (sk && sk->sk_security) { - ssp = sk->sk_security; - skp = ssp->smk_out; - skb->secmark = skp->smk_secid; - } - - return NF_ACCEPT; -} -#endif /* IPV6 */ - -static unsigned int smack_ipv4_output(void *priv, +static unsigned int smack_ip_output(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { @@ -57,14 +37,14 @@ static unsigned int smack_ipv4_output(void *priv, static const struct nf_hook_ops smack_nf_ops[] = { { - .hook = smack_ipv4_output, + .hook = smack_ip_output, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, }, #if IS_ENABLED(CONFIG_IPV6) { - .hook = smack_ipv6_output, + .hook = smack_ip_output, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP6_PRI_SELINUX_FIRST, diff --git a/sound/core/Makefile b/sound/core/Makefile index 79e1407cd0..350d704ced 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -19,7 +19,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o -snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o diff --git a/sound/core/control_led.c b/sound/core/control_led.c index a95332b2b9..207828f309 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -509,7 +509,7 @@ static char *parse_string(char *s, char *val, size_t val_size) return s; } -static char *parse_iface(char *s, unsigned int *val) +static char *parse_iface(char *s, snd_ctl_elem_iface_t *val) { if (!strncasecmp(s, "card", 4)) *val = SNDRV_CTL_ELEM_IFACE_CARD; diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 1ba887c795..ebc714b2f4 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -32,10 +32,8 @@ int snd_oss_info_register(int dev, int num, char *string) mutex_lock(&strings); if (string == NULL) { x = snd_sndstat_strings[num][dev]; - if (x) { - kfree(x); - x = NULL; - } + kfree(x); + x = NULL; } else { x = kstrdup(string, GFP_KERNEL); if (x == NULL) { diff --git a/sound/core/init.c b/sound/core/init.c index ac335f5906..31ba7024e3 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -1111,29 +1111,14 @@ EXPORT_SYMBOL(snd_card_file_remove); */ int snd_power_ref_and_wait(struct snd_card *card) { - wait_queue_entry_t wait; - int result = 0; - snd_power_ref(card); - /* fastpath */ if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) return 0; - init_waitqueue_entry(&wait, current); - add_wait_queue(&card->power_sleep, &wait); - while (1) { - if (card->shutdown) { - result = -ENODEV; - break; - } - if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) - break; - snd_power_unref(card); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(30 * HZ); - snd_power_ref(card); - } - remove_wait_queue(&card->power_sleep, &wait); - return result; + wait_event_cmd(card->power_sleep, + card->shutdown || + snd_power_get_state(card) == SNDRV_CTL_POWER_D0, + snd_power_unref(card), snd_power_ref(card)); + return card->shutdown ? -ENODEV : 0; } EXPORT_SYMBOL_GPL(snd_power_ref_and_wait); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 2d84298257..6fd763d4d1 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #ifdef CONFIG_X86 #include @@ -39,9 +40,11 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) } /** - * snd_dma_alloc_pages - allocate the buffer area according to the given type + * snd_dma_alloc_dir_pages - allocate the buffer area according to the given + * type and direction * @type: the DMA buffer type * @device: the device pointer + * @dir: DMA direction * @size: the buffer size to allocate * @dmab: buffer allocation record to store the allocated data * @@ -51,8 +54,9 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) * Return: Zero if the buffer with the given size is allocated successfully, * otherwise a negative value on error. */ -int snd_dma_alloc_pages(int type, struct device *device, size_t size, - struct snd_dma_buffer *dmab) +int snd_dma_alloc_dir_pages(int type, struct device *device, + enum dma_data_direction dir, size_t size, + struct snd_dma_buffer *dmab) { if (WARN_ON(!size)) return -ENXIO; @@ -62,6 +66,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, size = PAGE_ALIGN(size); dmab->dev.type = type; dmab->dev.dev = device; + dmab->dev.dir = dir; dmab->bytes = 0; dmab->addr = 0; dmab->private_data = NULL; @@ -71,7 +76,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = size; return 0; } -EXPORT_SYMBOL(snd_dma_alloc_pages); +EXPORT_SYMBOL(snd_dma_alloc_dir_pages); /** * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback @@ -129,9 +134,10 @@ static void __snd_release_pages(struct device *dev, void *res) } /** - * snd_devm_alloc_pages - allocate the buffer and manage with devres + * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres * @dev: the device pointer * @type: the DMA buffer type + * @dir: DMA direction * @size: the buffer size to allocate * * Allocate buffer pages depending on the given type and manage using devres. @@ -144,7 +150,8 @@ static void __snd_release_pages(struct device *dev, void *res) * The function returns the snd_dma_buffer object at success, or NULL if failed. */ struct snd_dma_buffer * -snd_devm_alloc_pages(struct device *dev, int type, size_t size) +snd_devm_alloc_dir_pages(struct device *dev, int type, + enum dma_data_direction dir, size_t size) { struct snd_dma_buffer *dmab; int err; @@ -157,7 +164,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size) if (!dmab) return NULL; - err = snd_dma_alloc_pages(type, dev, size, dmab); + err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab); if (err < 0) { devres_free(dmab); return NULL; @@ -166,7 +173,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size) devres_add(dev, dmab); return dmab; } -EXPORT_SYMBOL_GPL(snd_devm_alloc_pages); +EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages); /** * snd_dma_buffer_mmap - perform mmap of the given DMA buffer @@ -188,6 +195,26 @@ int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab, } EXPORT_SYMBOL(snd_dma_buffer_mmap); +#ifdef CONFIG_HAS_DMA +/** + * snd_dma_buffer_sync - sync DMA buffer between CPU and device + * @dmab: buffer allocation information + * @mode: sync mode + */ +void snd_dma_buffer_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + const struct snd_malloc_ops *ops; + + if (!dmab || !dmab->dev.need_sync) + return; + ops = snd_dma_get_ops(dmab); + if (ops && ops->sync) + ops->sync(dmab, mode); +} +EXPORT_SYMBOL_GPL(snd_dma_buffer_sync); +#endif /* CONFIG_HAS_DMA */ + /** * snd_sgbuf_get_addr - return the physical address at the corresponding offset * @dmab: buffer allocation information @@ -471,6 +498,225 @@ static const struct snd_malloc_ops snd_dma_wc_ops = { .mmap = snd_dma_wc_mmap, }; #endif /* CONFIG_X86 */ + +/* + * Non-contiguous pages allocator + */ +static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + struct sg_table *sgt; + void *p; + + sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir, + DEFAULT_GFP, 0); + if (!sgt) + return NULL; + dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, + sg_dma_address(sgt->sgl)); + p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt); + if (p) + dmab->private_data = sgt; + else + dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir); + return p; +} + +static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab) +{ + dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area); + dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data, + dmab->dev.dir); +} + +static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + return dma_mmap_noncontiguous(dmab->dev.dev, area, + dmab->bytes, dmab->private_data); +} + +static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + if (mode == SNDRV_DMA_SYNC_CPU) { + if (dmab->dev.dir == DMA_TO_DEVICE) + return; + invalidate_kernel_vmap_range(dmab->area, dmab->bytes); + dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data, + dmab->dev.dir); + } else { + if (dmab->dev.dir == DMA_FROM_DEVICE) + return; + flush_kernel_vmap_range(dmab->area, dmab->bytes); + dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data, + dmab->dev.dir); + } +} + +static inline void snd_dma_noncontig_iter_set(struct snd_dma_buffer *dmab, + struct sg_page_iter *piter, + size_t offset) +{ + struct sg_table *sgt = dmab->private_data; + + __sg_page_iter_start(piter, sgt->sgl, sgt->orig_nents, + offset >> PAGE_SHIFT); +} + +static dma_addr_t snd_dma_noncontig_get_addr(struct snd_dma_buffer *dmab, + size_t offset) +{ + struct sg_dma_page_iter iter; + + snd_dma_noncontig_iter_set(dmab, &iter.base, offset); + __sg_page_iter_dma_next(&iter); + return sg_page_iter_dma_address(&iter) + offset % PAGE_SIZE; +} + +static struct page *snd_dma_noncontig_get_page(struct snd_dma_buffer *dmab, + size_t offset) +{ + struct sg_page_iter iter; + + snd_dma_noncontig_iter_set(dmab, &iter, offset); + __sg_page_iter_next(&iter); + return sg_page_iter_page(&iter); +} + +static unsigned int +snd_dma_noncontig_get_chunk_size(struct snd_dma_buffer *dmab, + unsigned int ofs, unsigned int size) +{ + struct sg_dma_page_iter iter; + unsigned int start, end; + unsigned long addr; + + start = ALIGN_DOWN(ofs, PAGE_SIZE); + end = ofs + size - 1; /* the last byte address */ + snd_dma_noncontig_iter_set(dmab, &iter.base, start); + if (!__sg_page_iter_dma_next(&iter)) + return 0; + /* check page continuity */ + addr = sg_page_iter_dma_address(&iter); + for (;;) { + start += PAGE_SIZE; + if (start > end) + break; + addr += PAGE_SIZE; + if (!__sg_page_iter_dma_next(&iter) || + sg_page_iter_dma_address(&iter) != addr) + return start - ofs; + } + /* ok, all on continuous pages */ + return size; +} + +static const struct snd_malloc_ops snd_dma_noncontig_ops = { + .alloc = snd_dma_noncontig_alloc, + .free = snd_dma_noncontig_free, + .mmap = snd_dma_noncontig_mmap, + .sync = snd_dma_noncontig_sync, + .get_addr = snd_dma_noncontig_get_addr, + .get_page = snd_dma_noncontig_get_page, + .get_chunk_size = snd_dma_noncontig_get_chunk_size, +}; + +/* x86-specific SG-buffer with WC pages */ +#ifdef CONFIG_SND_DMA_SGBUF +#define sg_wc_address(it) ((unsigned long)page_address(sg_page_iter_page(it))) + +static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + void *p = snd_dma_noncontig_alloc(dmab, size); + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + if (!p) + return NULL; + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wc(sg_wc_address(&iter), 1); + return p; +} + +static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab) +{ + struct sg_table *sgt = dmab->private_data; + struct sg_page_iter iter; + + for_each_sgtable_page(sgt, &iter, 0) + set_memory_wb(sg_wc_address(&iter), 1); + snd_dma_noncontig_free(dmab); +} + +static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return dma_mmap_noncontiguous(dmab->dev.dev, area, + dmab->bytes, dmab->private_data); +} + +static const struct snd_malloc_ops snd_dma_sg_wc_ops = { + .alloc = snd_dma_sg_wc_alloc, + .free = snd_dma_sg_wc_free, + .mmap = snd_dma_sg_wc_mmap, + .sync = snd_dma_noncontig_sync, + .get_addr = snd_dma_noncontig_get_addr, + .get_page = snd_dma_noncontig_get_page, + .get_chunk_size = snd_dma_noncontig_get_chunk_size, +}; +#endif /* CONFIG_SND_DMA_SGBUF */ + +/* + * Non-coherent pages allocator + */ +static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + void *p; + + p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr, + dmab->dev.dir, DEFAULT_GFP); + if (p) + dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr); + return p; +} + +static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab) +{ + dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area, + dmab->addr, dmab->dev.dir); +} + +static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = vm_get_page_prot(area->vm_flags); + return dma_mmap_pages(dmab->dev.dev, area, + area->vm_end - area->vm_start, + virt_to_page(dmab->area)); +} + +static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + if (mode == SNDRV_DMA_SYNC_CPU) { + if (dmab->dev.dir != DMA_TO_DEVICE) + dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr, + dmab->bytes, dmab->dev.dir); + } else { + if (dmab->dev.dir != DMA_FROM_DEVICE) + dma_sync_single_for_device(dmab->dev.dev, dmab->addr, + dmab->bytes, dmab->dev.dir); + } +} + +static const struct snd_malloc_ops snd_dma_noncoherent_ops = { + .alloc = snd_dma_noncoherent_alloc, + .free = snd_dma_noncoherent_free, + .mmap = snd_dma_noncoherent_mmap, + .sync = snd_dma_noncoherent_sync, +}; + #endif /* CONFIG_HAS_DMA */ /* @@ -482,14 +728,15 @@ static const struct snd_malloc_ops *dma_ops[] = { #ifdef CONFIG_HAS_DMA [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops, [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops, + [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops, + [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops, +#ifdef CONFIG_SND_DMA_SGBUF + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops, +#endif #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, - [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, -#endif }; static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab) diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h index 9f2e0a608b..a6f3a87194 100644 --- a/sound/core/memalloc_local.h +++ b/sound/core/memalloc_local.h @@ -10,6 +10,7 @@ struct snd_malloc_ops { unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab, unsigned int ofs, unsigned int size); int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area); + void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode); }; #ifdef CONFIG_SND_DMA_SGBUF diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 3ee9edf858..f158f0abd2 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -774,6 +774,11 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, if (oss_period_size < 16) return -EINVAL; + + /* don't allocate too large period; 1MB period must be enough */ + if (oss_period_size > 1024 * 1024) + return -ENOMEM; + runtime->oss.period_bytes = oss_period_size; runtime->oss.period_frames = 1; runtime->oss.periods = oss_periods; @@ -1043,10 +1048,9 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) goto failure; } #endif - oss_period_size *= oss_frame_size; - - oss_buffer_size = oss_period_size * runtime->oss.periods; - if (oss_buffer_size < 0) { + oss_period_size = array_size(oss_period_size, oss_frame_size); + oss_buffer_size = array_size(oss_period_size, runtime->oss.periods); + if (oss_buffer_size <= 0) { err = -EINVAL; goto failure; } diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 061ba06bc9..82e180c776 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -62,7 +62,10 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t width = snd_pcm_format_physical_width(format->format); if (width < 0) return width; - size = frames * format->channels * width; + size = array3_size(frames, format->channels, width); + /* check for too large period size once again */ + if (size > 1024 * 1024) + return -ENOMEM; if (snd_BUG_ON(size % 8)) return -ENXIO; size /= 8; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index ba4a987ed1..edd9849210 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -969,6 +969,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, init_waitqueue_head(&runtime->tsleep); runtime->status->state = SNDRV_PCM_STATE_OPEN; + mutex_init(&runtime->buffer_mutex); substream->runtime = runtime; substream->private_data = pcm->private_data; @@ -1002,6 +1003,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) } else { substream->runtime = NULL; } + mutex_destroy(&runtime->buffer_mutex); kfree(runtime); put_pid(substream->pid); substream->pid = NULL; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index dfe5a64e19..e4e176854c 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -453,6 +453,8 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, sstatus.suspended_state = status->suspended_state; sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || @@ -533,6 +535,8 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream, sync_ptr.s.status.suspended_state = status->suspended_state; sync_ptr.s.status.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) return -EFAULT; return 0; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 1fc2fa0775..af6f717e1e 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -91,8 +91,8 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); * @dma_data: DAI DMA data * @slave_config: DMA slave configuration * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width + * fields of the DMA slave config from the same fields of the DAI DMA * data struct. The src and dst fields will be initialized depending on the * direction of the substream. If the substream is a playback stream the dst * fields will be initialized, if it is a capture stream the src fields will be @@ -124,7 +124,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data( slave_config->src_addr_width = dma_data->addr_width; } - slave_config->slave_id = dma_data->slave_id; slave_config->peripheral_config = dma_data->peripheral_config; slave_config->peripheral_size = dma_data->peripheral_size; } diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a144a3f68e..a40a35e51f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -106,6 +106,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram frames -= transfer; ofs = 0; } + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); } #ifdef CONFIG_SND_DEBUG @@ -1905,9 +1906,11 @@ static int wait_for_avail(struct snd_pcm_substream *substream, if (avail >= runtime->twake) break; snd_pcm_stream_unlock_irq(substream); + mutex_unlock(&runtime->buffer_mutex); tout = schedule_timeout(wait_time); + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); set_current_state(TASK_INTERRUPTIBLE); switch (runtime->status->state) { @@ -2127,11 +2130,28 @@ int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff; int ret; if (old_appl_ptr == appl_ptr) return 0; + if (appl_ptr >= runtime->boundary) + return -EINVAL; + /* + * check if a rewind is requested by the application + */ + if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) { + diff = appl_ptr - old_appl_ptr; + if (diff >= 0) { + if (diff > runtime->buffer_size) + return -EINVAL; + } else { + if (runtime->boundary + diff > runtime->buffer_size) + return -EINVAL; + } + } + runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) { ret = substream->ops->ack(substream); @@ -2201,6 +2221,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, nonblock = !!(substream->f_flags & O_NONBLOCK); + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); err = pcm_accessible_state(runtime); if (err < 0) @@ -2256,8 +2277,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, goto _end_unlock; } snd_pcm_stream_unlock_irq(substream); + if (!is_playback) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); err = writer(substream, appl_ofs, data, offset, frames, transfer); + if (is_playback) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; @@ -2288,6 +2313,7 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, if (xfer > 0 && err >= 0) snd_pcm_update_state(substream, runtime); snd_pcm_stream_unlock_irq(substream); + mutex_unlock(&runtime->buffer_mutex); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } EXPORT_SYMBOL(__snd_pcm_lib_xfer); diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index fe9689b8a6..ecb21697ae 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -73,4 +73,11 @@ void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq); for ((subs) = (pcm)->streams[str].substream; (subs); \ (subs) = (subs)->next) +static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream, + enum snd_dma_sync_mode mode) +{ + if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC) + snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode); +} + #endif /* __SOUND_CORE_PCM_LOCAL_H */ diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 7fbd1ccbb5..8848d2f316 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -32,15 +32,20 @@ module_param(max_alloc_per_card, ulong, 0644); MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card."); static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, - size_t size, struct snd_dma_buffer *dmab) + int str, size_t size, struct snd_dma_buffer *dmab) { + enum dma_data_direction dir; int err; if (max_alloc_per_card && card->total_pcm_alloc_bytes + size > max_alloc_per_card) return -ENOMEM; - err = snd_dma_alloc_pages(type, dev, size, dmab); + if (str == SNDRV_PCM_STREAM_PLAYBACK) + dir = DMA_TO_DEVICE; + else + dir = DMA_FROM_DEVICE; + err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab); if (!err) { mutex_lock(&card->memory_mutex); card->total_pcm_alloc_bytes += dmab->bytes; @@ -77,7 +82,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, do { err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev, - size, dmab); + substream->stream, size, dmab); if (err != -ENOMEM) return err; if (no_fallback) @@ -158,32 +163,34 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, size_t size; struct snd_dma_buffer new_dmab; + mutex_lock(&substream->pcm->open_mutex); if (substream->runtime) { buffer->error = -EBUSY; - return; + goto unlock; } if (!snd_info_get_line(buffer, line, sizeof(line))) { snd_info_get_str(str, line, sizeof(str)); size = simple_strtoul(str, NULL, 10) * 1024; if ((size != 0 && size < 8192) || size > substream->dma_max) { buffer->error = -EINVAL; - return; + goto unlock; } if (substream->dma_buffer.bytes == size) - return; + goto unlock; memset(&new_dmab, 0, sizeof(new_dmab)); new_dmab.dev = substream->dma_buffer.dev; if (size > 0) { if (do_alloc_pages(card, substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, + substream->stream, size, &new_dmab) < 0) { buffer->error = -ENOMEM; pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", substream->pcm->card->number, substream->pcm->device, substream->stream ? 'c' : 'p', substream->number, substream->pcm->name, size); - return; + goto unlock; } substream->buffer_bytes_max = size; } else { @@ -195,6 +202,8 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, } else { buffer->error = -EINVAL; } + unlock: + mutex_unlock(&substream->pcm->open_mutex); } static inline void preallocate_info_init(struct snd_pcm_substream *substream) @@ -418,6 +427,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) if (do_alloc_pages(card, substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, + substream->stream, size, dmab) < 0) { kfree(dmab); pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d233cb3b41..704fdc9ebf 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -172,6 +172,19 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); +unsigned long _snd_pcm_stream_lock_irqsave_nested(struct snd_pcm_substream *substream) +{ + unsigned long flags = 0; + if (substream->pcm->nonatomic) + mutex_lock_nested(&substream->self_group.mutex, + SINGLE_DEPTH_NESTING); + else + spin_lock_irqsave_nested(&substream->self_group.lock, flags, + SINGLE_DEPTH_NESTING); + return flags; +} +EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave_nested); + /** * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream * @substream: PCM substream @@ -672,33 +685,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, return 0; } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +#define is_oss_stream(substream) ((substream)->oss.oss) +#else +#define is_oss_stream(substream) false +#endif + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime; - int err, usecs; + int err = 0, usecs; unsigned int bits; snd_pcm_uframes_t frames; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: + if (!is_oss_stream(substream) && + atomic_read(&substream->mmap_count)) + err = -EBADFD; break; default: - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + err = -EBADFD; + break; } snd_pcm_stream_unlock_irq(substream); -#if IS_ENABLED(CONFIG_SND_PCM_OSS) - if (!substream->oss.oss) -#endif - if (atomic_read(&substream->mmap_count)) - return -EBADFD; + if (err) + goto unlock; snd_pcm_sync_stop(substream, true); @@ -786,16 +806,21 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, if (usecs >= 0) cpu_latency_qos_add_request(&substream->latency_pm_qos_req, usecs); - return 0; + err = 0; _error: - /* hardware might be unusable from this time, - so we force application to retry to set - the correct hardware parameter settings */ - snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); - if (substream->ops->hw_free != NULL) - substream->ops->hw_free(substream); - if (substream->managed_buffer_alloc) - snd_pcm_lib_free_pages(substream); + if (err) { + /* hardware might be unusable from this time, + * so we force application to retry to set + * the correct hardware parameter settings + */ + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + if (substream->managed_buffer_alloc) + snd_pcm_lib_free_pages(substream); + } + unlock: + mutex_unlock(&runtime->buffer_mutex); return err; } @@ -835,26 +860,31 @@ static int do_hw_free(struct snd_pcm_substream *substream) static int snd_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - int result; + int result = 0; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + mutex_lock(&runtime->buffer_mutex); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: + if (atomic_read(&substream->mmap_count)) + result = -EBADFD; break; default: - snd_pcm_stream_unlock_irq(substream); - return -EBADFD; + result = -EBADFD; + break; } snd_pcm_stream_unlock_irq(substream); - if (atomic_read(&substream->mmap_count)) - return -EBADFD; + if (result) + goto unlock; result = do_hw_free(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); cpu_latency_qos_remove_request(&substream->latency_pm_qos_req); + unlock: + mutex_unlock(&runtime->buffer_mutex); return result; } @@ -1160,15 +1190,17 @@ struct action_ops { static int snd_pcm_action_group(const struct action_ops *ops, struct snd_pcm_substream *substream, snd_pcm_state_t state, - bool do_lock) + bool stream_lock) { struct snd_pcm_substream *s = NULL; struct snd_pcm_substream *s1; int res = 0, depth = 1; snd_pcm_group_for_each_entry(s, substream) { - if (do_lock && s != substream) { - if (s->pcm->nonatomic) + if (s != substream) { + if (!stream_lock) + mutex_lock_nested(&s->runtime->buffer_mutex, depth); + else if (s->pcm->nonatomic) mutex_lock_nested(&s->self_group.mutex, depth); else spin_lock_nested(&s->self_group.lock, depth); @@ -1196,18 +1228,18 @@ static int snd_pcm_action_group(const struct action_ops *ops, ops->post_action(s, state); } _unlock: - if (do_lock) { - /* unlock streams */ - snd_pcm_group_for_each_entry(s1, substream) { - if (s1 != substream) { - if (s1->pcm->nonatomic) - mutex_unlock(&s1->self_group.mutex); - else - spin_unlock(&s1->self_group.lock); - } - if (s1 == s) /* end */ - break; + /* unlock streams */ + snd_pcm_group_for_each_entry(s1, substream) { + if (s1 != substream) { + if (!stream_lock) + mutex_unlock(&s1->runtime->buffer_mutex); + else if (s1->pcm->nonatomic) + mutex_unlock(&s1->self_group.mutex); + else + spin_unlock(&s1->self_group.lock); } + if (s1 == s) /* end */ + break; } return res; } @@ -1337,10 +1369,12 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops, /* Guarantee the group members won't change during non-atomic action */ down_read(&snd_pcm_link_rwsem); + mutex_lock(&substream->runtime->buffer_mutex); if (snd_pcm_stream_linked(substream)) res = snd_pcm_action_group(ops, substream, state, false); else res = snd_pcm_action_single(ops, substream, state); + mutex_unlock(&substream->runtime->buffer_mutex); up_read(&snd_pcm_link_rwsem); return res; } @@ -1830,11 +1864,13 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; + snd_pcm_stream_lock_irq(substream); runtime->hw_ptr_base = 0; runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; runtime->silence_start = runtime->status->hw_ptr; runtime->silence_filled = 0; + snd_pcm_stream_unlock_irq(substream); return 0; } @@ -1842,10 +1878,12 @@ static void snd_pcm_post_reset(struct snd_pcm_substream *substream, snd_pcm_state_t state) { struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_stream_lock_irq(substream); runtime->control->appl_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); + snd_pcm_stream_unlock_irq(substream); } static const struct action_ops snd_pcm_action_reset = { @@ -2685,6 +2723,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, goto error; } + /* automatically set EXPLICIT_SYNC flag in the managed mode whenever + * the DMA buffer requires it + */ + if (substream->managed_buffer_alloc && + substream->dma_buffer.dev.need_sync) + substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC; + *rsubstream = substream; return 0; @@ -2912,6 +2957,8 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, ret = rewind_appl_ptr(substream, frames, snd_pcm_hw_avail(substream)); snd_pcm_stream_unlock_irq(substream); + if (ret >= 0) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; } @@ -2929,35 +2976,31 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream, ret = forward_appl_ptr(substream, frames, snd_pcm_avail(substream)); snd_pcm_stream_unlock_irq(substream); + if (ret >= 0) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; } -static int snd_pcm_hwsync(struct snd_pcm_substream *substream) -{ - int err; - - snd_pcm_stream_lock_irq(substream); - err = do_pcm_hwsync(substream); - snd_pcm_stream_unlock_irq(substream); - return err; -} - static int snd_pcm_delay(struct snd_pcm_substream *substream, snd_pcm_sframes_t *delay) { int err; - snd_pcm_sframes_t n = 0; snd_pcm_stream_lock_irq(substream); err = do_pcm_hwsync(substream); - if (!err) - n = snd_pcm_calc_delay(substream); + if (delay && !err) + *delay = snd_pcm_calc_delay(substream); snd_pcm_stream_unlock_irq(substream); - if (!err) - *delay = n; + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); + return err; } +static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream) +{ + return snd_pcm_delay(substream, NULL); +} + static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr __user *_sync_ptr) { @@ -3000,6 +3043,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, sync_ptr.s.status.suspended_state = status->suspended_state; sync_ptr.s.status.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) return -EFAULT; return 0; @@ -3096,6 +3141,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, sstatus.suspended_state = status->suspended_state; sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || @@ -3218,6 +3265,9 @@ static int snd_pcm_common_ioctl(struct file *file, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; + res = snd_power_wait(substream->pcm->card); if (res < 0) return res; @@ -3272,7 +3322,7 @@ static int snd_pcm_common_ioctl(struct file *file, return snd_pcm_hwsync(substream); case SNDRV_PCM_IOCTL_DELAY: { - snd_pcm_sframes_t delay; + snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t __user *res = arg; int err; @@ -3344,6 +3394,9 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, snd_pcm_uframes_t *frames = arg; snd_pcm_sframes_t result; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; + switch (cmd) { case SNDRV_PCM_IOCTL_FORWARD: { @@ -3386,7 +3439,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!frame_aligned(runtime, count)) return -EINVAL; @@ -3410,7 +3464,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!frame_aligned(runtime, count)) return -EINVAL; @@ -3436,7 +3491,8 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!iter_is_iovec(to)) return -EINVAL; @@ -3472,7 +3528,8 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!iter_is_iovec(from)) return -EINVAL; @@ -3511,6 +3568,9 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait) return ok | EPOLLERR; runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return ok | EPOLLERR; + poll_wait(file, &runtime->sleep, wait); mask = 0; @@ -3820,6 +3880,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { @@ -3856,6 +3918,8 @@ static int snd_pcm_fasync(int fd, struct file * file, int on) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; return fasync_helper(fd, file, on, &runtime->fasync); } diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 4abc38c70c..f5cae49500 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -262,6 +262,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream) return 0; } +/* + * drain output work queue + */ +static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream) +{ + struct snd_virmidi *vmidi = substream->runtime->private_data; + + flush_work(&vmidi->output_work); +} + /* * subscribe callback - allow output to rawmidi device */ @@ -336,6 +346,7 @@ static const struct snd_rawmidi_ops snd_virmidi_output_ops = { .open = snd_virmidi_output_open, .close = snd_virmidi_output_close, .trigger = snd_virmidi_output_trigger, + .drain = snd_virmidi_output_drain, }; /* diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 7f7eed6faa..58012de90c 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -90,15 +90,12 @@ static int snd_virmidi_probe(struct platform_device *devptr) } for (idx = 0; idx < midi_devs[dev]; idx++) { struct snd_rawmidi *rmidi; - struct snd_virmidi_dev *rdev; err = snd_virmidi_new(card, idx, &rmidi); if (err < 0) return err; - rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); - rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; } strcpy(card->driver, "VirMIDI"); diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index fd109bea4c..22b6c77968 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -169,6 +169,7 @@ config SND_FIREWIRE_MOTU * 828 * 896 * 828mk2 + * 896hd * Traveler * Ultralite * 8pre @@ -176,7 +177,9 @@ config SND_FIREWIRE_MOTU * 828mk3 (Hybrid) * Ultralite mk3 (FireWire only) * Ultralite mk3 (Hybrid) + * Traveler mk3 * Audio Express + * Track 16 * 4pre To compile this driver as a module, choose M here: the module diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index ac66f08acd..53dbd4d4b0 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -50,8 +50,9 @@ static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream) efw->firmware_version == 0x5070300 || efw->firmware_version == 0x5080000)) efw->tx_stream.flags |= CIP_UNALIGHED_DBC; - // AudioFire9 always reports wrong dbs. - if (efw->is_af9) + // AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0) + // also report wrong dbs at 88.2 kHz or greater. + if (efw->is_af9 || efw->firmware_version == 0x4060000) efw->tx_stream.flags |= CIP_WRONG_DBS; // Firmware version 5.5 reports fixed interval for dbc. if (efw->firmware_version == 0x5050000) diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile index acdf66564f..3bef2a0b1e 100644 --- a/sound/firewire/motu/Makefile +++ b/sound/firewire/motu/Makefile @@ -4,5 +4,6 @@ CFLAGS_amdtp-motu.o := -I$(src) snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \ motu-protocol-v2.o motu-protocol-v3.o \ - motu-protocol-v1.o + motu-protocol-v1.o motu-register-dsp-message-parser.o \ + motu-command-dsp-message-parser.o obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index a18c2c033e..2fb52f481d 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -333,6 +333,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, unsigned int packets, struct snd_pcm_substream *pcm) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); struct amdtp_motu *p = s->protocol; unsigned int pcm_frames = 0; int i; @@ -357,6 +358,14 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, read_midi_messages(s, buf, data_blocks); } + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + snd_motu_register_dsp_message_parser_parse(motu, descs, packets, + s->data_block_quadlets); + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + snd_motu_command_dsp_message_parser_parse(motu, descs, packets, + s->data_block_quadlets); + } + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) @@ -415,8 +424,6 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, if (p->midi_ports) write_midi_messages(s, buf, data_blocks); - // TODO: how to interact control messages between userspace? - write_sph(p->cache, buf, data_blocks, s->data_block_quadlets); } diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index b5ced5d277..a900fc0e76 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -16,6 +16,14 @@ #include "motu.h" +static bool has_dsp_event(struct snd_motu *motu) +{ + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) + return (snd_motu_register_dsp_message_parser_count_event(motu) > 0); + else + return false; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&motu->lock); - while (!motu->dev_lock_changed && motu->msg == 0) { + while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&motu->lock); schedule(); @@ -40,21 +48,47 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; event.lock_status.status = (motu->dev_lock_count > 0); motu->dev_lock_changed = false; + spin_unlock_irq(&motu->lock); - count = min_t(long, count, sizeof(event.lock_status)); - } else { + count = min_t(long, count, sizeof(event)); + if (copy_to_user(buf, &event, count)) + return -EFAULT; + } else if (motu->msg > 0) { event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION; event.motu_notification.message = motu->msg; motu->msg = 0; + spin_unlock_irq(&motu->lock); - count = min_t(long, count, sizeof(event.motu_notification)); + count = min_t(long, count, sizeof(event)); + if (copy_to_user(buf, &event, count)) + return -EFAULT; + } else if (has_dsp_event(motu)) { + size_t consumed = 0; + u32 __user *ptr; + u32 ev; + + spin_unlock_irq(&motu->lock); + + // Header is filled later. + consumed += sizeof(event.motu_register_dsp_change); + + while (consumed < count && + snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { + ptr = (u32 __user *)(buf + consumed); + if (put_user(ev, ptr)) + return -EFAULT; + consumed += sizeof(ev); + } + + event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; + event.motu_register_dsp_change.count = + (consumed - sizeof(event.motu_register_dsp_change)) / 4; + if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) + return -EFAULT; + + count = consumed; } - spin_unlock_irq(&motu->lock); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; - return count; } @@ -67,7 +101,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &motu->hwdep_wait, wait); spin_lock_irq(&motu->lock); - if (motu->dev_lock_changed || motu->msg) + if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) events = EPOLLIN | EPOLLRDNORM; else events = 0; @@ -155,6 +189,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, return hwdep_lock(motu); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return hwdep_unlock(motu); + case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER: + { + struct snd_firewire_motu_register_dsp_meter *meter; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) + return -ENXIO; + + meter = kzalloc(sizeof(*meter), GFP_KERNEL); + if (!meter) + return -ENOMEM; + + snd_motu_register_dsp_message_parser_copy_meter(motu, meter); + + err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); + kfree(meter); + + if (err) + return -EFAULT; + + return 0; + } + case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER: + { + struct snd_firewire_motu_command_dsp_meter *meter; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)) + return -ENXIO; + + meter = kzalloc(sizeof(*meter), GFP_KERNEL); + if (!meter) + return -ENOMEM; + + snd_motu_command_dsp_message_parser_copy_meter(motu, meter); + + err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); + kfree(meter); + + if (err) + return -EFAULT; + + return 0; + } + case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER: + { + struct snd_firewire_motu_register_dsp_parameter *param; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) + return -ENXIO; + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) + return -ENOMEM; + + snd_motu_register_dsp_message_parser_copy_parameter(motu, param); + + err = copy_to_user((void __user *)arg, param, sizeof(*param)); + kfree(param); + if (err) + return -EFAULT; + + return 0; + } default: return -ENOIOCTLCMD; } @@ -193,5 +292,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu) hwdep->private_data = motu; hwdep->exclusive = true; + motu->hwdep = hwdep; + return 0; } diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c index 2bd4485e4b..a5f70efa2e 100644 --- a/sound/firewire/motu/motu-protocol-v2.c +++ b/sound/firewire/motu/motu-protocol-v2.c @@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = { .name = "828mk2", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 0}, .rx_fixed_pcm_chunks = {14, 14, 0}, }; @@ -283,7 +284,7 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = { const struct snd_motu_spec snd_motu_spec_896hd = { .name = "896HD", .protocol_version = SND_MOTU_PROTOCOL_V2, - // No support for MIDI. + .flags = SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 8}, .rx_fixed_pcm_chunks = {14, 14, 8}, }; @@ -292,7 +293,8 @@ const struct snd_motu_spec snd_motu_spec_traveler = { .name = "Traveler", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 8}, .rx_fixed_pcm_chunks = {14, 14, 8}, }; @@ -301,7 +303,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite = { .name = "UltraLite", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 0}, .rx_fixed_pcm_chunks = {14, 14, 0}, }; @@ -310,7 +313,8 @@ const struct snd_motu_spec snd_motu_spec_8pre = { .name = "8pre", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, // Two dummy chunks always in the end of data block. .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {6, 6, 0}, diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index 56e4504e7e..8a0426920a 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -16,6 +16,7 @@ #define V3_CLOCK_SRC_INTERNAL 0x00 #define V3_CLOCK_SRC_WORD_ON_BNC 0x01 #define V3_CLOCK_SRC_SPH 0x02 +#define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08 #define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10 #define V3_CLOCK_SRC_OPT_IFACE_A 0x18 #define V3_CLOCK_SRC_OPT_IFACE_B 0x19 @@ -126,6 +127,9 @@ int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, case V3_CLOCK_SRC_SPH: *src = SND_MOTU_CLOCK_SOURCE_SPH; break; + case V3_CLOCK_SRC_AESEBU_ON_XLR: + *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR; + break; case V3_CLOCK_SRC_SPDIF_ON_COAX: *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; break; @@ -185,7 +189,7 @@ int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, sizeof(reg)); } -static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data) +static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data) { if (data & V3_ENABLE_OPT_IN_IFACE_A) { if (data & V3_NO_ADAT_OPT_IN_IFACE_A) { @@ -255,18 +259,21 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) motu->spec->rx_fixed_pcm_chunks, sizeof(motu->rx_packet_formats.pcm_chunks)); - if (motu->spec == &snd_motu_spec_828mk3_fw || motu->spec == &snd_motu_spec_828mk3_hybrid) - return detect_packet_formats_828mk3(motu, data); + if (motu->spec == &snd_motu_spec_828mk3_fw || + motu->spec == &snd_motu_spec_828mk3_hybrid || + motu->spec == &snd_motu_spec_traveler_mk3 || + motu->spec == &snd_motu_spec_track16) + return detect_packet_formats_with_opt_ifaces(motu, data); else return 0; } - const struct snd_motu_spec snd_motu_spec_828mk3_fw = { .name = "828mk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 18, 14}, .rx_fixed_pcm_chunks = {14, 14, 10}, }; @@ -275,16 +282,28 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { .name = "828mk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 18, 14}, .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. }; +const struct snd_motu_spec snd_motu_spec_traveler_mk3 = { + .name = "TravelerMk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {14, 14, 10}, +}; + const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = { .name = "UltraLiteMk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 14, 10}, .rx_fixed_pcm_chunks = {14, 14, 14}, }; @@ -293,14 +312,26 @@ const struct snd_motu_spec snd_motu_spec_audio_express = { .name = "AudioExpress", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {10, 10, 0}, }; +const struct snd_motu_spec snd_motu_spec_track16 = { + .name = "Track16", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, + .tx_fixed_pcm_chunks = {14, 14, 14}, + .rx_fixed_pcm_chunks = {6, 6, 6}, +}; + const struct snd_motu_spec snd_motu_spec_4pre = { .name = "4pre", .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {10, 10, 0}, }; diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 9e6ca39ebd..64aec9c3ee 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -255,6 +255,16 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) return err; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + err = snd_motu_register_dsp_message_parser_init(motu); + if (err < 0) + return err; + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc); + if (err < 0) + return err; + } + err = begin_session(motu); if (err < 0) { dev_err(&motu->unit->device, diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index f65426238d..f8b7fe3875 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -112,6 +112,16 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent if (err < 0) goto error; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + err = snd_motu_register_dsp_message_parser_new(motu); + if (err < 0) + goto error; + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + err = snd_motu_command_dsp_message_parser_new(motu); + if (err < 0) + goto error; + } + err = snd_card_register(card); if (err < 0) goto error; @@ -159,9 +169,11 @@ static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only. SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. + SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3), SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid. SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), + SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16), SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), { } }; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index f1a830b358..4189f21922 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -74,10 +74,13 @@ struct snd_motu { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + struct snd_hwdep *hwdep; struct amdtp_domain domain; struct amdtp_motu_cache cache; + + void *message_parser; }; enum snd_motu_spec_flags { @@ -85,6 +88,8 @@ enum snd_motu_spec_flags { SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002, SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004, SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008, + SND_MOTU_SPEC_REGISTER_DSP = 0x0010, + SND_MOTU_SPEC_COMMAND_DSP = 0x0020, }; #define SND_MOTU_CLOCK_RATE_COUNT 6 @@ -133,8 +138,10 @@ extern const struct snd_motu_spec snd_motu_spec_8pre; extern const struct snd_motu_spec snd_motu_spec_828mk3_fw; extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid; +extern const struct snd_motu_spec snd_motu_spec_traveler_mk3; extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; extern const struct snd_motu_spec snd_motu_spec_audio_express; +extern const struct snd_motu_spec snd_motu_spec_track16; extern const struct snd_motu_spec snd_motu_spec_4pre; int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -270,4 +277,22 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) return -ENXIO; } +int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu); +int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu); +void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_meter *meter); +void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_parameter *params); +unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu); +bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event); + +int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu); +int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc); +void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_command_dsp_meter *meter); + #endif diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 37154ed43b..d2b5724b46 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -18,7 +18,7 @@ /** * snd_hdac_ext_stream_init - initialize each stream (aka device) * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @idx: stream index number * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) * @tag: the tag id to assign @@ -27,34 +27,34 @@ * invoke hdac stream initialization routine */ void snd_hdac_ext_stream_init(struct hdac_bus *bus, - struct hdac_ext_stream *stream, - int idx, int direction, int tag) + struct hdac_ext_stream *hext_stream, + int idx, int direction, int tag) { if (bus->ppcap) { - stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + + hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + AZX_PPHC_INTERVAL * idx; - stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + + hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + AZX_PPLC_MULTI * bus->num_streams + AZX_PPLC_INTERVAL * idx; } if (bus->spbcap) { - stream->spib_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_SPIB; - stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + + hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + AZX_SPB_INTERVAL * idx + AZX_SPB_MAXFIFO; } if (bus->drsmcap) - stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + + hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + AZX_DRSM_INTERVAL * idx; - stream->decoupled = false; - snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); + hext_stream->decoupled = false; + snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); @@ -67,18 +67,18 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); * @dir: direction of streams */ int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, - int num_stream, int dir) + int num_stream, int dir) { int stream_tag = 0; int i, tag, idx = start_idx; for (i = 0; i < num_stream; i++) { - struct hdac_ext_stream *stream = - kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) + struct hdac_ext_stream *hext_stream = + kzalloc(sizeof(*hext_stream), GFP_KERNEL); + if (!hext_stream) return -ENOMEM; tag = ++stream_tag; - snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); + snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); idx++; } @@ -95,22 +95,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); void snd_hdac_stream_free_all(struct hdac_bus *bus) { struct hdac_stream *s, *_s; - struct hdac_ext_stream *stream; + struct hdac_ext_stream *hext_stream; list_for_each_entry_safe(s, _s, &bus->stream_list, list) { - stream = stream_to_hdac_ext_stream(s); - snd_hdac_ext_stream_decouple(bus, stream, false); + hext_stream = stream_to_hdac_ext_stream(s); + snd_hdac_ext_stream_decouple(bus, hext_stream, false); list_del(&s->list); - kfree(stream); + kfree(hext_stream); } } EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, - struct hdac_ext_stream *stream, + struct hdac_ext_stream *hext_stream, bool decouple) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); @@ -121,76 +121,76 @@ void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, else if (!decouple && val) snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); - stream->decoupled = decouple; + hext_stream->decoupled = decouple; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); /** * snd_hdac_ext_stream_decouple - decouple the hdac stream * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize + * @hext_stream: HD-audio ext core stream object to initialize * @decouple: flag to decouple */ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, - struct hdac_ext_stream *stream, bool decouple) + struct hdac_ext_stream *hext_stream, bool decouple) { spin_lock_irq(&bus->reg_lock); - snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); spin_unlock_irq(&bus->reg_lock); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); /** * snd_hdac_ext_link_stream_start - start a stream - * @stream: HD-audio ext core stream to start + * @hext_stream: HD-audio ext core stream to start */ -void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); /** * snd_hdac_ext_link_stream_clear - stop a stream DMA - * @stream: HD-audio ext core stream to stop + * @hext_stream: HD-audio ext core stream to stop */ -void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream) { - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); /** * snd_hdac_ext_link_stream_reset - reset a stream - * @stream: HD-audio ext core stream to reset + * @hext_stream: HD-audio ext core stream to reset */ -void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) +void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream) { unsigned char val; int timeout; - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); - snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, + snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); udelay(3); timeout = 50; do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (val) break; udelay(3); } while (--timeout); val &= ~AZX_PPLCCTL_STRST; - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); udelay(3); timeout = 50; /* waiting for hardware to report that the stream is out of reset */ do { - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; if (!val) break; udelay(3); @@ -201,24 +201,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); /** * snd_hdac_ext_link_stream_setup - set up the SD for streaming - * @stream: HD-audio ext core stream to set up + * @hext_stream: HD-audio ext core stream to set up * @fmt: stream format */ -int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) +int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *hext_stream, int fmt) { - struct hdac_stream *hstream = &stream->hstream; + struct hdac_stream *hstream = &hext_stream->hstream; unsigned int val; /* make sure the run bit is zero for SD */ - snd_hdac_ext_link_stream_clear(stream); + snd_hdac_ext_link_stream_clear(hext_stream); /* program the stream_tag */ - val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); + val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); val = (val & ~AZX_PPLCCTL_STRM_MASK) | (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); - writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); + writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); /* program the stream format */ - writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); + writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT); return 0; } @@ -230,7 +230,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); * @stream: stream id */ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, - int stream) + int stream) { snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream); } @@ -250,10 +250,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); static struct hdac_ext_stream * hdac_ext_link_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -261,22 +261,22 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; /* check if decoupled stream and not in use is available */ - if (hstream->decoupled && !hstream->link_locked) { - res = hstream; + if (hext_stream->decoupled && !hext_stream->link_locked) { + res = hext_stream; break; } - if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hext_stream->link_locked) { + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -290,10 +290,10 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, static struct hdac_ext_stream * hdac_ext_host_stream_assign(struct hdac_bus *bus, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream) { struct hdac_ext_stream *res = NULL; - struct hdac_stream *stream = NULL; + struct hdac_stream *hstream = NULL; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); @@ -301,17 +301,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus, } spin_lock_irq(&bus->reg_lock); - list_for_each_entry(stream, &bus->stream_list, list) { - struct hdac_ext_stream *hstream = container_of(stream, - struct hdac_ext_stream, - hstream); - if (stream->direction != substream->stream) + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + if (hstream->direction != substream->stream) continue; - if (!stream->opened) { - if (!hstream->decoupled) - snd_hdac_ext_stream_decouple_locked(bus, hstream, true); - res = hstream; + if (!hstream->opened) { + if (!hext_stream->decoupled) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); + res = hext_stream; break; } } @@ -346,16 +346,17 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type) { - struct hdac_ext_stream *hstream = NULL; - struct hdac_stream *stream = NULL; + struct hdac_ext_stream *hext_stream = NULL; + struct hdac_stream *hstream = NULL; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - stream = snd_hdac_stream_assign(bus, substream); - if (stream) - hstream = container_of(stream, - struct hdac_ext_stream, hstream); - return hstream; + hstream = snd_hdac_stream_assign(bus, substream); + if (hstream) + hext_stream = container_of(hstream, + struct hdac_ext_stream, + hstream); + return hext_stream; case HDAC_EXT_STREAM_TYPE_HOST: return hdac_ext_host_stream_assign(bus, substream); @@ -371,34 +372,34 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); /** * snd_hdac_ext_stream_release - release the assigned stream - * @stream: HD-audio ext core stream to release + * @hext_stream: HD-audio ext core stream to release * @type: type of stream (coupled, host or link stream) * * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). */ -void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) +void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) { - struct hdac_bus *bus = stream->hstream.bus; + struct hdac_bus *bus = hext_stream->hstream.bus; switch (type) { case HDAC_EXT_STREAM_TYPE_COUPLED: - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_HOST: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); + if (hext_stream->decoupled && !hext_stream->link_locked) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); spin_unlock_irq(&bus->reg_lock); - snd_hdac_stream_release(&stream->hstream); + snd_hdac_stream_release(&hext_stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: spin_lock_irq(&bus->reg_lock); - if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple_locked(bus, stream, false); - stream->link_locked = 0; - stream->link_substream = NULL; + if (hext_stream->decoupled && !hext_stream->hstream.opened) + snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); + hext_stream->link_locked = 0; + hext_stream->link_substream = NULL; spin_unlock_irq(&bus->reg_lock); break; @@ -437,11 +438,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); /** * snd_hdac_ext_stream_set_spib - sets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: spib value to set */ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->spbcap) { @@ -449,7 +450,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->spib_addr); + writel(value, hext_stream->spib_addr); return 0; } @@ -458,12 +459,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); /** * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * * Return maxfifo for the stream */ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, - struct hdac_ext_stream *stream) + struct hdac_ext_stream *hext_stream) { if (!bus->spbcap) { @@ -471,27 +472,10 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, return -EINVAL; } - return readl(stream->fifo_addr); + return readl(hext_stream->fifo_addr); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); - -/** - * snd_hdac_ext_stop_streams - stop all stream if running - * @bus: HD-audio core bus - */ -void snd_hdac_ext_stop_streams(struct hdac_bus *bus) -{ - struct hdac_stream *stream; - - if (bus->chip_init) { - list_for_each_entry(stream, &bus->stream_list, list) - snd_hdac_stream_stop(stream); - snd_hdac_bus_stop_chip(bus); - } -} -EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); - /** * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream * @bus: HD-audio core bus @@ -520,11 +504,11 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); /** * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream * @bus: HD-audio core bus - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: dpib value to set */ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, - struct hdac_ext_stream *stream, u32 value) + struct hdac_ext_stream *hext_stream, u32 value) { if (!bus->drsmcap) { @@ -532,7 +516,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, return -EINVAL; } - writel(value, stream->dpibr_addr); + writel(value, hext_stream->dpibr_addr); return 0; } @@ -540,12 +524,12 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); /** * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream - * @stream: hdac_ext_stream + * @hext_stream: hdac_ext_stream * @value: lpib value to set */ -int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) +int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value) { - snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); + snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value); return 0; } diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index aa7955fdf6..f3582012d2 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -142,6 +142,22 @@ void snd_hdac_stream_stop(struct hdac_stream *azx_dev) } EXPORT_SYMBOL_GPL(snd_hdac_stream_stop); +/** + * snd_hdac_stop_streams_and_chip - stop all streams and chip if running + * @bus: HD-audio core bus + */ +void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus) +{ + struct hdac_stream *stream; + + if (bus->chip_init) { + list_for_each_entry(stream, &bus->stream_list, list) + snd_hdac_stream_stop(stream); + snd_hdac_bus_stop_chip(bus); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip); + /** * snd_hdac_stream_reset - reset a stream * @azx_dev: HD-audio core stream to reset diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 4208fa8a4d..4fb90ceb40 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -248,15 +248,15 @@ static const struct config_entry config_table[] = { {} } }, - { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x02c8, - }, { .flags = FLAG_SOF, .device = 0x02c8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x02c8, + }, /* Cometlake-H */ { .flags = FLAG_SOF, @@ -278,14 +278,14 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, - .device = 0x06c8, - }, - { .flags = FLAG_SOF, .device = 0x06c8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x06c8, + }, #endif /* Icelake */ @@ -309,13 +309,30 @@ static const struct config_entry config_table[] = { }, #endif -/* JasperLake */ +/* Jasper Lake */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) + { + .flags = FLAG_SOF, + .device = 0x4dc8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, { .flags = FLAG_SOF, .device = 0x4dc8, .codec_hid = "ESSX8336", }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x4dc8, + }, #endif /* Tigerlake */ @@ -333,6 +350,11 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SOF, + .device = 0xa0c8, + .codec_hid = "ESSX8336", + }, { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0xa0c8, @@ -341,11 +363,6 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x43c8, }, - { - .flags = FLAG_SOF, - .device = 0xa0c8, - .codec_hid = "ESSX8336", - }, #endif /* Elkhart Lake */ @@ -374,6 +391,14 @@ static const struct config_entry config_table[] = { .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x51cc, }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x51cd, + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, + .device = 0x54c8, + }, #endif }; @@ -403,7 +428,7 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci) nhlt = intel_nhlt_init(&pci->dev); if (nhlt) { - if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) + if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_DMIC)) ret = 1; intel_nhlt_free(nhlt); } diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index e2237239d9..128476aa7c 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -110,3 +110,105 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt) return dmic_geo; } EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo); + +bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type) +{ + struct nhlt_endpoint *epnt; + int i; + + if (!nhlt) + return false; + + epnt = (struct nhlt_endpoint *)nhlt->desc; + for (i = 0; i < nhlt->endpoint_count; i++) { + if (epnt->linktype == link_type) + return true; + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + return false; +} +EXPORT_SYMBOL(intel_nhlt_has_endpoint_type); + +static struct nhlt_specific_cfg * +nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch, + u32 rate, u8 vbps, u8 bps) +{ + struct nhlt_fmt_cfg *cfg = fmt->fmt_config; + struct wav_fmt *wfmt; + u16 _bps, _vbps; + int i; + + dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count); + + for (i = 0; i < fmt->fmt_count; i++) { + wfmt = &cfg->fmt_ext.fmt; + _bps = wfmt->bits_per_sample; + _vbps = cfg->fmt_ext.sample.valid_bits_per_sample; + + dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n", + wfmt->channels, _vbps, _bps, wfmt->samples_per_sec); + + if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate && + vbps == _vbps && bps == _bps) + return &cfg->config; + + cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size); + } + + return NULL; +} + +static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, + u32 bus_id, u8 link_type, u8 dir, u8 dev_type) +{ + dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n", + epnt->virtual_bus_id, epnt->linktype, + epnt->direction, epnt->device_type); + + if ((epnt->virtual_bus_id != bus_id) || + (epnt->linktype != link_type) || + (epnt->direction != dir)) + return false; + + /* link of type DMIC bypasses device_type check */ + return epnt->linktype == NHLT_LINK_DMIC || + epnt->device_type == dev_type; +} + +struct nhlt_specific_cfg * +intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt, + u32 bus_id, u8 link_type, u8 vbps, u8 bps, + u8 num_ch, u32 rate, u8 dir, u8 dev_type) +{ + struct nhlt_specific_cfg *cfg; + struct nhlt_endpoint *epnt; + struct nhlt_fmt *fmt; + int i; + + if (!nhlt) + return NULL; + + dev_dbg(dev, "Looking for configuration:\n"); + dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n", + bus_id, link_type, dir, dev_type); + dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate); + dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count); + + epnt = (struct nhlt_endpoint *)nhlt->desc; + + for (i = 0; i < nhlt->endpoint_count; i++) { + if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) { + fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size); + + cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate, vbps, bps); + if (cfg) + return cfg; + } + + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } + + return NULL; +} +EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob); diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index b7758dbe23..5cb92f7ccb 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -50,11 +50,11 @@ static bool is_link_enabled(struct fwnode_handle *fw_node, int i) static int sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) { - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); int ret, i; u8 count; - if (acpi_bus_get_device(info->handle, &adev)) + if (!adev) return -EINVAL; /* Found controller, find links supported */ @@ -119,7 +119,6 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { struct sdw_intel_acpi_info *info = cdata; - struct acpi_device *adev; acpi_status status; u64 adr; @@ -127,7 +126,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, if (ACPI_FAILURE(status)) return AE_OK; /* keep going */ - if (acpi_bus_get_device(handle, &adev)) { + if (!acpi_fetch_acpi_dev(handle)) { pr_err("%s: Couldn't find ACPI handle\n", __func__); return AE_NOT_FOUND; } diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index ff9480f249..3e56c01c45 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -24,8 +24,9 @@ void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup) } } -static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, - struct snd_gf1_mem_block * block) +static struct snd_gf1_mem_block * +snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block, + const char *name) { struct snd_gf1_mem_block *pblock, *nblock; @@ -33,6 +34,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, if (nblock == NULL) return NULL; *nblock = *block; + nblock->name = kstrdup(name, GFP_KERNEL); + if (!nblock->name) { + kfree(nblock); + return NULL; + } + pblock = alloc->first; while (pblock) { if (pblock->ptr > nblock->ptr) { @@ -44,7 +51,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc, else nblock->prev->next = nblock; mutex_unlock(&alloc->memory_mutex); - return NULL; + return nblock; } pblock = pblock->next; } @@ -198,8 +205,7 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne if (share_id != NULL) memcpy(&block.share_id, share_id, sizeof(block.share_id)); block.owner = owner; - block.name = kstrdup(name, GFP_KERNEL); - nblock = snd_gf1_mem_xalloc(alloc, &block); + nblock = snd_gf1_mem_xalloc(alloc, &block, name); snd_gf1_mem_lock(alloc, 1); return nblock; } @@ -236,14 +242,12 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) if (gus->gf1.enh_mode) { block.ptr = 0; block.size = 1024; - block.name = kstrdup("InterWave LFOs", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs")) return -ENOMEM; } block.ptr = gus->gf1.default_voice_address; block.size = 4; - block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL); - if (snd_gf1_mem_xalloc(alloc, &block) == NULL) + if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)")) return -ENOMEM; #ifdef CONFIG_SND_DEBUG snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read); diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index ea20236f35..dab801d9d3 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -298,7 +298,6 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_MICGAINZ 0x01 /* mic boost */ #define CM_MICGAINZ_SHIFT 0 -#define CM_REG_MIXER3 0x24 #define CM_REG_AUX_VOL 0x26 #define CM_VAUXL_MASK 0xf0 #define CM_VAUXR_MASK 0x0f @@ -3218,7 +3217,6 @@ static int snd_cmipci_probe(struct pci_dev *pci, { static int dev; struct snd_card *card; - struct cmipci *cm; int err; if (dev >= SNDRV_CARDS) @@ -3229,10 +3227,9 @@ static int snd_cmipci_probe(struct pci_dev *pci, } err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, - sizeof(*cm), &card); + sizeof(struct cmipci), &card); if (err < 0) return err; - cm = card->private_data; switch (pci->device) { case PCI_DEVICE_ID_CMEDIA_CM8738: @@ -3267,7 +3264,7 @@ static int snd_cmipci_probe(struct pci_dev *pci, */ static const unsigned char saved_regs[] = { CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL, - CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL, + CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL, CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2, CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC, CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0, diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index ab9d2746e8..febe1c2b7d 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -91,6 +91,39 @@ config SND_HDA_PATCH_LOADER start up. The "patch" file can be specified via patch module option, such as patch=hda-init. +config SND_HDA_SCODEC_CS35L41 + tristate + +config SND_HDA_SCODEC_CS35L41_I2C + tristate "Build CS35L41 HD-audio side codec support for I2C Bus" + depends on I2C + depends on ACPI + depends on SND_SOC + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 I2C HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m + +config SND_HDA_SCODEC_CS35L41_SPI + tristate "Build CS35L41 HD-audio codec support for SPI Bus" + depends on SPI_MASTER + depends on ACPI + depends on SND_SOC + select SND_HDA_GENERIC + select SND_SOC_CS35L41_LIB + select SND_HDA_SCODEC_CS35L41 + help + Say Y or M here to include CS35L41 SPI HD-audio side codec support + in snd-hda-intel driver, such as ALC287. + +comment "Set to Y if you want auto-loading the side codec driver" + depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m + config SND_HDA_CODEC_REALTEK tristate "Build Realtek HD-audio codec support" select SND_HDA_GENERIC diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index b8fa682ce6..3e7bc608d4 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -27,6 +27,11 @@ snd-hda-codec-conexant-objs := patch_conexant.o snd-hda-codec-via-objs := patch_via.o snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o +# side codecs +snd-hda-scodec-cs35l41-objs := cs35l41_hda.o +snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o +snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o + # common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -45,6 +50,11 @@ obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o +# side codecs +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o +obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o + # this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe # when built in kernel diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 500d0d474d..cd1db943b7 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -92,14 +92,10 @@ static int compare_input_type(const void *ap, const void *bp) */ static void reorder_outputs(unsigned int nums, hda_nid_t *pins) { - hda_nid_t nid; - switch (nums) { case 3: case 4: - nid = pins[1]; - pins[1] = pins[2]; - pins[2] = nid; + swap(pins[1], pins[2]); break; } } diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index 7153bd53e1..c572fb5886 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -14,6 +14,7 @@ #include #include #include "hda_local.h" +#include "hda_jack.h" /* * find a matching codec id @@ -158,6 +159,7 @@ static int hda_codec_driver_remove(struct device *dev) refcount_dec(&codec->pcm_ref); snd_hda_codec_disconnect_pcms(codec); + snd_hda_jack_tbl_disconnect(codec); wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref)); snd_power_sync_ref(codec->bus->card); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 362ddcaea1..34eba40cc6 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -183,7 +183,7 @@ struct hda_gen_spec { struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; /* for pin sensing */ - /* current status; set in hda_geneic.c */ + /* current status; set in hda_generic.c */ unsigned int hp_jack_present:1; unsigned int line_jack_present:1; unsigned int speaker_muted:1; /* current status of speaker mute */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9e36f99260..572ff0d1fa 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1350,8 +1350,12 @@ static void azx_free(struct azx *chip) if (hda->freed) return; - if (azx_has_pm_runtime(chip) && chip->running) + if (azx_has_pm_runtime(chip) && chip->running) { pm_runtime_get_noresume(&pci->dev); + pm_runtime_forbid(&pci->dev); + pm_runtime_dont_use_autosuspend(&pci->dev); + } + chip->running = 0; azx_del_card_list(chip); @@ -1937,6 +1941,7 @@ static int azx_first_init(struct azx *chip) dma_bits = 32; if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits))) dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(&pci->dev, UINT_MAX); /* read number of streams from GCAP register instead of using * hardcoded value @@ -2369,6 +2374,7 @@ static void azx_remove(struct pci_dev *pci) cancel_delayed_work_sync(&hda->probe_work); device_lock(&pci->dev); + pci_set_drvdata(pci, NULL); snd_card_free(card); } } @@ -2489,9 +2495,14 @@ static const struct pci_device_id azx_ids[] = { /* Alderlake-P */ { PCI_DEVICE(0x8086, 0x51c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x51cd), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Alderlake-M */ { PCI_DEVICE(0x8086, 0x51cc), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Alderlake-N */ + { PCI_DEVICE(0x8086, 0x54c8), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index f29975e3e9..7d7786df60 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -158,6 +158,17 @@ snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id) return jack; } +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec) +{ + struct hda_jack_tbl *jack = codec->jacktbl.list; + int i; + + for (i = 0; i < codec->jacktbl.used; i++, jack++) { + if (!codec->bus->shutdown && jack->jack) + snd_device_disconnect(codec->card, jack->jack); + } +} + void snd_hda_jack_tbl_clear(struct hda_codec *codec) { struct hda_jack_tbl *jack = codec->jacktbl.list; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 2abf7aac24..ff7d289c03 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -69,6 +69,7 @@ struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag, int dev_id); +void snd_hda_jack_tbl_disconnect(struct hda_codec *codec); void snd_hda_jack_tbl_clear(struct hda_codec *codec); void snd_hda_jack_set_dirty_all(struct hda_codec *codec); diff --git a/sound/pci/hda/patch_cs8409-tables.c b/sound/pci/hda/patch_cs8409-tables.c index df0b4522ba..2d1fa70632 100644 --- a/sound/pci/hda/patch_cs8409-tables.c +++ b/sound/pci/hda/patch_cs8409-tables.c @@ -490,6 +490,8 @@ const struct snd_pci_quirk cs8409_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK), SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK), SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), + SND_PCI_QUIRK(0x1028, 0x0BB6, "Warlock V3 15 TGL-U Nuvoton EC", CS8409_WARLOCK), SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG), SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG), SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ffcde7409d..92df4f243e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1535,7 +1535,7 @@ static void update_eld(struct hda_codec *codec, } } - if (!eld->eld_valid || eld->eld_size <= 0) { + if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) { eld->eld_valid = false; eld->eld_size = 0; } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 83b56c1ba3..75ff7e8498 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -25,6 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_generic.h" +#include "hda_component.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 @@ -127,6 +128,10 @@ struct alc_spec { unsigned int coef0; struct input_dev *kb_dev; u8 alc_mute_keycode_map[1]; + + /* component binding */ + struct component_match *match; + struct hda_component comps[HDA_MAX_COMPONENTS]; }; /* @@ -6576,6 +6581,133 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec, } } +static int comp_match_dev_name(struct device *dev, void *data) +{ + return strcmp(dev_name(dev), data) == 0; +} + +static int find_comp_by_dev_name(struct alc_spec *spec, const char *name) +{ + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (strcmp(spec->comps[i].name, name) == 0) + return i; + } + + return -ENODEV; +} + +static int comp_bind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + return component_bind_all(dev, spec->comps); +} + +static void comp_unbind(struct device *dev) +{ + struct hda_codec *cdc = dev_to_hda_codec(dev); + struct alc_spec *spec = cdc->spec; + + component_unbind_all(dev, spec->comps); +} + +static const struct component_master_ops comp_master_ops = { + .bind = comp_bind, + .unbind = comp_unbind, +}; + +static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + int i; + + for (i = 0; i < HDA_MAX_COMPONENTS; i++) { + if (spec->comps[i].dev) + spec->comps[i].playback_hook(spec->comps[i].dev, action); + } +} + +static void cs35l41_generic_fixup(struct hda_codec *cdc, int action, const char *bus, + const char *hid, int count) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + char *name; + int ret, i; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + for (i = 0; i < count; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, + "%s-%s:00-cs35l41-hda.%d", bus, hid, i); + if (!name) + return; + component_match_add(dev, &spec->match, comp_match_dev_name, name); + } + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = comp_generic_playback_hook; + break; + } +} + +static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action) +{ + cs35l41_generic_fixup(cdc, action, "i2c", "CSC3551", 2); +} + +static void alc287_legion_16achg6_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc, + struct snd_pcm_substream *sub, int action) +{ + struct alc_spec *spec = cdc->spec; + unsigned int rx_slot; + int i; + + switch (action) { + case HDA_GEN_PCM_ACT_PREPARE: + rx_slot = 0; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.0"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + + rx_slot = 1; + i = find_comp_by_dev_name(spec, "i2c-CLSA0100:00-cs35l41-hda.1"); + if (i >= 0) + spec->comps[i].set_channel_map(spec->comps[i].dev, 0, NULL, 1, &rx_slot); + break; + } + + comp_generic_playback_hook(hinfo, cdc, sub, action); +} + +static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix, + int action) +{ + struct device *dev = hda_codec_dev(cdc); + struct alc_spec *spec = cdc->spec; + int ret; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.0"); + component_match_add(dev, &spec->match, comp_match_dev_name, + "i2c-CLSA0100:00-cs35l41-hda.1"); + ret = component_master_add_with_match(dev, &comp_master_ops, spec->match); + if (ret) + codec_err(cdc, "Fail to register component aggregator %d\n", ret); + else + spec->gen.pcm_playback_hook = alc287_legion_16achg6_playback_hook; + break; + } +} + /* for alc295_fixup_hp_top_speakers */ #include "hp_x360_helper.c" @@ -6865,6 +6997,9 @@ enum { ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME, ALC285_FIXUP_LEGION_Y9000X_SPEAKERS, ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE, + ALC287_FIXUP_LEGION_16ACHG6, + ALC287_FIXUP_CS35L41_I2C_2, + ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED, }; static const struct hda_fixup alc269_fixups[] = { @@ -8607,6 +8742,24 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, + [ALC287_FIXUP_LEGION_16ACHG6] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_legion_16achg6_speakers, + }, + [ALC287_FIXUP_CS35L41_I2C_2] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs35l41_fixup_i2c_two, + }, + [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 }, + { } + }, + .chained = true, + .chain_id = ALC285_FIXUP_HP_MUTE_LED, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -8820,6 +8973,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED), + 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), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), @@ -8866,6 +9020,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401), + SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), @@ -8949,6 +9104,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC), SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME), SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -9005,6 +9162,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), @@ -9024,6 +9184,7 @@ static const struct snd_pci_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, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP), + SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6), SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS), 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), @@ -10909,6 +11070,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), + SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index fb8895af03..a047ed0f84 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -23,8 +23,6 @@ #define MSG_DESCRIPTOR_SIZE 0x24 #define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4) -#define MSG_DEFAULT_SIZE 512 - #define MSG_TYPE_MASK 0x00000003 /* mask for following types */ #define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ #define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ @@ -444,6 +442,9 @@ irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id) struct mixart_timer_notify *notify; notify = (struct mixart_timer_notify *)mixart_msg_data; + BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data)); + if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams))) + break; for(i=0; istream_count; i++) { u32 buffer_id = notify->streams[i].buffer_id; diff --git a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h index fbf4731a27..2f0e29ed5d 100644 --- a/sound/pci/mixart/mixart_core.h +++ b/sound/pci/mixart/mixart_core.h @@ -49,6 +49,7 @@ enum mixart_message_id { MSG_CLOCK_SET_PROPERTIES = 0x200002, }; +#define MSG_DEFAULT_SIZE 512 struct mixart_msg { @@ -251,10 +252,17 @@ struct mixart_sample_pos u32 sample_pos_low_part; } __attribute__((packed)); +/* + * This structure is limited by the size of MSG_DEFAULT_SIZE. Instead of + * having MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS many streams, + * this is capped to have a total size below MSG_DEFAULT_SIZE. + */ +#define MIXART_MAX_TIMER_NOTIFY_STREAMS \ + ((MSG_DEFAULT_SIZE - sizeof(u32)) / sizeof(struct mixart_sample_pos)) struct mixart_timer_notify { u32 stream_count; - struct mixart_sample_pos streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS]; + struct mixart_sample_pos streams[MIXART_MAX_TIMER_NOTIFY_STREAMS]; } __attribute__((packed)); diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 0f4bce1c0d..bf289783ea 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -99,7 +99,7 @@ static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, return -1; switch (code) { - case SND_BELL: if (hz) hz = 1000; + case SND_BELL: if (hz) hz = 1000; break; case SND_TONE: break; default: return -1; } diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 49ff5e73e9..7a9e45094f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -6,6 +6,7 @@ config SND_SOC_AMD_ACP config SND_SOC_AMD_CZ_DA7219MX98357_MACH tristate "AMD CZ support for DA7219, RT5682 and MAX9835" + select CLK_FIXED_FCH select SND_SOC_DA7219 select SND_SOC_RT5682_I2C select SND_SOC_MAX98357A @@ -30,13 +31,14 @@ config SND_SOC_AMD_ACP3x config SND_SOC_AMD_RV_RT5682_MACH tristate "AMD RV support for RT5682" + select CLK_FIXED_FCH select SND_SOC_RT5682_I2C select SND_SOC_MAX98357A select SND_SOC_CROS_EC_CODEC select I2C_CROS_EC_TUNNEL select SND_SOC_RT1015 select SND_SOC_RT1015P - depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC + depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC && GPIOLIB help This option enables machine driver for RT5682 and MAX9835. @@ -49,7 +51,7 @@ config SND_SOC_AMD_RENOIR config SND_SOC_AMD_RENOIR_MACH tristate "AMD Renoir support for DMIC" select SND_SOC_DMIC - depends on SND_SOC_AMD_RENOIR + depends on SND_SOC_AMD_RENOIR && GPIOLIB help This option enables machine driver for DMIC @@ -61,3 +63,44 @@ config SND_SOC_AMD_ACP5x By enabling this flag build will trigger for ACP PCI driver, ACP DMA driver, CPU DAI driver. + +config SND_SOC_AMD_VANGOGH_MACH + tristate "AMD Vangogh support for NAU8821 CS35L41" + select SND_SOC_NAU8821 + select SND_SOC_CS35L41_SPI + depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER + help + This option enables machine driver for Vangogh platform + using NAU8821 and CS35L41 codecs. + Say m if you have such a device. + If unsure select "N". + +config SND_SOC_AMD_ACP6x + tristate "AMD Audio Coprocessor-v6.x Yellow Carp support" + depends on X86 && PCI + help + This option enables Audio Coprocessor i.e ACP v6.x support on + AMD Yellow Carp platform. By enabling this flag build will be + triggered for ACP PCI driver, ACP PDM DMA driver. + Say m if you have such a device. + If unsure select "N". + +config SND_SOC_AMD_YC_MACH + tristate "AMD YC support for DMIC" + select SND_SOC_DMIC + depends on SND_SOC_AMD_ACP6x + help + This option enables machine driver for Yellow Carp platform + using dmic. ACP IP has PDM Decoder block with DMA controller. + DMIC can be connected directly to ACP IP. + Say m if you have such a device. + If unsure select "N". + +config SND_AMD_ACP_CONFIG + tristate "AMD ACP configuration selection" + select SND_SOC_ACPI if ACPI + help + This option adds an auto detection to determine which ACP + driver modules to use + +source "sound/soc/amd/acp/Kconfig" diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 07150d26f3..4b1f77930a 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -3,6 +3,7 @@ acp_audio_dma-objs := acp-pcm-dma.o snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o snd-soc-acp-rt5645-mach-objs := acp-rt5645.o snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o +snd-acp-config-objs := acp-config.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o @@ -11,3 +12,6 @@ obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/ obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/ obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/ +obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/ +obj-$(CONFIG_SND_SOC_AMD_ACP_COMMON) += acp/ +obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index b3df98a9f9..3bf86c2424 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -33,7 +33,7 @@ static struct clk *da7219_dai_wclk; static struct clk *da7219_dai_bclk; static struct clk *rt5682_dai_wclk; static struct clk *rt5682_dai_bclk; -extern bool bt_uart_enable; + void *acp_soc_is_rltk_max(struct device *dev); static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) @@ -522,7 +522,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_da7219_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -533,7 +533,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-da7219-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_da7219_cap_ops, @@ -543,7 +543,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_max_play_ops, @@ -554,7 +554,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic0_cap_ops, @@ -565,7 +565,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_dmic1_cap_ops, @@ -578,7 +578,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-play", .stream_name = "Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_rt5682_init, .dpcm_playback = 1, .stop_dma_first = 1, @@ -589,7 +589,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-rt5682-cap", .stream_name = "Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_cap_ops, @@ -599,7 +599,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "amd-max98357-play", .stream_name = "HiFi Playback", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_playback = 1, .stop_dma_first = 1, .ops = &cz_rt5682_max_play_ops, @@ -610,7 +610,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic0", .stream_name = "DMIC0 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic0_cap_ops, @@ -621,7 +621,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { .name = "dmic1", .stream_name = "DMIC1 Capture", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .dpcm_capture = 1, .stop_dma_first = 1, .ops = &cz_rt5682_dmic1_cap_ops, @@ -760,8 +760,8 @@ static int cz_probe(struct platform_device *pdev) "devm_snd_soc_register_card(%s) failed\n", card->name); } - bt_uart_enable = !device_property_read_bool(&pdev->dev, - "bt-pad-enable"); + acp_bt_uart_enable = !device_property_read_bool(&pdev->dev, + "bt-pad-enable"); return 0; } diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 11b3c4f39e..8fa2e2fde4 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -36,8 +36,8 @@ #define ST_MIN_BUFFER ST_MAX_BUFFER #define DRV_NAME "acp_audio_dma" -bool bt_uart_enable = true; -EXPORT_SYMBOL(bt_uart_enable); +bool acp_bt_uart_enable = true; +EXPORT_SYMBOL(acp_bt_uart_enable); static const struct snd_pcm_hardware acp_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | @@ -596,7 +596,7 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type) acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET); /* For BT instance change pins from UART to BT */ - if (!bt_uart_enable) { + if (!acp_bt_uart_enable) { val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL); val |= ACP_BT_UART_PAD_SELECT_MASK; acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL); @@ -1003,6 +1003,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; + struct audio_drv_data *adata = dev_get_drvdata(component->dev); if (!rtd) return -EINVAL; @@ -1023,7 +1024,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, } if (bytescount > 0) { delay = do_div(bytescount, period_bytes); - runtime->delay = bytes_to_frames(runtime, delay); + adata->delay += bytes_to_frames(runtime, delay); } } else { buffersize = frames_to_bytes(runtime, runtime->buffer_size); @@ -1035,6 +1036,17 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component, return bytes_to_frames(runtime, pos); } +static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct audio_drv_data *adata = dev_get_drvdata(component->dev); + snd_pcm_sframes_t delay = adata->delay; + + adata->delay = 0; + + return delay; +} + static int acp_dma_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -1198,6 +1210,7 @@ static const struct snd_soc_component_driver acp_asoc_platform = { .hw_params = acp_dma_hw_params, .trigger = acp_dma_trigger, .pointer = acp_dma_pointer, + .delay = acp_dma_delay, .prepare = acp_dma_prepare, .pcm_construct = acp_dma_new, }; diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index d6ba94677a..a79a46646d 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -91,7 +91,7 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static struct snd_soc_ops cz_aif1_ops = { +static const struct snd_soc_ops cz_aif1_ops = { .hw_params = cz_aif1_hw_params, }; @@ -111,7 +111,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-play", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .init = cz_init, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware1, codec, platform), @@ -120,7 +120,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = { .name = "amd-rt5645-cap", .stream_name = "RT5645_AIF1", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM, + | SND_SOC_DAIFMT_CBP_CFP, .ops = &cz_aif1_ops, SND_SOC_DAILINK_REG(designware2, codec, platform), }, diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index e5ab6c6040..db80a73aa5 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -151,6 +151,7 @@ struct audio_drv_data { struct snd_pcm_substream *capture_i2sbt_stream; void __iomem *acp_mmio; u32 asic_type; + snd_pcm_sframes_t delay; }; /* @@ -204,4 +205,6 @@ typedef struct acp_dma_dscr_transfer { u32 reserved; } acp_dma_dscr_transfer_t; +extern bool acp_bt_uart_enable; + #endif /*__ACP_HW_H */ diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile index 3353f93dc6..c9e53e04e2 100644 --- a/sound/soc/amd/vangogh/Makefile +++ b/sound/soc/amd/vangogh/Makefile @@ -3,7 +3,9 @@ snd-pci-acp5x-objs := pci-acp5x.o snd-acp5x-i2s-objs := acp5x-i2s.o snd-acp5x-pcm-dma-objs := acp5x-pcm-dma.o +snd-soc-acp5x-mach-objs := acp5x-mach.o obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o +obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c index 2705e57eb7..002db3971c 100644 --- a/sound/soc/amd/vangogh/acp5x-i2s.c +++ b/sound/soc/amd/vangogh/acp5x-i2s.c @@ -348,7 +348,7 @@ static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops acp5x_i2s_dai_ops = { +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, diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c index a57b762d9f..2b6b9edc36 100644 --- a/sound/soc/amd/vangogh/pci-acp5x.c +++ b/sound/soc/amd/vangogh/pci-acp5x.c @@ -213,6 +213,9 @@ static int snd_acp5x_probe(struct pci_dev *pci, pdevinfo[2].num_res = 1; pdevinfo[2].res = &adata->res[2]; + pdevinfo[3].name = "acp5x_mach"; + pdevinfo[3].id = 0; + pdevinfo[3].parent = &pci->dev; for (i = 0; i < ACP5x_DEVS; i++) { adata->pdev[i] = platform_device_register_full(&pdevinfo[i]); diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 6b3d9c05ea..1934767690 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -342,8 +342,8 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* codec is slave, so cpu is master */ mr |= ATMEL_I2SC_MR_MODE_MASTER; ret = atmel_i2s_get_gck_param(dev, params_rate(params)); @@ -351,7 +351,7 @@ static int atmel_i2s_hw_params(struct snd_pcm_substream *substream, return ret; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: /* codec is master, so cpu is slave */ mr |= ATMEL_I2SC_MR_MODE_SLAVE; dev->gck_param = NULL; diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 6a63e8797a..26e2bc690d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -209,8 +209,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, if (frame_size < 0) return frame_size; - switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFS: + switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFC: if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) && ssc->clk_from_rk_pin) /* Receiver Frame Synchro (i.e. capture) @@ -220,7 +220,7 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, mck_div = 3; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) && !ssc->clk_from_rk_pin) /* Transmit Frame Synchro (i.e. playback) @@ -232,8 +232,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, break; } - switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: r.num = ssc_p->mck_rate / mck_div / frame_size; ret = snd_interval_ratnum(i, 1, &r, &num, &den); @@ -243,8 +243,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, } break; - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFC: + case SND_SOC_DAIFMT_CBP_CFP: t.min = 8000; t.max = ssc_p->mck_rate / mck_div / frame_size; t.openmin = t.openmax = 0; @@ -429,9 +429,9 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, /* Is the cpu-dai master of the frame clock? */ static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p) { - switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBS_CFS: + switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFC: + case SND_SOC_DAIFMT_CBC_CFC: return 1; } return 0; @@ -440,9 +440,9 @@ static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p) /* Is the cpu-dai master of the bit clock? */ static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p) { - switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBS_CFS: + switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBC_CFC: return 1; } return 0; diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index 8988f024a7..6d1227a1d6 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -350,7 +350,7 @@ static int mchp_i2s_mcc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; /* We can't generate only FSYNC */ - if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFS) + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_CBP_CFC) return -EINVAL; /* We can only reconfigure the IP when it's stopped */ @@ -546,20 +546,20 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* cpu is BCLK and LRC master */ mra |= MCHP_I2SMCC_MRA_MODE_MASTER; if (dev->sysclk) mra |= MCHP_I2SMCC_MRA_IMCKMODE_GEN; set_divs = 1; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: /* cpu is BCLK master */ mrb |= MCHP_I2SMCC_MRB_CLKSEL_INT; set_divs = 1; fallthrough; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: /* cpu is slave */ mra |= MCHP_I2SMCC_MRA_MODE_SLAVE; if (dev->sysclk) diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c index 0be7b4221c..627564c18c 100644 --- a/sound/soc/atmel/mikroe-proto.c +++ b/sound/soc/atmel/mikroe-proto.c @@ -129,9 +129,9 @@ static int snd_proto_probe(struct platform_device *pdev) } if (bitclkmaster) { if (codec_np == bitclkmaster) - dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; else - dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; } else { dai_fmt |= snd_soc_daifmt_parse_clock_provider_as_flag(np, NULL); } @@ -144,9 +144,9 @@ static int snd_proto_probe(struct platform_device *pdev) of_node_put(cpu_np); ret = snd_soc_register_card(&snd_proto); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card() failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index 50c3dc6936..ef537de771 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -304,7 +304,7 @@ static struct snd_soc_dai_link tse850_dailink = { .stream_name = "TSE-850-PCM", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFS, + | SND_SOC_DAIFMT_CBP_CFC, SND_SOC_DAILINK_REG(pcm), }; @@ -371,35 +371,27 @@ static int tse850_probe(struct platform_device *pdev) } tse850->add = devm_gpiod_get(dev, "axentia,add", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->add)) { - if (PTR_ERR(tse850->add) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'add' gpio\n"); - return PTR_ERR(tse850->add); - } + if (IS_ERR(tse850->add)) + return dev_err_probe(dev, PTR_ERR(tse850->add), + "failed to get 'add' gpio\n"); tse850->add_cache = 1; tse850->loop1 = devm_gpiod_get(dev, "axentia,loop1", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop1)) { - if (PTR_ERR(tse850->loop1) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop1' gpio\n"); - return PTR_ERR(tse850->loop1); - } + if (IS_ERR(tse850->loop1)) + return dev_err_probe(dev, PTR_ERR(tse850->loop1), + "failed to get 'loop1' gpio\n"); tse850->loop1_cache = 1; tse850->loop2 = devm_gpiod_get(dev, "axentia,loop2", GPIOD_OUT_HIGH); - if (IS_ERR(tse850->loop2)) { - if (PTR_ERR(tse850->loop2) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'loop2' gpio\n"); - return PTR_ERR(tse850->loop2); - } + if (IS_ERR(tse850->loop2)) + return dev_err_probe(dev, PTR_ERR(tse850->loop2), + "failed to get 'loop2' gpio\n"); tse850->loop2_cache = 1; tse850->ana = devm_regulator_get(dev, "axentia,ana"); - if (IS_ERR(tse850->ana)) { - if (PTR_ERR(tse850->ana) != -EPROBE_DEFER) - dev_err(dev, "failed to get 'ana' regulator\n"); - return PTR_ERR(tse850->ana); - } + if (IS_ERR(tse850->ana)) + return dev_err_probe(dev, PTR_ERR(tse850->ana), + "failed to get 'ana' regulator\n"); ret = regulator_enable(tse850->ana); if (ret < 0) { diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c index 65bd39f503..740d4e052e 100644 --- a/sound/soc/au1x/i2sc.c +++ b/sound/soc/au1x/i2sc.c @@ -119,9 +119,9 @@ static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) goto out; } - /* I2S controller only supports master */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */ + /* I2S controller only supports provider */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* CODEC consumer */ break; default: goto out; diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c index 767ce950d0..b2b8896bb5 100644 --- a/sound/soc/au1x/psc-i2s.c +++ b/sound/soc/au1x/psc-i2s.c @@ -90,12 +90,12 @@ static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, goto out; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */ - ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: /* CODEC provider */ + ct |= PSC_I2SCFG_MS; /* PSC I2S consumer mode */ break; - case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */ - ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */ + case SND_SOC_DAIFMT_CBC_CFC: /* CODEC consumer */ + ct &= ~PSC_I2SCFG_MS; /* PSC I2S provider mode */ break; default: goto out; diff --git a/sound/soc/bcm/audioinjector-pi-soundcard.c b/sound/soc/bcm/audioinjector-pi-soundcard.c index 3f24fa7390..ad337201c5 100644 --- a/sound/soc/bcm/audioinjector-pi-soundcard.c +++ b/sound/soc/bcm/audioinjector-pi-soundcard.c @@ -158,9 +158,11 @@ static int audioinjector_pi_soundcard_probe(struct platform_device *pdev) } } - if ((ret = devm_snd_soc_register_card(&pdev->dev, card))) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - } + if ((ret = devm_snd_soc_register_card(&pdev->dev, card))) + return dev_err_probe(&pdev->dev, ret, "%s\n", __func__); + + dev_info(&pdev->dev, "successfully loaded\n"); + return ret; } diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 3d668f449b..e3fc4bee8c 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -127,14 +127,14 @@ struct bcm2835_i2s_dev { static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) { - unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; + unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; if (dev->clk_prepared) return; - switch (master) { - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBS_CFM: + switch (provider) { + case SND_SOC_DAIFMT_CBC_CFC: + case SND_SOC_DAIFMT_CBC_CFP: clk_prepare_enable(dev->clk); dev->clk_prepared = true; break; @@ -337,8 +337,8 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, unsigned int rx_mask, tx_mask; unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos; unsigned int mode, format; - bool bit_clock_master = false; - bool frame_sync_master = false; + bool bit_clock_provider = false; + bool frame_sync_provider = false; bool frame_start_falling_edge = false; uint32_t csreg; int ret = 0; @@ -383,36 +383,36 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, if (data_length > slot_width) return -EINVAL; - /* Check if CPU is bit clock master */ - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBS_CFM: - bit_clock_master = true; + /* Check if CPU is bit clock provider */ + switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + case SND_SOC_DAIFMT_CBC_CFP: + bit_clock_provider = true; break; - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBM_CFM: - bit_clock_master = false; + case SND_SOC_DAIFMT_CBP_CFC: + case SND_SOC_DAIFMT_CBP_CFP: + bit_clock_provider = false; break; default: return -EINVAL; } - /* Check if CPU is frame sync master */ - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBM_CFS: - frame_sync_master = true; + /* Check if CPU is frame sync provider */ + switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + case SND_SOC_DAIFMT_CBP_CFC: + frame_sync_provider = true; break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFM: - frame_sync_master = false; + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFP: + frame_sync_provider = false; break; default: return -EINVAL; } /* Clock should only be set up here if CPU is clock master */ - if (bit_clock_master && + if (bit_clock_provider && (!dev->clk_prepared || dev->clk_rate != bclk_rate)) { if (dev->clk_prepared) bcm2835_i2s_stop_clock(dev); @@ -501,11 +501,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* * Transmitting data immediately after frame start, eg * in left-justified or DSP mode A, only works stable - * if bcm2835 is the frame clock master. + * if bcm2835 is the frame clock provider. */ - if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master) + if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider) dev_warn(dev->dev, - "Unstable slave config detected, L/R may be swapped"); + "Unstable consumer config detected, L/R may be swapped"); /* * Set format for both streams. @@ -538,11 +538,11 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, mode |= BCM2835_I2S_FSLEN(framesync_length); /* CLKM selects bcm2835 clock slave mode */ - if (!bit_clock_master) + if (!bit_clock_provider) mode |= BCM2835_I2S_CLKM; /* FSM selects bcm2835 frame sync slave mode */ - if (!frame_sync_master) + if (!frame_sync_provider) mode |= BCM2835_I2S_FSM; /* CLKI selects normal clocking mode, sampling on rising edge */ diff --git a/sound/soc/bcm/bcm63xx-i2s.h b/sound/soc/bcm/bcm63xx-i2s.h index edc328ba53..f30556bec8 100644 --- a/sound/soc/bcm/bcm63xx-i2s.h +++ b/sound/soc/bcm/bcm63xx-i2s.h @@ -74,7 +74,6 @@ struct bcm_i2s_priv { struct device *dev; - struct resource *r_irq; struct regmap *regmap_i2s; struct clk *i2s_clk; struct snd_pcm_substream *play_substream; diff --git a/sound/soc/bcm/bcm63xx-pcm-whistler.c b/sound/soc/bcm/bcm63xx-pcm-whistler.c index b5096f64c5..2c600b0175 100644 --- a/sound/soc/bcm/bcm63xx-pcm-whistler.c +++ b/sound/soc/bcm/bcm63xx-pcm-whistler.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -387,14 +388,12 @@ int bcm63xx_soc_platform_probe(struct platform_device *pdev, { int ret; - i2s_priv->r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!i2s_priv->r_irq) { - dev_err(&pdev->dev, "Unable to get register irq resource.\n"); - return -ENODEV; - } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; - ret = devm_request_irq(&pdev->dev, i2s_priv->r_irq->start, i2s_dma_isr, - i2s_priv->r_irq->flags, "i2s_dma", (void *)i2s_priv); + ret = devm_request_irq(&pdev->dev, ret, i2s_dma_isr, + irq_get_trigger_type(ret), "i2s_dma", (void *)i2s_priv); if (ret) { dev_err(&pdev->dev, "i2s_init: failed to request interrupt.ret=%d\n", ret); diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index fca5a3f2ee..9698f4531c 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -848,12 +848,12 @@ static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ssp_newcfg = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE); aio->is_slave = 1; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE); aio->is_slave = 0; break; diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 0d26550d0d..2c8cd843d0 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -111,9 +111,9 @@ static void ep93xx_i2s_enable(struct ep93xx_i2s_info *info, int stream) if ((ep93xx_i2s_read_reg(info, EP93XX_I2S_TX0EN) & 0x1) == 0 && (ep93xx_i2s_read_reg(info, EP93XX_I2S_RX0EN) & 0x1) == 0) { /* Enable clocks */ - clk_enable(info->mclk); - clk_enable(info->sclk); - clk_enable(info->lrclk); + clk_prepare_enable(info->mclk); + clk_prepare_enable(info->sclk); + clk_prepare_enable(info->lrclk); /* Enable i2s */ ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 1); @@ -156,9 +156,9 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) ep93xx_i2s_write_reg(info, EP93XX_I2S_GLCTRL, 0); /* Disable clocks */ - clk_disable(info->lrclk); - clk_disable(info->sclk); - clk_disable(info->mclk); + clk_disable_unprepare(info->lrclk); + clk_disable_unprepare(info->sclk); + clk_disable_unprepare(info->mclk); } } @@ -245,14 +245,14 @@ static int ep93xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* CPU is master */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* CPU is provider */ clk_cfg |= EP93XX_I2S_CLKCFG_MASTER; break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Codec is master */ + case SND_SOC_DAIFMT_CBP_CFP: + /* Codec is provider */ clk_cfg &= ~EP93XX_I2S_CLKCFG_MASTER; break; diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index cac7e557ed..c6043fa58c 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -968,16 +968,16 @@ static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBP_CFC: if (pm860x->dir == PM860X_CLK_DIR_OUT) { inf |= PCM_INF2_MASTER; ret = 0; } break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: if (pm860x->dir == PM860X_CLK_DIR_IN) { inf &= ~PCM_INF2_MASTER; ret = 0; @@ -1072,15 +1072,15 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: if (pm860x->dir == PM860X_CLK_DIR_OUT) inf |= PCM_INF2_MASTER; else return -EINVAL; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: if (pm860x->dir == PM860X_CLK_DIR_IN) inf &= ~PCM_INF2_MASTER; else diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8dedbc825b..500f452f4d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ADS117X imply SND_SOC_AK4104 imply SND_SOC_AK4118 + imply SND_SOC_AK4375 imply SND_SOC_AK4458 imply SND_SOC_AK4535 imply SND_SOC_AK4554 @@ -61,6 +62,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L34 imply SND_SOC_CS35L35 imply SND_SOC_CS35L36 + imply SND_SOC_CS35L41_SPI + imply SND_SOC_CS35L41_I2C imply SND_SOC_CS42L42 imply SND_SOC_CS42L51_I2C imply SND_SOC_CS42L52 @@ -117,6 +120,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MAX98357A imply SND_SOC_MAX98371 imply SND_SOC_MAX98504 + imply SND_SOC_MAX98520 imply SND_SOC_MAX9867 imply SND_SOC_MAX98925 imply SND_SOC_MAX98926 @@ -138,6 +142,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_NAU8315 imply SND_SOC_NAU8540 imply SND_SOC_NAU8810 + imply SND_SOC_NAU8821 imply SND_SOC_NAU8822 imply SND_SOC_NAU8824 imply SND_SOC_NAU8825 @@ -183,6 +188,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT5677 imply SND_SOC_RT5682_I2C imply SND_SOC_RT5682_SDW + imply SND_SOC_RT5682S imply SND_SOC_RT700_SDW imply SND_SOC_RT711_SDW imply SND_SOC_RT711_SDCA_SDW @@ -190,6 +196,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW + imply SND_SOC_RT9120 imply SND_SOC_SDW_MOCKUP imply SND_SOC_SGTL5000 imply SND_SOC_SI476X @@ -217,6 +224,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TDA7419 imply SND_SOC_TFA9879 imply SND_SOC_TFA989X + imply SND_SOC_TLV320ADC3XXX imply SND_SOC_TLV320ADCX140 imply SND_SOC_TLV320AIC23_I2C imply SND_SOC_TLV320AIC23_SPI @@ -333,17 +341,22 @@ config SND_SOC_WM_HUBS config SND_SOC_WM_ADSP tristate + select CS_DSP select SND_SOC_COMPRESS default y if SND_SOC_MADERA=y default y if SND_SOC_CS47L24=y default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y default y if SND_SOC_WM2200=y + default y if SND_SOC_CS35L41_SPI=y + default y if SND_SOC_CS35L41_I2C=y default m if SND_SOC_MADERA=m default m if SND_SOC_CS47L24=m default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m default m if SND_SOC_WM2200=m + default m if SND_SOC_CS35L41_SPI=m + default m if SND_SOC_CS35L41_I2C=m config SND_SOC_AB8500_CODEC tristate @@ -515,6 +528,16 @@ config SND_SOC_AK4118 depends on I2C select REGMAP_I2C +config SND_SOC_AK4375 + tristate "AKM AK4375 CODEC" + depends on I2C + select REGMAP_I2C + help + Enable support for the Asahi-Kasei AK4375 codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-ak4375. + config SND_SOC_AK4458 tristate "AKM AK4458 CODEC" depends on I2C @@ -605,6 +628,26 @@ config SND_SOC_CS35L36 tristate "Cirrus Logic CS35L36 CODEC" depends on I2C +config SND_SOC_CS35L41_LIB + tristate + +config SND_SOC_CS35L41 + tristate + +config SND_SOC_CS35L41_SPI + tristate "Cirrus Logic CS35L41 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 + select REGMAP_SPI + +config SND_SOC_CS35L41_I2C + tristate "Cirrus Logic CS35L41 CODEC (I2C)" + depends on I2C + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 + select REGMAP_I2C + config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C @@ -932,6 +975,17 @@ config SND_SOC_MAX98927 tristate "Maxim Integrated MAX98927 Speaker Amplifier" depends on I2C +config SND_SOC_MAX98520 + tristate "Maxim Integrated MAX98520 Speaker Amplifier" + depends on I2C + help + Enable support for Maxim Integrated MAX98520 audio + amplifier, which implements a tripler charge pump + based boost converter and supports sample rates of + 8KHz to 192KHz. + + To compile this driver as a module, choose M here. + config SND_SOC_MAX98373 tristate @@ -1263,6 +1317,10 @@ config SND_SOC_RT5682_SDW select SND_SOC_RT5682 select REGMAP_SOUNDWIRE +config SND_SOC_RT5682S + tristate + depends on I2C + config SND_SOC_RT700 tristate @@ -1302,6 +1360,15 @@ config SND_SOC_RT715_SDCA_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT9120 + tristate "Richtek RT9120 Stereo Class-D Amplifier" + depends on I2C + select REGMAP_I2C + select GPIOLIB + help + Enable support for Richtek RT9120 20W, stereo, inductor-less, + high-efficiency Class-D audio amplifier. + config SND_SOC_SDW_MOCKUP tristate "SoundWire mockup codec" depends on EXPERT @@ -1462,6 +1529,13 @@ config SND_SOC_TFA989X Note that the driver currently bypasses the built-in "CoolFlux DSP" and does not support (hardware) volume control. +config SND_SOC_TLV320ADC3XXX + tristate "Texas Instruments TLV320ADC3001/3101 audio ADC" + depends on I2C + help + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. + config SND_SOC_TLV320AIC23 tristate @@ -1922,6 +1996,10 @@ config SND_SOC_NAU8810 tristate "Nuvoton Technology Corporation NAU88C10 CODEC" depends on I2C +config SND_SOC_NAU8821 + tristate "Nuvoton Technology Corporation NAU88L21 CODEC" + depends on I2C + config SND_SOC_NAU8822 tristate "Nuvoton Technology Corporation NAU88C22 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 58493a2b0a..8a93e13e9a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -34,6 +34,7 @@ snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4118-objs := ak4118.o +snd-soc-ak4375-objs := ak4375.o snd-soc-ak4458-objs := ak4458.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o @@ -54,6 +55,10 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs35l36-objs := cs35l36.o +snd-soc-cs35l41-lib-objs := cs35l41-lib.o +snd-soc-cs35l41-objs := cs35l41.o +snd-soc-cs35l41-spi-objs := cs35l41-spi.o +snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -125,6 +130,7 @@ snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o snd-soc-max98927-objs := max98927.o +snd-soc-max98520-objs := max98520.o snd-soc-max98373-objs := max98373.o snd-soc-max98373-i2c-objs := max98373-i2c.o snd-soc-max98373-sdw-objs := max98373-sdw.o @@ -143,6 +149,7 @@ snd-soc-mt6660-objs := mt6660.o snd-soc-nau8315-objs := nau8315.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o +snd-soc-nau8821-objs := nau8821.o snd-soc-nau8822-objs := nau8822.o snd-soc-nau8824-objs := nau8824.o snd-soc-nau8825-objs := nau8825.o @@ -201,11 +208,13 @@ snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-rt5682-objs := rt5682.o snd-soc-rt5682-sdw-objs := rt5682-sdw.o snd-soc-rt5682-i2c-objs := rt5682-i2c.o +snd-soc-rt5682s-objs := rt5682s.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt9120-objs := rt9120.o snd-soc-sdw-mockup-objs := sdw-mockup.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o @@ -236,6 +245,7 @@ snd-soc-tas2770-objs := tas2770.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tas5713-objs := tas5713.o snd-soc-tfa989x-objs := tfa989x.o +snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -367,6 +377,7 @@ obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o +obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o @@ -389,6 +400,10 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o +obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o +obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o +obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o +obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o @@ -456,6 +471,7 @@ obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o +obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o @@ -474,6 +490,7 @@ obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o +obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o @@ -533,11 +550,13 @@ obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o obj-$(CONFIG_SND_SOC_RT5682_I2C) += snd-soc-rt5682-i2c.o obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o +obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o 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_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o @@ -568,6 +587,7 @@ obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o obj-$(CONFIG_SND_SOC_TAS5713) += snd-soc-tas5713.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o +obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 5525e1ccab..aefafb0b7b 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2104,26 +2104,26 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) BIT(AB8500_DIGIFCONF3_IF0MASTER); val = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: dev_dbg(dai->component->dev, - "%s: IF0 Master-mode: AB8500 master.\n", __func__); + "%s: IF0 Master-mode: AB8500 provider.\n", __func__); val |= BIT(AB8500_DIGIFCONF3_IF0MASTER); break; - case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & FRM slave */ + case SND_SOC_DAIFMT_CBC_CFC: dev_dbg(dai->component->dev, - "%s: IF0 Master-mode: AB8500 slave.\n", __func__); + "%s: IF0 Master-mode: AB8500 consumer.\n", __func__); break; - case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */ - case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: dev_err(dai->component->dev, - "%s: ERROR: The device is either a master or a slave.\n", + "%s: ERROR: The device is either a provider or a consumer.\n", __func__); fallthrough; default: dev_err(dai->component->dev, - "%s: ERROR: Unsupporter master mask 0x%x\n", - __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); + "%s: ERROR: Unsupporter clocking mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); return -EINVAL; } diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index 08a5651bed..29e1689da6 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -148,9 +148,9 @@ static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - /* ALCLK,ABCLK are both output, AD1836 can only be master */ - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + /* ALCLK,ABCLK are both output, AD1836 can only be provider */ + case SND_SOC_DAIFMT_CBP_CFP: break; default: return -EINVAL; diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 278a55af15..30b98b4267 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -243,22 +243,22 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, if (fmt & SND_SOC_DAIFMT_DSP_A) dac_fmt ^= AD193X_DAC_LEFT_HIGH; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: adc_fmt |= AD193X_ADC_LCR_MASTER; adc_fmt |= AD193X_ADC_BCLK_MASTER; dac_fmt |= AD193X_DAC_LCR_MASTER; dac_fmt |= AD193X_DAC_BCLK_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */ + case SND_SOC_DAIFMT_CBC_CFP: adc_fmt |= AD193X_ADC_LCR_MASTER; dac_fmt |= AD193X_DAC_LCR_MASTER; break; - case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ + case SND_SOC_DAIFMT_CBP_CFC: adc_fmt |= AD193X_ADC_BCLK_MASTER; dac_fmt |= AD193X_DAC_BCLK_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index 6811a8b386..1faa4c4263 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -30,7 +30,7 @@ struct adau1372 { void (*switch_mode)(struct device *dev); bool use_pll; bool enabled; - bool master; + bool clock_provider; struct snd_pcm_hw_constraint_list rate_constraints; unsigned int slot_width; @@ -578,13 +578,13 @@ static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int sai0 = 0, sai1 = 0; bool invert_lrclk = false; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - adau1372->master = true; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + adau1372->clock_provider = true; sai1 |= ADAU1372_SAI1_MS; break; - case SND_SOC_DAIFMT_CBS_CFS: - adau1372->master = false; + case SND_SOC_DAIFMT_CBC_CFC: + adau1372->clock_provider = false; break; default: return -EINVAL; @@ -714,7 +714,7 @@ static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, break; case 4: sai0 = ADAU1372_SAI0_SAI_TDM4; - if (adau1372->master) + if (adau1372->clock_provider) adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER; else adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4; diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 9887aa6f0b..46128aacea 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -28,7 +28,7 @@ struct adau1373_dai { unsigned int clk_src; unsigned int sysclk; bool enable_src; - bool master; + bool clock_provider; }; struct adau1373 { @@ -827,7 +827,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source, dai = sink->name[3] - '1'; - if (!adau1373->dais[dai].master) + if (!adau1373->dais[dai].clock_provider) return 0; if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1) @@ -1102,14 +1102,14 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id]; unsigned int ctrl; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: ctrl = ADAU1373_DAI_MASTER; - adau1373_dai->master = true; + adau1373_dai->clock_provider = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ctrl = 0; - adau1373_dai->master = false; + adau1373_dai->clock_provider = false; break; default: return -EINVAL; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 5ce7469756..dba9af7531 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -13,8 +13,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -106,8 +106,8 @@ static const char * const supply_names[] = { }; struct adau1701 { - int gpio_nreset; - int gpio_pll_mode[2]; + struct gpio_desc *gpio_nreset; + struct gpio_descs *gpio_pll_mode; unsigned int dai_fmt; unsigned int pll_clkdiv; unsigned int sysclk; @@ -303,39 +303,41 @@ static int adau1701_reset(struct snd_soc_component *component, unsigned int clkd struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); int ret; + DECLARE_BITMAP(values, 2); sigmadsp_reset(adau1701->sigmadsp); - if (clkdiv != ADAU1707_CLKDIV_UNSET && - gpio_is_valid(adau1701->gpio_pll_mode[0]) && - gpio_is_valid(adau1701->gpio_pll_mode[1])) { + if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) { switch (clkdiv) { case 64: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 0); + __assign_bit(1, values, 0); break; case 256: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 0); + __assign_bit(1, values, 1); break; case 384: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); + __assign_bit(0, values, 1); + __assign_bit(1, values, 0); break; - case 0: /* fallback */ + case 0: /* fallback */ case 512: - gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); - gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); + __assign_bit(0, values, 1); + __assign_bit(1, values, 1); break; } + gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs, + adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info, + values); } adau1701->pll_clkdiv = clkdiv; - if (gpio_is_valid(adau1701->gpio_nreset)) { - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) { + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); /* minimum reset time is 20ns */ udelay(1); - gpio_set_value_cansleep(adau1701->gpio_nreset, 1); + gpiod_set_value_cansleep(adau1701->gpio_nreset, 1); /* power-up time may be as long as 85ms */ mdelay(85); } @@ -482,13 +484,13 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int serictl = 0x00, seroctl = 0x00; bool invert_lrclk; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: /* master, 64-bits per sample, 1 frame per sample */ seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16 | ADAU1701_SEROCTL_OLF1024; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; @@ -719,8 +721,8 @@ static void adau1701_remove(struct snd_soc_component *component) { struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(adau1701->gpio_nreset)) - gpio_set_value_cansleep(adau1701->gpio_nreset, 0); + if (adau1701->gpio_nreset) + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); } @@ -788,8 +790,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, { struct adau1701 *adau1701; struct device *dev = &client->dev; - int gpio_nreset = -EINVAL; - int gpio_pll_mode[2] = { -EINVAL, -EINVAL }; int ret, i; adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); @@ -823,26 +823,6 @@ static int adau1701_i2c_probe(struct i2c_client *client, if (dev->of_node) { - gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); - if (gpio_nreset < 0 && gpio_nreset != -ENOENT) { - ret = gpio_nreset; - goto exit_regulators_disable; - } - - gpio_pll_mode[0] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 0); - if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT) { - ret = gpio_pll_mode[0]; - goto exit_regulators_disable; - } - - gpio_pll_mode[1] = of_get_named_gpio(dev->of_node, - "adi,pll-mode-gpios", 1); - if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT) { - ret = gpio_pll_mode[1]; - goto exit_regulators_disable; - } - of_property_read_u32(dev->of_node, "adi,pll-clkdiv", &adau1701->pll_clkdiv); @@ -851,32 +831,20 @@ static int adau1701_i2c_probe(struct i2c_client *client, ARRAY_SIZE(adau1701->pin_config)); } - if (gpio_is_valid(gpio_nreset)) { - ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, - "ADAU1701 Reset"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN); + + if (IS_ERR(adau1701->gpio_nreset)) { + ret = PTR_ERR(adau1701->gpio_nreset); + goto exit_regulators_disable; } - if (gpio_is_valid(gpio_pll_mode[0]) && - gpio_is_valid(gpio_pll_mode[1])) { - ret = devm_gpio_request_one(dev, gpio_pll_mode[0], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 0"); - if (ret < 0) - goto exit_regulators_disable; + adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW); - ret = devm_gpio_request_one(dev, gpio_pll_mode[1], - GPIOF_OUT_INIT_LOW, - "ADAU1701 PLL mode 1"); - if (ret < 0) - goto exit_regulators_disable; + if (IS_ERR(adau1701->gpio_pll_mode)) { + ret = PTR_ERR(adau1701->gpio_pll_mode); + goto exit_regulators_disable; } - adau1701->gpio_nreset = gpio_nreset; - adau1701->gpio_pll_mode[0] = gpio_pll_mode[0]; - adau1701->gpio_pll_mode[1] = gpio_pll_mode[1]; - i2c_set_clientdata(client, adau1701); adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 8aae7ab740..af05463af4 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -556,12 +556,12 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai, unsigned int ctrl0_mask; int lrclk_pol; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER; adau->master = true; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ctrl0 = 0; adau->master = false; break; diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index e347a48131..5fcbdf2ec3 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -124,10 +124,10 @@ struct adau1977 { struct device *dev; void (*switch_mode)(struct device *dev); - unsigned int max_master_fs; + unsigned int max_clock_provider_fs; unsigned int slot_width; bool enabled; - bool master; + bool clock_provider; }; static const struct reg_default adau1977_reg_defaults[] = { @@ -330,7 +330,7 @@ static int adau1977_hw_params(struct snd_pcm_substream *substream, ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK; } - if (adau1977->master) { + if (adau1977->clock_provider) { switch (params_width(params)) { case 16: ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT; @@ -504,7 +504,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, if (slots == 0) { /* 0 = No fixed slot width */ adau1977->slot_width = 0; - adau1977->max_master_fs = 192000; + adau1977->max_clock_provider_fs = 192000; return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK, ADAU1977_SAI_CTRL0_SAI_I2S); @@ -533,7 +533,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, break; case 24: /* We can only generate 16 bit or 32 bit wide slots */ - if (adau1977->master) + if (adau1977->clock_provider) return -EINVAL; ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24; break; @@ -593,8 +593,8 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, adau1977->slot_width = width; - /* In master mode the maximum bitclock is 24.576 MHz */ - adau1977->max_master_fs = min(192000, 24576000 / width / slots); + /* In clock provider mode the maximum bitclock is 24.576 MHz */ + adau1977->max_clock_provider_fs = min(192000, 24576000 / width / slots); return 0; } @@ -620,13 +620,13 @@ static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) bool invert_lrclk; int ret; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - adau1977->master = false; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + adau1977->clock_provider = false; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ctrl1 |= ADAU1977_SAI_CTRL1_MASTER; - adau1977->master = true; + adau1977->clock_provider = true; break; default: return -EINVAL; @@ -714,9 +714,10 @@ static int adau1977_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints); - if (adau1977->master) + if (adau1977->clock_provider) snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs); + SNDRV_PCM_HW_PARAM_RATE, 8000, + adau1977->max_clock_provider_fs); if (formats != 0) snd_pcm_hw_constraint_mask64(substream->runtime, @@ -913,7 +914,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap, adau1977->type = type; adau1977->regmap = regmap; adau1977->switch_mode = switch_mode; - adau1977->max_master_fs = 192000; + adau1977->max_clock_provider_fs = 192000; adau1977->constraints.list = adau1977_rates; adau1977->constraints.count = ARRAY_SIZE(adau1977_rates); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 75a6491081..90f3a5e9e3 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -369,12 +369,12 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) unsigned int capture = 0x00; unsigned int playback = 0x00; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: capture |= ADAV80X_CAPTURE_MODE_MASTER; playback |= ADAV80X_PLAYBACK_MODE_MASTER; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 979cfb165e..dc4747c77a 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -81,8 +81,8 @@ static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - /* This device can only be slave */ - if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) + /* This device can only be consumer */ + if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) return -EINVAL; ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1, diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 5d46ae8556..2e6bafd2a8 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -151,8 +151,8 @@ static const struct snd_soc_dapm_route ak4118_dapm_routes[] = { }; -static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118, - unsigned int format) +static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118, + unsigned int format) { int dif; @@ -173,8 +173,8 @@ static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118, return dif; } -static int ak4118_set_dai_fmt_slave(struct ak4118_priv *ak4118, - unsigned int format) +static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118, + unsigned int format) { int dif; @@ -201,14 +201,12 @@ static int ak4118_set_dai_fmt(struct snd_soc_dai *dai, int dif; int ret = 0; - switch (format & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - /* component is master */ - dif = ak4118_set_dai_fmt_master(ak4118, format); + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + dif = ak4118_set_dai_fmt_provider(ak4118, format); break; - case SND_SOC_DAIFMT_CBS_CFS: - /*component is slave */ - dif = ak4118_set_dai_fmt_slave(ak4118, format); + case SND_SOC_DAIFMT_CBC_CFC: + dif = ak4118_set_dai_fmt_consumer(ak4118, format); break; default: ret = -ENOTSUPP; @@ -376,20 +374,14 @@ static int ak4118_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, ak4118); ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(ak4118->reset)) { - ret = PTR_ERR(ak4118->reset); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->reset)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset), + "Failed to get reset\n"); ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); - if (IS_ERR(ak4118->irq)) { - ret = PTR_ERR(ak4118->irq); - if (ret != -EPROBE_DEFER) - dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret); - return ret; - } + if (IS_ERR(ak4118->irq)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq), + "Failed to get IRQ\n"); ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), NULL, ak4118_irq_handler, diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 29eb78702b..baa9ff5d0c 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -464,14 +464,14 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); int ret; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: /* Slave Mode */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */ break; - case SND_SOC_DAIFMT_CBM_CFM: /* Master Mode is not supported */ - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */ + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: default: - dev_err(component->dev, "Master mode unsupported\n"); + dev_err(component->dev, "Clock provider mode unsupported\n"); return -EINVAL; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index c49c58eeb4..c284dcc5af 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -392,13 +392,13 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) data = MCKO | PMPLL; /* use MCKO */ bcko = 0; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* set clocking for audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: data |= MS; bcko = BCKO_64; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index eb435235b5..e9d1251c42 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -520,11 +520,11 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) /* set master/slave audio interface */ mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1); - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: mode |= AK4671_M_S; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: mode &= ~(AK4671_M_S); break; default: diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 37d4600b6f..c94cfde3e4 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -198,13 +198,13 @@ static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct snd_soc_component *component = dai->component; u8 format; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: break; - case SND_SOC_DAIFMT_CBS_CFM: - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: default: dev_err(dai->dev, "Clock mode unsupported"); return -EINVAL; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 54f4898371..b10357a6d6 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -641,12 +641,12 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; u16 iface = 0; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: iface = ALC5623_DAI_SDP_MASTER_MODE; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: iface = ALC5623_DAI_SDP_SLAVE_MODE; break; default: diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 79813882a9..6d7af3736a 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -815,12 +815,12 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; u16 iface = 0; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: iface = ALC5632_DAI_SDP_MASTER_MODE; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: iface = ALC5632_DAI_SDP_SLAVE_MODE; break; default: diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c index f1c13f42e1..ffdf8b615e 100644 --- a/sound/soc/codecs/cpcap.c +++ b/sound/soc/codecs/cpcap.c @@ -1168,15 +1168,15 @@ static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, /* * "HiFi Playback" should always be configured as - * SND_SOC_DAIFMT_CBM_CFM - codec clk & frm master + * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider * SND_SOC_DAIFMT_I2S - I2S mode */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: val &= ~BIT(CPCAP_BIT_SMB_ST_DAC); break; default: - dev_err(dev, "HiFi dai fmt failed: CPCAP should be master"); + dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider"); return -EINVAL; } @@ -1318,15 +1318,15 @@ static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, /* * "Voice Playback" and "Voice Capture" should always be - * configured as SND_SOC_DAIFMT_CBM_CFM - codec clk & frm - * master + * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm + * provider */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: val &= ~BIT(CPCAP_BIT_SMB_CDC); break; default: - dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the master"); + dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider"); val &= ~BIT(CPCAP_BIT_SMB_CDC); break; } diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index a201d652ac..9b92e1a0d1 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -283,8 +283,8 @@ static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct ec_param_ec_codec_i2s_rx p; enum ec_codec_i2s_rx_daifmt daifmt; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 7a5588f1df..961a3e07e7 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1311,7 +1311,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc"); classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); - classh_config->classh_algo_enable = classh ? true : false; + classh_config->classh_algo_enable = (classh != NULL); if (classh_config->classh_algo_enable) { classh_config->classh_bst_override = diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index b49cb92d7b..4415fb364d 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -610,8 +610,8 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, if (devid != CS4265_CHIP_ID_VAL) { ret = -ENODEV; dev_err(&i2c_client->dev, - "CS4265 Device ID (%X). Expected %X\n", - devid, CS4265_CHIP_ID); + "CS4265 Part Number ID: 0x%x Expected: 0x%x\n", + devid >> 4, CS4265_CHIP_ID_VAL >> 4); return ret; } dev_info(&i2c_client->dev, @@ -625,6 +625,16 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, ARRAY_SIZE(cs4265_dai)); } +static int cs4265_i2c_remove(struct i2c_client *i2c) +{ + struct cs4265_private *cs4265 = i2c_get_clientdata(i2c); + + if (cs4265->reset_gpio) + gpiod_set_value_cansleep(cs4265->reset_gpio, 0); + + return 0; +} + static const struct of_device_id cs4265_of_match[] = { { .compatible = "cirrus,cs4265", }, { } @@ -644,6 +654,7 @@ static struct i2c_driver cs4265_i2c_driver = { }, .id_table = cs4265_id, .probe = cs4265_i2c_probe, + .remove = cs4265_i2c_remove, }; module_i2c_driver(cs4265_i2c_driver); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 762d9de73d..43d98bdb5b 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -41,9 +40,9 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_FRZ_CTL, 0x00 }, { CS42L42_SRC_CTL, 0x10 }, - { CS42L42_MCLK_STATUS, 0x02 }, { CS42L42_MCLK_CTL, 0x02 }, { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, { CS42L42_I2C_DEBOUNCE, 0x88 }, { CS42L42_I2C_STRETCH, 0x03 }, { CS42L42_I2C_TIMEOUT, 0xB7 }, @@ -53,15 +52,12 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_RSENSE_CTL1, 0x40 }, { CS42L42_RSENSE_CTL2, 0x00 }, { CS42L42_OSC_SWITCH, 0x00 }, - { CS42L42_OSC_SWITCH_STATUS, 0x05 }, { CS42L42_RSENSE_CTL3, 0x1B }, { CS42L42_TSENSE_CTL, 0x1B }, { CS42L42_TSRS_INT_DISABLE, 0x00 }, - { CS42L42_TRSENSE_STATUS, 0x00 }, { CS42L42_HSDET_CTL1, 0x77 }, { CS42L42_HSDET_CTL2, 0x00 }, { CS42L42_HS_SWITCH_CTL, 0xF3 }, - { CS42L42_HS_DET_STATUS, 0x00 }, { CS42L42_HS_CLAMP_DISABLE, 0x00 }, { CS42L42_MCLK_SRC_SEL, 0x00 }, { CS42L42_SPDIF_CLK_CFG, 0x00 }, @@ -75,18 +71,6 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_IN_ASRC_CLK, 0x00 }, { CS42L42_OUT_ASRC_CLK, 0x00 }, { CS42L42_PLL_DIV_CFG1, 0x00 }, - { CS42L42_ADC_OVFL_STATUS, 0x00 }, - { CS42L42_MIXER_STATUS, 0x00 }, - { CS42L42_SRC_STATUS, 0x00 }, - { CS42L42_ASP_RX_STATUS, 0x00 }, - { CS42L42_ASP_TX_STATUS, 0x00 }, - { CS42L42_CODEC_STATUS, 0x00 }, - { CS42L42_DET_INT_STATUS1, 0x00 }, - { CS42L42_DET_INT_STATUS2, 0x00 }, - { CS42L42_SRCPL_INT_STATUS, 0x00 }, - { CS42L42_VPMON_STATUS, 0x00 }, - { CS42L42_PLL_LOCK_STATUS, 0x00 }, - { CS42L42_TSRS_PLUG_STATUS, 0x00 }, { CS42L42_ADC_OVFL_INT_MASK, 0x01 }, { CS42L42_MIXER_INT_MASK, 0x0F }, { CS42L42_SRC_INT_MASK, 0x0F }, @@ -105,8 +89,6 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_PLL_CTL3, 0x10 }, { CS42L42_PLL_CAL_RATIO, 0x80 }, { CS42L42_PLL_CTL4, 0x03 }, - { CS42L42_LOAD_DET_RCSTAT, 0x00 }, - { CS42L42_LOAD_DET_DONE, 0x00 }, { CS42L42_LOAD_DET_EN, 0x00 }, { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 }, { CS42L42_WAKE_CTL, 0xC0 }, @@ -115,8 +97,6 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_MISC_DET_CTL, 0x03 }, { CS42L42_MIC_DET_CTL1, 0x1F }, { CS42L42_MIC_DET_CTL2, 0x2F }, - { CS42L42_DET_STATUS1, 0x00 }, - { CS42L42_DET_STATUS2, 0x00 }, { CS42L42_DET_INT1_MASK, 0xE0 }, { CS42L42_DET_INT2_MASK, 0xFF }, { CS42L42_HS_BIAS_CTL, 0xC2 }, @@ -182,7 +162,6 @@ static const struct reg_default cs42l42_reg_defaults[] = { { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 }, { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 }, { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 }, - { CS42L42_SUB_REVID, 0x03 }, }; static bool cs42l42_readable_register(struct device *dev, unsigned int reg) @@ -199,6 +178,7 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg) case CS42L42_MCLK_STATUS: case CS42L42_MCLK_CTL: case CS42L42_SFTRAMP_RATE: + case CS42L42_SLOW_START_ENABLE: case CS42L42_I2C_DEBOUNCE: case CS42L42_I2C_STRETCH: case CS42L42_I2C_TIMEOUT: @@ -351,6 +331,7 @@ static bool cs42l42_volatile_register(struct device *dev, unsigned int reg) case CS42L42_DEVID_CD: case CS42L42_DEVID_E: case CS42L42_MCLK_STATUS: + case CS42L42_OSC_SWITCH_STATUS: case CS42L42_TRSENSE_STATUS: case CS42L42_HS_DET_STATUS: case CS42L42_ADC_OVFL_STATUS: @@ -408,6 +389,28 @@ static const struct regmap_config cs42l42_regmap = { static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true); static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true); +static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 val; + + /* all bits of SLOW_START_EN much change together */ + switch (ucontrol->value.integer.value[0]) { + case 0: + val = 0; + break; + case 1: + val = CS42L42_SLOW_START_EN_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_MASK, val); +} + static const char * const cs42l42_hpf_freq_text[] = { "1.86Hz", "120Hz", "235Hz", "466Hz" }; @@ -452,13 +455,43 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = { CS42L42_DAC_HPF_EN_SHIFT, true, false), SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, - 0x3f, 1, mixer_tlv) + 0x3f, 1, mixer_tlv), + + SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_SHIFT, true, false, + snd_soc_get_volsw, cs42l42_slow_start_put), }; +static int cs42l42_hp_adc_ev(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 cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + cs42l42->hp_adc_up_pending = true; + break; + case SND_SOC_DAPM_POST_PMU: + /* Only need one delay if HP and ADC are both powering-up */ + if (cs42l42->hp_adc_up_pending) { + usleep_range(CS42L42_HP_ADC_EN_TIME_US, + CS42L42_HP_ADC_EN_TIME_US + 1000); + cs42l42->hp_adc_up_pending = false; + } + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { /* Playback Path */ SND_SOC_DAPM_OUTPUT("HP"), - SND_SOC_DAPM_DAC("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1), + SND_SOC_DAPM_DAC_E("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1, + cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0), SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, SND_SOC_NOPM, 0, 0), @@ -468,7 +501,8 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { /* Capture Path */ SND_SOC_DAPM_INPUT("HS"), - SND_SOC_DAPM_ADC("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC_E("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1, + cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0), @@ -515,28 +549,29 @@ 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->jack_detect_mutex); cs42l42->jack = jk; - regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | CS42L42_RS_UNPLUG_MASK | - CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | (1 << CS42L42_RS_UNPLUG_SHIFT) | - (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT)); - - return 0; -} - -static int cs42l42_component_probe(struct snd_soc_component *component) -{ - struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); - - cs42l42->component = component; + if (jk) { + switch (cs42l42->hs_type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); + break; + default: + break; + } + } + mutex_unlock(&cs42l42->jack_detect_mutex); return 0; } static const struct snd_soc_component_driver soc_component_dev_cs42l42 = { - .probe = cs42l42_component_probe, .set_jack = cs42l42_set_jack, .dapm_widgets = cs42l42_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), @@ -569,7 +604,6 @@ static const struct reg_sequence cs42l42_to_osc_seq[] = { struct cs42l42_pll_params { u32 sclk; - u8 mclk_div; u8 mclk_src_sel; u8 sclk_prediv; u8 pll_div_int; @@ -586,24 +620,24 @@ struct cs42l42_pll_params { * Table 4-5 from the Datasheet */ static const struct cs42l42_pll_params pll_ratio_table[] = { - { 1411200, 0, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2}, - { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2}, - { 2304000, 0, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2}, - { 2400000, 0, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, - { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, - { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, - { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, - { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, - { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, - { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, - { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, - { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, - { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, - { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, - { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, - { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0, 1}, - { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0, 1}, - { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0, 1} + { 1411200, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2}, + { 1536000, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2}, + { 2304000, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2}, + { 2400000, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 2822400, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 3000000, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, + { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, + { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, + { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, + { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, + { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} }; static int cs42l42_pll_config(struct snd_soc_component *component) @@ -618,6 +652,14 @@ static int cs42l42_pll_config(struct snd_soc_component *component) else clk = cs42l42->sclk; + /* Don't reconfigure if there is an audio stream running */ + if (cs42l42->stream_use) { + if (pll_ratio_table[cs42l42->pll_config].sclk == clk) + return 0; + else + return -EBUSY; + } + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { if (pll_ratio_table[i].sclk == clk) { cs42l42->pll_config = i; @@ -631,10 +673,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) 24000000)) << CS42L42_INTERNAL_FS_SHIFT); - snd_soc_component_update_bits(component, CS42L42_MCLK_SRC_SEL, - CS42L42_MCLKDIV_MASK, - (pll_ratio_table[i].mclk_div << - CS42L42_MCLKDIV_SHIFT)); /* Set up the LRCLK */ fsync = clk / cs42l42->srate; if (((fsync * cs42l42->srate) != clk) @@ -668,22 +706,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) CS42L42_FSYNC_PULSE_WIDTH_MASK, CS42L42_FRAC1_VAL(fsync - 1) << CS42L42_FSYNC_PULSE_WIDTH_SHIFT); - /* Set the sample rates (96k or lower) */ - snd_soc_component_update_bits(component, CS42L42_FS_RATE_EN, - CS42L42_FS_EN_MASK, - (CS42L42_FS_EN_IASRC_96K | - CS42L42_FS_EN_OASRC_96K) << - CS42L42_FS_EN_SHIFT); - /* Set the input/output internal MCLK clock ~12 MHz */ - snd_soc_component_update_bits(component, CS42L42_IN_ASRC_CLK, - CS42L42_CLK_IASRC_SEL_MASK, - CS42L42_CLK_IASRC_SEL_12 << - CS42L42_CLK_IASRC_SEL_SHIFT); - snd_soc_component_update_bits(component, - CS42L42_OUT_ASRC_CLK, - CS42L42_CLK_OASRC_SEL_MASK, - CS42L42_CLK_OASRC_SEL_12 << - CS42L42_CLK_OASRC_SEL_SHIFT); if (pll_ratio_table[i].mclk_src_sel == 0) { /* Pass the clock straight through */ snd_soc_component_update_bits(component, @@ -729,10 +751,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component) CS42L42_PLL_DIVOUT_MASK, (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n) << CS42L42_PLL_DIVOUT_SHIFT); - if (pll_ratio_table[i].n != 1) - cs42l42->pll_divout = pll_ratio_table[i].pll_divout; - else - cs42l42->pll_divout = 0; snd_soc_component_update_bits(component, CS42L42_PLL_CAL_RATIO, CS42L42_PLL_CAL_RATIO_MASK, @@ -746,6 +764,39 @@ static int cs42l42_pll_config(struct snd_soc_component *component) return -EINVAL; } +static void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + unsigned int fs; + + /* Don't reconfigure if there is an audio stream running */ + if (cs42l42->stream_use) + return; + + /* SRC MCLK must be as close as possible to 125 * sample rate */ + if (sample_rate <= 48000) + fs = CS42L42_CLK_IASRC_SEL_6; + else + fs = CS42L42_CLK_IASRC_SEL_12; + + /* Set the sample rates (96k or lower) */ + snd_soc_component_update_bits(component, + CS42L42_FS_RATE_EN, + CS42L42_FS_EN_MASK, + (CS42L42_FS_EN_IASRC_96K | + CS42L42_FS_EN_OASRC_96K) << + CS42L42_FS_EN_SHIFT); + + snd_soc_component_update_bits(component, + CS42L42_IN_ASRC_CLK, + CS42L42_CLK_IASRC_SEL_MASK, + fs << CS42L42_CLK_IASRC_SEL_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_OUT_ASRC_CLK, + CS42L42_CLK_OASRC_SEL_MASK, + fs << CS42L42_CLK_OASRC_SEL_SHIFT); +} + static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; @@ -824,7 +875,7 @@ static int cs42l42_dai_startup(struct snd_pcm_substream *substream, struct snd_s /* Machine driver has not set a SCLK, limit bottom end to 44.1 kHz */ return snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, - 44100, 192000); + 44100, 96000); } static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, @@ -836,6 +887,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); unsigned int width = (params_width(params) / 8) - 1; unsigned int val = 0; + int ret; cs42l42->srate = params_rate(params); cs42l42->bclk = snd_soc_params_to_bclk(params); @@ -851,7 +903,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, if (params_width(params) == 24) cs42l42->bclk = (cs42l42->bclk / 3) * 4; - switch(substream->stream) { + switch (substream->stream) { case SNDRV_PCM_STREAM_CAPTURE: /* channel 2 on high LRCLK */ val = CS42L42_ASP_TX_CH2_AP_MASK | @@ -889,7 +941,13 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, break; } - return cs42l42_pll_config(component); + ret = cs42l42_pll_config(component); + if (ret) + return ret; + + cs42l42_src_config(component, params_rate(params)); + + return 0; } static int cs42l42_set_sysclk(struct snd_soc_dai *dai, @@ -933,7 +991,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) CS42L42_HP_ANA_BMUTE_MASK); cs42l42->stream_use &= ~(1 << stream); - if(!cs42l42->stream_use) { + if (!cs42l42->stream_use) { /* * Switch to the internal oscillator. * SCLK must remain running until after this clock switch. @@ -959,12 +1017,13 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, CS42L42_PLL_START_MASK, 1); - if (cs42l42->pll_divout) { + if (pll_ratio_table[cs42l42->pll_config].n > 1) { usleep_range(CS42L42_PLL_DIVOUT_TIME_US, CS42L42_PLL_DIVOUT_TIME_US * 2); + regval = pll_ratio_table[cs42l42->pll_config].pll_divout; snd_soc_component_update_bits(component, CS42L42_PLL_CTL3, CS42L42_PLL_DIVOUT_MASK, - cs42l42->pll_divout << + regval << CS42L42_PLL_DIVOUT_SHIFT); } @@ -1004,7 +1063,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) #define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE ) + SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops cs42l42_ops = { .startup = cs42l42_dai_startup, @@ -1020,14 +1079,14 @@ static struct snd_soc_dai_driver cs42l42_dai = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_8000_96000, .formats = CS42L42_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_8000_96000, .formats = CS42L42_FORMATS, }, .symmetric_rate = 1, @@ -1035,11 +1094,121 @@ static struct snd_soc_dai_driver cs42l42_dai = { .ops = &cs42l42_ops, }; +static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int hs_det_status; + unsigned int hs_det_comp1; + unsigned int hs_det_comp2; + unsigned int hs_det_sw; + + /* Set hs detect to manual, active mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (1 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL1, + CS42L42_HSDET_COMP1_LVL_MASK | + CS42L42_HSDET_COMP2_LVL_MASK, + (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); + + /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); + + msleep(100); + + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT; + hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT; + + /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); + + msleep(100); + + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; + hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; + + /* Use Comparator 1 with 1.25V Threshold. */ + switch (hs_det_comp1) { + case CS42L42_HSDET_COMP_TYPE1: + cs42l42->hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + cs42l42->hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + default: + /* Fallback to Comparator 2 with 1.75V Threshold. */ + switch (hs_det_comp2) { + case CS42L42_HSDET_COMP_TYPE1: + cs42l42->hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + cs42l42->hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + case CS42L42_HSDET_COMP_TYPE3: + cs42l42->hs_type = CS42L42_PLUG_HEADPHONE; + hs_det_sw = CS42L42_HSDET_SW_TYPE3; + break; + default: + cs42l42->hs_type = CS42L42_PLUG_INVALID; + hs_det_sw = CS42L42_HSDET_SW_TYPE4; + break; + } + } + + /* Set Switches */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw); + + /* Set HSDET mode to Manual—Disabled */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL1, + CS42L42_HSDET_COMP1_LVL_MASK | + CS42L42_HSDET_COMP2_LVL_MASK, + (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); +} + static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) { unsigned int hs_det_status; unsigned int int_status; + /* Read and save the hs detection result */ + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + /* Mask the auto detect interrupt */ regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK, @@ -1048,6 +1217,10 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) (1 << CS42L42_PDN_DONE_SHIFT) | (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >> + CS42L42_HSDET_TYPE_SHIFT; + /* Set hs detect to automatic, disabled mode */ regmap_update_bits(cs42l42->regmap, CS42L42_HSDET_CTL2, @@ -1060,11 +1233,15 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) (0 << CS42L42_HSBIAS_REF_SHIFT) | (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); - /* Read and save the hs detection result */ - regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); - - cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >> - CS42L42_HSDET_TYPE_SHIFT; + /* Run Manual detection if auto detect has not found a headset. + * We Re-Run with Manual Detection if the original detection was invalid or headphones, + * to ensure that a headset mic is detected in all cases. + */ + if (cs42l42->hs_type == CS42L42_PLUG_INVALID || + cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) { + dev_dbg(cs42l42->dev, "Running Manual Detection Fallback\n"); + cs42l42_manual_hs_type_detect(cs42l42); + } /* Set up button detection */ if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) || @@ -1107,10 +1284,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Turn on level detect circuitry */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1137,10 +1312,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); } @@ -1161,12 +1334,8 @@ static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) /* Unmask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (0 << CS42L42_TS_PLUG_SHIFT) | (0 << CS42L42_TS_UNPLUG_SHIFT)); } @@ -1176,22 +1345,16 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Mask tip sense interrupts */ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, - CS42L42_RS_PLUG_MASK | - CS42L42_RS_UNPLUG_MASK | CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK, - (1 << CS42L42_RS_PLUG_SHIFT) | - (1 << CS42L42_RS_UNPLUG_SHIFT) | (1 << CS42L42_TS_PLUG_SHIFT) | (1 << CS42L42_TS_UNPLUG_SHIFT)); /* Make sure button detect and HS bias circuits are off */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1235,10 +1398,8 @@ static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) /* Power up HS bias to 2.7V */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (3 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1285,10 +1446,8 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) /* Ground HS bias */ regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, - CS42L42_DETECT_MODE_MASK | CS42L42_HSBIAS_CTL_MASK | CS42L42_PDN_MIC_LVL_DET_MASK, - (0 << CS42L42_DETECT_MODE_SHIFT) | (1 << CS42L42_HSBIAS_CTL_SHIFT) | (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); @@ -1361,19 +1520,19 @@ static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42) switch (bias_level) { case 1: /* Function C button press */ bias_level = SND_JACK_BTN_2; - dev_dbg(cs42l42->component->dev, "Function C button press\n"); + dev_dbg(cs42l42->dev, "Function C button press\n"); break; case 2: /* Function B button press */ bias_level = SND_JACK_BTN_1; - dev_dbg(cs42l42->component->dev, "Function B button press\n"); + dev_dbg(cs42l42->dev, "Function B button press\n"); break; case 3: /* Function D button press */ bias_level = SND_JACK_BTN_3; - dev_dbg(cs42l42->component->dev, "Function D button press\n"); + dev_dbg(cs42l42->dev, "Function D button press\n"); break; case 4: /* Function A button press */ bias_level = SND_JACK_BTN_0; - dev_dbg(cs42l42->component->dev, "Function A button press\n"); + dev_dbg(cs42l42->dev, "Function A button press\n"); break; default: bias_level = 0; @@ -1447,7 +1606,6 @@ static const struct cs42l42_irq_params irq_params_table[] = { static irqreturn_t cs42l42_irq_thread(int irq, void *data) { struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data; - struct snd_soc_component *component = cs42l42->component; unsigned int stickies[12]; unsigned int masks[12]; unsigned int current_plug_status; @@ -1477,11 +1635,13 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) CS42L42_M_DETECT_FT_MASK | CS42L42_M_HSBIAS_HIZ_MASK); + mutex_lock(&cs42l42->jack_detect_mutex); + /* Check auto-detect status */ if ((~masks[5]) & irq_params_table[5].mask) { if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { cs42l42_process_hs_type_detect(cs42l42); - switch(cs42l42->hs_type){ + switch (cs42l42->hs_type) { case CS42L42_PLUG_CTIA: case CS42L42_PLUG_OMTP: snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET, @@ -1494,7 +1654,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) default: break; } - dev_dbg(component->dev, "Auto detect done (%d)\n", cs42l42->hs_type); + dev_dbg(cs42l42->dev, "Auto detect done (%d)\n", cs42l42->hs_type); } } @@ -1513,22 +1673,12 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) cs42l42->plug_state = CS42L42_TS_UNPLUG; cs42l42_cancel_hs_type_detect(cs42l42); - switch(cs42l42->hs_type){ - case CS42L42_PLUG_CTIA: - case CS42L42_PLUG_OMTP: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADSET); - break; - case CS42L42_PLUG_HEADPHONE: - snd_soc_jack_report(cs42l42->jack, 0, SND_JACK_HEADPHONE); - break; - default: - break; - } snd_soc_jack_report(cs42l42->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3); - dev_dbg(component->dev, "Unplug event\n"); + dev_dbg(cs42l42->dev, "Unplug event\n"); } break; @@ -1544,7 +1694,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) CS42L42_M_HSBIAS_HIZ_MASK)) { if (current_button_status & CS42L42_M_DETECT_TF_MASK) { - dev_dbg(component->dev, "Button released\n"); + dev_dbg(cs42l42->dev, "Button released\n"); report = 0; } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) { report = cs42l42_handle_button_press(cs42l42); @@ -1555,6 +1705,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data) } } + mutex_unlock(&cs42l42->jack_detect_mutex); + return IRQ_HANDLED; } @@ -1657,8 +1809,8 @@ static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42) CS42L42_TS_UNPLUG_MASK, (1 << CS42L42_RS_PLUG_SHIFT) | (1 << CS42L42_RS_UNPLUG_SHIFT) | - (1 << CS42L42_TS_PLUG_SHIFT) | - (1 << CS42L42_TS_UNPLUG_SHIFT)); + (0 << CS42L42_TS_PLUG_SHIFT) | + (0 << CS42L42_TS_UNPLUG_SHIFT)); } static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) @@ -1667,6 +1819,9 @@ static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) cs42l42->hs_type = CS42L42_PLUG_INVALID; + regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK, 0); + /* Latch analog controls to VP power domain */ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_LATCH_TO_VP_MASK | @@ -1897,7 +2052,9 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, if (!cs42l42) return -ENOMEM; + cs42l42->dev = &i2c_client->dev; i2c_set_clientdata(i2c_client, cs42l42); + mutex_init(&cs42l42->jack_detect_mutex); cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); if (IS_ERR(cs42l42->regmap)) { @@ -1931,7 +2088,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, "reset", GPIOD_OUT_LOW); if (IS_ERR(cs42l42->reset_gpio)) { ret = PTR_ERR(cs42l42->reset_gpio); - goto err_disable; + goto err_disable_noreset; } if (cs42l42->reset_gpio) { @@ -1940,17 +2097,20 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, } usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); - /* Request IRQ */ - ret = devm_request_threaded_irq(&i2c_client->dev, - i2c_client->irq, - NULL, cs42l42_irq_thread, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "cs42l42", cs42l42); - if (ret == -EPROBE_DEFER) - goto err_disable; - else if (ret != 0) - dev_err(&i2c_client->dev, - "Failed to request IRQ: %d\n", ret); + /* Request IRQ if one was specified */ + if (i2c_client->irq) { + ret = request_threaded_irq(i2c_client->irq, + NULL, cs42l42_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs42l42", cs42l42); + if (ret == -EPROBE_DEFER) { + goto err_disable_noirq; + } else if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request IRQ: %d\n", ret); + goto err_disable_noirq; + } + } /* initialize codec */ devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB); @@ -1971,7 +2131,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, ret = regmap_read(cs42l42->regmap, CS42L42_REVID, ®); if (ret < 0) { dev_err(&i2c_client->dev, "Get Revision ID failed\n"); - goto err_disable; + goto err_shutdown; } dev_info(&i2c_client->dev, @@ -1996,7 +2156,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, ret = cs42l42_handle_device_data(&i2c_client->dev, cs42l42); if (ret != 0) - goto err_disable; + goto err_shutdown; /* Setup headset detection */ cs42l42_setup_hs_type_detect(cs42l42); @@ -2008,10 +2168,22 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client, ret = devm_snd_soc_register_component(&i2c_client->dev, &soc_component_dev_cs42l42, &cs42l42_dai, 1); if (ret < 0) - goto err_disable; + goto err_shutdown; + return 0; +err_shutdown: + regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); + err_disable: + if (i2c_client->irq) + free_irq(i2c_client->irq, cs42l42); + +err_disable_noirq: + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); +err_disable_noreset: regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); return ret; @@ -2021,60 +2193,23 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client) { struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client); - devm_free_irq(&i2c_client->dev, i2c_client->irq, cs42l42); - pm_runtime_suspend(&i2c_client->dev); - pm_runtime_disable(&i2c_client->dev); + if (i2c_client->irq) + free_irq(i2c_client->irq, cs42l42); - return 0; -} + /* + * The driver might not have control of reset and power supplies, + * so ensure that the chip internals are powered down. + */ + regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); -#ifdef CONFIG_PM -static int cs42l42_runtime_suspend(struct device *dev) -{ - struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); - - regcache_cache_only(cs42l42->regmap, true); - regcache_mark_dirty(cs42l42->regmap); - - /* Hold down reset */ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); - - /* remove power */ - regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), - cs42l42->supplies); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); return 0; } -static int cs42l42_runtime_resume(struct device *dev) -{ - struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); - int ret; - - /* Enable power */ - ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), - cs42l42->supplies); - if (ret != 0) { - dev_err(dev, "Failed to enable supplies: %d\n", - ret); - return ret; - } - - gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); - usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); - - regcache_cache_only(cs42l42->regmap, false); - regcache_sync(cs42l42->regmap); - - return 0; -} -#endif - -static const struct dev_pm_ops cs42l42_runtime_pm = { - SET_RUNTIME_PM_OPS(cs42l42_runtime_suspend, cs42l42_runtime_resume, - NULL) -}; - #ifdef CONFIG_OF static const struct of_device_id cs42l42_of_match[] = { { .compatible = "cirrus,cs42l42", }, @@ -2101,7 +2236,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l42_id); static struct i2c_driver cs42l42_i2c_driver = { .driver = { .name = "cs42l42", - .pm = &cs42l42_runtime_pm, .of_match_table = of_match_ptr(cs42l42_of_match), .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 8734f6828f..9fff183dce 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -12,6 +12,7 @@ #ifndef __CS42L42_H__ #define __CS42L42_H__ +#include #include #define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */ @@ -62,6 +63,9 @@ #define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT) #define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A) +#define CS42L42_SLOW_START_ENABLE (CS42L42_PAGE_10 + 0x0B) +#define CS42L42_SLOW_START_EN_MASK GENMASK(6, 4) +#define CS42L42_SLOW_START_EN_SHIFT 4 #define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E) #define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F) #define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10) @@ -188,6 +192,11 @@ #define CS42L42_HSDET_COMP2_LVL_SHIFT 4 #define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT) +#define CS42L42_HSDET_COMP1_LVL_VAL 12 /* 1.25V Comparator */ +#define CS42L42_HSDET_COMP2_LVL_VAL 2 /* 1.75V Comparator */ +#define CS42L42_HSDET_COMP1_LVL_DEFAULT 7 /* 1V Comparator */ +#define CS42L42_HSDET_COMP2_LVL_DEFAULT 7 /* 2V Comparator */ + #define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20) #define CS42L42_HSDET_AUTO_TIME_SHIFT 0 #define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT) @@ -228,6 +237,60 @@ #define CS42L42_PLUG_HEADPHONE 2 #define CS42L42_PLUG_INVALID 3 +#define CS42L42_HSDET_SW_COMP1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (0 << CS42L42_SW_REF_HS4_SHIFT) | \ + (1 << CS42L42_SW_REF_HS3_SHIFT)) +#define CS42L42_HSDET_SW_COMP2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (1 << CS42L42_SW_REF_HS4_SHIFT) | \ + (0 << CS42L42_SW_REF_HS3_SHIFT)) +#define CS42L42_HSDET_SW_TYPE1 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (0 << CS42L42_SW_REF_HS4_SHIFT) | \ + (1 << CS42L42_SW_REF_HS3_SHIFT)) +#define CS42L42_HSDET_SW_TYPE2 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (0 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (1 << CS42L42_SW_REF_HS4_SHIFT) | \ + (0 << CS42L42_SW_REF_HS3_SHIFT)) +#define CS42L42_HSDET_SW_TYPE3 ((1 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (1 << CS42L42_SW_REF_HS4_SHIFT) | \ + (1 << CS42L42_SW_REF_HS3_SHIFT)) +#define CS42L42_HSDET_SW_TYPE4 ((0 << CS42L42_SW_GNDHS_HS4_SHIFT) | \ + (1 << CS42L42_SW_GNDHS_HS3_SHIFT) | \ + (1 << CS42L42_SW_HSB_HS4_SHIFT) | \ + (0 << CS42L42_SW_HSB_HS3_SHIFT) | \ + (0 << CS42L42_SW_HSB_FILT_HS4_SHIFT) | \ + (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT) | \ + (0 << CS42L42_SW_REF_HS4_SHIFT) | \ + (1 << CS42L42_SW_REF_HS3_SHIFT)) + +#define CS42L42_HSDET_COMP_TYPE1 1 +#define CS42L42_HSDET_COMP_TYPE2 2 +#define CS42L42_HSDET_COMP_TYPE3 0 +#define CS42L42_HSDET_COMP_TYPE4 3 + #define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29) #define CS42L42_HS_CLAMP_DISABLE_SHIFT 0 #define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT) @@ -288,6 +351,7 @@ #define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A) #define CS42L42_CLK_IASRC_SEL_SHIFT 0 #define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT) +#define CS42L42_CLK_IASRC_SEL_6 0 #define CS42L42_CLK_IASRC_SEL_12 1 #define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B) @@ -761,6 +825,7 @@ #define CS42L42_CLOCK_SWITCH_DELAY_US 150 #define CS42L42_PLL_LOCK_POLL_US 250 #define CS42L42_PLL_LOCK_TIMEOUT_US 1250 +#define CS42L42_HP_ADC_EN_TIME_US 20000 static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { "VA", @@ -772,16 +837,16 @@ static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = { struct cs42l42_private { struct regmap *regmap; - struct snd_soc_component *component; + struct device *dev; struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES]; struct gpio_desc *reset_gpio; struct completion pdn_done; struct snd_soc_jack *jack; + struct mutex jack_detect_mutex; int pll_config; int bclk; u32 sclk; u32 srate; - u8 pll_divout; u8 plug_state; u8 hs_type; u8 ts_inv; @@ -794,6 +859,7 @@ struct cs42l42_private { u8 hs_bias_ramp_time; u8 hs_bias_sense_en; u8 stream_use; + bool hp_adc_up_pending; }; #endif /* __CS42L42_H__ */ diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index 1ee83160b8..391fd7da33 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -45,7 +45,7 @@ struct cs47l15 { bool in1_lp_mode; }; -static const struct wm_adsp_region cs47l15_dsp1_regions[] = { +static const struct cs_dsp_region cs47l15_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x080000 }, { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, @@ -1402,18 +1402,18 @@ static int cs47l15_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); cs47l15->core.adsp[0].part = "cs47l15"; - cs47l15->core.adsp[0].num = 1; - cs47l15->core.adsp[0].type = WMFW_ADSP2; - cs47l15->core.adsp[0].rev = 2; - cs47l15->core.adsp[0].dev = madera->dev; - cs47l15->core.adsp[0].regmap = madera->regmap_32bit; + cs47l15->core.adsp[0].cs_dsp.num = 1; + cs47l15->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + cs47l15->core.adsp[0].cs_dsp.rev = 2; + cs47l15->core.adsp[0].cs_dsp.dev = madera->dev; + cs47l15->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit; - cs47l15->core.adsp[0].base = MADERA_DSP1_CONFIG_1; - cs47l15->core.adsp[0].mem = cs47l15_dsp1_regions; - cs47l15->core.adsp[0].num_mems = ARRAY_SIZE(cs47l15_dsp1_regions); + cs47l15->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1; + cs47l15->core.adsp[0].cs_dsp.mem = cs47l15_dsp1_regions; + cs47l15->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l15_dsp1_regions); - cs47l15->core.adsp[0].lock_regions = - WM_ADSP2_REGION_1 | WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3; + cs47l15->core.adsp[0].cs_dsp.lock_regions = + CS_ADSP2_REGION_1 | CS_ADSP2_REGION_2 | CS_ADSP2_REGION_3; ret = wm_adsp2_init(&cs47l15->core.adsp[0]); if (ret != 0) diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 6b6d088160..6356f81aaf 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -37,21 +37,21 @@ struct cs47l24_priv { struct arizona_fll fll[2]; }; -static const struct wm_adsp_region cs47l24_dsp2_regions[] = { +static const struct cs_dsp_region cs47l24_dsp2_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x200000 }, { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, { .type = WMFW_ADSP2_XM, .base = 0x290000 }, { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, }; -static const struct wm_adsp_region cs47l24_dsp3_regions[] = { +static const struct cs_dsp_region cs47l24_dsp3_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x300000 }, { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, { .type = WMFW_ADSP2_XM, .base = 0x390000 }, { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, }; -static const struct wm_adsp_region *cs47l24_dsp_regions[] = { +static const struct cs_dsp_region *cs47l24_dsp_regions[] = { cs47l24_dsp2_regions, cs47l24_dsp3_regions, }; @@ -1234,15 +1234,15 @@ static int cs47l24_probe(struct platform_device *pdev) for (i = 1; i <= 2; i++) { cs47l24->core.adsp[i].part = "cs47l24"; - cs47l24->core.adsp[i].num = i + 1; - cs47l24->core.adsp[i].type = WMFW_ADSP2; - cs47l24->core.adsp[i].dev = arizona->dev; - cs47l24->core.adsp[i].regmap = arizona->regmap; + cs47l24->core.adsp[i].cs_dsp.num = i + 1; + cs47l24->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l24->core.adsp[i].cs_dsp.dev = arizona->dev; + cs47l24->core.adsp[i].cs_dsp.regmap = arizona->regmap; - cs47l24->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 + + cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 + (0x100 * i); - cs47l24->core.adsp[i].mem = cs47l24_dsp_regions[i - 1]; - cs47l24->core.adsp[i].num_mems = + cs47l24->core.adsp[i].cs_dsp.mem = cs47l24_dsp_regions[i - 1]; + cs47l24->core.adsp[i].cs_dsp.num_mems = ARRAY_SIZE(cs47l24_dsp2_regions); ret = wm_adsp2_init(&cs47l24->core.adsp[i]); diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index 3f04a2a745..db2f844b8b 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -37,28 +37,28 @@ struct cs47l35 { struct madera_fll fll; }; -static const struct wm_adsp_region cs47l35_dsp1_regions[] = { +static const struct cs_dsp_region cs47l35_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x080000 }, { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, }; -static const struct wm_adsp_region cs47l35_dsp2_regions[] = { +static const struct cs_dsp_region cs47l35_dsp2_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, { .type = WMFW_ADSP2_XM, .base = 0x120000 }, { .type = WMFW_ADSP2_YM, .base = 0x140000 }, }; -static const struct wm_adsp_region cs47l35_dsp3_regions[] = { +static const struct cs_dsp_region cs47l35_dsp3_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x180000 }, { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, }; -static const struct wm_adsp_region *cs47l35_dsp_regions[] = { +static const struct cs_dsp_region *cs47l35_dsp_regions[] = { cs47l35_dsp1_regions, cs47l35_dsp2_regions, cs47l35_dsp3_regions, @@ -1686,15 +1686,15 @@ static int cs47l35_probe(struct platform_device *pdev) for (i = 0; i < CS47L35_NUM_ADSP; i++) { cs47l35->core.adsp[i].part = "cs47l35"; - cs47l35->core.adsp[i].num = i + 1; - cs47l35->core.adsp[i].type = WMFW_ADSP2; - cs47l35->core.adsp[i].rev = 1; - cs47l35->core.adsp[i].dev = madera->dev; - cs47l35->core.adsp[i].regmap = madera->regmap_32bit; + cs47l35->core.adsp[i].cs_dsp.num = i + 1; + cs47l35->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l35->core.adsp[i].cs_dsp.rev = 1; + cs47l35->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l35->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; - cs47l35->core.adsp[i].base = wm_adsp2_control_bases[i]; - cs47l35->core.adsp[i].mem = cs47l35_dsp_regions[i]; - cs47l35->core.adsp[i].num_mems = + cs47l35->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i]; + cs47l35->core.adsp[i].cs_dsp.mem = cs47l35_dsp_regions[i]; + cs47l35->core.adsp[i].cs_dsp.num_mems = ARRAY_SIZE(cs47l35_dsp1_regions); ret = wm_adsp2_init(&cs47l35->core.adsp[i]); diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index 748a180870..d4fedc5ad5 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -37,56 +37,56 @@ struct cs47l85 { struct madera_fll fll[3]; }; -static const struct wm_adsp_region cs47l85_dsp1_regions[] = { +static const struct cs_dsp_region cs47l85_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x080000 }, { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, }; -static const struct wm_adsp_region cs47l85_dsp2_regions[] = { +static const struct cs_dsp_region cs47l85_dsp2_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, { .type = WMFW_ADSP2_XM, .base = 0x120000 }, { .type = WMFW_ADSP2_YM, .base = 0x140000 }, }; -static const struct wm_adsp_region cs47l85_dsp3_regions[] = { +static const struct cs_dsp_region cs47l85_dsp3_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x180000 }, { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, }; -static const struct wm_adsp_region cs47l85_dsp4_regions[] = { +static const struct cs_dsp_region cs47l85_dsp4_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x200000 }, { .type = WMFW_ADSP2_ZM, .base = 0x260000 }, { .type = WMFW_ADSP2_XM, .base = 0x220000 }, { .type = WMFW_ADSP2_YM, .base = 0x240000 }, }; -static const struct wm_adsp_region cs47l85_dsp5_regions[] = { +static const struct cs_dsp_region cs47l85_dsp5_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x280000 }, { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x2a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x2c0000 }, }; -static const struct wm_adsp_region cs47l85_dsp6_regions[] = { +static const struct cs_dsp_region cs47l85_dsp6_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x300000 }, { .type = WMFW_ADSP2_ZM, .base = 0x360000 }, { .type = WMFW_ADSP2_XM, .base = 0x320000 }, { .type = WMFW_ADSP2_YM, .base = 0x340000 }, }; -static const struct wm_adsp_region cs47l85_dsp7_regions[] = { +static const struct cs_dsp_region cs47l85_dsp7_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x380000 }, { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x3a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x3c0000 }, }; -static const struct wm_adsp_region *cs47l85_dsp_regions[] = { +static const struct cs_dsp_region *cs47l85_dsp_regions[] = { cs47l85_dsp1_regions, cs47l85_dsp2_regions, cs47l85_dsp3_regions, @@ -2632,15 +2632,15 @@ static int cs47l85_probe(struct platform_device *pdev) for (i = 0; i < CS47L85_NUM_ADSP; i++) { cs47l85->core.adsp[i].part = "cs47l85"; - cs47l85->core.adsp[i].num = i + 1; - cs47l85->core.adsp[i].type = WMFW_ADSP2; - cs47l85->core.adsp[i].rev = 1; - cs47l85->core.adsp[i].dev = madera->dev; - cs47l85->core.adsp[i].regmap = madera->regmap_32bit; + cs47l85->core.adsp[i].cs_dsp.num = i + 1; + cs47l85->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l85->core.adsp[i].cs_dsp.rev = 1; + cs47l85->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l85->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; - cs47l85->core.adsp[i].base = wm_adsp2_control_bases[i]; - cs47l85->core.adsp[i].mem = cs47l85_dsp_regions[i]; - cs47l85->core.adsp[i].num_mems = + cs47l85->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i]; + cs47l85->core.adsp[i].cs_dsp.mem = cs47l85_dsp_regions[i]; + cs47l85->core.adsp[i].cs_dsp.num_mems = ARRAY_SIZE(cs47l85_dsp1_regions); ret = wm_adsp2_init(&cs47l85->core.adsp[i]); diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index d2911c014b..5aec937a24 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -37,56 +37,56 @@ struct cs47l90 { struct madera_fll fll[3]; }; -static const struct wm_adsp_region cs47l90_dsp1_regions[] = { +static const struct cs_dsp_region cs47l90_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x080000 }, { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, }; -static const struct wm_adsp_region cs47l90_dsp2_regions[] = { +static const struct cs_dsp_region cs47l90_dsp2_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, { .type = WMFW_ADSP2_XM, .base = 0x120000 }, { .type = WMFW_ADSP2_YM, .base = 0x140000 }, }; -static const struct wm_adsp_region cs47l90_dsp3_regions[] = { +static const struct cs_dsp_region cs47l90_dsp3_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x180000 }, { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, }; -static const struct wm_adsp_region cs47l90_dsp4_regions[] = { +static const struct cs_dsp_region cs47l90_dsp4_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x200000 }, { .type = WMFW_ADSP2_ZM, .base = 0x260000 }, { .type = WMFW_ADSP2_XM, .base = 0x220000 }, { .type = WMFW_ADSP2_YM, .base = 0x240000 }, }; -static const struct wm_adsp_region cs47l90_dsp5_regions[] = { +static const struct cs_dsp_region cs47l90_dsp5_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x280000 }, { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x2a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x2c0000 }, }; -static const struct wm_adsp_region cs47l90_dsp6_regions[] = { +static const struct cs_dsp_region cs47l90_dsp6_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x300000 }, { .type = WMFW_ADSP2_ZM, .base = 0x360000 }, { .type = WMFW_ADSP2_XM, .base = 0x320000 }, { .type = WMFW_ADSP2_YM, .base = 0x340000 }, }; -static const struct wm_adsp_region cs47l90_dsp7_regions[] = { +static const struct cs_dsp_region cs47l90_dsp7_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x380000 }, { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x3a0000 }, { .type = WMFW_ADSP2_YM, .base = 0x3c0000 }, }; -static const struct wm_adsp_region *cs47l90_dsp_regions[] = { +static const struct cs_dsp_region *cs47l90_dsp_regions[] = { cs47l90_dsp1_regions, cs47l90_dsp2_regions, cs47l90_dsp3_regions, @@ -2543,18 +2543,18 @@ static int cs47l90_probe(struct platform_device *pdev) for (i = 0; i < CS47L90_NUM_ADSP; i++) { cs47l90->core.adsp[i].part = "cs47l90"; - cs47l90->core.adsp[i].num = i + 1; - cs47l90->core.adsp[i].type = WMFW_ADSP2; - cs47l90->core.adsp[i].rev = 2; - cs47l90->core.adsp[i].dev = madera->dev; - cs47l90->core.adsp[i].regmap = madera->regmap_32bit; + cs47l90->core.adsp[i].cs_dsp.num = i + 1; + cs47l90->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l90->core.adsp[i].cs_dsp.rev = 2; + cs47l90->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l90->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; - cs47l90->core.adsp[i].base = cs47l90_dsp_control_bases[i]; - cs47l90->core.adsp[i].mem = cs47l90_dsp_regions[i]; - cs47l90->core.adsp[i].num_mems = + cs47l90->core.adsp[i].cs_dsp.base = cs47l90_dsp_control_bases[i]; + cs47l90->core.adsp[i].cs_dsp.mem = cs47l90_dsp_regions[i]; + cs47l90->core.adsp[i].cs_dsp.num_mems = ARRAY_SIZE(cs47l90_dsp1_regions); - cs47l90->core.adsp[i].lock_regions = WM_ADSP2_REGION_1_9; + cs47l90->core.adsp[i].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9; ret = wm_adsp2_init(&cs47l90->core.adsp[i]); diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index 1a0280416d..a1b8dcdb9f 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -37,7 +37,7 @@ struct cs47l92 { struct madera_fll fll[2]; }; -static const struct wm_adsp_region cs47l92_dsp1_regions[] = { +static const struct cs_dsp_region cs47l92_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x080000 }, { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, @@ -2002,17 +2002,17 @@ static int cs47l92_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); cs47l92->core.adsp[0].part = "cs47l92"; - cs47l92->core.adsp[0].num = 1; - cs47l92->core.adsp[0].type = WMFW_ADSP2; - cs47l92->core.adsp[0].rev = 2; - cs47l92->core.adsp[0].dev = madera->dev; - cs47l92->core.adsp[0].regmap = madera->regmap_32bit; + cs47l92->core.adsp[0].cs_dsp.num = 1; + cs47l92->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + cs47l92->core.adsp[0].cs_dsp.rev = 2; + cs47l92->core.adsp[0].cs_dsp.dev = madera->dev; + cs47l92->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit; - cs47l92->core.adsp[0].base = MADERA_DSP1_CONFIG_1; - cs47l92->core.adsp[0].mem = cs47l92_dsp1_regions; - cs47l92->core.adsp[0].num_mems = ARRAY_SIZE(cs47l92_dsp1_regions); + cs47l92->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1; + cs47l92->core.adsp[0].cs_dsp.mem = cs47l92_dsp1_regions; + cs47l92->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l92_dsp1_regions); - cs47l92->core.adsp[0].lock_regions = WM_ADSP2_REGION_1_9; + cs47l92->core.adsp[0].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9; ret = wm_adsp2_init(&cs47l92->core.adsp[0]); if (ret != 0) diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 13258f3ca9..1af0bf5f1e 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -252,10 +252,9 @@ static void v253_close(struct tty_struct *tty) } /* Line discipline .hangup() */ -static int v253_hangup(struct tty_struct *tty) +static void v253_hangup(struct tty_struct *tty) { v253_close(tty); - return 0; } /* Line discipline .receive_buf() */ diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c index 2344a0b035..9f20bfb855 100644 --- a/sound/soc/codecs/es7241.c +++ b/sound/soc/codecs/es7241.c @@ -255,7 +255,6 @@ static int es7241_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct es7241_data *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -271,28 +270,19 @@ static int es7241_probe(struct platform_device *pdev) es7241_parse_fmt(dev, priv); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - err = PTR_ERR(priv->reset); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'reset' gpio: %d", err); - return err; - } + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "Failed to get 'reset' gpio"); priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); - if (IS_ERR(priv->m0)) { - err = PTR_ERR(priv->m0); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm0' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m0)) + return dev_err_probe(dev, PTR_ERR(priv->m0), + "Failed to get 'm0' gpio"); priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); - if (IS_ERR(priv->m1)) { - err = PTR_ERR(priv->m1); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'm1' gpio: %d", err); - return err; - } + if (IS_ERR(priv->m1)) + return dev_err_probe(dev, PTR_ERR(priv->m1), + "Failed to get 'm1' gpio"); return devm_snd_soc_register_component(&pdev->dev, &es7241_component_driver, diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 5fb02635c1..8f30a3ea8b 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -840,6 +840,7 @@ MODULE_DEVICE_TABLE(of, es8316_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id es8316_acpi_match[] = { {"ESSX8316", 0}, + {"ESSX8336", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, es8316_acpi_match); diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 390dd6c7f6..de5955db0a 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -46,9 +46,8 @@ static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width); +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, + int direction); static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); @@ -58,7 +57,7 @@ static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .prepare = hdac_hda_dai_prepare, .hw_params = hdac_hda_dai_hw_params, .hw_free = hdac_hda_dai_hw_free, - .set_tdm_slot = hdac_hda_dai_set_tdm_slot, + .set_stream = hdac_hda_dai_set_stream, }; static struct snd_soc_dai_driver hdac_hda_dais[] = { @@ -180,21 +179,22 @@ static struct snd_soc_dai_driver hdac_hda_dais[] = { }; -static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, unsigned int rx_mask, - int slots, int slot_width) +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) { struct snd_soc_component *component = dai->component; struct hdac_hda_priv *hda_pvt; struct hdac_hda_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; hda_pvt = snd_soc_component_get_drvdata(component); pcm = &hda_pvt->pcm[dai->id]; + hstream = (struct hdac_stream *)stream; - if (tx_mask) - pcm->stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; - else - pcm->stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + pcm->stream_tag[direction] = hstream->stream_tag; return 0; } diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c index 6b60120f59..1d0c467ab5 100644 --- a/sound/soc/codecs/jz4770.c +++ b/sound/soc/codecs/jz4770.c @@ -307,6 +307,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); /* Unconditional controls. */ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { @@ -319,6 +320,14 @@ static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4770_CODEC_REG_GCR_MIXADC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4770_CODEC_REG_GCR_MIXDAC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), }; static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index 1c0409350e..6ffe88345d 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -3531,7 +3531,7 @@ static int rx_macro_probe(struct platform_device *pdev) rx->clks[3].id = "npl"; rx->clks[4].id = "fsgen"; - ret = devm_clk_bulk_get(dev, RX_NUM_CLKS_MAX, rx->clks); + ret = devm_clk_bulk_get_optional(dev, RX_NUM_CLKS_MAX, rx->clks); if (ret) { dev_err(dev, "Error getting RX Clocks (%d)\n", ret); return ret; @@ -3577,6 +3577,7 @@ static int rx_macro_remove(struct platform_device *pdev) } static const struct of_device_id rx_macro_dt_match[] = { + { .compatible = "qcom,sc7280-lpass-rx-macro" }, { .compatible = "qcom,sm8250-lpass-rx-macro" }, { } }; diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 27a0d5defd..a4c0a155af 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -272,7 +272,7 @@ struct tx_macro { static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); -static const struct reg_default tx_defaults[] = { +static struct reg_default tx_defaults[] = { /* TX Macro */ { CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, { CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, @@ -1674,6 +1674,9 @@ static int tx_macro_component_probe(struct snd_soc_component *comp) snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F, 0x0A); + /* Enable swr mic0 and mic1 clock */ + snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00); + snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00); return 0; } @@ -1778,9 +1781,10 @@ static const struct snd_soc_component_driver tx_macro_component_drv = { static int tx_macro_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct tx_macro *tx; void __iomem *base; - int ret; + int ret, reg; tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL); if (!tx) @@ -1792,7 +1796,7 @@ static int tx_macro_probe(struct platform_device *pdev) tx->clks[3].id = "npl"; tx->clks[4].id = "fsgen"; - ret = devm_clk_bulk_get(dev, TX_NUM_CLKS_MAX, tx->clks); + ret = devm_clk_bulk_get_optional(dev, TX_NUM_CLKS_MAX, tx->clks); if (ret) { dev_err(dev, "Error getting RX Clocks (%d)\n", ret); return ret; @@ -1802,6 +1806,20 @@ static int tx_macro_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + /* Update defaults for lpass sc7280 */ + if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) { + for (reg = 0; reg < ARRAY_SIZE(tx_defaults); reg++) { + switch (tx_defaults[reg].reg) { + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: + tx_defaults[reg].def = 0x0E; + break; + default: + break; + } + } + } + tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config); dev_set_drvdata(dev, tx); @@ -1843,6 +1861,7 @@ static int tx_macro_remove(struct platform_device *pdev) } static const struct of_device_id tx_macro_dt_match[] = { + { .compatible = "qcom,sc7280-lpass-tx-macro" }, { .compatible = "qcom,sm8250-lpass-tx-macro" }, { } }; diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 56c93f4465..11147e3568 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -1408,7 +1408,7 @@ static int va_macro_probe(struct platform_device *pdev) va->clks[1].id = "dcodec"; va->clks[2].id = "mclk"; - ret = devm_clk_bulk_get(dev, VA_NUM_CLKS_MAX, va->clks); + ret = devm_clk_bulk_get_optional(dev, VA_NUM_CLKS_MAX, va->clks); if (ret) { dev_err(dev, "Error getting VA Clocks (%d)\n", ret); return ret; @@ -1472,6 +1472,7 @@ static int va_macro_remove(struct platform_device *pdev) } static const struct of_device_id va_macro_dt_match[] = { + { .compatible = "qcom,sc7280-lpass-va-macro" }, { .compatible = "qcom,sm8250-lpass-va-macro" }, {} }; diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index d3ac318fd6..75baf8eb70 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -2445,6 +2445,7 @@ static int wsa_macro_remove(struct platform_device *pdev) } static const struct of_device_id wsa_macro_dt_match[] = { + {.compatible = "qcom,sc7280-lpass-wsa-macro"}, {.compatible = "qcom,sm8250-lpass-wsa-macro"}, {} }; diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c index f4ed7e0467..272041c623 100644 --- a/sound/soc/codecs/madera.c +++ b/sound/soc/codecs/madera.c @@ -905,7 +905,7 @@ static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, */ mutex_lock(&priv->rate_lock); - if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].base)) { + 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); @@ -964,7 +964,7 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv, unsigned int mask = MADERA_DSP_RATE_MASK; int ret; - val = priv->adsp_rate_cache[dsp->num - 1] << MADERA_DSP_RATE_SHIFT; + val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT; switch (priv->madera->type) { case CS47L35: @@ -978,15 +978,15 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv, /* Configure exact dsp frequency */ dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq); - ret = regmap_write(dsp->regmap, - dsp->base + MADERA_DSP_CONFIG_2_OFFS, freq); + ret = regmap_write(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq); if (ret) goto err; break; } - ret = regmap_update_bits(dsp->regmap, - dsp->base + MADERA_DSP_CONFIG_1_OFFS, + ret = regmap_update_bits(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS, mask, val); if (ret) goto err; @@ -996,7 +996,7 @@ static int madera_write_adsp_clk_setting(struct madera_priv *priv, return 0; err: - dev_err(dsp->dev, "Failed to set DSP%d clock: %d\n", dsp->num, ret); + dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret); return ret; } @@ -1018,7 +1018,7 @@ int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num, * changes are locked out by the domain_group_ref reference count. */ - ret = regmap_read(dsp->regmap, dsp->base, &cur); + ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur); if (ret) { dev_err(madera->dev, "Failed to read current DSP rate: %d\n", ret); @@ -1027,7 +1027,7 @@ int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num, cur &= MADERA_DSP_RATE_MASK; - new = priv->adsp_rate_cache[dsp->num - 1] << MADERA_DSP_RATE_SHIFT; + new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT; if (new == cur) { dev_dbg(madera->dev, "DSP rate not changed\n"); diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c index 0c261335c8..bc57d7687f 100644 --- a/sound/soc/codecs/max9759.c +++ b/sound/soc/codecs/max9759.c @@ -141,7 +141,6 @@ static int max9759_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct max9759 *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -150,29 +149,20 @@ static int max9759_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio"); priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_mute)) { - err = PTR_ERR(priv->gpiod_mute); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mute' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mute)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), + "Failed to get 'mute' gpio"); priv->is_mute = true; priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH); - if (IS_ERR(priv->gpiod_gain)) { - err = PTR_ERR(priv->gpiod_gain); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'gain' gpios: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_gain)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain), + "Failed to get 'gain' gpios"); priv->gain = 0; if (priv->gpiod_gain->ndescs != 2) { diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index dc520effc6..f47e956d4f 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -741,7 +741,7 @@ static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops max98373_dai_sdw_ops = { .hw_params = max98373_sdw_dai_hw_params, .hw_free = max98373_pcm_hw_free, - .set_sdw_stream = max98373_set_sdw_stream, + .set_stream = max98373_set_sdw_stream, .shutdown = max98373_shutdown, .set_tdm_slot = max98373_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index b392567c2b..d1882cbc93 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1021,7 +1021,7 @@ static int max98390_i2c_probe(struct i2c_client *i2c, int reg = 0; struct max98390_priv *max98390 = NULL; - struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent); + struct i2c_adapter *adapter = i2c->adapter; ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index dd29b183ec..7c9686be59 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -606,12 +606,9 @@ static int max9860_probe(struct i2c_client *i2c) return -ENOMEM; max9860->dvddio = devm_regulator_get(dev, "DVDDIO"); - if (IS_ERR(max9860->dvddio)) { - ret = PTR_ERR(max9860->dvddio); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret); - return ret; - } + if (IS_ERR(max9860->dvddio)) + return dev_err_probe(dev, PTR_ERR(max9860->dvddio), + "Failed to get DVDDIO supply\n"); max9860->dvddio_nb.notifier_call = max9860_dvddio_event; @@ -643,8 +640,7 @@ static int max9860_probe(struct i2c_client *i2c) if (IS_ERR(mclk)) { ret = PTR_ERR(mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get MCLK: %d\n", ret); + dev_err_probe(dev, ret, "Failed to get MCLK\n"); goto err_regulator; } diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 8b206ee777..5ba5f876ea 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -897,6 +897,19 @@ static int max98927_i2c_probe(struct i2c_client *i2c, "Failed to allocate regmap: %d\n", ret); return ret; } + + max98927->reset_gpio + = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98927->reset_gpio)) { + ret = PTR_ERR(max98927->reset_gpio); + return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin"); + } + + if (max98927->reset_gpio) { + gpiod_set_value_cansleep(max98927->reset_gpio, 0); + /* Wait for i2c port to be ready */ + usleep_range(5000, 6000); + } /* Check Revision ID */ ret = regmap_read(max98927->regmap, @@ -921,6 +934,17 @@ static int max98927_i2c_probe(struct i2c_client *i2c, return ret; } +static int max98927_i2c_remove(struct i2c_client *i2c) +{ + struct max98927_priv *max98927 = i2c_get_clientdata(i2c); + + if (max98927->reset_gpio) { + gpiod_set_value_cansleep(max98927->reset_gpio, 1); + } + + return 0; +} + static const struct i2c_device_id max98927_i2c_id[] = { { "max98927", 0}, { }, @@ -952,6 +976,7 @@ static struct i2c_driver max98927_i2c_driver = { .pm = &max98927_pm, }, .probe = max98927_i2c_probe, + .remove = max98927_i2c_remove, .id_table = max98927_i2c_id, }; diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h index 05f495db91..13f5066d74 100644 --- a/sound/soc/codecs/max98927.h +++ b/sound/soc/codecs/max98927.h @@ -255,6 +255,7 @@ struct max98927_priv { struct regmap *regmap; struct snd_soc_component *component; struct max98927_pdata *pdata; + struct gpio_desc *reset_gpio; unsigned int spk_gain; unsigned int sysclk; unsigned int v_l_slot; diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 3ddd822240..485cda46db 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -822,8 +822,8 @@ static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { {"EAR PA", NULL, "EAR CP"}, /* Headset (RX MIX1 and RX MIX2) */ - {"HEADPHONE", NULL, "HPHL PA"}, - {"HEADPHONE", NULL, "HPHR PA"}, + {"HPH_L", NULL, "HPHL PA"}, + {"HPH_R", NULL, "HPHR PA"}, {"HPHL DAC", NULL, "EAR_HPHL_CLK"}, {"HPHR DAC", NULL, "EAR_HPHR_CLK"}, @@ -870,7 +870,8 @@ static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { SND_SOC_DAPM_INPUT("AMIC3"), SND_SOC_DAPM_INPUT("AMIC2"), SND_SOC_DAPM_OUTPUT("EAR"), - SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_OUTPUT("HPH_L"), + SND_SOC_DAPM_OUTPUT("HPH_R"), /* RX stuff */ SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index 2d6a4a29b8..f8532aa7e4 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -2697,7 +2697,7 @@ static int mt6359_codec_probe(struct snd_soc_component *cmpnt) static void mt6359_codec_remove(struct snd_soc_component *cmpnt) { - snd_soc_component_exit_regmap(cmpnt); + cmpnt->regmap = NULL; } static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0); diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index 358c500377..3a881523c3 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -47,13 +47,12 @@ static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val) struct mt6660_chip *chip = context; int size = mt6660_get_reg_size(reg); u8 reg_data[4]; - int i, ret; + int i; for (i = 0; i < size; i++) reg_data[size - i - 1] = (val >> (8 * i)) & 0xff; - ret = i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); - return ret; + return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); } static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val) diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index f7018f2dd2..d0dd1542f7 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -29,6 +29,7 @@ #include "nau8824.h" #define NAU8824_JD_ACTIVE_HIGH BIT(0) +#define NAU8824_MONO_SPEAKER BIT(1) static int nau8824_quirk; static int quirk_override = -1; @@ -1861,7 +1862,25 @@ static const struct dmi_system_id nau8824_quirk_table[] = { DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"), }, - .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH | + NAU8824_MONO_SPEAKER), + }, + { + /* CUBE iwork8 Air */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "cube"), + DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + .driver_data = (void *)(NAU8824_MONO_SPEAKER), + }, + { + /* Pipo W2S */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PIPO"), + DMI_MATCH(DMI_PRODUCT_NAME, "W2S"), + }, + .driver_data = (void *)(NAU8824_MONO_SPEAKER), }, {} }; @@ -1880,6 +1899,17 @@ static void nau8824_check_quirks(void) nau8824_quirk = (unsigned long)dmi_id->driver_data; } +const char *nau8824_components(void) +{ + nau8824_check_quirks(); + + if (nau8824_quirk & NAU8824_MONO_SPEAKER) + return "cfg-spk:1"; + else + return "cfg-spk:2"; +} +EXPORT_SYMBOL_GPL(nau8824_components); + static int nau8824_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h index 1d7bdd8e05..de4bae8281 100644 --- a/sound/soc/codecs/nau8824.h +++ b/sound/soc/codecs/nau8824.h @@ -197,7 +197,7 @@ /* JACK_DET_CTRL (0x0D) */ #define NAU8824_JACK_EJECT_DT_SFT 2 #define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT) -#define NAU8824_JACK_LOGIC 0x1 +#define NAU8824_JACK_LOGIC (0x1 << 1) /* INTERRUPT_SETTING_1 (0x0F) */ @@ -470,6 +470,7 @@ struct nau8824_osr_attr { int nau8824_enable_jack_detect(struct snd_soc_component *component, struct snd_soc_jack *jack); +const char *nau8824_components(void); #endif /* _NAU8824_H */ diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 67de0e49cc..7734bc35ab 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -47,6 +47,7 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, unsigned int freq); +static bool nau8825_is_jack_inserted(struct regmap *regmap); struct nau8825_fll { int mclk_src; @@ -981,6 +982,31 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, return 0; } +static int system_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8825->regmap; + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + dev_dbg(nau8825->dev, "system clock control : POWER OFF\n"); + /* Set clock source to disable or internal clock before the + * playback or capture end. Codec needs clock for Jack + * detection and button press if jack inserted; otherwise, + * the clock should be closed. + */ + if (nau8825_is_jack_inserted(regmap)) { + nau8825_configure_sysclk(nau8825, + NAU8825_CLK_INTERNAL, 0); + } else { + nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0); + } + } + + return 0; +} + static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1094,6 +1120,9 @@ static const struct snd_kcontrol_new nau8825_dacr_mux = static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2, 15, 1), + SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, + system_clock_control, SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_INPUT("MIC"), SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), @@ -1182,9 +1211,11 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { {"ADC", NULL, "ADC Clock"}, {"ADC", NULL, "ADC Power"}, {"AIFTX", NULL, "ADC"}, + {"AIFTX", NULL, "System Clock"}, - {"DDACL", NULL, "Playback"}, - {"DDACR", NULL, "Playback"}, + {"AIFRX", NULL, "System Clock"}, + {"DDACL", NULL, "AIFRX"}, + {"DDACR", NULL, "AIFRX"}, {"DDACL", NULL, "DDAC Clock"}, {"DDACR", NULL, "DDAC Clock"}, {"DACL Mux", "DACL", "DDACL"}, @@ -1434,6 +1465,12 @@ int nau8825_enable_jack_detect(struct snd_soc_component *component, nau8825->jack = jack; + if (!nau8825->jack) { + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, 0); + return 0; + } /* Ground HP Outputs[1:0], needed for headset auto detection * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] */ @@ -2416,6 +2453,12 @@ static int __maybe_unused nau8825_resume(struct snd_soc_component *component) return 0; } +static int nau8825_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + return nau8825_enable_jack_detect(component, jack); +} + static const struct snd_soc_component_driver nau8825_component_driver = { .probe = nau8825_component_probe, .remove = nau8825_component_remove, @@ -2430,6 +2473,7 @@ static const struct snd_soc_component_driver nau8825_component_driver = { .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), .dapm_routes = nau8825_dapm_routes, .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), + .set_jack = nau8825_set_jack, .suspend_bias_off = 1, .idle_bias_on = 1, .use_pmdown_time = 1, diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index b6fd412441..fdf92c8b28 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -751,21 +751,14 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); - if (IS_ERR(pcm3168a->gpio_rst)) { - ret = PTR_ERR(pcm3168a->gpio_rst); - if (ret != -EPROBE_DEFER ) - dev_err(dev, "failed to acquire RST gpio: %d\n", ret); - - return ret; - } + if (IS_ERR(pcm3168a->gpio_rst)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), + "failed to acquire RST gpio\n"); pcm3168a->scki = devm_clk_get(dev, "scki"); - if (IS_ERR(pcm3168a->scki)) { - ret = PTR_ERR(pcm3168a->scki); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire clock 'scki': %d\n", ret); - return ret; - } + if (IS_ERR(pcm3168a->scki)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), + "failed to acquire clock 'scki'\n"); ret = clk_prepare_enable(pcm3168a->scki); if (ret) { @@ -781,8 +774,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request supplies: %d\n", ret); + dev_err_probe(dev, ret, "failed to request supplies\n"); goto err_clk; } diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index faff2b5586..b62301a628 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -1311,6 +1311,55 @@ static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol, .put = rt1011_r0_load_mode_put \ } +static const char * const rt1011_i2s_ref[] = { + "None", "Left Channel", "Right Channel" +}; + +static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, 0, 0, + rt1011_i2s_ref); + +static int rt1011_i2s_ref_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + rt1011->i2s_ref = ucontrol->value.enumerated.item[0]; + switch (rt1011->i2s_ref) { + case RT1011_I2S_REF_LEFT_CH: + regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x1022); + regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4); + break; + case RT1011_I2S_REF_RIGHT_CH: + regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x10a2); + regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4); + break; + default: + dev_info(component->dev, "I2S Reference: Do nothing\n"); + } + + return 0; +} + +static int rt1011_i2s_ref_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = rt1011->i2s_ref; + + return 0; +} + static const struct snd_kcontrol_new rt1011_snd_controls[] = { /* I2S Data In Selection */ SOC_ENUM("DIN Source", rt1011_din_source_enum), @@ -1349,6 +1398,9 @@ static const struct snd_kcontrol_new rt1011_snd_controls[] = { /* R0 temperature */ SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP, 2, 255, 0), + /* I2S Reference */ + SOC_ENUM_EXT("I2S Reference", rt1011_i2s_ref_enum, + rt1011_i2s_ref_get, rt1011_i2s_ref_put), }; static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, @@ -2007,6 +2059,7 @@ static int rt1011_probe(struct snd_soc_component *component) schedule_work(&rt1011->cali_work); + rt1011->i2s_ref = 0; rt1011->bq_drc_params = devm_kcalloc(component->dev, RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *), GFP_KERNEL); diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h index 68fadc15fa..4d6e7492d9 100644 --- a/sound/soc/codecs/rt1011.h +++ b/sound/soc/codecs/rt1011.h @@ -654,6 +654,12 @@ enum { RT1011_AIFS }; +enum { + RT1011_I2S_REF_NONE, + RT1011_I2S_REF_LEFT_CH, + RT1011_I2S_REF_RIGHT_CH, +}; + /* BiQual & DRC related settings */ #define RT1011_BQ_DRC_NUM 128 struct rt1011_bq_drc_params { @@ -692,6 +698,7 @@ struct rt1011_priv { unsigned int r0_reg, cali_done; unsigned int r0_calib, temperature_calib; int recv_spk_mode; + int i2s_ref; }; #endif /* end of _RT1011_H_ */ diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index c0c5952cdf..6a27dfacd8 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -864,7 +864,7 @@ static int rt1015_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 7561d20227..9845cdddcb 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -490,7 +490,7 @@ static int rt1016_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out * 4, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index 8c0b00242b..80b7ca0e4e 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -359,7 +359,7 @@ static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 7a0094578e..a9c473537a 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -841,7 +841,7 @@ static int rt1305_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index f716668de6..149a76075c 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -613,7 +613,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { .hw_params = rt1308_sdw_hw_params, .hw_free = rt1308_sdw_pcm_hw_free, - .set_sdw_stream = rt1308_set_sdw_stream, + .set_stream = rt1308_set_sdw_stream, .shutdown = rt1308_sdw_shutdown, .set_tdm_slot = rt1308_sdw_set_tdm_slot, }; diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index b4e5546e2e..c555b77b3c 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -664,7 +664,7 @@ static int rt1308_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 09b4914bba..c66d7b20cb 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -602,7 +602,7 @@ static const struct snd_soc_component_driver soc_component_sdw_rt1316 = { static const struct snd_soc_dai_ops rt1316_aif_dai_ops = { .hw_params = rt1316_sdw_hw_params, .hw_free = rt1316_sdw_pcm_hw_free, - .set_sdw_stream = rt1316_set_sdw_stream, + .set_stream = rt1316_set_sdw_stream, .shutdown = rt1316_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 4b1ad5054e..577680df70 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -936,7 +936,7 @@ static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index fd0d3a08e9..8e6414468a 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1133,7 +1133,7 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index cd1db5caab..e7a82565b9 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -195,6 +195,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg) case RT5640_PRIV_DATA: case RT5640_PGM_REG_ARR1: case RT5640_PGM_REG_ARR3: + case RT5640_DUMMY2: case RT5640_VENDOR_ID: case RT5640_VENDOR_ID1: case RT5640_VENDOR_ID2: @@ -1909,7 +1910,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } @@ -1972,7 +1973,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); snd_soc_component_update_bits(component, RT5640_DUMMY1, - 0x0301, 0x0301); + 0x1, 0x1); snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); } @@ -2159,7 +2160,11 @@ static bool rt5640_jack_inserted(struct snd_soc_component *component) struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int val; - val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + if (rt5640->jd_gpio) + val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0; + else + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); if (rt5640->jd_inverted) @@ -2297,10 +2302,42 @@ EXPORT_SYMBOL_GPL(rt5640_detect_headset); static void rt5640_jack_work(struct work_struct *work) { struct rt5640_priv *rt5640 = - container_of(work, struct rt5640_priv, jack_work); + container_of(work, struct rt5640_priv, jack_work.work); struct snd_soc_component *component = rt5640->component; int status; + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { + int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged; + + /* mic jack */ + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + hda_mic_plugged = !(val & RT5640_JD_STATUS); + dev_dbg(component->dev, "mic jack status %d\n", + hda_mic_plugged); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1, + RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT); + + if (hda_mic_plugged) + jack_type |= SND_JACK_MICROPHONE; + + /* headphone jack */ + val = snd_soc_component_read(component, RT5640_DUMMY2); + hda_hp_plugged = !(val & (0x1 << 11)); + dev_dbg(component->dev, "headphone jack status %d\n", + hda_hp_plugged); + + snd_soc_component_update_bits(component, RT5640_DUMMY2, + (0x1 << 10), !hda_hp_plugged << 10); + + if (hda_hp_plugged) + jack_type |= SND_JACK_HEADPHONE; + + snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET); + + return; + } + if (!rt5640_jack_inserted(component)) { /* Jack removed, or spurious IRQ? */ if (rt5640->jack->status & SND_JACK_HEADPHONE) { @@ -2348,7 +2385,7 @@ static void rt5640_jack_work(struct work_struct *work) * disabled the OVCD IRQ, the IRQ pin will stay high and as * we react to edges, we miss the unplug event -> recheck. */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } } @@ -2357,7 +2394,17 @@ static irqreturn_t rt5640_irq(int irq, void *data) struct rt5640_priv *rt5640 = data; if (rt5640->jack) - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + queue_delayed_work(system_long_wq, &rt5640->jack_work, + msecs_to_jiffies(JACK_SETTLE_TIME)); return IRQ_HANDLED; } @@ -2366,7 +2413,7 @@ static void rt5640_cancel_work(void *data) { struct rt5640_priv *rt5640 = data; - cancel_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->jack_work); cancel_delayed_work_sync(&rt5640->bp_work); } @@ -2406,7 +2453,12 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) if (!rt5640->jack) return; - free_irq(rt5640->irq, rt5640); + if (rt5640->jd_gpio_irq_requested) + free_irq(rt5640->jd_gpio_irq, rt5640); + + if (rt5640->irq_requested) + free_irq(rt5640->irq, rt5640); + rt5640_cancel_work(rt5640); if (rt5640->jack->status & SND_JACK_MICROPHONE) { @@ -2415,11 +2467,15 @@ static void rt5640_disable_jack_detect(struct snd_soc_component *component) snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); } + rt5640->jd_gpio_irq_requested = false; + rt5640->irq_requested = false; + rt5640->jd_gpio = NULL; rt5640->jack = NULL; } static void rt5640_enable_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) + struct snd_soc_jack *jack, + struct rt5640_set_jack_data *jack_data) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); int ret; @@ -2463,28 +2519,90 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640_enable_micbias1_ovcd_irq(component); } + if (jack_data && jack_data->codec_irq_override) + rt5640->irq = jack_data->codec_irq_override; + + if (jack_data && jack_data->jd_gpio) { + rt5640->jd_gpio = jack_data->jd_gpio; + rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); + + ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", + rt5640->jd_gpio_irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->jd_gpio_irq_requested = true; + } + ret = request_irq(rt5640->irq, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); - rt5640->irq = -ENXIO; - /* Undo above settings */ rt5640_disable_jack_detect(component); return; } + rt5640->irq_requested = true; + + /* sync initial jack state */ + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); +} + +static void rt5640_enable_hda_jack_detect( + struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; + + /* Select JD1 for Mic */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, RT5640_JD_JD1_IN4P); + snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); + + /* Select JD2 for Headphone */ + snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); + + rt5640->jack = jack; + + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + rt5640->irq = -ENXIO; + return; + } /* sync initial jack state */ - queue_work(system_long_wq, &rt5640->jack_work); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } static int rt5640_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data) { - if (jack) - rt5640_enable_jack_detect(component, jack); - else + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + rt5640_enable_hda_jack_detect(component, jack); + else + rt5640_enable_jack_detect(component, jack, data); + } else { rt5640_disable_jack_detect(component); + } return 0; } @@ -2574,11 +2692,16 @@ static int rt5640_probe(struct snd_soc_component *component) if (device_property_read_u32(component->dev, "realtek,jack-detect-source", &val) == 0) { - if (val <= RT5640_JD_SRC_GPIO4) + if (val <= RT5640_JD_SRC_GPIO4) { rt5640->jd_src = val << RT5640_JD_SFT; - else + } else if (val == RT5640_JD_SRC_HDA_HEADER) { + rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER; + snd_soc_component_update_bits(component, RT5640_DUMMY1, + 0x0300, 0x0); + } else { dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", val); + } } if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) @@ -2632,6 +2755,7 @@ static int rt5640_suspend(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + rt5640_cancel_work(rt5640); snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); @@ -2654,6 +2778,17 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); + if (rt5640->jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + snd_soc_component_update_bits(component, + RT5640_DUMMY2, 0x1100, 0x1100); + else + snd_soc_component_write(component, RT5640_DUMMY2, + 0x4001); + + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + } + return 0; } #else @@ -2856,7 +2991,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, rt5640->hp_mute = true; rt5640->irq = i2c->irq; INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); - INIT_WORK(&rt5640->jack_work, rt5640_jack_work); + INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); /* Make sure work is stopped on probe-error / remove */ ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 2c28f83e33..9e49b9a0cc 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2124,6 +2124,7 @@ struct rt5640_priv { int ldo1_en; /* GPIO for LDO1_EN */ int irq; + int jd_gpio_irq; int sysclk; int sysclk_src; int lrck[RT5640_AIFS]; @@ -2136,6 +2137,8 @@ struct rt5640_priv { bool hp_mute; bool asrc_en; + bool irq_requested; + bool jd_gpio_irq_requested; /* Jack and button detect data */ bool ovcd_irq_enabled; @@ -2145,14 +2148,20 @@ struct rt5640_priv { int release_count; int poll_count; struct delayed_work bp_work; - struct work_struct jack_work; + struct delayed_work jack_work; struct snd_soc_jack *jack; + struct gpio_desc *jd_gpio; unsigned int jd_src; bool jd_inverted; unsigned int ovcd_th; unsigned int ovcd_sf; }; +struct rt5640_set_jack_data { + int codec_irq_override; + struct gpio_desc *jd_gpio; +}; + int rt5640_dmic_enable(struct snd_soc_component *component, bool dmic1_data_pin, bool dmic2_data_pin); int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 9408ee63cb..197c560479 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -2969,7 +2969,7 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 93820561b9..f302c25688 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -1487,7 +1487,7 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 4a50b169fe..e1503c2eee 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3509,7 +3509,7 @@ static int rt5659_set_component_pll(struct snd_soc_component *component, int pll ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 33ff915635..3b50fb2986 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1046,7 +1046,7 @@ static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index ee09ccd448..2138f62e6a 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -2941,7 +2941,7 @@ static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index e59323fd5b..33e889802f 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4374,7 +4374,7 @@ static int rt5665_set_component_pll(struct snd_soc_component *component, int pll ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index 1186ceb5a9..5b12cbf2ba 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2173,7 +2173,7 @@ static int rt5668_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ecbaf129a6..ce7684752b 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2577,7 +2577,7 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index f655228c8c..4a8c267d4f 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -4557,7 +4557,7 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, ret = rt5677_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", freq_in); + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index b9d5d7a097..20fc0f3766 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -59,18 +59,12 @@ static void rt5682_jd_check_handler(struct work_struct *work) struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, jd_check_work.work); - if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) - & RT5682_JDH_RS_MASK) { + if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) & RT5682_JDH_RS_MASK) /* jack out */ - rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); - - snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3); - } else { + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, 0); + else schedule_delayed_work(&rt5682->jd_check_work, 500); - } } static irqreturn_t rt5682_irq(int irq, void *data) @@ -139,6 +133,8 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5682); + rt5682->i2c_dev = &i2c->dev; + rt5682->pdata = i2s_default_platform_data; if (pdata) @@ -276,6 +272,21 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); } +#ifdef CONFIG_COMMON_CLK + /* Check if MCLK provided */ + rt5682->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5682->mclk)) + return PTR_ERR(rt5682->mclk); + + /* Register CCF DAI clock control */ + ret = rt5682_register_dai_clks(rt5682); + if (ret) + return ret; + + /* Initial setup for CCF */ + rt5682->lrck[RT5682_AIF1] = 48000; +#endif + return devm_snd_soc_register_component(&i2c->dev, &rt5682_soc_component_dev, rt5682_dai, ARRAY_SIZE(rt5682_dai)); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 31a4f28604..248257a2e4 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -272,7 +272,7 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt5682_sdw_ops = { .hw_params = rt5682_sdw_hw_params, .hw_free = rt5682_sdw_hw_free, - .set_sdw_stream = rt5682_set_sdw_stream, + .set_stream = rt5682_set_sdw_stream, .shutdown = rt5682_sdw_shutdown, }; diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 6ad3159ece..be68d573a4 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -48,6 +48,8 @@ static const struct reg_sequence patch_list[] = { {RT5682_SAR_IL_CMD_6, 0x0110}, {RT5682_CHARGE_PUMP_1, 0x0210}, {RT5682_HP_LOGIC_CTRL_2, 0x0007}, + {RT5682_SAR_IL_CMD_2, 0xac00}, + {RT5682_CBJ_CTRL_7, 0x0104}, }; void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev) @@ -920,15 +922,13 @@ static void rt5682_enable_push_button_irq(struct snd_soc_component *component, * * Returns detect status. */ -int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) +static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = &component->dapm; unsigned int val, count; if (jack_insert) { - snd_soc_dapm_mutex_lock(dapm); - snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, RT5682_PWR_VREF2 | RT5682_PWR_MB, RT5682_PWR_VREF2 | RT5682_PWR_MB); @@ -942,6 +942,10 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1, RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0); + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + usleep_range(55000, 60000); snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); @@ -975,8 +979,6 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) snd_soc_component_update_bits(component, RT5682_MICBIAS_2, RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK, RT5682_PWR_CLK25M_PU | RT5682_PWR_CLK1M_PU); - - snd_soc_dapm_mutex_unlock(dapm); } else { rt5682_enable_push_button_irq(component, false); snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, @@ -1005,7 +1007,6 @@ int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type); return rt5682->jack_type; } -EXPORT_SYMBOL_GPL(rt5682_headset_detect); static int rt5682_set_jack_detect(struct snd_soc_component *component, struct snd_soc_jack *hs_jack, void *data) @@ -1088,6 +1089,7 @@ void rt5682_jack_detect_handler(struct work_struct *work) { struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, jack_detect_work.work); + struct snd_soc_dapm_context *dapm; int val, btn_type; if (!rt5682->component || !rt5682->component->card || @@ -1098,6 +1100,9 @@ void rt5682_jack_detect_handler(struct work_struct *work) return; } + dapm = snd_soc_component_get_dapm(rt5682->component); + + snd_soc_dapm_mutex_lock(dapm); mutex_lock(&rt5682->calibrate_mutex); val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) @@ -1157,6 +1162,9 @@ void rt5682_jack_detect_handler(struct work_struct *work) rt5682->irq_work_delay_time = 50; } + mutex_unlock(&rt5682->calibrate_mutex); + snd_soc_dapm_mutex_unlock(dapm); + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | @@ -1169,8 +1177,6 @@ void rt5682_jack_detect_handler(struct work_struct *work) else cancel_delayed_work_sync(&rt5682->jd_check_work); } - - mutex_unlock(&rt5682->calibrate_mutex); } EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler); @@ -2373,7 +2379,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component, pll2_fout1 = 3840000; ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } @@ -2385,7 +2391,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", + dev_err(component->dev, "Unsupported input clock %d\n", pll2_fout1); return ret; } @@ -2436,7 +2442,7 @@ static int rt5682_set_component_pll(struct snd_soc_component *component, ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); if (ret < 0) { - dev_err(component->dev, "Unsupport input clock %d\n", + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); return ret; } @@ -2556,7 +2562,7 @@ static int rt5682_set_bias_level(struct snd_soc_component *component, static bool rt5682_clk_check(struct rt5682_priv *rt5682) { if (!rt5682->master[RT5682_AIF1]) { - dev_dbg(rt5682->component->dev, "sysclk/dai not set correctly\n"); + dev_dbg(rt5682->i2c_dev, "sysclk/dai not set correctly\n"); return false; } return true; @@ -2567,13 +2573,15 @@ static int rt5682_wclk_prepare(struct clk_hw *hw) struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); - struct snd_soc_component *component = rt5682->component; - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); + struct snd_soc_component *component; + struct snd_soc_dapm_context *dapm; if (!rt5682_clk_check(rt5682)) return -EINVAL; + component = rt5682->component; + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_mutex_lock(dapm); snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); @@ -2603,13 +2611,15 @@ static void rt5682_wclk_unprepare(struct clk_hw *hw) struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); - struct snd_soc_component *component = rt5682->component; - struct snd_soc_dapm_context *dapm = - snd_soc_component_get_dapm(component); + struct snd_soc_component *component; + struct snd_soc_dapm_context *dapm; if (!rt5682_clk_check(rt5682)) return; + component = rt5682->component; + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_mutex_lock(dapm); snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); @@ -2633,7 +2643,6 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); - struct snd_soc_component *component = rt5682->component; const char * const clk_name = clk_hw_get_name(hw); if (!rt5682_clk_check(rt5682)) @@ -2643,7 +2652,7 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, */ if (rt5682->lrck[RT5682_AIF1] != CLK_48 && rt5682->lrck[RT5682_AIF1] != CLK_44) { - dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", + dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); return 0; } @@ -2657,7 +2666,6 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); - struct snd_soc_component *component = rt5682->component; const char * const clk_name = clk_hw_get_name(hw); if (!rt5682_clk_check(rt5682)) @@ -2667,7 +2675,7 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, * It will force to 48kHz if not both. */ if (rate != CLK_48 && rate != CLK_44) { - dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", + dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", __func__, clk_name, CLK_44, CLK_48); rate = CLK_48; } @@ -2681,7 +2689,7 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); - struct snd_soc_component *component = rt5682->component; + struct snd_soc_component *component; struct clk_hw *parent_hw; const char * const clk_name = clk_hw_get_name(hw); int pre_div; @@ -2690,6 +2698,8 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, if (!rt5682_clk_check(rt5682)) return -EINVAL; + component = rt5682->component; + /* * Whether the wclk's parent clk (mclk) exists or not, please ensure * it is fixed or set to 48MHz before setting wclk rate. It's a @@ -2699,12 +2709,12 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, */ parent_hw = clk_hw_get_parent(hw); if (!parent_hw) - dev_warn(component->dev, + dev_warn(rt5682->i2c_dev, "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n", CLK_PLL2_FIN); if (parent_rate != CLK_PLL2_FIN) - dev_warn(component->dev, "clk %s only support %d Hz input\n", + dev_warn(rt5682->i2c_dev, "clk %s only support %d Hz input\n", clk_name, CLK_PLL2_FIN); /* @@ -2736,10 +2746,9 @@ static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw, struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_BCLK_IDX]); - struct snd_soc_component *component = rt5682->component; unsigned int bclks_per_wclk; - bclks_per_wclk = snd_soc_component_read(component, RT5682_TDM_TCON_CTRL); + regmap_read(rt5682->regmap, RT5682_TDM_TCON_CTRL, &bclks_per_wclk); switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) { case RT5682_TDM_BCLK_MS1_256: @@ -2800,20 +2809,22 @@ static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, struct rt5682_priv *rt5682 = container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_BCLK_IDX]); - struct snd_soc_component *component = rt5682->component; + struct snd_soc_component *component; struct snd_soc_dai *dai; unsigned long factor; if (!rt5682_clk_check(rt5682)) return -EINVAL; + component = rt5682->component; + factor = rt5682_bclk_get_factor(rate, parent_rate); for_each_component_dais(component, dai) if (dai->id == RT5682_AIF1) break; if (!dai) { - dev_err(component->dev, "dai %d not found in component\n", + dev_err(rt5682->i2c_dev, "dai %d not found in component\n", RT5682_AIF1); return -ENODEV; } @@ -2836,17 +2847,15 @@ static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { }, }; -static int rt5682_register_dai_clks(struct snd_soc_component *component) +int rt5682_register_dai_clks(struct rt5682_priv *rt5682) { - struct device *dev = component->dev; - struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct device *dev = rt5682->i2c_dev; struct rt5682_platform_data *pdata = &rt5682->pdata; struct clk_hw *dai_clk_hw; int i, ret; for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { struct clk_init_data init = { }; - struct clk_parent_data parent_data; const struct clk_hw *parent; dai_clk_hw = &rt5682->dai_clks_hw[i]; @@ -2855,10 +2864,8 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) case RT5682_DAI_WCLK_IDX: /* Make MCLK the parent of WCLK */ if (rt5682->mclk) { - parent_data = (struct clk_parent_data){ - .fw_name = "mclk", - }; - init.parent_data = &parent_data; + parent = __clk_get_hw(rt5682->mclk); + init.parent_hws = &parent; init.num_parents = 1; } break; @@ -2899,6 +2906,7 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) return 0; } +EXPORT_SYMBOL_GPL(rt5682_register_dai_clks); #endif /* CONFIG_COMMON_CLK */ static int rt5682_probe(struct snd_soc_component *component) @@ -2908,9 +2916,6 @@ static int rt5682_probe(struct snd_soc_component *component) unsigned long time; struct snd_soc_dapm_context *dapm = &component->dapm; -#ifdef CONFIG_COMMON_CLK - int ret; -#endif rt5682->component = component; if (rt5682->is_sdw) { @@ -2922,26 +2927,6 @@ static int rt5682_probe(struct snd_soc_component *component) dev_err(&slave->dev, "Initialization not complete, timed out\n"); return -ETIMEDOUT; } - } else { -#ifdef CONFIG_COMMON_CLK - /* Check if MCLK provided */ - rt5682->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(rt5682->mclk)) { - if (PTR_ERR(rt5682->mclk) != -ENOENT) { - ret = PTR_ERR(rt5682->mclk); - return ret; - } - rt5682->mclk = NULL; - } - - /* Register CCF DAI clock control */ - ret = rt5682_register_dai_clks(component); - if (ret) - return ret; - - /* Initial setup for CCF */ - rt5682->lrck[RT5682_AIF1] = CLK_48; -#endif } snd_soc_dapm_disable_pin(dapm, "MICBIAS"); @@ -2968,10 +2953,7 @@ static int rt5682_suspend(struct snd_soc_component *component) cancel_delayed_work_sync(&rt5682->jack_detect_work); cancel_delayed_work_sync(&rt5682->jd_check_work); - if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { - snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, - RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, - RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG); + if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { val = snd_soc_component_read(component, RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; @@ -2993,10 +2975,17 @@ static int rt5682_suspend(struct snd_soc_component *component) /* enter SAR ADC power saving mode */ snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | - RT5682_SAR_BUTDET_RST_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, 0); + RT5682_SAR_SEL_MB1_MB2_MASK, 0); + usleep_range(5000, 6000); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, + RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG); + usleep_range(10000, 12000); snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, - RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_BUTDET_RST_MASK, - RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV | RT5682_SAR_BUTDET_RST_NORMAL); + RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK, + RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV); + snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1, + RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0); } regcache_cache_only(rt5682->regmap, true); @@ -3014,10 +3003,11 @@ static int rt5682_resume(struct snd_soc_component *component) regcache_cache_only(rt5682->regmap, false); regcache_sync(rt5682->regmap); - if (rt5682->hs_jack && rt5682->jack_type == SND_JACK_HEADSET) { + if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO); + usleep_range(5000, 6000); snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM); @@ -3025,8 +3015,9 @@ static int rt5682_resume(struct snd_soc_component *component) RT5682_PWR_CBJ, RT5682_PWR_CBJ); } + rt5682->jack_type = 0; mod_delayed_work(system_power_efficient_wq, - &rt5682->jack_detect_work, msecs_to_jiffies(250)); + &rt5682->jack_detect_work, msecs_to_jiffies(0)); return 0; } diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 8e3244a62c..52ff0d9c36 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1428,6 +1428,7 @@ enum { struct rt5682_priv { struct snd_soc_component *component; + struct device *i2c_dev; struct rt5682_platform_data pdata; struct regmap *regmap; struct regmap *sdw_regmap; @@ -1471,7 +1472,6 @@ int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev); -int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert); void rt5682_jack_detect_handler(struct work_struct *work); bool rt5682_volatile_register(struct device *dev, unsigned int reg); @@ -1482,6 +1482,8 @@ void rt5682_calibrate(struct rt5682_priv *rt5682); void rt5682_reset(struct rt5682_priv *rt5682); int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev); +int rt5682_register_dai_clks(struct rt5682_priv *rt5682); + #define RT5682_REG_NUM 318 extern const struct reg_default rt5682_reg[RT5682_REG_NUM]; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 921382724f..e61a8257bf 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1005,7 +1005,7 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt700_ops = { .hw_params = rt700_pcm_hw_params, .hw_free = rt700_pcm_hw_free, - .set_sdw_stream = rt700_set_sdw_stream, + .set_stream = rt700_set_sdw_stream, .shutdown = rt700_shutdown, }; diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 2e992589f1..bdb1375f03 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -1358,7 +1358,7 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_sdca_ops = { .hw_params = rt711_sdca_pcm_hw_params, .hw_free = rt711_sdca_pcm_hw_free, - .set_sdw_stream = rt711_sdca_set_sdw_stream, + .set_stream = rt711_sdca_set_sdw_stream, .shutdown = rt711_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index a7c5608a0e..6770825d03 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -1089,7 +1089,7 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt711_ops = { .hw_params = rt711_pcm_hw_params, .hw_free = rt711_pcm_hw_free, - .set_sdw_stream = rt711_set_sdw_stream, + .set_stream = rt711_set_sdw_stream, .shutdown = rt711_shutdown, }; diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 66e166568c..bfa536bd71 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -938,7 +938,7 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_sdca_ops = { .hw_params = rt715_sdca_pcm_hw_params, .hw_free = rt715_sdca_pcm_hw_free, - .set_sdw_stream = rt715_sdca_set_sdw_stream, + .set_stream = rt715_sdca_set_sdw_stream, .shutdown = rt715_sdca_shutdown, }; diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 1352869cc0..a64d11a747 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -909,7 +909,7 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops rt715_ops = { .hw_params = rt715_pcm_hw_params, .hw_free = rt715_pcm_hw_free, - .set_sdw_stream = rt715_set_sdw_stream, + .set_stream = rt715_set_sdw_stream, .shutdown = rt715_shutdown, }; diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index 8ea13cfa9f..7c612aaf31 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -138,7 +138,7 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops sdw_mockup_ops = { .hw_params = sdw_mockup_pcm_hw_params, .hw_free = sdw_mockup_pcm_hw_free, - .set_sdw_stream = sdw_mockup_set_sdw_stream, + .set_stream = sdw_mockup_set_sdw_stream, .shutdown = sdw_mockup_shutdown, }; diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 97bf1f2228..8eebf27d0e 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1612,9 +1612,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, if (ret == -ENOENT) ret = -EPROBE_DEFER; - if (ret != -EPROBE_DEFER) - dev_err(&client->dev, "Failed to get mclock: %d\n", - ret); + dev_err_probe(&client->dev, ret, "Failed to get mclock\n"); + goto disable_regs; } diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index b30fc1f894..d306c585b5 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -69,7 +69,6 @@ static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_amp *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (priv == NULL) @@ -78,12 +77,9 @@ static int simple_amp_probe(struct platform_device *pdev) priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_enable)) { - err = PTR_ERR(priv->gpiod_enable); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'enable' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_enable)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable), + "Failed to get 'enable' gpio"); return devm_snd_soc_register_component(dev, &simple_amp_component_driver, diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index e0a09dadfa..d30c0d24d9 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -82,7 +82,6 @@ static int simple_mux_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct simple_mux *priv; - int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -91,12 +90,9 @@ static int simple_mux_probe(struct platform_device *pdev) dev_set_drvdata(dev, priv); priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_mux)) { - err = PTR_ERR(priv->gpiod_mux); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'mux' gpio: %d", err); - return err; - } + if (IS_ERR(priv->gpiod_mux)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), + "Failed to get 'mux' gpio"); return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); } diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c index 2968959c4b..1d022643c3 100644 --- a/sound/soc/codecs/ssm2305.c +++ b/sound/soc/codecs/ssm2305.c @@ -57,7 +57,6 @@ static int ssm2305_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ssm2305 *priv; - int err; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -69,13 +68,9 @@ static int ssm2305_probe(struct platform_device *pdev) /* Get shutdown gpio */ priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_shutdown)) { - err = PTR_ERR(priv->gpiod_shutdown); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'shutdown' gpio: %d\n", - err); - return err; - } + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio\n"); return devm_snd_soc_register_component(dev, &ssm2305_component_driver, NULL, 0); diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h index f16900e00a..80bf56093d 100644 --- a/sound/soc/codecs/sta350.h +++ b/sound/soc/codecs/sta350.h @@ -14,7 +14,7 @@ #ifndef _ASOC_STA_350_H #define _ASOC_STA_350_H -/* STA50 register addresses */ +/* STA350 register addresses */ #define STA350_REGISTER_COUNT 0x4D #define STA350_COEF_COUNT 62 diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index 643b45188b..dc86852752 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -7,6 +7,7 @@ * Copyright (C) 2013 Sony Mobile Communications Inc. */ +#include #include #include #include @@ -19,6 +20,7 @@ #define TFA989X_REVISIONNUMBER 0x03 #define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */ #define TFA989X_I2SREG 0x04 +#define TFA989X_I2SREG_RCV 2 /* receiver mode */ #define TFA989X_I2SREG_CHSA 6 /* amplifier input select */ #define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6) #define TFA989X_I2SREG_I2SSR 12 /* sample rate */ @@ -53,7 +55,9 @@ struct tfa989x_rev { }; struct tfa989x { + const struct tfa989x_rev *rev; struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; }; static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) @@ -97,7 +101,35 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"Amp Input", "Right", "AIFINR"}, }; +static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + +static const char * const mode_text[] = { "Speaker", "Receiver" }; +static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); +static const struct snd_kcontrol_new tfa989x_mode_controls[] = { + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), +}; + +static int tfa989x_probe(struct snd_soc_component *component) +{ + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + if (tfa989x->rev->rev == TFA9897_REVISION) + return snd_soc_add_component_controls(component, tfa989x_mode_controls, + ARRAY_SIZE(tfa989x_mode_controls)); + + return 0; +} + static const struct snd_soc_component_driver tfa989x_component = { + .probe = tfa989x_probe, .dapm_widgets = tfa989x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets), .dapm_routes = tfa989x_dapm_routes, @@ -273,6 +305,7 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) if (!tfa989x) return -ENOMEM; + tfa989x->rev = rev; i2c_set_clientdata(i2c, tfa989x); tfa989x->vddd_supply = devm_regulator_get(dev, "vddd"); @@ -280,6 +313,12 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), "Failed to get vddd regulator\n"); + if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap); diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 52d2c968b5..e77342aff4 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,7 @@ struct aic31xx_priv { struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; struct snd_soc_jack *jack; + u32 sysclk_id; unsigned int sysclk; u8 p_div; int rate_div_line; @@ -180,6 +182,7 @@ struct aic31xx_priv { struct aic31xx_rate_divs { u32 mclk_p; u32 rate; + u8 pll_r; u8 pll_j; u16 pll_d; u16 dosr; @@ -192,51 +195,71 @@ struct aic31xx_rate_divs { /* ADC dividers can be disabled by configuring them to 0 */ static const struct aic31xx_rate_divs aic31xx_divs[] = { - /* mclk/p rate pll: j d dosr ndac mdac aors nadc madc */ + /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */ /* 8k rate */ - {12000000, 8000, 8, 1920, 128, 48, 2, 128, 48, 2}, - {12000000, 8000, 8, 1920, 128, 32, 3, 128, 32, 3}, - {12500000, 8000, 7, 8643, 128, 48, 2, 128, 48, 2}, + { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, + {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2}, /* 11.025k rate */ - {12000000, 11025, 7, 5264, 128, 32, 2, 128, 32, 2}, - {12000000, 11025, 8, 4672, 128, 24, 3, 128, 24, 3}, - {12500000, 11025, 7, 2253, 128, 32, 2, 128, 32, 2}, + { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3}, + {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, + {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2}, /* 16k rate */ - {12000000, 16000, 8, 1920, 128, 24, 2, 128, 24, 2}, - {12000000, 16000, 8, 1920, 128, 16, 3, 128, 16, 3}, - {12500000, 16000, 7, 8643, 128, 24, 2, 128, 24, 2}, + { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3}, + { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3}, + {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, + {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2}, /* 22.05k rate */ - {12000000, 22050, 7, 5264, 128, 16, 2, 128, 16, 2}, - {12000000, 22050, 8, 4672, 128, 12, 3, 128, 12, 3}, - {12500000, 22050, 7, 2253, 128, 16, 2, 128, 16, 2}, + { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3}, + { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3}, + {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, + {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2}, /* 32k rate */ - {12000000, 32000, 8, 1920, 128, 12, 2, 128, 12, 2}, - {12000000, 32000, 8, 1920, 128, 8, 3, 128, 8, 3}, - {12500000, 32000, 7, 8643, 128, 12, 2, 128, 12, 2}, + { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2}, + { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, + {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2}, /* 44.1k rate */ - {12000000, 44100, 7, 5264, 128, 8, 2, 128, 8, 2}, - {12000000, 44100, 8, 4672, 128, 6, 3, 128, 6, 3}, - {12500000, 44100, 7, 2253, 128, 8, 2, 128, 8, 2}, + { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, + {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2}, /* 48k rate */ - {12000000, 48000, 8, 1920, 128, 8, 2, 128, 8, 2}, - {12000000, 48000, 7, 6800, 96, 5, 4, 96, 5, 4}, - {12500000, 48000, 7, 8643, 128, 8, 2, 128, 8, 2}, + { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, + {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2}, /* 88.2k rate */ - {12000000, 88200, 7, 5264, 64, 8, 2, 64, 8, 2}, - {12000000, 88200, 8, 4672, 64, 6, 3, 64, 6, 3}, - {12500000, 88200, 7, 2253, 64, 8, 2, 64, 8, 2}, + { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, + {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2}, /* 96k rate */ - {12000000, 96000, 8, 1920, 64, 8, 2, 64, 8, 2}, - {12000000, 96000, 7, 6800, 48, 5, 4, 48, 5, 4}, - {12500000, 96000, 7, 8643, 64, 8, 2, 64, 8, 2}, + { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, + {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2}, /* 176.4k rate */ - {12000000, 176400, 7, 5264, 32, 8, 2, 32, 8, 2}, - {12000000, 176400, 8, 4672, 32, 6, 3, 32, 6, 3}, - {12500000, 176400, 7, 2253, 32, 8, 2, 32, 8, 2}, + { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, + {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2}, /* 192k rate */ - {12000000, 192000, 8, 1920, 32, 8, 2, 32, 8, 2}, - {12000000, 192000, 7, 6800, 24, 5, 4, 24, 5, 4}, - {12500000, 192000, 7, 8643, 32, 8, 2, 32, 8, 2}, + { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, + {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2}, }; static const char * const ldac_in_text[] = { @@ -888,7 +911,7 @@ static int aic31xx_setup_pll(struct snd_soc_component *component, /* PLL configuration */ snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK, - (aic31xx->p_div << 4) | 0x01); + (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r); snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j); snd_soc_component_write(component, AIC31XX_PLLDMSB, @@ -941,6 +964,7 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); u8 data = 0; dev_dbg(component->dev, "## %s: width %d rate %d\n", @@ -972,6 +996,16 @@ static int aic31xx_hw_params(struct snd_pcm_substream *substream, AIC31XX_IFACE1_DATALEN_MASK, data); + /* + * If BCLK is used as PLL input, the sysclk is determined by the hw + * params. So it must be updated here to match the input frequency. + */ + if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) { + aic31xx->sysclk = params_rate(params) * params_width(params) * + params_channels(params); + aic31xx->p_div = 1; + } + return aic31xx_setup_pll(component, params); } @@ -1156,6 +1190,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK, clk_id << AIC31XX_PLL_CLKIN_SHIFT); + aic31xx->sysclk_id = clk_id; aic31xx->sysclk = freq; return 0; @@ -1645,11 +1680,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(aic31xx->gpio_reset)) { - if (PTR_ERR(aic31xx->gpio_reset) != -EPROBE_DEFER) - dev_err(aic31xx->dev, "not able to acquire gpio\n"); - return PTR_ERR(aic31xx->gpio_reset); - } + if (IS_ERR(aic31xx->gpio_reset)) + return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset), + "not able to acquire gpio\n"); for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) aic31xx->supplies[i].supply = aic31xx_supply_names[i]; @@ -1657,12 +1690,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, ret = devm_regulator_bulk_get(aic31xx->dev, ARRAY_SIZE(aic31xx->supplies), aic31xx->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(aic31xx->dev, - "Failed to request supplies: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n"); aic31xx_configure_ocmv(aic31xx); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index 04ad383113..ed70e3d9ba 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -44,7 +44,9 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c, static int aic32x4_i2c_remove(struct i2c_client *i2c) { - return aic32x4_remove(&i2c->dev); + aic32x4_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id aic32x4_i2c_id[] = { diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c index e81c72958a..a8958cd1c6 100644 --- a/sound/soc/codecs/tlv320aic32x4-spi.c +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -48,7 +48,9 @@ static int aic32x4_spi_probe(struct spi_device *spi) static int aic32x4_spi_remove(struct spi_device *spi) { - return aic32x4_remove(&spi->dev); + aic32x4_remove(&spi->dev); + + return 0; } static const struct spi_device_id aic32x4_spi_id[] = { diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index d39c7d52ec..8f42fd7bc0 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1418,13 +1418,11 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) } EXPORT_SYMBOL(aic32x4_probe); -int aic32x4_remove(struct device *dev) +void aic32x4_remove(struct device *dev) { struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev); aic32x4_disable_regulators(aic32x4); - - return 0; } EXPORT_SYMBOL(aic32x4_remove); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index e9fd2e55d6..4de5bd9e8c 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -18,7 +18,7 @@ enum aic32x4_type { extern const struct regmap_config aic32x4_regmap_config; int aic32x4_probe(struct device *dev, struct regmap *regmap); -int aic32x4_remove(struct device *dev); +void aic32x4_remove(struct device *dev); int aic32x4_register_clocks(struct device *dev, const char *mclk_name); /* tlv320aic32x4 register space (in decimal to match datasheet) */ diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index cd0558ed4d..2f272bc3f5 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -32,7 +32,9 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i static int aic3x_i2c_remove(struct i2c_client *i2c) { - return aic3x_remove(&i2c->dev); + aic3x_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id aic3x_i2c_id[] = { diff --git a/sound/soc/codecs/tlv320aic3x-spi.c b/sound/soc/codecs/tlv320aic3x-spi.c index 8c7b6bb922..494e844022 100644 --- a/sound/soc/codecs/tlv320aic3x-spi.c +++ b/sound/soc/codecs/tlv320aic3x-spi.c @@ -37,7 +37,9 @@ static int aic3x_spi_probe(struct spi_device *spi) static int aic3x_spi_remove(struct spi_device *spi) { - return aic3x_remove(&spi->dev); + aic3x_remove(&spi->dev); + + return 0; } static const struct spi_device_id aic3x_spi_id[] = { diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 7731593a55..d53037b150 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1870,7 +1870,7 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver } EXPORT_SYMBOL(aic3x_probe); -int aic3x_remove(struct device *dev) +void aic3x_remove(struct device *dev) { struct aic3x_priv *aic3x = dev_get_drvdata(dev); @@ -1881,7 +1881,6 @@ int aic3x_remove(struct device *dev) gpio_set_value(aic3x->gpio_reset, 0); gpio_free(aic3x->gpio_reset); } - return 0; } EXPORT_SYMBOL(aic3x_remove); diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index 7e00639130..14298f9e6d 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -14,7 +14,7 @@ struct regmap_config; extern const struct regmap_config aic3x_regmap; int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data); -int aic3x_remove(struct device *dev); +void aic3x_remove(struct device *dev); #define AIC3X_MODEL_3X 0 #define AIC3X_MODEL_33 1 diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 405128ccb4..7488a150a1 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -1022,6 +1022,52 @@ static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) return plug_type; } +static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) { + if (mbhc->cfg->micb_mv == micbias_mv) + hs_threshold = mbhc->cfg->hs_thr; + else + hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) / + WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc) +{ + bool is_spl_hs = false; + int output_mv, hs_threshold, hph_threshold; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + return false; + + /* Bump up MIC_BIAS2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true); + usleep_range(10000, 10100); + + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc); + hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); + + if (!(output_mv > hs_threshold || output_mv < hph_threshold)) + is_spl_hs = true; + + /* Back MIC_BIAS2 to 1.8v if the type is not special headset */ + if (!is_spl_hs) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + return is_spl_hs; +} + static void wcd_correct_swch_plug(struct work_struct *work) { struct wcd_mbhc *mbhc; @@ -1029,12 +1075,14 @@ static void wcd_correct_swch_plug(struct work_struct *work) enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; unsigned long timeout; int pt_gnd_mic_swap_cnt = 0; - int output_mv, cross_conn, hs_threshold, try = 0; + int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv; + bool is_spl_hs = false; bool is_pa_on; mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); component = mbhc->component; + micbias_mv = wcd_mbhc_get_micbias(mbhc); hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); /* Mask ADC COMPLETE interrupt */ @@ -1097,6 +1145,16 @@ static void wcd_correct_swch_plug(struct work_struct *work) plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); + if (output_mv > hs_threshold && !is_spl_hs) { + is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + if (is_spl_hs) { + hs_threshold *= wcd_mbhc_get_micbias(mbhc); + hs_threshold /= micbias_mv; + } + } + if ((output_mv <= hs_threshold) && !is_pa_on) { /* Check for cross connection*/ cross_conn = wcd_check_cross_conn(mbhc); @@ -1122,14 +1180,19 @@ static void wcd_correct_swch_plug(struct work_struct *work) } } - if (output_mv > hs_threshold) /* cable is extension cable */ + /* cable is extension cable */ + if (output_mv > hs_threshold || mbhc->force_linein) plug_type = MBHC_PLUG_TYPE_HIGH_HPH; } wcd_mbhc_bcs_enable(mbhc, plug_type, true); - if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) - wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (is_spl_hs) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + } wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); @@ -1176,7 +1239,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; unsigned long timeout; int adc_threshold, output_mv, retry = 0; - bool hphpa_on = false; mutex_lock(&mbhc->lock); timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); @@ -1210,10 +1272,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) wcd_mbhc_elec_hs_report_unplug(mbhc); wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); - if (hphpa_on) { - hphpa_on = false; - wcd_mbhc_write_field(mbhc, WCD_MBHC_HPH_PA_EN, 3); - } exit: mutex_unlock(&mbhc->lock); return IRQ_HANDLED; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index bc5d68c53e..1e60db4056 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -341,7 +341,7 @@ struct wcd9335_codec { int reset_gpio; struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; - unsigned int rx_port_value; + unsigned int rx_port_value[WCD9335_RX_MAX]; unsigned int tx_port_value; int hph_l_gain; int hph_r_gain; @@ -1269,10 +1269,11 @@ static const struct snd_kcontrol_new sb_tx8_mux = static int slim_rx_mux_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); - struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + u32 port_id = w->shift; - ucontrol->value.enumerated.item[0] = wcd->rx_port_value; + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id]; return 0; } @@ -1286,9 +1287,9 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, struct snd_soc_dapm_update *update = NULL; u32 port_id = w->shift; - wcd->rx_port_value = ucontrol->value.enumerated.item[0]; + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; - switch (wcd->rx_port_value) { + switch (wcd->rx_port_value[port_id]) { case 0: list_del_init(&wcd->rx_chs[port_id].list); break; @@ -1309,11 +1310,11 @@ static int slim_rx_mux_put(struct snd_kcontrol *kc, &wcd->dai[AIF4_PB].slim_ch_list); break; default: - dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value); + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]); goto err; } - snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value, + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], e, update); return 0; diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index e63c6b723d..6c468527fe 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -3423,7 +3423,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, { struct soc_enum *e = (struct soc_enum *)kc->private_value; struct snd_soc_component *component; - int reg, val, ret; + int reg, val; component = snd_soc_dapm_kcontrol_component(kc); val = ucontrol->value.enumerated.item[0]; @@ -3446,9 +3446,7 @@ static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, WCD934X_RX_DLY_ZN_EN_MASK, WCD934X_RX_DLY_ZN_DISABLE); - ret = snd_soc_dapm_put_enum_double(kc, ucontrol); - - return ret; + return snd_soc_dapm_put_enum_double(kc, ucontrol); } static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index bbc261ab20..36cbc66914 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3089,7 +3089,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon int micb_num, bool req_en) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - int rc, micb_mv; + int micb_mv; if (micb_num != MIC_BIAS_2) return -EINVAL; @@ -3103,9 +3103,7 @@ static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *compon micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv; - rc = wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); - - return rc; + return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); } static inline void wcd938x_mbhc_get_result_params(struct wcd938x_priv *wcd938x, @@ -4290,7 +4288,7 @@ static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai, static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = { .hw_params = wcd938x_codec_hw_params, .hw_free = wcd938x_codec_free, - .set_sdw_stream = wcd938x_codec_set_sdw_stream, + .set_stream = wcd938x_codec_set_sdw_stream, }; static struct snd_soc_dai_driver wcd938x_dais[] = { diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index c35673e7f4..8863b533f9 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -145,13 +145,13 @@ static const struct regmap_range_cfg wm2200_ranges[] = { .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, }, }; -static const struct wm_adsp_region wm2200_dsp1_regions[] = { +static const struct cs_dsp_region wm2200_dsp1_regions[] = { { .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE }, { .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE }, { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE }, }; -static const struct wm_adsp_region wm2200_dsp2_regions[] = { +static const struct cs_dsp_region wm2200_dsp2_regions[] = { { .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE }, { .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE }, { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE }, @@ -2202,23 +2202,23 @@ static int wm2200_i2c_probe(struct i2c_client *i2c, } for (i = 0; i < 2; i++) { - wm2200->dsp[i].type = WMFW_ADSP1; + wm2200->dsp[i].cs_dsp.type = WMFW_ADSP1; wm2200->dsp[i].part = "wm2200"; - wm2200->dsp[i].num = i + 1; - wm2200->dsp[i].dev = &i2c->dev; - wm2200->dsp[i].regmap = wm2200->regmap; - wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3; - wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK; - wm2200->dsp[i].sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT; + wm2200->dsp[i].cs_dsp.num = i + 1; + wm2200->dsp[i].cs_dsp.dev = &i2c->dev; + wm2200->dsp[i].cs_dsp.regmap = wm2200->regmap; + wm2200->dsp[i].cs_dsp.sysclk_reg = WM2200_CLOCKING_3; + wm2200->dsp[i].cs_dsp.sysclk_mask = WM2200_SYSCLK_FREQ_MASK; + wm2200->dsp[i].cs_dsp.sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT; } - wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1; - wm2200->dsp[0].mem = wm2200_dsp1_regions; - wm2200->dsp[0].num_mems = ARRAY_SIZE(wm2200_dsp1_regions); + wm2200->dsp[0].cs_dsp.base = WM2200_DSP1_CONTROL_1; + wm2200->dsp[0].cs_dsp.mem = wm2200_dsp1_regions; + wm2200->dsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp1_regions); - wm2200->dsp[1].base = WM2200_DSP2_CONTROL_1; - wm2200->dsp[1].mem = wm2200_dsp2_regions; - wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions); + wm2200->dsp[1].cs_dsp.base = WM2200_DSP2_CONTROL_1; + wm2200->dsp[1].cs_dsp.mem = wm2200_dsp2_regions; + wm2200->dsp[1].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp2_regions); for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++) wm_adsp1_init(&wm2200->dsp[i]); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 621598608b..da2f8998df 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -44,7 +44,7 @@ static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); -static const struct wm_adsp_region wm5102_dsp1_regions[] = { +static const struct cs_dsp_region wm5102_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, { .type = WMFW_ADSP2_ZM, .base = 0x180000 }, { .type = WMFW_ADSP2_XM, .base = 0x190000 }, @@ -2046,13 +2046,13 @@ static int wm5102_probe(struct platform_device *pdev) arizona_init_dvfs(&wm5102->core); wm5102->core.adsp[0].part = "wm5102"; - wm5102->core.adsp[0].num = 1; - wm5102->core.adsp[0].type = WMFW_ADSP2; - wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1; - wm5102->core.adsp[0].dev = arizona->dev; - wm5102->core.adsp[0].regmap = arizona->regmap; - wm5102->core.adsp[0].mem = wm5102_dsp1_regions; - wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions); + wm5102->core.adsp[0].cs_dsp.num = 1; + wm5102->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + wm5102->core.adsp[0].cs_dsp.base = ARIZONA_DSP1_CONTROL_1; + wm5102->core.adsp[0].cs_dsp.dev = arizona->dev; + wm5102->core.adsp[0].cs_dsp.regmap = arizona->regmap; + wm5102->core.adsp[0].cs_dsp.mem = wm5102_dsp1_regions; + wm5102->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm5102_dsp1_regions); ret = wm_adsp2_init(&wm5102->core.adsp[0]); if (ret != 0) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 5c2d45d05c..4973ba1ed7 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -45,35 +45,35 @@ struct wm5110_priv { unsigned int in_pga_cache[6]; }; -static const struct wm_adsp_region wm5110_dsp1_regions[] = { +static const struct cs_dsp_region wm5110_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, { .type = WMFW_ADSP2_ZM, .base = 0x180000 }, { .type = WMFW_ADSP2_XM, .base = 0x190000 }, { .type = WMFW_ADSP2_YM, .base = 0x1a8000 }, }; -static const struct wm_adsp_region wm5110_dsp2_regions[] = { +static const struct cs_dsp_region wm5110_dsp2_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x200000 }, { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, { .type = WMFW_ADSP2_XM, .base = 0x290000 }, { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, }; -static const struct wm_adsp_region wm5110_dsp3_regions[] = { +static const struct cs_dsp_region wm5110_dsp3_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x300000 }, { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, { .type = WMFW_ADSP2_XM, .base = 0x390000 }, { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, }; -static const struct wm_adsp_region wm5110_dsp4_regions[] = { +static const struct cs_dsp_region wm5110_dsp4_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x400000 }, { .type = WMFW_ADSP2_ZM, .base = 0x480000 }, { .type = WMFW_ADSP2_XM, .base = 0x490000 }, { .type = WMFW_ADSP2_YM, .base = 0x4a8000 }, }; -static const struct wm_adsp_region *wm5110_dsp_regions[] = { +static const struct cs_dsp_region *wm5110_dsp_regions[] = { wm5110_dsp1_regions, wm5110_dsp2_regions, wm5110_dsp3_regions, @@ -2409,15 +2409,15 @@ static int wm5110_probe(struct platform_device *pdev) for (i = 0; i < WM5110_NUM_ADSP; i++) { wm5110->core.adsp[i].part = "wm5110"; - wm5110->core.adsp[i].num = i + 1; - wm5110->core.adsp[i].type = WMFW_ADSP2; - wm5110->core.adsp[i].dev = arizona->dev; - wm5110->core.adsp[i].regmap = arizona->regmap; + wm5110->core.adsp[i].cs_dsp.num = i + 1; + wm5110->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + wm5110->core.adsp[i].cs_dsp.dev = arizona->dev; + wm5110->core.adsp[i].cs_dsp.regmap = arizona->regmap; - wm5110->core.adsp[i].base = ARIZONA_DSP1_CONTROL_1 + wm5110->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 + (0x100 * i); - wm5110->core.adsp[i].mem = wm5110_dsp_regions[i]; - wm5110->core.adsp[i].num_mems + wm5110->core.adsp[i].cs_dsp.mem = wm5110_dsp_regions[i]; + wm5110->core.adsp[i].cs_dsp.num_mems = ARRAY_SIZE(wm5110_dsp1_regions); ret = wm_adsp2_init(&wm5110->core.adsp[i]); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index dcee7b2bd3..86b1f6eaa5 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -713,18 +713,12 @@ static int wm8731_spi_probe(struct spi_device *spi) return 0; } -static int wm8731_spi_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver wm8731_spi_driver = { .driver = { .name = "wm8731", .of_match_table = wm8731_of_match, }, .probe = wm8731_spi_probe, - .remove = wm8731_spi_remove, }; #endif /* CONFIG_SPI_MASTER */ diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index a9a6d766a1..bf3a4415a8 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1252,17 +1252,11 @@ static int wm8900_spi_probe(struct spi_device *spi) return ret; } -static int wm8900_spi_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver wm8900_spi_driver = { .driver = { .name = "wm8900", }, .probe = wm8900_spi_probe, - .remove = wm8900_spi_remove, }; #endif /* CONFIG_SPI_MASTER */ diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index ba16bdf9e4..a5584ba962 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3538,9 +3538,8 @@ static int wm8962_set_pdata_from_of(struct i2c_client *i2c, pdata->gpio_init[i] = 0x0; } - pdata->mclk = devm_clk_get(&i2c->dev, NULL); - - return 0; + pdata->mclk = devm_clk_get_optional(&i2c->dev, NULL); + return PTR_ERR_OR_ZERO(pdata->mclk); } static int wm8962_i2c_probe(struct i2c_client *i2c, @@ -3572,14 +3571,6 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, return ret; } - /* Mark the mclk pointer to NULL if no mclk assigned */ - if (IS_ERR(wm8962->pdata.mclk)) { - /* But do not ignore the request for probe defer */ - if (PTR_ERR(wm8962->pdata.mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - wm8962->pdata.mclk = NULL; - } - for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) wm8962->supplies[i].supply = wm8962_supply_names[i]; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index f7c800927c..0582585236 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -33,15 +32,15 @@ #include "wm_adsp.h" #define adsp_crit(_dsp, fmt, ...) \ - dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) + dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) #define adsp_err(_dsp, fmt, ...) \ - dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) + dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) #define adsp_warn(_dsp, fmt, ...) \ - dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) + dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) #define adsp_info(_dsp, fmt, ...) \ - dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) + dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) #define adsp_dbg(_dsp, fmt, ...) \ - dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) + dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) #define compr_err(_obj, fmt, ...) \ adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ @@ -50,301 +49,10 @@ adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ ##__VA_ARGS__) -#define ADSP1_CONTROL_1 0x00 -#define ADSP1_CONTROL_2 0x02 -#define ADSP1_CONTROL_3 0x03 -#define ADSP1_CONTROL_4 0x04 -#define ADSP1_CONTROL_5 0x06 -#define ADSP1_CONTROL_6 0x07 -#define ADSP1_CONTROL_7 0x08 -#define ADSP1_CONTROL_8 0x09 -#define ADSP1_CONTROL_9 0x0A -#define ADSP1_CONTROL_10 0x0B -#define ADSP1_CONTROL_11 0x0C -#define ADSP1_CONTROL_12 0x0D -#define ADSP1_CONTROL_13 0x0F -#define ADSP1_CONTROL_14 0x10 -#define ADSP1_CONTROL_15 0x11 -#define ADSP1_CONTROL_16 0x12 -#define ADSP1_CONTROL_17 0x13 -#define ADSP1_CONTROL_18 0x14 -#define ADSP1_CONTROL_19 0x16 -#define ADSP1_CONTROL_20 0x17 -#define ADSP1_CONTROL_21 0x18 -#define ADSP1_CONTROL_22 0x1A -#define ADSP1_CONTROL_23 0x1B -#define ADSP1_CONTROL_24 0x1C -#define ADSP1_CONTROL_25 0x1E -#define ADSP1_CONTROL_26 0x20 -#define ADSP1_CONTROL_27 0x21 -#define ADSP1_CONTROL_28 0x22 -#define ADSP1_CONTROL_29 0x23 -#define ADSP1_CONTROL_30 0x24 -#define ADSP1_CONTROL_31 0x26 - -/* - * ADSP1 Control 19 - */ -#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ -#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ -#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ - - -/* - * ADSP1 Control 30 - */ -#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ -#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ -#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ -#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ -#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ -#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ -#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ -#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ -#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ -#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ -#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ -#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ -#define ADSP1_START 0x0001 /* DSP1_START */ -#define ADSP1_START_MASK 0x0001 /* DSP1_START */ -#define ADSP1_START_SHIFT 0 /* DSP1_START */ -#define ADSP1_START_WIDTH 1 /* DSP1_START */ - -/* - * ADSP1 Control 31 - */ -#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ -#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ -#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ - -#define ADSP2_CONTROL 0x0 -#define ADSP2_CLOCKING 0x1 -#define ADSP2V2_CLOCKING 0x2 -#define ADSP2_STATUS1 0x4 -#define ADSP2_WDMA_CONFIG_1 0x30 -#define ADSP2_WDMA_CONFIG_2 0x31 -#define ADSP2V2_WDMA_CONFIG_2 0x32 -#define ADSP2_RDMA_CONFIG_1 0x34 - -#define ADSP2_SCRATCH0 0x40 -#define ADSP2_SCRATCH1 0x41 -#define ADSP2_SCRATCH2 0x42 -#define ADSP2_SCRATCH3 0x43 - -#define ADSP2V2_SCRATCH0_1 0x40 -#define ADSP2V2_SCRATCH2_3 0x42 - -/* - * ADSP2 Control - */ - -#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ -#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ -#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ -#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ -#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ -#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ -#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ -#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ -#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ -#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ -#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ -#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ -#define ADSP2_START 0x0001 /* DSP1_START */ -#define ADSP2_START_MASK 0x0001 /* DSP1_START */ -#define ADSP2_START_SHIFT 0 /* DSP1_START */ -#define ADSP2_START_WIDTH 1 /* DSP1_START */ - -/* - * ADSP2 clocking - */ -#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ -#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ -#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ - -/* - * ADSP2V2 clocking - */ -#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ -#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ -#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ - -#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ -#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ -#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ - -/* - * ADSP2 Status 1 - */ -#define ADSP2_RAM_RDY 0x0001 -#define ADSP2_RAM_RDY_MASK 0x0001 -#define ADSP2_RAM_RDY_SHIFT 0 -#define ADSP2_RAM_RDY_WIDTH 1 - -/* - * ADSP2 Lock support - */ -#define ADSP2_LOCK_CODE_0 0x5555 -#define ADSP2_LOCK_CODE_1 0xAAAA - -#define ADSP2_WATCHDOG 0x0A -#define ADSP2_BUS_ERR_ADDR 0x52 -#define ADSP2_REGION_LOCK_STATUS 0x64 -#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 -#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 -#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A -#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C -#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E -#define ADSP2_LOCK_REGION_CTRL 0x7A -#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C - -#define ADSP2_REGION_LOCK_ERR_MASK 0x8000 -#define ADSP2_ADDR_ERR_MASK 0x4000 -#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 -#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 -#define ADSP2_CTRL_ERR_EINT 0x0001 - -#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF -#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF -#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 -#define ADSP2_PMEM_ERR_ADDR_SHIFT 16 -#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD - -#define ADSP2_LOCK_REGION_SHIFT 16 - #define ADSP_MAX_STD_CTRL_SIZE 512 -#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 -#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 -#define WM_ADSP_ACKED_CTL_MIN_VALUE 0 -#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF - -/* - * Event control messages - */ -#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 - -/* - * HALO system info - */ -#define HALO_AHBM_WINDOW_DEBUG_0 0x02040 -#define HALO_AHBM_WINDOW_DEBUG_1 0x02044 - -/* - * HALO core - */ -#define HALO_SCRATCH1 0x005c0 -#define HALO_SCRATCH2 0x005c8 -#define HALO_SCRATCH3 0x005d0 -#define HALO_SCRATCH4 0x005d8 -#define HALO_CCM_CORE_CONTROL 0x41000 -#define HALO_CORE_SOFT_RESET 0x00010 -#define HALO_WDT_CONTROL 0x47000 - -/* - * HALO MPU banks - */ -#define HALO_MPU_XMEM_ACCESS_0 0x43000 -#define HALO_MPU_YMEM_ACCESS_0 0x43004 -#define HALO_MPU_WINDOW_ACCESS_0 0x43008 -#define HALO_MPU_XREG_ACCESS_0 0x4300C -#define HALO_MPU_YREG_ACCESS_0 0x43014 -#define HALO_MPU_XMEM_ACCESS_1 0x43018 -#define HALO_MPU_YMEM_ACCESS_1 0x4301C -#define HALO_MPU_WINDOW_ACCESS_1 0x43020 -#define HALO_MPU_XREG_ACCESS_1 0x43024 -#define HALO_MPU_YREG_ACCESS_1 0x4302C -#define HALO_MPU_XMEM_ACCESS_2 0x43030 -#define HALO_MPU_YMEM_ACCESS_2 0x43034 -#define HALO_MPU_WINDOW_ACCESS_2 0x43038 -#define HALO_MPU_XREG_ACCESS_2 0x4303C -#define HALO_MPU_YREG_ACCESS_2 0x43044 -#define HALO_MPU_XMEM_ACCESS_3 0x43048 -#define HALO_MPU_YMEM_ACCESS_3 0x4304C -#define HALO_MPU_WINDOW_ACCESS_3 0x43050 -#define HALO_MPU_XREG_ACCESS_3 0x43054 -#define HALO_MPU_YREG_ACCESS_3 0x4305C -#define HALO_MPU_XM_VIO_ADDR 0x43100 -#define HALO_MPU_XM_VIO_STATUS 0x43104 -#define HALO_MPU_YM_VIO_ADDR 0x43108 -#define HALO_MPU_YM_VIO_STATUS 0x4310C -#define HALO_MPU_PM_VIO_ADDR 0x43110 -#define HALO_MPU_PM_VIO_STATUS 0x43114 -#define HALO_MPU_LOCK_CONFIG 0x43140 - -/* - * HALO_AHBM_WINDOW_DEBUG_1 - */ -#define HALO_AHBM_CORE_ERR_ADDR_MASK 0x0fffff00 -#define HALO_AHBM_CORE_ERR_ADDR_SHIFT 8 -#define HALO_AHBM_FLAGS_ERR_MASK 0x000000ff - -/* - * HALO_CCM_CORE_CONTROL - */ -#define HALO_CORE_RESET 0x00000200 -#define HALO_CORE_EN 0x00000001 - -/* - * HALO_CORE_SOFT_RESET - */ -#define HALO_CORE_SOFT_RESET_MASK 0x00000001 - -/* - * HALO_WDT_CONTROL - */ -#define HALO_WDT_EN_MASK 0x00000001 - -/* - * HALO_MPU_?M_VIO_STATUS - */ -#define HALO_MPU_VIO_STS_MASK 0x007e0000 -#define HALO_MPU_VIO_STS_SHIFT 17 -#define HALO_MPU_VIO_ERR_WR_MASK 0x00008000 -#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff -#define HALO_MPU_VIO_ERR_SRC_SHIFT 0 - -static const struct wm_adsp_ops wm_adsp1_ops; -static const struct wm_adsp_ops wm_adsp2_ops[]; -static const struct wm_adsp_ops wm_halo_ops; - -struct wm_adsp_buf { - struct list_head list; - void *buf; -}; - -static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, - struct list_head *list) -{ - struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); - - if (buf == NULL) - return NULL; - - buf->buf = vmalloc(len); - if (!buf->buf) { - kfree(buf); - return NULL; - } - memcpy(buf->buf, src, len); - - if (list) - list_add_tail(&buf->list, list); - - return buf; -} - -static void wm_adsp_buf_free(struct list_head *list) -{ - while (!list_empty(list)) { - struct wm_adsp_buf *buf = list_first_entry(list, - struct wm_adsp_buf, - list); - list_del(&buf->list); - vfree(buf->buf); - kfree(buf); - } -} +static const struct cs_dsp_client_ops wm_adsp1_client_ops; +static const struct cs_dsp_client_ops wm_adsp2_client_ops; #define WM_ADSP_FW_MBC_VSS 0 #define WM_ADSP_FW_HIFI 1 @@ -470,12 +178,10 @@ struct wm_adsp_compr { const char *name; }; -#define WM_ADSP_DATA_WORD_SIZE 3 - #define WM_ADSP_MIN_FRAGMENTS 1 #define WM_ADSP_MAX_FRAGMENTS 256 -#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) -#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) +#define WM_ADSP_MIN_FRAGMENT_SIZE (64 * CS_DSP_DATA_WORD_SIZE) +#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 @@ -598,183 +304,11 @@ static const struct { struct wm_coeff_ctl { const char *name; - const char *fw_name; - /* Subname is needed to match with firmware */ - const char *subname; - unsigned int subname_len; - struct wm_adsp_alg_region alg_region; - struct wm_adsp *dsp; - unsigned int enabled:1; - struct list_head list; - void *cache; - unsigned int offset; - size_t len; - unsigned int set:1; + struct cs_dsp_coeff_ctl *cs_ctl; struct soc_bytes_ext bytes_ext; - unsigned int flags; - snd_ctl_elem_type_t type; + struct work_struct work; }; -static const char *wm_adsp_mem_region_name(unsigned int type) -{ - switch (type) { - case WMFW_ADSP1_PM: - return "PM"; - case WMFW_HALO_PM_PACKED: - return "PM_PACKED"; - case WMFW_ADSP1_DM: - return "DM"; - case WMFW_ADSP2_XM: - return "XM"; - case WMFW_HALO_XM_PACKED: - return "XM_PACKED"; - case WMFW_ADSP2_YM: - return "YM"; - case WMFW_HALO_YM_PACKED: - return "YM_PACKED"; - case WMFW_ADSP1_ZM: - return "ZM"; - default: - return NULL; - } -} - -#ifdef CONFIG_DEBUG_FS -static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) -{ - char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); - - kfree(dsp->wmfw_file_name); - dsp->wmfw_file_name = tmp; -} - -static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) -{ - char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); - - kfree(dsp->bin_file_name); - dsp->bin_file_name = tmp; -} - -static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) -{ - kfree(dsp->wmfw_file_name); - kfree(dsp->bin_file_name); - dsp->wmfw_file_name = NULL; - dsp->bin_file_name = NULL; -} - -static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct wm_adsp *dsp = file->private_data; - ssize_t ret; - - mutex_lock(&dsp->pwr_lock); - - if (!dsp->wmfw_file_name || !dsp->booted) - ret = 0; - else - ret = simple_read_from_buffer(user_buf, count, ppos, - dsp->wmfw_file_name, - strlen(dsp->wmfw_file_name)); - - mutex_unlock(&dsp->pwr_lock); - return ret; -} - -static ssize_t wm_adsp_debugfs_bin_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct wm_adsp *dsp = file->private_data; - ssize_t ret; - - mutex_lock(&dsp->pwr_lock); - - if (!dsp->bin_file_name || !dsp->booted) - ret = 0; - else - ret = simple_read_from_buffer(user_buf, count, ppos, - dsp->bin_file_name, - strlen(dsp->bin_file_name)); - - mutex_unlock(&dsp->pwr_lock); - return ret; -} - -static const struct { - const char *name; - const struct file_operations fops; -} wm_adsp_debugfs_fops[] = { - { - .name = "wmfw_file_name", - .fops = { - .open = simple_open, - .read = wm_adsp_debugfs_wmfw_read, - }, - }, - { - .name = "bin_file_name", - .fops = { - .open = simple_open, - .read = wm_adsp_debugfs_bin_read, - }, - }, -}; - -static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, - struct snd_soc_component *component) -{ - struct dentry *root = NULL; - int i; - - root = debugfs_create_dir(dsp->name, component->debugfs_root); - - debugfs_create_bool("booted", 0444, root, &dsp->booted); - debugfs_create_bool("running", 0444, root, &dsp->running); - debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id); - debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version); - - for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) - debugfs_create_file(wm_adsp_debugfs_fops[i].name, 0444, root, - dsp, &wm_adsp_debugfs_fops[i].fops); - - dsp->debugfs_root = root; -} - -static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) -{ - wm_adsp_debugfs_clear(dsp); - debugfs_remove_recursive(dsp->debugfs_root); - dsp->debugfs_root = NULL; -} -#else -static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, - struct snd_soc_component *component) -{ -} - -static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) -{ -} - -static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, - const char *s) -{ -} - -static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, - const char *s) -{ -} - -static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) -{ -} -#endif - int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -802,14 +336,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].pwr_lock); + mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); - if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) + if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) ret = -EBUSY; else dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; - mutex_unlock(&dsp[e->shift_l].pwr_lock); + mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); return ret; } @@ -826,270 +360,49 @@ const struct soc_enum wm_adsp_fw_enum[] = { }; EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); -static const struct wm_adsp_region *wm_adsp_find_region(struct wm_adsp *dsp, - int type) -{ - int i; - - for (i = 0; i < dsp->num_mems; i++) - if (dsp->mem[i].type == type) - return &dsp->mem[i]; - - return NULL; -} - -static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, - unsigned int offset) -{ - switch (mem->type) { - case WMFW_ADSP1_PM: - return mem->base + (offset * 3); - case WMFW_ADSP1_DM: - case WMFW_ADSP2_XM: - case WMFW_ADSP2_YM: - case WMFW_ADSP1_ZM: - return mem->base + (offset * 2); - default: - WARN(1, "Unknown memory region type"); - return offset; - } -} - -static unsigned int wm_halo_region_to_reg(struct wm_adsp_region const *mem, - unsigned int offset) -{ - switch (mem->type) { - case WMFW_ADSP2_XM: - case WMFW_ADSP2_YM: - return mem->base + (offset * 4); - case WMFW_HALO_XM_PACKED: - case WMFW_HALO_YM_PACKED: - return (mem->base + (offset * 3)) & ~0x3; - case WMFW_HALO_PM_PACKED: - return mem->base + (offset * 5); - default: - WARN(1, "Unknown memory region type"); - return offset; - } -} - -static void wm_adsp_read_fw_status(struct wm_adsp *dsp, - int noffs, unsigned int *offs) -{ - unsigned int i; - int ret; - - for (i = 0; i < noffs; ++i) { - ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); - if (ret) { - adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); - return; - } - } -} - -static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) -{ - unsigned int offs[] = { - ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, - }; - - wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); - - adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", - offs[0], offs[1], offs[2], offs[3]); -} - -static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) -{ - unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; - - wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); - - adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", - offs[0] & 0xFFFF, offs[0] >> 16, - offs[1] & 0xFFFF, offs[1] >> 16); -} - -static void wm_halo_show_fw_status(struct wm_adsp *dsp) -{ - unsigned int offs[] = { - HALO_SCRATCH1, HALO_SCRATCH2, HALO_SCRATCH3, HALO_SCRATCH4, - }; - - wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); - - adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", - offs[0], offs[1], offs[2], offs[3]); -} - static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) { return container_of(ext, struct wm_coeff_ctl, bytes_ext); } -static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) -{ - const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; - struct wm_adsp *dsp = ctl->dsp; - const struct wm_adsp_region *mem; - - mem = wm_adsp_find_region(dsp, alg_region->type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", - alg_region->type); - return -EINVAL; - } - - *reg = dsp->ops->region_to_reg(mem, ctl->alg_region.base + ctl->offset); - - return 0; -} - static int wm_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; - switch (ctl->type) { + switch (cs_ctl->type) { case WMFW_CTL_TYPE_ACKED: uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; - uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; + uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; + uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; uinfo->value.integer.step = 1; uinfo->count = 1; break; default: uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = ctl->len; + uinfo->count = cs_ctl->len; break; } return 0; } -static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, - unsigned int event_id) -{ - struct wm_adsp *dsp = ctl->dsp; - __be32 val = cpu_to_be32(event_id); - unsigned int reg; - int i, ret; - - ret = wm_coeff_base_reg(ctl, ®); - if (ret) - return ret; - - adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", - event_id, ctl->alg_region.alg, - wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); - - ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); - if (ret) { - adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); - return ret; - } - - /* - * Poll for ack, we initially poll at ~1ms intervals for firmwares - * that respond quickly, then go to ~10ms polls. A firmware is unlikely - * to ack instantly so we do the first 1ms delay before reading the - * control to avoid a pointless bus transaction - */ - for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { - switch (i) { - case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: - usleep_range(1000, 2000); - i++; - break; - default: - usleep_range(10000, 20000); - i += 10; - break; - } - - ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); - if (ret) { - adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); - return ret; - } - - if (val == 0) { - adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); - return 0; - } - } - - adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", - reg, ctl->alg_region.alg, - wm_adsp_mem_region_name(ctl->alg_region.type), - ctl->offset); - - return -ETIMEDOUT; -} - -static int wm_coeff_write_ctrl_raw(struct wm_coeff_ctl *ctl, - const void *buf, size_t len) -{ - struct wm_adsp *dsp = ctl->dsp; - void *scratch; - int ret; - unsigned int reg; - - ret = wm_coeff_base_reg(ctl, ®); - if (ret) - return ret; - - scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); - if (!scratch) - return -ENOMEM; - - ret = regmap_raw_write(dsp->regmap, reg, scratch, - len); - if (ret) { - adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", - len, reg, ret); - kfree(scratch); - return ret; - } - adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); - - kfree(scratch); - - return 0; -} - -static int wm_coeff_write_ctrl(struct wm_coeff_ctl *ctl, - const void *buf, size_t len) -{ - int ret = 0; - - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - ret = -EPERM; - else if (buf != ctl->cache) - memcpy(ctl->cache, buf, len); - - ctl->set = 1; - if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_write_ctrl_raw(ctl, buf, len); - - return ret; -} - static int wm_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; char *p = ucontrol->value.bytes.data; int ret = 0; - mutex_lock(&ctl->dsp->pwr_lock); - ret = wm_coeff_write_ctrl(ctl, p, ctl->len); - mutex_unlock(&ctl->dsp->pwr_lock); + mutex_lock(&cs_ctl->dsp->pwr_lock); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); + mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; } @@ -1100,16 +413,17 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; int ret = 0; - mutex_lock(&ctl->dsp->pwr_lock); + mutex_lock(&cs_ctl->dsp->pwr_lock); - if (copy_from_user(ctl->cache, bytes, size)) + if (copy_from_user(cs_ctl->cache, bytes, size)) ret = -EFAULT; else - ret = wm_coeff_write_ctrl(ctl, ctl->cache, size); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size); - mutex_unlock(&ctl->dsp->pwr_lock); + mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; } @@ -1120,71 +434,21 @@ static int wm_coeff_put_acked(struct snd_kcontrol *kctl, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; unsigned int val = ucontrol->value.integer.value[0]; int ret; if (val == 0) return 0; /* 0 means no event */ - mutex_lock(&ctl->dsp->pwr_lock); + mutex_lock(&cs_ctl->dsp->pwr_lock); - if (ctl->enabled && ctl->dsp->running) - ret = wm_coeff_write_acked_control(ctl, val); + if (cs_ctl->enabled) + ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); else ret = -EPERM; - mutex_unlock(&ctl->dsp->pwr_lock); - - return ret; -} - -static int wm_coeff_read_ctrl_raw(struct wm_coeff_ctl *ctl, - void *buf, size_t len) -{ - struct wm_adsp *dsp = ctl->dsp; - void *scratch; - int ret; - unsigned int reg; - - ret = wm_coeff_base_reg(ctl, ®); - if (ret) - return ret; - - scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); - if (!scratch) - return -ENOMEM; - - ret = regmap_raw_read(dsp->regmap, reg, scratch, len); - if (ret) { - adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", - len, reg, ret); - kfree(scratch); - return ret; - } - adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); - - memcpy(buf, scratch, len); - kfree(scratch); - - return 0; -} - -static int wm_coeff_read_ctrl(struct wm_coeff_ctl *ctl, void *buf, size_t len) -{ - int ret = 0; - - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { - if (ctl->enabled && ctl->dsp->running) - return wm_coeff_read_ctrl_raw(ctl, buf, len); - else - return -EPERM; - } else { - if (!ctl->flags && ctl->enabled && ctl->dsp->running) - ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); - - if (buf != ctl->cache) - memcpy(buf, ctl->cache, len); - } + mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; } @@ -1195,12 +459,13 @@ static int wm_coeff_get(struct snd_kcontrol *kctl, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; char *p = ucontrol->value.bytes.data; int ret; - mutex_lock(&ctl->dsp->pwr_lock); - ret = wm_coeff_read_ctrl(ctl, p, ctl->len); - mutex_unlock(&ctl->dsp->pwr_lock); + mutex_lock(&cs_ctl->dsp->pwr_lock); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); + mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; } @@ -1211,16 +476,17 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, struct soc_bytes_ext *bytes_ext = (struct soc_bytes_ext *)kctl->private_value; struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; int ret = 0; - mutex_lock(&ctl->dsp->pwr_lock); + mutex_lock(&cs_ctl->dsp->pwr_lock); - ret = wm_coeff_read_ctrl(ctl, ctl->cache, size); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); - if (!ret && copy_to_user(bytes, ctl->cache, size)) + if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) ret = -EFAULT; - mutex_unlock(&ctl->dsp->pwr_lock); + mutex_unlock(&cs_ctl->dsp->pwr_lock); return ret; } @@ -1240,12 +506,6 @@ static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, return 0; } -struct wmfw_ctl_work { - struct wm_adsp *dsp; - struct wm_coeff_ctl *ctl; - struct work_struct work; -}; - static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) { unsigned int out, rd, wr, vol; @@ -1277,33 +537,36 @@ static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) return out; } -static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) +static void wm_adsp_ctl_work(struct work_struct *work) { + struct wm_coeff_ctl *ctl = container_of(work, + struct wm_coeff_ctl, + work); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, + struct wm_adsp, + cs_dsp); struct snd_kcontrol_new *kcontrol; - int ret; - - if (!ctl || !ctl->name) - return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); if (!kcontrol) - return -ENOMEM; + return; kcontrol->name = ctl->name; kcontrol->info = wm_coeff_info; kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; kcontrol->tlv.c = snd_soc_bytes_tlv_callback; kcontrol->private_value = (unsigned long)&ctl->bytes_ext; - kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); + kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len); - switch (ctl->type) { + switch (cs_ctl->type) { case WMFW_CTL_TYPE_ACKED: kcontrol->get = wm_coeff_get_acked; kcontrol->put = wm_coeff_put_acked; break; default: if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - ctl->bytes_ext.max = ctl->len; + ctl->bytes_ext.max = cs_ctl->len; ctl->bytes_ext.get = wm_coeff_tlv_get; ctl->bytes_ext.put = wm_coeff_tlv_put; } else { @@ -1313,141 +576,49 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) break; } - ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); - if (ret < 0) - goto err_kcontrol; + snd_soc_add_component_controls(dsp->component, kcontrol, 1); kfree(kcontrol); - - return 0; - -err_kcontrol: - kfree(kcontrol); - return ret; } -static int wm_coeff_init_control_caches(struct wm_adsp *dsp) +static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) { + struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); + struct cs_dsp *cs_dsp = &dsp->cs_dsp; struct wm_coeff_ctl *ctl; - int ret; - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (!ctl->enabled || ctl->set) - continue; - if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) - continue; - - /* - * For readable controls populate the cache from the DSP memory. - * For non-readable controls the cache was zero-filled when - * created so we don't need to do anything. - */ - if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { - ret = wm_coeff_read_ctrl_raw(ctl, ctl->cache, ctl->len); - if (ret < 0) - return ret; - } - } - - return 0; -} - -static int wm_coeff_sync_controls(struct wm_adsp *dsp) -{ - struct wm_coeff_ctl *ctl; - int ret; - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (!ctl->enabled) - continue; - if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { - ret = wm_coeff_write_ctrl_raw(ctl, ctl->cache, - ctl->len); - if (ret < 0) - return ret; - } - } - - return 0; -} - -static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, - unsigned int event) -{ - struct wm_coeff_ctl *ctl; - int ret; - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) - continue; - - if (!ctl->enabled) - continue; - - ret = wm_coeff_write_acked_control(ctl, event); - if (ret) - adsp_warn(dsp, - "Failed to send 0x%x event to alg 0x%x (%d)\n", - event, ctl->alg_region.alg, ret); - } -} - -static void wm_adsp_ctl_work(struct work_struct *work) -{ - struct wmfw_ctl_work *ctl_work = container_of(work, - struct wmfw_ctl_work, - work); - - wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); - kfree(ctl_work); -} - -static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) -{ - kfree(ctl->cache); - kfree(ctl->name); - kfree(ctl->subname); - kfree(ctl); -} - -static int wm_adsp_create_control(struct wm_adsp *dsp, - const struct wm_adsp_alg_region *alg_region, - unsigned int offset, unsigned int len, - const char *subname, unsigned int subname_len, - unsigned int flags, snd_ctl_elem_type_t type) -{ - struct wm_coeff_ctl *ctl; - struct wmfw_ctl_work *ctl_work; char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; const char *region_name; int ret; - region_name = wm_adsp_mem_region_name(alg_region->type); + if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) + return 0; + + region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type); if (!region_name) { - adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); + adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type); return -EINVAL; } - switch (dsp->fw_ver) { + switch (cs_dsp->fw_ver) { case 0: case 1: - snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", - dsp->name, region_name, alg_region->alg); - subname = NULL; /* don't append subname */ + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s %s %x", cs_dsp->name, region_name, + cs_ctl->alg_region.alg); break; case 2: ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s%c %.12s %x", dsp->name, *region_name, - wm_adsp_fw_text[dsp->fw], alg_region->alg); + "%s%c %.12s %x", cs_dsp->name, *region_name, + wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); break; default: ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, - "%s %.12s %x", dsp->name, - wm_adsp_fw_text[dsp->fw], alg_region->alg); + "%s %.12s %x", cs_dsp->name, + wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); break; } - if (subname) { + if (cs_ctl->subname) { int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; int skip = 0; @@ -1455,613 +626,70 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, avail -= strlen(dsp->component->name_prefix) + 1; /* Truncate the subname from the start if it is too long */ - if (subname_len > avail) - skip = subname_len - avail; + if (cs_ctl->subname_len > avail) + skip = cs_ctl->subname_len - avail; snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, - " %.*s", subname_len - skip, subname + skip); - } - - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (!strcmp(ctl->name, name)) { - if (!ctl->enabled) - ctl->enabled = 1; - return 0; - } + " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); } ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); if (!ctl) return -ENOMEM; - ctl->fw_name = wm_adsp_fw_text[dsp->fw]; - ctl->alg_region = *alg_region; + ctl->cs_ctl = cs_ctl; + ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); if (!ctl->name) { ret = -ENOMEM; goto err_ctl; } - if (subname) { - ctl->subname_len = subname_len; - ctl->subname = kmemdup(subname, - strlen(subname) + 1, GFP_KERNEL); - if (!ctl->subname) { - ret = -ENOMEM; - goto err_ctl_name; - } - } - ctl->enabled = 1; - ctl->set = 0; - ctl->dsp = dsp; - ctl->flags = flags; - ctl->type = type; - ctl->offset = offset; - ctl->len = len; - ctl->cache = kzalloc(ctl->len, GFP_KERNEL); - if (!ctl->cache) { - ret = -ENOMEM; - goto err_ctl_subname; - } + cs_ctl->priv = ctl; - list_add(&ctl->list, &dsp->ctl_list); - - if (flags & WMFW_CTL_FLAG_SYS) - return 0; - - ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); - if (!ctl_work) { - ret = -ENOMEM; - goto err_list_del; - } - - ctl_work->dsp = dsp; - ctl_work->ctl = ctl; - INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); - schedule_work(&ctl_work->work); + INIT_WORK(&ctl->work, wm_adsp_ctl_work); + schedule_work(&ctl->work); return 0; -err_list_del: - list_del(&ctl->list); - kfree(ctl->cache); -err_ctl_subname: - kfree(ctl->subname); -err_ctl_name: - kfree(ctl->name); err_ctl: kfree(ctl); return ret; } -struct wm_coeff_parsed_alg { - int id; - const u8 *name; - int name_len; - int ncoeff; -}; - -struct wm_coeff_parsed_coeff { - int offset; - int mem_type; - const u8 *name; - int name_len; - snd_ctl_elem_type_t ctl_type; - int flags; - int len; -}; - -static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) +static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) { - int length; + struct wm_coeff_ctl *ctl = cs_ctl->priv; - switch (bytes) { - case 1: - length = **pos; - break; - case 2: - length = le16_to_cpu(*((__le16 *)*pos)); - break; - default: - return 0; - } + cancel_work_sync(&ctl->work); - if (str) - *str = *pos + bytes; - - *pos += ((length + bytes) + 3) & ~0x03; - - return length; -} - -static int wm_coeff_parse_int(int bytes, const u8 **pos) -{ - int val = 0; - - switch (bytes) { - case 2: - val = le16_to_cpu(*((__le16 *)*pos)); - break; - case 4: - val = le32_to_cpu(*((__le32 *)*pos)); - break; - default: - break; - } - - *pos += bytes; - - return val; -} - -static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, - struct wm_coeff_parsed_alg *blk) -{ - const struct wmfw_adsp_alg_data *raw; - - switch (dsp->fw_ver) { - case 0: - case 1: - raw = (const struct wmfw_adsp_alg_data *)*data; - *data = raw->data; - - blk->id = le32_to_cpu(raw->id); - blk->name = raw->name; - blk->name_len = strlen(raw->name); - blk->ncoeff = le32_to_cpu(raw->ncoeff); - break; - default: - blk->id = wm_coeff_parse_int(sizeof(raw->id), data); - blk->name_len = wm_coeff_parse_string(sizeof(u8), data, - &blk->name); - wm_coeff_parse_string(sizeof(u16), data, NULL); - blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); - break; - } - - adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); - adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); - adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); -} - -static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, - struct wm_coeff_parsed_coeff *blk) -{ - const struct wmfw_adsp_coeff_data *raw; - const u8 *tmp; - int length; - - switch (dsp->fw_ver) { - case 0: - case 1: - raw = (const struct wmfw_adsp_coeff_data *)*data; - *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); - - blk->offset = le16_to_cpu(raw->hdr.offset); - blk->mem_type = le16_to_cpu(raw->hdr.type); - blk->name = raw->name; - blk->name_len = strlen(raw->name); - blk->ctl_type = (__force snd_ctl_elem_type_t)le16_to_cpu(raw->ctl_type); - blk->flags = le16_to_cpu(raw->flags); - blk->len = le32_to_cpu(raw->len); - break; - default: - tmp = *data; - blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); - blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); - length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); - blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, - &blk->name); - wm_coeff_parse_string(sizeof(u8), &tmp, NULL); - wm_coeff_parse_string(sizeof(u16), &tmp, NULL); - blk->ctl_type = - (__force snd_ctl_elem_type_t)wm_coeff_parse_int(sizeof(raw->ctl_type), - &tmp); - blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); - blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); - - *data = *data + sizeof(raw->hdr) + length; - break; - } - - adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); - adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); - adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); - adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); - adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); - adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); -} - -static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, - const struct wm_coeff_parsed_coeff *coeff_blk, - unsigned int f_required, - unsigned int f_illegal) -{ - if ((coeff_blk->flags & f_illegal) || - ((coeff_blk->flags & f_required) != f_required)) { - adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", - coeff_blk->flags, coeff_blk->ctl_type); - return -EINVAL; - } - - return 0; -} - -static int wm_adsp_parse_coeff(struct wm_adsp *dsp, - const struct wmfw_region *region) -{ - struct wm_adsp_alg_region alg_region = {}; - struct wm_coeff_parsed_alg alg_blk; - struct wm_coeff_parsed_coeff coeff_blk; - const u8 *data = region->data; - int i, ret; - - wm_coeff_parse_alg(dsp, &data, &alg_blk); - for (i = 0; i < alg_blk.ncoeff; i++) { - wm_coeff_parse_coeff(dsp, &data, &coeff_blk); - - switch (coeff_blk.ctl_type) { - case SNDRV_CTL_ELEM_TYPE_BYTES: - break; - case WMFW_CTL_TYPE_ACKED: - if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) - continue; /* ignore */ - - ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, - WMFW_CTL_FLAG_VOLATILE | - WMFW_CTL_FLAG_WRITEABLE | - WMFW_CTL_FLAG_READABLE, - 0); - if (ret) - return -EINVAL; - break; - case WMFW_CTL_TYPE_HOSTEVENT: - ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, - WMFW_CTL_FLAG_SYS | - WMFW_CTL_FLAG_VOLATILE | - WMFW_CTL_FLAG_WRITEABLE | - WMFW_CTL_FLAG_READABLE, - 0); - if (ret) - return -EINVAL; - break; - case WMFW_CTL_TYPE_HOST_BUFFER: - ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, - WMFW_CTL_FLAG_SYS | - WMFW_CTL_FLAG_VOLATILE | - WMFW_CTL_FLAG_READABLE, - 0); - if (ret) - return -EINVAL; - break; - default: - adsp_err(dsp, "Unknown control type: %d\n", - coeff_blk.ctl_type); - return -EINVAL; - } - - alg_region.type = coeff_blk.mem_type; - alg_region.alg = alg_blk.id; - - ret = wm_adsp_create_control(dsp, &alg_region, - coeff_blk.offset, - coeff_blk.len, - coeff_blk.name, - coeff_blk.name_len, - coeff_blk.flags, - coeff_blk.ctl_type); - if (ret < 0) - adsp_err(dsp, "Failed to create control: %.*s, %d\n", - coeff_blk.name_len, coeff_blk.name, ret); - } - - return 0; -} - -static unsigned int wm_adsp1_parse_sizes(struct wm_adsp *dsp, - const char * const file, - unsigned int pos, - const struct firmware *firmware) -{ - const struct wmfw_adsp1_sizes *adsp1_sizes; - - adsp1_sizes = (void *)&firmware->data[pos]; - - adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, - le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), - le32_to_cpu(adsp1_sizes->zm)); - - return pos + sizeof(*adsp1_sizes); -} - -static unsigned int wm_adsp2_parse_sizes(struct wm_adsp *dsp, - const char * const file, - unsigned int pos, - const struct firmware *firmware) -{ - const struct wmfw_adsp2_sizes *adsp2_sizes; - - adsp2_sizes = (void *)&firmware->data[pos]; - - adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, - le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), - le32_to_cpu(adsp2_sizes->pm), le32_to_cpu(adsp2_sizes->zm)); - - return pos + sizeof(*adsp2_sizes); -} - -static bool wm_adsp_validate_version(struct wm_adsp *dsp, unsigned int version) -{ - switch (version) { - case 0: - adsp_warn(dsp, "Deprecated file format %d\n", version); - return true; - case 1: - case 2: - return true; - default: - return false; - } -} - -static bool wm_halo_validate_version(struct wm_adsp *dsp, unsigned int version) -{ - switch (version) { - case 3: - return true; - default: - return false; - } -} - -static int wm_adsp_load(struct wm_adsp *dsp) -{ - LIST_HEAD(buf_list); - const struct firmware *firmware; - struct regmap *regmap = dsp->regmap; - unsigned int pos = 0; - const struct wmfw_header *header; - const struct wmfw_adsp1_sizes *adsp1_sizes; - const struct wmfw_footer *footer; - const struct wmfw_region *region; - const struct wm_adsp_region *mem; - const char *region_name; - char *file, *text = NULL; - struct wm_adsp_buf *buf; - unsigned int reg; - int regions = 0; - int ret, offset, type; - - file = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (file == NULL) - return -ENOMEM; - - snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, - wm_adsp_fw[dsp->fw].file); - file[PAGE_SIZE - 1] = '\0'; - - ret = request_firmware(&firmware, file, dsp->dev); - if (ret != 0) { - adsp_err(dsp, "Failed to request '%s'\n", file); - goto out; - } - ret = -EINVAL; - - pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); - if (pos >= firmware->size) { - adsp_err(dsp, "%s: file too short, %zu bytes\n", - file, firmware->size); - goto out_fw; - } - - header = (void *)&firmware->data[0]; - - if (memcmp(&header->magic[0], "WMFW", 4) != 0) { - adsp_err(dsp, "%s: invalid magic\n", file); - goto out_fw; - } - - if (!dsp->ops->validate_version(dsp, header->ver)) { - adsp_err(dsp, "%s: unknown file format %d\n", - file, header->ver); - goto out_fw; - } - - adsp_info(dsp, "Firmware version: %d\n", header->ver); - dsp->fw_ver = header->ver; - - if (header->core != dsp->type) { - adsp_err(dsp, "%s: invalid core %d != %d\n", - file, header->core, dsp->type); - goto out_fw; - } - - pos = sizeof(*header); - pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); - - footer = (void *)&firmware->data[pos]; - pos += sizeof(*footer); - - if (le32_to_cpu(header->len) != pos) { - adsp_err(dsp, "%s: unexpected header length %d\n", - file, le32_to_cpu(header->len)); - goto out_fw; - } - - adsp_dbg(dsp, "%s: timestamp %llu\n", file, - le64_to_cpu(footer->timestamp)); - - while (pos < firmware->size && - sizeof(*region) < firmware->size - pos) { - region = (void *)&(firmware->data[pos]); - region_name = "Unknown"; - reg = 0; - text = NULL; - offset = le32_to_cpu(region->offset) & 0xffffff; - type = be32_to_cpu(region->type) & 0xff; - - switch (type) { - case WMFW_NAME_TEXT: - region_name = "Firmware name"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); - break; - case WMFW_ALGORITHM_DATA: - region_name = "Algorithm"; - ret = wm_adsp_parse_coeff(dsp, region); - if (ret != 0) - goto out_fw; - break; - case WMFW_INFO_TEXT: - region_name = "Information"; - text = kzalloc(le32_to_cpu(region->len) + 1, - GFP_KERNEL); - break; - case WMFW_ABSOLUTE: - region_name = "Absolute"; - reg = offset; - break; - case WMFW_ADSP1_PM: - case WMFW_ADSP1_DM: - case WMFW_ADSP2_XM: - case WMFW_ADSP2_YM: - case WMFW_ADSP1_ZM: - case WMFW_HALO_PM_PACKED: - case WMFW_HALO_XM_PACKED: - case WMFW_HALO_YM_PACKED: - mem = wm_adsp_find_region(dsp, type); - if (!mem) { - adsp_err(dsp, "No region of type: %x\n", type); - ret = -EINVAL; - goto out_fw; - } - - region_name = wm_adsp_mem_region_name(type); - reg = dsp->ops->region_to_reg(mem, offset); - break; - default: - adsp_warn(dsp, - "%s.%d: Unknown region type %x at %d(%x)\n", - file, regions, type, pos, pos); - break; - } - - adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, - regions, le32_to_cpu(region->len), offset, - region_name); - - if (le32_to_cpu(region->len) > - firmware->size - pos - sizeof(*region)) { - adsp_err(dsp, - "%s.%d: %s region len %d bytes exceeds file length %zu\n", - file, regions, region_name, - le32_to_cpu(region->len), firmware->size); - ret = -EINVAL; - goto out_fw; - } - - if (text) { - memcpy(text, region->data, le32_to_cpu(region->len)); - adsp_info(dsp, "%s: %s\n", file, text); - kfree(text); - text = NULL; - } - - if (reg) { - buf = wm_adsp_buf_alloc(region->data, - le32_to_cpu(region->len), - &buf_list); - if (!buf) { - adsp_err(dsp, "Out of memory\n"); - ret = -ENOMEM; - goto out_fw; - } - - ret = regmap_raw_write_async(regmap, reg, buf->buf, - le32_to_cpu(region->len)); - if (ret != 0) { - adsp_err(dsp, - "%s.%d: Failed to write %d bytes at %d in %s: %d\n", - file, regions, - le32_to_cpu(region->len), offset, - region_name, ret); - goto out_fw; - } - } - - pos += le32_to_cpu(region->len) + sizeof(*region); - regions++; - } - - ret = regmap_async_complete(regmap); - if (ret != 0) { - adsp_err(dsp, "Failed to complete async write: %d\n", ret); - goto out_fw; - } - - if (pos > firmware->size) - adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", - file, regions, pos - firmware->size); - - wm_adsp_debugfs_save_wmfwname(dsp, file); - -out_fw: - regmap_async_complete(regmap); - wm_adsp_buf_free(&buf_list); - release_firmware(firmware); - kfree(text); -out: - kfree(file); - - return ret; -} - -/* - * Find wm_coeff_ctl with input name as its subname - * If not found, return NULL - */ -static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp, - const char *name, int type, - unsigned int alg) -{ - struct wm_coeff_ctl *pos, *rslt = NULL; - const char *fw_txt = wm_adsp_fw_text[dsp->fw]; - - list_for_each_entry(pos, &dsp->ctl_list, list) { - if (!pos->subname) - continue; - if (strncmp(pos->subname, name, pos->subname_len) == 0 && - pos->fw_name == fw_txt && - pos->alg_region.alg == alg && - pos->alg_region.type == type) { - rslt = pos; - break; - } - } - - return rslt; + kfree(ctl->name); + kfree(ctl); } int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { + struct cs_dsp_coeff_ctl *cs_ctl; struct wm_coeff_ctl *ctl; struct snd_kcontrol *kcontrol; char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; - ctl = wm_adsp_get_ctl(dsp, name, type, alg); - if (!ctl) + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + if (!cs_ctl) return -EINVAL; - if (len > ctl->len) + ctl = cs_ctl->priv; + + if (len > cs_ctl->len) return -EINVAL; - ret = wm_coeff_write_ctrl(ctl, buf, len); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); if (ret) return ret; - if (ctl->flags & WMFW_CTL_FLAG_SYS) + if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) return 0; if (dsp->component->name_prefix) @@ -2087,683 +715,83 @@ 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) { - struct wm_coeff_ctl *ctl; + struct cs_dsp_coeff_ctl *cs_ctl; - ctl = wm_adsp_get_ctl(dsp, name, type, alg); - if (!ctl) + cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + if (!cs_ctl) return -EINVAL; - if (len > ctl->len) + if (len > cs_ctl->len) return -EINVAL; - return wm_coeff_read_ctrl(ctl, buf, len); + return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len); } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); -static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, - const struct wm_adsp_alg_region *alg_region) +static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, + const struct firmware *wmfw_firmware, + char *wmfw_filename, + const struct firmware *coeff_firmware, + char *coeff_filename) { - struct wm_coeff_ctl *ctl; + if (wmfw_firmware) + release_firmware(wmfw_firmware); + kfree(wmfw_filename); - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && - alg_region->alg == ctl->alg_region.alg && - alg_region->type == ctl->alg_region.type) { - ctl->alg_region.base = alg_region->base; - } - } + if (coeff_firmware) + release_firmware(coeff_firmware); + kfree(coeff_filename); } -static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, - const struct wm_adsp_region *mem, - unsigned int pos, unsigned int len) +static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, + const struct firmware **firmware, + char **filename, + char *suffix) { - void *alg; - unsigned int reg; - int ret; - __be32 val; + struct cs_dsp *cs_dsp = &dsp->cs_dsp; + int ret = 0; - if (n_algs == 0) { - adsp_err(dsp, "No algorithms\n"); - return ERR_PTR(-EINVAL); - } + *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", dsp->part, dsp->fwf_name, + wm_adsp_fw[dsp->fw].file, suffix); + if (*filename == NULL) + return -ENOMEM; - if (n_algs > 1024) { - adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); - return ERR_PTR(-EINVAL); - } - - /* Read the terminator first to validate the length */ - reg = dsp->ops->region_to_reg(mem, pos + len); - - ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); + ret = request_firmware(firmware, *filename, cs_dsp->dev); if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list end: %d\n", - ret); - return ERR_PTR(ret); + adsp_err(dsp, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; } - if (be32_to_cpu(val) != 0xbedead) - adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", - reg, be32_to_cpu(val)); - - /* Convert length from DSP words to bytes */ - len *= sizeof(u32); - - alg = kzalloc(len, GFP_KERNEL | GFP_DMA); - if (!alg) - return ERR_PTR(-ENOMEM); - - reg = dsp->ops->region_to_reg(mem, pos); - - ret = regmap_raw_read(dsp->regmap, reg, alg, len); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); - kfree(alg); - return ERR_PTR(ret); - } - - return alg; + return ret; } -static struct wm_adsp_alg_region * - wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) +static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, + const struct firmware **wmfw_firmware, + char **wmfw_filename, + const struct firmware **coeff_firmware, + char **coeff_filename) { - struct wm_adsp_alg_region *alg_region; + int ret = 0; - list_for_each_entry(alg_region, &dsp->alg_regions, list) { - if (id == alg_region->alg && type == alg_region->type) - return alg_region; - } + ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "wmfw"); + if (ret != 0) + return ret; - return NULL; -} - -static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, - int type, __be32 id, - __be32 base) -{ - struct wm_adsp_alg_region *alg_region; - - alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); - if (!alg_region) - return ERR_PTR(-ENOMEM); - - alg_region->type = type; - alg_region->alg = be32_to_cpu(id); - alg_region->base = be32_to_cpu(base); - - list_add_tail(&alg_region->list, &dsp->alg_regions); - - if (dsp->fw_ver > 0) - wm_adsp_ctl_fixup_base(dsp, alg_region); - - return alg_region; -} - -static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) -{ - struct wm_adsp_alg_region *alg_region; - - while (!list_empty(&dsp->alg_regions)) { - alg_region = list_first_entry(&dsp->alg_regions, - struct wm_adsp_alg_region, - list); - list_del(&alg_region->list); - kfree(alg_region); - } -} - -static void wmfw_parse_id_header(struct wm_adsp *dsp, - struct wmfw_id_hdr *fw, int nalgs) -{ - dsp->fw_id = be32_to_cpu(fw->id); - dsp->fw_id_version = be32_to_cpu(fw->ver); - - adsp_info(dsp, "Firmware: %x v%d.%d.%d, %d algorithms\n", - dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, - (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, - nalgs); -} - -static void wmfw_v3_parse_id_header(struct wm_adsp *dsp, - struct wmfw_v3_id_hdr *fw, int nalgs) -{ - dsp->fw_id = be32_to_cpu(fw->id); - dsp->fw_id_version = be32_to_cpu(fw->ver); - dsp->fw_vendor_id = be32_to_cpu(fw->vendor_id); - - adsp_info(dsp, "Firmware: %x vendor: 0x%x v%d.%d.%d, %d algorithms\n", - dsp->fw_id, dsp->fw_vendor_id, - (dsp->fw_id_version & 0xff0000) >> 16, - (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, - nalgs); -} - -static int wm_adsp_create_regions(struct wm_adsp *dsp, __be32 id, int nregions, - const int *type, __be32 *base) -{ - struct wm_adsp_alg_region *alg_region; - int i; - - for (i = 0; i < nregions; i++) { - alg_region = wm_adsp_create_region(dsp, type[i], id, base[i]); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - } + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, "bin"); return 0; } -static int wm_adsp1_setup_algs(struct wm_adsp *dsp) -{ - struct wmfw_adsp1_id_hdr adsp1_id; - struct wmfw_adsp1_alg_hdr *adsp1_alg; - struct wm_adsp_alg_region *alg_region; - const struct wm_adsp_region *mem; - unsigned int pos, len; - size_t n_algs; - int i, ret; - - mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); - if (WARN_ON(!mem)) - return -EINVAL; - - ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, - sizeof(adsp1_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - n_algs = be32_to_cpu(adsp1_id.n_algs); - - wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, - adsp1_id.fw.id, adsp1_id.zm); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, - adsp1_id.fw.id, adsp1_id.dm); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - - /* Calculate offset and length in DSP words */ - pos = sizeof(adsp1_id) / sizeof(u32); - len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); - - adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); - if (IS_ERR(adsp1_alg)) - return PTR_ERR(adsp1_alg); - - for (i = 0; i < n_algs; i++) { - adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", - i, be32_to_cpu(adsp1_alg[i].alg.id), - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp1_alg[i].dm), - be32_to_cpu(adsp1_alg[i].zm)); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, - adsp1_alg[i].alg.id, - adsp1_alg[i].dm); - if (IS_ERR(alg_region)) { - ret = PTR_ERR(alg_region); - goto out; - } - if (dsp->fw_ver == 0) { - if (i + 1 < n_algs) { - len = be32_to_cpu(adsp1_alg[i + 1].dm); - len -= be32_to_cpu(adsp1_alg[i].dm); - len *= 4; - wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0, - SNDRV_CTL_ELEM_TYPE_BYTES); - } else { - adsp_warn(dsp, "Missing length info for region DM with ID %x\n", - be32_to_cpu(adsp1_alg[i].alg.id)); - } - } - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, - adsp1_alg[i].alg.id, - adsp1_alg[i].zm); - if (IS_ERR(alg_region)) { - ret = PTR_ERR(alg_region); - goto out; - } - if (dsp->fw_ver == 0) { - if (i + 1 < n_algs) { - len = be32_to_cpu(adsp1_alg[i + 1].zm); - len -= be32_to_cpu(adsp1_alg[i].zm); - len *= 4; - wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0, - SNDRV_CTL_ELEM_TYPE_BYTES); - } else { - adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", - be32_to_cpu(adsp1_alg[i].alg.id)); - } - } - } - -out: - kfree(adsp1_alg); - return ret; -} - -static int wm_adsp2_setup_algs(struct wm_adsp *dsp) -{ - struct wmfw_adsp2_id_hdr adsp2_id; - struct wmfw_adsp2_alg_hdr *adsp2_alg; - struct wm_adsp_alg_region *alg_region; - const struct wm_adsp_region *mem; - unsigned int pos, len; - size_t n_algs; - int i, ret; - - mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); - if (WARN_ON(!mem)) - return -EINVAL; - - ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, - sizeof(adsp2_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - n_algs = be32_to_cpu(adsp2_id.n_algs); - - wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, - adsp2_id.fw.id, adsp2_id.xm); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, - adsp2_id.fw.id, adsp2_id.ym); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, - adsp2_id.fw.id, adsp2_id.zm); - if (IS_ERR(alg_region)) - return PTR_ERR(alg_region); - - /* Calculate offset and length in DSP words */ - pos = sizeof(adsp2_id) / sizeof(u32); - len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); - - adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); - if (IS_ERR(adsp2_alg)) - return PTR_ERR(adsp2_alg); - - for (i = 0; i < n_algs; i++) { - adsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", - i, be32_to_cpu(adsp2_alg[i].alg.id), - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp2_alg[i].xm), - be32_to_cpu(adsp2_alg[i].ym), - be32_to_cpu(adsp2_alg[i].zm)); - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, - adsp2_alg[i].alg.id, - adsp2_alg[i].xm); - if (IS_ERR(alg_region)) { - ret = PTR_ERR(alg_region); - goto out; - } - if (dsp->fw_ver == 0) { - if (i + 1 < n_algs) { - len = be32_to_cpu(adsp2_alg[i + 1].xm); - len -= be32_to_cpu(adsp2_alg[i].xm); - len *= 4; - wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0, - SNDRV_CTL_ELEM_TYPE_BYTES); - } else { - adsp_warn(dsp, "Missing length info for region XM with ID %x\n", - be32_to_cpu(adsp2_alg[i].alg.id)); - } - } - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, - adsp2_alg[i].alg.id, - adsp2_alg[i].ym); - if (IS_ERR(alg_region)) { - ret = PTR_ERR(alg_region); - goto out; - } - if (dsp->fw_ver == 0) { - if (i + 1 < n_algs) { - len = be32_to_cpu(adsp2_alg[i + 1].ym); - len -= be32_to_cpu(adsp2_alg[i].ym); - len *= 4; - wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0, - SNDRV_CTL_ELEM_TYPE_BYTES); - } else { - adsp_warn(dsp, "Missing length info for region YM with ID %x\n", - be32_to_cpu(adsp2_alg[i].alg.id)); - } - } - - alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, - adsp2_alg[i].alg.id, - adsp2_alg[i].zm); - if (IS_ERR(alg_region)) { - ret = PTR_ERR(alg_region); - goto out; - } - if (dsp->fw_ver == 0) { - if (i + 1 < n_algs) { - len = be32_to_cpu(adsp2_alg[i + 1].zm); - len -= be32_to_cpu(adsp2_alg[i].zm); - len *= 4; - wm_adsp_create_control(dsp, alg_region, 0, - len, NULL, 0, 0, - SNDRV_CTL_ELEM_TYPE_BYTES); - } else { - adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", - be32_to_cpu(adsp2_alg[i].alg.id)); - } - } - } - -out: - kfree(adsp2_alg); - return ret; -} - -static int wm_halo_create_regions(struct wm_adsp *dsp, __be32 id, - __be32 xm_base, __be32 ym_base) -{ - static const int types[] = { - WMFW_ADSP2_XM, WMFW_HALO_XM_PACKED, - WMFW_ADSP2_YM, WMFW_HALO_YM_PACKED - }; - __be32 bases[] = { xm_base, xm_base, ym_base, ym_base }; - - return wm_adsp_create_regions(dsp, id, ARRAY_SIZE(types), types, bases); -} - -static int wm_halo_setup_algs(struct wm_adsp *dsp) -{ - struct wmfw_halo_id_hdr halo_id; - struct wmfw_halo_alg_hdr *halo_alg; - const struct wm_adsp_region *mem; - unsigned int pos, len; - size_t n_algs; - int i, ret; - - mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); - if (WARN_ON(!mem)) - return -EINVAL; - - ret = regmap_raw_read(dsp->regmap, mem->base, &halo_id, - sizeof(halo_id)); - if (ret != 0) { - adsp_err(dsp, "Failed to read algorithm info: %d\n", - ret); - return ret; - } - - n_algs = be32_to_cpu(halo_id.n_algs); - - wmfw_v3_parse_id_header(dsp, &halo_id.fw, n_algs); - - ret = wm_halo_create_regions(dsp, halo_id.fw.id, - halo_id.xm_base, halo_id.ym_base); - if (ret) - return ret; - - /* Calculate offset and length in DSP words */ - pos = sizeof(halo_id) / sizeof(u32); - len = (sizeof(*halo_alg) * n_algs) / sizeof(u32); - - halo_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); - if (IS_ERR(halo_alg)) - return PTR_ERR(halo_alg); - - for (i = 0; i < n_algs; i++) { - adsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", - i, be32_to_cpu(halo_alg[i].alg.id), - (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(halo_alg[i].alg.ver) & 0xff, - be32_to_cpu(halo_alg[i].xm_base), - be32_to_cpu(halo_alg[i].ym_base)); - - ret = wm_halo_create_regions(dsp, halo_alg[i].alg.id, - halo_alg[i].xm_base, - halo_alg[i].ym_base); - if (ret) - goto out; - } - -out: - kfree(halo_alg); - return ret; -} - -static int wm_adsp_load_coeff(struct wm_adsp *dsp) -{ - LIST_HEAD(buf_list); - struct regmap *regmap = dsp->regmap; - struct wmfw_coeff_hdr *hdr; - struct wmfw_coeff_item *blk; - const struct firmware *firmware; - const struct wm_adsp_region *mem; - struct wm_adsp_alg_region *alg_region; - const char *region_name; - int ret, pos, blocks, type, offset, reg; - char *file; - struct wm_adsp_buf *buf; - - file = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (file == NULL) - return -ENOMEM; - - snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, - wm_adsp_fw[dsp->fw].file); - file[PAGE_SIZE - 1] = '\0'; - - ret = request_firmware(&firmware, file, dsp->dev); - if (ret != 0) { - adsp_warn(dsp, "Failed to request '%s'\n", file); - ret = 0; - goto out; - } - ret = -EINVAL; - - if (sizeof(*hdr) >= firmware->size) { - adsp_err(dsp, "%s: file too short, %zu bytes\n", - file, firmware->size); - goto out_fw; - } - - hdr = (void *)&firmware->data[0]; - if (memcmp(hdr->magic, "WMDR", 4) != 0) { - adsp_err(dsp, "%s: invalid magic\n", file); - goto out_fw; - } - - switch (be32_to_cpu(hdr->rev) & 0xff) { - case 1: - break; - default: - adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", - file, be32_to_cpu(hdr->rev) & 0xff); - ret = -EINVAL; - goto out_fw; - } - - adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, - (le32_to_cpu(hdr->ver) >> 16) & 0xff, - (le32_to_cpu(hdr->ver) >> 8) & 0xff, - le32_to_cpu(hdr->ver) & 0xff); - - pos = le32_to_cpu(hdr->len); - - blocks = 0; - while (pos < firmware->size && - sizeof(*blk) < firmware->size - pos) { - blk = (void *)(&firmware->data[pos]); - - type = le16_to_cpu(blk->type); - offset = le16_to_cpu(blk->offset); - - adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", - file, blocks, le32_to_cpu(blk->id), - (le32_to_cpu(blk->ver) >> 16) & 0xff, - (le32_to_cpu(blk->ver) >> 8) & 0xff, - le32_to_cpu(blk->ver) & 0xff); - adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", - file, blocks, le32_to_cpu(blk->len), offset, type); - - reg = 0; - region_name = "Unknown"; - switch (type) { - case (WMFW_NAME_TEXT << 8): - case (WMFW_INFO_TEXT << 8): - case (WMFW_METADATA << 8): - break; - case (WMFW_ABSOLUTE << 8): - /* - * Old files may use this for global - * coefficients. - */ - if (le32_to_cpu(blk->id) == dsp->fw_id && - offset == 0) { - region_name = "global coefficients"; - mem = wm_adsp_find_region(dsp, type); - if (!mem) { - adsp_err(dsp, "No ZM\n"); - break; - } - reg = dsp->ops->region_to_reg(mem, 0); - - } else { - region_name = "register"; - reg = offset; - } - break; - - case WMFW_ADSP1_DM: - case WMFW_ADSP1_ZM: - case WMFW_ADSP2_XM: - case WMFW_ADSP2_YM: - case WMFW_HALO_XM_PACKED: - case WMFW_HALO_YM_PACKED: - case WMFW_HALO_PM_PACKED: - adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", - file, blocks, le32_to_cpu(blk->len), - type, le32_to_cpu(blk->id)); - - mem = wm_adsp_find_region(dsp, type); - if (!mem) { - adsp_err(dsp, "No base for region %x\n", type); - break; - } - - alg_region = wm_adsp_find_alg_region(dsp, type, - le32_to_cpu(blk->id)); - if (alg_region) { - reg = alg_region->base; - reg = dsp->ops->region_to_reg(mem, reg); - reg += offset; - } else { - adsp_err(dsp, "No %x for algorithm %x\n", - type, le32_to_cpu(blk->id)); - } - break; - - default: - adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", - file, blocks, type, pos); - break; - } - - if (reg) { - if (le32_to_cpu(blk->len) > - firmware->size - pos - sizeof(*blk)) { - adsp_err(dsp, - "%s.%d: %s region len %d bytes exceeds file length %zu\n", - file, blocks, region_name, - le32_to_cpu(blk->len), - firmware->size); - ret = -EINVAL; - goto out_fw; - } - - buf = wm_adsp_buf_alloc(blk->data, - le32_to_cpu(blk->len), - &buf_list); - if (!buf) { - adsp_err(dsp, "Out of memory\n"); - ret = -ENOMEM; - goto out_fw; - } - - adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", - file, blocks, le32_to_cpu(blk->len), - reg); - ret = regmap_raw_write_async(regmap, reg, buf->buf, - le32_to_cpu(blk->len)); - if (ret != 0) { - adsp_err(dsp, - "%s.%d: Failed to write to %x in %s: %d\n", - file, blocks, reg, region_name, ret); - } - } - - pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; - blocks++; - } - - ret = regmap_async_complete(regmap); - if (ret != 0) - adsp_err(dsp, "Failed to complete async write: %d\n", ret); - - if (pos > firmware->size) - adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", - file, blocks, pos - firmware->size); - - wm_adsp_debugfs_save_binname(dsp, file); - -out_fw: - regmap_async_complete(regmap); - release_firmware(firmware); - wm_adsp_buf_free(&buf_list); -out: - kfree(file); - return ret; -} - -static int wm_adsp_create_name(struct wm_adsp *dsp) +static int wm_adsp_common_init(struct wm_adsp *dsp) { char *p; - if (!dsp->name) { - dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", - dsp->num); - if (!dsp->name) - return -ENOMEM; - } + INIT_LIST_HEAD(&dsp->compr_list); + INIT_LIST_HEAD(&dsp->buffer_list); if (!dsp->fwf_name) { - p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); + p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL); if (!p) return -ENOMEM; @@ -2775,28 +803,16 @@ static int wm_adsp_create_name(struct wm_adsp *dsp) return 0; } -static int wm_adsp_common_init(struct wm_adsp *dsp) +int wm_adsp1_init(struct wm_adsp *dsp) { int ret; - ret = wm_adsp_create_name(dsp); + dsp->cs_dsp.client_ops = &wm_adsp1_client_ops; + + ret = cs_dsp_adsp1_init(&dsp->cs_dsp); if (ret) return ret; - INIT_LIST_HEAD(&dsp->alg_regions); - INIT_LIST_HEAD(&dsp->ctl_list); - INIT_LIST_HEAD(&dsp->compr_list); - INIT_LIST_HEAD(&dsp->buffer_list); - - mutex_init(&dsp->pwr_lock); - - return 0; -} - -int wm_adsp1_init(struct wm_adsp *dsp) -{ - dsp->ops = &wm_adsp1_ops; - return wm_adsp_common_init(dsp); } EXPORT_SYMBOL_GPL(wm_adsp1_init); @@ -2808,314 +824,49 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_coeff_ctl *ctl; - int ret; - unsigned int val; + int ret = 0; + char *wmfw_filename = NULL; + const struct firmware *wmfw_firmware = NULL; + char *coeff_filename = NULL; + const struct firmware *coeff_firmware = NULL; dsp->component = component; - mutex_lock(&dsp->pwr_lock); - switch (event) { case SND_SOC_DAPM_POST_PMU: - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, - ADSP1_SYS_ENA, ADSP1_SYS_ENA); + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + break; - /* - * For simplicity set the DSP clock rate to be the - * SYSCLK rate rather than making it configurable. - */ - if (dsp->sysclk_reg) { - ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); - if (ret != 0) { - adsp_err(dsp, "Failed to read SYSCLK state: %d\n", - ret); - goto err_mutex; - } + ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename, + wm_adsp_fw_text[dsp->fw]); - val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; - - ret = regmap_update_bits(dsp->regmap, - dsp->base + ADSP1_CONTROL_31, - ADSP1_CLK_SEL_MASK, val); - if (ret != 0) { - adsp_err(dsp, "Failed to set clock rate: %d\n", - ret); - goto err_mutex; - } - } - - ret = wm_adsp_load(dsp); - if (ret != 0) - goto err_ena; - - ret = wm_adsp1_setup_algs(dsp); - if (ret != 0) - goto err_ena; - - ret = wm_adsp_load_coeff(dsp); - if (ret != 0) - goto err_ena; - - /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp); - if (ret != 0) - goto err_ena; - - /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp); - if (ret != 0) - goto err_ena; - - dsp->booted = true; - - /* Start the core running */ - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, - ADSP1_CORE_ENA | ADSP1_START, - ADSP1_CORE_ENA | ADSP1_START); - - dsp->running = true; + wm_adsp_release_firmware_files(dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); break; - case SND_SOC_DAPM_PRE_PMD: - dsp->running = false; - dsp->booted = false; - - /* Halt the core */ - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, - ADSP1_CORE_ENA | ADSP1_START, 0); - - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, - ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); - - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, - ADSP1_SYS_ENA, 0); - - list_for_each_entry(ctl, &dsp->ctl_list, list) - ctl->enabled = 0; - - - wm_adsp_free_alg_regions(dsp); + cs_dsp_adsp1_power_down(&dsp->cs_dsp); break; - default: break; } - mutex_unlock(&dsp->pwr_lock); - - return 0; - -err_ena: - regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, - ADSP1_SYS_ENA, 0); -err_mutex: - mutex_unlock(&dsp->pwr_lock); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp1_event); -static int wm_adsp2v2_enable_core(struct wm_adsp *dsp) -{ - unsigned int val; - int ret, count; - - /* Wait for the RAM to start, should be near instantaneous */ - for (count = 0; count < 10; ++count) { - ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); - if (ret != 0) - return ret; - - if (val & ADSP2_RAM_RDY) - break; - - usleep_range(250, 500); - } - - if (!(val & ADSP2_RAM_RDY)) { - adsp_err(dsp, "Failed to start DSP RAM\n"); - return -EBUSY; - } - - adsp_dbg(dsp, "RAM ready after %d polls\n", count); - - return 0; -} - -static int wm_adsp2_enable_core(struct wm_adsp *dsp) -{ - int ret; - - ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, ADSP2_SYS_ENA); - if (ret != 0) - return ret; - - return wm_adsp2v2_enable_core(dsp); -} - -static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) -{ - struct regmap *regmap = dsp->regmap; - unsigned int code0, code1, lock_reg; - - if (!(lock_regions & WM_ADSP2_REGION_ALL)) - return 0; - - lock_regions &= WM_ADSP2_REGION_ALL; - lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; - - while (lock_regions) { - code0 = code1 = 0; - if (lock_regions & BIT(0)) { - code0 = ADSP2_LOCK_CODE_0; - code1 = ADSP2_LOCK_CODE_1; - } - if (lock_regions & BIT(1)) { - code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; - code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; - } - regmap_write(regmap, lock_reg, code0); - regmap_write(regmap, lock_reg, code1); - lock_regions >>= 2; - lock_reg += 2; - } - - return 0; -} - -static int wm_adsp2_enable_memory(struct wm_adsp *dsp) -{ - return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_MEM_ENA, ADSP2_MEM_ENA); -} - -static void wm_adsp2_disable_memory(struct wm_adsp *dsp) -{ - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_MEM_ENA, 0); -} - -static void wm_adsp2_disable_core(struct wm_adsp *dsp) -{ - regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); - - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_SYS_ENA, 0); -} - -static void wm_adsp2v2_disable_core(struct wm_adsp *dsp) -{ - regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); - regmap_write(dsp->regmap, dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); -} - -static void wm_adsp_boot_work(struct work_struct *work) -{ - struct wm_adsp *dsp = container_of(work, - struct wm_adsp, - boot_work); - int ret; - - mutex_lock(&dsp->pwr_lock); - - if (dsp->ops->enable_memory) { - ret = dsp->ops->enable_memory(dsp); - if (ret != 0) - goto err_mutex; - } - - if (dsp->ops->enable_core) { - ret = dsp->ops->enable_core(dsp); - if (ret != 0) - goto err_mem; - } - - ret = wm_adsp_load(dsp); - if (ret != 0) - goto err_ena; - - ret = dsp->ops->setup_algs(dsp); - if (ret != 0) - goto err_ena; - - ret = wm_adsp_load_coeff(dsp); - if (ret != 0) - goto err_ena; - - /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp); - if (ret != 0) - goto err_ena; - - if (dsp->ops->disable_core) - dsp->ops->disable_core(dsp); - - dsp->booted = true; - - mutex_unlock(&dsp->pwr_lock); - - return; - -err_ena: - if (dsp->ops->disable_core) - dsp->ops->disable_core(dsp); -err_mem: - if (dsp->ops->disable_memory) - dsp->ops->disable_memory(dsp); -err_mutex: - mutex_unlock(&dsp->pwr_lock); -} - -static int wm_halo_configure_mpu(struct wm_adsp *dsp, unsigned int lock_regions) -{ - struct reg_sequence config[] = { - { dsp->base + HALO_MPU_LOCK_CONFIG, 0x5555 }, - { dsp->base + HALO_MPU_LOCK_CONFIG, 0xAAAA }, - { dsp->base + HALO_MPU_XMEM_ACCESS_0, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_YMEM_ACCESS_0, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_WINDOW_ACCESS_0, lock_regions }, - { dsp->base + HALO_MPU_XREG_ACCESS_0, lock_regions }, - { dsp->base + HALO_MPU_YREG_ACCESS_0, lock_regions }, - { dsp->base + HALO_MPU_XMEM_ACCESS_1, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_YMEM_ACCESS_1, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_WINDOW_ACCESS_1, lock_regions }, - { dsp->base + HALO_MPU_XREG_ACCESS_1, lock_regions }, - { dsp->base + HALO_MPU_YREG_ACCESS_1, lock_regions }, - { dsp->base + HALO_MPU_XMEM_ACCESS_2, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_YMEM_ACCESS_2, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_WINDOW_ACCESS_2, lock_regions }, - { dsp->base + HALO_MPU_XREG_ACCESS_2, lock_regions }, - { dsp->base + HALO_MPU_YREG_ACCESS_2, lock_regions }, - { dsp->base + HALO_MPU_XMEM_ACCESS_3, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_YMEM_ACCESS_3, 0xFFFFFFFF }, - { dsp->base + HALO_MPU_WINDOW_ACCESS_3, lock_regions }, - { dsp->base + HALO_MPU_XREG_ACCESS_3, lock_regions }, - { dsp->base + HALO_MPU_YREG_ACCESS_3, lock_regions }, - { dsp->base + HALO_MPU_LOCK_CONFIG, 0 }, - }; - - return regmap_multi_reg_write(dsp->regmap, config, ARRAY_SIZE(config)); -} - int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) { struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - int ret; - ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, - ADSP2_CLK_SEL_MASK, - freq << ADSP2_CLK_SEL_SHIFT); - if (ret) - adsp_err(dsp, "Failed to set clock rate: %d\n", ret); - - return ret; + return cs_dsp_set_dspclk(&dsp->cs_dsp, freq); } EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); @@ -3145,11 +896,12 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, struct wm_adsp *dsp = &dsps[mc->shift - 1]; char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); + if (dsp->preloaded == ucontrol->value.integer.value[0]) + return 0; - dsp->preloaded = ucontrol->value.integer.value[0]; + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); - if (ucontrol->value.integer.value[0]) + if (ucontrol->value.integer.value[0] || dsp->toggle_preload) snd_soc_component_force_enable_pin(component, preload); else snd_soc_component_disable_pin(component, preload); @@ -3158,20 +910,42 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, flush_work(&dsp->boot_work); + dsp->preloaded = ucontrol->value.integer.value[0]; + + if (dsp->toggle_preload) { + snd_soc_component_disable_pin(component, preload); + snd_soc_dapm_sync(dapm); + } + return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); -static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) +static void wm_adsp_boot_work(struct work_struct *work) { - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, - ADSP2_WDT_ENA_MASK, 0); -} + struct wm_adsp *dsp = container_of(work, + struct wm_adsp, + boot_work); + int ret = 0; + char *wmfw_filename = NULL; + const struct firmware *wmfw_firmware = NULL; + char *coeff_filename = NULL; + const struct firmware *coeff_firmware = NULL; -static void wm_halo_stop_watchdog(struct wm_adsp *dsp) -{ - regmap_update_bits(dsp->regmap, dsp->base + HALO_WDT_CONTROL, - HALO_WDT_EN_MASK, 0); + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + return; + + cs_dsp_power_up(&dsp->cs_dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename, + wm_adsp_fw_text[dsp->fw]); + + wm_adsp_release_firmware_files(dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); } int wm_adsp_early_event(struct snd_soc_dapm_widget *w, @@ -3180,33 +954,13 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - struct wm_coeff_ctl *ctl; switch (event) { case SND_SOC_DAPM_PRE_PMU: queue_work(system_unbound_wq, &dsp->boot_work); break; case SND_SOC_DAPM_PRE_PMD: - mutex_lock(&dsp->pwr_lock); - - wm_adsp_debugfs_clear(dsp); - - dsp->fw_id = 0; - dsp->fw_id_version = 0; - - dsp->booted = false; - - if (dsp->ops->disable_memory) - dsp->ops->disable_memory(dsp); - - list_for_each_entry(ctl, &dsp->ctl_list, list) - ctl->enabled = 0; - - wm_adsp_free_alg_regions(dsp); - - mutex_unlock(&dsp->pwr_lock); - - adsp_dbg(dsp, "Shutdown complete\n"); + cs_dsp_power_down(&dsp->cs_dsp); break; default: break; @@ -3216,17 +970,24 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(wm_adsp_early_event); -static int wm_adsp2_start_core(struct wm_adsp *dsp) +static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp) { - return regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_CORE_ENA | ADSP2_START, - ADSP2_CORE_ENA | ADSP2_START); + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (wm_adsp_fw[dsp->fw].num_caps != 0) + return wm_adsp_buffer_init(dsp); + + return 0; } -static void wm_adsp2_stop_core(struct wm_adsp *dsp) +static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) { - regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_CORE_ENA | ADSP2_START, 0); + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (wm_adsp_fw[dsp->fw].num_caps != 0) + wm_adsp_buffer_free(dsp); + + dsp->fatal_error = false; } int wm_adsp_event(struct snd_soc_dapm_widget *w, @@ -3235,127 +996,32 @@ int wm_adsp_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); struct wm_adsp *dsp = &dsps[w->shift]; - int ret; + int ret = 0; switch (event) { case SND_SOC_DAPM_POST_PMU: flush_work(&dsp->boot_work); - - mutex_lock(&dsp->pwr_lock); - - if (!dsp->booted) { - ret = -EIO; - goto err; - } - - if (dsp->ops->enable_core) { - ret = dsp->ops->enable_core(dsp); - if (ret != 0) - goto err; - } - - /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp); - if (ret != 0) - goto err; - - if (dsp->ops->lock_memory) { - ret = dsp->ops->lock_memory(dsp, dsp->lock_regions); - if (ret != 0) { - adsp_err(dsp, "Error configuring MPU: %d\n", - ret); - goto err; - } - } - - if (dsp->ops->start_core) { - ret = dsp->ops->start_core(dsp); - if (ret != 0) - goto err; - } - - if (wm_adsp_fw[dsp->fw].num_caps != 0) { - ret = wm_adsp_buffer_init(dsp); - if (ret < 0) - goto err; - } - - dsp->running = true; - - mutex_unlock(&dsp->pwr_lock); + ret = cs_dsp_run(&dsp->cs_dsp); break; - case SND_SOC_DAPM_PRE_PMD: - /* Tell the firmware to cleanup */ - wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); - - if (dsp->ops->stop_watchdog) - dsp->ops->stop_watchdog(dsp); - - /* Log firmware state, it can be useful for analysis */ - if (dsp->ops->show_fw_status) - dsp->ops->show_fw_status(dsp); - - mutex_lock(&dsp->pwr_lock); - - dsp->running = false; - - if (dsp->ops->stop_core) - dsp->ops->stop_core(dsp); - if (dsp->ops->disable_core) - dsp->ops->disable_core(dsp); - - if (wm_adsp_fw[dsp->fw].num_caps != 0) - wm_adsp_buffer_free(dsp); - - dsp->fatal_error = false; - - mutex_unlock(&dsp->pwr_lock); - - adsp_dbg(dsp, "Execution stopped\n"); + cs_dsp_stop(&dsp->cs_dsp); break; - default: break; } - return 0; -err: - if (dsp->ops->stop_core) - dsp->ops->stop_core(dsp); - if (dsp->ops->disable_core) - dsp->ops->disable_core(dsp); - mutex_unlock(&dsp->pwr_lock); return ret; } EXPORT_SYMBOL_GPL(wm_adsp_event); -static int wm_halo_start_core(struct wm_adsp *dsp) -{ - return regmap_update_bits(dsp->regmap, - dsp->base + HALO_CCM_CORE_CONTROL, - HALO_CORE_RESET | HALO_CORE_EN, - HALO_CORE_RESET | HALO_CORE_EN); -} - -static void wm_halo_stop_core(struct wm_adsp *dsp) -{ - regmap_update_bits(dsp->regmap, dsp->base + HALO_CCM_CORE_CONTROL, - HALO_CORE_EN, 0); - - /* reset halo core with CORE_SOFT_RESET */ - regmap_update_bits(dsp->regmap, dsp->base + HALO_CORE_SOFT_RESET, - HALO_CORE_SOFT_RESET_MASK, 1); -} - int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) { char preload[32]; - snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); snd_soc_component_disable_pin(component, preload); - wm_adsp2_init_debugfs(dsp, component); + cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root); dsp->component = component; @@ -3365,7 +1031,7 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) { - wm_adsp2_cleanup_debugfs(dsp); + cs_dsp_cleanup_debugfs(&dsp->cs_dsp); return 0; } @@ -3375,37 +1041,16 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; - ret = wm_adsp_common_init(dsp); + INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); + + dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr); + dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; + + ret = cs_dsp_adsp2_init(&dsp->cs_dsp); if (ret) return ret; - switch (dsp->rev) { - case 0: - /* - * Disable the DSP memory by default when in reset for a small - * power saving. - */ - ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, - ADSP2_MEM_ENA, 0); - if (ret) { - adsp_err(dsp, - "Failed to clear memory retention: %d\n", ret); - return ret; - } - - dsp->ops = &wm_adsp2_ops[0]; - break; - case 1: - dsp->ops = &wm_adsp2_ops[1]; - break; - default: - dsp->ops = &wm_adsp2_ops[2]; - break; - } - - INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); - - return 0; + return wm_adsp_common_init(dsp); } EXPORT_SYMBOL_GPL(wm_adsp2_init); @@ -3413,28 +1058,22 @@ int wm_halo_init(struct wm_adsp *dsp) { int ret; - ret = wm_adsp_common_init(dsp); + INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); + + dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr); + dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; + + ret = cs_dsp_halo_init(&dsp->cs_dsp); if (ret) return ret; - dsp->ops = &wm_halo_ops; - - INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); - - return 0; + return wm_adsp_common_init(dsp); } EXPORT_SYMBOL_GPL(wm_halo_init); void wm_adsp2_remove(struct wm_adsp *dsp) { - struct wm_coeff_ctl *ctl; - - while (!list_empty(&dsp->ctl_list)) { - ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, - list); - list_del(&ctl->list); - wm_adsp_free_ctl_blk(ctl); - } + cs_dsp_remove(&dsp->cs_dsp); } EXPORT_SYMBOL_GPL(wm_adsp2_remove); @@ -3487,7 +1126,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) struct snd_soc_pcm_runtime *rtd = stream->private_data; int ret = 0; - mutex_lock(&dsp->pwr_lock); + mutex_lock(&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", @@ -3527,7 +1166,7 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) stream->runtime->private_data = compr; out: - mutex_unlock(&dsp->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return ret; } @@ -3539,7 +1178,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->pwr_lock); + mutex_lock(&dsp->cs_dsp.pwr_lock); wm_adsp_compr_detach(compr); list_del(&compr->list); @@ -3547,7 +1186,7 @@ int wm_adsp_compr_free(struct snd_soc_component *component, kfree(compr->raw_buf); kfree(compr); - mutex_unlock(&dsp->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return 0; } @@ -3566,7 +1205,7 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || - params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { + params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) { compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", params->buffer.fragment_size, params->buffer.fragments); @@ -3605,7 +1244,7 @@ static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) { - return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; + return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE; } int wm_adsp_compr_set_params(struct snd_soc_component *component, @@ -3661,88 +1300,19 @@ int wm_adsp_compr_get_caps(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); -static int wm_adsp_read_raw_data_block(struct wm_adsp *dsp, int mem_type, - unsigned int mem_addr, - unsigned int num_words, __be32 *data) -{ - struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); - unsigned int reg; - int ret; - - if (!mem) - return -EINVAL; - - reg = dsp->ops->region_to_reg(mem, mem_addr); - - ret = regmap_raw_read(dsp->regmap, reg, data, - sizeof(*data) * num_words); - if (ret < 0) - return ret; - - return 0; -} - -static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, - unsigned int mem_addr, u32 *data) -{ - __be32 raw; - int ret; - - ret = wm_adsp_read_raw_data_block(dsp, mem_type, mem_addr, 1, &raw); - if (ret < 0) - return ret; - - *data = be32_to_cpu(raw) & 0x00ffffffu; - - return 0; -} - -static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, - unsigned int mem_addr, u32 data) -{ - struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); - __be32 val = cpu_to_be32(data & 0x00ffffffu); - unsigned int reg; - - if (!mem) - return -EINVAL; - - reg = dsp->ops->region_to_reg(mem, mem_addr); - - return regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); -} - static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 *data) { - return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, - buf->host_buf_ptr + field_offset, data); + return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, + buf->host_buf_ptr + field_offset, data); } static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, unsigned int field_offset, u32 data) { - return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, - buf->host_buf_ptr + field_offset, data); -} - -static void wm_adsp_remove_padding(u32 *buf, int nwords) -{ - const __be32 *pack_in = (__be32 *)buf; - u8 *pack_out = (u8 *)buf; - int i; - - /* - * DSP words from the register map have pad bytes and the data bytes - * are in swapped order. This swaps back to the original little-endian - * order and strips the pad bytes. - */ - for (i = 0; i < nwords; i++) { - u32 word = be32_to_cpu(*pack_in++); - *pack_out++ = (u8)word; - *pack_out++ = (u8)(word >> 8); - *pack_out++ = (u8)(word >> 16); - } + return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, + buf->host_buf_ptr + field_offset, + data); } static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) @@ -3810,12 +1380,12 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) { - struct wm_adsp_alg_region *alg_region; + struct cs_dsp_alg_region *alg_region; struct wm_adsp_compr_buf *buf; u32 xmalg, addr, magic; int i, ret; - alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); + alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id); if (!alg_region) { adsp_err(dsp, "No algorithm region found\n"); return -EINVAL; @@ -3825,10 +1395,10 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) if (!buf) return -ENOMEM; - xmalg = dsp->ops->sys_config_size / sizeof(__be32); + xmalg = dsp->sys_config_size / sizeof(__be32); addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); - ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); + ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic); if (ret < 0) return ret; @@ -3837,8 +1407,8 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); for (i = 0; i < 5; ++i) { - ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, - &buf->host_buf_ptr); + ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, + &buf->host_buf_ptr); if (ret < 0) return ret; @@ -3862,40 +1432,37 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) return 0; } -static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) +static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) { struct wm_adsp_host_buf_coeff_v1 coeff_v1; struct wm_adsp_compr_buf *buf; - unsigned int reg, version; - __be32 bufp; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); + unsigned int version; int ret, i; - ret = wm_coeff_base_reg(ctl, ®); - if (ret) - return ret; - for (i = 0; i < 5; ++i) { - ret = regmap_raw_read(ctl->dsp->regmap, reg, &bufp, sizeof(bufp)); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, + min(cs_ctl->len, sizeof(coeff_v1))); if (ret < 0) return ret; - if (bufp) + if (coeff_v1.host_buf_ptr) break; usleep_range(1000, 2000); } - if (!bufp) { - adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); + if (!coeff_v1.host_buf_ptr) { + adsp_err(dsp, "Failed to acquire host buffer\n"); return -EIO; } - buf = wm_adsp_buffer_alloc(ctl->dsp); + buf = wm_adsp_buffer_alloc(dsp); if (!buf) return -ENOMEM; - buf->host_buf_mem_type = ctl->alg_region.type; - buf->host_buf_ptr = be32_to_cpu(bufp); + buf->host_buf_mem_type = cs_ctl->alg_region.type; + buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr); ret = wm_adsp_buffer_populate(buf); if (ret < 0) @@ -3905,29 +1472,24 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) * v0 host_buffer coefficients didn't have versioning, so if the * control is one word, assume version 0. */ - if (ctl->len == 4) { + if (cs_ctl->len == 4) { compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); return 0; } - ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, - sizeof(coeff_v1)); - if (ret < 0) - return ret; - version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { - adsp_err(ctl->dsp, + adsp_err(dsp, "Host buffer coeff ver %u > supported version %u\n", version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); return -EINVAL; } - wm_adsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); + cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); - buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, + buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part, (char *)&coeff_v1.name); compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", @@ -3938,17 +1500,17 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) static int wm_adsp_buffer_init(struct wm_adsp *dsp) { - struct wm_coeff_ctl *ctl; + struct cs_dsp_coeff_ctl *cs_ctl; int ret; - list_for_each_entry(ctl, &dsp->ctl_list, list) { - if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) { + if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) continue; - if (!ctl->enabled) + if (!cs_ctl->enabled) continue; - ret = wm_adsp_buffer_parse_coeff(ctl); + ret = wm_adsp_buffer_parse_coeff(cs_ctl); if (ret < 0) { adsp_err(dsp, "Failed to parse coeff: %d\n", ret); goto error; @@ -4016,7 +1578,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, compr_dbg(compr, "Trigger: %d\n", cmd); - mutex_lock(&dsp->pwr_lock); + mutex_lock(&dsp->cs_dsp.pwr_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -4052,7 +1614,7 @@ int wm_adsp_compr_trigger(struct snd_soc_component *component, break; } - mutex_unlock(&dsp->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return ret; } @@ -4101,7 +1663,7 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) avail += wm_adsp_buffer_size(buf); compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", - buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); + buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE); buf->avail = avail; @@ -4114,7 +1676,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) struct wm_adsp_compr *compr; int ret = 0; - mutex_lock(&dsp->pwr_lock); + mutex_lock(&dsp->cs_dsp.pwr_lock); if (list_empty(&dsp->buffer_list)) { ret = -ENODEV; @@ -4152,7 +1714,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) } out: - mutex_unlock(&dsp->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return ret; } @@ -4182,7 +1744,7 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, compr_dbg(compr, "Pointer request\n"); - mutex_lock(&dsp->pwr_lock); + mutex_lock(&dsp->cs_dsp.pwr_lock); buf = compr->buf; @@ -4222,11 +1784,11 @@ int wm_adsp_compr_pointer(struct snd_soc_component *component, } tstamp->copied_total = compr->copied_total; - tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; + tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; tstamp->sampling_rate = compr->sample_rate; out: - mutex_unlock(&dsp->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return ret; } @@ -4264,12 +1826,12 @@ static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) return 0; /* Read data from DSP */ - ret = wm_adsp_read_raw_data_block(buf->dsp, mem_type, adsp_addr, - nwords, (__be32 *)compr->raw_buf); + ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr, + nwords, (__be32 *)compr->raw_buf); if (ret < 0) return ret; - wm_adsp_remove_padding(compr->raw_buf, nwords); + cs_dsp_remove_padding(compr->raw_buf, nwords); /* update read index to account for words read */ buf->read_index += nwords; @@ -4301,7 +1863,7 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr, return -EIO; } - count /= WM_ADSP_DATA_WORD_SIZE; + count /= CS_DSP_DATA_WORD_SIZE; do { nwords = wm_adsp_buffer_capture_block(compr, count); @@ -4311,7 +1873,7 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr, return nwords; } - nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; + nbytes = nwords * CS_DSP_DATA_WORD_SIZE; compr_dbg(compr, "Read %d bytes\n", nbytes); @@ -4338,21 +1900,22 @@ int wm_adsp_compr_copy(struct snd_soc_component *component, struct wm_adsp *dsp = compr->dsp; int ret; - mutex_lock(&dsp->pwr_lock); + mutex_lock(&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->pwr_lock); + mutex_unlock(&dsp->cs_dsp.pwr_lock); return ret; } EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); -static void wm_adsp_fatal_error(struct wm_adsp *dsp) +static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp) { + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); struct wm_adsp_compr *compr; dsp->fatal_error = true; @@ -4366,64 +1929,8 @@ static void wm_adsp_fatal_error(struct wm_adsp *dsp) irqreturn_t wm_adsp2_bus_error(int irq, void *data) { struct wm_adsp *dsp = (struct wm_adsp *)data; - unsigned int val; - struct regmap *regmap = dsp->regmap; - int ret = 0; - mutex_lock(&dsp->pwr_lock); - - ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); - if (ret) { - adsp_err(dsp, - "Failed to read Region Lock Ctrl register: %d\n", ret); - goto error; - } - - if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { - adsp_err(dsp, "watchdog timeout error\n"); - dsp->ops->stop_watchdog(dsp); - wm_adsp_fatal_error(dsp); - } - - if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { - if (val & ADSP2_ADDR_ERR_MASK) - adsp_err(dsp, "bus error: address error\n"); - else - adsp_err(dsp, "bus error: region lock error\n"); - - ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); - if (ret) { - adsp_err(dsp, - "Failed to read Bus Err Addr register: %d\n", - ret); - goto error; - } - - adsp_err(dsp, "bus error address = 0x%x\n", - val & ADSP2_BUS_ERR_ADDR_MASK); - - ret = regmap_read(regmap, - dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, - &val); - if (ret) { - adsp_err(dsp, - "Failed to read Pmem Xmem Err Addr register: %d\n", - ret); - goto error; - } - - adsp_err(dsp, "xmem error address = 0x%x\n", - val & ADSP2_XMEM_ERR_ADDR_MASK); - adsp_err(dsp, "pmem error address = 0x%x\n", - (val & ADSP2_PMEM_ERR_ADDR_MASK) >> - ADSP2_PMEM_ERR_ADDR_SHIFT); - } - - regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, - ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); - -error: - mutex_unlock(&dsp->pwr_lock); + cs_dsp_adsp2_bus_error(&dsp->cs_dsp); return IRQ_HANDLED; } @@ -4432,55 +1939,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); irqreturn_t wm_halo_bus_error(int irq, void *data) { struct wm_adsp *dsp = (struct wm_adsp *)data; - struct regmap *regmap = dsp->regmap; - unsigned int fault[6]; - struct reg_sequence clear[] = { - { dsp->base + HALO_MPU_XM_VIO_STATUS, 0x0 }, - { dsp->base + HALO_MPU_YM_VIO_STATUS, 0x0 }, - { dsp->base + HALO_MPU_PM_VIO_STATUS, 0x0 }, - }; - int ret; - mutex_lock(&dsp->pwr_lock); - - ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_1, - fault); - if (ret) { - adsp_warn(dsp, "Failed to read AHB DEBUG_1: %d\n", ret); - goto exit_unlock; - } - - adsp_warn(dsp, "AHB: STATUS: 0x%x ADDR: 0x%x\n", - *fault & HALO_AHBM_FLAGS_ERR_MASK, - (*fault & HALO_AHBM_CORE_ERR_ADDR_MASK) >> - HALO_AHBM_CORE_ERR_ADDR_SHIFT); - - ret = regmap_read(regmap, dsp->base_sysinfo + HALO_AHBM_WINDOW_DEBUG_0, - fault); - if (ret) { - adsp_warn(dsp, "Failed to read AHB DEBUG_0: %d\n", ret); - goto exit_unlock; - } - - adsp_warn(dsp, "AHB: SYS_ADDR: 0x%x\n", *fault); - - ret = regmap_bulk_read(regmap, dsp->base + HALO_MPU_XM_VIO_ADDR, - fault, ARRAY_SIZE(fault)); - if (ret) { - adsp_warn(dsp, "Failed to read MPU fault info: %d\n", ret); - goto exit_unlock; - } - - adsp_warn(dsp, "XM: STATUS:0x%x ADDR:0x%x\n", fault[1], fault[0]); - adsp_warn(dsp, "YM: STATUS:0x%x ADDR:0x%x\n", fault[3], fault[2]); - adsp_warn(dsp, "PM: STATUS:0x%x ADDR:0x%x\n", fault[5], fault[4]); - - ret = regmap_multi_reg_write(dsp->regmap, clear, ARRAY_SIZE(clear)); - if (ret) - adsp_warn(dsp, "Failed to clear MPU status: %d\n", ret); - -exit_unlock: - mutex_unlock(&dsp->pwr_lock); + cs_dsp_halo_bus_error(&dsp->cs_dsp); return IRQ_HANDLED; } @@ -4490,99 +1950,23 @@ irqreturn_t wm_halo_wdt_expire(int irq, void *data) { struct wm_adsp *dsp = data; - mutex_lock(&dsp->pwr_lock); - - adsp_warn(dsp, "WDT Expiry Fault\n"); - dsp->ops->stop_watchdog(dsp); - wm_adsp_fatal_error(dsp); - - mutex_unlock(&dsp->pwr_lock); + cs_dsp_halo_wdt_expire(&dsp->cs_dsp); return IRQ_HANDLED; } EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); -static const struct wm_adsp_ops wm_adsp1_ops = { - .validate_version = wm_adsp_validate_version, - .parse_sizes = wm_adsp1_parse_sizes, - .region_to_reg = wm_adsp_region_to_reg, +static const struct cs_dsp_client_ops wm_adsp1_client_ops = { + .control_add = wm_adsp_control_add, + .control_remove = wm_adsp_control_remove, }; -static const struct wm_adsp_ops wm_adsp2_ops[] = { - { - .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), - .parse_sizes = wm_adsp2_parse_sizes, - .validate_version = wm_adsp_validate_version, - .setup_algs = wm_adsp2_setup_algs, - .region_to_reg = wm_adsp_region_to_reg, - - .show_fw_status = wm_adsp2_show_fw_status, - - .enable_memory = wm_adsp2_enable_memory, - .disable_memory = wm_adsp2_disable_memory, - - .enable_core = wm_adsp2_enable_core, - .disable_core = wm_adsp2_disable_core, - - .start_core = wm_adsp2_start_core, - .stop_core = wm_adsp2_stop_core, - - }, - { - .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), - .parse_sizes = wm_adsp2_parse_sizes, - .validate_version = wm_adsp_validate_version, - .setup_algs = wm_adsp2_setup_algs, - .region_to_reg = wm_adsp_region_to_reg, - - .show_fw_status = wm_adsp2v2_show_fw_status, - - .enable_memory = wm_adsp2_enable_memory, - .disable_memory = wm_adsp2_disable_memory, - .lock_memory = wm_adsp2_lock, - - .enable_core = wm_adsp2v2_enable_core, - .disable_core = wm_adsp2v2_disable_core, - - .start_core = wm_adsp2_start_core, - .stop_core = wm_adsp2_stop_core, - }, - { - .sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr), - .parse_sizes = wm_adsp2_parse_sizes, - .validate_version = wm_adsp_validate_version, - .setup_algs = wm_adsp2_setup_algs, - .region_to_reg = wm_adsp_region_to_reg, - - .show_fw_status = wm_adsp2v2_show_fw_status, - .stop_watchdog = wm_adsp_stop_watchdog, - - .enable_memory = wm_adsp2_enable_memory, - .disable_memory = wm_adsp2_disable_memory, - .lock_memory = wm_adsp2_lock, - - .enable_core = wm_adsp2v2_enable_core, - .disable_core = wm_adsp2v2_disable_core, - - .start_core = wm_adsp2_start_core, - .stop_core = wm_adsp2_stop_core, - }, -}; - -static const struct wm_adsp_ops wm_halo_ops = { - .sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr), - .parse_sizes = wm_adsp2_parse_sizes, - .validate_version = wm_halo_validate_version, - .setup_algs = wm_halo_setup_algs, - .region_to_reg = wm_halo_region_to_reg, - - .show_fw_status = wm_halo_show_fw_status, - .stop_watchdog = wm_halo_stop_watchdog, - - .lock_memory = wm_halo_configure_mpu, - - .start_core = wm_halo_start_core, - .stop_core = wm_halo_stop_core, +static const struct cs_dsp_client_ops wm_adsp2_client_ops = { + .control_add = wm_adsp_control_add, + .control_remove = wm_adsp_control_remove, + .post_run = wm_adsp_event_post_run, + .post_stop = wm_adsp_event_post_stop, + .watchdog_expired = wm_adsp_fatal_error, }; MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index f22131d9cc..7f4fabbc6a 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -10,128 +10,45 @@ #ifndef __WM_ADSP_H #define __WM_ADSP_H +#include +#include + #include #include #include -#include "wmfw.h" - /* Return values for wm_adsp_compr_handle_irq */ #define WM_ADSP_COMPR_OK 0 #define WM_ADSP_COMPR_VOICE_TRIGGER 1 -#define WM_ADSP2_REGION_0 BIT(0) -#define WM_ADSP2_REGION_1 BIT(1) -#define WM_ADSP2_REGION_2 BIT(2) -#define WM_ADSP2_REGION_3 BIT(3) -#define WM_ADSP2_REGION_4 BIT(4) -#define WM_ADSP2_REGION_5 BIT(5) -#define WM_ADSP2_REGION_6 BIT(6) -#define WM_ADSP2_REGION_7 BIT(7) -#define WM_ADSP2_REGION_8 BIT(8) -#define WM_ADSP2_REGION_9 BIT(9) -#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \ - WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \ - WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \ - WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \ - WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9) -#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9) - -struct wm_adsp_region { - int type; - unsigned int base; -}; - -struct wm_adsp_alg_region { - struct list_head list; - unsigned int alg; - int type; - unsigned int base; -}; - struct wm_adsp_compr; struct wm_adsp_compr_buf; -struct wm_adsp_ops; struct wm_adsp { + struct cs_dsp cs_dsp; const char *part; - const char *name; const char *fwf_name; - int rev; - int num; - int type; - struct device *dev; - struct regmap *regmap; struct snd_soc_component *component; - const struct wm_adsp_ops *ops; - - unsigned int base; - unsigned int base_sysinfo; - unsigned int sysclk_reg; - unsigned int sysclk_mask; - unsigned int sysclk_shift; - - struct list_head alg_regions; - - unsigned int fw_id; - unsigned int fw_id_version; - unsigned int fw_vendor_id; - - const struct wm_adsp_region *mem; - int num_mems; + unsigned int sys_config_size; int fw; - int fw_ver; - - bool preloaded; - bool booted; - bool running; - bool fatal_error; - - struct list_head ctl_list; struct work_struct boot_work; + bool preloaded; + bool fatal_error; + struct list_head compr_list; struct list_head buffer_list; - struct mutex pwr_lock; - - unsigned int lock_regions; - -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_root; - char *wmfw_file_name; - char *bin_file_name; -#endif - -}; - -struct wm_adsp_ops { - unsigned int sys_config_size; - - bool (*validate_version)(struct wm_adsp *dsp, unsigned int version); - unsigned int (*parse_sizes)(struct wm_adsp *dsp, - const char * const file, - unsigned int pos, - const struct firmware *firmware); - int (*setup_algs)(struct wm_adsp *dsp); - unsigned int (*region_to_reg)(struct wm_adsp_region const *mem, - unsigned int offset); - - void (*show_fw_status)(struct wm_adsp *dsp); - void (*stop_watchdog)(struct wm_adsp *dsp); - - int (*enable_memory)(struct wm_adsp *dsp); - void (*disable_memory)(struct wm_adsp *dsp); - int (*lock_memory)(struct wm_adsp *dsp, unsigned int lock_regions); - - int (*enable_core)(struct wm_adsp *dsp); - void (*disable_core)(struct wm_adsp *dsp); - - int (*start_core)(struct wm_adsp *dsp); - void (*stop_core)(struct wm_adsp *dsp); + /* + * Flag indicating the preloader widget only needs power toggled + * on state change rather than held on for the duration of the + * preload, useful for devices that can retain firmware memory + * across power down. + */ + bool toggle_preload; }; #define WM_ADSP1(wname, num) \ diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 564b78f3cd..0222370ff9 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -1026,7 +1026,7 @@ static const struct snd_soc_dai_ops wsa881x_dai_ops = { .hw_params = wsa881x_hw_params, .hw_free = wsa881x_hw_free, .mute_stream = wsa881x_digital_mute, - .set_sdw_stream = wsa881x_set_sdw_stream, + .set_stream = wsa881x_set_sdw_stream, }; static struct snd_soc_dai_driver wsa881x_dais[] = { diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c index d21a72314d..6cae0fb080 100644 --- a/sound/soc/codecs/zl38060.c +++ b/sound/soc/codecs/zl38060.c @@ -250,8 +250,8 @@ static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: /* always 32 bits per frame (= 16 bits/channel, 2 channels) */ err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK, CFG_CLK_MASTER | CFG_CLK_PCLK_MASK, @@ -589,9 +589,7 @@ static int zl38_spi_probe(struct spi_device *spi) sizeof(template_chip), GFP_KERNEL); if (!priv->gpio_chip) return -ENOMEM; -#ifdef CONFIG_OF_GPIO - priv->gpio_chip->of_node = dev->of_node; -#endif + priv->gpio_chip->parent = dev; err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap); if (err) return err; diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 33ce257ae1..5cb5892909 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -356,25 +356,25 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); int ret = 0; - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: if (dev->capability & DW_I2S_SLAVE) ret = 0; else ret = -EINVAL; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: if (dev->capability & DW_I2S_MASTER) ret = 0; else ret = -EINVAL; break; - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBP_CFC: + case SND_SOC_DAIFMT_CBC_CFP: ret = -EINVAL; break; default: - dev_dbg(dev->dev, "dwc : Invalid master/slave format\n"); + dev_dbg(dev->dev, "dwc : Invalid clock provider format\n"); ret = -EINVAL; break; } diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 8e05d09279..10fa387534 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -311,6 +311,7 @@ config SND_SOC_FSL_ASOC_CARD select SND_SOC_FSL_ESAI select SND_SOC_FSL_SAI select SND_SOC_FSL_SSI + select SND_SOC_TLV320AIC31XX select SND_SOC_WM8994 select MFD_WM8994 help diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 06107ae46e..5ee9455052 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -26,6 +26,7 @@ #include "../codecs/wm8962.h" #include "../codecs/wm8960.h" #include "../codecs/wm8994.h" +#include "../codecs/tlv320aic31xx.h" #define CS427x_SYSCLK_MCLK 0 @@ -356,8 +357,8 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, * If only 4 wires are needed, just set SSI into * synchronous mode and enable 4 PADs in IOMUX. */ - switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | @@ -367,7 +368,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_TFSDIR | IMX_AUDMUX_V2_PTCR_TCLKDIR; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | IMX_AUDMUX_V2_PTCR_RCLKDIR | @@ -377,7 +378,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_RFSDIR | IMX_AUDMUX_V2_PTCR_TFSDIR; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | IMX_AUDMUX_V2_PTCR_RFSDIR | @@ -387,7 +388,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np, IMX_AUDMUX_V2_PTCR_RCLKDIR | IMX_AUDMUX_V2_PTCR_TCLKDIR; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) | IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) | IMX_AUDMUX_V2_PTCR_TFSEL(int_port) | @@ -533,8 +534,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) struct device_node *cpu_np, *codec_np, *asrc_np; struct device_node *np = pdev->dev.of_node; struct platform_device *asrc_pdev = NULL; - struct device_node *bitclkmaster = NULL; - struct device_node *framemaster = 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 = NULL; @@ -617,29 +618,39 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) 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_CBS_CFS; + priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) { codec_dai_name = "cs4271-hifi"; priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) { codec_dai_name = "sgtl5000"; priv->codec_priv.mclk_id = SGTL5000_SYSCLK; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { codec_dai_name = "tlv320aic32x4-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; + } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) { + codec_dai_name = "tlv320dac31xx-hifi"; + priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->dai_link[1].dpcm_capture = 0; + priv->dai_link[2].dpcm_capture = 0; + priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT; + priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT; + priv->codec_priv.mclk_id = AIC31XX_PLL_CLKIN_BCLK; + 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 = "wm8962"; priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; priv->codec_priv.fll_id = WM8962_SYSCLK_FLL; priv->codec_priv.pll_id = WM8962_FLL; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) { codec_dai_name = "wm8960-hifi"; priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO; priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) { codec_dai_name = "ac97-hifi"; priv->dai_fmt = SND_SOC_DAIFMT_AC97; @@ -648,7 +659,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) { codec_dai_name = "fsl-mqs-dai"; priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J | - SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_NB_NF; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; @@ -656,7 +667,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) { codec_dai_name = "wm8524-hifi"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; priv->dai_link[1].dpcm_capture = 0; priv->dai_link[2].dpcm_capture = 0; priv->cpu_priv.slot_width = 32; @@ -664,12 +675,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { codec_dai_name = "si476x-codec"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + 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 = "wm8994-aif1"; - priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1; priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1; priv->codec_priv.pll_id = WM8994_FLL1; @@ -683,29 +694,29 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Format info from DT is optional. */ - snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkmaster, &framemaster); - if (bitclkmaster || framemaster) { + snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider); + if (bitclkprovider || frameprovider) { unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL); - if (codec_np == bitclkmaster) - daifmt |= (codec_np == framemaster) ? - SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + if (codec_np == bitclkprovider) + daifmt |= (codec_np == frameprovider) ? + SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC; else - daifmt |= (codec_np == framemaster) ? - SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + daifmt |= (codec_np == frameprovider) ? + SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC; /* Override dai_fmt with value from DT */ priv->dai_fmt = daifmt; } /* Change direction according to format */ - if (priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) { + if (priv->dai_fmt & SND_SOC_DAIFMT_CBP_CFP) { priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_IN; priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_IN; } - of_node_put(bitclkmaster); - of_node_put(framemaster); + of_node_put(bitclkprovider); + of_node_put(frameprovider); if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) { dev_dbg(&pdev->dev, "failed to find codec device\n"); @@ -842,8 +853,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &priv->card); if (ret) { - if (ret != -EPROBE_DEFER) - 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 asrc_fail; } @@ -888,6 +898,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .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", }, diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index f931288e25..6dbb8c99f6 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -257,10 +257,10 @@ static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - /* For playback the AUDMIX is slave, and for record is master */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBS_CFS: + /* For playback the AUDMIX is consumer, and for record is provider */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index bda66b30e0..3a9e2df4e1 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -52,7 +52,7 @@ struct fsl_esai_soc_data { * @sck_rate: clock rate of desired SCKx clock * @hck_dir: the direction of HCKx pads * @sck_div: if using PSR/PM dividers for SCKx clock - * @slave_mode: if fully using DAI slave mode + * @consumer_mode: if fully using DAI clock consumer mode * @synchronous: if using tx/rx synchronous mode * @name: driver name */ @@ -78,7 +78,7 @@ struct fsl_esai { u32 sck_rate[2]; bool hck_dir[2]; bool sck_div[2]; - bool slave_mode; + bool consumer_mode; bool synchronous; char name[32]; }; @@ -366,8 +366,8 @@ static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) u32 sub, ratio = hck_rate / freq; int ret; - /* Don't apply for fully slave mode or unchanged bclk */ - if (esai_priv->slave_mode || esai_priv->sck_rate[tx] == freq) + /* Don't apply for fully consumer mode or unchanged bclk */ + if (esai_priv->consumer_mode || esai_priv->sck_rate[tx] == freq) return 0; if (ratio * freq > hck_rate) @@ -476,20 +476,20 @@ static int fsl_esai_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - esai_priv->slave_mode = false; + esai_priv->consumer_mode = false; - /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - esai_priv->slave_mode = true; + /* DAI clock provider masks */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + esai_priv->consumer_mode = true; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: xccr |= ESAI_xCCR_xCKD; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: xccr |= ESAI_xCCR_xFSD; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: xccr |= ESAI_xCCR_xFSD | ESAI_xCCR_xCKD; break; default: @@ -1016,8 +1016,8 @@ static int fsl_esai_probe(struct platform_device *pdev) /* Set a default slot number */ esai_priv->slots = 2; - /* Set a default master/slave state */ - esai_priv->slave_mode = true; + /* Set a default clock provider state */ + esai_priv->consumer_mode = true; /* Determine the FIFO depth */ iprop = of_get_property(np, "fsl,fifo-depth", NULL); diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c index 0d4efbed41..ceaecbe3a2 100644 --- a/sound/soc/fsl/fsl_mqs.c +++ b/sound/soc/fsl/fsl_mqs.c @@ -102,8 +102,8 @@ static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c index d60f4dac6c..8508bc7f23 100644 --- a/sound/soc/fsl/fsl_rpmsg.c +++ b/sound/soc/fsl/fsl_rpmsg.c @@ -138,11 +138,43 @@ static const struct snd_soc_component_driver fsl_component = { .name = "fsl-rpmsg", }; +static const struct fsl_rpmsg_soc_data imx7ulp_data = { + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, +}; + +static const struct fsl_rpmsg_soc_data imx8mm_data = { + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U8 | + SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U32_LE, +}; + +static const struct fsl_rpmsg_soc_data imx8mn_data = { + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, +}; + +static const struct fsl_rpmsg_soc_data imx8mp_data = { + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, +}; + static const struct of_device_id fsl_rpmsg_ids[] = { - { .compatible = "fsl,imx7ulp-rpmsg-audio"}, - { .compatible = "fsl,imx8mm-rpmsg-audio"}, - { .compatible = "fsl,imx8mn-rpmsg-audio"}, - { .compatible = "fsl,imx8mp-rpmsg-audio"}, + { .compatible = "fsl,imx7ulp-rpmsg-audio", .data = &imx7ulp_data}, + { .compatible = "fsl,imx8mm-rpmsg-audio", .data = &imx8mm_data}, + { .compatible = "fsl,imx8mn-rpmsg-audio", .data = &imx8mn_data}, + { .compatible = "fsl,imx8mp-rpmsg-audio", .data = &imx8mp_data}, + { .compatible = "fsl,imx8ulp-rpmsg-audio", .data = &imx7ulp_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids); @@ -157,6 +189,13 @@ static int fsl_rpmsg_probe(struct platform_device *pdev) if (!rpmsg) return -ENOMEM; + rpmsg->soc_data = of_device_get_match_data(&pdev->dev); + + fsl_rpmsg_dai.playback.rates = rpmsg->soc_data->rates; + fsl_rpmsg_dai.capture.rates = rpmsg->soc_data->rates; + fsl_rpmsg_dai.playback.formats = rpmsg->soc_data->formats; + fsl_rpmsg_dai.capture.formats = rpmsg->soc_data->formats; + if (of_property_read_bool(np, "fsl,enable-lpa")) { rpmsg->enable_lpa = 1; rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE; diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h index 4f5b49eb18..b04086fbf8 100644 --- a/sound/soc/fsl/fsl_rpmsg.h +++ b/sound/soc/fsl/fsl_rpmsg.h @@ -6,6 +6,16 @@ #ifndef __FSL_RPMSG_H #define __FSL_RPMSG_H +/* + * struct fsl_rpmsg_soc_data + * @rates: supported rates + * @formats: supported formats + */ +struct fsl_rpmsg_soc_data { + int rates; + u64 formats; +}; + /* * struct fsl_rpmsg - rpmsg private data * @@ -15,6 +25,7 @@ * @pll8k: parent clock for multiple of 8kHz frequency * @pll11k: parent clock for multiple of 11kHz frequency * @card_pdev: Platform_device pointer to register a sound card + * @soc_data: soc specific data * @mclk_streams: Active streams that are using baudclk * @force_lpa: force enable low power audio routine if condition satisfy * @enable_lpa: enable low power audio routine according to dts setting @@ -27,6 +38,7 @@ struct fsl_rpmsg { struct clk *pll8k; struct clk *pll11k; struct platform_device *card_pdev; + const struct fsl_rpmsg_soc_data *soc_data; unsigned int mclk_streams; int force_lpa; int enable_lpa; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 38f6362099..10544fa27d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -297,23 +297,23 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + /* DAI clock provider masks */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_slave_mode = false; + sai->is_consumer_mode = false; break; - case SND_SOC_DAIFMT_CBM_CFM: - sai->is_slave_mode = true; + case SND_SOC_DAIFMT_CBP_CFP: + sai->is_consumer_mode = true; break; - case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBC_CFP: val_cr2 |= FSL_SAI_CR2_BCD_MSTR; - sai->is_slave_mode = false; + sai->is_consumer_mode = false; break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; - sai->is_slave_mode = true; + sai->is_consumer_mode = true; break; default: return -EINVAL; @@ -356,8 +356,8 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) u32 id; int ret = 0; - /* Don't apply to slave mode */ - if (sai->is_slave_mode) + /* Don't apply to consumer mode */ + if (sai->is_consumer_mode) return 0; /* @@ -462,7 +462,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, pins = DIV_ROUND_UP(channels, slots); - if (!sai->is_slave_mode) { + if (!sai->is_consumer_mode) { if (sai->bclk_ratio) ret = fsl_sai_set_bclk(cpu_dai, tx, sai->bclk_ratio * @@ -502,12 +502,12 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr4 |= FSL_SAI_CR4_CHMOD; /* - * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will + * For SAI provider mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), * RCR5(TCR5) for playback(capture), or there will be sync error. */ - if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) { + if (!sai->is_consumer_mode && fsl_sai_dir_is_synced(sai, adir)) { regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | FSL_SAI_CR4_CHMOD_MASK, @@ -543,7 +543,7 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), FSL_SAI_CR3_TRCE_MASK, 0); - if (!sai->is_slave_mode && + if (!sai->is_consumer_mode && sai->mclk_streams & BIT(substream->stream)) { clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[tx]]); sai->mclk_streams &= ~BIT(substream->stream); @@ -577,7 +577,7 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) * This is a hardware bug, and will be fix in the * next sai version. */ - if (!sai->is_slave_mode) { + if (!sai->is_consumer_mode) { /* Software Reset */ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); /* Clear SR bit to finish the reset */ diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index bc60030967..9aaf231bc0 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -259,7 +259,7 @@ struct fsl_sai { struct clk *bus_clk; struct clk *mclk_clk[FSL_SAI_MCLK_MAX]; - bool is_slave_mode; + bool is_consumer_mode; bool is_lsb_first; bool is_dsp_mode; bool synchronous[2]; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 1c53719bb6..d178b479c8 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -111,6 +111,7 @@ struct spdif_mixer_control { * @dma_params_tx: DMA parameters for transmit channel * @dma_params_rx: DMA parameters for receive channel * @regcache_srpc: regcache for SRPC + * @bypass: status of bypass input to output */ struct fsl_spdif_priv { const struct fsl_spdif_soc_data *soc; @@ -133,6 +134,7 @@ struct fsl_spdif_priv { struct snd_dmaengine_dai_dma_data dma_params_rx; /* regcache for SRPC */ u32 regcache_srpc; + bool bypass; }; static struct fsl_spdif_soc_data fsl_spdif_vf610 = { @@ -186,6 +188,16 @@ static struct fsl_spdif_soc_data fsl_spdif_imx8mm = { .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, }; +static struct fsl_spdif_soc_data fsl_spdif_imx8ulp = { + .imx = true, + .shared_root_clock = true, + .raw_capture_mode = false, + .interrupts = 1, + .tx_burst = 2, /* Applied for EDMA */ + .rx_burst = 2, /* Applied for EDMA */ + .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */ +}; + /* Check if clk is a root clock that does not share clock source with others */ static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk) { @@ -895,6 +907,69 @@ static int fsl_spdif_rx_rcm_put(struct snd_kcontrol *kcontrol, return 0; } +static int fsl_spdif_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.integer.value[0] = priv->bypass ? 1 : 0; + + return 0; +} + +static int fsl_spdif_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *priv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_card *card = dai->component->card; + bool set = (ucontrol->value.integer.value[0] != 0); + struct regmap *regmap = priv->regmap; + struct snd_soc_pcm_runtime *rtd; + u32 scr, mask; + int stream; + + rtd = snd_soc_get_pcm_runtime(card, card->dai_link); + + if (priv->bypass == set) + return 0; /* nothing to do */ + + if (snd_soc_dai_active(dai)) { + dev_err(dai->dev, "Cannot change BYPASS mode while stream is running.\n"); + return -EBUSY; + } + + pm_runtime_get_sync(dai->dev); + + if (set) { + /* Disable interrupts */ + regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0); + + /* Configure BYPASS mode */ + scr = SCR_TXSEL_RX | SCR_RXFIFO_OFF; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK | + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK | SCR_TXSEL_MASK; + /* Power up SPDIF module */ + mask |= SCR_LOW_POWER; + } else { + /* Power down SPDIF module, disable TX */ + scr = SCR_LOW_POWER | SCR_TXSEL_OFF; + mask = SCR_LOW_POWER | SCR_TXSEL_MASK; + } + + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Disable playback & capture if BYPASS mode is enabled, enable otherwise */ + for_each_pcm_streams(stream) + rtd->pcm->streams[stream].substream_count = (set ? 0 : 1); + + priv->bypass = set; + pm_runtime_put_sync(dai->dev); + + return 0; +} + /* DPLL lock information */ static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1065,6 +1140,15 @@ static struct snd_kcontrol_new fsl_spdif_ctrls[] = { .info = fsl_spdif_rxrate_info, .get = fsl_spdif_rxrate_get, }, + /* RX bypass controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Bypass Mode", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_mono_info, + .get = fsl_spdif_bypass_get, + .put = fsl_spdif_bypass_put, + }, /* User bit sync mode set/get controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1560,6 +1644,7 @@ static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, }, { .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, }, { .compatible = "fsl,imx8mm-spdif", .data = &fsl_spdif_imx8mm, }, + { .compatible = "fsl,imx8ulp-spdif", .data = &fsl_spdif_imx8ulp, }, {} }; MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ecbc1c365d..1169d1104b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -350,16 +350,16 @@ static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi) SND_SOC_DAIFMT_AC97; } -static bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi) +static bool fsl_ssi_is_i2s_clock_provider(struct fsl_ssi *ssi) { - return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBS_CFS; + return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == + SND_SOC_DAIFMT_CBC_CFC; } -static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi) +static bool fsl_ssi_is_i2s_cbp_cfc(struct fsl_ssi *ssi) { - return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == - SND_SOC_DAIFMT_CBM_CFS; + return (ssi->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == + SND_SOC_DAIFMT_CBP_CFC; } /** @@ -808,7 +808,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, u32 wl = SSI_SxCCR_WL(sample_size); int ret; - if (fsl_ssi_is_i2s_master(ssi)) { + if (fsl_ssi_is_i2s_clock_provider(ssi)) { ret = fsl_ssi_set_bclk(substream, dai, hw_params); if (ret) return ret; @@ -841,7 +841,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, u8 i2s_net = ssi->i2s_net; /* Normal + Network mode to send 16-bit data in 32-bit frames */ - if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) + if (fsl_ssi_is_i2s_cbp_cfc(ssi) && sample_size == 16) i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; /* Use Normal mode to send mono data at 1st slot of 2 slots */ @@ -865,7 +865,7 @@ static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); - if (fsl_ssi_is_i2s_master(ssi) && + if (fsl_ssi_is_i2s_clock_provider(ssi) && ssi->baudclk_streams & BIT(substream->stream)) { clk_disable_unprepare(ssi->baudclk); ssi->baudclk_streams &= ~BIT(substream->stream); @@ -891,18 +891,18 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) ssi->i2s_net = SSI_SCR_NET; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: if (IS_ERR(ssi->baudclk)) { dev_err(ssi->dev, "missing baudclk for master mode\n"); return -EINVAL; } fallthrough; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE; break; default: @@ -962,17 +962,17 @@ static int _fsl_ssi_set_dai_fmt(struct fsl_ssi *ssi, unsigned int fmt) return -EINVAL; } - /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: + /* DAI clock provider masks */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* Output bit and frame sync clocks */ strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; scr |= SSI_SCR_SYS_CLK_EN; break; - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: /* Input bit or frame sync clocks */ break; - case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBP_CFC: /* Input bit clock but output frame sync clock */ strcr |= SSI_STCR_TFDIR; break; @@ -1341,7 +1341,7 @@ static int fsl_ssi_imx_probe(struct platform_device *pdev, } } - /* Do not error out for slave cases that live without a baud clock */ + /* Do not error out for consumer cases that live without a baud clock */ ssi->baudclk = devm_clk_get(dev, "baud"); if (IS_ERR(ssi->baudclk)) dev_dbg(dev, "failed to get baud clock: %ld\n", diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index a364e2415d..502fe1b522 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -80,8 +80,8 @@ static int imx_audmix_fe_hw_params(struct snd_pcm_substream *substream, u32 channels = params_channels(params); int ret, dir; - /* For playback the AUDMIX is slave, and for record is master */ - fmt |= tx ? SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM; + /* For playback the AUDMIX is consumer, and for record is provider */ + fmt |= tx ? SND_SOC_DAIFMT_CBC_CFC : SND_SOC_DAIFMT_CBP_CFP; dir = tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN; /* set DAI configuration */ @@ -121,8 +121,8 @@ static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream, if (!tx) return 0; - /* For playback the AUDMIX is slave */ - fmt |= SND_SOC_DAIFMT_CBM_CFM; + /* For playback the AUDMIX is consumer */ + fmt |= SND_SOC_DAIFMT_CBP_CFP; /* set AUDMIX DAI configuration */ ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), fmt); @@ -132,12 +132,12 @@ static int imx_audmix_be_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops imx_audmix_fe_ops = { +static const struct snd_soc_ops imx_audmix_fe_ops = { .startup = imx_audmix_fe_startup, .hw_params = imx_audmix_fe_hw_params, }; -static struct snd_soc_ops imx_audmix_be_ops = { +static const struct snd_soc_ops imx_audmix_be_ops = { .hw_params = imx_audmix_be_hw_params, }; diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c index db94718061..6f8efd838f 100644 --- a/sound/soc/fsl/imx-card.c +++ b/sound/soc/fsl/imx-card.c @@ -443,12 +443,12 @@ static int imx_aif_startup(struct snd_pcm_substream *substream) return ret; } -static struct snd_soc_ops imx_aif_ops = { +static const struct snd_soc_ops imx_aif_ops = { .hw_params = imx_aif_hw_params, .startup = imx_aif_startup, }; -static struct snd_soc_ops imx_aif_ops_be = { +static const struct snd_soc_ops imx_aif_ops_be = { .hw_params = imx_aif_hw_params, }; @@ -579,9 +579,8 @@ static int imx_card_parse_of(struct imx_card_data *data) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -589,9 +588,8 @@ static int imx_card_parse_of(struct imx_card_data *data) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(dev, ret, "%s: codec dai not found\n", + link->name); goto err; } @@ -668,7 +666,7 @@ static int imx_card_parse_of(struct imx_card_data *data) NULL, &link->dai_fmt); if (ret) link->dai_fmt = SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S; /* Get tdm slot */ @@ -830,11 +828,8 @@ static int imx_card_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c index ef8d7a65eb..929f69b758 100644 --- a/sound/soc/fsl/imx-hdmi.c +++ b/sound/soc/fsl/imx-hdmi.c @@ -59,7 +59,7 @@ static int imx_hdmi_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops imx_hdmi_ops = { +static const struct snd_soc_ops imx_hdmi_ops = { .hw_params = imx_hdmi_hw_params, }; @@ -173,7 +173,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) data->dai.codecs->name = "hdmi-audio-codec.1"; data->dai.dai_fmt = data->dai_fmt | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS; + SND_SOC_DAIFMT_CBC_CFC; } if (hdmi_in) { @@ -183,7 +183,7 @@ static int imx_hdmi_probe(struct platform_device *pdev) data->dai.codecs->name = "hdmi-audio-codec.2"; data->dai.dai_fmt = data->dai_fmt | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; } data->card.dapm_widgets = imx_hdmi_widgets; diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index f45cb4bbb6..8daced42d5 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -153,7 +153,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->dai.platforms->of_node = ssi_np; data->dai.init = &imx_sgtl5000_dai_init; data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; + SND_SOC_DAIFMT_CBP_CFP; data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); @@ -173,9 +173,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, &data->card); if (ret) { - if (ret != -EPROBE_DEFER) - 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 fail; } diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 6c4dadf603..4446fba755 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -70,8 +70,8 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) goto end; ret = devm_snd_soc_register_card(&pdev->dev, &data->card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); end: of_node_put(spdif_np); diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index 4cafcf0e2b..b6df4e26bc 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -17,3 +17,23 @@ config SND_AUDIO_GRAPH_CARD This option enables generic simple sound card support with OF-graph DT bindings. It also support DPCM of multi CPU single Codec ststem. + +config SND_AUDIO_GRAPH_CARD2 + tristate "ASoC Audio Graph sound card2 support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple sound card2 support + with OF-graph DT bindings. + +config SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE + tristate "ASoC Audio Graph Card2 base custom sample support" + depends on SND_AUDIO_GRAPH_CARD2 + help + This option enables Audio Graph Card2 base custom sample + +config SND_TEST_COMPONENT + tristate "ASoC Test component sound support" + depends on OF + help + This option enables test component sound driver support. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 21c29e5e06..0848621565 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -2,7 +2,13 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o +snd-soc-audio-graph-card2-objs := audio-graph-card2.o +snd-soc-audio-graph-card2-custom-sample-objs := audio-graph-card2-custom-sample.o +snd-soc-test-component-objs := test-component.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2) += snd-soc-audio-graph-card2.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE) += snd-soc-audio-graph-card2-custom-sample.o +obj-$(CONFIG_SND_TEST_COMPONENT) += snd-soc-test-component.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 546f6fd060..2b598af8fe 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -310,8 +310,10 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where * there are 'n' BE components in the path. */ - if (card->component_chaining && !soc_component_is_pcm(cpus)) + if (card->component_chaining && !soc_component_is_pcm(cpus)) { dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_simple_be_hw_params_fixup; + } asoc_simple_canonicalize_cpu(cpus, is_single_links); asoc_simple_canonicalize_platform(platforms, cpus); @@ -591,10 +593,7 @@ int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) err: asoc_simple_clean_reference(card); - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - - return ret; + return dev_err_probe(dev, ret, "parse error\n"); } EXPORT_SYMBOL_GPL(audio_graph_parse_of); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 10c63b7390..a81323d169 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -355,9 +355,9 @@ static int asoc_simple_init_dai_link_params(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hardware hw; int i, ret, stream; - /* Only codecs should have non_legacy_dai_naming set. */ + /* Only Codecs */ for_each_rtd_components(rtd, i, component) { - if (!component->driver->non_legacy_dai_naming) + if (!snd_soc_component_is_codec(component)) return 0; } @@ -499,57 +499,14 @@ EXPORT_SYMBOL_GPL(asoc_simple_parse_widgets); int asoc_simple_parse_pin_switches(struct snd_soc_card *card, char *prefix) { - const unsigned int nb_controls_max = 16; - const char **strings, *control_name; - struct snd_kcontrol_new *controls; - struct device *dev = card->dev; - unsigned int i, nb_controls; char prop[128]; - int ret; if (!prefix) prefix = ""; snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - if (!of_property_read_bool(dev->of_node, prop)) - return 0; - - strings = devm_kcalloc(dev, nb_controls_max, - sizeof(*strings), GFP_KERNEL); - if (!strings) - return -ENOMEM; - - ret = of_property_read_string_array(dev->of_node, prop, - strings, nb_controls_max); - if (ret < 0) - return ret; - - nb_controls = (unsigned int)ret; - - controls = devm_kcalloc(dev, nb_controls, - sizeof(*controls), GFP_KERNEL); - if (!controls) - return -ENOMEM; - - for (i = 0; i < nb_controls; i++) { - control_name = devm_kasprintf(dev, GFP_KERNEL, - "%s Switch", strings[i]); - if (!control_name) - return -ENOMEM; - - controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; - controls[i].name = control_name; - controls[i].info = snd_soc_dapm_info_pin_switch; - controls[i].get = snd_soc_dapm_get_pin_switch; - controls[i].put = snd_soc_dapm_put_pin_switch; - controls[i].private_value = (unsigned long)strings[i]; - } - - card->controls = controls; - card->num_controls = nb_controls; - - return 0; + return snd_soc_of_parse_pin_switches(card, prop); } EXPORT_SYMBOL_GPL(asoc_simple_parse_pin_switches); @@ -619,7 +576,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, struct asoc_simple_dai *dais; struct snd_soc_dai_link_component *dlcs; struct snd_soc_codec_conf *cconf = NULL; - int i, dai_num = 0, dlc_num = 0, cnf_num = 0; + struct snd_soc_pcm_stream *c2c_conf = NULL; + int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0; dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL); @@ -638,6 +596,8 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, if (!li->num[i].cpus) cnf_num += li->num[i].codecs; + + c2c_num += li->num[i].c2c; } dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL); @@ -651,6 +611,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, return -ENOMEM; } + if (c2c_num) { + c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL); + if (!c2c_conf) + return -ENOMEM; + } + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", li->link, dai_num, cnf_num); @@ -664,6 +630,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, priv->dais = dais; priv->dlcs = dlcs; priv->codec_conf = cconf; + priv->c2c_conf = c2c_conf; card->dai_link = priv->dai_link; card->num_links = li->link; @@ -681,6 +648,12 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv, dlcs += li->num[i].cpus; dais += li->num[i].cpus; + + if (li->num[i].c2c) { + /* Codec2Codec */ + dai_props[i].c2c_conf = c2c_conf; + c2c_conf += li->num[i].c2c; + } } else { /* DPCM Be's CPU = dummy */ dai_props[i].cpus = @@ -759,6 +732,34 @@ int asoc_graph_card_probe(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(asoc_graph_card_probe); +int asoc_graph_is_ports0(struct device_node *np) +{ + struct device_node *port, *ports, *ports0, *top; + int ret; + + /* np is "endpoint" or "port" */ + if (of_node_name_eq(np, "endpoint")) { + port = of_get_parent(np); + } else { + port = np; + of_node_get(port); + } + + ports = of_get_parent(port); + top = of_get_parent(ports); + ports0 = of_get_child_by_name(top, "ports"); + + ret = ports0 == ports; + + of_node_put(port); + of_node_put(ports); + of_node_put(ports0); + of_node_put(top); + + return ret; +} +EXPORT_SYMBOL_GPL(asoc_graph_is_ports0); + /* Module information */ MODULE_AUTHOR("Kuninori Morimoto "); MODULE_DESCRIPTION("ALSA SoC Simple Card Utils"); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index bc3e24c6a2..78419e1871 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -666,8 +666,7 @@ static int asoc_simple_probe(struct platform_device *pdev) ret = simple_parse_of(priv, li); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); + dev_err_probe(dev, ret, "parse error\n"); goto err; } diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 1bf5d6edbd..f1f36f15a5 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -451,11 +451,9 @@ static int img_i2s_in_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 4f90d36dc7..28f48ca150 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -457,25 +457,19 @@ static int img_i2s_out_probe(struct platform_device *pdev) i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20); i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(i2s->rst)) { - if (PTR_ERR(i2s->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(i2s->rst); - } + if (IS_ERR(i2s->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->rst), + "No top level reset found\n"); i2s->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(i2s->clk_sys)) { - if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(i2s->clk_sys); - } + if (IS_ERR(i2s->clk_sys)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_sys), + "Failed to acquire clock 'sys'\n"); i2s->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(i2s->clk_ref)) { - if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(i2s->clk_ref); - } + if (IS_ERR(i2s->clk_ref)) + return dev_err_probe(dev, PTR_ERR(i2s->clk_ref), + "Failed to acquire clock 'ref'\n"); i2s->suspend_ch_ctl = devm_kcalloc(dev, i2s->max_i2s_chan, sizeof(*i2s->suspend_ch_ctl), GFP_KERNEL); diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index ce0f08d377..800f247283 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -229,25 +229,19 @@ static int img_prl_out_probe(struct platform_device *pdev) prl->base = base; prl->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(prl->rst)) { - if (PTR_ERR(prl->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(prl->rst); - } + if (IS_ERR(prl->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(prl->rst), + "No top level reset found\n"); prl->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(prl->clk_sys)) { - if (PTR_ERR(prl->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(prl->clk_sys); - } + if (IS_ERR(prl->clk_sys)) + return dev_err_probe(dev, PTR_ERR(prl->clk_sys), + "Failed to acquire clock 'sys'\n"); prl->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(prl->clk_ref)) { - if (PTR_ERR(prl->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(prl->clk_ref); - } + if (IS_ERR(prl->clk_ref)) + return dev_err_probe(dev, PTR_ERR(prl->clk_ref), + "Failed to acquire clock 'ref'\n"); ret = clk_prepare_enable(prl->clk_sys); if (ret) diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 6364eb742f..95914d0612 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -739,11 +739,9 @@ static int img_spdif_in_probe(struct platform_device *pdev) spdif->base = base; spdif->clk_sys = devm_clk_get(dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 858e1b8538..c3189d9ff7 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -342,25 +342,19 @@ static int img_spdif_out_probe(struct platform_device *pdev) spdif->base = base; spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); - if (IS_ERR(spdif->rst)) { - if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "No top level reset found\n"); - return PTR_ERR(spdif->rst); - } + if (IS_ERR(spdif->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdif->rst), + "No top level reset found\n"); spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(spdif->clk_sys)) { - if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'sys'\n"); - return PTR_ERR(spdif->clk_sys); - } + if (IS_ERR(spdif->clk_sys)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_sys), + "Failed to acquire clock 'sys'\n"); spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); - if (IS_ERR(spdif->clk_ref)) { - if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) - dev_err(dev, "Failed to acquire clock 'ref'\n"); - return PTR_ERR(spdif->clk_ref); - } + if (IS_ERR(spdif->clk_ref)) + return dev_err_probe(dev, PTR_ERR(spdif->clk_ref), + "Failed to acquire clock 'ref'\n"); pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c index fe181c2e51..802c0ee63a 100644 --- a/sound/soc/img/pistachio-internal-dac.c +++ b/sound/soc/img/pistachio-internal-dac.c @@ -161,12 +161,9 @@ static int pistachio_internal_dac_probe(struct platform_device *pdev) return PTR_ERR(dac->regmap); dac->supply = devm_regulator_get(dev, "VDD"); - if (IS_ERR(dac->supply)) { - ret = PTR_ERR(dac->supply); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire supply 'VDD-supply': %d\n", ret); - return ret; - } + if (IS_ERR(dac->supply)) + return dev_err_probe(dev, PTR_ERR(dac->supply), + "failed to acquire supply 'VDD-supply'\n"); ret = regulator_enable(dac->supply); if (ret) { diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 5db2f4865b..a56dd48c04 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -653,10 +653,21 @@ static snd_pcm_uframes_t sst_soc_pointer(struct snd_soc_component *component, dev_err(rtd->dev, "sst: error code = %d\n", ret_val); return ret_val; } - substream->runtime->delay = str_info->pcm_delay; return str_info->buffer_ptr; } +static snd_pcm_sframes_t sst_soc_delay(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = substream->runtime->private_data; + struct pcm_stream_info *str_info = &stream->stream_info; + + if (sst_get_stream_status(stream) == SST_PLATFORM_INIT) + return 0; + + return str_info->pcm_delay; +} + static int sst_soc_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { @@ -695,6 +706,7 @@ static const struct snd_soc_component_driver sst_soc_platform_drv = { .open = sst_soc_open, .trigger = sst_soc_trigger, .pointer = sst_soc_pointer, + .delay = sst_soc_delay, .compress_ops = &sst_platform_compress_ops, .pcm_construct = sst_soc_pcm_new, }; diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 61b71d6c44..34ccefcc30 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -371,7 +371,7 @@ config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH tristate "KBL with DA7219 and MAX98357A in I2S Mode" - depends on I2C && ACPI + depends on I2C && ACPI && GPIOLIB depends on MFD_INTEL_LPSS || COMPILE_TEST select SND_SOC_INTEL_DA7219_MAX98357A_GENERIC help @@ -427,6 +427,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH depends on MFD_INTEL_LPSS || COMPILE_TEST depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_MAX98357A select SND_SOC_DMIC select SND_SOC_HDAC_HDMI @@ -466,10 +467,12 @@ config SND_SOC_INTEL_SOF_RT5682_MACH (MFD_INTEL_LPSS || COMPILE_TEST)) ||\ (SND_SOC_SOF_BAYTRAIL && (X86_INTEL_LPSS || COMPILE_TEST)) select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98390 select SND_SOC_RT1011 select SND_SOC_RT1015 select SND_SOC_RT1015P select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDAC_HDMI select SND_SOC_INTEL_HDA_DSP_COMMON @@ -511,6 +514,39 @@ config SND_SOC_INTEL_SOF_PCM512x_MACH Say Y or m if you have such a device. If unsure select "N". +config SND_SOC_INTEL_SOF_ES8336_MACH + tristate "SOF with ES8336 codec in I2S mode" + depends on I2C && ACPI && GPIOLIB + depends on MFD_INTEL_LPSS || COMPILE_TEST + depends on SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC + select SND_SOC_ES8316 + select SND_SOC_DMIC + select SND_SOC_INTEL_HDA_DSP_COMMON + help + This adds support for ASoC machine driver for SOF platforms + with es8336 codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SOF_NAU8825_MACH + tristate "SOF with nau8825 codec in I2S Mode" + depends on I2C && ACPI && GPIOLIB + depends on ((SND_HDA_CODEC_HDMI && SND_SOC_SOF_HDA_AUDIO_CODEC) &&\ + (MFD_INTEL_LPSS || COMPILE_TEST)) + select SND_SOC_NAU8825 + select SND_SOC_RT1015P + select SND_SOC_MAX98373_I2C + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_SOC_INTEL_HDA_DSP_COMMON + select SND_SOC_INTEL_SOF_MAXIM_COMMON + help + This adds support for ASoC machine driver for SOF platforms + with nau8825 codec. + Say Y if you have such a device. + If unsure select "N". + endif ## SND_SOC_SOF_HDA_LINK || SND_SOC_SOF_BAYTRAIL if (SND_SOC_SOF_COMETLAKE && SND_SOC_SOF_HDA_LINK) diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index ed21b82a4c..3ea273d271 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -21,6 +21,8 @@ snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o snd-soc-sof_rt5682-objs := sof_rt5682.o sof_realtek_common.o snd-soc-sof_cs42l42-objs := sof_cs42l42.o +snd-soc-sof_es8336-objs := sof_es8336.o +snd-soc-sof_nau8825-objs := sof_nau8825.o sof_realtek_common.o snd-soc-cml_rt1011_rt5682-objs := cml_rt1011_rt5682.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o @@ -42,6 +44,8 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_dmic.o sof_sdw_hdmi.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_ES8336_MACH) += snd-soc-sof_es8336.o +obj-$(CONFIG_SND_SOC_INTEL_SOF_NAU8825_MACH) += snd-soc-sof_nau8825.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index a9e51bbf01..ffd497a5b5 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -126,7 +126,7 @@ static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_CBC_CFC); if (ret < 0) { dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); return ret; @@ -147,7 +147,7 @@ static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_RATE, 48000); } -static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { +static const struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { .startup = byt_cht_cx2072x_aif1_startup, }; @@ -195,7 +195,7 @@ static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = { .id = 0, .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .init = byt_cht_cx2072x_init, .be_hw_params_fixup = byt_cht_cx2072x_fixup, .dpcm_playback = 1, diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 9b48fe701a..115c2bcaab 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -61,7 +61,7 @@ static int codec_fixup(struct snd_soc_pcm_runtime *rtd, ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_CBC_CFC); if (ret < 0) { dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); @@ -93,7 +93,7 @@ static int aif1_startup(struct snd_pcm_substream *substream) &constraints_48000); } -static struct snd_soc_ops aif1_ops = { +static const struct snd_soc_ops aif1_ops = { .startup = aif1_startup, }; @@ -141,7 +141,7 @@ static struct snd_soc_dai_link dais[] = { .id = 0, .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .be_hw_params_fixup = codec_fixup, .ignore_suspend = 1, .dpcm_playback = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a6e837290c..2ace32c03e 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -40,6 +40,8 @@ enum { BYT_RT5640_NO_INTERNAL_MIC_MAP, }; +#define RT5640_JD_SRC_EXT_GPIO 0x0f + enum { BYT_RT5640_JD_SRC_GPIO1 = (RT5640_JD_SRC_GPIO1 << 4), BYT_RT5640_JD_SRC_JD1_IN4P = (RT5640_JD_SRC_JD1_IN4P << 4), @@ -47,6 +49,7 @@ enum { BYT_RT5640_JD_SRC_GPIO2 = (RT5640_JD_SRC_GPIO2 << 4), BYT_RT5640_JD_SRC_GPIO3 = (RT5640_JD_SRC_GPIO3 << 4), BYT_RT5640_JD_SRC_GPIO4 = (RT5640_JD_SRC_GPIO4 << 4), + BYT_RT5640_JD_SRC_EXT_GPIO = (RT5640_JD_SRC_EXT_GPIO << 4) }; enum { @@ -79,6 +82,7 @@ enum { #define BYT_RT5640_LINEOUT_AS_HP2 BIT(26) #define BYT_RT5640_HSMIC2_ON_IN1 BIT(27) #define BYT_RT5640_JD_HP_ELITEP_1000G2 BIT(28) +#define BYT_RT5640_USE_AMCR0F28 BIT(29) #define BYTCR_INPUT_DEFAULTS \ (BYT_RT5640_IN3_MAP | \ @@ -93,6 +97,7 @@ enum { struct byt_rt5640_private { struct snd_soc_jack jack; struct snd_soc_jack jack2; + struct rt5640_set_jack_data jack_data; struct gpio_desc *hsmic_detect; struct clk *mclk; struct device *codec_dev; @@ -269,13 +274,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return -EIO; if (SND_SOC_DAPM_EVENT_ON(event)) { - if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { - ret = clk_prepare_enable(priv->mclk); - if (ret < 0) { - dev_err(card->dev, - "could not configure MCLK state\n"); - return ret; - } + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, "could not configure MCLK state\n"); + return ret; } ret = byt_rt5640_prepare_and_enable_pll1(codec_dai, 48000); } else { @@ -287,10 +289,8 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK, 48000 * 512, SND_SOC_CLOCK_IN); - if (!ret) { - if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) - clk_disable_unprepare(priv->mclk); - } + if (!ret) + clk_disable_unprepare(priv->mclk); } if (ret < 0) { @@ -602,7 +602,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_OVCD_TH_2000UA | BYT_RT5640_OVCD_SF_0P75 | BYT_RT5640_SSP0_AIF1 | - BYT_RT5640_MCLK_EN), + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), }, { .matches = { @@ -629,6 +630,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_EXT_GPIO | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN | + BYT_RT5640_USE_AMCR0F28), + }, { /* Chuwi Vi8 (CWI506) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), @@ -1085,9 +1099,11 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { - props[cnt++] = PROPERTY_ENTRY_U32( - "realtek,jack-detect-source", - BYT_RT5640_JDSRC(byt_rt5640_quirk)); + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) != RT5640_JD_SRC_EXT_GPIO) { + props[cnt++] = PROPERTY_ENTRY_U32( + "realtek,jack-detect-source", + BYT_RT5640_JDSRC(byt_rt5640_quirk)); + } props[cnt++] = PROPERTY_ENTRY_U32( "realtek,over-current-threshold-microamp", @@ -1114,6 +1130,51 @@ static int byt_rt5640_add_codec_device_props(struct device *i2c_dev, return ret; } +/* Some Android devs specify IRQs/GPIOS in a special AMCR0F28 ACPI device */ +static const struct acpi_gpio_params amcr0f28_jd_gpio = { 1, 0, false }; + +static const struct acpi_gpio_mapping amcr0f28_gpios[] = { + { "rt5640-jd-gpios", &amcr0f28_jd_gpio, 1 }, + { } +}; + +static int byt_rt5640_get_amcr0f28_settings(struct snd_soc_card *card) +{ + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct rt5640_set_jack_data *data = &priv->jack_data; + struct acpi_device *adev; + int ret = 0; + + adev = acpi_dev_get_first_match_dev("AMCR0F28", "1", -1); + if (!adev) { + dev_err(card->dev, "error cannot find AMCR0F28 adev\n"); + return -ENOENT; + } + + data->codec_irq_override = acpi_dev_gpio_irq_get(adev, 0); + if (data->codec_irq_override < 0) { + ret = data->codec_irq_override; + dev_err(card->dev, "error %d getting codec IRQ\n", ret); + goto put_adev; + } + + if (BYT_RT5640_JDSRC(byt_rt5640_quirk) == RT5640_JD_SRC_EXT_GPIO) { + acpi_dev_add_driver_gpios(adev, amcr0f28_gpios); + data->jd_gpio = devm_fwnode_gpiod_get(card->dev, acpi_fwnode_handle(adev), + "rt5640-jd", GPIOD_IN, "rt5640-jd"); + acpi_dev_remove_driver_gpios(adev); + + if (IS_ERR(data->jd_gpio)) { + ret = PTR_ERR(data->jd_gpio); + dev_err(card->dev, "error %d getting jd GPIO\n", ret); + } + } + +put_adev: + acpi_dev_put(adev); + return ret; +} + static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_card *card = runtime->card; @@ -1217,30 +1278,25 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) return ret; } - if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { - /* - * The firmware might enable the clock at - * boot (this information may or may not - * be reflected in the enable clock register). - * To change the rate we must disable the clock - * first to cover these cases. Due to common - * clock framework restrictions that do not allow - * to disable a clock that has not been enabled, - * we need to enable the clock first. - */ - ret = clk_prepare_enable(priv->mclk); - if (!ret) - clk_disable_unprepare(priv->mclk); + /* + * The firmware might enable the clock at boot (this information + * may or may not be reflected in the enable clock register). + * To change the rate we must disable the clock first to cover + * these cases. Due to common clock framework restrictions that + * do not allow to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); - if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) - ret = clk_set_rate(priv->mclk, 25000000); - else - ret = clk_set_rate(priv->mclk, 19200000); - - if (ret) { - dev_err(card->dev, "unable to set MCLK rate\n"); - return ret; - } + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); + if (ret) { + dev_err(card->dev, "unable to set MCLK rate\n"); + return ret; } if (BYT_RT5640_JDSRC(byt_rt5640_quirk)) { @@ -1254,7 +1310,14 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) } snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_soc_component_set_jack(component, &priv->jack, NULL); + + if (byt_rt5640_quirk & BYT_RT5640_USE_AMCR0F28) { + ret = byt_rt5640_get_amcr0f28_settings(card); + if (ret) + return ret; + } + + snd_soc_component_set_jack(component, &priv->jack, &priv->jack_data); } if (byt_rt5640_quirk & BYT_RT5640_JD_HP_ELITEP_1000G2) { @@ -1336,7 +1399,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_CBC_CFC); if (ret < 0) { dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); return ret; @@ -1411,7 +1474,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { .id = 0, .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .be_hw_params_fixup = byt_rt5640_codec_fixup, .dpcm_playback = 1, .dpcm_capture = 1, @@ -1458,7 +1521,8 @@ static int byt_rt5640_resume(struct snd_soc_card *card) for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); - snd_soc_component_set_jack(component, &priv->jack, NULL); + snd_soc_component_set_jack(component, &priv->jack, + &priv->jack_data); break; } } @@ -1495,12 +1559,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; static const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3", "none" }; + struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); __maybe_unused const char *spk_type; const struct dmi_system_id *dmi_id; const char *headset2_string = ""; const char *lineout_string = ""; struct byt_rt5640_private *priv; - struct snd_soc_acpi_mach *mach; const char *platform_name; struct acpi_device *adev; struct device *codec_dev; @@ -1511,13 +1575,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) int aif; is_bytcr = false; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* register the soc card */ - byt_rt5640_card.dev = &pdev->dev; - mach = byt_rt5640_card.dev->platform_data; + byt_rt5640_card.dev = dev; snd_soc_card_set_drvdata(&byt_rt5640_card, priv); /* fix index of codec dai */ @@ -1537,7 +1600,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) put_device(&adev->dev); byt_rt5640_dais[dai_index].codecs->name = byt_rt5640_codec_name; } else { - dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id); + dev_err(dev, "Error cannot find '%s' dev\n", mach->id); return -ENXIO; } @@ -1580,13 +1643,13 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { - dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); + dev_info(dev, "BIOS Routing: AIF1 connected\n"); byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF1; } else if (chan_package.aif_value == 2) { - dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); + dev_info(dev, "BIOS Routing: AIF2 connected\n"); byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2; } else { - dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); + dev_info(dev, "BIOS Routing isn't valid, ignored\n"); pkg_found = false; } } @@ -1610,7 +1673,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) if (dmi_id) byt_rt5640_quirk = (unsigned long)dmi_id->driver_data; if (quirk_override != -1) { - dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n", byt_rt5640_quirk, quirk_override); byt_rt5640_quirk = quirk_override; } @@ -1624,12 +1687,12 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) acpi_dev_add_driver_gpios(ACPI_COMPANION(priv->codec_dev), byt_rt5640_hp_elitepad_1000g2_gpios); - priv->hsmic_detect = devm_fwnode_gpiod_get(&pdev->dev, codec_dev->fwnode, + priv->hsmic_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode, "headset-mic-detect", GPIOD_IN, "headset-mic-detect"); if (IS_ERR(priv->hsmic_detect)) { - ret_val = PTR_ERR(priv->hsmic_detect); - dev_err_probe(&pdev->dev, ret_val, "getting hsmic-detect GPIO\n"); + ret_val = dev_err_probe(dev, PTR_ERR(priv->hsmic_detect), + "getting hsmic-detect GPIO\n"); goto err_device; } } @@ -1639,7 +1702,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) if (ret_val) goto err_remove_gpios; - log_quirks(&pdev->dev); + log_quirks(dev); if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { @@ -1654,23 +1717,18 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_dais[dai_index].cpus->dai_name = "ssp0-port"; if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { - priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { - ret_val = PTR_ERR(priv->mclk); - - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %d\n", - ret_val); - - /* - * Fall back to bit clock usage for -ENOENT (clock not - * available likely due to missing dependencies), bail - * for all other errors, including -EPROBE_DEFER - */ - if (ret_val != -ENOENT) - goto err; - byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN; + ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk), + "Failed to get MCLK from pmc_plt_clk_3\n"); + goto err; } + /* + * Fall back to bit clock usage when clock is not + * available likely due to missing dependencies. + */ + if (!priv->mclk) + byt_rt5640_quirk &= ~BYT_RT5640_MCLK_EN; } if (byt_rt5640_quirk & BYT_RT5640_NO_SPEAKERS) { @@ -1714,7 +1772,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) if (ret_val) goto err; - sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); + sof_parent = snd_soc_acpi_sof_parent(dev); /* set card and driver name */ if (sof_parent) { @@ -1729,11 +1787,9 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) if (sof_parent) dev->driver->pm = &snd_soc_pm_ops; - ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); - + ret_val = devm_snd_soc_register_card(dev, &byt_rt5640_card); if (ret_val) { - dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", - ret_val); + dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val); goto err; } platform_set_drvdata(pdev, &byt_rt5640_card); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index e94c9124d4..5e9c53dadb 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -188,13 +188,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, } if (SND_SOC_DAPM_EVENT_ON(event)) { - if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { - ret = clk_prepare_enable(priv->mclk); - if (ret < 0) { - dev_err(card->dev, - "could not configure MCLK state"); - return ret; - } + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, "could not configure MCLK state"); + return ret; } ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50); } else { @@ -207,8 +204,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, 48000 * 512, SND_SOC_CLOCK_IN); if (!ret) - if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) - clk_disable_unprepare(priv->mclk); + clk_disable_unprepare(priv->mclk); } if (ret < 0) { @@ -629,29 +625,25 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) return ret; } - if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { - /* - * The firmware might enable the clock at - * boot (this information may or may not - * be reflected in the enable clock register). - * To change the rate we must disable the clock - * first to cover these cases. Due to common - * clock framework restrictions that do not allow - * to disable a clock that has not been enabled, - * we need to enable the clock first. - */ - ret = clk_prepare_enable(priv->mclk); - if (!ret) - clk_disable_unprepare(priv->mclk); + /* + * The firmware might enable the clock at boot (this information + * may or may not be reflected in the enable clock register). + * To change the rate we must disable the clock first to cover + * these cases. Due to common clock framework restrictions that + * do not allow to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); - if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) - ret = clk_set_rate(priv->mclk, 25000000); - else - ret = clk_set_rate(priv->mclk, 19200000); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); - if (ret) - dev_err(card->dev, "unable to set MCLK rate\n"); - } + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); report = 0; if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) @@ -713,7 +705,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS + SND_SOC_DAIFMT_CBC_CFC ); if (ret < 0) { @@ -798,7 +790,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { .id = 0, .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .be_hw_params_fixup = byt_rt5651_codec_fixup, .dpcm_playback = 1, .dpcm_capture = 1, @@ -894,9 +886,10 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; + struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct byt_rt5651_private *priv; - struct snd_soc_acpi_mach *mach; const char *platform_name; struct acpi_device *adev; struct device *codec_dev; @@ -906,14 +899,12 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) int dai_index = 0; int i; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* register the soc card */ - byt_rt5651_card.dev = &pdev->dev; - - mach = byt_rt5651_card.dev->platform_data; + byt_rt5651_card.dev = dev; snd_soc_card_set_drvdata(&byt_rt5651_card, priv); /* fix index of codec dai */ @@ -933,7 +924,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) put_device(&adev->dev); byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name; } else { - dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id); + dev_err(dev, "Error cannot find '%s' dev\n", mach->id); return -ENXIO; } @@ -981,13 +972,13 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { - dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); + dev_info(dev, "BIOS Routing: AIF1 connected\n"); byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF1; } else if (chan_package.aif_value == 2) { - dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); + dev_info(dev, "BIOS Routing: AIF2 connected\n"); byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; } else { - dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); + dev_info(dev, "BIOS Routing isn't valid, ignored\n"); pkg_found = false; } } @@ -1002,7 +993,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) dmi_check_system(byt_rt5651_quirk_table); if (quirk_override != -1) { - dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n", byt_rt5651_quirk, quirk_override); byt_rt5651_quirk = quirk_override; } @@ -1018,8 +1009,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) if (byt_rt5651_gpios) { devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios); - priv->ext_amp_gpio = devm_fwnode_gpiod_get(&pdev->dev, - codec_dev->fwnode, + priv->ext_amp_gpio = devm_fwnode_gpiod_get(dev, codec_dev->fwnode, "ext-amp-enable", GPIOD_OUT_LOW, "speaker-amp"); @@ -1030,15 +1020,13 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) priv->ext_amp_gpio = NULL; break; default: - dev_err(&pdev->dev, "Failed to get ext-amp-enable GPIO: %d\n", - ret_val); + dev_err(dev, "Failed to get ext-amp-enable GPIO: %d\n", ret_val); fallthrough; case -EPROBE_DEFER: goto err; } } - priv->hp_detect = devm_fwnode_gpiod_get(&pdev->dev, - codec_dev->fwnode, + priv->hp_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode, "hp-detect", GPIOD_IN, "hp-detect"); @@ -1049,8 +1037,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) priv->hp_detect = NULL; break; default: - dev_err(&pdev->dev, "Failed to get hp-detect GPIO: %d\n", - ret_val); + dev_err(dev, "Failed to get hp-detect GPIO: %d\n", ret_val); fallthrough; case -EPROBE_DEFER: goto err; @@ -1058,7 +1045,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } } - log_quirks(&pdev->dev); + log_quirks(dev); if ((byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) || (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) @@ -1069,21 +1056,18 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) byt_rt5651_dais[dai_index].cpus->dai_name = "ssp0-port"; if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { - priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { - ret_val = PTR_ERR(priv->mclk); - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %d\n", - ret_val); - /* - * Fall back to bit clock usage for -ENOENT (clock not - * available likely due to missing dependencies), bail - * for all other errors, including -EPROBE_DEFER - */ - if (ret_val != -ENOENT) - goto err; - byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN; + ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk), + "Failed to get MCLK from pmc_plt_clk_3\n"); + goto err; } + /* + * Fall back to bit clock usage when clock is not + * available likely due to missing dependencies. + */ + if (!priv->mclk) + byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN; } snprintf(byt_rt5651_components, sizeof(byt_rt5651_components), @@ -1112,7 +1096,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) if (ret_val) goto err; - sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); + sof_parent = snd_soc_acpi_sof_parent(dev); /* set card and driver name */ if (sof_parent) { @@ -1125,13 +1109,11 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) /* set pm ops */ if (sof_parent) - pdev->dev.driver->pm = &snd_soc_pm_ops; - - ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); + dev->driver->pm = &snd_soc_pm_ops; + ret_val = devm_snd_soc_register_card(dev, &byt_rt5651_card); if (ret_val) { - dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", - ret_val); + dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val); goto err; } platform_set_drvdata(pdev, &byt_rt5651_card); diff --git a/sound/soc/intel/boards/cht_bsw_nau8824.c b/sound/soc/intel/boards/cht_bsw_nau8824.c index da5a5cbc87..bad32d2bdf 100644 --- a/sound/soc/intel/boards/cht_bsw_nau8824.c +++ b/sound/soc/intel/boards/cht_bsw_nau8824.c @@ -214,7 +214,7 @@ static struct snd_soc_dai_link cht_dailink[] = { .id = 0, .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF - | SND_SOC_DAIFMT_CBS_CFS, + | SND_SOC_DAIFMT_CBC_CFC, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, .dpcm_playback = 1, @@ -278,6 +278,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) snd_soc_card_cht.driver_name = DRIVER_NAME; } + snd_soc_card_cht.components = nau8824_components(); + /* set pm ops */ if (sof_parent) pdev->dev.driver->pm = &snd_soc_pm_ops; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index 71fe26a1b7..bad3829e52 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -18,14 +18,18 @@ #include #include #include "../../codecs/rt5682.h" +#include "../../codecs/rt5682s.h" #include "../../codecs/hdac_hdmi.h" #include "hda_dsp_common.h" /* The platform clock outputs 19.2Mhz clock to codec as I2S MCLK */ #define GLK_PLAT_CLK_FREQ 19200000 #define RT5682_PLL_FREQ (48000 * 512) -#define GLK_REALTEK_CODEC_DAI "rt5682-aif1" +#define RT5682_DAI_NAME "rt5682-aif1" +#define RT5682S_DAI_NAME "rt5682s-aif1" #define GLK_MAXIM_CODEC_DAI "HiFi" +#define RT5682_DEV0_NAME "i2c-10EC5682:00" +#define RT5682S_DEV0_NAME "i2c-RTL5682:00" #define MAXIM_DEV0_NAME "MX98357A:00" #define DUAL_CHANNEL 2 #define QUAD_CHANNEL 4 @@ -43,6 +47,7 @@ struct glk_card_private { struct snd_soc_jack geminilake_headset; struct list_head hdmi_pcm_list; bool common_hdmi_codec_drv; + int is_rt5682s; }; enum { @@ -139,9 +144,19 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_jack *jack; - int ret; + int pll_id, pll_source, clk_id, ret; - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, + if (ctx->is_rt5682s) { + pll_id = RT5682S_PLL2; + pll_source = RT5682S_PLL_S_MCLK; + clk_id = RT5682S_SCLK_S_PLL2; + } else { + pll_id = RT5682_PLL1; + pll_source = RT5682_PLL1_S_MCLK; + clk_id = RT5682_SCLK_S_PLL1; + } + + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, GLK_PLAT_CLK_FREQ, RT5682_PLL_FREQ); if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); @@ -149,7 +164,7 @@ static int geminilake_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) } /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, RT5682_PLL_FREQ, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); @@ -344,9 +359,12 @@ SND_SOC_DAILINK_DEF(ssp1_codec, SND_SOC_DAILINK_DEF(ssp2_pin, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); -SND_SOC_DAILINK_DEF(ssp2_codec, - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", - GLK_REALTEK_CODEC_DAI))); +SND_SOC_DAILINK_DEF(ssp2_codec_5682, + DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, + RT5682_DAI_NAME))); +SND_SOC_DAILINK_DEF(ssp2_codec_5682s, + DAILINK_COMP_ARRAY(COMP_CODEC(RT5682S_DEV0_NAME, + RT5682S_DAI_NAME))); SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin"))); @@ -473,7 +491,7 @@ static struct snd_soc_dai_link geminilake_dais[] = { .no_pcm = 1, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ignore_pmdown_time = 1, .be_hw_params_fixup = geminilake_ssp_fixup, .dpcm_playback = 1, @@ -486,13 +504,13 @@ static struct snd_soc_dai_link geminilake_dais[] = { .no_pcm = 1, .init = geminilake_rt5682_codec_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, + SND_SOC_DAIFMT_CBC_CFC, .ignore_pmdown_time = 1, .be_hw_params_fixup = geminilake_ssp_fixup, .ops = &geminilake_rt5682_ops, .dpcm_playback = 1, .dpcm_capture = 1, - SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec, platform), + SND_SOC_DAILINK_REG(ssp2_pin, ssp2_codec_5682, platform), }, { .name = "dmic01", @@ -592,12 +610,28 @@ static int geminilake_audio_probe(struct platform_device *pdev) struct snd_soc_acpi_mach *mach; const char *platform_name; struct snd_soc_card *card; - int ret; + int ret, i; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + /* Detect the headset codec variant */ + if (acpi_dev_present("RTL5682", NULL, -1)) { + /* ALC5682I-VS is detected */ + ctx->is_rt5682s = 1; + + for (i = 0; i < glk_audio_card_rt5682_m98357a.num_links; i++) { + if (strcmp(geminilake_dais[i].name, "SSP2-Codec")) + continue; + + /* update the dai link to use rt5682s codec */ + geminilake_dais[i].codecs = ssp2_codec_5682s; + geminilake_dais[i].num_codecs = ARRAY_SIZE(ssp2_codec_5682s); + break; + } + } + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); card = &glk_audio_card_rt5682_m98357a; diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c index efdc4bc4bb..5c31ddc088 100644 --- a/sound/soc/intel/boards/hda_dsp_common.c +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -68,7 +68,7 @@ int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, hpcm->pcm = NULL; hpcm->device = SNDRV_PCM_INVALID_DEVICE; dev_warn(card->dev, - "%s: no PCM in topology for HDMI converter %d\n\n", + "%s: no PCM in topology for HDMI converter %d\n", __func__, i); } i++; diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index e66dfe6669..112e89951d 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,185 @@ void max_98373_set_codec_conf(struct snd_soc_card *card) } EXPORT_SYMBOL_NS(max_98373_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); +/* + * Maxim MAX98390 + */ +static const struct snd_soc_dapm_route max_98390_dapm_routes[] = { + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, +}; + +static const struct snd_kcontrol_new max_98390_tt_kcontrols[] = { + SOC_DAPM_PIN_SWITCH("TL Spk"), + SOC_DAPM_PIN_SWITCH("TR Spk"), +}; + +static const struct snd_soc_dapm_widget max_98390_tt_dapm_widgets[] = { + SND_SOC_DAPM_SPK("TL Spk", NULL), + SND_SOC_DAPM_SPK("TR Spk", NULL), +}; + +static const struct snd_soc_dapm_route max_98390_tt_dapm_routes[] = { + /* Tweeter speaker */ + { "TL Spk", NULL, "Tweeter Left BE_OUT" }, + { "TR Spk", NULL, "Tweeter Right BE_OUT" }, +}; + +static struct snd_soc_codec_conf max_98390_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, +}; + +static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), + .name_prefix = "Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), + .name_prefix = "Tweeter Right", + }, + { + .dlc = COMP_CODEC_CONF(MAX_98390_DEV3_NAME), + .name_prefix = "Tweeter Left", + }, +}; + +struct snd_soc_dai_link_component max_98390_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +struct snd_soc_dai_link_component max_98390_4spk_components[] = { + { + .name = MAX_98390_DEV0_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV1_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV2_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, + { + .name = MAX_98390_DEV3_NAME, + .dai_name = MAX_98390_CODEC_DAI, + }, +}; +EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +static int max_98390_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + if (i >= ARRAY_SIZE(max_98390_4spk_components)) { + dev_err(codec_dai->dev, "invalid codec index %d\n", i); + return -ENODEV; + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { + /* DEV0 tdm slot configuration Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { + /* DEV1 tdm slot configuration Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); + } + + if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { + /* DEVi2 tdm slot configuration Tweeter Right */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); + } + if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { + /* DEV3 tdm slot configuration Tweeter Left */ + snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); + } + } + return 0; +} + +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + /* add regular speakers dapm route */ + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, + ARRAY_SIZE(max_98390_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); + return ret; + } + + /* add widgets/controls/dapm for tweeter speakers */ + if (acpi_dev_present("MX98390", "3", -1)) { + ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, + ARRAY_SIZE(max_98390_tt_dapm_widgets)); + + if (ret) { + dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); + /* Don't need to add routes if widget addition failed */ + return ret; + } + + ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, + ARRAY_SIZE(max_98390_tt_kcontrols)); + if (ret) { + dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, + ARRAY_SIZE(max_98390_tt_dapm_routes)); + if (ret) + dev_err(rtd->dev, + "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); + } + return ret; +} +EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +const struct snd_soc_ops max_98390_ops = { + .hw_params = max_98390_hw_params, +}; +EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) +{ + if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { + card->codec_conf = max_98390_4spk_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); + } else { + card->codec_conf = max_98390_codec_conf; + card->num_configs = ARRAY_SIZE(max_98390_codec_conf); + } +} +EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); + /* * Maxim MAX98357A/MAX98360A */ diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h index 3ff5e8fec4..7a8c53049e 100644 --- a/sound/soc/intel/boards/sof_maxim_common.h +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -24,6 +24,22 @@ int max_98373_spk_codec_init(struct snd_soc_pcm_runtime *rtd); void max_98373_set_codec_conf(struct snd_soc_card *card); int max_98373_trigger(struct snd_pcm_substream *substream, int cmd); +/* + * Maxim MAX98390 + */ +#define MAX_98390_CODEC_DAI "max98390-aif1" +#define MAX_98390_DEV0_NAME "i2c-MX98390:00" +#define MAX_98390_DEV1_NAME "i2c-MX98390:01" +#define MAX_98390_DEV2_NAME "i2c-MX98390:02" +#define MAX_98390_DEV3_NAME "i2c-MX98390:03" + +extern struct snd_soc_dai_link_component max_98390_components[2]; +extern struct snd_soc_dai_link_component max_98390_4spk_components[4]; +extern const struct snd_soc_ops max_98390_ops; + +void max_98390_set_codec_conf(struct snd_soc_card *card, int ch); +int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd); + /* * Maxim MAX98357A/MAX98360A */ diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index 2ec34f8df9..4cf131310a 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -12,12 +12,13 @@ #include #include #include "../../codecs/rt1011.h" +#include "../../codecs/rt1015.h" #include "sof_realtek_common.h" /* * Current only 2-amp configuration is supported for rt1011 */ -static const struct snd_soc_dapm_route rt1011_dapm_routes[] = { +static const struct snd_soc_dapm_route speaker_map_lr[] = { /* speaker */ { "Left Spk", NULL, "Left SPO" }, { "Right Spk", NULL, "Right SPO" }, @@ -117,8 +118,8 @@ static int rt1011_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; int ret; - ret = snd_soc_dapm_add_routes(&card->dapm, rt1011_dapm_routes, - ARRAY_SIZE(rt1011_dapm_routes)); + ret = snd_soc_dapm_add_routes(&card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); if (ret) dev_err(rtd->dev, "Speaker map addition failed: %d\n", ret); return ret; @@ -241,3 +242,115 @@ void sof_rt1015p_codec_conf(struct snd_soc_card *card) card->codec_conf = rt1015p_codec_confs; card->num_configs = ARRAY_SIZE(rt1015p_codec_confs); } + +/* + * RT1015 audio amplifier + */ + +static int rt1015_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 64, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + + return 0; +} + +static int rt1015_hw_params_pll_and_tdm(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai; + int i, fs = 100, ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, + params_rate(params) * fs, + params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, + params_rate(params) * 256, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + /* rx slot 1 for RT1015_DEV0_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 0), + 0x0, 0x1, 4, 24); + if (ret) + return ret; + + /* rx slot 2 for RT1015_DEV1_NAME */ + ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_codec(rtd, 1), + 0x0, 0x2, 4, 24); + if (ret) + return ret; + + return 0; +} + +static struct snd_soc_ops rt1015_ops = { + .hw_params = rt1015_hw_params, +}; + +static struct snd_soc_codec_conf rt1015_amp_conf[] = { + { + .dlc = COMP_CODEC_CONF(RT1015_DEV0_NAME), + .name_prefix = "Left", + }, + { + .dlc = COMP_CODEC_CONF(RT1015_DEV1_NAME), + .name_prefix = "Right", + }, +}; + +static struct snd_soc_dai_link_component rt1015_components[] = { + { + .name = RT1015_DEV0_NAME, + .dai_name = RT1015_CODEC_DAI, + }, + { + .name = RT1015_DEV1_NAME, + .dai_name = RT1015_CODEC_DAI, + }, +}; + +static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) +{ + return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, + ARRAY_SIZE(speaker_map_lr)); +} + +void sof_rt1015_codec_conf(struct snd_soc_card *card) +{ + card->codec_conf = rt1015_amp_conf; + card->num_configs = ARRAY_SIZE(rt1015_amp_conf); +} + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs) +{ + link->codecs = rt1015_components; + link->num_codecs = ARRAY_SIZE(rt1015_components); + link->init = speaker_codec_init_lr; + link->ops = &rt1015_ops; + + if (fs == 100) + rt1015_ops.hw_params = rt1015_hw_params_pll_and_tdm; +} diff --git a/sound/soc/intel/boards/sof_realtek_common.h b/sound/soc/intel/boards/sof_realtek_common.h index cb0b49b285..228ac9c084 100644 --- a/sound/soc/intel/boards/sof_realtek_common.h +++ b/sound/soc/intel/boards/sof_realtek_common.h @@ -28,4 +28,11 @@ void sof_rt1011_codec_conf(struct snd_soc_card *card); void sof_rt1015p_dai_link(struct snd_soc_dai_link *link); void sof_rt1015p_codec_conf(struct snd_soc_card *card); +#define RT1015_CODEC_DAI "rt1015-aif" +#define RT1015_DEV0_NAME "i2c-10EC1015:00" +#define RT1015_DEV1_NAME "i2c-10EC1015:01" + +void sof_rt1015_dai_link(struct snd_soc_dai_link *link, unsigned int fs); +void sof_rt1015_codec_conf(struct snd_soc_card *card); + #endif /* __SOF_REALTEK_COMMON_H */ diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index f096bd6d69..bd6d2e7dea 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -18,9 +18,10 @@ #include #include #include +#include #include -#include "../../codecs/rt1015.h" #include "../../codecs/rt5682.h" +#include "../../codecs/rt5682s.h" #include "../../codecs/hdac_hdmi.h" #include "../common/soc-intel-quirks.h" #include "hda_dsp_common.h" @@ -56,6 +57,10 @@ #define SOF_BT_OFFLOAD_SSP(quirk) \ (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22) +#define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) +#define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) +#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) + /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -176,6 +181,36 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_MAX98390_TWEETER_SPEAKER_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + + }, {} }; @@ -208,9 +243,16 @@ static int sof_rt5682_codec_init(struct snd_soc_pcm_runtime *rtd) /* need to enable ASRC function for 24MHz mclk rate */ if ((sof_rt5682_quirk & SOF_RT5682_MCLK_EN) && (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ)) { - rt5682_sel_asrc_clk_src(component, RT5682_DA_STEREO1_FILTER | - RT5682_AD_STEREO1_FILTER, - RT5682_CLK_SEL_I2S1_ASRC); + if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) + rt5682s_sel_asrc_clk_src(component, + RT5682S_DA_STEREO1_FILTER | + RT5682S_AD_STEREO1_FILTER, + RT5682S_CLK_SEL_I2S1_ASRC); + else + rt5682_sel_asrc_clk_src(component, + RT5682_DA_STEREO1_FILTER | + RT5682_AD_STEREO1_FILTER, + RT5682_CLK_SEL_I2S1_ASRC); } if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { @@ -277,7 +319,7 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int clk_id, clk_freq, pll_out, ret; + int pll_id, pll_source, pll_in, pll_out, clk_id, ret; if (sof_rt5682_quirk & SOF_RT5682_MCLK_EN) { if (sof_rt5682_quirk & SOF_RT5682_MCLK_BYTCHT_EN) { @@ -289,35 +331,52 @@ static int sof_rt5682_hw_params(struct snd_pcm_substream *substream, } } - clk_id = RT5682_PLL1_S_MCLK; + if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) + pll_source = RT5682S_PLL_S_MCLK; + else + pll_source = RT5682_PLL1_S_MCLK; /* get the tplg configured mclk. */ - clk_freq = sof_dai_get_mclk(rtd); + pll_in = sof_dai_get_mclk(rtd); /* mclk from the quirk is the first choice */ if (sof_rt5682_quirk & SOF_RT5682_MCLK_24MHZ) { - if (clk_freq != 24000000) + if (pll_in != 24000000) dev_warn(rtd->dev, "configure wrong mclk in tplg, please use 24MHz.\n"); - clk_freq = 24000000; - } else if (clk_freq == 0) { + pll_in = 24000000; + } else if (pll_in == 0) { /* use default mclk if not specified correct in topology */ - clk_freq = 19200000; - } else if (clk_freq < 0) { - return clk_freq; + pll_in = 19200000; + } else if (pll_in < 0) { + return pll_in; } } else { - clk_id = RT5682_PLL1_S_BCLK1; - clk_freq = params_rate(params) * 50; + if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) + pll_source = RT5682S_PLL_S_BCLK1; + else + pll_source = RT5682_PLL1_S_BCLK1; + + pll_in = params_rate(params) * 50; + } + + if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) { + pll_id = RT5682S_PLL2; + clk_id = RT5682S_SCLK_S_PLL2; + } else { + pll_id = RT5682_PLL1; + clk_id = RT5682_SCLK_S_PLL1; } pll_out = params_rate(params) * 512; - ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out); + /* Configure pll for codec */ + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, pll_in, + pll_out); if (ret < 0) dev_err(rtd->dev, "snd_soc_dai_set_pll err = %d\n", ret); /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); @@ -340,67 +399,6 @@ static struct snd_soc_ops sof_rt5682_ops = { .hw_params = sof_rt5682_hw_params, }; -static int sof_rt1015_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_card *card = rtd->card; - struct snd_soc_dai *codec_dai; - int i, fs, ret; - - if (!snd_soc_card_get_codec_dai(card, "rt1015-aif")) - return 0; - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) - fs = 100; - else - fs = 64; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK, - params_rate(params) * fs, - params_rate(params) * 256); - if (ret < 0) { - dev_err(card->dev, "failed to set pll\n"); - return ret; - } - /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT1015_SCLK_S_PLL, - params_rate(params) * 256, - SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(card->dev, "failed to set sysclk\n"); - return ret; - } - - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_100FS) { - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:00")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x1, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - - if (!strcmp(codec_dai->component->name, "i2c-10EC1015:01")) { - ret = snd_soc_dai_set_tdm_slot(codec_dai, - 0x0, 0x2, 4, 24); - if (ret < 0) { - dev_err(card->dev, "failed to set tdm slot\n"); - return ret; - } - } - } - } - - return 0; -} - -static struct snd_soc_ops sof_rt1015_ops = { - .hw_params = sof_rt1015_hw_params, -}; - static struct snd_soc_dai_link_component platform_component[] = { { /* name might be overridden during probe */ @@ -459,6 +457,7 @@ static int sof_card_late_probe(struct snd_soc_card *card) if (err < 0) return err; } + return hdac_hdmi_jack_port_init(component, &card->dapm); } @@ -490,22 +489,11 @@ static const struct snd_soc_dapm_route sof_map[] = { { "IN1P", NULL, "Headset Mic" }, }; -static const struct snd_soc_dapm_route speaker_map_lr[] = { - { "Left Spk", NULL, "Left SPO" }, - { "Right Spk", NULL, "Right SPO" }, -}; - static const struct snd_soc_dapm_route dmic_map[] = { /* digital mics */ {"DMic", NULL, "SoC DMIC"}, }; -static int speaker_codec_init_lr(struct snd_soc_pcm_runtime *rtd) -{ - return snd_soc_dapm_add_routes(&rtd->card->dapm, speaker_map_lr, - ARRAY_SIZE(speaker_map_lr)); -} - static int dmic_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -528,17 +516,6 @@ static int dmic_init(struct snd_soc_pcm_runtime *rtd) return ret; } -static struct snd_soc_codec_conf rt1015_amp_conf[] = { - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:00"), - .name_prefix = "Left", - }, - { - .dlc = COMP_CODEC_CONF("i2c-10EC1015:01"), - .name_prefix = "Right", - }, -}; - /* sof audio machine driver for rt5682 codec */ static struct snd_soc_card sof_audio_card_rt5682 = { .name = "rt5682", /* the sof- prefix is added by the core */ @@ -560,6 +537,13 @@ static struct snd_soc_dai_link_component rt5682_component[] = { } }; +static struct snd_soc_dai_link_component rt5682s_component[] = { + { + .name = "i2c-RTL5682:00", + .dai_name = "rt5682s-aif1", + } +}; + static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -567,17 +551,6 @@ static struct snd_soc_dai_link_component dmic_component[] = { } }; -static struct snd_soc_dai_link_component rt1015_components[] = { - { - .name = "i2c-10EC1015:00", - .dai_name = "rt1015-aif", - }, - { - .name = "i2c-10EC1015:01", - .dai_name = "rt1015-aif", - }, -}; - static struct snd_soc_dai_link_component dummy_component[] = { { .name = "snd-soc-dummy", @@ -610,8 +583,13 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, goto devm_err; links[id].id = id; - links[id].codecs = rt5682_component; - links[id].num_codecs = ARRAY_SIZE(rt5682_component); + if (sof_rt5682_quirk & SOF_RT5682S_HEADPHONE_CODEC_PRESENT) { + links[id].codecs = rt5682s_component; + links[id].num_codecs = ARRAY_SIZE(rt5682s_component); + } else { + links[id].codecs = rt5682_component; + links[id].num_codecs = ARRAY_SIZE(rt5682_component); + } links[id].platforms = platform_component; links[id].num_platforms = ARRAY_SIZE(platform_component); links[id].init = sof_rt5682_codec_init; @@ -725,10 +703,8 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].id = id; if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - links[id].codecs = rt1015_components; - links[id].num_codecs = ARRAY_SIZE(rt1015_components); - links[id].init = speaker_codec_init_lr; - links[id].ops = &sof_rt1015_ops; + sof_rt1015_dai_link(&links[id], (sof_rt5682_quirk & + SOF_RT1015_SPEAKER_AMP_100FS) ? 100 : 64); } else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) { sof_rt1015p_dai_link(&links[id]); } else if (sof_rt5682_quirk & @@ -745,6 +721,20 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, } else if (sof_rt5682_quirk & SOF_RT1011_SPEAKER_AMP_PRESENT) { sof_rt1011_dai_link(&links[id]); + } else if (sof_rt5682_quirk & + SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & + SOF_MAX98390_TWEETER_SPEAKER_PRESENT) { + links[id].codecs = max_98390_4spk_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components); + } else { + links[id].codecs = max_98390_components; + links[id].num_codecs = ARRAY_SIZE(max_98390_components); + } + links[id].init = max_98390_spk_codec_init; + links[id].ops = &max_98390_ops; + links[id].dpcm_capture = 1; + } else { max_98357a_dai_link(&links[id]); } @@ -825,6 +815,14 @@ static int sof_audio_probe(struct platform_device *pdev) if ((sof_rt5682_quirk & SOF_SPEAKER_AMP_PRESENT) && !mach->quirk_data) sof_rt5682_quirk &= ~SOF_SPEAKER_AMP_PRESENT; + /* Detect the headset codec variant */ + if (acpi_dev_present("RTL5682", NULL, -1)) + sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + + /* Detect the headset codec variant to support machines in DMI quirk */ + if (acpi_dev_present("RTL5682", NULL, -1)) + sof_rt5682_quirk |= SOF_RT5682S_HEADPHONE_CODEC_PRESENT; + if (soc_intel_is_byt() || soc_intel_is_cht()) { is_legacy_cpu = 1; dmic_be_num = 0; @@ -881,6 +879,14 @@ static int sof_audio_probe(struct platform_device *pdev) sof_rt1011_codec_conf(&sof_audio_card_rt5682); else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) sof_rt1015p_codec_conf(&sof_audio_card_rt5682); + else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { + if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT) + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_4spk_components)); + else + max_98390_set_codec_conf(&sof_audio_card_rt5682, + ARRAY_SIZE(max_98390_components)); + } if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) sof_audio_card_rt5682.num_links++; @@ -892,10 +898,8 @@ static int sof_audio_probe(struct platform_device *pdev) sof_audio_card_rt5682.dai_link = dai_links; - if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) { - sof_audio_card_rt5682.codec_conf = rt1015_amp_conf; - sof_audio_card_rt5682.num_configs = ARRAY_SIZE(rt1015_amp_conf); - } + if (sof_rt5682_quirk & SOF_RT1015_SPEAKER_AMP_PRESENT) + sof_rt1015_codec_conf(&sof_audio_card_rt5682); INIT_LIST_HEAD(&ctx->hdmi_pcm_list); @@ -920,7 +924,7 @@ static const struct platform_device_id board_ids[] = { .name = "sof_rt5682", }, { - .name = "tgl_mx98357a_rt5682", + .name = "tgl_mx98357_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | @@ -950,7 +954,7 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "jsl_rt5682_mx98360a", + .name = "jsl_rt5682_mx98360", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_MCLK_24MHZ | SOF_RT5682_SSP_CODEC(0) | @@ -1000,13 +1004,35 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { - .name = "adl_mx98357a_rt5682", + .name = "adl_mx98357_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | SOF_SPEAKER_AMP_PRESENT | SOF_RT5682_SSP_AMP(2) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .name = "adl_max98390_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98390_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, + { + .name = "adl_mx98360_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98360A_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(1) | + SOF_RT5682_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); @@ -1026,6 +1052,7 @@ MODULE_DESCRIPTION("SOF Audio Machine driver"); MODULE_AUTHOR("Bard Liao "); MODULE_AUTHOR("Sathya Prakash M R "); MODULE_AUTHOR("Brent Lu "); +MODULE_AUTHOR("Mac Chiang "); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 76759b2099..da515eb1dd 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -36,8 +36,6 @@ static void log_quirks(struct device *dev) if (SOF_SSP_GET_PORT(sof_sdw_quirk)) dev_dbg(dev, "SSP port %ld\n", SOF_SSP_GET_PORT(sof_sdw_quirk)); - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dev_dbg(dev, "quirk SOF_RT715_DAI_ID_FIX enabled\n"); if (sof_sdw_quirk & SOF_SDW_NO_AGGREGATION) dev_dbg(dev, "quirk SOF_SDW_NO_AGGREGATION enabled\n"); } @@ -64,8 +62,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { /* early version of SKU 09C6 */ @@ -74,8 +71,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") }, - .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + .driver_data = (void *)RT711_JD2, }, { .callback = sof_sdw_quirk_cb, @@ -84,7 +80,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -94,7 +89,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), }, .driver_data = (void *)(RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, /* IceLake devices */ @@ -126,8 +120,17 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), + }, + { + /* another SKU of Dell Latitude 9520 */ + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3F") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2), }, { /* Dell XPS 9710 */ @@ -138,7 +141,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -149,7 +151,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -210,7 +211,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | RT711_JD2 | - SOF_RT715_DAI_ID_FIX | SOF_SDW_FOUR_SPK), }, { @@ -220,8 +220,7 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A45") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | - RT711_JD2 | - SOF_RT715_DAI_ID_FIX), + RT711_JD2), }, /* AlderLake devices */ { @@ -232,7 +231,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { }, .driver_data = (void *)(RT711_JD2_100K | SOF_SDW_TGL_HDMI | - SOF_RT715_DAI_ID_FIX | SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, @@ -248,6 +246,75 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AF3"), + }, + /* No Jack */ + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B11") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B12") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B13"), + }, + /* No Jack */ + .driver_data = (void *)SOF_SDW_TGL_HDMI, + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B29"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, {} }; @@ -280,7 +347,7 @@ int sdw_prepare(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -300,7 +367,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -339,7 +406,7 @@ int sdw_hw_free(struct snd_pcm_substream *substream) /* Find stream from first CPU DAI */ dai = asoc_rtd_to_cpu(rtd, 0); - sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); if (IS_ERR(sdw_stream)) { dev_err(rtd->dev, "no stream found for DAI %s", dai->name); @@ -362,26 +429,13 @@ static const struct snd_soc_ops sdw_ops = { .shutdown = sdw_shutdown, }; -static int sof_sdw_mic_codec_mockup_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* - * force DAI link to use same ID as RT715 and DMIC - * to reuse topologies - */ - dai_links->id = SDW_DMIC_DAI_ID; - return 0; -} - static struct sof_sdw_codec_info codec_info_list[] = { { .part_id = 0x700, .direction = {true, true}, .dai_name = "rt700-aif1", .init = sof_sdw_rt700_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -390,6 +444,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-sdca-aif1", .init = sof_sdw_rt711_sdca_init, .exit = sof_sdw_rt711_sdca_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x711, @@ -398,6 +453,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt711-aif1", .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x1308, @@ -406,12 +462,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "rt1308-aif", .ops = &sof_sdw_rt1308_i2s_ops, .init = sof_sdw_rt1308_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, .direction = {true, true}, .dai_name = "rt1316-aif", .init = sof_sdw_rt1316_init, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x714, @@ -420,6 +478,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -428,6 +487,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x714, @@ -436,6 +496,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x715, @@ -444,6 +505,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ignore_pch_dmic = true, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, { .part_id = 0x8373, @@ -451,12 +513,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_name = "max98373-aif1", .init = sof_sdw_mx8373_init, .codec_card_late_probe = sof_sdw_mx8373_late_probe, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5682, .direction = {true, true}, .dai_name = "rt5682-sdw", .init = sof_sdw_rt5682_init, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaaaa, /* generic codec mockup */ @@ -464,6 +528,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0xaa55, /* headset codec mockup */ @@ -471,6 +536,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_JACK, }, { .part_id = 0x55aa, /* amplifier mockup */ @@ -478,13 +544,14 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "sdw-mockup-aif1", .init = NULL, + .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x5555, .version_id = 0, .direction = {false, true}, .dai_name = "sdw-mockup-aif1", - .init = sof_sdw_mic_codec_mockup_init, + .codec_type = SOF_SDW_CODEC_TYPE_MIC, }, }; @@ -532,10 +599,11 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) * Since some sdw slaves may be aggregated, the CPU DAI number * may be larger than the number of BE dailinks. */ -static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, +static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links, int *sdw_be_num, int *sdw_cpu_dai_num) { const struct snd_soc_acpi_link_adr *link; + int _codec_type = SOF_SDW_CODEC_TYPE_JACK; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; @@ -561,6 +629,12 @@ static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, if (codec_index < 0) return codec_index; + if (codec_info_list[codec_index].codec_type < _codec_type) + dev_warn(dev, + "Unexpected address table ordering. Expected order: jack -> amp -> mic\n"); + + _codec_type = codec_info_list[codec_index].codec_type; + endpoint = link->adr_d->endpoints; /* count DAI number for playback and capture */ @@ -819,14 +893,14 @@ static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, } static int create_sdw_dailink(struct snd_soc_card *card, - struct device *dev, int *be_index, + struct device *dev, int *link_index, struct snd_soc_dai_link *dai_links, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, const struct snd_soc_acpi_link_adr *link, int *cpu_id, bool *group_generated, struct snd_soc_codec_conf *codec_conf, - int codec_count, + int codec_count, int *link_id, int *codec_conf_index, bool *ignore_pch_dmic) { @@ -884,6 +958,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (codec_info_list[codec_index].ignore_pch_dmic) *ignore_pch_dmic = true; + /* Shift the first amplifier's *link_id to SDW_AMP_DAI_ID */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_AMP && + *link_id < SDW_AMP_DAI_ID) + *link_id = SDW_AMP_DAI_ID; + + /* + * DAI ID is fixed at SDW_DMIC_DAI_ID for MICs to + * keep sdw DMIC and HDMI setting static in UCM + */ + if (codec_info_list[codec_index].codec_type == SOF_SDW_CODEC_TYPE_MIC && + *link_id < SDW_DMIC_DAI_ID) + *link_id = SDW_DMIC_DAI_ID; + cpu_dai_index = *cpu_id; for_each_pcm_streams(stream) { char *name, *cpu_name; @@ -922,8 +1009,12 @@ static int create_sdw_dailink(struct snd_soc_card *card, cpus[cpu_dai_index++].dai_name = cpu_name; } - if (*be_index >= sdw_be_num) { - dev_err(dev, " invalid be dai index %d", *be_index); + /* + * We create sdw dai links at first stage, so link index should + * not be larger than sdw_be_num + */ + if (*link_index >= sdw_be_num) { + dev_err(dev, "invalid dai link index %d", *link_index); return -EINVAL; } @@ -934,18 +1025,19 @@ static int create_sdw_dailink(struct snd_soc_card *card, playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *be_index, *be_index, name, + init_dai_link(dev, dai_links + *link_index, (*link_id)++, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, NULL, &sdw_ops); + /* * SoundWire DAILINKs use 'stream' functions and Bank Switch operations * based on wait_for_completion(), tag them as 'nonatomic'. */ - dai_links[*be_index].nonatomic = true; + dai_links[*link_index].nonatomic = true; - ret = set_codec_init_func(card, link, dai_links + (*be_index)++, + ret = set_codec_init_func(card, link, dai_links + (*link_index)++, playback, group_id); if (ret < 0) { dev_err(dev, "failed to init codec %d", codec_index); @@ -959,17 +1051,6 @@ static int create_sdw_dailink(struct snd_soc_card *card, return 0; } -/* - * DAI link ID of SSP & DMIC & HDMI are based on last - * link ID used by sdw link. Since be_id may be changed - * in init func of sdw codec, it is not equal to be_id - */ -static inline int get_next_be_id(struct snd_soc_dai_link *links, - int be_id) -{ - return links[be_id - 1].id + 1; -} - #define IDISP_CODEC_MASK 0x4 static int sof_card_codec_conf_alloc(struct device *dev, @@ -1026,7 +1107,7 @@ static int sof_card_dai_links_create(struct device *dev, bool group_generated[SDW_MAX_GROUPS]; int ssp_codec_index, ssp_mask; struct snd_soc_dai_link *links; - int num_links, link_id = 0; + int num_links, link_index = 0; char *name, *cpu_name; int total_cpu_dai_num; int sdw_cpu_dai_num; @@ -1062,7 +1143,7 @@ static int sof_card_dai_links_create(struct device *dev, ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; comp_num = hdmi_num + ssp_num; - ret = get_sdw_dailink_info(mach_params->links, + ret = get_sdw_dailink_info(dev, mach_params->links, &sdw_be_num, &sdw_cpu_dai_num); if (ret < 0) { dev_err(dev, "failed to get sdw link info %d", ret); @@ -1126,24 +1207,18 @@ static int sof_card_dai_links_create(struct device *dev, group_generated[endpoint->group_id]) continue; - ret = create_sdw_dailink(card, dev, &be_id, links, sdw_be_num, + ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, sdw_cpu_dai_num, cpus, adr_link, &cpu_id, group_generated, codec_conf, codec_conf_count, - &codec_conf_index, + &be_id, &codec_conf_index, &ignore_pch_dmic); if (ret < 0) { - dev_err(dev, "failed to create dai link %d", be_id); - return -ENOMEM; + dev_err(dev, "failed to create dai link %d", link_index); + return ret; } } - /* non-sdw DAI follows sdw DAI */ - link_id = be_id; - - /* get BE ID for non-sdw DAI */ - be_id = get_next_be_id(links, be_id); - SSP: /* SSP */ if (!ssp_num) @@ -1183,17 +1258,17 @@ static int sof_card_dai_links_create(struct device *dev, playback = info->direction[SNDRV_PCM_STREAM_PLAYBACK]; capture = info->direction[SNDRV_PCM_STREAM_CAPTURE]; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->init(card, NULL, links + link_id, info, 0); + ret = info->init(card, NULL, links + link_index, info, 0); if (ret < 0) return ret; - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } DMIC: @@ -1204,21 +1279,21 @@ static int sof_card_dai_links_create(struct device *dev, goto HDMI; } cpus[cpu_id].dai_name = "DMIC01 Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic01", + init_dai_link(dev, links + link_index, be_id, "dmic01", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, sof_sdw_dmic_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); cpus[cpu_id].dai_name = "DMIC16k Pin"; - init_dai_link(dev, links + link_id, be_id, "dmic16k", + init_dai_link(dev, links + link_index, be_id, "dmic16k", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, /* don't call sof_sdw_dmic_init() twice */ NULL, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } HDMI: @@ -1256,12 +1331,12 @@ static int sof_card_dai_links_create(struct device *dev, return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, + init_dai_link(dev, links + link_index, be_id, name, 1, 0, // HDMI only supports playback cpus + cpu_id, 1, idisp_components + i, 1, sof_sdw_hdmi_init, NULL); - INC_ID(be_id, cpu_id, link_id); + INC_ID(be_id, cpu_id, link_index); } if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) { @@ -1285,7 +1360,7 @@ static int sof_card_dai_links_create(struct device *dev, return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_id, be_id, name, 1, 1, + init_dai_link(dev, links + link_index, be_id, name, 1, 1, cpus + cpu_id, 1, ssp_components, 1, NULL, NULL); } diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index b35f5a9b96..e2457738a3 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -15,6 +15,7 @@ #define MAX_NO_PROPS 2 #define MAX_HDMI_NUM 4 +#define SDW_AMP_DAI_ID 2 #define SDW_DMIC_DAI_ID 4 #define SDW_MAX_CPU_DAIS 16 #define SDW_INTEL_BIDIR_PDI_BASE 2 @@ -42,7 +43,6 @@ enum { #define SOF_SDW_PCH_DMIC BIT(6) #define SOF_SSP_PORT(x) (((x) & GENMASK(5, 0)) << 7) #define SOF_SSP_GET_PORT(quirk) (((quirk) >> 7) & GENMASK(5, 0)) -#define SOF_RT715_DAI_ID_FIX BIT(13) #define SOF_SDW_NO_AGGREGATION BIT(14) /* BT audio offload: reserve 3 bits for future */ @@ -52,9 +52,14 @@ enum { (((quirk) << SOF_BT_OFFLOAD_SSP_SHIFT) & SOF_BT_OFFLOAD_SSP_MASK) #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(18) +#define SOF_SDW_CODEC_TYPE_JACK 0 +#define SOF_SDW_CODEC_TYPE_AMP 1 +#define SOF_SDW_CODEC_TYPE_MIC 2 + struct sof_sdw_codec_info { const int part_id; const int version_id; + const int codec_type; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool direction[2]; // playback & capture support diff --git a/sound/soc/intel/boards/sof_sdw_rt715.c b/sound/soc/intel/boards/sof_sdw_rt715.c index c8af3780cb..7c068dc6b9 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715.c +++ b/sound/soc/intel/boards/sof_sdw_rt715.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715 to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_rtd_init; return 0; diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c index 85d3d8c355..ca0cf3db2e 100644 --- a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -30,13 +30,6 @@ int sof_sdw_rt715_sdca_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback) { - /* - * DAI ID is fixed at SDW_DMIC_DAI_ID for 715-SDCA to - * keep sdw DMIC and HDMI setting static in UCM - */ - if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) - dai_links->id = SDW_DMIC_DAI_ID; - dai_links->init = rt715_sdca_rtd_init; return 0; diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index ebb27daeb1..939a9b801d 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -259,9 +259,9 @@ static enum catpt_channel_config catpt_get_channel_config(u32 num_channels) static int catpt_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_template *template; struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct resource *res; int ret; @@ -306,8 +306,8 @@ static int catpt_dai_startup(struct snd_pcm_substream *substream, static void catpt_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); @@ -329,9 +329,9 @@ static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol); static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, struct catpt_stream_runtime *stream) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct snd_soc_component *component = dai->component; struct snd_kcontrol *pos; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); const char *name; int ret; u32 id = stream->info.stream_hw_id; @@ -374,12 +374,12 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_dma_buffer *dmab; struct catpt_stream_runtime *stream; struct catpt_audio_format afmt; struct catpt_ring_info rinfo; - struct snd_pcm_runtime *rtm = substream->runtime; - struct snd_dma_buffer *dmab; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -427,8 +427,8 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, static int catpt_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); stream = snd_soc_dai_get_dma_data(dai, substream); if (!stream->allocated) @@ -444,8 +444,8 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream, static int catpt_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; stream = snd_soc_dai_get_dma_data(dai, substream); @@ -467,9 +467,9 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream, static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - struct catpt_stream_runtime *stream; struct snd_pcm_runtime *runtime = substream->runtime; + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); snd_pcm_uframes_t pos; int ret; @@ -595,9 +595,8 @@ static int catpt_component_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtm = substream->private_data; - if (rtm->dai_link->no_pcm) - return 0; - snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + if (!rtm->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); return 0; } @@ -605,10 +604,10 @@ static snd_pcm_uframes_t catpt_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - struct catpt_stream_runtime *stream; struct snd_soc_pcm_runtime *rtm = substream->private_data; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); + struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); u32 pos; if (rtm->dai_link->no_pcm) @@ -633,8 +632,8 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, struct snd_soc_dai *dai) { struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0); - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); struct catpt_ssp_device_format devfmt; + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); int ret; devfmt.iface = dai->driver->id; @@ -894,8 +893,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; u32 dspvol; int i; @@ -926,8 +925,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); long *ctlvol = (long *)kcontrol->private_value; int ret, i; @@ -1002,8 +1001,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); struct catpt_stream_runtime *stream; + struct catpt_dev *cdev = dev_get_drvdata(component->dev); bool mute; int ret; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index a0f6a69c70..f32bcb2b2e 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -74,6 +74,15 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt711_sdca_2_adr[] = { + { + .adr = 0x000230025D071101ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt711" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { { .adr = 0x000131025D131601ull, /* unique ID is set for some reason */ @@ -101,6 +110,24 @@ static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1316_0_group2_adr[] = { + { + .adr = 0x000031025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1316-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = { + { + .adr = 0x000130025D131601ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" + } +}; + static const struct snd_soc_acpi_adr_device rt1316_2_single_adr[] = { { .adr = 0x000230025D131601ull, @@ -209,6 +236,63 @@ static const struct snd_soc_acpi_link_adr adl_sdca_3_in_1[] = { {} }; +static const struct snd_soc_acpi_link_adr adl_sdw_rt711_link2_rt1316_link01_rt714_link3[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt711_sdca_2_adr), + .adr_d = rt711_sdca_2_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt1316_0_group2_adr), + .adr_d = rt1316_0_group2_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group2_adr), + .adr_d = rt1316_1_group2_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt714_3_adr), + .adr_d = rt714_3_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link12_rt714_link0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group1_adr), + .adr_d = rt1316_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group1_adr), + .adr_d = rt1316_2_group1_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link3[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_single_adr), + .adr_d = rt1316_2_single_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt714_3_adr), + .adr_d = rt714_3_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr adl_sdw_rt1316_link2_rt714_link0[] = { { .mask = BIT(2), @@ -280,9 +364,29 @@ static const struct snd_soc_acpi_codecs adl_max98357a_amp = { .codecs = {"MX98357A"} }; +static const struct snd_soc_acpi_codecs adl_max98360a_amp = { + .num_codecs = 1, + .codecs = {"MX98360A"} +}; + +static const struct snd_soc_acpi_codecs adl_rt5682_rt5682s_hp = { + .num_codecs = 2, + .codecs = {"10EC5682", "RTL5682"}, +}; + +static const struct snd_soc_acpi_codecs adl_rt1019p_amp = { + .num_codecs = 1, + .codecs = {"RTL1019"} +}; + +static const struct snd_soc_acpi_codecs adl_max98390_amp = { + .num_codecs = 1, + .codecs = {"MX98390"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { { - .id = "10EC5682", + .comp_ids = &adl_rt5682_rt5682s_hp, .drv_name = "adl_mx98373_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98373_amp, @@ -290,13 +394,59 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { .sof_tplg_filename = "sof-adl-max98373-rt5682.tplg", }, { - .id = "10EC5682", - .drv_name = "adl_mx98357a_rt5682", + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_mx98357_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &adl_max98357a_amp, .sof_fw_filename = "sof-adl.ri", .sof_tplg_filename = "sof-adl-max98357a-rt5682.tplg", }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_mx98360_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98360a_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98360a-rt5682.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_rt1019p_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_rt1019p_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt1019-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_max98373_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98373_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98373-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "adl_mx98360a_nau8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98360a_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-mx98360a-nau8825.tplg", + }, + { + .id = "10508825", + .drv_name = "sof_nau8825", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-nau8825.tplg", + }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_max98390_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_max98390_amp, + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-max98390-rt5682.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_adl_machines); @@ -321,6 +471,27 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-rt711-l0-rt1316-l13-rt714-l2.tplg", }, + { + .link_mask = 0xF, /* 4 active links required */ + .links = adl_sdw_rt711_link2_rt1316_link01_rt714_link3, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt711-l2-rt1316-l01-rt714-l3.tplg", + }, + { + .link_mask = 0xC, /* rt1316 on link2 & rt714 on link3 */ + .links = adl_sdw_rt1316_link2_rt714_link3, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg", + }, + { + .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */ + .links = adl_sdw_rt1316_link12_rt714_link0, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-adl.ri", + .sof_tplg_filename = "sof-adl-rt1316-l12-rt714-l0.tplg", + }, { .link_mask = 0x5, /* 2 active links required */ .links = adl_sdw_rt1316_link2_rt714_link0, diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 576407b5da..342d340522 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -41,7 +41,7 @@ static struct snd_soc_acpi_mach *apl_quirk(void *arg) return mach; } -static struct snd_soc_acpi_codecs bxt_codecs = { +static const struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; @@ -82,6 +82,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_fw_filename = "sof-apl.ri", .sof_tplg_filename = "sof-apl-tdf8532.tplg", }, + { + .id = "ESSX8336", + .drv_name = "sof-essx8336", + .sof_fw_filename = "sof-apl.ri", + .sof_tplg_filename = "sof-apl-es8336.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 510a5f38b7..1420009918 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -120,9 +120,29 @@ static struct snd_soc_acpi_mach *byt_quirk(void *arg) } } +static const struct snd_soc_acpi_codecs rt5640_comp_ids = { + .num_codecs = 3, + .codecs = { "10EC5640", "10EC5642", "INTCCFFD"}, +}; + +static const struct snd_soc_acpi_codecs wm5102_comp_ids = { + .num_codecs = 2, + .codecs = { "WM510204", "WM510205"}, +}; + +static const struct snd_soc_acpi_codecs da7213_comp_ids = { + .num_codecs = 2, + .codecs = { "DGLS7212", "DGLS7213"}, +}; + +static const struct snd_soc_acpi_codecs rt5645_comp_ids = { + .num_codecs = 2, + .codecs = { "10EC5645", "10EC5648"}, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { { - .id = "10EC5640", + .comp_ids = &rt5640_comp_ids, .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_rt5640", @@ -130,22 +150,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .sof_fw_filename = "sof-byt.ri", .sof_tplg_filename = "sof-byt-rt5640.tplg", }, - { - .id = "10EC5642", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .sof_fw_filename = "sof-byt.ri", - .sof_tplg_filename = "sof-byt-rt5640.tplg", - }, - { - .id = "INTCCFFD", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .sof_fw_filename = "sof-byt.ri", - .sof_tplg_filename = "sof-byt-rt5640.tplg", - }, { .id = "10EC5651", .drv_name = "bytcr_rt5651", @@ -155,7 +159,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .sof_tplg_filename = "sof-byt-rt5651.tplg", }, { - .id = "WM510204", + .comp_ids = &wm5102_comp_ids, .drv_name = "bytcr_wm5102", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcr_wm5102", @@ -163,23 +167,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { .sof_tplg_filename = "sof-byt-wm5102.tplg", }, { - .id = "WM510205", - .drv_name = "bytcr_wm5102", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_wm5102", - .sof_fw_filename = "sof-byt.ri", - .sof_tplg_filename = "sof-byt-wm5102.tplg", - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .sof_fw_filename = "sof-byt.ri", - .sof_tplg_filename = "sof-byt-da7213.tplg", - }, - { - .id = "DLGS7213", + .comp_ids = &da7213_comp_ids, .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_0f28.bin", .board = "bytcht_da7213", @@ -202,15 +190,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { }, /* some Baytrail platforms rely on RT5645, use CHT machine driver */ { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .sof_fw_filename = "sof-byt.ri", - .sof_tplg_filename = "sof-byt-rt5645.tplg", - }, - { - .id = "10EC5648", + .comp_ids = &rt5645_comp_ids, .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_0f28.bin", .board = "cht-bsw", diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index 227424236f..c60a5e8e7b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -51,10 +51,31 @@ static struct snd_soc_acpi_mach *cht_quirk(void *arg) return mach; } +static const struct snd_soc_acpi_codecs rt5640_comp_ids = { + .num_codecs = 2, + .codecs = { "10EC5640", "10EC3276" }, +}; + +static const struct snd_soc_acpi_codecs rt5670_comp_ids = { + .num_codecs = 2, + .codecs = { "10EC5670", "10EC5672" }, +}; + +static const struct snd_soc_acpi_codecs rt5645_comp_ids = { + .num_codecs = 3, + .codecs = { "10EC5645", "10EC5650", "10EC3270" }, +}; + +static const struct snd_soc_acpi_codecs da7213_comp_ids = { + .num_codecs = 2, + .codecs = { "DGLS7212", "DGLS7213"}, + +}; + /* Cherryview-based platforms: CherryTrail and Braswell */ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { { - .id = "10EC5670", + .comp_ids = &rt5670_comp_ids, .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", @@ -62,31 +83,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .sof_tplg_filename = "sof-cht-rt5670.tplg", }, { - .id = "10EC5672", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", - .sof_tplg_filename = "sof-cht-rt5670.tplg", - }, - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", - .sof_tplg_filename = "sof-cht-rt5645.tplg", - }, - { - .id = "10EC5650", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .sof_fw_filename = "sof-cht.ri", - .sof_tplg_filename = "sof-cht-rt5645.tplg", - }, - { - .id = "10EC3270", + .comp_ids = &rt5645_comp_ids, .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", .board = "cht-bsw", @@ -110,15 +107,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .sof_tplg_filename = "sof-cht-nau8824.tplg", }, { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .sof_fw_filename = "sof-cht.ri", - .sof_tplg_filename = "sof-cht-da7213.tplg", - }, - { - .id = "DLGS7213", + .comp_ids = &da7213_comp_ids, .drv_name = "bytcht_da7213", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcht_da7213", @@ -135,7 +124,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { }, /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ { - .id = "10EC5640", + .comp_ids = &rt5640_comp_ids, .drv_name = "bytcr_rt5640", .fw_filename = "intel/fw_sst_22a8.bin", .board = "bytcr_rt5640", @@ -143,14 +132,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { .sof_fw_filename = "sof-cht.ri", .sof_tplg_filename = "sof-cht-rt5640.tplg", }, - { - .id = "10EC3276", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .sof_fw_filename = "sof-cht.ri", - .sof_tplg_filename = "sof-cht-rt5640.tplg", - }, { .id = "10EC5682", .drv_name = "sof_rt5682", diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index b591c6fd13..4eebc79d4b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -9,22 +9,22 @@ #include #include -static struct snd_soc_acpi_codecs rt1011_spk_codecs = { +static const struct snd_soc_acpi_codecs rt1011_spk_codecs = { .num_codecs = 1, .codecs = {"10EC1011"} }; -static struct snd_soc_acpi_codecs rt1015_spk_codecs = { +static const struct snd_soc_acpi_codecs rt1015_spk_codecs = { .num_codecs = 1, .codecs = {"10EC1015"} }; -static struct snd_soc_acpi_codecs max98357a_spk_codecs = { +static const struct snd_soc_acpi_codecs max98357a_spk_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; -static struct snd_soc_acpi_codecs max98390_spk_codecs = { +static const struct snd_soc_acpi_codecs max98390_spk_codecs = { .num_codecs = 1, .codecs = {"MX98390"} }; @@ -81,6 +81,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_machines[] = { .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-da7219-max98390.tplg", }, + { + .id = "ESSX8336", + .drv_name = "sof-essx8336", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-es8336.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cml_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-glk-match.c b/sound/soc/intel/common/soc-acpi-intel-glk-match.c index da1e151190..8492b7e2a9 100644 --- a/sound/soc/intel/common/soc-acpi-intel-glk-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-glk-match.c @@ -9,7 +9,7 @@ #include #include -static struct snd_soc_acpi_codecs glk_codecs = { +static const struct snd_soc_acpi_codecs glk_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; @@ -40,6 +40,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-rt5682.tplg", }, + { + .id = "RTL5682", + .drv_name = "glk_rt5682_max98357a", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &glk_codecs, + .sof_fw_filename = "sof-glk.ri", + .sof_tplg_filename = "sof-glk-rt5682.tplg", + }, { .id = "10134242", .drv_name = "glk_cs4242_mx98357a", @@ -49,7 +57,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[] = { .sof_fw_filename = "sof-glk.ri", .sof_tplg_filename = "sof-glk-cs42l42.tplg", }, - + { + .id = "ESSX8336", + .drv_name = "sof-essx8336", + .sof_fw_filename = "sof-glk.ri", + .sof_tplg_filename = "sof-glk-es8336.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_glk_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index 69ff7286d3..278ec196da 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -9,26 +9,31 @@ #include #include -static struct snd_soc_acpi_codecs jsl_7219_98373_codecs = { +static const struct snd_soc_acpi_codecs jsl_7219_98373_codecs = { .num_codecs = 1, .codecs = {"MX98373"} }; -static struct snd_soc_acpi_codecs rt1015_spk = { +static const struct snd_soc_acpi_codecs rt1015_spk = { .num_codecs = 1, .codecs = {"10EC1015"} }; -static struct snd_soc_acpi_codecs rt1015p_spk = { +static const struct snd_soc_acpi_codecs rt1015p_spk = { .num_codecs = 1, .codecs = {"RTL1015"} }; -static struct snd_soc_acpi_codecs mx98360a_spk = { +static const struct snd_soc_acpi_codecs mx98360a_spk = { .num_codecs = 1, .codecs = {"MX98360A"} }; +static const struct snd_soc_acpi_codecs rt5682_rt5682s_hp = { + .num_codecs = 2, + .codecs = {"10EC5682", "RTL5682"}, +}; + /* * When adding new entry to the snd_soc_acpi_intel_jsl_machines array, * use .quirk_data member to distinguish different machine driver, @@ -50,7 +55,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .sof_tplg_filename = "sof-jsl-da7219-mx98360a.tplg", }, { - .id = "10EC5682", + .comp_ids = &rt5682_rt5682s_hp, .drv_name = "jsl_rt5682_rt1015", .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, @@ -58,7 +63,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { - .id = "10EC5682", + .comp_ids = &rt5682_rt5682s_hp, .drv_name = "jsl_rt5682_rt1015p", .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, @@ -66,8 +71,8 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .sof_tplg_filename = "sof-jsl-rt5682-rt1015.tplg", }, { - .id = "10EC5682", - .drv_name = "jsl_rt5682_mx98360a", + .comp_ids = &rt5682_rt5682s_hp, + .drv_name = "jsl_rt5682_mx98360", .sof_fw_filename = "sof-jsl.ri", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &mx98360a_spk, @@ -81,6 +86,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-cs42l42-mx98360a.tplg", }, + { + .id = "ESSX8336", + .drv_name = "sof-essx8336", + .sof_fw_filename = "sof-jsl.ri", + .sof_tplg_filename = "sof-jsl-es8336.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_jsl_machines); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index 741bf2f9e0..4e817f559d 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -12,32 +12,32 @@ static struct skl_machine_pdata skl_dmic_data; -static struct snd_soc_acpi_codecs kbl_codecs = { +static const struct snd_soc_acpi_codecs kbl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { +static const struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} }; -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { +static const struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { .num_codecs = 2, .codecs = {"10EC5663", "10EC5514"} }; -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { +static const struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; -static struct snd_soc_acpi_codecs kbl_7219_98927_codecs = { +static const struct snd_soc_acpi_codecs kbl_7219_98927_codecs = { .num_codecs = 1, .codecs = {"MX98927"} }; -static struct snd_soc_acpi_codecs kbl_7219_98373_codecs = { +static const struct snd_soc_acpi_codecs kbl_7219_98373_codecs = { .num_codecs = 1, .codecs = {"MX98373"} }; diff --git a/sound/soc/intel/common/soc-acpi-intel-skl-match.c b/sound/soc/intel/common/soc-acpi-intel-skl-match.c index 961df8d6b5..75302e9567 100644 --- a/sound/soc/intel/common/soc-acpi-intel-skl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-skl-match.c @@ -12,7 +12,7 @@ static struct skl_machine_pdata skl_dmic_data; -static struct snd_soc_acpi_codecs skl_codecs = { +static const struct snd_soc_acpi_codecs skl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 11801b905e..da31bb3cca 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -358,17 +358,22 @@ static const struct snd_soc_acpi_codecs tgl_rt1011_amp = { .codecs = {"10EC1011"} }; +static const struct snd_soc_acpi_codecs tgl_rt5682_rt5682s_hp = { + .num_codecs = 2, + .codecs = {"10EC5682", "RTL5682"}, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { { - .id = "10EC5682", - .drv_name = "tgl_mx98357a_rt5682", + .comp_ids = &tgl_rt5682_rt5682s_hp, + .drv_name = "tgl_mx98357_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_codecs, .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-max98357a-rt5682.tplg", }, { - .id = "10EC5682", + .comp_ids = &tgl_rt5682_rt5682s_hp, .drv_name = "tgl_mx98373_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_max98373_amp, @@ -376,13 +381,19 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = { .sof_tplg_filename = "sof-tgl-max98373-rt5682.tplg", }, { - .id = "10EC5682", + .comp_ids = &tgl_rt5682_rt5682s_hp, .drv_name = "tgl_rt1011_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &tgl_rt1011_amp, .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt1011-rt5682.tplg", }, + { + .id = "ESSX8336", + .drv_name = "sof-essx8336", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-es8336.tplg", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); diff --git a/sound/soc/intel/common/soc-intel-quirks.h b/sound/soc/intel/common/soc-intel-quirks.h index a93987ab7f..de4e550c5b 100644 --- a/sound/soc/intel/common/soc-intel-quirks.h +++ b/sound/soc/intel/common/soc-intel-quirks.h @@ -9,34 +9,13 @@ #ifndef _SND_SOC_INTEL_QUIRKS_H #define _SND_SOC_INTEL_QUIRKS_H +#include + #if IS_ENABLED(CONFIG_X86) #include -#include -#include #include -#define SOC_INTEL_IS_CPU(soc, type) \ -static inline bool soc_intel_is_##soc(void) \ -{ \ - static const struct x86_cpu_id soc##_cpu_ids[] = { \ - X86_MATCH_INTEL_FAM6_MODEL(type, NULL), \ - {} \ - }; \ - const struct x86_cpu_id *id; \ - \ - id = x86_match_cpu(soc##_cpu_ids); \ - if (id) \ - return true; \ - return false; \ -} - -SOC_INTEL_IS_CPU(byt, ATOM_SILVERMONT); -SOC_INTEL_IS_CPU(cht, ATOM_AIRMONT); -SOC_INTEL_IS_CPU(apl, ATOM_GOLDMONT); -SOC_INTEL_IS_CPU(glk, ATOM_GOLDMONT_PLUS); -SOC_INTEL_IS_CPU(cml, KABYLAKE_L); - static inline bool soc_intel_is_byt_cr(struct platform_device *pdev) { /* @@ -114,30 +93,6 @@ static inline bool soc_intel_is_byt_cr(struct platform_device *pdev) return false; } -static inline bool soc_intel_is_byt(void) -{ - return false; -} - -static inline bool soc_intel_is_cht(void) -{ - return false; -} - -static inline bool soc_intel_is_apl(void) -{ - return false; -} - -static inline bool soc_intel_is_glk(void) -{ - return false; -} - -static inline bool soc_intel_is_cml(void) -{ - return false; -} #endif - #endif /* _SND_SOC_INTEL_QUIRKS_H */ +#endif /* _SND_SOC_INTEL_QUIRKS_H */ diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 64226072f0..2439a574ac 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -13,108 +13,6 @@ #include "skl.h" #include "skl-i2s.h" -static struct nhlt_specific_cfg *skl_get_specific_cfg( - struct device *dev, struct nhlt_fmt *fmt, - u8 no_ch, u32 rate, u16 bps, u8 linktype) -{ - struct nhlt_specific_cfg *sp_config; - struct wav_fmt *wfmt; - struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; - int i; - - dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); - - for (i = 0; i < fmt->fmt_count; i++) { - wfmt = &fmt_config->fmt_ext.fmt; - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, - wfmt->bits_per_sample, wfmt->samples_per_sec); - if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { - /* - * if link type is dmic ignore rate check as the blob is - * generic for all rates - */ - sp_config = &fmt_config->config; - if (linktype == NHLT_LINK_DMIC) - return sp_config; - - if (wfmt->samples_per_sec == rate) - return sp_config; - } - - fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + - fmt_config->config.size); - } - - return NULL; -} - -static void dump_config(struct device *dev, u32 instance_id, u8 linktype, - u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) -{ - dev_dbg(dev, "Input configuration\n"); - dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); - dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); - dev_dbg(dev, "bits_per_sample=%d\n", bps); -} - -static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, - u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) -{ - dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", - epnt->virtual_bus_id, epnt->linktype, - epnt->direction, epnt->device_type); - - if ((epnt->virtual_bus_id == instance_id) && - (epnt->linktype == link_type) && - (epnt->direction == dirn)) { - /* do not check dev_type for DMIC link type */ - if (epnt->linktype == NHLT_LINK_DMIC) - return true; - - if (epnt->device_type == dev_type) - return true; - } - - return false; -} - -struct nhlt_specific_cfg -*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type, - u8 s_fmt, u8 num_ch, u32 s_rate, - u8 dirn, u8 dev_type) -{ - struct nhlt_fmt *fmt; - struct nhlt_endpoint *epnt; - struct hdac_bus *bus = skl_to_bus(skl); - struct device *dev = bus->dev; - struct nhlt_specific_cfg *sp_config; - struct nhlt_acpi_table *nhlt = skl->nhlt; - u16 bps = (s_fmt == 16) ? 16 : 32; - u8 j; - - dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); - - epnt = (struct nhlt_endpoint *)nhlt->desc; - - dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); - - for (j = 0; j < nhlt->endpoint_count; j++) { - if (skl_check_ep_match(dev, epnt, instance, link_type, - dirn, dev_type)) { - fmt = (struct nhlt_fmt *)(epnt->config.caps + - epnt->config.size); - sp_config = skl_get_specific_cfg(dev, fmt, num_ch, - s_rate, bps, link_type); - if (sp_config) - return sp_config; - } - - epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); - } - - return NULL; -} - static void skl_nhlt_trim_space(char *trim) { char *s = trim; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index e4aa366d35..55f310e91b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -317,6 +317,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "dma_id=%d\n", dma_id); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.host_dma_id = dma_id; @@ -405,6 +406,7 @@ static int skl_be_hw_params(struct snd_pcm_substream *substream, struct skl_pipe_params p_params = {0}; p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; @@ -562,13 +564,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.s_cont = snd_pcm_format_physical_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b036852d68..9bdf020a2b 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -285,7 +285,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, { struct skl_module_cfg *m_cfg = w->priv; int link_type, dir; - u32 ch, s_freq, s_fmt; + u32 ch, s_freq, s_fmt, s_cont; struct nhlt_specific_cfg *cfg; u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type); int fmt_idx = m_cfg->fmt_idx; @@ -301,7 +301,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, link_type = NHLT_LINK_DMIC; dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; break; @@ -310,12 +311,14 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) { dir = SNDRV_PCM_STREAM_PLAYBACK; s_freq = m_iface->outputs[0].fmt.s_freq; - s_fmt = m_iface->outputs[0].fmt.bit_depth; + s_fmt = m_iface->outputs[0].fmt.valid_bit_depth; + s_cont = m_iface->outputs[0].fmt.bit_depth; ch = m_iface->outputs[0].fmt.channels; } else { dir = SNDRV_PCM_STREAM_CAPTURE; s_freq = m_iface->inputs[0].fmt.s_freq; - s_fmt = m_iface->inputs[0].fmt.bit_depth; + s_fmt = m_iface->inputs[0].fmt.valid_bit_depth; + s_cont = m_iface->inputs[0].fmt.bit_depth; ch = m_iface->inputs[0].fmt.channels; } break; @@ -325,16 +328,17 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, } /* update the blob based on virtual bus_id and default params */ - cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, - s_fmt, ch, s_freq, dir, dev_type); + cfg = intel_nhlt_get_endpoint_blob(skl->dev, skl->nhlt, m_cfg->vbus_id, + link_type, s_fmt, s_cont, ch, + s_freq, dir, dev_type); if (cfg) { m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; } else { dev_err(skl->dev, "Blob NULL for id %x type %d dirn %d\n", m_cfg->vbus_id, link_type, dir); - dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d\n", - ch, s_freq, s_fmt); + dev_err(skl->dev, "PCM: ch %d, freq %d, fmt %d/%d\n", + ch, s_freq, s_fmt, s_cont); return -EIO; } @@ -1849,10 +1853,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt; /* update the blob based on virtual bus_id*/ - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, - pipe_fmt->bps, pipe_fmt->channels, - pipe_fmt->freq, pipe->direction, - dev_type); + cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt, + mconfig->vbus_id, link_type, + pipe_fmt->bps, params->s_cont, + pipe_fmt->channels, pipe_fmt->freq, + pipe->direction, dev_type); if (cfg) { mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *)&cfg->caps; @@ -3637,7 +3642,7 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, return 0; } -static void skl_tplg_complete(struct snd_soc_component *component) +static int skl_tplg_complete(struct snd_soc_component *component) { struct snd_soc_dobj *dobj; struct snd_soc_acpi_mach *mach; @@ -3646,7 +3651,7 @@ static void skl_tplg_complete(struct snd_soc_component *component) val = kmalloc(sizeof(*val), GFP_KERNEL); if (!val) - return; + return -ENOMEM; mach = dev_get_platdata(component->card->dev); list_for_each_entry(dobj, &component->dobj_list, list) { @@ -3671,7 +3676,9 @@ static void skl_tplg_complete(struct snd_soc_component *component) } } } + kfree(val); + return 0; } static struct snd_soc_tplg_ops skl_tplg_ops = { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index f0695b2ac5..22963634fb 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -284,6 +284,7 @@ struct skl_pipe_params { u32 ch; u32 s_freq; u32 s_fmt; + u32 s_cont; u8 linktype; snd_pcm_format_t format; int link_index; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5b1a15e399..aeca58246f 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -439,7 +439,7 @@ static int skl_free(struct hdac_bus *bus) skl->init_done = 0; /* to be sure */ - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); if (bus->irq >= 0) free_irq(bus->irq, (void *)bus); @@ -952,6 +952,7 @@ static int skl_first_init(struct hdac_bus *bus) /* allow 64bit DMA address if supported by H/W */ if (dma_set_mask_and_coherent(bus->dev, DMA_BIT_MASK(64))) dma_set_mask_and_coherent(bus->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(bus->dev, UINT_MAX); /* initialize streams */ snd_hdac_ext_stream_init_all @@ -1096,7 +1097,7 @@ static void skl_shutdown(struct pci_dev *pci) if (!skl->init_done) return; - snd_hdac_ext_stop_streams(bus); + snd_hdac_stop_streams_and_chip(bus); list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 33ed274fc0..f55f8b3dbd 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -165,10 +165,6 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); -struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance, - u8 link_type, u8 s_fmt, u8 num_ch, - u32 s_rate, u8 dirn, u8 dev_type); - int skl_nhlt_update_topology_bin(struct skl_dev *skl); int skl_init_dsp(struct skl_dev *skl); int skl_free_dsp(struct skl_dev *skl); diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 81ad2dcee9..0d154350f1 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -120,7 +120,7 @@ config SND_SOC_MT8183 config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A tristate "ASoC Audio driver for MT8183 with MT6358 TS3A227E MAX98357A RT1015 codec" - depends on I2C + depends on I2C && GPIOLIB depends on SND_SOC_MT8183 select SND_SOC_MT6358 select SND_SOC_MAX98357A @@ -138,7 +138,7 @@ config SND_SOC_MT8183_MT6358_TS3A227E_MAX98357A config SND_SOC_MT8183_DA7219_MAX98357A tristate "ASoC Audio driver for MT8183 with DA7219 MAX98357A RT1015 codec" - depends on SND_SOC_MT8183 && I2C + depends on SND_SOC_MT8183 && I2C && GPIOLIB select SND_SOC_MT6358 select SND_SOC_MAX98357A select SND_SOC_RT1015 @@ -173,7 +173,7 @@ config SND_SOC_MT8192 config SND_SOC_MT8192_MT6359_RT1015_RT5682 tristate "ASoC Audio driver for MT8192 with MT6359 RT1015 RT5682 codec" - depends on I2C + depends on I2C && GPIOLIB depends on SND_SOC_MT8192 && MTK_PMIC_WRAP select SND_SOC_MT6359 select SND_SOC_RT1015 @@ -200,11 +200,12 @@ config SND_SOC_MT8195 config SND_SOC_MT8195_MT6359_RT1019_RT5682 tristate "ASoC Audio driver for MT8195 with MT6359 RT1019 RT5682 codec" - depends on I2C + depends on I2C && GPIOLIB depends on SND_SOC_MT8195 && MTK_PMIC_WRAP select SND_SOC_MT6359 select SND_SOC_RT1015P select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_DMIC select SND_SOC_HDMI_CODEC help @@ -212,3 +213,19 @@ config SND_SOC_MT8195_MT6359_RT1019_RT5682 with the MT6359 RT1019 RT5682 audio codec. Select Y if you have such device. If unsure select "N". + +config SND_SOC_MT8195_MT6359_RT1011_RT5682 + tristate "ASoC Audio driver for MT8195 with MT6359 RT1011 RT5682 codec" + depends on I2C && GPIOLIB + depends on SND_SOC_MT8195 && MTK_PMIC_WRAP + select SND_SOC_MT6359 + select SND_SOC_RT1011 + select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S + select SND_SOC_DMIC + select SND_SOC_HDMI_CODEC + help + This adds ASoC driver for Mediatek MT8195 boards + with the MT6359 RT1011 RT5682 audio codec. + Select Y if you have such device. + If unsure select "N". diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c index e95c7c018e..395be97f13 100644 --- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c +++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c @@ -288,7 +288,6 @@ const struct snd_soc_dai_ops mtk_afe_fe_ops = { }; EXPORT_SYMBOL_GPL(mtk_afe_fe_ops); -static DEFINE_MUTEX(irqs_lock); int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe) { int i; @@ -351,7 +350,7 @@ int mtk_afe_resume(struct snd_soc_component *component) struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); struct device *dev = afe->dev; struct regmap *regmap = afe->regmap; - int i = 0; + int i; if (pm_runtime_status_suspended(dev) || !afe->suspended) return 0; diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c index bc3d046647..0f178de92a 100644 --- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c +++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c @@ -1474,9 +1474,7 @@ static struct platform_driver mt2701_afe_pcm_driver = { .driver = { .name = "mt2701-audio", .of_match_table = mt2701_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt2701_afe_pm_ops, -#endif }, .probe = mt2701_afe_pcm_dev_probe, .remove = mt2701_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c index 44a8d5cfb0..d9fd6eb786 100644 --- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c +++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -146,7 +146,7 @@ static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops mt2701_cs42448_be_ops = { +static const struct snd_soc_ops mt2701_cs42448_be_ops = { .hw_params = mt2701_cs42448_be_ops_hw_params }; diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c index 414e422c0e..f56de1b918 100644 --- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c +++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -40,7 +40,7 @@ static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops mt2701_wm8960_be_ops = { +static const struct snd_soc_ops mt2701_wm8960_be_ops = { .hw_params = mt2701_wm8960_be_ops_hw_params }; diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 3d68e4726e..fb4abec9aa 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -901,9 +901,7 @@ static struct platform_driver mt6797_afe_pcm_driver = { .driver = { .name = "mt6797-audio", .of_match_table = mt6797_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt6797_afe_pm_ops, -#endif }, .probe = mt6797_afe_pcm_dev_probe, .remove = mt6797_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index 6350390414..3149493043 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1054,6 +1054,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) int irq_id; struct mtk_base_afe *afe; struct mt8173_afe_private *afe_priv; + struct snd_soc_component *comp_pcm, *comp_hdmi; ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33)); if (ret) @@ -1142,23 +1143,55 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) if (ret) goto err_pm_disable; - ret = devm_snd_soc_register_component(&pdev->dev, - &mt8173_afe_pcm_dai_component, - mt8173_afe_pcm_dais, - ARRAY_SIZE(mt8173_afe_pcm_dais)); + comp_pcm = devm_kzalloc(&pdev->dev, sizeof(*comp_pcm), GFP_KERNEL); + if (!comp_pcm) { + ret = -ENOMEM; + goto err_pm_disable; + } + + ret = snd_soc_component_initialize(comp_pcm, + &mt8173_afe_pcm_dai_component, + &pdev->dev); if (ret) goto err_pm_disable; - ret = devm_snd_soc_register_component(&pdev->dev, - &mt8173_afe_hdmi_dai_component, - mt8173_afe_hdmi_dais, - ARRAY_SIZE(mt8173_afe_hdmi_dais)); +#ifdef CONFIG_DEBUG_FS + comp_pcm->debugfs_prefix = "pcm"; +#endif + + ret = snd_soc_add_component(comp_pcm, + mt8173_afe_pcm_dais, + ARRAY_SIZE(mt8173_afe_pcm_dais)); if (ret) goto err_pm_disable; + comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL); + if (!comp_hdmi) { + ret = -ENOMEM; + goto err_pm_disable; + } + + ret = snd_soc_component_initialize(comp_hdmi, + &mt8173_afe_hdmi_dai_component, + &pdev->dev); + if (ret) + goto err_pm_disable; + +#ifdef CONFIG_DEBUG_FS + comp_hdmi->debugfs_prefix = "hdmi"; +#endif + + ret = snd_soc_add_component(comp_hdmi, + mt8173_afe_hdmi_dais, + ARRAY_SIZE(mt8173_afe_hdmi_dais)); + if (ret) + goto err_cleanup_components; + dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n"); return 0; +err_cleanup_components: + snd_soc_unregister_component(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); return ret; @@ -1166,6 +1199,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev) { + snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) mt8173_afe_runtime_suspend(&pdev->dev); diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index 3bdd493131..4cb90da892 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -177,9 +177,6 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(codec_node); of_node_put(platform_node); @@ -196,9 +193,7 @@ static struct platform_driver mt8173_max98090_driver = { .driver = { .name = "mt8173-max98090", .of_match_table = mt8173_max98090_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_max98090_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 390da5bf72..b55122b99f 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -215,9 +215,6 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; @@ -233,9 +230,7 @@ static struct platform_driver mt8173_rt5650_rt5514_driver = { .driver = { .name = "mtk-rt5650-rt5514", .of_match_table = mt8173_rt5650_rt5514_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5514_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index c8e4e85e10..5716d92990 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -282,9 +282,6 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; @@ -300,9 +297,7 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = { .driver = { .name = "mtk-rt5650-rt5676", .of_match_table = mt8173_rt5650_rt5676_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_rt5676_dev_probe, }; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index e168d31f44..fc164f4f95 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -30,15 +30,15 @@ static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = { }; static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), }; static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { - {"Speaker", NULL, "SPOL"}, - {"Speaker", NULL, "SPOR"}, + {"Ext Spk", NULL, "SPOL"}, + {"Ext Spk", NULL, "SPOR"}, {"DMIC L1", NULL, "Int Mic"}, {"DMIC R1", NULL, "Int Mic"}, {"Headphone", NULL, "HPOL"}, @@ -48,7 +48,7 @@ static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = { }; static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), SOC_DAPM_PIN_SWITCH("Int Mic"), SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -320,9 +320,6 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); return ret; @@ -338,9 +335,7 @@ static struct platform_driver mt8173_rt5650_driver = { .driver = { .name = "mtk-rt5650", .of_match_table = mt8173_rt5650_dt_match, -#ifdef CONFIG_PM .pm = &snd_soc_pm_ops, -#endif }, .probe = mt8173_rt5650_dev_probe, }; diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c index 14e77df06b..86c8a523fe 100644 --- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c @@ -1279,9 +1279,7 @@ static struct platform_driver mt8183_afe_pcm_driver = { .driver = { .name = "mt8183-audio", .of_match_table = mt8183_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8183_afe_pm_ops, -#endif }, .probe = mt8183_afe_pcm_dev_probe, .remove = mt8183_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index bda103211e..718505c754 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -155,9 +155,9 @@ static const struct snd_soc_ops mt8183_da7219_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); @@ -167,9 +167,9 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -685,7 +685,6 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link; struct mt8183_da7219_max98357_priv *priv; struct pinctrl *pinctrl; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -695,11 +694,9 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index c7b10c48c6..b0ec5ebd4f 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -94,11 +94,11 @@ static const struct snd_soc_ops mt8183_mt6358_rt1015_i2s_ops = { static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S32_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S32_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S32_LE); return 0; @@ -107,11 +107,11 @@ static int mt8183_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int mt8183_rt1015_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - dev_dbg(rtd->dev, "%s(), fix format to 32bit\n", __func__); + dev_dbg(rtd->dev, "%s(), fix format to S24_LE\n", __func__); - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; @@ -335,7 +335,7 @@ static void mt8183_mt6358_tdm_shutdown(struct snd_pcm_substream *substream) __func__, ret); } -static struct snd_soc_ops mt8183_mt6358_tdm_ops = { +static const struct snd_soc_ops mt8183_mt6358_tdm_ops = { .startup = mt8183_mt6358_tdm_startup, .shutdown = mt8183_mt6358_tdm_shutdown, }; @@ -637,7 +637,6 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *ec_codec, *hdmi_codec; struct snd_soc_dai_link *dai_link; struct mt8183_mt6358_ts3a227_max98357_priv *priv; - const struct of_device_id *match; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -647,11 +646,9 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c index 31c280339c..e1e4ca9315 100644 --- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c @@ -2381,9 +2381,7 @@ static struct platform_driver mt8192_afe_pcm_driver = { .driver = { .name = "mt8192-audio", .of_match_table = mt8192_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8192_afe_pm_ops, -#endif }, .probe = mt8192_afe_pcm_dev_probe, .remove = mt8192_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c index 24a5d0adec..f7daad1bfe 100644 --- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c +++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c @@ -350,9 +350,9 @@ static int mt8192_mt6359_hdmi_init(struct snd_soc_pcm_runtime *rtd) static int mt8192_i2s_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), - 0, SNDRV_PCM_FORMAT_LAST); + 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); @@ -1106,7 +1106,6 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) struct device_node *platform_node, *hdmi_codec; int ret, i; struct snd_soc_dai_link *dai_link; - const struct of_device_id *match; struct mt8192_mt6359_priv *priv; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -1116,11 +1115,9 @@ static int mt8192_mt6359_dev_probe(struct platform_device *pdev) return -EINVAL; } - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) + card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); + if (!card) return -EINVAL; - - card = (struct snd_soc_card *)match->data; card->dev = &pdev->dev; hdmi_codec = of_parse_phandle(pdev->dev.of_node, diff --git a/sound/soc/mediatek/mt8195/Makefile b/sound/soc/mediatek/mt8195/Makefile index 44775f400b..e5f0df5010 100644 --- a/sound/soc/mediatek/mt8195/Makefile +++ b/sound/soc/mediatek/mt8195/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_SND_SOC_MT8195) += snd-soc-mt8195-afe.o # machine driver obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1019_RT5682) += mt8195-mt6359-rt1019-rt5682.o +obj-$(CONFIG_SND_SOC_MT8195_MT6359_RT1011_RT5682) += mt8195-mt6359-rt1011-rt5682.o diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c index 8420b2c713..c2543f4cff 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-clk.c @@ -326,7 +326,7 @@ int mt8195_afe_enable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_SCP_ADSP_AUDIODSP, /* bus clock for infra */ MT8195_CLK_TOP_AUDIO_H_SEL, /* clock for ADSP bus */ MT8195_CLK_TOP_AUDIO_LOCAL_BUS_SEL, /* bus clock for DRAM access */ @@ -347,7 +347,7 @@ int mt8195_afe_disable_reg_rw_clk(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A1SYS_HP, MT8195_CLK_AUD_AFE, @@ -380,11 +380,11 @@ static int mt8195_afe_enable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A1SYS, MT8195_CLK_AUD_A2SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_A1SYS_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_26M_TIMING, @@ -403,11 +403,11 @@ static int mt8195_afe_disable_timing_sys(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; int i; - unsigned int clk_array[] = { + static const unsigned int clk_array[] = { MT8195_CLK_AUD_A2SYS, MT8195_CLK_AUD_A1SYS, }; - unsigned int cg_array[] = { + static const unsigned int cg_array[] = { MT8195_TOP_CG_26M_TIMING, MT8195_TOP_CG_A2SYS_TIMING, MT8195_TOP_CG_A1SYS_TIMING, diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c index 2edb40fe27..e425f86847 100644 --- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "mt8195-afe-common.h" #include "mt8195-afe-clk.h" @@ -2232,7 +2233,7 @@ static const struct mtk_base_memif_data memif_data[MT8195_AFE_MEMIF_NUM] = { }, }; -static const struct mtk_base_irq_data irq_data[MT8195_AFE_IRQ_NUM] = { +static const struct mtk_base_irq_data irq_data_array[MT8195_AFE_IRQ_NUM] = { [MT8195_AFE_IRQ_1] = { .id = MT8195_AFE_IRQ_1, .irq_cnt_reg = -1, @@ -3057,11 +3058,16 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) { struct mtk_base_afe *afe; struct mt8195_afe_private *afe_priv; - struct resource *res; struct device *dev = &pdev->dev; int i, irq_id, ret; struct snd_soc_component *component; + ret = of_reserved_mem_device_init(dev); + if (ret) { + dev_err(dev, "failed to assign memory region: %d\n", ret); + return ret; + } + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33)); if (ret) return ret; @@ -3078,8 +3084,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) afe_priv = afe->platform_priv; afe->dev = &pdev->dev; - 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); @@ -3102,7 +3107,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev) return -ENOMEM; for (i = 0; i < afe->irqs_size; i++) - afe->irqs[i].irq_data = &irq_data[i]; + afe->irqs[i].irq_data = &irq_data_array[i]; /* init memif */ afe->memif_size = MT8195_AFE_MEMIF_NUM; @@ -3266,9 +3271,7 @@ static struct platform_driver mt8195_afe_pcm_driver = { .driver = { .name = "mt8195-audio", .of_match_table = mt8195_afe_pcm_dt_match, -#ifdef CONFIG_PM .pm = &mt8195_afe_pm_ops, -#endif }, .probe = mt8195_afe_pcm_dev_probe, .remove = mt8195_afe_pcm_dev_remove, diff --git a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c index 740aa6ddda..e0670e0dbd 100644 --- a/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c +++ b/sound/soc/mediatek/mt8195/mt8195-audsys-clk.c @@ -59,93 +59,93 @@ struct afe_gate { static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = { /* AUD0 */ - GATE_AUD0(CLK_AUD_AFE, "aud_afe", "a1sys_hp_sel", 2), - GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "a1sys_hp_sel", 4), - GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "apll4_sel", 10), - GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "apll4_sel", 11), - GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "a1sys_hp_sel", 18), - GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "apll1_sel", 19), - GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "apll2_sel", 20), - GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "aud_iec_sel", 21), - GATE_AUD0(CLK_AUD_APLL, "aud_apll", "apll1_sel", 23), - GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "apll2_sel", 24), - GATE_AUD0(CLK_AUD_DAC, "aud_dac", "a1sys_hp_sel", 25), - GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "a1sys_hp_sel", 26), - GATE_AUD0(CLK_AUD_TML, "aud_tml", "a1sys_hp_sel", 27), - GATE_AUD0(CLK_AUD_ADC, "aud_adc", "a1sys_hp_sel", 28), - GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "audio_h_sel", 31), + GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2), + GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4), + GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10), + GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11), + GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18), + GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19), + GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20), + GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21), + GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23), + GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24), + GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25), + GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26), + GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27), + GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28), + GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31), /* AUD1 */ - GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "a1sys_hp_sel", 2), - GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "a1sys_hp_sel", 10), - GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "a1sys_hp_sel", 11), - GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "a1sys_hp_sel", 12), - GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "a1sys_hp_sel", 13), - GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "a1sys_hp_sel", 14), - GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "audio_h_sel", 16), - GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "audio_h_sel", 17), - GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "a1sys_hp_sel", 18), - GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "audio_h_sel", 19), + GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2), + GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10), + GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11), + GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12), + GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13), + GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14), + GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16), + GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17), + GATE_AUD1(CLK_AUD_ADDA6_ADC, "aud_adda6_adc", "top_a1sys_hp", 18), + GATE_AUD1(CLK_AUD_ADDA6_ADC_HIRES, "aud_adda6_adc_hires", "top_audio_h", 19), /* AUD3 */ - GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "apll5_sel", 5), - GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "apll3_sel", 7), + GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5), + GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7), /* AUD4 */ - GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "a1sys_hp_sel", 0), - GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "a1sys_hp_sel", 1), - GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "a1sys_hp_sel", 6), - GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "a1sys_hp_sel", 7), - GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "a1sys_hp_sel", 8), - GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "a1sys_hp_sel", 16), - GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "a1sys_hp_sel", 17), + GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0), + GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1), + GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6), + GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7), + GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8), + GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16), + GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17), GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19), - GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "intdir_sel", 20), - GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "a1sys_hp_sel", 21), - GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "a2sys_sel", 22), - GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "a1sys_hp_sel", 24), - GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "a3sys_sel", 30), - GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "a4sys_sel", 31), + GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20), + GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21), + GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys_hf", 22), + GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24), + GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys_hf", 30), + GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys_hf", 31), /* AUD5 */ - GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "a1sys_hp_sel", 0), - GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "a1sys_hp_sel", 1), - GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "a1sys_hp_sel", 2), - GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "a1sys_hp_sel", 3), - GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "a1sys_hp_sel", 4), - GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "a1sys_hp_sel", 5), - GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "a1sys_hp_sel", 7), - GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "a1sys_hp_sel", 8), - GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "a1sys_hp_sel", 9), - GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "a1sys_hp_sel", 18), - GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "a1sys_hp_sel", 19), - GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "a1sys_hp_sel", 22), - GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "a1sys_hp_sel", 23), - GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "a1sys_hp_sel", 24), - GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "a1sys_hp_sel", 26), - GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "a1sys_hp_sel", 27), + GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0), + GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1), + GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2), + GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3), + GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4), + GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5), + GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7), + GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8), + GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9), + GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18), + GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19), + GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22), + GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23), + GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24), + GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26), + GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27), /* AUD6 */ - GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "asm_h_sel", 0), - GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "asm_h_sel", 1), - GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "asm_h_sel", 2), - GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "asm_h_sel", 3), - GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "asm_h_sel", 4), - GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "asm_h_sel", 5), - GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "asm_h_sel", 6), - GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "asm_h_sel", 7), - GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "asm_h_sel", 8), - GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "asm_h_sel", 9), - GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "asm_h_sel", 10), - GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "asm_h_sel", 11), - GATE_AUD6(CLK_AUD_GASRC12, "aud_gasrc12", "asm_h_sel", 12), - GATE_AUD6(CLK_AUD_GASRC13, "aud_gasrc13", "asm_h_sel", 13), - GATE_AUD6(CLK_AUD_GASRC14, "aud_gasrc14", "asm_h_sel", 14), - GATE_AUD6(CLK_AUD_GASRC15, "aud_gasrc15", "asm_h_sel", 15), - GATE_AUD6(CLK_AUD_GASRC16, "aud_gasrc16", "asm_h_sel", 16), - GATE_AUD6(CLK_AUD_GASRC17, "aud_gasrc17", "asm_h_sel", 17), - GATE_AUD6(CLK_AUD_GASRC18, "aud_gasrc18", "asm_h_sel", 18), - GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "asm_h_sel", 19), + GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0), + GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1), + GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2), + GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3), + GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4), + GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5), + GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6), + GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7), + GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8), + GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9), + GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10), + GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11), + GATE_AUD6(CLK_AUD_GASRC12, "aud_gasrc12", "top_asm_h", 12), + GATE_AUD6(CLK_AUD_GASRC13, "aud_gasrc13", "top_asm_h", 13), + GATE_AUD6(CLK_AUD_GASRC14, "aud_gasrc14", "top_asm_h", 14), + GATE_AUD6(CLK_AUD_GASRC15, "aud_gasrc15", "top_asm_h", 15), + GATE_AUD6(CLK_AUD_GASRC16, "aud_gasrc16", "top_asm_h", 16), + GATE_AUD6(CLK_AUD_GASRC17, "aud_gasrc17", "top_asm_h", 17), + GATE_AUD6(CLK_AUD_GASRC18, "aud_gasrc18", "top_asm_h", 18), + GATE_AUD6(CLK_AUD_GASRC19, "aud_gasrc19", "top_asm_h", 19), }; int mt8195_audsys_clk_register(struct mtk_base_afe *afe) diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-adda.c b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c index 878dec0b69..f04bd17813 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-adda.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-adda.c @@ -788,9 +788,11 @@ static int init_adda_priv_data(struct mtk_base_afe *afe) { struct mt8195_afe_private *afe_priv = afe->platform_priv; struct mtk_dai_adda_priv *adda_priv; - int adda_dai_list[] = { MT8195_AFE_IO_DL_SRC, - MT8195_AFE_IO_UL_SRC1, - MT8195_AFE_IO_UL_SRC2}; + static const int adda_dai_list[] = { + MT8195_AFE_IO_DL_SRC, + MT8195_AFE_IO_UL_SRC1, + MT8195_AFE_IO_UL_SRC2 + }; int i; for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) { diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c index 7378e42f27..c02c10da36 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c @@ -1316,7 +1316,7 @@ static int mt8195_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id) } out: spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags); - return 0; + return ret; } static int etdm_cowork_slv_sel(int id, int slave_mode) @@ -2094,7 +2094,7 @@ static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai, { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8195_afe_private *afe_priv = afe->platform_priv; - struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id]; + struct mtk_dai_etdm_priv *etdm_data; int dai_id; dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n", diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c index a3fa8efc8f..29c2d3407c 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359-rt1019-rt5682.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 -// -// mt8195-mt6359-rt1019-rt5682.c -- -// MT8195-MT6359-RT1019-RT6358 ALSA SoC machine driver -// -// Copyright (c) 2021 MediaTek Inc. -// Author: Trevor Wu -// +/* + * mt8195-mt6359-rt1019-rt5682.c -- + * MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver + * + * Copyright (c) 2021 MediaTek Inc. + * Author: Trevor Wu + * YC Hung + */ #include #include @@ -13,10 +14,12 @@ #include #include #include +#include #include #include "../../codecs/mt6359.h" #include "../../codecs/rt5682.h" #include "../common/mtk-afe-platform-driver.h" +#include "mt8195-afe-clk.h" #include "mt8195-afe-common.h" #define RT1019_CODEC_DAI "HiFi" @@ -25,10 +28,26 @@ #define RT5682_CODEC_DAI "rt5682-aif1" #define RT5682_DEV0_NAME "rt5682.2-001a" +#define RT5682S_CODEC_DAI "rt5682s-aif1" +#define RT5682S_DEV0_NAME "rt5682s.2-001a" + +#define SOF_DMA_DL2 "SOF_DMA_DL2" +#define SOF_DMA_DL3 "SOF_DMA_DL3" +#define SOF_DMA_UL4 "SOF_DMA_UL4" +#define SOF_DMA_UL5 "SOF_DMA_UL5" + +struct sof_conn_stream { + const char *normal_link; + const char *sof_link; + const char *sof_dma; + int stream_dir; +}; + struct mt8195_mt6359_rt1019_rt5682_priv { struct snd_soc_jack headset_jack; struct snd_soc_jack dp_jack; struct snd_soc_jack hdmi_jack; + struct clk *i2so1_mclk; }; static const struct snd_soc_dapm_widget @@ -36,6 +55,10 @@ static const struct snd_soc_dapm_widget SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_DL3, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL4, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER(SOF_DMA_UL5, SND_SOC_NOPM, 0, 0, NULL, 0), }; static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { @@ -45,6 +68,16 @@ static const struct snd_soc_dapm_route mt8195_mt6359_rt1019_rt5682_routes[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, { "IN1P", NULL, "Headset Mic" }, + /* SOF Uplink */ + {SOF_DMA_UL4, NULL, "O034"}, + {SOF_DMA_UL4, NULL, "O035"}, + {SOF_DMA_UL5, NULL, "O036"}, + {SOF_DMA_UL5, NULL, "O037"}, + /* SOF Downlink */ + {"I070", NULL, SOF_DMA_DL2}, + {"I071", NULL, SOF_DMA_DL2}, + {"I020", NULL, SOF_DMA_DL3}, + {"I021", NULL, SOF_DMA_DL3}, }; static const struct snd_kcontrol_new mt8195_mt6359_rt1019_rt5682_controls[] = { @@ -61,8 +94,6 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); unsigned int rate = params_rate(params); - unsigned int mclk_fs_ratio = 128; - unsigned int mclk_fs = rate * mclk_fs_ratio; int bitwidth; int ret; @@ -78,25 +109,22 @@ static int mt8195_rt5682_etdm_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, - RT5682_PLL1_S_BCLK1, - params_rate(params) * 64, - params_rate(params) * 512); + ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL1, RT5682_PLL1_S_MCLK, + rate * 256, rate * 512); if (ret) { dev_err(card->dev, "failed to set pll\n"); return ret; } - ret = snd_soc_dai_set_sysclk(codec_dai, - RT5682_SCLK_S_PLL1, - params_rate(params) * 512, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, + rate * 512, SND_SOC_CLOCK_IN); if (ret) { dev_err(card->dev, "failed to set sysclk\n"); return ret; } - return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_fs, SND_SOC_CLOCK_OUT); + return snd_soc_dai_set_sysclk(cpu_dai, 0, rate * 256, + SND_SOC_CLOCK_OUT); } static const struct snd_soc_ops mt8195_rt5682_etdm_ops = { @@ -291,8 +319,14 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) struct mt8195_mt6359_rt1019_rt5682_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_jack *jack = &priv->headset_jack; + struct snd_soc_component *cmpnt_afe = + snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt_afe); + struct mt8195_afe_private *afe_priv = afe->platform_priv; int ret; + priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2]; + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | @@ -320,7 +354,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd) static int mt8195_etdm_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -388,7 +422,7 @@ static int mt8195_dptx_hw_params(struct snd_pcm_substream *substream, SND_SOC_CLOCK_OUT); } -static struct snd_soc_ops mt8195_dptx_ops = { +static const struct snd_soc_ops mt8195_dptx_ops = { .hw_params = mt8195_dptx_hw_params, }; @@ -428,7 +462,7 @@ static int mt8195_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { - /* fix BE i2s format to 32bit, clean param mask first */ + /* fix BE i2s format to S24_LE, clean param mask first */ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST); @@ -529,6 +563,48 @@ static const struct snd_soc_ops mt8195_capture_ops = { .startup = mt8195_capture_startup, }; +static int mt8195_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + struct mt8195_mt6359_rt1019_rt5682_priv *priv = + snd_soc_card_get_drvdata(card); + int ret; + + /* + * It's required to control mclk directly in the set_bias_level_post + * function for rt5682 and rt5682s codec, or the unexpected pop happens + * at the end of playback. + */ + if (!component || + (strcmp(component->name, RT5682_DEV0_NAME) && + strcmp(component->name, RT5682S_DEV0_NAME))) + return 0; + + + switch (level) { + case SND_SOC_BIAS_OFF: + if (!__clk_is_enabled(priv->i2so1_mclk)) + return 0; + + clk_disable_unprepare(priv->i2so1_mclk); + dev_dbg(card->dev, "Disable i2so1 mclk\n"); + break; + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(priv->i2so1_mclk); + if (ret) { + dev_err(card->dev, "Can't enable i2so1 mclk: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Enable i2so1 mclk\n"); + break; + default: + break; + } + + return 0; +} + enum { DAI_LINK_DL2_FE, DAI_LINK_DL3_FE, @@ -556,8 +632,17 @@ enum { DAI_LINK_PCM1_BE, DAI_LINK_UL_SRC1_BE, DAI_LINK_UL_SRC2_BE, + DAI_LINK_REGULAR_LAST = DAI_LINK_UL_SRC2_BE, + DAI_LINK_SOF_START, + DAI_LINK_SOF_DL2_BE = DAI_LINK_SOF_START, + DAI_LINK_SOF_DL3_BE, + DAI_LINK_SOF_UL4_BE, + DAI_LINK_SOF_UL5_BE, + DAI_LINK_SOF_END = DAI_LINK_SOF_UL5_BE, }; +#define DAI_LINK_REGULAR_NUM (DAI_LINK_REGULAR_LAST + 1) + /* FE */ SND_SOC_DAILINK_DEFS(DL2_FE, DAILINK_COMP_ARRAY(COMP_CPU("DL2")), @@ -658,14 +743,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE, SND_SOC_DAILINK_DEFS(ETDM2_IN_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE, DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")), - DAILINK_COMP_ARRAY(COMP_CODEC(RT5682_DEV0_NAME, - RT5682_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_EMPTY())); SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE, @@ -698,6 +781,154 @@ SND_SOC_DAILINK_DEFS(UL_SRC2_BE, "mt6359-snd-codec-aif2")), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(AFE_SOF_DL2, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL2")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_DL3, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_DL3")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL4, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL4")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(AFE_SOF_UL5, + DAILINK_COMP_ARRAY(COMP_CPU("SOF_UL5")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static const struct sof_conn_stream g_sof_conn_streams[] = { + { "ETDM2_OUT_BE", "AFE_SOF_DL2", SOF_DMA_DL2, SNDRV_PCM_STREAM_PLAYBACK}, + { "ETDM1_OUT_BE", "AFE_SOF_DL3", SOF_DMA_DL3, SNDRV_PCM_STREAM_PLAYBACK}, + { "UL_SRC1_BE", "AFE_SOF_UL4", SOF_DMA_UL4, SNDRV_PCM_STREAM_CAPTURE}, + { "ETDM2_IN_BE", "AFE_SOF_UL5", SOF_DMA_UL5, SNDRV_PCM_STREAM_CAPTURE}, +}; + +/* fixup the BE DAI link to match any values from topology */ +static int mt8195_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai_link *sof_dai_link = NULL; + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_dai *cpu_dai; + int i, j, ret = 0; + + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + + if (strcmp(rtd->dai_link->name, conn->normal_link)) + continue; + + for_each_card_rtds(card, runtime) { + if (strcmp(runtime->dai_link->name, conn->sof_link)) + continue; + + for_each_rtd_cpu_dais(runtime, j, cpu_dai) { + if (cpu_dai->stream_active[conn->stream_dir] > 0) { + sof_dai_link = runtime->dai_link; + break; + } + } + break; + } + + if (sof_dai_link && sof_dai_link->be_hw_params_fixup) + ret = sof_dai_link->be_hw_params_fixup(runtime, params); + + break; + } + + if (!strcmp(rtd->dai_link->name, "ETDM2_IN_BE") || + !strcmp(rtd->dai_link->name, "ETDM1_OUT_BE")) { + mt8195_etdm_hw_params_fixup(runtime, params); + } + + return ret; +} + +static int mt8195_mt6359_rt1019_rt5682_card_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *runtime; + struct snd_soc_component *sof_comp = NULL; + int i; + + /* 1. find sof component */ + for_each_card_rtds(card, runtime) { + for (i = 0; i < runtime->num_components; i++) { + if (!runtime->components[i]->driver->name) + continue; + if (!strcmp(runtime->components[i]->driver->name, "sof-audio-component")) { + sof_comp = runtime->components[i]; + break; + } + } + } + + if (!sof_comp) { + dev_info(card->dev, " probe without component\n"); + return 0; + } + /* 2. add route path and fixup callback */ + for (i = 0; i < ARRAY_SIZE(g_sof_conn_streams); i++) { + const struct sof_conn_stream *conn = &g_sof_conn_streams[i]; + struct snd_soc_pcm_runtime *sof_rtd = NULL; + struct snd_soc_pcm_runtime *normal_rtd = NULL; + struct snd_soc_pcm_runtime *rtd = NULL; + + for_each_card_rtds(card, rtd) { + if (!strcmp(rtd->dai_link->name, conn->sof_link)) { + sof_rtd = rtd; + continue; + } + if (!strcmp(rtd->dai_link->name, conn->normal_link)) { + normal_rtd = rtd; + continue; + } + if (normal_rtd && sof_rtd) + break; + } + if (normal_rtd && sof_rtd) { + int j; + struct snd_soc_dai *cpu_dai; + + for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { + struct snd_soc_dapm_route route; + struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_widget *play_widget = + cpu_dai->playback_widget; + struct snd_soc_dapm_widget *cap_widget = + cpu_dai->capture_widget; + memset(&route, 0, sizeof(route)); + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && + cap_widget) { + snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + route.source = conn->sof_dma; + route.sink = p->sink->name; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && + play_widget){ + snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + route.source = p->source->name; + route.sink = conn->sof_dma; + snd_soc_dapm_add_routes(&card->dapm, &route, 1); + } + } else { + dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); + } + } + normal_rtd->dai_link->be_hw_params_fixup = mt8195_dai_link_fixup; + } + } + + return 0; +} + static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* FE */ [DAI_LINK_DL2_FE] = { @@ -892,7 +1123,6 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { /* BE */ [DAI_LINK_DL_SRC_BE] = { .name = "DL_SRC_BE", - .init = mt8195_mt6359_init, .no_pcm = 1, .dpcm_playback = 1, SND_SOC_DAILINK_REG(DL_SRC_BE), @@ -961,6 +1191,7 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, .dpcm_capture = 1, SND_SOC_DAILINK_REG(PCM1_BE), }, @@ -976,6 +1207,31 @@ static struct snd_soc_dai_link mt8195_mt6359_rt1019_rt5682_dai_links[] = { .dpcm_capture = 1, SND_SOC_DAILINK_REG(UL_SRC2_BE), }, + /* SOF BE */ + [DAI_LINK_SOF_DL2_BE] = { + .name = "AFE_SOF_DL2", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL2), + }, + [DAI_LINK_SOF_DL3_BE] = { + .name = "AFE_SOF_DL3", + .no_pcm = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(AFE_SOF_DL3), + }, + [DAI_LINK_SOF_UL4_BE] = { + .name = "AFE_SOF_UL4", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL4), + }, + [DAI_LINK_SOF_UL5_BE] = { + .name = "AFE_SOF_UL5", + .no_pcm = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(AFE_SOF_UL5), + }, }; static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { @@ -989,18 +1245,83 @@ static struct snd_soc_card mt8195_mt6359_rt1019_rt5682_soc_card = { .num_dapm_widgets = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_widgets), .dapm_routes = mt8195_mt6359_rt1019_rt5682_routes, .num_dapm_routes = ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_routes), + .set_bias_level_post = mt8195_set_bias_level_post, }; +static int mt8195_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, + const char *propname) +{ + struct device *dev = card->dev; + struct snd_soc_dai_link *link; + const char *dai_name = NULL; + int i, j, ret, num_links; + + num_links = of_property_count_strings(np, "mediatek,dai-link"); + + if (num_links < 0 || num_links > ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links)) { + dev_dbg(dev, "number of dai-link is invalid\n"); + return -EINVAL; + } + + card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + card->num_links = 0; + link = card->dai_link; + + for (i = 0; i < num_links; i++) { + ret = of_property_read_string_index(np, propname, i, &dai_name); + if (ret) { + dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n", + propname, i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(mt8195_mt6359_rt1019_rt5682_dai_links); j++) { + if (!strcmp(dai_name, mt8195_mt6359_rt1019_rt5682_dai_links[j].name)) { + memcpy(link, &mt8195_mt6359_rt1019_rt5682_dai_links[j], + sizeof(struct snd_soc_dai_link)); + link++; + card->num_links++; + break; + } + } + } + + if (card->num_links != num_links) + return -EINVAL; + + return 0; +} + static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8195_mt6359_rt1019_rt5682_soc_card; - struct device_node *platform_node; struct snd_soc_dai_link *dai_link; - struct mt8195_mt6359_rt1019_rt5682_priv *priv = NULL; + struct mt8195_mt6359_rt1019_rt5682_priv *priv; + struct device_node *platform_node, *adsp_node, *dp_node, *hdmi_node; + int is5682s = 0; + int init6359 = 0; + int sof_on = 0; int ret, i; card->dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(card, "model"); + if (ret) { + dev_err(&pdev->dev, "%s new card name parsing error %d\n", + __func__, ret); + return ret; + } + + if (strstr(card->name, "_5682s")) + is5682s = 1; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); if (!platform_node) { @@ -1008,51 +1329,79 @@ static int mt8195_mt6359_rt1019_rt5682_dev_probe(struct platform_device *pdev) return -EINVAL; } + adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0); + if (adsp_node) + sof_on = 1; + + dp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,dptx-codec", 0); + hdmi_node = of_parse_phandle(pdev->dev.of_node, + "mediatek,hdmi-codec", 0); + + if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) { + ret = mt8195_dailink_parse_of(card, pdev->dev.of_node, + "mediatek,dai-link"); + if (ret) { + dev_dbg(&pdev->dev, "Parse dai-link fail\n"); + return -EINVAL; + } + } else { + if (!sof_on) + card->num_links = DAI_LINK_REGULAR_NUM; + } + for_each_card_prelinks(card, i, dai_link) { - if (!dai_link->platforms->name) - dai_link->platforms->of_node = platform_node; + if (!dai_link->platforms->name) { + if (!strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")) && sof_on) + dai_link->platforms->of_node = adsp_node; + else + dai_link->platforms->of_node = platform_node; + } if (strcmp(dai_link->name, "DPTX_BE") == 0) { - dai_link->codecs->of_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,dptx-codec", 0); - if (!dai_link->codecs->of_node) { + if (!dp_node) { dev_dbg(&pdev->dev, "No property 'dptx-codec'\n"); } else { + dai_link->codecs->of_node = dp_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_dptx_codec_init; } - } - - if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { - dai_link->codecs->of_node = - of_parse_phandle(pdev->dev.of_node, - "mediatek,hdmi-codec", 0); - if (!dai_link->codecs->of_node) { + } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { + if (!hdmi_node) { dev_dbg(&pdev->dev, "No property 'hdmi-codec'\n"); } else { + dai_link->codecs->of_node = hdmi_node; dai_link->codecs->name = NULL; dai_link->codecs->dai_name = "i2s-hifi"; dai_link->init = mt8195_hdmi_codec_init; } + } else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 || + strcmp(dai_link->name, "ETDM2_IN_BE") == 0) { + dai_link->codecs->name = + is5682s ? RT5682S_DEV0_NAME : RT5682_DEV0_NAME; + dai_link->codecs->dai_name = + is5682s ? RT5682S_CODEC_DAI : RT5682_CODEC_DAI; + } else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 || + strcmp(dai_link->name, "UL_SRC1_BE") == 0 || + strcmp(dai_link->name, "UL_SRC2_BE") == 0) { + if (!init6359) { + dai_link->init = mt8195_mt6359_init; + init6359 = 1; + } } } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - of_node_put(platform_node); - return -ENOMEM; - } + if (sof_on) + card->late_probe = mt8195_mt6359_rt1019_rt5682_card_late_probe; snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n", - __func__, ret); of_node_put(platform_node); + of_node_put(adsp_node); + of_node_put(dp_node); + of_node_put(hdmi_node); return ret; } @@ -1084,5 +1433,6 @@ module_platform_driver(mt8195_mt6359_rt1019_rt5682_driver); /* Module information */ MODULE_DESCRIPTION("MT8195-MT6359-RT1019-RT5682 ALSA SoC machine driver"); MODULE_AUTHOR("Trevor Wu "); -MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("YC Hung "); +MODULE_LICENSE("GPL"); MODULE_ALIAS("mt8195_mt6359_rt1019_rt5682 soc card"); diff --git a/sound/soc/meson/aiu-encoder-spdif.c b/sound/soc/meson/aiu-encoder-spdif.c index de85091397..97da60db2c 100644 --- a/sound/soc/meson/aiu-encoder-spdif.c +++ b/sound/soc/meson/aiu-encoder-spdif.c @@ -113,7 +113,7 @@ static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream, val |= AIU_958_MISC_MODE_32BITS; break; default: - dev_err(dai->dev, "Unsupport physical width\n"); + dev_err(dai->dev, "Unsupported physical width\n"); return -EINVAL; } diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index ba15d5762b..d299a70db7 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -218,34 +218,23 @@ static int aiu_clk_get(struct device *dev) int ret; aiu->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(aiu->pclk)) { - if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu pclk\n"); - return PTR_ERR(aiu->pclk); - } + if (IS_ERR(aiu->pclk)) + return dev_err_probe(dev, PTR_ERR(aiu->pclk), "Can't get the aiu pclk\n"); aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); - if (IS_ERR(aiu->spdif_mclk)) { - if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER) - dev_err(dev, "Can't get the aiu spdif master clock\n"); - return PTR_ERR(aiu->spdif_mclk); - } + if (IS_ERR(aiu->spdif_mclk)) + return dev_err_probe(dev, PTR_ERR(aiu->spdif_mclk), + "Can't get the aiu spdif master clock\n"); ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids), &aiu->i2s); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the i2s clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the i2s clocks\n"); ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids), &aiu->spdif); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Can't get the spdif clocks\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Can't get the spdif clocks\n"); ret = clk_prepare_enable(aiu->pclk); if (ret) { @@ -281,11 +270,8 @@ static int aiu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, aiu); ret = device_reset(dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to reset device\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/meson/axg-card.c b/sound/soc/meson/axg-card.c index 2b77010c2c..cbbaa55d92 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -320,6 +320,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, dai_link->cpus = cpu; dai_link->num_cpus = 1; + dai_link->nonatomic = true; ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, &dai_link->cpus->dai_name); diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index b9af2d513e..bccfb770b3 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -351,20 +351,12 @@ int axg_fifo_probe(struct platform_device *pdev) } fifo->pclk = devm_clk_get(dev, NULL); - if (IS_ERR(fifo->pclk)) { - if (PTR_ERR(fifo->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %ld\n", - PTR_ERR(fifo->pclk)); - return PTR_ERR(fifo->pclk); - } + if (IS_ERR(fifo->pclk)) + return dev_err_probe(dev, PTR_ERR(fifo->pclk), "failed to get pclk\n"); fifo->arb = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(fifo->arb)) { - if (PTR_ERR(fifo->arb) != -EPROBE_DEFER) - dev_err(dev, "failed to get arb reset: %ld\n", - PTR_ERR(fifo->arb)); - return PTR_ERR(fifo->arb); - } + if (IS_ERR(fifo->arb)) + return dev_err_probe(dev, PTR_ERR(fifo->arb), "failed to get arb reset\n"); fifo->irq = of_irq_get(dev->of_node, 0); if (fifo->irq <= 0) { diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index bfd37d49a7..672e43a972 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -586,7 +586,6 @@ static int axg_pdm_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_pdm *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -611,28 +610,16 @@ static int axg_pdm_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->dclk = devm_clk_get(dev, "dclk"); - if (IS_ERR(priv->dclk)) { - ret = PTR_ERR(priv->dclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->dclk)) + return dev_err_probe(dev, PTR_ERR(priv->dclk), "failed to get dclk\n"); priv->sysclk = devm_clk_get(dev, "sysclk"); - if (IS_ERR(priv->sysclk)) { - ret = PTR_ERR(priv->sysclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get dclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->sysclk)) + return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n"); return devm_snd_soc_register_component(dev, &axg_pdm_component_drv, &axg_pdm_dai_drv, 1); diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index d0d09f945b..4ba44e0d65 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -454,7 +454,6 @@ static int axg_spdifin_probe(struct platform_device *pdev) struct axg_spdifin *priv; struct snd_soc_dai_driver *dai_drv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -479,20 +478,12 @@ static int axg_spdifin_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->refclk = devm_clk_get(dev, "refclk"); - if (IS_ERR(priv->refclk)) { - ret = PTR_ERR(priv->refclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->refclk)) + return dev_err_probe(dev, PTR_ERR(priv->refclk), "failed to get mclk\n"); dai_drv = axg_spdifin_get_dai_drv(dev, priv); if (IS_ERR(dai_drv)) { diff --git a/sound/soc/meson/axg-spdifout.c b/sound/soc/meson/axg-spdifout.c index e769a5ee6e..3960d082e1 100644 --- a/sound/soc/meson/axg-spdifout.c +++ b/sound/soc/meson/axg-spdifout.c @@ -403,7 +403,6 @@ static int axg_spdifout_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct axg_spdifout *priv; void __iomem *regs; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -422,20 +421,12 @@ static int axg_spdifout_probe(struct platform_device *pdev) } priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get pclk\n"); priv->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(priv->mclk)) { - ret = PTR_ERR(priv->mclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + if (IS_ERR(priv->mclk)) + return dev_err_probe(dev, PTR_ERR(priv->mclk), "failed to get mclk\n"); return devm_snd_soc_register_component(dev, &axg_spdifout_component_drv, axg_spdifout_dai_drv, ARRAY_SIZE(axg_spdifout_dai_drv)); diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index cab7fa2851..9883dc777f 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -255,7 +255,6 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) const struct axg_tdm_formatter_driver *drv; struct axg_tdm_formatter *formatter; void __iomem *regs; - int ret; drv = of_device_get_match_data(dev); if (!drv) { @@ -282,57 +281,34 @@ int axg_tdm_formatter_probe(struct platform_device *pdev) /* Peripharal clock */ formatter->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(formatter->pclk)) { - ret = PTR_ERR(formatter->pclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get pclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->pclk)) + return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n"); /* Formatter bit clock */ formatter->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(formatter->sclk)) { - ret = PTR_ERR(formatter->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n"); /* Formatter sample clock */ formatter->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(formatter->lrclk)) { - ret = PTR_ERR(formatter->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n"); /* Formatter bit clock input multiplexer */ formatter->sclk_sel = devm_clk_get(dev, "sclk_sel"); - if (IS_ERR(formatter->sclk_sel)) { - ret = PTR_ERR(formatter->sclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->sclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n"); /* Formatter sample clock input multiplexer */ formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel"); - if (IS_ERR(formatter->lrclk_sel)) { - ret = PTR_ERR(formatter->lrclk_sel); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk_sel: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->lrclk_sel)) + return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel), + "failed to get lrclk_sel\n"); /* Formatter dedicated reset line */ formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(formatter->reset)) { - ret = PTR_ERR(formatter->reset); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get reset: %d\n", ret); - return ret; - } + if (IS_ERR(formatter->reset)) + return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n"); return devm_snd_soc_register_component(dev, drv->component_drv, NULL, 0); diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 87cac440b3..0c31934a96 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -351,13 +351,29 @@ static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, return 0; } -static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, +static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { - struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); + struct axg_tdm_stream *ts = + snd_soc_dai_get_dma_data(dai, substream); - /* Force all attached formatters to update */ - return axg_tdm_stream_reset(ts); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + axg_tdm_stream_start(ts); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + axg_tdm_stream_stop(ts); + break; + default: + return -EINVAL; + } + + return 0; } static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) @@ -397,8 +413,8 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = { .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, .hw_params = axg_tdm_iface_hw_params, - .prepare = axg_tdm_iface_prepare, .hw_free = axg_tdm_iface_hw_free, + .trigger = axg_tdm_iface_trigger, }; /* TDM Backend DAIs */ @@ -517,21 +533,13 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) /* Bit clock provided on the pad */ iface->sclk = devm_clk_get(dev, "sclk"); - if (IS_ERR(iface->sclk)) { - ret = PTR_ERR(iface->sclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get sclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->sclk)) + return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n"); /* Sample clock provided on the pad */ iface->lrclk = devm_clk_get(dev, "lrclk"); - if (IS_ERR(iface->lrclk)) { - ret = PTR_ERR(iface->lrclk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get lrclk: %d\n", ret); - return ret; - } + if (IS_ERR(iface->lrclk)) + return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n"); /* * mclk maybe be missing when the cpu dai is in slave mode and @@ -542,13 +550,10 @@ static int axg_tdm_iface_probe(struct platform_device *pdev) iface->mclk = devm_clk_get(dev, "mclk"); if (IS_ERR(iface->mclk)) { ret = PTR_ERR(iface->mclk); - if (ret == -ENOENT) { + if (ret == -ENOENT) iface->mclk = NULL; - } else { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get mclk: %d\n", ret); - return ret; - } + else + return dev_err_probe(dev, ret, "failed to get mclk\n"); } return devm_snd_soc_register_component(dev, diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 415cc0046e..2d8d5717fd 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -85,11 +85,9 @@ int meson_card_parse_dai(struct snd_soc_card *card, ret = of_parse_phandle_with_args(node, "sound-dai", "#sound-dai-cells", 0, &args); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "can't parse dai %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(card->dev, ret, "can't parse dai\n"); + *dai_of_node = args.np; return snd_soc_get_dai_name(&args, dai_name); @@ -302,6 +300,7 @@ int meson_card_probe(struct platform_device *pdev) priv->card.owner = THIS_MODULE; priv->card.dev = dev; + priv->card.driver_name = dev->driver->name; priv->match_data = data; ret = snd_soc_of_parse_card_name(&priv->card, "model"); diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c index d07270d17c..2870cfad81 100644 --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -113,9 +113,6 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, /* Replace link params with the input params */ rtd->dai_link->params = &in_data->params; - if (!in_data->fmt) - return 0; - return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); } EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup); diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c index 4c1349dd1e..a9b8c4e77d 100644 --- a/sound/soc/meson/t9015.c +++ b/sound/soc/meson/t9015.c @@ -258,18 +258,12 @@ static int t9015_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - if (PTR_ERR(priv->pclk) != -EPROBE_DEFER) - dev_err(dev, "failed to get core clock\n"); - return PTR_ERR(priv->pclk); - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "failed to get core clock\n"); priv->avdd = devm_regulator_get(dev, "AVDD"); - if (IS_ERR(priv->avdd)) { - if (PTR_ERR(priv->avdd) != -EPROBE_DEFER) - dev_err(dev, "failed to AVDD\n"); - return PTR_ERR(priv->avdd); - } + if (IS_ERR(priv->avdd)) + return dev_err_probe(dev, PTR_ERR(priv->avdd), "failed to AVDD\n"); ret = clk_prepare_enable(priv->pclk); if (ret) { diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index a6407f4388..2412dc7e65 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -160,12 +160,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) } ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return 0; } diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index cc7c1de2f1..cf3e151bb6 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -38,6 +38,7 @@ config SND_SOC_LPASS_SC7180 config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" + depends on GPIOLIB select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -84,6 +85,25 @@ config SND_SOC_QDSP6_ASM_DAI select SND_SOC_COMPRESS tristate +config SND_SOC_QDSP6_APM_DAI + tristate + select SND_SOC_COMPRESS + +config SND_SOC_QDSP6_APM_LPASS_DAI + tristate + +config SND_SOC_QDSP6_APM + tristate + select SND_SOC_QDSP6_APM_DAI + select SND_SOC_QDSP6_APM_LPASS_DAI + +config SND_SOC_QDSP6_PRM_LPASS_CLOCKS + tristate + +config SND_SOC_QDSP6_PRM + tristate + select SND_SOC_QDSP6_PRM_LPASS_CLOCKS + config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" depends on QCOM_APR @@ -97,6 +117,9 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_ASM select SND_SOC_QDSP6_ASM_DAI + select SND_SOC_TOPOLOGY + select SND_SOC_QDSP6_APM + select SND_SOC_QDSP6_PRM help To add support for MSM QDSP6 Soc Audio. This will enable sound soc platform specific @@ -141,11 +164,12 @@ config SND_SOC_SM8250 config SND_SOC_SC7180 tristate "SoC Machine driver for SC7180 boards" - depends on I2C + depends on I2C && GPIOLIB select SND_SOC_QCOM_COMMON select SND_SOC_LPASS_SC7180 select SND_SOC_MAX98357A select SND_SOC_RT5682_I2C + select SND_SOC_RT5682S select SND_SOC_ADAU7002 help To add support for audio on Qualcomm Technologies Inc. diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index ba2a98268e..f9d6937532 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -17,6 +17,9 @@ #include #include #include "common.h" +#include "qdsp6/q6afe.h" + +#define MI2S_COUNT (MI2S_QUATERNARY + 1) struct apq8016_sbc_data { struct snd_soc_card card; @@ -24,6 +27,7 @@ struct apq8016_sbc_data { void __iomem *spkr_iomux; struct snd_soc_jack jack; bool jack_setup; + int mi2s_clk_count[MI2S_COUNT]; }; #define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21) @@ -38,10 +42,10 @@ struct apq8016_sbc_data { #define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18) #define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18) #define DEFAULT_MCLK_RATE 9600000 +#define MI2S_BCLK_RATE 1536000 -static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) { - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai; struct snd_soc_component *component; struct snd_soc_card *card = rtd->card; @@ -49,7 +53,7 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) int i, rval; u32 value; - switch (cpu_dai->id) { + switch (mi2s) { case MI2S_PRIMARY: writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11, pdata->spkr_iomux); @@ -128,6 +132,13 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + return apq8016_dai_init(rtd, cpu_dai->id); +} + static void apq8016_sbc_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -137,6 +148,113 @@ static void apq8016_sbc_add_ops(struct snd_soc_card *card) link->init = apq8016_sbc_dai_init; } +static int qdsp6_dai_get_lpass_id(struct snd_soc_dai *cpu_dai) +{ + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + return MI2S_PRIMARY; + case SECONDARY_MI2S_RX: + case SECONDARY_MI2S_TX: + return MI2S_SECONDARY; + case TERTIARY_MI2S_RX: + case TERTIARY_MI2S_TX: + return MI2S_TERTIARY; + case QUATERNARY_MI2S_RX: + case QUATERNARY_MI2S_TX: + return MI2S_QUATERNARY; + default: + return -EINVAL; + } +} + +static int msm8916_qdsp6_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + + snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS); + return apq8016_dai_init(rtd, qdsp6_dai_get_lpass_id(cpu_dai)); +} + +static int msm8916_qdsp6_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return mi2s; + + if (++data->mi2s_clk_count[mi2s] > 1) + return 0; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, MI2S_BCLK_RATE, 0); + if (ret) + dev_err(card->dev, "Failed to enable LPAIF bit clk: %d\n", ret); + return ret; +} + +static void msm8916_qdsp6_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct apq8016_sbc_data *data = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int mi2s, ret; + + mi2s = qdsp6_dai_get_lpass_id(cpu_dai); + if (mi2s < 0) + return; + + if (--data->mi2s_clk_count[mi2s] > 0) + return; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_BIT_CLK, 0, 0); + if (ret) + dev_err(card->dev, "Failed to disable LPAIF bit clk: %d\n", ret); +} + +static const struct snd_soc_ops msm8916_qdsp6_be_ops = { + .startup = msm8916_qdsp6_startup, + .shutdown = msm8916_qdsp6_shutdown, +}; + +static int msm8916_qdsp6_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static void msm8916_qdsp6_add_ops(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *link; + int i; + + /* Make it obvious to userspace that QDSP6 is used */ + card->components = "qdsp6"; + + for_each_card_prelinks(card, i, link) { + if (link->no_pcm) { + link->init = msm8916_qdsp6_dai_init; + link->ops = &msm8916_qdsp6_be_ops; + link->be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup; + } + } +} + static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { SND_SOC_DAPM_MIC("Handset Mic", NULL), @@ -148,11 +266,16 @@ static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { static int apq8016_sbc_platform_probe(struct platform_device *pdev) { + void (*add_ops)(struct snd_soc_card *card); struct device *dev = &pdev->dev; struct snd_soc_card *card; struct apq8016_sbc_data *data; int ret; + add_ops = device_get_match_data(&pdev->dev); + if (!add_ops) + return -EINVAL; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -177,12 +300,13 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, data); - apq8016_sbc_add_ops(card); + add_ops(card); return devm_snd_soc_register_card(&pdev->dev, card); } static const struct of_device_id apq8016_sbc_device_id[] __maybe_unused = { - { .compatible = "qcom,apq8016-sbc-sndcard" }, + { .compatible = "qcom,apq8016-sbc-sndcard", .data = apq8016_sbc_add_ops }, + { .compatible = "qcom,msm8916-qdsp6-sndcard", .data = msm8916_qdsp6_add_ops }, {}, }; MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id); diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c index 1a69baefc5..c7b7d0864d 100644 --- a/sound/soc/qcom/apq8096.c +++ b/sound/soc/qcom/apq8096.c @@ -60,7 +60,7 @@ static int msm_snd_hw_params(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_ops apq8096_ops = { +static const struct snd_soc_ops apq8096_ops = { .hw_params = msm_snd_hw_params, }; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 09af007007..c407684ce1 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -26,6 +26,12 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + if (of_property_read_bool(dev->of_node, "widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(card, "widgets"); + if (ret) + return ret; + } + /* DAPM routes */ if (of_property_read_bool(dev->of_node, "audio-routing")) { ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); @@ -39,12 +45,16 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + ret = snd_soc_of_parse_pin_switches(card, "pin-switches"); + if (ret) + return ret; + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); if (ret) return ret; /* Populate links */ - num_links = of_get_child_count(dev->of_node); + num_links = of_get_available_child_count(dev->of_node); /* Allocate the DAI link array */ card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL); @@ -54,7 +64,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) card->num_links = num_links; link = card->dai_link; - for_each_child_of_node(dev->of_node, np) { + for_each_available_child_of_node(dev->of_node, np) { dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL); if (!dlc) { ret = -ENOMEM; @@ -94,9 +104,8 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dai_name(cpu, &link->cpus->dai_name); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: error getting cpu dai name: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: error getting cpu dai name\n", link->name); goto err; } @@ -116,9 +125,8 @@ 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) { - if (ret != -EPROBE_DEFER) - dev_err(card->dev, "%s: codec dai not found: %d\n", - link->name, ret); + dev_err_probe(card->dev, ret, + "%s: codec dai not found\n", link->name); goto err; } diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 3c1dd9f32f..3963bf2346 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o +snd-q6dsp-common-objs := q6dsp-common.o q6dsp-lpass-ports.o q6dsp-lpass-clocks.o +snd-q6apm-objs := q6apm.o audioreach.o topology.o + +obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += snd-q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o @@ -8,3 +11,9 @@ obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o + +obj-$(CONFIG_SND_SOC_QDSP6_APM) += snd-q6apm.o +obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o +obj-$(CONFIG_SND_SOC_QDSP6_PRM) += q6prm.o +obj-$(CONFIG_SND_SOC_QDSP6_PRM_LPASS_CLOCKS) += q6prm-clocks.o diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 3d831b6355..72c5719f1d 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -390,7 +390,7 @@ struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate, int ret = 0; if (port_id < 0) { - dev_err(dev, "Invalid port_id 0x%x\n", port_id); + dev_err(dev, "Invalid port_id %d\n", port_id); return ERR_PTR(-EINVAL); } @@ -508,7 +508,7 @@ int q6adm_matrix_map(struct device *dev, int path, int port_idx = payload_map.port_id[i]; if (port_idx < 0) { - dev_err(dev, "Invalid port_id 0x%x\n", + dev_err(dev, "Invalid port_id %d\n", payload_map.port_id[i]); kfree(pkt); return -EINVAL; diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c index 9431656283..1ccab64ff0 100644 --- a/sound/soc/qcom/qdsp6/q6afe-clocks.c +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -7,115 +7,18 @@ #include #include #include -#include -#include +#include "q6dsp-lpass-clocks.h" #include "q6afe.h" #define Q6AFE_CLK(id) { \ .clk_id = id, \ - .afe_clk_id = Q6AFE_##id, \ + .q6dsp_clk_id = Q6AFE_##id, \ .name = #id, \ .rate = 19200000, \ } -#define Q6AFE_VOTE_CLK(id, blkid, n) { \ - .clk_id = id, \ - .afe_clk_id = blkid, \ - .name = n, \ - } -struct q6afe_clk_init { - int clk_id; - int afe_clk_id; - char *name; - int rate; -}; - -struct q6afe_clk { - struct device *dev; - int afe_clk_id; - int attributes; - int rate; - uint32_t handle; - struct clk_hw hw; -}; - -#define to_q6afe_clk(_hw) container_of(_hw, struct q6afe_clk, hw) - -struct q6afe_cc { - struct device *dev; - struct q6afe_clk *clks[Q6AFE_MAX_CLK_ID]; -}; - -static int clk_q6afe_prepare(struct clk_hw *hw) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - return q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, - Q6AFE_LPASS_CLK_ROOT_DEFAULT, clk->rate); -} - -static void clk_q6afe_unprepare(struct clk_hw *hw) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, - Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0); -} - -static int clk_q6afe_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - clk->rate = rate; - - return 0; -} - -static unsigned long clk_q6afe_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - return clk->rate; -} - -static long clk_q6afe_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - return rate; -} - -static const struct clk_ops clk_q6afe_ops = { - .prepare = clk_q6afe_prepare, - .unprepare = clk_q6afe_unprepare, - .set_rate = clk_q6afe_set_rate, - .round_rate = clk_q6afe_round_rate, - .recalc_rate = clk_q6afe_recalc_rate, -}; - -static int clk_vote_q6afe_block(struct clk_hw *hw) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - return q6afe_vote_lpass_core_hw(clk->dev, clk->afe_clk_id, - clk_hw_get_name(&clk->hw), &clk->handle); -} - -static void clk_unvote_q6afe_block(struct clk_hw *hw) -{ - struct q6afe_clk *clk = to_q6afe_clk(hw); - - q6afe_unvote_lpass_core_hw(clk->dev, clk->afe_clk_id, clk->handle); -} - -static const struct clk_ops clk_vote_q6afe_ops = { - .prepare = clk_vote_q6afe_block, - .unprepare = clk_unvote_q6afe_block, -}; - -static const struct q6afe_clk_init q6afe_clks[] = { +static const struct q6dsp_clk_init q6afe_clks[] = { Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT), Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT), Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT), @@ -176,88 +79,28 @@ static const struct q6afe_clk_init q6afe_clks[] = { Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_MCLK), Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK), Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK), - Q6AFE_VOTE_CLK(LPASS_HW_AVTIMER_VOTE, + Q6DSP_VOTE_CLK(LPASS_HW_AVTIMER_VOTE, Q6AFE_LPASS_CORE_AVTIMER_BLOCK, "LPASS_AVTIMER_MACRO"), - Q6AFE_VOTE_CLK(LPASS_HW_MACRO_VOTE, + Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6AFE_LPASS_CORE_HW_MACRO_BLOCK, "LPASS_HW_MACRO"), - Q6AFE_VOTE_CLK(LPASS_HW_DCODEC_VOTE, + Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK, "LPASS_HW_DCODEC"), }; -static struct clk_hw *q6afe_of_clk_hw_get(struct of_phandle_args *clkspec, - void *data) -{ - struct q6afe_cc *cc = data; - unsigned int idx = clkspec->args[0]; - unsigned int attr = clkspec->args[1]; - - if (idx >= Q6AFE_MAX_CLK_ID || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) { - dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr); - return ERR_PTR(-EINVAL); - } - - if (cc->clks[idx]) { - cc->clks[idx]->attributes = attr; - return &cc->clks[idx]->hw; - } - - return ERR_PTR(-ENOENT); -} - -static int q6afe_clock_dev_probe(struct platform_device *pdev) -{ - struct q6afe_cc *cc; - struct device *dev = &pdev->dev; - int i, ret; - - cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); - if (!cc) - return -ENOMEM; - - cc->dev = dev; - for (i = 0; i < ARRAY_SIZE(q6afe_clks); i++) { - unsigned int id = q6afe_clks[i].clk_id; - struct clk_init_data init = { - .name = q6afe_clks[i].name, - }; - struct q6afe_clk *clk; - - clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); - if (!clk) - return -ENOMEM; - - clk->dev = dev; - clk->afe_clk_id = q6afe_clks[i].afe_clk_id; - clk->rate = q6afe_clks[i].rate; - clk->hw.init = &init; - - if (clk->rate) - init.ops = &clk_q6afe_ops; - else - init.ops = &clk_vote_q6afe_ops; - - cc->clks[id] = clk; - - ret = devm_clk_hw_register(dev, &clk->hw); - if (ret) - return ret; - } - - ret = devm_of_clk_add_hw_provider(dev, q6afe_of_clk_hw_get, cc); - if (ret) - return ret; - - dev_set_drvdata(dev, cc); - - return 0; -} +static const struct q6dsp_clk_desc q6dsp_clk_q6afe __maybe_unused = { + .clks = q6afe_clks, + .num_clks = ARRAY_SIZE(q6afe_clks), + .lpass_set_clk = q6afe_set_lpass_clock, + .lpass_vote_clk = q6afe_vote_lpass_core_hw, + .lpass_unvote_clk = q6afe_unvote_lpass_core_hw, +}; #ifdef CONFIG_OF static const struct of_device_id q6afe_clock_device_id[] = { - { .compatible = "qcom,q6afe-clocks" }, + { .compatible = "qcom,q6afe-clocks", .data = &q6dsp_clk_q6afe }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_clock_device_id); @@ -268,7 +111,7 @@ static struct platform_driver q6afe_clock_platform_driver = { .name = "q6afe-clock", .of_match_table = of_match_ptr(q6afe_clock_device_id), }, - .probe = q6afe_clock_dev_probe, + .probe = q6dsp_clock_dev_probe, }; module_platform_driver(q6afe_clock_platform_driver); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index ac8f7324e9..8bb7452b8f 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -11,91 +11,9 @@ #include #include #include +#include "q6dsp-lpass-ports.h" #include "q6afe.h" -#define Q6AFE_TDM_PB_DAI(pre, num, did) { \ - .playback = { \ - .stream_name = pre" TDM"#num" Playback", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ - .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ - .channels_min = 1, \ - .channels_max = 8, \ - .rate_min = 8000, \ - .rate_max = 176400, \ - }, \ - .name = #did, \ - .ops = &q6tdm_ops, \ - .id = did, \ - .probe = msm_dai_q6_dai_probe, \ - .remove = msm_dai_q6_dai_remove, \ - } - -#define Q6AFE_TDM_CAP_DAI(pre, num, did) { \ - .capture = { \ - .stream_name = pre" TDM"#num" Capture", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ - .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ - .channels_min = 1, \ - .channels_max = 8, \ - .rate_min = 8000, \ - .rate_max = 176400, \ - }, \ - .name = #did, \ - .ops = &q6tdm_ops, \ - .id = did, \ - .probe = msm_dai_q6_dai_probe, \ - .remove = msm_dai_q6_dai_remove, \ - } - -#define Q6AFE_CDC_DMA_RX_DAI(did) { \ - .playback = { \ - .stream_name = #did" Playback", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ - .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ - .channels_min = 1, \ - .channels_max = 8, \ - .rate_min = 8000, \ - .rate_max = 176400, \ - }, \ - .name = #did, \ - .ops = &q6dma_ops, \ - .id = did, \ - .probe = msm_dai_q6_dai_probe, \ - .remove = msm_dai_q6_dai_remove, \ - } - -#define Q6AFE_CDC_DMA_TX_DAI(did) { \ - .capture = { \ - .stream_name = #did" Capture", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ - .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ - .channels_min = 1, \ - .channels_max = 8, \ - .rate_min = 8000, \ - .rate_max = 176400, \ - }, \ - .name = #did, \ - .ops = &q6dma_ops, \ - .id = did, \ - .probe = msm_dai_q6_dai_probe, \ - .remove = msm_dai_q6_dai_remove, \ - } struct q6afe_dai_priv_data { uint32_t sd_line_mask; @@ -784,591 +702,6 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) return 0; } -static struct snd_soc_dai_driver q6afe_dais[] = { - { - .playback = { - .stream_name = "HDMI Playback", - .rates = SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 2, - .channels_max = 8, - .rate_max = 192000, - .rate_min = 48000, - }, - .ops = &q6hdmi_ops, - .id = HDMI_RX, - .name = "HDMI", - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .name = "SLIMBUS_0_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_0_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .playback = { - .stream_name = "Slimbus Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .name = "SLIMBUS_0_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_0_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus1 Playback", - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 192000, - }, - .name = "SLIMBUS_1_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_1_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .name = "SLIMBUS_1_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_1_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus1 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus2 Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - .name = "SLIMBUS_2_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_2_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - - }, { - .name = "SLIMBUS_2_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_2_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus2 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus3 Playback", - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 192000, - }, - .name = "SLIMBUS_3_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_3_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - - }, { - .name = "SLIMBUS_3_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_3_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus3 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus4 Playback", - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 192000, - }, - .name = "SLIMBUS_4_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_4_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - - }, { - .name = "SLIMBUS_4_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_4_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus4 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus5 Playback", - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 192000, - }, - .name = "SLIMBUS_5_RX", - .ops = &q6slim_ops, - .id = SLIMBUS_5_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - - }, { - .name = "SLIMBUS_5_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_5_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus5 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Slimbus6 Playback", - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 2, - .rate_min = 8000, - .rate_max = 192000, - }, - .ops = &q6slim_ops, - .name = "SLIMBUS_6_RX", - .id = SLIMBUS_6_RX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - - }, { - .name = "SLIMBUS_6_TX", - .ops = &q6slim_ops, - .id = SLIMBUS_6_TX, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - .capture = { - .stream_name = "Slimbus6 Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - }, { - .playback = { - .stream_name = "Primary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = PRIMARY_MI2S_RX, - .name = "PRI_MI2S_RX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .capture = { - .stream_name = "Primary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = PRIMARY_MI2S_TX, - .name = "PRI_MI2S_TX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .playback = { - .stream_name = "Secondary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .name = "SEC_MI2S_RX", - .id = SECONDARY_MI2S_RX, - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .capture = { - .stream_name = "Secondary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = SECONDARY_MI2S_TX, - .name = "SEC_MI2S_TX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .playback = { - .stream_name = "Tertiary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .name = "TERT_MI2S_RX", - .id = TERTIARY_MI2S_RX, - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .capture = { - .stream_name = "Tertiary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = TERTIARY_MI2S_TX, - .name = "TERT_MI2S_TX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .playback = { - .stream_name = "Quaternary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .name = "QUAT_MI2S_RX", - .id = QUATERNARY_MI2S_RX, - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .capture = { - .stream_name = "Quaternary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = QUATERNARY_MI2S_TX, - .name = "QUAT_MI2S_TX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .playback = { - .stream_name = "Quinary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 192000, - }, - .id = QUINARY_MI2S_RX, - .name = "QUIN_MI2S_RX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, { - .capture = { - .stream_name = "Quinary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .channels_min = 1, - .channels_max = 8, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = QUINARY_MI2S_TX, - .name = "QUIN_MI2S_TX", - .ops = &q6i2s_ops, - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, - 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), - Q6AFE_TDM_PB_DAI("Primary", 3, PRIMARY_TDM_RX_3), - Q6AFE_TDM_PB_DAI("Primary", 4, PRIMARY_TDM_RX_4), - Q6AFE_TDM_PB_DAI("Primary", 5, PRIMARY_TDM_RX_5), - Q6AFE_TDM_PB_DAI("Primary", 6, PRIMARY_TDM_RX_6), - Q6AFE_TDM_PB_DAI("Primary", 7, PRIMARY_TDM_RX_7), - Q6AFE_TDM_CAP_DAI("Primary", 0, PRIMARY_TDM_TX_0), - Q6AFE_TDM_CAP_DAI("Primary", 1, PRIMARY_TDM_TX_1), - Q6AFE_TDM_CAP_DAI("Primary", 2, PRIMARY_TDM_TX_2), - Q6AFE_TDM_CAP_DAI("Primary", 3, PRIMARY_TDM_TX_3), - Q6AFE_TDM_CAP_DAI("Primary", 4, PRIMARY_TDM_TX_4), - Q6AFE_TDM_CAP_DAI("Primary", 5, PRIMARY_TDM_TX_5), - Q6AFE_TDM_CAP_DAI("Primary", 6, PRIMARY_TDM_TX_6), - Q6AFE_TDM_CAP_DAI("Primary", 7, PRIMARY_TDM_TX_7), - Q6AFE_TDM_PB_DAI("Secondary", 0, SECONDARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Secondary", 1, SECONDARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Secondary", 2, SECONDARY_TDM_RX_2), - Q6AFE_TDM_PB_DAI("Secondary", 3, SECONDARY_TDM_RX_3), - Q6AFE_TDM_PB_DAI("Secondary", 4, SECONDARY_TDM_RX_4), - Q6AFE_TDM_PB_DAI("Secondary", 5, SECONDARY_TDM_RX_5), - Q6AFE_TDM_PB_DAI("Secondary", 6, SECONDARY_TDM_RX_6), - Q6AFE_TDM_PB_DAI("Secondary", 7, SECONDARY_TDM_RX_7), - Q6AFE_TDM_CAP_DAI("Secondary", 0, SECONDARY_TDM_TX_0), - Q6AFE_TDM_CAP_DAI("Secondary", 1, SECONDARY_TDM_TX_1), - Q6AFE_TDM_CAP_DAI("Secondary", 2, SECONDARY_TDM_TX_2), - Q6AFE_TDM_CAP_DAI("Secondary", 3, SECONDARY_TDM_TX_3), - Q6AFE_TDM_CAP_DAI("Secondary", 4, SECONDARY_TDM_TX_4), - Q6AFE_TDM_CAP_DAI("Secondary", 5, SECONDARY_TDM_TX_5), - Q6AFE_TDM_CAP_DAI("Secondary", 6, SECONDARY_TDM_TX_6), - Q6AFE_TDM_CAP_DAI("Secondary", 7, SECONDARY_TDM_TX_7), - Q6AFE_TDM_PB_DAI("Tertiary", 0, TERTIARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Tertiary", 1, TERTIARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Tertiary", 2, TERTIARY_TDM_RX_2), - Q6AFE_TDM_PB_DAI("Tertiary", 3, TERTIARY_TDM_RX_3), - Q6AFE_TDM_PB_DAI("Tertiary", 4, TERTIARY_TDM_RX_4), - Q6AFE_TDM_PB_DAI("Tertiary", 5, TERTIARY_TDM_RX_5), - Q6AFE_TDM_PB_DAI("Tertiary", 6, TERTIARY_TDM_RX_6), - Q6AFE_TDM_PB_DAI("Tertiary", 7, TERTIARY_TDM_RX_7), - Q6AFE_TDM_CAP_DAI("Tertiary", 0, TERTIARY_TDM_TX_0), - Q6AFE_TDM_CAP_DAI("Tertiary", 1, TERTIARY_TDM_TX_1), - Q6AFE_TDM_CAP_DAI("Tertiary", 2, TERTIARY_TDM_TX_2), - Q6AFE_TDM_CAP_DAI("Tertiary", 3, TERTIARY_TDM_TX_3), - Q6AFE_TDM_CAP_DAI("Tertiary", 4, TERTIARY_TDM_TX_4), - Q6AFE_TDM_CAP_DAI("Tertiary", 5, TERTIARY_TDM_TX_5), - Q6AFE_TDM_CAP_DAI("Tertiary", 6, TERTIARY_TDM_TX_6), - Q6AFE_TDM_CAP_DAI("Tertiary", 7, TERTIARY_TDM_TX_7), - Q6AFE_TDM_PB_DAI("Quaternary", 0, QUATERNARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Quaternary", 1, QUATERNARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Quaternary", 2, QUATERNARY_TDM_RX_2), - Q6AFE_TDM_PB_DAI("Quaternary", 3, QUATERNARY_TDM_RX_3), - Q6AFE_TDM_PB_DAI("Quaternary", 4, QUATERNARY_TDM_RX_4), - Q6AFE_TDM_PB_DAI("Quaternary", 5, QUATERNARY_TDM_RX_5), - Q6AFE_TDM_PB_DAI("Quaternary", 6, QUATERNARY_TDM_RX_6), - Q6AFE_TDM_PB_DAI("Quaternary", 7, QUATERNARY_TDM_RX_7), - Q6AFE_TDM_CAP_DAI("Quaternary", 0, QUATERNARY_TDM_TX_0), - Q6AFE_TDM_CAP_DAI("Quaternary", 1, QUATERNARY_TDM_TX_1), - Q6AFE_TDM_CAP_DAI("Quaternary", 2, QUATERNARY_TDM_TX_2), - Q6AFE_TDM_CAP_DAI("Quaternary", 3, QUATERNARY_TDM_TX_3), - Q6AFE_TDM_CAP_DAI("Quaternary", 4, QUATERNARY_TDM_TX_4), - Q6AFE_TDM_CAP_DAI("Quaternary", 5, QUATERNARY_TDM_TX_5), - Q6AFE_TDM_CAP_DAI("Quaternary", 6, QUATERNARY_TDM_TX_6), - Q6AFE_TDM_CAP_DAI("Quaternary", 7, QUATERNARY_TDM_TX_7), - Q6AFE_TDM_PB_DAI("Quinary", 0, QUINARY_TDM_RX_0), - Q6AFE_TDM_PB_DAI("Quinary", 1, QUINARY_TDM_RX_1), - Q6AFE_TDM_PB_DAI("Quinary", 2, QUINARY_TDM_RX_2), - Q6AFE_TDM_PB_DAI("Quinary", 3, QUINARY_TDM_RX_3), - Q6AFE_TDM_PB_DAI("Quinary", 4, QUINARY_TDM_RX_4), - Q6AFE_TDM_PB_DAI("Quinary", 5, QUINARY_TDM_RX_5), - Q6AFE_TDM_PB_DAI("Quinary", 6, QUINARY_TDM_RX_6), - Q6AFE_TDM_PB_DAI("Quinary", 7, QUINARY_TDM_RX_7), - Q6AFE_TDM_CAP_DAI("Quinary", 0, QUINARY_TDM_TX_0), - Q6AFE_TDM_CAP_DAI("Quinary", 1, QUINARY_TDM_TX_1), - Q6AFE_TDM_CAP_DAI("Quinary", 2, QUINARY_TDM_TX_2), - Q6AFE_TDM_CAP_DAI("Quinary", 3, QUINARY_TDM_TX_3), - Q6AFE_TDM_CAP_DAI("Quinary", 4, QUINARY_TDM_TX_4), - Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5), - Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6), - Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7), - { - .playback = { - .stream_name = "Display Port Playback", - .rates = SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .channels_min = 2, - .channels_max = 8, - .rate_max = 192000, - .rate_min = 48000, - }, - .ops = &q6hdmi_ops, - .id = DISPLAY_PORT_RX, - .name = "DISPLAY_PORT", - .probe = msm_dai_q6_dai_probe, - .remove = msm_dai_q6_dai_remove, - }, - Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0), - Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0), - Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1), - Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1), - Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2), - Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0), - Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1), - Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5), - Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6), - Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7), -}; - -static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, - const struct of_phandle_args *args, - const char **dai_name) -{ - int id = args->args[0]; - int ret = -EINVAL; - int i; - - for (i = 0; i < ARRAY_SIZE(q6afe_dais); i++) { - if (q6afe_dais[i].id == id) { - *dai_name = q6afe_dais[i].name; - ret = 0; - break; - } - } - - return ret; -} - static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, SND_SOC_NOPM, 0, 0), @@ -1627,7 +960,7 @@ static const struct snd_soc_component_driver q6afe_dai_component = { .num_dapm_widgets = ARRAY_SIZE(q6afe_dai_widgets), .dapm_routes = q6afe_dapm_routes, .num_dapm_routes = ARRAY_SIZE(q6afe_dapm_routes), - .of_xlate_dai_name = q6afe_of_xlate_dai_name, + .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, }; @@ -1715,19 +1048,29 @@ static void of_q6afe_parse_dai_data(struct device *dev, static int q6afe_dai_dev_probe(struct platform_device *pdev) { + struct q6dsp_audio_port_dai_driver_config cfg; + struct snd_soc_dai_driver *dais; struct q6afe_dai_data *dai_data; struct device *dev = &pdev->dev; + int num_dais; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); - of_q6afe_parse_dai_data(dev, dai_data); - return devm_snd_soc_register_component(dev, &q6afe_dai_component, - q6afe_dais, ARRAY_SIZE(q6afe_dais)); + cfg.probe = msm_dai_q6_dai_probe; + cfg.remove = msm_dai_q6_dai_remove; + cfg.q6hdmi_ops = &q6hdmi_ops; + cfg.q6slim_ops = &q6slim_ops; + cfg.q6i2s_ops = &q6i2s_ops; + cfg.q6tdm_ops = &q6tdm_ops; + cfg.q6dma_ops = &q6dma_ops; + dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); + + return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais); } #ifdef CONFIG_OF @@ -1747,5 +1090,5 @@ static struct platform_driver q6afe_dai_platform_driver = { }; module_platform_driver(q6afe_dai_platform_driver); -MODULE_DESCRIPTION("Q6 Audio Fronend dai driver"); +MODULE_DESCRIPTION("Q6 Audio Frontend dai driver"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 18c90bb492..928fd23e2c 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -372,6 +372,12 @@ int q6routing_stream_open(int fedai_id, int perf_mode, } session = &routing_data->sessions[stream_id - 1]; + if (session->port_id < 0) { + dev_err(routing_data->dev, "Routing not setup for MultiMedia%d Session\n", + session->fedai_id); + return -EINVAL; + } + pdata = &routing_data->port_data[session->port_id]; mutex_lock(&routing_data->lock); diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index 768566bb57..37225ef256 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -17,6 +17,7 @@ #include #include "../codecs/rt5682.h" +#include "../codecs/rt5682s.h" #include "common.h" #include "lpass.h" @@ -128,7 +129,21 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int ret; + int pll_id, pll_source, pll_in, pll_out, clk_id, ret; + + if (!strcmp(codec_dai->name, "rt5682-aif1")) { + pll_source = RT5682_PLL1_S_MCLK; + pll_id = 0; + clk_id = RT5682_SCLK_S_PLL1; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } else if (!strcmp(codec_dai->name, "rt5682s-aif1")) { + pll_source = RT5682S_PLL_S_MCLK; + pll_id = RT5682S_PLL2; + clk_id = RT5682S_SCLK_S_PLL2; + pll_out = RT5682_PLL1_FREQ; + pll_in = DEFAULT_MCLK_RATE; + } switch (cpu_dai->id) { case MI2S_PRIMARY: @@ -145,16 +160,15 @@ static int sc7180_snd_startup(struct snd_pcm_substream *substream) SND_SOC_DAIFMT_I2S); /* Configure PLL1 for codec */ - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5682_PLL1_S_MCLK, - DEFAULT_MCLK_RATE, RT5682_PLL1_FREQ); + ret = snd_soc_dai_set_pll(codec_dai, pll_id, pll_source, + pll_in, pll_out); if (ret) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; } /* Configure sysclk for codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL1, - RT5682_PLL1_FREQ, + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, pll_out, SND_SOC_CLOCK_IN); if (ret) dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 0adfc57089..5c1d13eccb 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -33,6 +33,7 @@ struct sdm845_snd_data { struct snd_soc_jack jack; bool jack_setup; + bool slim_port_setup; bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; @@ -56,8 +57,8 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, int ret = 0, i; for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; @@ -224,6 +225,7 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *link = rtd->dai_link; struct snd_jack *jack; /* * Codec SLIMBUS configuration @@ -276,6 +278,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) } break; case SLIMBUS_0_RX...SLIMBUS_6_TX: + /* setting up wcd multiple times for slim port is redundant */ + if (pdata->slim_port_setup || !link->no_pcm) + return 0; + for_each_rtd_codec_dais(rtd, i, codec_dai) { rval = snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch), @@ -295,8 +301,10 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) dev_warn(card->dev, "Failed to set jack: %d\n", rval); return rval; } - } + + pdata->slim_port_setup = true; + break; default: break; diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c index fe8fd7367e..114a29e01c 100644 --- a/sound/soc/qcom/sm8250.c +++ b/sound/soc/qcom/sm8250.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "qdsp6/q6afe.h" #include "common.h" @@ -18,8 +20,66 @@ struct sm8250_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; + struct snd_soc_jack jack; + bool jack_setup; }; +static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_card *card = rtd->card; + int rval, i; + + if (!data->jack_setup) { + struct snd_jack *jack; + + rval = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &data->jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add Headphone Jack\n"); + return rval; + } + + jack = data->jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + data->jack_setup = true; + } + + switch (cpu_dai->id) { + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: + for_each_rtd_codec_dais(rtd, i, codec_dai) { + rval = snd_soc_component_set_jack(codec_dai->component, + &data->jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + + break; + default: + break; + } + + + return 0; +} + static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -69,9 +129,15 @@ static int sm8250_snd_hw_params(struct snd_pcm_substream *substream, switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: for_each_rtd_codec_dais(rtd, i, codec_dai) { - sruntime = snd_soc_dai_get_sdw_stream(codec_dai, - substream->stream); + sruntime = snd_soc_dai_get_stream(codec_dai, + substream->stream); if (sruntime != ERR_PTR(-ENOTSUPP)) pdata->sruntime[cpu_dai->id] = sruntime; } @@ -129,6 +195,12 @@ static int sm8250_snd_prepare(struct snd_pcm_substream *substream) switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: return sm8250_snd_wsa_dma_prepare(substream); default: break; @@ -147,6 +219,12 @@ static int sm8250_snd_hw_free(struct snd_pcm_substream *substream) switch (cpu_dai->id) { case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: if (sruntime && data->stream_prepared[cpu_dai->id]) { sdw_disable_stream(sruntime); sdw_deprepare_stream(sruntime); @@ -174,6 +252,7 @@ static void sm8250_add_be_ops(struct snd_soc_card *card) for_each_card_prelinks(card, i, link) { if (link->no_pcm == 1) { + link->init = sm8250_snd_init; link->be_hw_params_fixup = sm8250_be_hw_params_fixup; link->ops = &sm8250_be_ops; } diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 053097b73e..42f76bc0fb 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -16,6 +16,17 @@ config SND_SOC_ROCKCHIP_I2S Rockchip I2S device. The device supports upto maximum of 8 channels each for play and record. +config SND_SOC_ROCKCHIP_I2S_TDM + tristate "Rockchip I2S/TDM Device Driver" + depends on HAVE_CLK && SND_SOC_ROCKCHIP + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the I2S/TDM driver for + Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices + interface between the AHB bus and the I2S bus, and support up to a + maximum of 8 channels each for playback and recording. + + config SND_SOC_ROCKCHIP_PDM tristate "Rockchip PDM Controller Driver" depends on HAVE_CLK && SND_SOC_ROCKCHIP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 65e814d460..30c57c0d76 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -1,13 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 # ROCKCHIP Platform Support snd-soc-rockchip-i2s-objs := rockchip_i2s.o -snd-soc-rockchip-pcm-objs := rockchip_pcm.o +snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o -obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c index 33a0077474..b052642ea6 100644 --- a/sound/soc/rockchip/rk3288_hdmi_analog.c +++ b/sound/soc/rockchip/rk3288_hdmi_analog.c @@ -249,13 +249,9 @@ static int snd_rk_mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, machine); ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (ret) { - dev_err(&pdev->dev, - "Soc register card failed %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Soc register card failed\n"); return ret; } diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 7e89f5b0c2..a6d7656c20 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -20,7 +20,6 @@ #include #include "rockchip_i2s.h" -#include "rockchip_pcm.h" #define DRV_NAME "rockchip-i2s" @@ -756,7 +755,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev) goto err_suspend; } - ret = rockchip_pcm_platform_register(&pdev->dev); + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM\n"); goto err_suspend; diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 38bd603eeb..64d9891b64 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -20,10 +20,12 @@ #define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */ #define PDM_SIGNOFF_CLK_RATE (100000000) +#define PDM_PATH_MAX (4) enum rk_pdm_version { RK_PDM_RK3229, RK_PDM_RK3308, + RK_PDM_RV1126, }; struct rk_pdm_dev { @@ -121,6 +123,55 @@ static unsigned int get_pdm_ds_ratio(unsigned int sr) return ratio; } +static unsigned int get_pdm_cic_ratio(unsigned int clk) +{ + switch (clk) { + case 4096000: + case 5644800: + case 6144000: + return 0; + case 2048000: + case 2822400: + case 3072000: + return 1; + case 1024000: + case 1411200: + case 1536000: + return 2; + default: + return 1; + } +} + +static unsigned int samplerate_to_bit(unsigned int samplerate) +{ + switch (samplerate) { + case 8000: + case 11025: + case 12000: + return 0; + case 16000: + case 22050: + case 24000: + return 1; + case 32000: + return 2; + case 44100: + case 48000: + return 3; + case 64000: + case 88200: + case 96000: + return 4; + case 128000: + case 176400: + case 192000: + return 5; + default: + return 1; + } +} + static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai) { return snd_soc_dai_get_drvdata(dai); @@ -166,7 +217,8 @@ static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream, if (ret) return -EINVAL; - if (pdm->version == RK_PDM_RK3308) { + if (pdm->version == RK_PDM_RK3308 || + pdm->version == RK_PDM_RV1126) { rational_best_approximation(clk_out, clk_src, GENMASK(16 - 1, 0), GENMASK(16 - 1, 0), @@ -194,8 +246,18 @@ static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream, PDM_CLK_FD_RATIO_MSK, val); } - val = get_pdm_ds_ratio(samplerate); - regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); + + if (pdm->version == RK_PDM_RV1126) { + val = get_pdm_cic_ratio(clk_out); + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val); + val = samplerate_to_bit(samplerate); + regmap_update_bits(pdm->regmap, PDM_CTRL0, + PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val)); + } else { + val = get_pdm_ds_ratio(samplerate); + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val); + } + regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, PDM_HPF_CF_MSK, PDM_HPF_60HZ); regmap_update_bits(pdm->regmap, PDM_HPF_CTRL, @@ -441,9 +503,10 @@ static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg) } static const struct reg_default rockchip_pdm_reg_defaults[] = { - {0x04, 0x78000017}, - {0x08, 0x0bb8ea60}, - {0x18, 0x0000001f}, + { PDM_CTRL0, 0x78000017 }, + { PDM_CTRL1, 0x0bb8ea60 }, + { PDM_CLK_CTRL, 0x0000e401 }, + { PDM_DMA_CTRL, 0x0000001f }, }; static const struct regmap_config rockchip_pdm_regmap_config = { @@ -469,12 +532,44 @@ static const struct of_device_id rockchip_pdm_match[] __maybe_unused = { .data = (void *)RK_PDM_RK3308 }, { .compatible = "rockchip,rk3308-pdm", .data = (void *)RK_PDM_RK3308 }, + { .compatible = "rockchip,rk3568-pdm", + .data = (void *)RK_PDM_RV1126 }, + { .compatible = "rockchip,rv1126-pdm", + .data = (void *)RK_PDM_RV1126 }, {}, }; MODULE_DEVICE_TABLE(of, rockchip_pdm_match); +static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node) +{ + unsigned int path[PDM_PATH_MAX]; + int cnt = 0, ret = 0, i = 0, val = 0, msk = 0; + + cnt = of_count_phandle_with_args(node, "rockchip,path-map", + NULL); + if (cnt != PDM_PATH_MAX) + return cnt; + + ret = of_property_read_u32_array(node, "rockchip,path-map", + path, cnt); + if (ret) + return ret; + + for (i = 0; i < cnt; i++) { + if (path[i] >= PDM_PATH_MAX) + return -EINVAL; + msk |= PDM_PATH_MASK(i); + val |= PDM_PATH(i, path[i]); + } + + regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val); + + return 0; +} + static int rockchip_pdm_probe(struct platform_device *pdev) { + struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; struct rk_pdm_dev *pdm; struct resource *res; @@ -540,6 +635,11 @@ static int rockchip_pdm_probe(struct platform_device *pdev) } rockchip_pdm_rxctrl(pdm, 0); + + ret = rockchip_pdm_path_parse(pdm, node); + if (ret != 0 && ret != -ENOENT) + 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); diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h index 8e5bbafef7..cab977272e 100644 --- a/sound/soc/rockchip/rockchip_pdm.h +++ b/sound/soc/rockchip/rockchip_pdm.h @@ -41,6 +41,8 @@ #define PDM_PATH1_EN BIT(28) #define PDM_PATH0_EN BIT(27) #define PDM_HWT_EN BIT(26) +#define PDM_SAMPLERATE_MSK GENMASK(7, 5) +#define PDM_SAMPLERATE(x) ((x) << 5) #define PDM_VDW_MSK (0x1f << 0) #define PDM_VDW(X) ((X - 1) << 0) @@ -51,6 +53,9 @@ #define PDM_FD_DENOMINATOR_MSK GENMASK(15, 0) /* PDM CLK CTRL */ +#define PDM_PATH_SHIFT(x) (8 + (x) * 2) +#define PDM_PATH_MASK(x) (0x3 << PDM_PATH_SHIFT(x)) +#define PDM_PATH(x, v) ((v) << PDM_PATH_SHIFT(x)) #define PDM_CLK_FD_RATIO_MSK BIT(6) #define PDM_CLK_FD_RATIO_40 (0X0 << 6) #define PDM_CLK_FD_RATIO_35 BIT(6) @@ -66,6 +71,7 @@ #define PDM_CLK_1280FS (0x2 << 0) #define PDM_CLK_2560FS (0x3 << 0) #define PDM_CLK_5120FS (0x4 << 0) +#define PDM_CIC_RATIO_MSK (0x3 << 0) /* PDM HPF CTRL */ #define PDM_HPF_LE BIT(3) diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 313ab650f8..5265e546b1 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -585,19 +585,16 @@ static int aries_audio_probe(struct platform_device *pdev) extcon_np = of_parse_phandle(np, "extcon", 0); priv->usb_extcon = extcon_find_edev_by_node(extcon_np); - if (IS_ERR(priv->usb_extcon)) { - if (PTR_ERR(priv->usb_extcon) != -EPROBE_DEFER) - dev_err(dev, "Failed to get extcon device"); - return PTR_ERR(priv->usb_extcon); - } + if (IS_ERR(priv->usb_extcon)) + return dev_err_probe(dev, PTR_ERR(priv->usb_extcon), + "Failed to get extcon device"); of_node_put(extcon_np); priv->adc = devm_iio_channel_get(dev, "headset-detect"); - if (IS_ERR(priv->adc)) { - if (PTR_ERR(priv->adc) != -EPROBE_DEFER) - dev_err(dev, "Failed to get ADC channel"); - return PTR_ERR(priv->adc); - } + if (IS_ERR(priv->adc)) + return dev_err_probe(dev, PTR_ERR(priv->adc), + "Failed to get ADC channel"); + if (priv->adc->channel->type != IIO_VOLTAGE) return -EINVAL; diff --git a/sound/soc/samsung/arndale.c b/sound/soc/samsung/arndale.c index 606ac5e33a..a5dc640d0d 100644 --- a/sound/soc/samsung/arndale.c +++ b/sound/soc/samsung/arndale.c @@ -174,9 +174,8 @@ static int arndale_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(card->dev, card); if (ret) { - if (ret != -EPROBE_DEFER) - 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_of_nodes; } return 0; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 390f2dd735..34067cc314 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -325,9 +325,8 @@ static int littlemill_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 998d10cf8c..7b12ccd2a9 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -183,9 +183,8 @@ static int lowland_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index ca643a488c..4ff12e2e70 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -311,9 +311,7 @@ static int odroid_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "snd_soc_register_card() failed: %d\n", - ret); + dev_err_probe(dev, ret, "snd_soc_register_card() failed\n"); goto err_put_clk_i2s; } diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index e9481187a0..de66cc422e 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -397,6 +397,8 @@ static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd, /* clear again, just in case */ writel(0x0, i2s->regs + S3C2412_IISFIC); + fallthrough; + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!i2s->master) { diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 7661b63794..821ad1eb1b 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -179,8 +179,8 @@ static int smdk_audio_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 029448f5be..d77dc54cae 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -118,8 +118,8 @@ static int snd_smdk_probe(struct platform_device *pdev) smdk_pcm.dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, &smdk_pcm); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index 6da674e901..02372109c2 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -212,12 +212,9 @@ static int snow_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(dev, card); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "snd_soc_register_card failed (%d)\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "snd_soc_register_card failed\n"); return ret; } diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index f5f6ba00d0..37b1f4f60b 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -330,9 +330,8 @@ static int speyside_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index a2c77e6def..d611ec9e53 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -612,8 +612,7 @@ static int tm2_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to register card: %d\n", ret); + dev_err_probe(dev, ret, "Failed to register card\n"); goto dai_node_put; } diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 15223d860c..8d3149a47a 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -229,9 +229,8 @@ static int tobermory_probe(struct platform_device *pdev) card->dev = &pdev->dev; ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret && ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", - ret); + if (ret) + dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); return ret; } diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index fa0cc08f70..e8d98b362f 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -1020,7 +1020,12 @@ static int rz_ssi_probe(struct platform_device *pdev) reset_control_deassert(ssi->rstc); pm_runtime_enable(&pdev->dev); - pm_runtime_resume_and_get(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) { + pm_runtime_disable(ssi->dev); + reset_control_assert(ssi->rstc); + return dev_err_probe(ssi->dev, ret, "pm_runtime_resume_and_get failed\n"); + } spin_lock_init(&ssi->lock); dev_set_drvdata(&pdev->dev, ssi); diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 395229bf5c..142476f139 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -8,14 +8,36 @@ #include #include +static bool snd_soc_acpi_id_present(struct snd_soc_acpi_mach *machine) +{ + const struct snd_soc_acpi_codecs *comp_ids = machine->comp_ids; + int i; + + if (machine->id[0]) { + if (acpi_dev_present(machine->id, NULL, -1)) + return true; + } + + if (comp_ids) { + for (i = 0; i < comp_ids->num_codecs; i++) { + if (acpi_dev_present(comp_ids->codecs[i], NULL, -1)) { + strscpy(machine->id, comp_ids->codecs[i], ACPI_ID_LEN); + return true; + } + } + } + + return false; +} + struct snd_soc_acpi_mach * snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) { struct snd_soc_acpi_mach *mach; struct snd_soc_acpi_mach *mach_alt; - for (mach = machines; mach->id[0]; mach++) { - if (acpi_dev_present(mach->id, NULL, -1)) { + for (mach = machines; mach->id[0] || mach->comp_ids; mach++) { + if (snd_soc_acpi_id_present(mach)) { if (mach->machine_quirk) { mach_alt = mach->machine_quirk(mach); if (!mach_alt) @@ -33,16 +55,13 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine); static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level, void *context, void **ret) { - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); acpi_status status; struct snd_soc_acpi_package_context *pkg_ctx = context; pkg_ctx->data_valid = false; - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (adev->status.present && adev->status.functional) { + if (adev && adev->status.present && adev->status.functional) { struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *myobj = NULL; diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 8e8d917d22..c0664f9499 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -13,9 +13,10 @@ #include #include -#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret) +#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret, -1) +#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret(dai, __func__, ret, reg) static inline int _soc_component_ret(struct snd_soc_component *component, - const char *func, int ret) + const char *func, int ret, int reg) { /* Positive/Zero values are not errors */ if (ret >= 0) @@ -27,9 +28,14 @@ static inline int _soc_component_ret(struct snd_soc_component *component, case -ENOTSUPP: break; default: - dev_err(component->dev, - "ASoC: error at %s on %s: %d\n", - func, component->name, ret); + if (reg == -1) + dev_err(component->dev, + "ASoC: error at %s on %s: %d\n", + func, component->name, ret); + else + dev_err(component->dev, + "ASoC: error at %s on %s for register: [0x%08x] %d\n", + func, component->name, reg, ret); } return ret; @@ -251,8 +257,7 @@ int snd_soc_component_set_jack(struct snd_soc_component *component, EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); int snd_soc_component_module_get(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open) + void *mark, int upon_open) { int ret = 0; @@ -260,25 +265,24 @@ int snd_soc_component_module_get(struct snd_soc_component *component, !try_module_get(component->dev->driver->owner)) ret = -ENODEV; - /* mark substream if succeeded */ + /* mark module if succeeded */ if (ret == 0) - soc_component_mark_push(component, substream, module); + soc_component_mark_push(component, mark, module); return soc_component_ret(component, ret); } void snd_soc_component_module_put(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int upon_open, int rollback) + void *mark, int upon_open, int rollback) { - if (rollback && !soc_component_mark_match(component, substream, module)) + if (rollback && !soc_component_mark_match(component, mark, module)) return; if (component->driver->module_get_upon_open == !!upon_open) module_put(component->dev->driver->owner); - /* remove marked substream */ - soc_component_mark_pop(component, substream, module); + /* remove the mark from module */ + soc_component_mark_pop(component, mark, module); } int snd_soc_component_open(struct snd_soc_component *component, @@ -425,43 +429,36 @@ EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); #endif -int snd_soc_component_compr_open(struct snd_compr_stream *cstream) +int snd_soc_component_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; - int i, ret; + int ret = 0; - for_each_rtd_components(rtd, i, component) { - if (component->driver->compress_ops && - component->driver->compress_ops->open) { - ret = component->driver->compress_ops->open(component, cstream); - if (ret < 0) - return soc_component_ret(component, ret); - } + if (component->driver->compress_ops && + component->driver->compress_ops->open) + ret = component->driver->compress_ops->open(component, cstream); + + /* mark substream if succeeded */ + if (ret == 0) soc_component_mark_push(component, cstream, compr_open); - } - return 0; + return soc_component_ret(component, ret); } EXPORT_SYMBOL_GPL(snd_soc_component_compr_open); -void snd_soc_component_compr_free(struct snd_compr_stream *cstream, +void snd_soc_component_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int rollback) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; - int i; + if (rollback && !soc_component_mark_match(component, cstream, compr_open)) + return; - for_each_rtd_components(rtd, i, component) { - if (rollback && !soc_component_mark_match(component, cstream, compr_open)) - continue; + if (component->driver->compress_ops && + component->driver->compress_ops->free) + component->driver->compress_ops->free(component, cstream); - if (component->driver->compress_ops && - component->driver->compress_ops->free) - component->driver->compress_ops->free(component, cstream); - - soc_component_mark_pop(component, cstream, compr_open); - } + /* remove marked substream */ + soc_component_mark_pop(component, cstream, compr_open); } EXPORT_SYMBOL_GPL(snd_soc_component_compr_free); @@ -696,7 +693,7 @@ static unsigned int soc_component_read_no_lock( ret = -EIO; if (ret < 0) - return soc_component_ret(component, ret); + return soc_component_ret_reg_rw(component, ret, reg); return val; } @@ -732,7 +729,7 @@ static int soc_component_write_no_lock( else if (component->driver->write) ret = component->driver->write(component, reg, val); - return soc_component_ret(component, ret); + return soc_component_ret_reg_rw(component, ret, reg); } /** @@ -774,7 +771,7 @@ static int snd_soc_component_update_bits_legacy( mutex_unlock(&component->io_mutex); - return soc_component_ret(component, ret); + return soc_component_ret_reg_rw(component, ret, reg); } /** @@ -802,7 +799,7 @@ int snd_soc_component_update_bits(struct snd_soc_component *component, mask, val, &change); if (ret < 0) - return soc_component_ret(component, ret); + return soc_component_ret_reg_rw(component, ret, reg); return change; } EXPORT_SYMBOL_GPL(snd_soc_component_update_bits); @@ -838,7 +835,7 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component, mask, val, &change); if (ret < 0) - return soc_component_ret(component, ret); + return soc_component_ret_reg_rw(component, ret, reg); return change; } EXPORT_SYMBOL_GPL(snd_soc_component_update_bits_async); @@ -935,6 +932,34 @@ int snd_soc_pcm_component_pointer(struct snd_pcm_substream *substream) return 0; } +void snd_soc_pcm_component_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *component; + snd_pcm_sframes_t delay; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the Components doing transmit and the maximum of the + * Components doing receive (ie, all CPUs and all CODECs) rather than + * just the maximum of all Components. + */ + for_each_rtd_components(rtd, i, component) { + if (!component->driver->delay) + continue; + + delay = component->driver->delay(component, substream); + + if (snd_soc_component_is_codec(component)) + *codec_delay = max(*codec_delay, delay); + else + *cpu_delay = max(*cpu_delay, delay); + } +} + int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 36060800e9..8e2494a9f3 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -22,6 +22,39 @@ #include #include +static int snd_soc_compr_components_open(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + int ret = 0; + int i; + + for_each_rtd_components(rtd, i, component) { + ret = snd_soc_component_module_get_when_open(component, cstream); + if (ret < 0) + break; + + ret = snd_soc_component_compr_open(component, cstream); + if (ret < 0) + break; + } + + return ret; +} + +static void snd_soc_compr_components_free(struct snd_compr_stream *cstream, + int rollback) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_component *component; + int i; + + for_each_rtd_components(rtd, i, component) { + snd_soc_component_compr_free(component, cstream, rollback); + snd_soc_component_module_put_when_close(component, cstream, rollback); + } +} + static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -44,7 +77,7 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback) snd_soc_link_compr_shutdown(cstream, rollback); - snd_soc_component_compr_free(cstream, rollback); + snd_soc_compr_components_free(cstream, rollback); snd_soc_dai_compr_shutdown(cpu_dai, cstream, rollback); @@ -80,7 +113,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) if (ret < 0) goto err; - ret = snd_soc_component_compr_open(cstream); + ret = snd_soc_compr_components_open(cstream); if (ret < 0) goto err; @@ -137,7 +170,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) if (ret < 0) goto out; - ret = snd_soc_component_compr_open(cstream); + ret = snd_soc_compr_components_open(cstream); if (ret < 0) goto open_err; @@ -160,7 +193,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) return 0; machine_err: - snd_soc_component_compr_free(cstream, 1); + snd_soc_compr_components_free(cstream, 1); open_err: snd_soc_dai_compr_shutdown(cpu_dai, cstream, 1); out: @@ -205,7 +238,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) snd_soc_link_compr_shutdown(cstream, 0); - snd_soc_component_compr_free(cstream, 0); + snd_soc_compr_components_free(cstream, 0); snd_soc_dai_compr_shutdown(cpu_dai, cstream, 0); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b84f461160..9735026aa5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -229,31 +229,12 @@ static void snd_soc_debugfs_exit(void) #else -static inline void soc_init_component_debugfs( - struct snd_soc_component *component) -{ -} - -static inline void soc_cleanup_component_debugfs( - struct snd_soc_component *component) -{ -} - -static inline void soc_init_card_debugfs(struct snd_soc_card *card) -{ -} - -static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) -{ -} - -static inline void snd_soc_debugfs_init(void) -{ -} - -static inline void snd_soc_debugfs_exit(void) -{ -} +static inline void soc_init_component_debugfs(struct snd_soc_component *component) { } +static inline void soc_cleanup_component_debugfs(struct snd_soc_component *component) { } +static inline void soc_init_card_debugfs(struct snd_soc_card *card) { } +static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { } +static inline void snd_soc_debugfs_init(void) { } +static inline void snd_soc_debugfs_exit(void) { } #endif @@ -739,9 +720,7 @@ static void soc_resume_init(struct snd_soc_card *card) #else #define snd_soc_suspend NULL #define snd_soc_resume NULL -static inline void soc_resume_init(struct snd_soc_card *card) -{ -} +static inline void soc_resume_init(struct snd_soc_card *card) { } #endif static struct device_node @@ -1239,6 +1218,9 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int i; int ret; + if (!dai_fmt) + return 0; + for_each_rtd_codec_dais(rtd, i, codec_dai) { unsigned int codec_dai_fmt = dai_fmt; @@ -1255,7 +1237,6 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, /* * Flip the polarity for the "CPU" end of a CODEC<->CODEC link - * the component which has non_legacy_dai_naming is Codec */ inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1275,7 +1256,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, for_each_rtd_cpu_dais(rtd, i, cpu_dai) { unsigned int fmt = dai_fmt; - if (cpu_dai->component->driver->non_legacy_dai_naming) + if (snd_soc_component_is_codec(cpu_dai->component)) fmt = inv_dai_fmt; ret = snd_soc_dai_set_fmt(cpu_dai, fmt); @@ -1304,11 +1285,9 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, return ret; snd_soc_runtime_get_dai_fmt(rtd); - if (dai_link->dai_fmt) { - ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); - if (ret) - return ret; - } + ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); + if (ret) + return ret; /* add DPCM sysfs entries */ soc_dpcm_debugfs_add(rtd); @@ -1384,9 +1363,6 @@ static void soc_remove_component(struct snd_soc_component *component, if (probed) snd_soc_component_remove(component); - /* For framework level robustness */ - snd_soc_component_set_jack(component, NULL, NULL); - list_del_init(&component->card_list); snd_soc_dapm_free(snd_soc_component_get_dapm(component)); soc_cleanup_component_debugfs(component); @@ -2360,7 +2336,6 @@ int snd_soc_register_card(struct snd_soc_card *card) mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); - spin_lock_init(&card->dpcm_lock); return snd_soc_bind_card(card); } @@ -2542,7 +2517,7 @@ static int snd_soc_register_dais(struct snd_soc_component *component, for (i = 0; i < count; i++) { dai = snd_soc_register_dai(component, dai_drv + i, count == 1 && - !component->driver->non_legacy_dai_naming); + !snd_soc_component_is_codec(component)); if (dai == NULL) { ret = -ENOMEM; goto err; @@ -2869,6 +2844,56 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); +int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) +{ + const unsigned int nb_controls_max = 16; + const char **strings, *control_name; + struct snd_kcontrol_new *controls; + struct device *dev = card->dev; + unsigned int i, nb_controls; + int ret; + + if (!of_property_read_bool(dev->of_node, prop)) + return 0; + + strings = devm_kcalloc(dev, nb_controls_max, + sizeof(*strings), GFP_KERNEL); + if (!strings) + return -ENOMEM; + + ret = of_property_read_string_array(dev->of_node, prop, + strings, nb_controls_max); + if (ret < 0) + return ret; + + nb_controls = (unsigned int)ret; + + controls = devm_kcalloc(dev, nb_controls, + sizeof(*controls), GFP_KERNEL); + if (!controls) + return -ENOMEM; + + for (i = 0; i < nb_controls; i++) { + control_name = devm_kasprintf(dev, GFP_KERNEL, + "%s Switch", strings[i]); + if (!control_name) + return -ENOMEM; + + controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + controls[i].name = control_name; + controls[i].info = snd_soc_dapm_info_pin_switch; + controls[i].get = snd_soc_dapm_get_pin_switch; + controls[i].put = snd_soc_dapm_put_pin_switch; + controls[i].private_value = (unsigned long)strings[i]; + } + + card->controls = controls; + card->num_controls = nb_controls; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_pin_switches); + int snd_soc_of_get_slot_mask(struct device_node *np, const char *prop_name, unsigned int *mask) diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 3db0fcf243..6078afe335 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -453,18 +453,6 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai, soc_dai_mark_pop(dai, substream, startup); } -snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) -{ - int delay = 0; - - if (dai->driver->ops && - dai->driver->ops->delay) - delay = dai->driver->ops->delay(substream, dai); - - return delay; -} - int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num) { @@ -693,6 +681,34 @@ int snd_soc_pcm_dai_bespoke_trigger(struct snd_pcm_substream *substream, return 0; } +void snd_soc_pcm_dai_delay(struct snd_pcm_substream *substream, + snd_pcm_sframes_t *cpu_delay, + snd_pcm_sframes_t *codec_delay) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + int i; + + /* + * We're looking for the delay through the full audio path so it needs to + * be the maximum of the DAIs doing transmit and the maximum of the DAIs + * doing receive (ie, all CPUs and all CODECs) rather than just the maximum + * of all DAIs. + */ + + /* for CPU */ + for_each_rtd_cpu_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *cpu_delay = max(*cpu_delay, dai->driver->ops->delay(substream, dai)); + + /* for Codec */ + for_each_rtd_codec_dais(rtd, i, dai) + if (dai->driver->ops && + dai->driver->ops->delay) + *codec_delay = max(*codec_delay, dai->driver->ops->delay(substream, dai)); +} + int snd_soc_dai_compr_startup(struct snd_soc_dai *dai, struct snd_compr_stream *cstream) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 0479bb0005..b06c568244 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1331,11 +1331,13 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, return paths; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets); void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list) { dapm_widget_list_free(list); } +EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets); /* * Handler for regulator supply widget. diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index 4aa48c74f2..c54c8ca8d7 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -15,6 +15,10 @@ #include +static unsigned int prealloc_buffer_size_kbytes = 512; +module_param(prealloc_buffer_size_kbytes, uint, 0444); +MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB)."); + /* * The platforms dmaengine driver does not support reporting the amount of * bytes that are still left to transfer. @@ -237,7 +241,7 @@ static int dmaengine_pcm_new(struct snd_soc_component *component, prealloc_buffer_size = config->prealloc_buffer_size; max_buffer_size = config->pcm_hardware->buffer_bytes_max; } else { - prealloc_buffer_size = 512 * 1024; + prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; max_buffer_size = SIZE_MAX; } diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index ee3782ecd7..a0ca58ba16 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -316,26 +316,26 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, if (sign_bit) mask = BIT(sign_bit + 1) - 1; + if (ucontrol->value.integer.value[0] < 0) + return -EINVAL; val = ucontrol->value.integer.value[0]; if (mc->platform_max && ((int)val + min) > mc->platform_max) return -EINVAL; if (val > max - min) return -EINVAL; - if (val < 0) - return -EINVAL; val = (val + min) & mask; if (invert) val = max - val; val_mask = mask << shift; val = val << shift; if (snd_soc_volsw_is_stereo(mc)) { + if (ucontrol->value.integer.value[1] < 0) + return -EINVAL; val2 = ucontrol->value.integer.value[1]; if (mc->platform_max && ((int)val2 + min) > mc->platform_max) return -EINVAL; if (val2 > max - min) return -EINVAL; - if (val2 < 0) - return -EINVAL; val2 = (val2 + min) & mask; if (invert) val2 = max - val2; @@ -430,13 +430,13 @@ int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol, int ret; unsigned int val, val_mask; + if (ucontrol->value.integer.value[0] < 0) + return -EINVAL; val = ucontrol->value.integer.value[0]; if (mc->platform_max && val > mc->platform_max) return -EINVAL; if (val > max - min) return -EINVAL; - if (val < 0) - return -EINVAL; val_mask = mask << shift; val = (val + min) & mask; val = val << shift; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 48f71bb81a..9a954680d4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -27,6 +27,37 @@ #include #include +static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); +} + +static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd) +{ + mutex_unlock(&rtd->card->pcm_mutex); +} + +#define snd_soc_dpcm_mutex_assert_held(rtd) \ + lockdep_assert_held(&(rtd)->card->pcm_mutex) + +static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + +#define snd_soc_dpcm_stream_lock_irqsave_nested(rtd, stream, flags) \ + snd_pcm_stream_lock_irqsave_nested(snd_soc_dpcm_get_substream(rtd, stream), flags) + +static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, + int stream) +{ + snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); +} + +#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \ + snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags) + #define DPCM_MAX_BE_USERS 8 static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd) @@ -73,7 +104,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; struct snd_soc_dpcm *dpcm; ssize_t offset = 0; - unsigned long flags; /* FE state */ offset += scnprintf(buf + offset, size - offset, @@ -101,7 +131,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, goto out; } - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; params = &dpcm->hw_params; @@ -122,7 +151,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe, params_channels(params), params_rate(params)); } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); out: return offset; } @@ -145,11 +173,13 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; + snd_soc_dpcm_mutex_lock(fe); for_each_pcm_streams(stream) if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream)) offset += dpcm_show_state(fe, stream, buf + offset, out_count - offset); + snd_soc_dpcm_mutex_unlock(fe); ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset); @@ -221,14 +251,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); - snd_pcm_stream_lock_irq(substream); + snd_soc_dpcm_stream_lock_irq(fe, stream); if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) { dpcm_fe_dai_do_trigger(substream, fe->dpcm[stream].trigger_pending - 1); fe->dpcm[stream].trigger_pending = 0; } fe->dpcm[stream].runtime_update = state; - snd_pcm_stream_unlock_irq(substream); + snd_soc_dpcm_stream_unlock_irq(fe, stream); } static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be, @@ -256,7 +286,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai; int i; - lockdep_assert_held(&rtd->card->pcm_mutex); + snd_soc_dpcm_mutex_assert_held(rtd); for_each_rtd_dais(rtd, i, dai) snd_soc_dai_action(dai, stream, action); @@ -309,6 +339,8 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, { struct snd_soc_dpcm *dpcm; + snd_soc_dpcm_mutex_assert_held(fe); + for_each_dpcm_be(fe, dir, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -646,14 +678,14 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, return ret; } -static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); if (!rollback) snd_soc_runtime_deactivate(rtd, substream->stream); @@ -665,9 +697,6 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) soc_pcm_components_close(substream, rollback); - - mutex_unlock(&rtd->card->pcm_mutex); - snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback); for_each_rtd_components(rtd, i, component) @@ -682,9 +711,21 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) * freed here. The cpu DAI, codec DAI, machine and components are also * shutdown. */ +static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_clean(rtd, substream, 0); +} + +/* PCM close ops for non-DPCM streams */ static int soc_pcm_close(struct snd_pcm_substream *substream) { - return soc_pcm_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + snd_soc_dpcm_mutex_lock(rtd); + soc_pcm_clean(rtd, substream, 0); + snd_soc_dpcm_mutex_unlock(rtd); + return 0; } static int soc_hw_sanity_check(struct snd_pcm_substream *substream) @@ -730,21 +771,21 @@ static int soc_hw_sanity_check(struct snd_pcm_substream *substream) * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, component, machine and codec DAI. */ -static int soc_pcm_open(struct snd_pcm_substream *substream) +static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; struct snd_soc_dai *dai; int i, ret = 0; + snd_soc_dpcm_mutex_assert_held(rtd); + for_each_rtd_components(rtd, i, component) pinctrl_pm_select_default_state(component->dev); ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) - goto pm_err; - - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + goto err; ret = soc_pcm_components_open(substream); if (ret < 0) @@ -791,16 +832,26 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) snd_soc_runtime_activate(rtd, substream->stream); ret = 0; err: - mutex_unlock(&rtd->card->pcm_mutex); -pm_err: if (ret < 0) { - soc_pcm_clean(substream, 1); + soc_pcm_clean(rtd, substream, 1); dev_err(rtd->dev, "%s() failed (%d)", __func__, ret); } return ret; } +/* PCM open ops for non-DPCM streams */ +static int soc_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_open(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { /* @@ -816,13 +867,13 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd) * rate, etc. This function is non atomic and can be called multiple times, * it can refer to the runtime info. */ -static int soc_pcm_prepare(struct snd_pcm_substream *substream) +static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = snd_soc_link_prepare(substream); if (ret < 0) @@ -850,14 +901,24 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dai_digital_mute(dai, 0, substream->stream); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); return ret; } +/* PCM prepare ops for non-DPCM streams */ +static int soc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_prepare(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, unsigned int mask) { @@ -869,22 +930,20 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params, interval->max = channels; } -static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) +static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, int rollback) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *dai; int i; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); /* clear the corresponding DAIs parameters when going to be inactive */ for_each_rtd_dais(rtd, i, dai) { - int active = snd_soc_dai_stream_active(dai, substream->stream); - if (snd_soc_dai_active(dai) == 1) soc_pcm_set_dai_params(dai, NULL); - if (active == 1) + if (snd_soc_dai_stream_active(dai, substream->stream) == 1) snd_soc_dai_digital_mute(dai, 1, substream->stream); } @@ -898,23 +957,32 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback) snd_soc_pcm_component_hw_free(substream, rollback); /* now free hw params for the DAIs */ - for_each_rtd_dais(rtd, i, dai) { - if (!snd_soc_dai_stream_valid(dai, substream->stream)) - continue; + for_each_rtd_dais(rtd, i, dai) + if (snd_soc_dai_stream_valid(dai, substream->stream)) + snd_soc_dai_hw_free(dai, substream, rollback); - snd_soc_dai_hw_free(dai, substream, rollback); - } - - mutex_unlock(&rtd->card->pcm_mutex); return 0; } /* * Frees resources allocated by hw_params, can be called multiple times */ +static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) +{ + return soc_pcm_hw_clean(rtd, substream, 0); +} + +/* hw_free PCM ops for non-DPCM streams */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { - return soc_pcm_hw_clean(substream, 0); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_free(rtd, substream); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; } /* @@ -922,15 +990,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) * function can also be called multiple times and can allocate buffers * (using snd_pcm_lib_* ). It's non-atomic. */ -static int soc_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; - mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); + snd_soc_dpcm_mutex_assert_held(rtd); ret = soc_pcm_params_symmetry(substream, params); if (ret) @@ -1002,16 +1070,27 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_pcm_component_hw_params(substream, params); out: - mutex_unlock(&rtd->card->pcm_mutex); - if (ret < 0) { - soc_pcm_hw_clean(substream, 1); + soc_pcm_hw_clean(rtd, substream, 1); dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret); } return ret; } +/* hw_params PCM ops for non-DPCM streams */ +static int soc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret; + + snd_soc_dpcm_mutex_lock(rtd); + ret = __soc_pcm_hw_params(rtd, substream, params); + snd_soc_dpcm_mutex_unlock(rtd); + return ret; +} + static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -1085,41 +1164,22 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) /* * soc level wrapper for pointer callback * If cpu_dai, codec_dai, component driver has the delay callback, then - * the runtime->delay will be updated accordingly. + * the runtime->delay will be updated via snd_soc_pcm_component/dai_delay(). */ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai; - struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t offset = 0; - snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t codec_delay = 0; snd_pcm_sframes_t cpu_delay = 0; - int i; - - /* clearing the previous total delay */ - runtime->delay = 0; offset = snd_soc_pcm_component_pointer(substream); - /* base delay if assigned in pointer callback */ - delay = runtime->delay; + /* should be called *after* snd_soc_pcm_component_pointer() */ + snd_soc_pcm_dai_delay(substream, &cpu_delay, &codec_delay); + snd_soc_pcm_component_delay(substream, &cpu_delay, &codec_delay); - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - cpu_delay = max(cpu_delay, - snd_soc_dai_delay(cpu_dai, substream)); - } - delay += cpu_delay; - - for_each_rtd_codec_dais(rtd, i, codec_dai) { - codec_delay = max(codec_delay, - snd_soc_dai_delay(codec_dai, substream)); - } - delay += codec_delay; - - runtime->delay = delay; + runtime->delay = cpu_delay + codec_delay; return offset; } @@ -1128,8 +1188,11 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream) { + struct snd_pcm_substream *fe_substream; + struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm; - unsigned long flags; + + snd_soc_dpcm_mutex_assert_held(fe); /* only add new dpcms */ for_each_dpcm_be(fe, stream, dpcm) { @@ -1137,7 +1200,21 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, return 0; } - dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL); + fe_substream = snd_soc_dpcm_get_substream(fe, stream); + be_substream = snd_soc_dpcm_get_substream(be, stream); + + if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) { + dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n", + __func__); + return -EINVAL; + } + if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) { + dev_warn(be->dev, "%s: FE is nonatomic but BE is not, forcing BE as nonatomic\n", + __func__); + be_substream->pcm->nonatomic = 1; + } + + dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC); if (!dpcm) return -ENOMEM; @@ -1145,10 +1222,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, dpcm->fe = fe; be->dpcm[stream].runtime = fe->dpcm[stream].runtime; dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_lock_irq(fe, stream); list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients); list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); + snd_soc_dpcm_stream_unlock_irq(fe, stream); dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n", stream ? "capture" : "playback", fe->dai_link->name, @@ -1191,8 +1268,11 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; - unsigned long flags; + LIST_HEAD(deleted_dpcms); + snd_soc_dpcm_mutex_assert_held(fe); + + snd_soc_dpcm_stream_lock_irq(fe, stream); for_each_dpcm_be_safe(fe, stream, dpcm, d) { dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n", stream ? "capture" : "playback", @@ -1208,12 +1288,16 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) /* BEs still alive need new FE */ dpcm_be_reparent(fe, dpcm->be, stream); - dpcm_remove_debugfs_state(dpcm); - - spin_lock_irqsave(&fe->card->dpcm_lock, flags); list_del(&dpcm->list_be); + list_move(&dpcm->list_fe, &deleted_dpcms); + } + snd_soc_dpcm_stream_unlock_irq(fe, stream); + + while (!list_empty(&deleted_dpcms)) { + dpcm = list_first_entry(&deleted_dpcms, struct snd_soc_dpcm, + list_fe); list_del(&dpcm->list_fe); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); + dpcm_remove_debugfs_state(dpcm); kfree(dpcm); } } @@ -1262,8 +1346,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } -static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, - enum snd_soc_dapm_direction dir) +bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir) { struct snd_soc_card *card = widget->dapm->card; struct snd_soc_pcm_runtime *rtd; @@ -1281,6 +1364,7 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, return false; } +EXPORT_SYMBOL_GPL(dpcm_end_walk_at_be); int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list) @@ -1395,6 +1479,16 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, if (!fe->dpcm[stream].runtime && !fe->fe_compr) continue; + /* + * Filter for systems with 'component_chaining' enabled. + * This helps to avoid unnecessary re-configuration of an + * already active BE on such systems. + */ + if (fe->card->component_chaining && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) + continue; + /* newly connected FE and BE */ err = dpcm_be_connect(fe, be, stream); if (err < 0) { @@ -1429,12 +1523,9 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; - unsigned long flags; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_NO); - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); } void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, @@ -1470,12 +1561,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream, continue; if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) { - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } } - soc_pcm_close(be_substream); + __soc_pcm_close(be, be_substream); be_substream->runtime = NULL; be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; } @@ -1523,7 +1614,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) stream ? "capture" : "playback", be->dai_link->name); be_substream->runtime = be->dpcm[stream].runtime; - err = soc_pcm_open(be_substream); + err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; if (be->dpcm[stream].users < 0) @@ -1534,7 +1625,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; } - + be->dpcm[stream].be_start = 0; be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; count++; } @@ -1767,7 +1858,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name); /* start the DAI frontend */ - ret = soc_pcm_open(fe_substream); + ret = __soc_pcm_open(fe, fe_substream); if (ret < 0) goto unwind; @@ -1798,6 +1889,8 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); /* shutdown the BEs */ @@ -1806,7 +1899,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name); /* now shutdown the frontend */ - soc_pcm_close(substream); + __soc_pcm_close(fe, substream); /* run the stream stop event */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); @@ -1851,7 +1944,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_free BE %s\n", be->dai_link->name); - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; } @@ -1862,13 +1955,13 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name); /* call hw_free on the frontend */ - soc_pcm_hw_free(substream); + soc_pcm_hw_clean(fe, substream, 0); /* only hw_params backends that are either sinks or sources * to this frontend DAI */ @@ -1877,7 +1970,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE; dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return 0; } @@ -1921,7 +2014,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: hw_params BE %s\n", be->dai_link->name); - ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params); + ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params); if (ret < 0) goto unwind; @@ -1951,7 +2044,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP)) continue; - soc_pcm_hw_free(be_substream); + __soc_pcm_hw_free(be, be_substream); } return ret; @@ -1963,7 +2056,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int ret, stream = substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); memcpy(&fe->dpcm[stream].hw_params, params, @@ -1977,7 +2070,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, params_channels(params), params_format(params)); /* call hw_params on the frontend */ - ret = soc_pcm_hw_params(substream, params); + ret = __soc_pcm_hw_params(fe, substream, params); if (ret < 0) dpcm_be_dai_hw_free(fe, stream); else @@ -1985,7 +2078,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret); @@ -1998,6 +2091,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, { struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; + unsigned long flags; int ret = 0; for_each_dpcm_be(fe, stream, dpcm) { @@ -2006,89 +2100,128 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); + snd_soc_dpcm_stream_lock_irqsave_nested(be, stream, flags); + /* is this op for this BE ? */ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) - continue; + goto next; dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", be->dai_link->name, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_RESUME: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) - continue; + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + if (!be->dpcm[stream].be_start && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && + (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) + goto next; + + be->dpcm[stream].be_start++; + if (be->dpcm[stream].be_start != 1) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start--; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_STOP: if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start--; + + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START) + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; break; case SNDRV_PCM_TRIGGER_SUSPEND: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) - continue; + goto next; - if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) - continue; + be->dpcm[stream].be_start--; + if (be->dpcm[stream].be_start != 0) + goto next; ret = soc_pcm_trigger(be_substream, cmd); - if (ret) - goto end; + if (ret) { + be->dpcm[stream].be_start++; + goto next; + } be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break; } +next: + snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags); + if (ret) + break; } -end: if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n", __func__, be->dai_link->name, ret); @@ -2256,7 +2389,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: prepare BE %s\n", be->dai_link->name); - ret = soc_pcm_prepare(be_substream); + ret = __soc_pcm_prepare(be, be_substream); if (ret < 0) break; @@ -2274,7 +2407,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream); int stream = substream->stream, ret = 0; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name); @@ -2293,7 +2426,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) goto out; /* call prepare on the frontend */ - ret = soc_pcm_prepare(substream); + ret = __soc_pcm_prepare(fe, substream); if (ret < 0) goto out; @@ -2301,7 +2434,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) out: dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2352,7 +2485,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) struct snd_soc_dpcm *dpcm; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int ret = 0; - unsigned long flags; dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n", stream ? "capture" : "playback", fe->dai_link->name); @@ -2421,7 +2553,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) dpcm_be_dai_shutdown(fe, stream); disconnect: /* disconnect any pending BEs */ - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; @@ -2433,7 +2564,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) be->dpcm[stream].state == SND_SOC_DPCM_STATE_NEW) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); if (ret < 0) dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret); @@ -2508,7 +2638,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) struct snd_soc_pcm_runtime *fe; int ret = 0; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass); /* shutdown all old paths first */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 0); @@ -2524,7 +2654,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) } out: - mutex_unlock(&card->mutex); + mutex_unlock(&card->pcm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); @@ -2535,6 +2665,8 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) struct snd_soc_dpcm *dpcm; int stream = fe_substream->stream; + snd_soc_dpcm_mutex_assert_held(fe); + /* mark FE's links ready to prune */ for_each_dpcm_be(fe, stream, dpcm) dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; @@ -2549,12 +2681,12 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream); int ret; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); ret = dpcm_fe_dai_shutdown(fe_substream); dpcm_fe_dai_cleanup(fe_substream); - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2565,7 +2697,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) int ret; int stream = fe_substream->stream; - mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + snd_soc_dpcm_mutex_lock(fe); fe->dpcm[stream].runtime = fe_substream->runtime; ret = dpcm_path_get(fe, stream, &list); @@ -2582,7 +2714,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) dpcm_clear_pending_state(fe, stream); dpcm_path_put(&list); open_end: - mutex_unlock(&fe->card->mutex); + snd_soc_dpcm_mutex_unlock(fe); return ret; } @@ -2843,10 +2975,8 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, struct snd_soc_dpcm *dpcm; int state; int ret = 1; - unsigned long flags; int i; - spin_lock_irqsave(&fe->card->dpcm_lock, flags); for_each_dpcm_fe(be, stream, dpcm) { if (dpcm->fe == fe) @@ -2860,7 +2990,6 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe, } } } - spin_unlock_irqrestore(&fe->card->dpcm_lock, flags); /* it's safe to do this BE DAI */ return ret; diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 7459956d62..2630df024d 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -56,7 +56,7 @@ struct soc_tplg { const struct firmware *fw; /* runtime FW parsing */ - const u8 *pos; /* read postion */ + const u8 *pos; /* read position */ const u8 *hdr_pos; /* header position */ unsigned int pass; /* pass number */ @@ -78,7 +78,7 @@ struct soc_tplg { }; static int soc_tplg_process_headers(struct soc_tplg *tplg); -static void soc_tplg_complete(struct soc_tplg *tplg); +static int soc_tplg_complete(struct soc_tplg *tplg); /* check we dont overflow the data for this control chunk */ static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, @@ -312,10 +312,12 @@ static int soc_tplg_dai_link_load(struct soc_tplg *tplg, } /* tell the component driver that all firmware has been loaded in this request */ -static void soc_tplg_complete(struct soc_tplg *tplg) +static int soc_tplg_complete(struct soc_tplg *tplg) { if (tplg->ops && tplg->ops->complete) - tplg->ops->complete(tplg->comp); + return tplg->ops->complete(tplg->comp); + + return 0; } /* add a dynamic kcontrol */ @@ -349,7 +351,7 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg, struct snd_soc_component *comp = tplg->comp; return soc_tplg_add_dcontrol(comp->card->snd_card, - comp->dev, k, comp->name_prefix, comp, kcontrol); + tplg->dev, k, comp->name_prefix, comp, kcontrol); } /* remove a mixer kcontrol */ @@ -1473,10 +1475,6 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, goto widget; } - control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; - dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n", - w->name, w->num_kcontrols, control_hdr->type); - template.num_kcontrols = le32_to_cpu(w->num_kcontrols); kc = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(*kc), GFP_KERNEL); if (!kc) @@ -1487,7 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, if (!kcontrol_type) goto err; - for (i = 0; i < w->num_kcontrols; i++) { + for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) { control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; switch (le32_to_cpu(control_hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1536,6 +1534,8 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, } template.kcontrol_news = kc; + dev_dbg(tplg->dev, "ASoC: template %s with %d/%d/%d (mixer/enum/bytes) control\n", + w->name, mixer_count, enum_count, bytes_count); widget: ret = soc_tplg_widget_load(tplg, &template, w); @@ -1591,11 +1591,28 @@ static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg, struct snd_soc_tplg_dapm_widget *widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos; int ret; + /* + * check if widget itself fits within topology file + * use sizeof instead of widget->size, as we can't be sure + * it is set properly yet (file may end before it is present) + */ + if (soc_tplg_get_offset(tplg) + sizeof(*widget) >= tplg->fw->size) { + dev_err(tplg->dev, "ASoC: invalid widget data size\n"); + return -EINVAL; + } + + /* check if widget has proper size */ if (le32_to_cpu(widget->size) != sizeof(*widget)) { dev_err(tplg->dev, "ASoC: invalid widget size\n"); return -EINVAL; } + /* check if widget private data fits within topology file */ + if (soc_tplg_get_offset(tplg) + le32_to_cpu(widget->priv.size) >= tplg->fw->size) { + dev_err(tplg->dev, "ASoC: invalid widget private data size\n"); + return -EINVAL; + } + ret = soc_tplg_dapm_widget_create(tplg, widget); if (ret < 0) { dev_err(tplg->dev, "ASoC: failed to load widget %s\n", @@ -2438,6 +2455,7 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg, _manifest = manifest; } else { abi_match = false; + ret = manifest_new_ver(tplg, manifest, &_manifest); if (ret < 0) return ret; @@ -2468,6 +2486,14 @@ static int soc_valid_header(struct soc_tplg *tplg, return -EINVAL; } + if (soc_tplg_get_hdr_offset(tplg) + hdr->payload_size >= tplg->fw->size) { + dev_err(tplg->dev, + "ASoC: invalid header of type %d at offset %ld payload_size %d\n", + le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), + hdr->payload_size); + return -EINVAL; + } + /* big endian firmware objects not supported atm */ if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) { dev_err(tplg->dev, @@ -2627,7 +2653,7 @@ static int soc_tplg_load(struct soc_tplg *tplg) ret = soc_tplg_process_headers(tplg); if (ret == 0) - soc_tplg_complete(tplg); + return soc_tplg_complete(tplg); return ret; } @@ -2642,17 +2668,17 @@ int snd_soc_tplg_component_load(struct snd_soc_component *comp, /* * check if we have sane parameters: * comp - needs to exist to keep and reference data while parsing - * comp->dev - used for resource management and prints * comp->card - used for setting card related parameters + * comp->card->dev - used for resource management and prints * fw - we need it, as it is the very thing we parse */ - if (!comp || !comp->dev || !comp->card || !fw) + if (!comp || !comp->card || !comp->card->dev || !fw) return -EINVAL; /* setup parsing context */ memset(&tplg, 0, sizeof(tplg)); tplg.fw = fw; - tplg.dev = comp->dev; + tplg.dev = comp->card->dev; tplg.comp = comp; if (ops) { tplg.ops = ops; diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 299b5d6ebf..a4efe7e52a 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -63,10 +63,23 @@ static const struct snd_pcm_hardware dummy_dma_hardware = { .periods_max = 128, }; + +static const struct snd_soc_component_driver dummy_platform; + static int dummy_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int i; + + /* + * If there are other components associated with rtd, we shouldn't + * override their hwparams + */ + for_each_rtd_components(rtd, i, component) { + if (component->driver == &dummy_platform) + return 0; + } /* BE's dont need dummy params */ if (!rtd->dai_link->no_pcm) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index cd659493b5..1a7d6cefd3 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_SOF_TOPLEVEL +menuconfig SND_SOC_SOF_TOPLEVEL bool "Sound Open Firmware Support" help This adds support for Sound Open Firmware (SOF). SOF is free and @@ -40,12 +40,18 @@ config SND_SOC_SOF_ACPI_DEV config SND_SOC_SOF_OF tristate "SOF OF enumeration support" depends on OF || COMPILE_TEST - select SND_SOC_SOF help This adds support for Device Tree enumeration. This option is - required to enable i.MX8 devices. + required to enable i.MX8 or Mediatek devices. Say Y if you need this option. If unsure select "N". +config SND_SOC_SOF_OF_DEV + tristate + +config SND_SOC_SOF_COMPRESS + bool + select SND_SOC_COMPRESS + config SND_SOC_SOF_DEBUG_PROBES bool "SOF enable data probing" select SND_SOC_COMPRESS @@ -57,7 +63,7 @@ config SND_SOC_SOF_DEBUG_PROBES config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" - depends on EXPERT + depends on EXPERT && SND_SOC_SOF help This option unlocks SOF developer options for debug/performance/ code hardening. @@ -188,6 +194,14 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST Say Y if you want to enable IPC flood test. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR + bool "SOF enable IPC message injector" + help + This option enables the IPC message injector which can be used to send + crafted IPC messages to the DSP to test its robustness. + Say Y if you want to enable the IPC message injector. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help @@ -219,8 +233,10 @@ config SND_SOC_SOF_PROBE_WORK_QUEUE When selected, the probe is handled in two steps, for example to avoid lockdeps if request_module is used in the probe. +source "sound/soc/sof/amd/Kconfig" source "sound/soc/sof/imx/Kconfig" source "sound/soc/sof/intel/Kconfig" +source "sound/soc/sof/mediatek/Kconfig" source "sound/soc/sof/xtensa/Kconfig" endif diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 606d8137cd..964b429146 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,8 +1,10 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o utils.o sof-audio.o -snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o compress.o + control.o trace.o utils.o sof-audio.o stream-ipc.o + +snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o +snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o @@ -15,9 +17,11 @@ obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF_ACPI_DEV) += snd-sof-acpi.o -obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o +obj-$(CONFIG_SND_SOC_SOF_OF_DEV) += snd-sof-of.o obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ +obj-$(CONFIG_SND_SOC_SOF_AMD_TOPLEVEL) += amd/ obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/ +obj-$(CONFIG_SND_SOC_SOF_MTK_TOPLEVEL) += mediatek/ diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c index 57d5bf0a17..01ca85f0b8 100644 --- a/sound/soc/sof/compress.c +++ b/sound/soc/sof/compress.c @@ -1,147 +1,51 @@ // 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) 2019-2020 Intel Corporation. All rights reserved. -// -// Author: Cezary Rojewski +// Copyright 2021 NXP // +// Author: Daniel Baluta #include -#include "compress.h" -#include "ops.h" -#include "probe.h" +#include +#include +#include "sof-audio.h" +#include "sof-priv.h" -const struct snd_compress_ops sof_probe_compressed_ops = { - .copy = sof_probe_compr_copy, -}; -EXPORT_SYMBOL(sof_probe_compressed_ops); - -int sof_probe_compr_open(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) +static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work) { - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - int ret; + struct snd_sof_pcm_stream *sps = + container_of(work, struct snd_sof_pcm_stream, + period_elapsed_work); - ret = snd_sof_probe_compr_assign(sdev, cstream, dai); - if (ret < 0) { - dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret); - return ret; + snd_compr_fragment_elapsed(sps->cstream); +} + +void snd_sof_compr_init_elapsed_work(struct work_struct *work) +{ + INIT_WORK(work, snd_sof_compr_fragment_elapsed_work); +} + +/* + * sof compr fragment elapse, this could be called in irq thread context + */ +void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) +{ + struct snd_soc_component *component; + struct snd_soc_pcm_runtime *rtd; + struct snd_sof_pcm *spcm; + + if (!cstream) + return; + + rtd = cstream->private_data; + component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) { + dev_err(component->dev, + "fragment elapsed called for unknown stream!\n"); + return; } - sdev->extractor_stream_tag = ret; - return 0; + /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ + schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); } -EXPORT_SYMBOL(sof_probe_compr_open); - -int sof_probe_compr_free(struct snd_compr_stream *cstream, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - struct sof_probe_point_desc *desc; - size_t num_desc; - int i, ret; - - /* disconnect all probe points */ - ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); - if (ret < 0) { - dev_err(dai->dev, "Failed to get probe points: %d\n", ret); - goto exit; - } - - for (i = 0; i < num_desc; i++) - sof_ipc_probe_points_remove(sdev, &desc[i].buffer_id, 1); - kfree(desc); - -exit: - ret = sof_ipc_probe_deinit(sdev); - if (ret < 0) - dev_err(dai->dev, "Failed to deinit probe: %d\n", ret); - - sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; - snd_compr_free_pages(cstream); - - return snd_sof_probe_compr_free(sdev, cstream, dai); -} -EXPORT_SYMBOL(sof_probe_compr_free); - -int sof_probe_compr_set_params(struct snd_compr_stream *cstream, - struct snd_compr_params *params, struct snd_soc_dai *dai) -{ - struct snd_compr_runtime *rtd = cstream->runtime; - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - int ret; - - cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; - cstream->dma_buffer.dev.dev = sdev->dev; - ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); - if (ret < 0) - return ret; - - ret = snd_sof_probe_compr_set_params(sdev, cstream, params, dai); - if (ret < 0) - return ret; - - ret = sof_ipc_probe_init(sdev, sdev->extractor_stream_tag, - rtd->dma_bytes); - if (ret < 0) { - dev_err(dai->dev, "Failed to init probe: %d\n", ret); - return ret; - } - - return 0; -} -EXPORT_SYMBOL(sof_probe_compr_set_params); - -int sof_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, - struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_trigger(sdev, cstream, cmd, dai); -} -EXPORT_SYMBOL(sof_probe_compr_trigger); - -int sof_probe_compr_pointer(struct snd_compr_stream *cstream, - struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) -{ - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - - return snd_sof_probe_compr_pointer(sdev, cstream, tstamp, dai); -} -EXPORT_SYMBOL(sof_probe_compr_pointer); - -int sof_probe_compr_copy(struct snd_soc_component *component, - struct snd_compr_stream *cstream, - char __user *buf, size_t count) -{ - struct snd_compr_runtime *rtd = cstream->runtime; - unsigned int offset, n; - void *ptr; - int ret; - - if (count > rtd->buffer_size) - count = rtd->buffer_size; - - div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); - ptr = rtd->dma_area + offset; - n = rtd->buffer_size - offset; - - if (count < n) { - ret = copy_to_user(buf, ptr, count); - } else { - ret = copy_to_user(buf, ptr, n); - ret += copy_to_user(buf + n, rtd->dma_area, count - n); - } - - if (ret) - return count - ret; - return count; -} -EXPORT_SYMBOL(sof_probe_compr_copy); diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index a5dd728c58..ef61936dad 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -65,6 +65,32 @@ static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) return i - 1; } +static void snd_sof_refresh_control(struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct snd_soc_component *scomp = scontrol->scomp; + int ret; + + if (!scontrol->comp_data_dirty) + return; + + if (!pm_runtime_active(scomp->dev)) + return; + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* refresh the component data from DSP */ + scontrol->comp_data_dirty = false; + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) { + dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); + /* Set the flag to re-try next time to get the data */ + scontrol->comp_data_dirty = true; + } +} + int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -74,6 +100,8 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + snd_sof_refresh_control(scontrol); + /* read back each channel */ for (i = 0; i < channels; i++) ucontrol->value.integer.value[i] = @@ -106,11 +134,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - SOF_CTRL_CMD_VOLUME, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -145,6 +169,8 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + snd_sof_refresh_control(scontrol); + /* read back each channel */ for (i = 0; i < channels; i++) ucontrol->value.integer.value[i] = cdata->chanv[i].value; @@ -177,11 +203,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, /* notify DSP of mixer updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - SOF_CTRL_CMD_SWITCH, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -195,6 +217,8 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; unsigned int i, channels = scontrol->num_channels; + snd_sof_refresh_control(scontrol); + /* read back each channel */ for (i = 0; i < channels; i++) ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; @@ -224,11 +248,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, /* notify DSP of enum updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_VALUE, - SOF_CTRL_TYPE_VALUE_CHAN_GET, - SOF_CTRL_CMD_ENUM, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return change; } @@ -244,6 +264,8 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct sof_abi_hdr *data = cdata->data; size_t size; + snd_sof_refresh_control(scontrol); + if (be->max > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, "error: data max %d exceeds ucontrol data array size\n", @@ -300,11 +322,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -349,7 +367,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, } /* Check that header id matches the command */ - if (header.numid != scontrol->cmd) { + if (header.numid != cdata->cmd) { dev_err_ratelimited(scomp->dev, "error: incorrect numid %d\n", header.numid); @@ -380,11 +398,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, - SOF_IPC_COMP_SET_DATA, - SOF_CTRL_TYPE_DATA_SET, - scontrol->cmd, - true); + snd_sof_ipc_set_get_comp_data(scontrol, true); return 0; } @@ -421,8 +435,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, - scontrol->cmd, false); + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) goto out; @@ -443,7 +456,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ goto out; } - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { ret = -EFAULT; @@ -475,6 +488,8 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, (struct snd_ctl_tlv __user *)binary_data; size_t data_size; + snd_sof_refresh_control(scontrol); + /* * Decrement the limit by ext bytes header size to * ensure the user space buffer is not exceeded. @@ -501,7 +516,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, if (data_size > size) return -ENOSPC; - header.numid = scontrol->cmd; + header.numid = cdata->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) return -EFAULT; @@ -511,3 +526,147 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, return 0; } + +static void snd_sof_update_control(struct snd_sof_control *scontrol, + struct sof_ipc_ctrl_data *cdata) +{ + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc_ctrl_data *local_cdata; + int i; + + local_cdata = scontrol->control_data; + + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + if (cdata->num_elems != local_cdata->data->size) { + dev_err(scomp->dev, + "error: cdata binary size mismatch %u - %u\n", + cdata->num_elems, local_cdata->data->size); + return; + } + + /* copy the new binary data */ + memcpy(local_cdata->data, cdata->data, cdata->num_elems); + } else if (cdata->num_elems != scontrol->num_channels) { + dev_err(scomp->dev, + "error: cdata channel count mismatch %u - %d\n", + cdata->num_elems, scontrol->num_channels); + } else { + /* copy the new values */ + for (i = 0; i < cdata->num_elems; i++) + local_cdata->chanv[i].value = cdata->chanv[i].value; + } +} + +void snd_sof_control_notify(struct snd_sof_dev *sdev, + struct sof_ipc_ctrl_data *cdata) +{ + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; + struct snd_sof_widget *swidget; + struct snd_kcontrol *kc = NULL; + struct soc_mixer_control *sm; + struct soc_bytes_ext *be; + size_t expected_size; + struct soc_enum *se; + bool found = false; + int i, type; + + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || + cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { + dev_err(sdev->dev, + "Component data is not supported in control notification\n"); + return; + } + + /* Find the swidget first */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == cdata->comp_id) { + found = true; + break; + } + } + + if (!found) + return; + + /* Translate SOF cmd to TPLG type */ + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + type = SND_SOC_TPLG_TYPE_MIXER; + break; + case SOF_CTRL_CMD_BINARY: + type = SND_SOC_TPLG_TYPE_BYTES; + break; + case SOF_CTRL_CMD_ENUM: + type = SND_SOC_TPLG_TYPE_ENUM; + break; + default: + dev_err(sdev->dev, "error: unknown cmd %u\n", cdata->cmd); + return; + } + + widget = swidget->widget; + for (i = 0; i < widget->num_kcontrols; i++) { + /* skip non matching types or non matching indexes within type */ + if (widget->dobj.widget.kcontrol_type[i] == type && + widget->kcontrol_news[i].index == cdata->index) { + kc = widget->kcontrols[i]; + break; + } + } + + if (!kc) + return; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SOF_CTRL_CMD_BINARY: + be = (struct soc_bytes_ext *)kc->private_value; + scontrol = be->dobj.private; + break; + case SOF_CTRL_CMD_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + default: + return; + } + + expected_size = sizeof(struct sof_ipc_ctrl_data); + switch (cdata->type) { + case SOF_CTRL_TYPE_VALUE_CHAN_GET: + case SOF_CTRL_TYPE_VALUE_CHAN_SET: + expected_size += cdata->num_elems * + sizeof(struct sof_ipc_ctrl_value_chan); + break; + case SOF_CTRL_TYPE_DATA_GET: + case SOF_CTRL_TYPE_DATA_SET: + expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + break; + default: + return; + } + + if (cdata->rhdr.hdr.size != expected_size) { + dev_err(sdev->dev, "error: component notification size mismatch\n"); + return; + } + + if (cdata->num_elems) + /* + * The message includes the updated value/data, update the + * control's local cache using the received notification + */ + snd_sof_update_control(scontrol, cdata); + else + /* Mark the scontrol that the value/data is changed in SOF */ + scontrol->comp_data_dirty = true; + + snd_ctl_notify_one(swidget->scomp->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); +} diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 59d0d7b2b5..8f32b5b12b 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -15,11 +15,11 @@ #include "sof-priv.h" #include "ops.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "probe.h" +#include "sof-probes.h" #endif /* see SOF_DBG_ flags */ -int sof_core_debug; +static int sof_core_debug = IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE); module_param_named(sof_debug, sof_core_debug, int, 0444); MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); @@ -27,6 +27,22 @@ MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)"); #define TIMEOUT_DEFAULT_IPC_MS 500 #define TIMEOUT_DEFAULT_BOOT_MS 2000 +/** + * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug + * @mask: Flag or combination of flags to check + * + * Returns true if all bits set in mask is also set in sof_core_debug, otherwise + * false + */ +bool sof_debug_check_flag(int mask) +{ + if ((sof_core_debug & mask) == mask) + return true; + + return false; +} +EXPORT_SYMBOL(sof_debug_check_flag); + /* * FW Panic/fault handling. */ @@ -52,23 +68,33 @@ static const struct sof_panic_msg panic_msg[] = { {SOF_IPC_PANIC_ASSERT, "assertion failed"}, }; -/* +/** + * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace + * @sdev: Pointer to the device's sdev + * @level: prink log level to use for the printing + * @panic_code: the panic code + * @tracep_code: tracepoint code + * @oops: Pointer to DSP specific oops data + * @panic_info: Pointer to the received panic information message + * @stack: Pointer to the call stack data + * @stack_words: Number of words in the stack data + * * helper to be called from .dbg_dump callbacks. No error code is * provided, it's left as an exercise for the caller of .dbg_dump * (typically IPC or loader) */ -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words) +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words) { u32 code; int i; /* is firmware dead ? */ if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) { - dev_err(sdev->dev, "error: unexpected fault 0x%8.8x trace 0x%8.8x\n", - panic_code, tracep_code); + dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n", + panic_code, tracep_code); return; /* no fault ? */ } @@ -76,54 +102,55 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, for (i = 0; i < ARRAY_SIZE(panic_msg); i++) { if (panic_msg[i].id == code) { - dev_err(sdev->dev, "error: %s\n", panic_msg[i].msg); - dev_err(sdev->dev, "error: trace point %8.8x\n", - tracep_code); + dev_printk(level, sdev->dev, "reason: %s (%#x)\n", + panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); goto out; } } /* unknown error */ - dev_err(sdev->dev, "error: unknown reason %8.8x\n", panic_code); - dev_err(sdev->dev, "error: trace point %8.8x\n", tracep_code); + dev_printk(level, sdev->dev, "unknown panic code: %#x\n", + code & SOF_IPC_PANIC_CODE_MASK); + dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code); out: - dev_err(sdev->dev, "error: panic at %s:%d\n", - panic_info->filename, panic_info->linenum); - sof_oops(sdev, oops); - sof_stack(sdev, oops, stack, stack_words); + dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename, + panic_info->linenum); + sof_oops(sdev, level, oops); + sof_stack(sdev, level, oops, stack, stack_words); } -EXPORT_SYMBOL(snd_sof_get_status); +EXPORT_SYMBOL(sof_print_oops_and_stack); /* * FW Boot State Transition Diagram * - * +-----------------------------------------------------------------------+ - * | | - * ------------------ ------------------ | - * | | | | | - * | BOOT_FAILED | | READY_FAILED |-------------------------+ | - * | | | | | | - * ------------------ ------------------ | | - * ^ ^ | | - * | | | | - * (FW Boot Timeout) (FW_READY FAIL) | | - * | | | | - * | | | | - * ------------------ | ------------------ | | - * | | | | | | | - * | IN_PROGRESS |---------------+------------->| COMPLETE | | | - * | | (FW Boot OK) (FW_READY OK) | | | | - * ------------------ ------------------ | | - * ^ | | | - * | | | | - * (FW Loading OK) (System Suspend/Runtime Suspend) - * | | | | - * | | | | - * ------------------ ------------------ | | | - * | | | |<-----+ | | - * | PREPARE | | NOT_STARTED |<---------------------+ | - * | | | |<---------------------------+ + * +----------------------------------------------------------------------+ + * | | + * ------------------ ------------------ | + * | | | | | + * | BOOT_FAILED |<-------| READY_FAILED | | + * | |<--+ | | ------------------ | + * ------------------ | ------------------ | | | + * ^ | ^ | CRASHED |---+ | + * | | | | | | | + * (FW Boot Timeout) | (FW_READY FAIL) ------------------ | | + * | | | ^ | | + * | | | |(DSP Panic) | | + * ------------------ | | ------------------ | | + * | | | | | | | | + * | IN_PROGRESS |---------------+------------->| COMPLETE | | | + * | | (FW Boot OK) (FW_READY OK) | | | | + * ------------------ | ------------------ | | + * ^ | | | | + * | | | | | + * (FW Loading OK) | (System Suspend/Runtime Suspend) + * | | | | | + * | (FW Loading Fail) | | | + * ------------------ | ------------------ | | | + * | | | | |<-----+ | | + * | PREPARE |---+ | NOT_STARTED |<---------------------+ | + * | | | |<--------------------------+ * ------------------ ------------------ * | ^ | ^ * | | | | @@ -147,7 +174,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return ret; } - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* check machine info */ ret = sof_machine_check(sdev); @@ -186,10 +213,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to load DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_load_err; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -199,11 +227,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (ret < 0) { dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); goto fw_run_err; } - if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE) || - (sof_core_debug & SOF_DBG_ENABLE_TRACE)) { + if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { sdev->dtrace_is_supported = true; /* init DMA trace */ @@ -266,7 +294,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) snd_sof_remove(sdev); /* all resources freed, update state to match */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->first_boot = true; return ret; @@ -301,7 +329,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->pdata = plat_data; sdev->first_boot = true; - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID; #endif @@ -326,9 +354,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->power_state_access); - if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) - INIT_WORK(&sdev->probe_work, sof_probe_work); - /* set default timeouts if none provided */ if (plat_data->desc->ipc_timeout == 0) sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS; @@ -340,6 +365,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sdev->boot_timeout = plat_data->desc->boot_timeout; if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) { + INIT_WORK(&sdev->probe_work, sof_probe_work); schedule_work(&sdev->probe_work); return 0; } @@ -365,7 +391,15 @@ int snd_sof_device_remove(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); + /* + * Unregister machine driver. This will unbind the snd_card which + * will remove the component driver and unload the topology + * before freeing the snd_card. + */ + snd_sof_machine_unregister(sdev, pdata); + if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { + snd_sof_free_trace(sdev); ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) dev_warn(dev, "error: %d failed to prepare DSP for device removal", @@ -373,16 +407,8 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); - snd_sof_free_trace(sdev); } - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Unregistering the machine driver results in unloading the topology. * Some widgets, ex: scheduler, attempt to power down the core they are diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index a51a928ea4..6d6757075f 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -20,7 +20,7 @@ #include "ops.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "probe.h" +#include "sof-probes.h" /** * strsplit_u32 - Split string into sequence of u32 tokens @@ -336,6 +336,104 @@ static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, } #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) +static ssize_t msg_inject_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct sof_ipc_reply *rhdr = dfse->msg_inject_rx; + + if (!rhdr->hdr.size || !count || *ppos) + return 0; + + if (count > rhdr->hdr.size) + count = rhdr->hdr.size; + + if (copy_to_user(buffer, dfse->msg_inject_rx, count)) + return -EFAULT; + + *ppos += count; + return count; +} + +static ssize_t msg_inject_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx; + size_t size; + int ret, err; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE, + ppos, buffer, count); + if (size != count) + return size > 0 ? -EFAULT : size; + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n", + __func__, ret); + pm_runtime_put_noidle(sdev->dev); + goto out; + } + + /* send the message */ + memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE); + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count, + dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE); + + pm_runtime_mark_last_busy(sdev->dev); + err = pm_runtime_put_autosuspend(sdev->dev); + if (err < 0) + dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n", + __func__, err); + + /* return size if test is successful */ + if (ret >= 0) + ret = size; + +out: + return ret; +} + +static const struct file_operations msg_inject_fops = { + .open = simple_open, + .read = msg_inject_read, + .write = msg_inject_write, + .llseek = default_llseek, +}; + +static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev, + const char *name, mode_t mode, + const struct file_operations *fops) +{ + struct snd_sof_dfsentry *dfse; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + /* pre allocate the tx and rx buffers */ + dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!dfse->msg_inject_tx || !dfse->msg_inject_rx) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->sdev = sdev; + + debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); + /* add to dfsentry list */ + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} +#endif + static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { @@ -546,10 +644,10 @@ static const struct file_operations sof_dfs_fops = { }; /* create FS entry for debug files that can expose DSP memories, registers */ -int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, - void __iomem *base, size_t size, - const char *name, - enum sof_debugfs_access_type access_type) +static int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, + void __iomem *base, size_t size, + const char *name, + enum sof_debugfs_access_type access_type) { struct snd_sof_dfsentry *dfse; @@ -586,7 +684,21 @@ int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, return 0; } -EXPORT_SYMBOL_GPL(snd_sof_debugfs_io_item); + +int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, u32 offset, + size_t size, const char *name, + enum sof_debugfs_access_type access_type) +{ + int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); + + if (bar < 0) + return bar; + + return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name, + access_type); +} +EXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem); /* create FS entry for debug files to expose kernel memory */ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, @@ -798,6 +910,15 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) return err; #endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644, + &msg_inject_fops); + + /* errors are only due to memory allocation, not debugfs */ + if (err < 0) + return err; +#endif + return 0; } EXPORT_SYMBOL_GPL(snd_sof_dbg_init); @@ -808,18 +929,86 @@ void snd_sof_free_debug(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_GPL(snd_sof_free_debug); +static const struct soc_fw_state_info { + enum sof_fw_state state; + const char *name; +} fw_state_dbg[] = { + {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, + {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, + {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, + {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, + {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, + {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, + {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, + {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, +}; + +static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { + if (sdev->fw_state == fw_state_dbg[i].state) { + dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", + fw_state_dbg[i].name, i); + return; + } + } + + dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); +} + +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) +{ + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; + bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); + + if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) + return; + + if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { + dev_printk(level, sdev->dev, + "------------[ DSP dump start ]------------\n"); + if (msg) + dev_printk(level, sdev->dev, "%s\n", msg); + snd_sof_dbg_print_fw_state(sdev, level); + sof_ops(sdev)->dbg_dump(sdev, flags); + dev_printk(level, sdev->dev, + "------------[ DSP dump end ]------------\n"); + if (!print_all) + sdev->dbg_dump_printed = true; + } else if (msg) { + dev_printk(level, sdev->dev, "%s\n", msg); + } +} +EXPORT_SYMBOL(snd_sof_dsp_dbg_dump); + +static void snd_sof_ipc_dump(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) { + dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); + sof_ops(sdev)->ipc_dump(sdev); + dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); + if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) + sdev->ipc_dump_printed = true; + } +} + void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) { if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || - (sof_core_debug & SOF_DBG_RETAIN_CTX)) { + sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { /* should we prevent DSP entering D3 ? */ - dev_info(sdev->dev, "info: preventing DSP entering D3 state to preserve context\n"); + if (!sdev->ipc_dump_printed) + dev_info(sdev->dev, + "preventing DSP entering D3 state to preserve context\n"); pm_runtime_get_noresume(sdev->dev); } /* dump vital information to the logs */ - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); snd_sof_ipc_dump(sdev); + snd_sof_dsp_dbg_dump(sdev, "Firmware exception", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); snd_sof_trace_notify_for_error(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 49d605cb09..9b8d5bb1e4 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -11,51 +11,33 @@ config SND_SOC_SOF_IMX_TOPLEVEL if SND_SOC_SOF_IMX_TOPLEVEL -config SND_SOC_SOF_IMX_OF - def_tristate SND_SOC_SOF_OF - select SND_SOC_SOF_IMX8 if SND_SOC_SOF_IMX8_SUPPORT - select SND_SOC_SOF_IMX8M if SND_SOC_SOF_IMX8M_SUPPORT - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - config SND_SOC_SOF_IMX_COMMON tristate + select SND_SOC_SOF_OF_DEV + select SND_SOC_SOF + select SND_SOC_SOF_XTENSA + select SND_SOC_SOF_COMPRESS help This option is not user-selectable but automagically handled by 'select' statements at a higher level. -config SND_SOC_SOF_IMX8_SUPPORT - bool "SOF support for i.MX8" - depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_IMX_OF +config SND_SOC_SOF_IMX8 + tristate "SOF support for i.MX8" + depends on IMX_SCU + depends on IMX_DSP + select SND_SOC_SOF_IMX_COMMON help This adds support for Sound Open Firmware for NXP i.MX8 platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8 - tristate +config SND_SOC_SOF_IMX8M + tristate "SOF support for i.MX8M" + depends on IMX_DSP select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -config SND_SOC_SOF_IMX8M_SUPPORT - bool "SOF support for i.MX8M" - depends on IMX_DSP=y || IMX_DSP=SND_SOC_SOF_OF help This adds support for Sound Open Firmware for NXP i.MX8M platforms. Say Y if you have such a device. If unsure select "N". -config SND_SOC_SOF_IMX8M - tristate - select SND_SOC_SOF_IMX_COMMON - select SND_SOC_SOF_XTENSA - help - This option is not user-selectable but automagically handled by - 'select' statements at a higher level. - -endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL +endif ## SND_SOC_SOF_IMX_TOPLEVEL diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 8826ef94f0..36e3d414a1 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -69,9 +69,33 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) IMX8_STACK_DUMP_SIZE); /* Print the information to the console */ - snd_sof_get_status(sdev, status, status, &xoops, &panic_info, stack, - IMX8_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, status, &xoops, + &panic_info, stack, IMX8_STACK_DUMP_SIZE); } EXPORT_SYMBOL(imx8_dump); +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + int ret; + + ret = devm_clk_bulk_get(sdev->dev, clks->num_dsp_clks, clks->dsp_clks); + if (ret) + dev_err(sdev->dev, "Failed to request DSP clocks\n"); + + return ret; +} +EXPORT_SYMBOL(imx8_parse_clocks); + +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + return clk_bulk_prepare_enable(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_enable_clocks); + +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks) +{ + clk_bulk_disable_unprepare(clks->num_dsp_clks, clks->dsp_clks); +} +EXPORT_SYMBOL(imx8_disable_clocks); + MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 1cc7d67041..ec4b3a5c74 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -3,6 +3,8 @@ #ifndef __IMX_COMMON_H__ #define __IMX_COMMON_H__ +#include + #define EXCEPT_MAX_HDR_SIZE 0x400 #define IMX8_STACK_DUMP_SIZE 32 @@ -13,4 +15,13 @@ void imx8_get_registers(struct snd_sof_dev *sdev, void imx8_dump(struct snd_sof_dev *sdev, u32 flags); +struct imx_clocks { + struct clk_bulk_data *dsp_clks; + int num_dsp_clks; +}; + +int imx8_parse_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +int imx8_enable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); +void imx8_disable_clocks(struct snd_sof_dev *sdev, struct imx_clocks *clks); + #endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 7e9723a10d..f6baecbb57 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -21,6 +21,7 @@ #include #include #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" /* DSP memories */ @@ -40,6 +41,13 @@ #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +/* DSP clocks */ +static struct clk_bulk_data imx8_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + struct imx8_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -56,42 +64,9 @@ struct imx8_priv { struct device **pd_dev; struct device_link **link; + struct imx_clocks *clks; }; -static void imx8_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -108,8 +83,7 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -123,7 +97,7 @@ static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } @@ -222,6 +196,11 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -334,6 +313,18 @@ static int imx8_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + /* init clocks info */ + priv->clks->dsp_clks = imx8_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + return 0; exit_pdev_unregister: @@ -352,6 +343,7 @@ static int imx8_remove(struct snd_sof_dev *sdev) struct imx8_priv *priv = sdev->pdata->hw_pdata; int i; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); for (i = 0; i < priv->num_domains; i++) { @@ -375,20 +367,92 @@ static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } -static void imx8_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz) +static void imx8_suspend(struct snd_sof_dev *sdev) { - sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); + int i; + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); } -static int imx8_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) +static int imx8_resume(struct snd_sof_dev *sdev) { + struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + return 0; } +static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + static struct snd_soc_dai_driver imx8_dai[] = { { .name = "esai0", @@ -414,8 +478,16 @@ static struct snd_soc_dai_driver imx8_dai[] = { }, }; +static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8_ops = { +static const struct snd_sof_dsp_ops sof_imx8_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -426,8 +498,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = { .block_read = sof_block_read, .block_write = sof_block_write, - /* Module IO */ - .read64 = sof_io_read64, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, /* ipc */ .send_msg = imx8_send_msg, @@ -435,8 +508,8 @@ struct snd_sof_dsp_ops sof_imx8_ops = { .get_mailbox_offset = imx8_get_mailbox_offset, .get_window_offset = imx8_get_window_offset, - .ipc_msg_data = imx8_ipc_msg_data, - .ipc_pcm_params = imx8_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -446,9 +519,14 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* Debug information */ .dbg_dump = imx8_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + + /* stream callbacks */ + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* Firmware ops */ - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, /* DAI drivers */ .drv = imx8_dai, @@ -460,11 +538,19 @@ struct snd_sof_dsp_ops sof_imx8_ops = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, }; -EXPORT_SYMBOL(sof_imx8_ops); /* i.MX8X ops */ -struct snd_sof_dsp_ops sof_imx8x_ops = { +static const struct snd_sof_dsp_ops sof_imx8x_ops = { /* probe and remove */ .probe = imx8_probe, .remove = imx8_remove, @@ -475,8 +561,9 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .block_read = sof_block_read, .block_write = sof_block_write, - /* Module IO */ - .read64 = sof_io_read64, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, /* ipc */ .send_msg = imx8_send_msg, @@ -484,8 +571,8 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .get_mailbox_offset = imx8_get_mailbox_offset, .get_window_offset = imx8_get_window_offset, - .ipc_msg_data = imx8_ipc_msg_data, - .ipc_pcm_params = imx8_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -495,14 +582,28 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { /* Debug information */ .dbg_dump = imx8_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + + /* stream callbacks */ + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* Firmware ops */ - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, /* DAI drivers */ .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + /* PM */ + .runtime_suspend = imx8_dsp_runtime_suspend, + .runtime_resume = imx8_dsp_runtime_resume, + + .suspend = imx8_dsp_suspend, + .resume = imx8_dsp_resume, + + .set_power_state = imx8_dsp_set_power_state, + /* ALSA HW info flags */ .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -510,7 +611,41 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP }; -EXPORT_SYMBOL(sof_imx8x_ops); + +static struct sof_dev_desc sof_of_imx8qxp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8x.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8x_ops, +}; + +static struct sof_dev_desc sof_of_imx8qm_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8_ops, +}; + +static const struct of_device_id sof_of_imx8_ids[] = { + { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, + { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 892e1482f9..788e77bcb6 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -6,10 +6,13 @@ // // Hardware interface for audio DSP on i.MX8M +#include #include +#include #include #include #include +#include #include #include @@ -17,11 +20,32 @@ #include #include "../ops.h" +#include "../sof-of-dev.h" #include "imx-common.h" #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 +static struct clk_bulk_data imx8m_dsp_clks[] = { + { .id = "ipg" }, + { .id = "ocram" }, + { .id = "core" }, +}; + +/* DAP registers */ +#define IMX8M_DAP_DEBUG 0x28800000 +#define IMX8M_DAP_DEBUG_SIZE (64 * 1024) +#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020) +#define IMX8M_PWRCTL_CORERESET BIT(16) + +/* DSP audio mix registers */ +#define AudioDSP_REG0 0x100 +#define AudioDSP_REG1 0x104 +#define AudioDSP_REG2 0x108 +#define AudioDSP_REG3 0x10c + +#define AudioDSP_REG2_RUNSTALL BIT(5) + struct imx8m_priv { struct device *dev; struct snd_sof_dev *sdev; @@ -29,42 +53,13 @@ struct imx8m_priv { /* DSP IPC handler */ struct imx_dsp_ipc *dsp_ipc; struct platform_device *ipc_dev; + + struct imx_clocks *clks; + + void __iomem *dap; + struct regmap *regmap; }; -static void imx8m_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -81,8 +76,7 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - imx8m_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -96,7 +90,7 @@ static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) /* Check to see if the message is a panic code (0x0dead***) */ if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) - snd_sof_dsp_panic(priv->sdev, p); + snd_sof_dsp_panic(priv->sdev, p, true); else snd_sof_ipc_msgs_rx(priv->sdev); } @@ -122,7 +116,34 @@ static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8m_run(struct snd_sof_dev *sdev) { - /* TODO: start DSP using Audio MIX bits */ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + + regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0); + + return 0; +} + +static int imx8m_reset(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + u32 pwrctl; + + /* put DSP into reset and stall */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl |= IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + + /* keep reset asserted for 10 cycles */ + usleep_range(1, 2); + + regmap_update_bits(priv->regmap, AudioDSP_REG2, + AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL); + + /* take the DSP out of reset and keep stalled for FW loading */ + pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL); + pwrctl &= ~IMX8M_PWRCTL_CORERESET; + writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL); + return 0; } @@ -142,6 +163,11 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; + priv->clks = devm_kzalloc(&pdev->dev, sizeof(*priv->clks), GFP_KERNEL); + if (!priv->clks) + return -ENOMEM; + + sdev->num_cores = 1; sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -174,6 +200,13 @@ static int imx8m_probe(struct snd_sof_dev *sdev) goto exit_pdev_unregister; } + priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE); + if (!priv->dap) { + dev_err(sdev->dev, "error: failed to map DAP debug memory area"); + ret = -ENODEV; + goto exit_pdev_unregister; + } + sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size); if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) { dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n", @@ -209,6 +242,25 @@ static int imx8m_probe(struct snd_sof_dev *sdev) /* set default mailbox offset for FW ready message */ sdev->dsp_box.offset = MBOX_OFFSET; + priv->regmap = syscon_regmap_lookup_by_compatible("fsl,dsp-ctrl"); + if (IS_ERR(priv->regmap)) { + dev_err(sdev->dev, "cannot find dsp-ctrl registers"); + ret = PTR_ERR(priv->regmap); + goto exit_pdev_unregister; + } + + /* init clocks info */ + priv->clks->dsp_clks = imx8m_dsp_clks; + priv->clks->num_dsp_clks = ARRAY_SIZE(imx8m_dsp_clks); + + ret = imx8_parse_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + goto exit_pdev_unregister; + return 0; exit_pdev_unregister: @@ -220,6 +272,7 @@ static int imx8m_remove(struct snd_sof_dev *sdev) { struct imx8m_priv *priv = sdev->pdata->hw_pdata; + imx8_disable_clocks(sdev, priv->clks); platform_device_unregister(priv->ipc_dev); return 0; @@ -238,21 +291,18 @@ static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type) } } -static void imx8m_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz) -{ - sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); -} - -static int imx8m_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply) -{ - return 0; -} - static struct snd_soc_dai_driver imx8m_dai[] = { +{ + .name = "sai1", + .playback = { + .channels_min = 1, + .channels_max = 32, + }, + .capture = { + .channels_min = 1, + .channels_max = 32, + }, +}, { .name = "sai3", .playback = { @@ -266,20 +316,116 @@ static struct snd_soc_dai_driver imx8m_dai[] = { }, }; +static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev, + const struct sof_dsp_power_state *target_state) +{ + sdev->dsp_power_state = *target_state; + + return 0; +} + +static int imx8m_resume(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int ret; + int i; + + ret = imx8_enable_clocks(sdev, priv->clks); + if (ret < 0) + return ret; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_request_channel(priv->dsp_ipc, i); + + return 0; +} + +static void imx8m_suspend(struct snd_sof_dev *sdev) +{ + struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata; + int i; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) + imx_dsp_free_channel(priv->dsp_ipc, i); + + imx8_disable_clocks(sdev, priv->clks); +} + +static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D3, + }; + + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + const struct sof_dsp_power_state target_dsp_state = { + .state = SOF_DSP_PM_D0, + }; + + ret = imx8m_resume(sdev); + if (ret < 0) + return ret; + + if (pm_runtime_suspended(sdev->dev)) { + pm_runtime_disable(sdev->dev); + pm_runtime_set_active(sdev->dev); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_enable(sdev->dev); + pm_runtime_idle(sdev->dev); + } + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + +static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state) +{ + const struct sof_dsp_power_state target_dsp_state = { + .state = target_state, + }; + + if (!pm_runtime_suspended(sdev->dev)) + imx8m_suspend(sdev); + + return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); +} + /* i.MX8 ops */ -struct snd_sof_dsp_ops sof_imx8m_ops = { +static const struct snd_sof_dsp_ops sof_imx8m_ops = { /* probe and remove */ .probe = imx8m_probe, .remove = imx8m_remove, /* DSP core boot */ .run = imx8m_run, + .reset = imx8m_reset, /* Block IO */ .block_read = sof_block_read, .block_write = sof_block_write, - /* Module IO */ - .read64 = sof_io_read64, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, /* ipc */ .send_msg = imx8m_send_msg, @@ -287,8 +433,8 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .get_mailbox_offset = imx8m_get_mailbox_offset, .get_window_offset = imx8m_get_window_offset, - .ipc_msg_data = imx8m_ipc_msg_data, - .ipc_pcm_params = imx8m_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -298,21 +444,58 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { /* Debug information */ .dbg_dump = imx8_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + /* stream callbacks */ + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* Firmware ops */ - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, /* DAI drivers */ .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), + .suspend = imx8m_dsp_suspend, + .resume = imx8m_dsp_resume, + + .runtime_suspend = imx8m_dsp_runtime_suspend, + .runtime_resume = imx8m_dsp_runtime_resume, + + .set_power_state = imx8m_dsp_set_power_state, + .hw_info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; -EXPORT_SYMBOL(sof_imx8m_ops); + +static struct sof_dev_desc sof_of_imx8mp_desc = { + .default_fw_path = "imx/sof", + .default_tplg_path = "imx/sof-tplg", + .default_fw_filename = "sof-imx8m.ri", + .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", + .ops = &sof_imx8m_ops, +}; + +static const struct of_device_id sof_of_imx8m_ids[] = { + { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, + { } +}; +MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids); + +/* DT driver definition */ +static struct platform_driver snd_sof_of_imx8m_driver = { + .probe = sof_of_probe, + .remove = sof_of_remove, + .driver = { + .name = "sof-audio-of-imx8m", + .pm = &sof_of_pm, + .of_match_table = sof_of_imx8m_ids, + }, +}; +module_platform_driver(snd_sof_of_imx8m_driver); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index feae487f02..1f473d4d84 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -3,13 +3,11 @@ snd-sof-acpi-intel-byt-objs := byt.o snd-sof-acpi-intel-bdw-objs := bdw.o -snd-sof-intel-ipc-objs := intel-ipc.o - snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-bus.o \ apl.o cnl.o tgl.o icl.o -snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o +snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o snd-sof-intel-hda-objs := hda-codec.o @@ -18,7 +16,6 @@ snd-sof-intel-atom-objs := atom.o obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o -obj-$(CONFIG_SND_SOC_SOF_INTEL_HIFI_EP_IPC) += snd-sof-intel-ipc.o obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index c7ed2b3d6a..810b8b6748 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -42,6 +42,10 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_thread = hda_dsp_ipc_irq_thread, @@ -65,6 +69,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .debug_map_count = ARRAY_SIZE(apl_dsp_debugfs), .dbg_dump = hda_dsp_dump, .ipc_dump = hda_ipc_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ .pcm_open = hda_dsp_pcm_open, @@ -73,6 +78,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -96,9 +102,8 @@ const struct snd_sof_dsp_ops sof_apl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* trace callback */ .trace_init = hda_dsp_trace_init, @@ -125,7 +130,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_apl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -142,5 +147,6 @@ const struct sof_intel_dsp_desc apl_chip_info = { .rom_init_timeout = 150, .ssp_count = APL_SSP_COUNT, .ssp_base_offset = APL_SSP_BASE_OFFSET, + .quirks = SOF_INTEL_PROCEN_FMT_QUIRK, }; EXPORT_SYMBOL_NS(apl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index d8804efede..ff5900b155 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -27,7 +27,6 @@ static void atom_host_done(struct snd_sof_dev *sdev); static void atom_dsp_done(struct snd_sof_dev *sdev); -static void atom_get_reply(struct snd_sof_dev *sdev); /* * Debug @@ -71,8 +70,8 @@ void atom_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX); atom_get_registers(sdev, &xoops, &panic_info, stack, STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX); @@ -154,8 +153,7 @@ irqreturn_t atom_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - atom_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); atom_dsp_done(sdev); @@ -167,8 +165,8 @@ irqreturn_t atom_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -195,45 +193,6 @@ int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) } EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -static void atom_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - int atom_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -283,11 +242,8 @@ int atom_run(struct snd_sof_dev *sdev) break; msleep(100); } - if (tries < 0) { - dev_err(sdev->dev, "error: unable to run DSP firmware\n"); - atom_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + if (tries < 0) return -ENODEV; - } /* return init core mask */ return 1; @@ -337,7 +293,7 @@ static const char *fixup_tplg_name(struct snd_sof_dev *sdev, return tplg_filename; } -void atom_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -348,7 +304,7 @@ void atom_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } pdev = to_platform_device(sdev->dev); @@ -366,12 +322,13 @@ void atom_machine_select(struct snd_sof_dev *sdev) if (!tplg_filename) { dev_dbg(sdev->dev, "error: no topology filename\n"); - return; + return NULL; } sof_pdata->tplg_filename = tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); @@ -446,14 +403,14 @@ struct snd_soc_dai_driver atom_dai[] = { }; EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; diff --git a/sound/soc/sof/intel/atom.h b/sound/soc/sof/intel/atom.h index 96a462c7a2..b965e5e080 100644 --- a/sound/soc/sof/intel/atom.h +++ b/sound/soc/sof/intel/atom.h @@ -65,8 +65,8 @@ int atom_run(struct snd_sof_dev *sdev); int atom_reset(struct snd_sof_dev *sdev); void atom_dump(struct snd_sof_dev *sdev, u32 flags); -void atom_machine_select(struct snd_sof_dev *sdev); -void atom_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev); +void atom_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); extern struct snd_soc_dai_driver atom_dai[]; diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 89a6c1f04a..d627b7498d 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -75,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = { static void bdw_host_done(struct snd_sof_dev *sdev); static void bdw_dsp_done(struct snd_sof_dev *sdev); -static void bdw_get_reply(struct snd_sof_dev *sdev); /* * DSP Control. @@ -259,8 +258,8 @@ static void bdw_dump(struct snd_sof_dev *sdev, u32 flags) panic = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IPCX); bdw_get_registers(sdev, &xoops, &panic_info, stack, BDW_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, - BDW_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops, + &panic_info, stack, BDW_STACK_DUMP_SIZE); /* provide some context for firmware debug */ imrx = snd_sof_dsp_read(sdev, BDW_DSP_BAR, SHIM_IMRX); @@ -326,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) * because the done bit can't be set in cmd_done function * which is triggered by msg */ - bdw_get_reply(sdev); - snd_sof_ipc_reply(sdev, ipcx); + snd_sof_ipc_process_reply(sdev, ipcx); bdw_dsp_done(sdev); @@ -346,8 +344,8 @@ static irqreturn_t bdw_irq_thread(int irq, void *context) /* Handle messages from DSP Core */ if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + - MBOX_OFFSET); + snd_sof_dsp_panic(sdev, BDW_PANIC_OFFSET(ipcx) + MBOX_OFFSET, + true); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -372,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) return 0; } -static void bdw_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - /* - * Sometimes, there is unexpected reply ipc arriving. The reply - * ipc belongs to none of the ipcs sent from driver. - * In this case, the driver must ignore the ipc. - */ - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev) { return MBOX_OFFSET; @@ -453,10 +412,19 @@ static int bdw_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* LPE base */ mmio = platform_get_resource(pdev, IORESOURCE_MEM, desc->resindex_lpe_base); @@ -535,13 +503,13 @@ static int bdw_probe(struct snd_sof_dev *sdev) return ret; } - /* set default mailbox */ - snd_sof_dsp_mailbox_init(sdev, MBOX_OFFSET, MBOX_SIZE, 0, 0); + /* set default mailbox offset for FW ready message */ + sdev->dsp_box.offset = MBOX_OFFSET; return ret; } -static void bdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *bdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -550,22 +518,23 @@ static void bdw_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (!mach) { dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); - return; + return NULL; } sof_pdata->tplg_filename = mach->sof_tplg_filename; mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc; - sof_pdata->machine = mach; + + return mach; } -static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach, +static void bdw_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; @@ -616,14 +585,18 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* ipc */ .send_msg = bdw_send_msg, .fw_ready = sof_fw_ready, .get_mailbox_offset = bdw_get_mailbox_offset, .get_window_offset = bdw_get_window_offset, - .ipc_msg_data = intel_ipc_msg_data, - .ipc_pcm_params = intel_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* machine driver */ .machine_select = bdw_machine_select, @@ -635,10 +608,11 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .debug_map = bdw_debugfs, .debug_map_count = ARRAY_SIZE(bdw_debugfs), .dbg_dump = bdw_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ - .pcm_open = intel_pcm_open, - .pcm_close = intel_pcm_close, + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* Module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -657,7 +631,7 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; static const struct sof_intel_dsp_desc bdw_chip_info = { diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 8edaf6fdd2..dcfeaedb8f 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -113,10 +113,19 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) const struct sof_dev_desc *desc = pdata->desc; struct platform_device *pdev = container_of(sdev->dev, struct platform_device, dev); + const struct sof_intel_dsp_desc *chip; struct resource *mmio; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(sdev->dev, DMA_BIT_MASK(31)); if (ret < 0) { @@ -226,6 +235,10 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_handler = atom_irq_handler, .irq_thread = atom_irq_thread, @@ -236,8 +249,8 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, - .ipc_msg_data = intel_ipc_msg_data, - .ipc_pcm_params = intel_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* machine driver */ .machine_select = atom_machine_select, @@ -249,10 +262,11 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .debug_map = byt_debugfs, .debug_map_count = ARRAY_SIZE(byt_debugfs), .dbg_dump = atom_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ - .pcm_open = intel_pcm_open, - .pcm_close = intel_pcm_close, + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -275,7 +289,7 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; static const struct sof_intel_dsp_desc byt_chip_info = { @@ -303,6 +317,10 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_handler = atom_irq_handler, .irq_thread = atom_irq_thread, @@ -313,8 +331,8 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, - .ipc_msg_data = intel_ipc_msg_data, - .ipc_pcm_params = intel_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* machine driver */ .machine_select = atom_machine_select, @@ -326,10 +344,11 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .debug_map = cht_debugfs, .debug_map_count = ARRAY_SIZE(cht_debugfs), .dbg_dump = atom_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ - .pcm_open = intel_pcm_open, - .pcm_close = intel_pcm_close, + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -353,7 +372,7 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; static const struct sof_intel_dsp_desc cht_chip_info = { diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index e115e12a85..e615125d57 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -82,9 +82,24 @@ irqreturn_t cnl_ipc_irq_thread(int irq, void *context) msg, msg_ext); /* handle messages from DSP */ - if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == - SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { snd_sof_ipc_msgs_rx(sdev); } @@ -247,6 +262,10 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_thread = cnl_ipc_irq_thread, @@ -270,6 +289,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .debug_map_count = ARRAY_SIZE(cnl_dsp_debugfs), .dbg_dump = hda_dsp_dump, .ipc_dump = cnl_ipc_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ .pcm_open = hda_dsp_pcm_open, @@ -278,6 +298,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -298,9 +319,8 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware, @@ -330,7 +350,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_cnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -353,6 +373,13 @@ const struct sof_intel_dsp_desc cnl_chip_info = { }; EXPORT_SYMBOL_NS(cnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); +/* + * JasperLake is technically derived from IceLake, and should be in + * described in icl.c. However since JasperLake was designed with + * two cores, it cannot support the IceLake-specific power-up sequences + * which rely on core3. To simplify, JasperLake uses the CannonLake ops and + * is described in cnl.c + */ const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ .cores_num = 2, diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 6744318de6..2f3f4a733d 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -20,6 +20,8 @@ #include "../../codecs/hdac_hda.h" #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#define CODEC_PROBE_RETRIES 3 + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define IDISP_VID_INTEL 0x80860000 @@ -121,12 +123,15 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; u32 resp = -1; - int ret; + int ret, retry = 0; + + do { + mutex_lock(&hbus->core.cmd_mutex); + snd_hdac_bus_send_cmd(&hbus->core, hda_cmd); + snd_hdac_bus_get_response(&hbus->core, address, &resp); + mutex_unlock(&hbus->core.cmd_mutex); + } while (resp == -1 && retry++ < CODEC_PROBE_RETRIES); - mutex_lock(&hbus->core.cmd_mutex); - snd_hdac_bus_send_cmd(&hbus->core, hda_cmd); - snd_hdac_bus_get_response(&hbus->core, address, &resp); - mutex_unlock(&hbus->core.cmd_mutex); if (resp == -1) return -EIO; dev_dbg(sdev->dev, "HDA codec #%d probed OK: response: %x\n", diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index fa5f0a7189..0c29bb196e 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -353,7 +353,7 @@ void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) snd_hdac_bus_stop_cmd_io(bus); #endif /* disable position buffer */ - if (bus->posbuf.addr) { + if (bus->use_posbuf && bus->posbuf.addr) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, 0); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 6704dbcd10..cd12589355 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -16,9 +16,11 @@ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) +#include "../sof-probes.h" +#endif + struct hda_pipe_params { - u8 host_dma_id; - u8 link_dma_id; u32 ch; u32 s_freq; u32 s_fmt; @@ -26,7 +28,6 @@ struct hda_pipe_params { snd_pcm_format_t format; int link_index; int stream; - unsigned int host_bps; unsigned int link_bps; }; @@ -152,49 +153,50 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream, return 0; } -/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */ -static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, - const char *dai_name, int channel, int dir) +/* Update config for the DAI widget */ +static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w, + int channel) { + struct snd_sof_widget *swidget = w->dobj.private; struct sof_ipc_dai_config *config; struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; - int ret = 0; - list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { - if (!sof_dai->cpu_dai_name) - continue; + if (!swidget) + return NULL; - if (!strcmp(dai_name, sof_dai->cpu_dai_name) && - dir == sof_dai->comp_dai.direction) { - config = sof_dai->dai_config; + sof_dai = swidget->private; - if (!config) { - dev_err(hda_stream->sdev->dev, - "error: no config for DAI %s\n", - sof_dai->name); - return -EINVAL; - } - - /* update config with stream tag */ - config->hda.link_dma_ch = channel; - - /* send IPC */ - ret = sof_ipc_tx_message(hda_stream->sdev->ipc, - config->hdr.cmd, - config, - config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) - dev_err(hda_stream->sdev->dev, - "error: failed to set dai config for %s\n", - sof_dai->name); - return ret; - } + if (!sof_dai || !sof_dai->dai_config) { + dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name); + return NULL; } - return -EINVAL; + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* update config with stream tag */ + config->hda.link_dma_ch = channel; + + return config; +} + +static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, + struct snd_soc_dapm_widget *w, + int channel, bool widget_setup) +{ + struct snd_sof_dev *sdev = hda_stream->sdev; + struct sof_ipc_dai_config *config; + + config = hda_dai_update_config(w, channel); + if (!config) { + dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); + return -ENOENT; + } + + /* set up/free DAI widget and send DAI_CONFIG IPC */ + if (widget_setup) + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); + + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); } static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -208,6 +210,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct sof_intel_hda_stream *hda_stream; struct hda_pipe_params p_params = {0}; + struct snd_soc_dapm_widget *w; struct hdac_ext_link *link; int stream_tag; int ret; @@ -226,9 +229,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); - /* update the DSP with the new tag */ - ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1, - substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + + /* set up the DAI widget and send the DAI_CONFIG with the new tag */ + ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true); if (ret < 0) return ret; @@ -236,17 +243,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, if (!link) return -EINVAL; - /* set the stream tag in the codec dai dma params */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); - else - snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); p_params.s_freq = params_rate(params); p_params.stream = substream->stream; - p_params.link_dma_id = stream_tag - 1; p_params.link_index = link->index; p_params.format = params_format(params); @@ -277,6 +280,36 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, dai); } +static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", w->name); + return -EINVAL; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* set PAUSE command flag */ + config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); + + return ret; +} + static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -284,6 +317,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, snd_soc_dai_get_dma_data(dai, substream); struct sof_intel_hda_stream *hda_stream; struct snd_soc_pcm_runtime *rtd; + struct snd_soc_dapm_widget *w; struct hdac_ext_link *link; struct hdac_stream *hstream; struct hdac_bus *bus; @@ -301,29 +335,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); - switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - /* set up hw_params */ - ret = hda_link_pcm_prepare(substream, dai); - if (ret < 0) { - dev_err(dai->dev, - "error: setting up hw_params during resume\n"); - return ret; - } - fallthrough; + w = snd_soc_dai_get_widget(dai, substream->stream); + + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_dev); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: + snd_hdac_ext_link_stream_clear(link_dev); + /* - * clear link DMA channel. It will be assigned when - * hw_params is set up again after resume. + * free DAI widget during stop/suspend to keep widget use_count's balanced. */ - ret = hda_link_config_ipc(hda_stream, dai->name, - DMA_CHAN_INVALID, substream->stream); + ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); if (ret < 0) return ret; @@ -333,10 +360,13 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, } link_dev->link_prepared = 0; - - fallthrough; + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_hdac_ext_link_stream_clear(link_dev); + + ret = hda_link_dai_config_pause_push_ipc(w); + if (ret < 0) + return ret; break; default: return -EINVAL; @@ -354,6 +384,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, struct hdac_stream *hstream; struct snd_soc_pcm_runtime *rtd; struct hdac_ext_stream *link_dev; + struct snd_soc_dapm_widget *w; int ret; hstream = substream->runtime->private_data; @@ -369,9 +400,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, hda_stream = hstream_to_sof_hda_stream(link_dev); - /* free the link DMA channel in the FW */ - ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID, - substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + + /* free the link DMA channel in the FW and the DAI widget */ + ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); if (ret < 0) return ret; @@ -401,61 +436,131 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = { .prepare = hda_link_pcm_prepare, }; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) -#include "../compress.h" +#endif -static struct snd_soc_cdai_ops sof_probe_compr_ops = { - .startup = sof_probe_compr_open, - .shutdown = sof_probe_compr_free, - .set_params = sof_probe_compr_set_params, - .trigger = sof_probe_compr_trigger, - .pointer = sof_probe_compr_pointer, +/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */ +struct ssp_dai_dma_data { + bool setup; }; -#endif -#endif - -static int ssp_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, + bool setup) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; - int ret; + struct snd_soc_component *component; + struct snd_sof_widget *swidget; + struct snd_soc_dapm_widget *w; + struct sof_ipc_fw_version *v; + struct snd_sof_dev *sdev; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + w = dai->playback_widget; + else + w = dai->capture_widget; + + swidget = w->dobj.private; + component = swidget->scomp; + sdev = snd_soc_component_get_drvdata(component); + v = &sdev->fw_ready.version; /* DAI_CONFIG IPC during hw_params is not supported in older firmware */ if (v->abi_version < SOF_ABI_VER(3, 18, 0)) return 0; - list_for_each_entry(sof_dai, &sdev->dai_list, list) { - if (!sof_dai->cpu_dai_name || !sof_dai->dai_config) - continue; + if (setup) + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); - if (!strcmp(dai->name, sof_dai->cpu_dai_name) && - substream->stream == sof_dai->comp_dai.direction) { - config = &sof_dai->dai_config[sof_dai->current_config]; + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); +} - /* send IPC */ - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, - config->hdr.size, &reply, sizeof(reply)); +static int ssp_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssp_dai_dma_data *dma_data; - if (ret < 0) - dev_err(sdev->dev, "error: failed to set DAI config for %s\n", - sof_dai->name); - return ret; - } - } + dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); + if (!dma_data) + return -ENOMEM; + + snd_soc_dai_set_dma_data(dai, substream, dma_data); return 0; } +static int ssp_dai_setup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + bool setup) +{ + struct ssp_dai_dma_data *dma_data; + int ret = 0; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + if (!dma_data) { + dev_err(dai->dev, "%s: failed to get dma_data\n", __func__); + return -EIO; + } + + if (dma_data->setup != setup) { + ret = ssp_dai_setup_or_free(substream, dai, setup); + if (!ret) + dma_data->setup = setup; + } + return ret; +} + +static int ssp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + /* params are ignored for now */ + return ssp_dai_setup(substream, dai, true); +} + +static int ssp_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * the SSP will only be reconfigured during resume operations and + * not in case of xruns + */ + return ssp_dai_setup(substream, dai, true); +} + +static int ssp_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + if (cmd != SNDRV_PCM_TRIGGER_SUSPEND) + return 0; + + return ssp_dai_setup(substream, dai, false); +} + +static int ssp_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return ssp_dai_setup(substream, dai, false); +} + +static void ssp_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssp_dai_dma_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + if (!dma_data) { + dev_err(dai->dev, "%s: failed to get dma_data\n", __func__); + return; + } + snd_soc_dai_set_dma_data(dai, substream, NULL); + kfree(dma_data); +} + static const struct snd_soc_dai_ops ssp_dai_ops = { + .startup = ssp_dai_startup, .hw_params = ssp_dai_hw_params, + .prepare = ssp_dai_prepare, + .trigger = ssp_dai_trigger, + .hw_free = ssp_dai_hw_free, + .shutdown = ssp_dai_shutdown, }; /* diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 262a70791a..916a257ea9 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -34,7 +34,7 @@ MODULE_PARM_DESC(enable_trace_D0I3_S0, * DSP Core control. */ -int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) { u32 adspcs; u32 reset; @@ -73,7 +73,7 @@ int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) { unsigned int crst; u32 adspcs; @@ -113,7 +113,7 @@ int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) { /* stall core */ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, @@ -125,6 +125,31 @@ int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask) return hda_dsp_core_reset_enter(sdev, core_mask); } +static bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + int val; + bool is_enable; + + val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); + +#define MASK_IS_EQUAL(v, m, field) ({ \ + u32 _m = field(m); \ + ((v) & _m) == _m; \ +}) + + is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) && + MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) && + !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && + !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + +#undef MASK_IS_EQUAL + + dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", + is_enable, core_mask); + + return is_enable; +} + int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) { int ret; @@ -156,7 +181,7 @@ int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask) * Power Management. */ -int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) { unsigned int cpa; u32 adspcs; @@ -195,7 +220,7 @@ int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) +static int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { u32 adspcs; int ret; @@ -218,32 +243,6 @@ int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) return ret; } -bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, - unsigned int core_mask) -{ - int val; - bool is_enable; - - val = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS); - -#define MASK_IS_EQUAL(v, m, field) ({ \ - u32 _m = field(m); \ - ((v) & _m) == _m; \ -}) - - is_enable = MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_CPA_MASK) && - MASK_IS_EQUAL(val, core_mask, HDA_DSP_ADSPCS_SPA_MASK) && - !(val & HDA_DSP_ADSPCS_CRST_MASK(core_mask)) && - !(val & HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); - -#undef MASK_IS_EQUAL - - dev_dbg(sdev->dev, "DSP core(s) enabled? %d : core_mask %x\n", - is_enable, core_mask); - - return is_enable; -} - int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -615,7 +614,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) struct hdac_bus *bus = sof_to_bus(sdev); #endif - int ret; + int ret, j; hda_sdw_int_enable(sdev, false); @@ -630,13 +629,17 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif /* power down DSP */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); if (ret < 0) { dev_err(sdev->dev, "error: failed to power down core during suspend\n"); return ret; } + /* reset ref counts for all cores */ + for (j = 0; j < chip->cores_num; j++) + sdev->dsp_core_ref_count[j] = 0; + /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); @@ -963,3 +966,47 @@ void hda_dsp_d0i3_work(struct work_struct *work) "error: failed to set DSP state %d substate %d\n", target_state.state, target_state.substate); } + +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + int ret, ret1; + + /* power up core */ + ret = hda_dsp_enable_core(sdev, BIT(core)); + if (ret < 0) { + dev_err(sdev->dev, "failed to power up core %d with err: %d\n", + core, ret); + return ret; + } + + /* No need to send IPC for primary core or if FW boot is not complete */ + if (sdev->fw_state != SOF_FW_BOOT_COMPLETE || core == SOF_DSP_PRIMARY_CORE) + return 0; + + /* Now notify DSP for secondary cores */ + ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); + if (ret < 0) { + dev_err(sdev->dev, "failed to enable secondary core '%d' failed with %d\n", + core, ret); + goto power_down; + } + + return ret; + +power_down: + /* power down core if it is host managed and return the original error if this fails too */ + ret1 = hda_dsp_core_reset_power_down(sdev, BIT(core)); + if (ret1 < 0) + dev_err(sdev->dev, "failed to power down core: %d with err: %d\n", core, ret1); + + return ret; +} diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index acfeca4260..f0cf8019d7 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -70,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) struct snd_sof_ipc_msg *msg = sdev->msg; struct sof_ipc_reply reply; struct sof_ipc_cmd_hdr *hdr; - int ret = 0; /* * Sometimes, there is unexpected reply ipc arriving. The reply @@ -94,35 +93,11 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) reply.hdr.cmd = SOF_IPC_GLB_REPLY; reply.hdr.size = sizeof(reply); memcpy(msg->reply_data, &reply, sizeof(reply)); - goto out; - } - /* get IPC reply from DSP in the mailbox */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, - sizeof(reply)); - - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; + msg->reply_error = 0; } else { - /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size && - /* getter payload is never known upfront */ - ((reply.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_PROBE)) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); + snd_sof_ipc_get_reply(sdev); } - -out: - msg->reply_error = ret; - } /* IPC handler thread */ @@ -198,8 +173,23 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) /* handle messages from DSP */ if ((hipct & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { - /* this is a PANIC message !! */ - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + bool non_recoverable = true; + + /* + * This is a PANIC message! + * + * If it is arriving during firmware boot and it is not + * the last boot attempt then change the non_recoverable + * to false as the DSP might be able to boot in the next + * iteration(s) + */ + if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS && + hda->boot_iteration < HDA_FW_BOOT_ATTEMPTS) + non_recoverable = false; + + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext), + non_recoverable); } else { /* normal message - process normally */ snd_sof_ipc_msgs_rx(sdev); @@ -253,9 +243,9 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) return SRAM_WINDOW_OFFSET(id); } -void hda_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz) +int hda_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz) { if (!substream || !sdev->stream_box.size) { sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz); @@ -268,10 +258,13 @@ void hda_ipc_msg_data(struct snd_sof_dev *sdev, hda_stream.hstream); /* The stream might already be closed */ - if (hstream) - sof_mailbox_read(sdev, hda_stream->stream.posn_offset, - p, sz); + if (!hstream) + return -ESTRPIPE; + + sof_mailbox_read(sdev, hda_stream->stream.posn_offset, p, sz); } + + return 0; } int hda_ipc_pcm_params(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 6f4771bf9d..33306d2023 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -23,7 +23,6 @@ #include "../ops.h" #include "hda.h" -#define HDA_FW_BOOT_ATTEMPTS 3 #define HDA_CL_STREAM_FORMAT 0x40 static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, @@ -88,12 +87,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; unsigned int status; - u32 flags; + unsigned long mask; + char *dump_msg; + u32 flags, j; int ret; int i; /* step 1: power up corex */ - ret = snd_sof_dsp_core_power_up(sdev, chip->host_managed_cores_mask); + ret = hda_dsp_enable_core(sdev, chip->host_managed_cores_mask); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); @@ -148,8 +149,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) chip->ipc_ack_mask); /* step 5: power down cores that are no longer needed */ - ret = snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask & - ~(chip->init_core_mask)); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask & + ~(chip->init_core_mask)); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -168,8 +169,14 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) HDA_DSP_REG_POLL_INTERVAL_US, chip->rom_init_timeout * USEC_PER_MSEC); - if (!ret) + if (!ret) { + /* set enabled cores mask and increment ref count for cores in init_core_mask */ + sdev->enabled_cores_mask |= chip->init_core_mask; + mask = sdev->enabled_cores_mask; + for_each_set_bit(j, &mask, SOF_MAX_DSP_NUM_CORES) + sdev->dsp_core_ref_count[j]++; return 0; + } if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -177,15 +184,18 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) __func__); err: - flags = SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX; + flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL; - /* force error log level after max boot attempts */ + /* after max boot attempts make sure that the dump is printed */ if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) - flags |= SOF_DBG_DUMP_FORCE_ERR_LEVEL; + flags &= ~SOF_DBG_DUMP_OPTIONAL; - hda_dsp_dump(sdev, flags); - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d", + hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS); + snd_sof_dsp_dbg_dump(sdev, dump_msg, flags); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); + kfree(dump_msg); return ret; } @@ -407,17 +417,19 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) hda_sdw_process_wakeen(sdev); /* - * at this point DSP ROM has been initialized and - * should be ready for code loading and firmware boot + * Set the boot_iteration to the last attempt, indicating that the + * DSP ROM has been initialized and from this point there will be no + * retry done to boot. + * + * Continue with code loading and firmware boot */ + hda->boot_iteration = HDA_FW_BOOT_ATTEMPTS; ret = cl_copy_fw(sdev, stream); - if (!ret) { + if (!ret) dev_dbg(sdev->dev, "Firmware download successful, booting...\n"); - } else { - hda_dsp_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | - SOF_DBG_DUMP_FORCE_ERR_LEVEL); - dev_err(sdev->dev, "error: load fw failed ret: %d\n", ret); - } + else + snd_sof_dsp_dbg_dump(sdev, "Firmware download failed", + SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX); cleanup: /* @@ -475,46 +487,6 @@ int hda_dsp_post_fw_run(struct snd_sof_dev *sdev) return hda_dsp_ctrl_clock_power_gating(sdev, true); } -/* - * post fw run operations for ICL, - * Core 3 will be powered up and in stall when HPRO is enabled - */ -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - int ret; - - if (sdev->first_boot) { - ret = hda_sdw_startup(sdev); - if (ret < 0) { - dev_err(sdev->dev, - "error: could not startup SoundWire links\n"); - return ret; - } - } - - hda_sdw_int_enable(sdev, true); - - /* - * The recommended HW programming sequence for ICL is to - * power up core 3 and keep it in stall if HPRO is enabled. - * Major difference between ICL and TGL, on ICL core 3 is managed by - * the host whereas on TGL it is handled by the firmware. - */ - if (!hda->clk_config_lpro) { - ret = snd_sof_dsp_core_power_up(sdev, BIT(3)); - if (ret < 0) { - dev_err(sdev->dev, "error: dsp core power up failed on core 3\n"); - return ret; - } - - snd_sof_dsp_stall(sdev, BIT(3)); - } - - /* re-enable clock gating and power gating */ - return hda_dsp_ctrl_clock_power_gating(sdev, true); -} - int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, const struct sof_ext_man_elem_header *hdr) { @@ -552,24 +524,3 @@ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, return 0; } - -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask) -{ - struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - const struct sof_intel_dsp_desc *chip = hda->desc; - - /* make sure core_mask in host managed cores */ - core_mask &= chip->host_managed_cores_mask; - if (!core_mask) { - dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); - return -EINVAL; - } - - /* stall core */ - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPCS, - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), - HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); - - return 0; -} diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index cc8ddef37f..d78aa5d855 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -32,6 +32,10 @@ static bool hda_always_enable_dmi_l1; module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444); MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1"); +static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS); +module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); +MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); + u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate) { switch (rate) { @@ -120,8 +124,11 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return ret; } - /* disable SPIB, to enable buffer wrap for stream */ - hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + /* enable SPIB when rewinds are disabled */ + if (hda_disable_rewinds) + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_ENABLE, 0); + else + hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); /* update no_stream_position flag for ipc params */ if (hda && hda->no_ipc_position) { @@ -140,6 +147,29 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, return 0; } +/* update SPIB register with appl position */ +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +{ + struct hdac_stream *hstream = substream->runtime->private_data; + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + struct snd_pcm_runtime *runtime = substream->runtime; + ssize_t appl_pos, buf_size; + u32 spib; + + appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + + spib = appl_pos % buf_size; + + /* Allowable value for SPIB is 1 byte to max buffer size */ + if (!spib) + spib = buf_size; + + sof_io_write(sdev, hext_stream->spib_addr, spib); + + return 0; +} + int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd) { @@ -172,38 +202,74 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, goto found; } - /* - * DPIB/posbuf position mode: - * For Playback, Use DPIB register from HDA space which - * reflects the actual data transferred. - * For Capture, Use the position buffer for pointer, as DPIB - * is not accurate enough, its update may be completed - * earlier than the data written to DDR. - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (sof_hda_position_quirk) { + case SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY: + /* + * This legacy code, inherited from the Skylake driver, + * mixes DPIB registers and DPIB DDR updates and + * does not seem to follow any known hardware recommendations. + * It's not clear e.g. why there is a different flow + * for capture and playback, the only information that matters is + * what traffic class is used, and on all SOF-enabled platforms + * only VC0 is supported so the work-around was likely not necessary + * and quite possibly wrong. + */ + + /* DPIB/posbuf position mode: + * For Playback, Use DPIB register from HDA space which + * reflects the actual data transferred. + * For Capture, Use the position buffer for pointer, as DPIB + * is not accurate enough, its update may be completed + * earlier than the data written to DDR. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + } else { + /* + * For capture stream, we need more workaround to fix the + * position incorrect issue: + * + * 1. Wait at least 20us before reading position buffer after + * the interrupt generated(IOC), to make sure position update + * happens on frame boundary i.e. 20.833uSec for 48KHz. + * 2. Perform a dummy Read to DPIB register to flush DMA + * position value. + * 3. Read the DMA Position from posbuf. Now the readback + * value should be >= period boundary. + */ + usleep_range(20, 21); + snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + AZX_REG_VS_SDXDPIB_XBASE + + (AZX_REG_VS_SDXDPIB_XINTERVAL * + hstream->index)); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + } + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS: + /* + * In case VC1 traffic is disabled this is the recommended option + */ pos = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, AZX_REG_VS_SDXDPIB_XBASE + (AZX_REG_VS_SDXDPIB_XINTERVAL * hstream->index)); - } else { + break; + case SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE: /* - * For capture stream, we need more workaround to fix the - * position incorrect issue: - * - * 1. Wait at least 20us before reading position buffer after - * the interrupt generated(IOC), to make sure position update - * happens on frame boundary i.e. 20.833uSec for 48KHz. - * 2. Perform a dummy Read to DPIB register to flush DMA - * position value. - * 3. Read the DMA Position from posbuf. Now the readback - * value should be >= period boundary. + * This is the recommended option when VC1 is enabled. + * While this isn't needed for SOF platforms it's added for + * consistency and debug. */ - usleep_range(20, 21); - snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - AZX_REG_VS_SDXDPIB_XBASE + - (AZX_REG_VS_SDXDPIB_XINTERVAL * - hstream->index)); pos = snd_hdac_stream_get_pos_posbuf(hstream); + break; + default: + dev_err_once(sdev->dev, "hda_position_quirk value %d not supported\n", + sof_hda_position_quirk); + pos = 0; + break; } if (pos >= hstream->bufsize) @@ -234,6 +300,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, return -EINVAL; } + /* + * if we want the .ack to work, we need to prevent the control from being mapped. + * The status can still be mapped. + */ + if (hda_disable_rewinds) + runtime->hw.info |= SNDRV_PCM_INFO_NO_REWINDS | SNDRV_PCM_INFO_SYNC_APPLPTR; + /* * All playback streams are DMI L1 capable, capture streams need * pause push/release to be disabled diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 63c367478f..ba60807fbd 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -25,6 +25,33 @@ #define HDA_LTRP_GB_VALUE_US 95 +static inline const char *hda_hstream_direction_str(struct hdac_stream *hstream) +{ + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + return "Playback"; + else + return "Capture"; +} + +static char *hda_hstream_dbg_get_stream_info_str(struct hdac_stream *hstream) +{ + struct snd_soc_pcm_runtime *rtd; + + if (hstream->substream) + rtd = asoc_substream_to_rtd(hstream->substream); + else if (hstream->cstream) + rtd = hstream->cstream->private_data; + else + /* Non audio DMA user, like dma-trace */ + return kasprintf(GFP_KERNEL, "-- (%s, stream_tag: %u)", + hda_hstream_direction_str(hstream), + hstream->stream_tag); + + return kasprintf(GFP_KERNEL, "dai_link \"%s\" (%s, stream_tag: %u)", + rtd->dai_link->name, hda_hstream_direction_str(hstream), + hstream->stream_tag); +} + /* * set up one of BDL entries for a stream */ @@ -89,13 +116,13 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, int remain, ioc; period_bytes = stream->period_bytes; - dev_dbg(sdev->dev, "period_bytes:0x%x\n", period_bytes); + dev_dbg(sdev->dev, "%s: period_bytes:0x%x\n", __func__, period_bytes); if (!period_bytes) period_bytes = stream->bufsize; periods = stream->bufsize / period_bytes; - dev_dbg(sdev->dev, "periods:%d\n", periods); + dev_dbg(sdev->dev, "%s: periods:%d\n", __func__, periods); remain = stream->bufsize % period_bytes; if (remain) @@ -244,25 +271,64 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag) HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN); if (!found) { - dev_dbg(sdev->dev, "stream_tag %d not opened!\n", stream_tag); + dev_dbg(sdev->dev, "%s: stream_tag %d not opened!\n", + __func__, stream_tag); return -ENODEV; } return 0; } +static int hda_dsp_stream_reset(struct snd_sof_dev *sdev, struct hdac_stream *hstream) +{ + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 val; + + /* enter stream reset */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, + SOF_STREAM_SD_OFFSET_CRST); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if (val & SOF_STREAM_SD_OFFSET_CRST) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream reset\n"); + return -ETIMEDOUT; + } + + timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + + /* exit stream reset and wait to read a zero before reading any other register */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_STREAM_SD_OFFSET_CRST, 0x0); + + /* wait for hardware to report that stream is out of reset */ + udelay(3); + do { + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, sd_offset); + if ((val & SOF_STREAM_SD_OFFSET_CRST) == 0) + break; + } while (--timeout); + if (timeout == 0) { + dev_err(sdev->dev, "timeout waiting for stream to exit reset\n"); + return -ETIMEDOUT; + } + + return 0; +} + int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd) { struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); u32 dma_start = SOF_HDA_SD_CTL_DMA_START; - int ret; + int ret = 0; u32 run; /* cmd must be for audio stream */ switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_START: snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, @@ -283,14 +349,9 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret < 0) { - dev_err(sdev->dev, - "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", - __func__, cmd); - return ret; - } + if (ret >= 0) + hstream->running = true; - hstream->running = true; break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -306,27 +367,32 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_STREAM_RUN_TIMEOUT); - if (ret < 0) { - dev_err(sdev->dev, - "error: %s: cmd %d: timeout on STREAM_SD_OFFSET read\n", - __func__, cmd); - return ret; + if (ret >= 0) { + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); + + hstream->running = false; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + SOF_HDA_INTCTL, + 1 << hstream->index, 0x0); } - - snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK); - - hstream->running = false; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - 1 << hstream->index, 0x0); break; default: dev_err(sdev->dev, "error: unknown command: %d\n", cmd); return -EINVAL; } - return 0; + if (ret < 0) { + char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); + + dev_err(sdev->dev, + "%s: cmd %d on %s: timeout on STREAM_SD_OFFSET read\n", + __func__, cmd, stream_name ? stream_name : "unknown stream"); + kfree(stream_name); + } + + return ret; } /* minimal recommended programming for ICCMAX stream */ @@ -405,12 +471,13 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params) { + const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + int ret; u32 dma_start = SOF_HDA_SD_CTL_DMA_START; - u32 val, mask; + u32 mask; u32 run; if (!stream) { @@ -440,9 +507,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, HDA_DSP_STREAM_RUN_TIMEOUT); if (ret < 0) { + char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); + dev_err(sdev->dev, - "error: %s: timeout on STREAM_SD_OFFSET read1\n", - __func__); + "%s: on %s: timeout on STREAM_SD_OFFSET read1\n", + __func__, stream_name ? stream_name : "unknown stream"); + kfree(stream_name); return ret; } @@ -452,36 +522,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_CL_DMA_SD_INT_MASK); /* stream reset */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x1); - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if (val & 0x1) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: stream reset failed\n"); - return -ETIMEDOUT; - } - - timeout = HDA_DSP_STREAM_RESET_TIMEOUT; - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, 0x1, - 0x0); - - /* wait for hardware to report that stream is out of reset */ - udelay(3); - do { - val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, - sd_offset); - if ((val & 0x1) == 0) - break; - } while (--timeout); - if (timeout == 0) { - dev_err(sdev->dev, "error: timeout waiting for stream reset\n"); - return -ETIMEDOUT; - } + ret = hda_dsp_stream_reset(sdev, hstream); + if (ret < 0) + return ret; if (hstream->posbuf) *hstream->posbuf = 0; @@ -506,9 +549,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, HDA_DSP_STREAM_RUN_TIMEOUT); if (ret < 0) { + char *stream_name = hda_hstream_dbg_get_stream_info_str(hstream); + dev_err(sdev->dev, - "error: %s: timeout on STREAM_SD_OFFSET read2\n", - __func__); + "%s: on %s: timeout on STREAM_SD_OFFSET read1\n", + __func__, stream_name ? stream_name : "unknown stream"); + kfree(stream_name); return ret; } @@ -538,6 +584,7 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, /* * Recommended hardware programming sequence for HDAudio DMA format + * on earlier platforms - this is not needed on newer platforms * * 1. Put DMA into coupled mode by clearing PPCTL.PROCEN bit * for corresponding stream index before the time of writing @@ -547,9 +594,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, * enable decoupled mode */ - /* couple host and link DMA, disable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, 0); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* couple host and link DMA, disable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, 0); + } /* program stream format */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -557,9 +606,11 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, SOF_HDA_ADSP_REG_CL_SD_FORMAT, 0xffff, hstream->format_val); - /* decouple host and link DMA, enable DSP features */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, - mask, mask); + if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) { + /* decouple host and link DMA, enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + } /* program last valid index */ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, @@ -574,9 +625,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, upper_32_bits(hstream->bdl.addr)); - /* enable position buffer */ - if (!(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) - & SOF_HDA_ADSP_DPLBASE_ENABLE)) { + /* enable position buffer, if needed */ + if (bus->use_posbuf && bus->posbuf.addr && + !(snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE) + & SOF_HDA_ADSP_DPLBASE_ENABLE)) { snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPUBASE, upper_32_bits(bus->posbuf.addr)); snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_ADSP_DPLBASE, @@ -613,6 +665,11 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, hstream); struct hdac_bus *bus = sof_to_bus(sdev); u32 mask = 0x1 << stream->index; + int ret; + + ret = hda_dsp_stream_reset(sdev, stream); + if (ret < 0) + return ret; spin_lock_irq(&bus->reg_lock); /* couple host and link DMA if link DMA channel is idle */ @@ -621,6 +678,8 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, SOF_HDA_REG_PP_PPCTL, mask, 0); spin_unlock_irq(&bus->reg_lock); + hda_dsp_stream_spib_config(sdev, link_dev, HDA_DSP_SPIB_DISABLE, 0); + stream->substream = NULL; return 0; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index ef92cca7ae..1385695d77 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,15 +41,100 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 -static const struct sof_intel_dsp_desc - *get_chip_info(struct snd_sof_pdata *pdata) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) { - const struct sof_dev_desc *desc = pdata->desc; - const struct sof_intel_dsp_desc *chip_info; + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; - chip_info = desc->chip_info; + sof_dai = swidget->private; - return chip_info; + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", w->name); + return -EINVAL; + } + + /* DAI already configured, reset it before reconfiguring it */ + if (sof_dai->configured) { + ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + if (ret < 0) + return ret; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* + * For static pipelines, the DAI widget would already be set up and calling + * sof_widget_setup() simply returns without doing anything. + * For dynamic pipelines, the DAI widget will be set up now. + */ + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) { + dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name); + return ret; + } + + /* set HW_PARAMS flag along with quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + + + /* send DAI_CONFIG IPC */ + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name); + return ret; + } + + sof_dai->configured = true; + + return 0; +} + +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret; + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name); + return -EINVAL; + } + + /* nothing to do if hw_free() is called without restarting the stream after resume. */ + if (!sof_dai->configured) + return 0; + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* set HW_FREE flag along with any quirks */ + config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name); + + /* + * Reset the configured_flag and free the widget even if the IPC fails to keep + * the widget use_count balanced + */ + sof_dai->configured = false; + + return sof_widget_free(sdev, swidget); } #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) @@ -64,36 +149,49 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET; module_param(sdw_clock_stop_quirks, int, 0444); MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); +static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget *w, + int link_id, int alh_stream_id, int dai_id, bool setup) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + + if (!swidget) { + dev_err(sdev->dev, "error: No private data for widget %s\n", w->name); + return -EINVAL; + } + + sof_dai = swidget->private; + + if (!sof_dai || !sof_dai->dai_config) { + dev_err(sdev->dev, "error: No config for DAI %s\n", w->name); + return -EINVAL; + } + + config = &sof_dai->dai_config[sof_dai->current_config]; + + /* update config with link and stream ID */ + config->dai_index = (link_id << 8) | dai_id; + config->alh.stream_id = alh_stream_id; + + if (setup) + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); + + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); +} + static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = params_data->dai; - struct sof_ipc_dai_config config; - struct sof_ipc_reply reply; - int link_id = params_data->link_id; - int alh_stream_id = params_data->alh_stream_id; - int ret; - u32 size = sizeof(config); + struct snd_soc_dapm_widget *w; - memset(&config, 0, size); - config.hdr.size = size; - config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config.type = SOF_DAI_INTEL_ALH; - config.dai_index = (link_id << 8) | (d->id); - config.alh.stream_id = alh_stream_id; + w = snd_soc_dai_get_widget(d, params_data->stream); - /* send message to DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - config.hdr.cmd, &config, size, &reply, - sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", - link_id, d->id, alh_stream_id); - } - - return ret; + return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, + d->id, true); } static int sdw_free_stream(struct device *dev, @@ -101,30 +199,12 @@ static int sdw_free_stream(struct device *dev, { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = free_data->dai; - struct sof_ipc_dai_config config; - struct sof_ipc_reply reply; - int link_id = free_data->link_id; - int ret; - u32 size = sizeof(config); + struct snd_soc_dapm_widget *w; - memset(&config, 0, size); - config.hdr.size = size; - config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config.type = SOF_DAI_INTEL_ALH; - config.dai_index = (link_id << 8) | d->id; - config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ + w = snd_soc_dai_get_widget(d, free_data->stream); - /* send message to DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - config.hdr.cmd, &config, size, &reply, - sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to free stream for link %d dai->id %d\n", - link_id, d->id); - } - - return ret; + /* send invalid stream_id */ + return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); } static const struct sdw_intel_ops sdw_callback = { @@ -294,7 +374,38 @@ void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) sdw_intel_process_wakeen_event(hdev->sdw); } -#endif +#else /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ +static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_probe(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline int hda_sdw_exit(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) +{ + return IRQ_HANDLED; +} + +static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + return false; +} + +#endif /* IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) */ /* * Debug @@ -313,6 +424,10 @@ MODULE_PARM_DESC(use_msi, "SOF HDA use PCI MSI mode"); #define hda_use_msi (1) #endif +int sof_hda_position_quirk = SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS; +module_param_named(position_quirk, sof_hda_position_quirk, int, 0444); +MODULE_PARM_DESC(position_quirk, "SOF HDaudio position quirk"); + static char *hda_model; module_param(hda_model, charp, 0444); MODULE_PARM_DESC(hda_model, "Use the given HDA board model."); @@ -351,7 +466,7 @@ static const struct hda_dsp_msg_code hda_dsp_rom_msg[] = { {HDA_DSP_ROM_NULL_FW_ENTRY, "error: null FW entry point"}, }; -static void hda_dsp_get_status(struct snd_sof_dev *sdev) +static void hda_dsp_get_status(struct snd_sof_dev *sdev, const char *level) { u32 status; int i; @@ -361,8 +476,8 @@ static void hda_dsp_get_status(struct snd_sof_dev *sdev) for (i = 0; i < ARRAY_SIZE(hda_dsp_rom_msg); i++) { if (status == hda_dsp_rom_msg[i].code) { - dev_err(sdev->dev, "%s - code %8.8x\n", - hda_dsp_rom_msg[i].msg, status); + dev_printk(level, sdev->dev, "%s - code %8.8x\n", + hda_dsp_rom_msg[i].msg, status); return; } } @@ -400,7 +515,8 @@ static void hda_dsp_get_registers(struct snd_sof_dev *sdev, } /* dump the first 8 dwords representing the extended ROM status */ -static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) +static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, const char *level, + u32 flags) { char msg[128]; int len = 0; @@ -412,31 +528,30 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev, u32 flags) len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); } - sof_dev_dbg_or_err(sdev->dev, flags & SOF_DBG_DUMP_FORCE_ERR_LEVEL, - "extended rom status: %s", msg); + dev_printk(level, sdev->dev, "extended rom status: %s", msg); } void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { + char *level = flags & SOF_DBG_DUMP_OPTIONAL ? KERN_DEBUG : KERN_ERR; struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; /* print ROM/FW status */ - hda_dsp_get_status(sdev); + hda_dsp_get_status(sdev, level); - /* print panic info if FW boot is complete. Otherwise, print the extended ROM status */ - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { + if (flags & SOF_DBG_DUMP_REGS) { u32 status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_STATUS); u32 panic = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_FW_TRACEP); hda_dsp_get_registers(sdev, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); - snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, - stack, HDA_DSP_STACK_DUMP_SIZE); + sof_print_oops_and_stack(sdev, level, status, panic, &xoops, + &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); } else { - hda_dsp_dump_ext_rom_status(sdev, flags); + hda_dsp_dump_ext_rom_status(sdev, level, flags); } } @@ -456,12 +571,9 @@ void hda_ipc_irq_dump(struct snd_sof_dev *sdev) ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS); rirbsts = snd_hdac_chip_readb(bus, RIRBSTS); - dev_err(sdev->dev, - "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n", + dev_err(sdev->dev, "hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n", intsts, intctl, rirbsts); - dev_err(sdev->dev, - "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", - ppsts, adspis); + dev_err(sdev->dev, "dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", ppsts, adspis); } void hda_ipc_dump(struct snd_sof_dev *sdev) @@ -479,8 +591,7 @@ void hda_ipc_dump(struct snd_sof_dev *sdev) /* dump the IPC regs */ /* TODO: parse the raw msg */ - dev_err(sdev->dev, - "error: host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n", + dev_err(sdev->dev, "host status 0x%8.8x dsp status 0x%8.8x mask 0x%8.8x\n", hipcie, hipct, hipcctl); } @@ -497,7 +608,10 @@ static int hda_init(struct snd_sof_dev *sdev) /* HDA bus init */ sof_hda_bus_init(bus, &pci->dev); - bus->use_posbuf = 1; + if (sof_hda_position_quirk == SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS) + bus->use_posbuf = 0; + else + bus->use_posbuf = 1; bus->bdl_pos_adj = 0; bus->sync_write = 1; @@ -794,6 +908,8 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) goto err; } + sdev->num_cores = chip->cores_num; + hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) return -ENOMEM; @@ -840,6 +956,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "DMA mask is 32 bit\n"); dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)); } + dma_set_max_seg_size(&pci->dev, UINT_MAX); /* init streams */ ret = hda_dsp_stream_init(sdev); @@ -929,9 +1046,9 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) int hda_dsp_remove(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; struct hdac_bus *bus = sof_to_bus(sdev); struct pci_dev *pci = to_pci_dev(sdev->dev); - const struct sof_intel_dsp_desc *chip = hda->desc; /* cancel any attempt for DSP D0I3 */ cancel_delayed_work_sync(&hda->d0i3_work); @@ -956,7 +1073,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) /* disable cores */ if (chip) - snd_sof_dsp_core_power_down(sdev, chip->host_managed_cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); /* disable DSP */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, @@ -983,7 +1100,8 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { struct hdac_bus *bus = sof_to_bus(sdev); struct snd_soc_acpi_mach_params *mach_params; @@ -1015,7 +1133,7 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) * - one HDMI codec, and/or * - one external HDAudio codec */ - if (!pdata->machine && codec_num <= 2) { + if (!*mach && codec_num <= 2) { hda_mach = snd_soc_acpi_intel_hda_machines; dev_info(bus->dev, "using HDA machine driver %s now\n", @@ -1030,10 +1148,9 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) tplg_filename = hda_mach->sof_tplg_filename; ret = dmic_topology_fixup(sdev, &tplg_filename, idisp_str, &dmic_num); if (ret < 0) - return ret; + return; hda_mach->mach_params.dmic_num = dmic_num; - pdata->machine = hda_mach; pdata->tplg_filename = tplg_filename; if (codec_num == 2) { @@ -1043,23 +1160,22 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) */ hda_mach->mach_params.link_mask = 0; } + + *mach = hda_mach; } } /* used by hda machine driver to create dai links */ - if (pdata->machine) { - mach_params = (struct snd_soc_acpi_mach_params *) - &pdata->machine->mach_params; + if (*mach) { + mach_params = &(*mach)->mach_params; mach_params->codec_mask = bus->codec_mask; mach_params->common_hdmi_codec_drv = hda_codec_use_common_hdmi; } - - return 0; } #else -static int hda_generic_machine_select(struct snd_sof_dev *sdev) +static void hda_generic_machine_select(struct snd_sof_dev *sdev, + struct snd_soc_acpi_mach **mach) { - return 0; } #endif @@ -1142,7 +1258,7 @@ static bool link_slaves_found(struct snd_sof_dev *sdev, return true; } -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct snd_soc_acpi_link_adr *link; @@ -1160,7 +1276,7 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) * machines, for mixed cases with I2C/I2S the detection relies * on the HID list. */ - if (link_mask && !pdata->machine) { + if (link_mask) { for (mach = pdata->desc->alt_machines; mach && mach->link_mask; mach++) { /* @@ -1195,7 +1311,6 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) if (mach && mach->link_mask) { int dmic_num = 0; - pdata->machine = mach; mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; mach->mach_params.platform = dev_name(sdev->dev); @@ -1217,9 +1332,8 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) int ret; ret = dmic_topology_fixup(sdev, &tplg_filename, "", &dmic_num); - if (ret < 0) - return ret; + return NULL; pdata->tplg_filename = tplg_filename; } @@ -1229,35 +1343,36 @@ static int hda_sdw_machine_select(struct snd_sof_dev *sdev) "SoundWire machine driver %s topology %s\n", mach->drv_name, pdata->tplg_filename); - } else { - dev_info(sdev->dev, - "No SoundWire machine driver found\n"); + + return mach; } + + dev_info(sdev->dev, "No SoundWire machine driver found\n"); } - return 0; + return NULL; } #else -static int hda_sdw_machine_select(struct snd_sof_dev *sdev) +static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { - return 0; + return NULL; } #endif -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct snd_soc_acpi_mach_params *mach_params; - mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params; + mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } -void hda_machine_select(struct snd_sof_dev *sdev) +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *sof_pdata = sdev->pdata; const struct sof_dev_desc *desc = sof_pdata->desc; @@ -1272,8 +1387,6 @@ void hda_machine_select(struct snd_sof_dev *sdev) if (!sof_pdata->tplg_filename) sof_pdata->tplg_filename = mach->sof_tplg_filename; - sof_pdata->machine = mach; - if (mach->link_mask) { mach->mach_params.links = mach->links; mach->mach_params.link_mask = mach->link_mask; @@ -1283,16 +1396,18 @@ void hda_machine_select(struct snd_sof_dev *sdev) /* * If I2S fails, try SoundWire */ - hda_sdw_machine_select(sdev); + if (!mach) + mach = hda_sdw_machine_select(sdev); /* * Choose HDA generic machine driver if mach is NULL. * Otherwise, set certain mach params. */ - hda_generic_machine_select(sdev); - - if (!sof_pdata->machine) + hda_generic_machine_select(sdev, &mach); + if (!mach) dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n"); + + return mach; } int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 4fdfb10864..03a6bb7a16 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -273,7 +273,7 @@ #define BXT_D0I3_DELAY 5000 #define FW_CL_STREAM_NUMBER 0x1 -#define HDA_FW_BOOT_ATTEMPTS 3 +#define HDA_FW_BOOT_ATTEMPTS 3 /* ADSPCS - Audio DSP Control & Status */ @@ -487,24 +487,18 @@ struct sof_intel_hda_stream { (SOF_HDA_ADSP_SD_ENTRY_SIZE * ((s)->index) \ + SOF_HDA_ADSP_LOADER_BASE) +#define SOF_STREAM_SD_OFFSET_CRST 0x1 + /* * DSP Core services. */ int hda_dsp_probe(struct snd_sof_dev *sdev); int hda_dsp_remove(struct snd_sof_dev *sdev); -int hda_dsp_core_reset_enter(struct snd_sof_dev *sdev, - unsigned int core_mask); -int hda_dsp_core_reset_leave(struct snd_sof_dev *sdev, - unsigned int core_mask); -int hda_dsp_core_stall_reset(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_core_run(struct snd_sof_dev *sdev, unsigned int core_mask); -int hda_dsp_core_power_up(struct snd_sof_dev *sdev, unsigned int core_mask); int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask); -int hda_dsp_core_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); -bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, - unsigned int core_mask); int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask); +int hda_dsp_core_get(struct snd_sof_dev *sdev, int core); void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev); void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev); @@ -542,6 +536,7 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); +int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* * DSP Stream Operations. @@ -572,9 +567,9 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int enable, u32 size); -void hda_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz); +int hda_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz); int hda_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, const struct sof_ipc_pcm_params_reply *reply); @@ -619,14 +614,10 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); */ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); -int hda_dsp_cl_boot_firmware_iccmax_icl(struct snd_sof_dev *sdev); -int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev); /* pre and post fw run ops */ int hda_dsp_pre_fw_run(struct snd_sof_dev *sdev); int hda_dsp_post_fw_run(struct snd_sof_dev *sdev); -int hda_dsp_post_fw_run_icl(struct snd_sof_dev *sdev); -int hda_dsp_core_stall_icl(struct snd_sof_dev *sdev, unsigned int core_mask); /* parse platform specific ext manifest ops */ int hda_dsp_ext_man_get_cavs_config_data(struct snd_sof_dev *sdev, @@ -695,45 +686,15 @@ bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev); #else -static inline int hda_sdw_acpi_scan(struct snd_sof_dev *sdev) -{ - return 0; -} - -static inline int hda_sdw_probe(struct snd_sof_dev *sdev) -{ - return 0; -} - static inline int hda_sdw_startup(struct snd_sof_dev *sdev) { return 0; } -static inline int hda_sdw_exit(struct snd_sof_dev *sdev) -{ - return 0; -} - static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { } -static inline bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev) -{ - return false; -} - -static inline irqreturn_t hda_dsp_sdw_thread(int irq, void *context) -{ - return IRQ_HANDLED; -} - -static inline bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) -{ - return false; -} - static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { } @@ -767,11 +728,22 @@ extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; /* machine driver select */ -void hda_machine_select(struct snd_sof_dev *sdev); -void hda_set_mach_params(const struct snd_soc_acpi_mach *mach, +struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); +void hda_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* PCI driver selection and probe */ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id); +struct snd_sof_dai; +struct sof_ipc_dai_config; +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); + +#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ +#define SOF_HDA_POSITION_QUIRK_USE_DPIB_DDR_UPDATE (2) /* recommended with VC0 or VC1 */ + +extern int sof_hda_position_quirk; + #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index ee095b8f2d..f75e398396 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -18,12 +18,75 @@ #include "hda-ipc.h" #include "../sof-audio.h" +#define ICL_DSP_HPRO_CORE_ID 3 + static const struct snd_sof_debugfs_map icl_dsp_debugfs[] = { {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int icl_dsp_core_stall(struct snd_sof_dev *sdev, unsigned int core_mask) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; + + /* make sure core_mask in host managed cores */ + core_mask &= chip->host_managed_cores_mask; + if (!core_mask) { + dev_err(sdev->dev, "error: core_mask is not in host managed cores\n"); + return -EINVAL; + } + + /* stall core */ + snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPCS, + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask), + HDA_DSP_ADSPCS_CSTALL_MASK(core_mask)); + + return 0; +} + +/* + * post fw run operation for ICL. + * Core 3 will be powered up and in stall when HPRO is enabled + */ +static int icl_dsp_post_fw_run(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + int ret; + + if (sdev->first_boot) { + ret = hda_sdw_startup(sdev); + if (ret < 0) { + dev_err(sdev->dev, "error: could not startup SoundWire links\n"); + return ret; + } + } + + hda_sdw_int_enable(sdev, true); + + /* + * The recommended HW programming sequence for ICL is to + * power up core 3 and keep it in stall if HPRO is enabled. + */ + if (!hda->clk_config_lpro) { + ret = hda_dsp_enable_core(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + if (ret < 0) { + dev_err(sdev->dev, "error: dsp core power up failed on core %d\n", + ICL_DSP_HPRO_CORE_ID); + return ret; + } + + sdev->enabled_cores_mask |= BIT(ICL_DSP_HPRO_CORE_ID); + sdev->dsp_core_ref_count[ICL_DSP_HPRO_CORE_ID]++; + + snd_sof_dsp_stall(sdev, BIT(ICL_DSP_HPRO_CORE_ID)); + } + + /* re-enable clock gating and power gating */ + return hda_dsp_ctrl_clock_power_gating(sdev, true); +} + /* Icelake ops */ const struct snd_sof_dsp_ops sof_icl_ops = { /* probe/remove/shutdown */ @@ -41,6 +104,10 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_thread = cnl_ipc_irq_thread, @@ -64,6 +131,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .debug_map_count = ARRAY_SIZE(icl_dsp_debugfs), .dbg_dump = hda_dsp_dump, .ipc_dump = cnl_ipc_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ .pcm_open = hda_dsp_pcm_open, @@ -72,6 +140,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -87,18 +156,17 @@ const struct snd_sof_dsp_ops sof_icl_ops = { /* pre/post fw run */ .pre_fw_run = hda_dsp_pre_fw_run, - .post_fw_run = hda_dsp_post_fw_run_icl, + .post_fw_run = icl_dsp_post_fw_run, /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = hda_dsp_core_get, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, - .stall = hda_dsp_core_stall_icl, + .stall = icl_dsp_core_stall, /* trace callback */ .trace_init = hda_dsp_trace_init, @@ -125,7 +193,7 @@ const struct snd_sof_dsp_ops sof_icl_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_icl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index f89e746c25..a023b3cc0a 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -26,7 +26,6 @@ static const struct sof_dev_desc bxt_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &apl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -42,7 +41,6 @@ static const struct sof_dev_desc glk_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &apl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index f23257adf2..40cf1cd000 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -27,7 +27,6 @@ static const struct sof_dev_desc cnl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -44,7 +43,6 @@ static const struct sof_dev_desc cfl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -61,7 +59,6 @@ static const struct sof_dev_desc cml_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &cnl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index 2f60c28ae8..39c84121b3 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -27,7 +27,6 @@ static const struct sof_dev_desc icl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &icl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -43,7 +42,6 @@ static const struct sof_dev_desc jsl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &jsl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index beb2fb3cd0..fd46210f17 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -27,7 +27,6 @@ static const struct sof_dev_desc tgl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &tgl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -44,7 +43,6 @@ static const struct sof_dev_desc tglh_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &tglh_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -60,7 +58,6 @@ static const struct sof_dev_desc ehl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &ehl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -77,7 +74,6 @@ static const struct sof_dev_desc adls_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &adls_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", @@ -94,7 +90,6 @@ static const struct sof_dev_desc adl_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = -1, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &tgl_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 4bded668b6..f8c841caa3 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -55,9 +55,18 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) struct snd_sof_pdata *pdata = sdev->pdata; const struct sof_dev_desc *desc = pdata->desc; struct pci_dev *pci = to_pci_dev(sdev->dev); + const struct sof_intel_dsp_desc *chip; u32 base, size; int ret; + chip = get_chip_info(sdev->pdata); + if (!chip) { + dev_err(sdev->dev, "error: no such device supported\n"); + return -EIO; + } + + sdev->num_cores = chip->cores_num; + /* DSP DMA can only access low 31 bits of host memory */ ret = dma_coerce_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31)); if (ret < 0) { @@ -142,6 +151,10 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_handler = atom_irq_handler, .irq_thread = atom_irq_thread, @@ -152,8 +165,8 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .get_mailbox_offset = atom_get_mailbox_offset, .get_window_offset = atom_get_window_offset, - .ipc_msg_data = intel_ipc_msg_data, - .ipc_pcm_params = intel_ipc_pcm_params, + .ipc_msg_data = sof_ipc_msg_data, + .ipc_pcm_params = sof_ipc_pcm_params, /* machine driver */ .machine_select = atom_machine_select, @@ -165,10 +178,11 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .debug_map = tng_debugfs, .debug_map_count = ARRAY_SIZE(tng_debugfs), .dbg_dump = atom_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ - .pcm_open = intel_pcm_open, - .pcm_close = intel_pcm_close, + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, /* module loading */ .load_module = snd_sof_parse_module_memcpy, @@ -187,7 +201,7 @@ const struct snd_sof_dsp_ops sof_tng_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_BATCH, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; const struct sof_intel_dsp_desc tng_chip_info = { @@ -201,7 +215,6 @@ static const struct sof_dev_desc tng_desc = { .resindex_pcicfg_base = -1, .resindex_imr_base = 0, .irqindex_host_ipc = -1, - .resindex_dma_base = -1, .chip_info = &tng_chip_info, .default_fw_path = "intel/sof", .default_tplg_path = "intel/sof-tplg", diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index e9f7d4d7fc..f36cd9d5eb 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -151,6 +151,9 @@ #define PCI_PMCS 0x84 #define PCI_PMCS_PS_MASK 0x3 +/* Intel quirks */ +#define SOF_INTEL_PROCEN_FMT_QUIRK BIT(0) + /* DSP hardware descriptor */ struct sof_intel_dsp_desc { int cores_num; @@ -166,6 +169,7 @@ struct sof_intel_dsp_desc { int ssp_base_offset; /* base address of the SSPs */ u32 sdw_shim_base; u32 sdw_alh_base; + u32 quirks; bool (*check_sdw_irq)(struct snd_sof_dev *sdev); }; @@ -177,4 +181,11 @@ struct sof_intel_stream { size_t posn_offset; }; +static inline const struct sof_intel_dsp_desc *get_chip_info(struct snd_sof_pdata *pdata) +{ + const struct sof_dev_desc *desc = pdata->desc; + + return desc->chip_info; +} + #endif diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 199d41a7dc..7f7929c5cb 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -20,6 +20,46 @@ static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = { {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, }; +static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + + /* power up primary core if not already powered up and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_enable_core(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + +static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask & ~BIT(core), + }; + + /* power down primary core and return */ + if (core == SOF_DSP_PRIMARY_CORE) + return hda_dsp_core_reset_power_down(sdev, BIT(core)); + + /* notify DSP for secondary cores */ + return sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); +} + /* Tigerlake ops */ const struct snd_sof_dsp_ops sof_tgl_ops = { /* probe/remove/shutdown */ @@ -37,6 +77,10 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Mailbox IO */ + .mailbox_read = sof_mailbox_read, + .mailbox_write = sof_mailbox_write, + /* doorbell */ .irq_thread = cnl_ipc_irq_thread, @@ -60,6 +104,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs), .dbg_dump = hda_dsp_dump, .ipc_dump = cnl_ipc_dump, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* stream callbacks */ .pcm_open = hda_dsp_pcm_open, @@ -68,6 +113,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) /* probe callbacks */ @@ -88,9 +134,9 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { /* parse platform specific extended manifest */ .parse_platform_ext_manifest = hda_dsp_ext_man_get_cavs_config_data, - /* dsp core power up/down */ - .core_power_up = hda_dsp_enable_core, - .core_power_down = hda_dsp_core_reset_power_down, + /* dsp core get/put */ + .core_get = tgl_dsp_core_get, + .core_put = tgl_dsp_core_put, /* firmware run */ .run = hda_dsp_cl_boot_firmware_iccmax, @@ -120,7 +166,7 @@ const struct snd_sof_dsp_ops sof_tgl_ops = { SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .arch_ops = &sof_xtensa_arch_ops, + .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index c2d07b783f..5bcf906d90 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -18,7 +18,7 @@ #include "sof-audio.h" #include "ops.h" -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id); +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type); static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd); /* @@ -173,7 +173,22 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) } break; case SOF_IPC_GLB_TRACE_MSG: - str = "GLB_TRACE_MSG"; break; + str = "GLB_TRACE_MSG"; + switch (type) { + case SOF_IPC_TRACE_DMA_PARAMS: + str2 = "DMA_PARAMS"; break; + case SOF_IPC_TRACE_DMA_POSITION: + str2 = "DMA_POSITION"; break; + case SOF_IPC_TRACE_DMA_PARAMS_EXT: + str2 = "DMA_PARAMS_EXT"; break; + case SOF_IPC_TRACE_FILTER_UPDATE: + str2 = "FILTER_UPDATE"; break; + case SOF_IPC_TRACE_DMA_FREE: + str2 = "DMA_FREE"; break; + default: + str2 = "unknown type"; break; + } + break; case SOF_IPC_GLB_TEST_MSG: str = "GLB_TEST_MSG"; switch (type) { @@ -192,6 +207,29 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) str2 = "unknown type"; break; } break; + case SOF_IPC_GLB_PROBE: + str = "GLB_PROBE"; + switch (type) { + case SOF_IPC_PROBE_INIT: + str2 = "INIT"; break; + case SOF_IPC_PROBE_DEINIT: + str2 = "DEINIT"; break; + case SOF_IPC_PROBE_DMA_ADD: + str2 = "DMA_ADD"; break; + case SOF_IPC_PROBE_DMA_INFO: + str2 = "DMA_INFO"; break; + case SOF_IPC_PROBE_DMA_REMOVE: + str2 = "DMA_REMOVE"; break; + case SOF_IPC_PROBE_POINT_ADD: + str2 = "POINT_ADD"; break; + case SOF_IPC_PROBE_POINT_INFO: + str2 = "POINT_INFO"; break; + case SOF_IPC_PROBE_POINT_REMOVE: + str2 = "POINT_REMOVE"; break; + default: + str2 = "unknown type"; break; + } + break; default: str = "unknown GLB command"; break; } @@ -226,15 +264,17 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, msecs_to_jiffies(sdev->ipc_timeout)); if (ret == 0) { - dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n", - hdr->cmd, hdr->size); + dev_err(sdev->dev, + "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", + hdr->cmd, hdr->size, msg->reply_size); snd_sof_handle_fw_exception(ipc->sdev); ret = -ETIMEDOUT; } else { ret = msg->reply_error; if (ret < 0) { - dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n", - hdr->cmd, msg->reply_size); + dev_err(sdev->dev, + "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", + hdr->cmd, hdr->size, msg->reply_size, ret); } else { ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); if (msg->reply_size) @@ -242,6 +282,12 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, memcpy(reply_data, msg->reply_data, msg->reply_size); } + + /* re-enable dumps after successful IPC tx */ + if (sdev->ipc_dump_printed) { + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + } } return ret; @@ -256,7 +302,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, struct snd_sof_ipc_msg *msg; int ret; - if (ipc->disable_ipc_tx) + if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE) return -ENODEV; /* @@ -286,7 +332,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, spin_unlock_irq(&sdev->ipc_lock); - if (ret < 0) { + if (ret) { dev_err_ratelimited(sdev->dev, "error: ipc tx failed with error %d\n", ret); @@ -296,10 +342,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, ipc_log_header(sdev->dev, "ipc tx", msg->header); /* now wait for completion */ - if (!ret) - ret = tx_wait_done(ipc, msg, reply_data); - - return ret; + return tx_wait_done(ipc, msg, reply_data); } /* send IPC message from host to DSP */ @@ -351,6 +394,67 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, } EXPORT_SYMBOL(sof_ipc_tx_message_no_pm); +/* Generic helper function to retrieve the reply */ +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg = sdev->msg; + struct sof_ipc_reply reply; + int ret = 0; + + /* + * Sometimes, there is unexpected reply ipc arriving. The reply + * ipc belongs to none of the ipcs sent from driver. + * In this case, the driver must ignore the ipc. + */ + if (!msg) { + dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n"); + return; + } + + /* get the generic reply */ + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply, + sizeof(reply)); + + if (reply.error < 0) { + memcpy(msg->reply_data, &reply, sizeof(reply)); + ret = reply.error; + } else if (!reply.hdr.size) { + /* Reply should always be >= sizeof(struct sof_ipc_reply) */ + if (msg->reply_size) + dev_err(sdev->dev, + "empty reply received, expected %zu bytes\n", + msg->reply_size); + else + dev_err(sdev->dev, "empty reply received\n"); + + ret = -EINVAL; + } else if (msg->reply_size > 0) { + if (reply.hdr.size == msg->reply_size) { + ret = 0; + } else if (reply.hdr.size < msg->reply_size) { + dev_dbg(sdev->dev, + "reply size (%u) is less than expected (%zu)\n", + reply.hdr.size, msg->reply_size); + + msg->reply_size = reply.hdr.size; + ret = 0; + } else { + dev_err(sdev->dev, + "reply size (%u) exceeds the buffer size (%zu)\n", + reply.hdr.size, msg->reply_size); + ret = -EINVAL; + } + + /* get the full message if reply.hdr.size <= msg->reply_size */ + if (!ret) + snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, + msg->reply_data, msg->reply_size); + } + + msg->reply_error = ret; +} +EXPORT_SYMBOL(snd_sof_ipc_get_reply); + /* handle reply message from DSP */ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) { @@ -369,15 +473,52 @@ void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id) } EXPORT_SYMBOL(snd_sof_ipc_reply); +static void ipc_comp_notification(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr) +{ + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + struct sof_ipc_ctrl_data *cdata; + int ret; + + switch (msg_type) { + case SOF_IPC_COMP_GET_VALUE: + case SOF_IPC_COMP_GET_DATA: + cdata = kmalloc(hdr->size, GFP_KERNEL); + if (!cdata) + return; + + /* read back full message */ + ret = snd_sof_ipc_msg_data(sdev, NULL, cdata, hdr->size); + if (ret < 0) { + dev_err(sdev->dev, + "error: failed to read component event: %d\n", ret); + goto err; + } + break; + default: + dev_err(sdev->dev, "error: unhandled component message %#x\n", msg_type); + return; + } + + snd_sof_control_notify(sdev, cdata); + +err: + kfree(cdata); +} + /* DSP firmware has sent host a message */ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) { struct sof_ipc_cmd_hdr hdr; u32 cmd, type; - int err = 0; + int err; /* read back header */ - snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + if (err < 0) { + dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); + return; + } ipc_log_header(sdev->dev, "ipc rx", hdr.cmd); cmd = hdr.cmd & SOF_GLB_TYPE_MASK; @@ -393,9 +534,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { err = sof_ops(sdev)->fw_ready(sdev, cmd); if (err < 0) - sdev->fw_state = SOF_FW_BOOT_READY_FAILED; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED); else - sdev->fw_state = SOF_FW_BOOT_COMPLETE; + sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK); /* wake up firmware loader */ wake_up(&sdev->boot_wait); @@ -404,7 +545,9 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev) case SOF_IPC_GLB_COMPOUND: case SOF_IPC_GLB_TPLG_MSG: case SOF_IPC_GLB_PM_MSG: + break; case SOF_IPC_GLB_COMP_MSG: + ipc_comp_notification(sdev, &hdr); break; case SOF_IPC_GLB_STREAM_MSG: /* need to pass msg id into the function */ @@ -426,19 +569,22 @@ EXPORT_SYMBOL(snd_sof_ipc_msgs_rx); * IPC trace mechanism. */ -static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id) +static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_type) { struct sof_ipc_dma_trace_posn posn; + int ret; - switch (msg_id) { + switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: /* read back full message */ - snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); - snd_sof_trace_update_pos(sdev, &posn); + ret = snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn)); + if (ret < 0) + dev_warn(sdev->dev, "failed to read trace position: %d\n", ret); + else + snd_sof_trace_update_pos(sdev, &posn); break; default: - dev_err(sdev->dev, "error: unhandled trace message %x\n", - msg_id); + dev_err(sdev->dev, "error: unhandled trace message %#x\n", msg_type); break; } } @@ -453,7 +599,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; - int direction; + int direction, ret; spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { @@ -464,15 +610,22 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); + return; + } dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", posn.host_posn, posn.dai_posn, posn.wallclock); memcpy(&stream->posn, &posn, sizeof(posn)); - /* only inform ALSA for period_wakeup mode */ - if (!stream->substream->runtime->no_period_wakeup) + if (spcm->pcm.compress) + snd_sof_compr_fragment_elapsed(stream->cstream); + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) + /* only inform ALSA for period_wakeup mode */ snd_sof_pcm_period_elapsed(stream->substream); } @@ -483,7 +636,7 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; struct snd_sof_pcm *spcm; - int direction; + int direction, ret; spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); if (!spcm) { @@ -493,7 +646,11 @@ static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id) } stream = &spcm->stream[direction]; - snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); + return; + } dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", posn.host_posn, posn.xrun_comp_id, posn.xrun_size); @@ -520,7 +677,7 @@ static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd) ipc_xrun(sdev, msg_id); break; default: - dev_err(sdev->dev, "error: unhandled stream message %x\n", + dev_err(sdev->dev, "error: unhandled stream message %#x\n", msg_id); break; } @@ -565,11 +722,6 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, sparams->src = (u8 *)src->chanv; sparams->dst = (u8 *)dst->chanv; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams->src = (u8 *)src->compv; - sparams->dst = (u8 *)dst->compv; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams->src = (u8 *)src->data->data; @@ -589,7 +741,7 @@ static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type, static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, struct sof_ipc_ctrl_data *cdata, struct sof_ipc_ctrl_data_params *sparams, - bool send) + bool set) { struct sof_ipc_ctrl_data *partdata; size_t send_bytes; @@ -604,7 +756,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (!partdata) return -ENOMEM; - if (send) + if (set) err = sof_get_ctrl_copy_params(cdata->type, cdata, partdata, sparams); else @@ -633,7 +785,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, msg_bytes -= send_bytes; partdata->elems_remaining = msg_bytes; - if (send) + if (set) memcpy(sparams->dst, sparams->src + offset, send_bytes); err = sof_ipc_tx_message_unlocked(sdev->ipc, @@ -645,7 +797,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, if (err < 0) break; - if (!send) + if (!set) memcpy(sparams->dst + offset, sparams->src, send_bytes); offset += pl_size; @@ -660,11 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, /* * IPC get()/set() for kcontrols. */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send) +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) { struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; @@ -672,28 +820,69 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_ctrl_data_params sparams; + enum sof_ipc_ctrl_type ctrl_type; + struct snd_sof_widget *swidget; + bool widget_found = false; size_t send_bytes; + u32 ipc_cmd; int err; + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == scontrol->comp_id) { + widget_found = true; + break; + } + } + + if (!widget_found) { + dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id); + return -EINVAL; + } + + /* + * Volatile controls should always be part of static pipelines and the widget use_count + * would always be > 0 in this case. For the others, just return the cached value if the + * widget is not set up. + */ + if (!swidget->use_count) + return 0; + /* read or write firmware volume */ if (scontrol->readback_offset != 0) { /* write/read value header via mmaped region */ send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) * cdata->num_elems; - if (send) - snd_sof_dsp_block_write(sdev, sdev->mmio_bar, - scontrol->readback_offset, - cdata->chanv, send_bytes); + if (set) + err = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, + scontrol->readback_offset, + cdata->chanv, send_bytes); else - snd_sof_dsp_block_read(sdev, sdev->mmio_bar, - scontrol->readback_offset, - cdata->chanv, send_bytes); - return 0; + err = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_IRAM, + scontrol->readback_offset, + cdata->chanv, send_bytes); + + if (err) + dev_err_once(sdev->dev, "error: %s TYPE_IRAM failed\n", + set ? "write to" : "read from"); + return err; + } + + /* + * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the + * direction + * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently + * for ctrl_type + */ + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA; + ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET; + } else { + ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE; + ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET; } cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd; - cdata->cmd = ctrl_cmd; cdata->type = ctrl_type; cdata->comp_id = scontrol->comp_id; cdata->msg_index = 0; @@ -707,13 +896,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); sparams.elems = scontrol->num_channels; break; - case SOF_CTRL_TYPE_VALUE_COMP_GET: - case SOF_CTRL_TYPE_VALUE_COMP_SET: - sparams.msg_bytes = scontrol->num_channels * - sizeof(struct sof_ipc_ctrl_value_comp); - sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data); - sparams.elems = scontrol->num_channels; - break; case SOF_CTRL_TYPE_DATA_GET: case SOF_CTRL_TYPE_DATA_SET: sparams.msg_bytes = cdata->data->size; @@ -752,7 +934,7 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, return -EINVAL; } - err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send); + err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, set); if (err < 0) dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n", @@ -762,22 +944,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, } EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data); -/* - * IPC layer enumeration. - */ - -int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, - size_t dspbox_size, u32 hostbox, - size_t hostbox_size) -{ - sdev->dsp_box.offset = dspbox; - sdev->dsp_box.size = dspbox_size; - sdev->host_box.offset = hostbox; - sdev->host_box.size = hostbox_size; - return 0; -} -EXPORT_SYMBOL(snd_sof_dsp_mailbox_init); - int snd_sof_ipc_valid(struct snd_sof_dev *sdev) { struct sof_ipc_fw_ready *ready = &sdev->fw_ready; @@ -829,6 +995,22 @@ int snd_sof_ipc_valid(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(snd_sof_ipc_valid); +int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev) +{ + struct snd_sof_ipc_msg *msg; + + msg = &sdev->ipc->msg; + msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!msg->msg_data) + return -ENOMEM; + + msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!msg->reply_data) + return -ENOMEM; + + return 0; +} + struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) { struct snd_sof_ipc *ipc; @@ -845,17 +1027,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) /* indicate that we aren't sending a message ATM */ msg->ipc_complete = true; - /* pre-allocate message data */ - msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, - GFP_KERNEL); - if (!msg->msg_data) - return NULL; - - msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, - GFP_KERNEL); - if (!msg->reply_data) - return NULL; - init_waitqueue_head(&msg->waitq); return ipc; diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index bb79c77775..697f03565a 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -13,6 +13,7 @@ #include #include #include +#include "sof-priv.h" #include "ops.h" static int get_ext_windows(struct snd_sof_dev *sdev, @@ -86,7 +87,7 @@ static int get_cc_info(struct snd_sof_dev *sdev, } /* parse the extended FW boot data structures from FW boot message */ -int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) +static int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) { struct sof_ipc_ext_data_hdr *ext_hdr; void *ext_data; @@ -97,15 +98,16 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) return -ENOMEM; /* get first header */ - snd_sof_dsp_block_read(sdev, bar, offset, ext_data, + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, sizeof(*ext_hdr)); ext_hdr = ext_data; while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { /* read in ext structure */ - snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr), - (void *)((u8 *)ext_data + sizeof(*ext_hdr)), - ext_hdr->hdr.size - sizeof(*ext_hdr)); + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, + offset + sizeof(*ext_hdr), + (void *)((u8 *)ext_data + sizeof(*ext_hdr)), + ext_hdr->hdr.size - sizeof(*ext_hdr)); dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", ext_hdr->type, ext_hdr->hdr.size); @@ -138,7 +140,7 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) /* move to next header */ offset += ext_hdr->hdr.size; - snd_sof_dsp_block_read(sdev, bar, offset, ext_data, + snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, ext_data, sizeof(*ext_hdr)); ext_hdr = ext_data; } @@ -146,7 +148,6 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) kfree(ext_data); return ret; } -EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); static int ext_man_get_fw_version(struct snd_sof_dev *sdev, const struct sof_ext_man_elem_header *hdr) @@ -372,7 +373,6 @@ static void sof_get_windows(struct snd_sof_dev *sdev) u32 debug_size = 0; u32 debug_offset = 0; int window_offset; - int bar; int i; if (!sdev->info_window) { @@ -380,12 +380,6 @@ static void sof_get_windows(struct snd_sof_dev *sdev) return; } - bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); - if (bar < 0) { - dev_err(sdev->dev, "error: have no bar mapping\n"); - return; - } - for (i = 0; i < sdev->info_window->num_windows; i++) { elem = &sdev->info_window->window[i]; @@ -400,64 +394,53 @@ static void sof_get_windows(struct snd_sof_dev *sdev) case SOF_IPC_REGION_UPBOX: inbox_offset = window_offset + elem->offset; inbox_size = elem->size; - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - inbox_offset, - elem->size, "inbox", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + inbox_offset, + elem->size, "inbox", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_DOWNBOX: outbox_offset = window_offset + elem->offset; outbox_size = elem->size; - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - outbox_offset, - elem->size, "outbox", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + outbox_offset, + elem->size, "outbox", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_TRACE: - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - window_offset + - elem->offset, - elem->size, "etrace", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "etrace", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_DEBUG: debug_offset = window_offset + elem->offset; debug_size = elem->size; - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - window_offset + - elem->offset, - elem->size, "debug", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "debug", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_STREAM: stream_offset = window_offset + elem->offset; stream_size = elem->size; - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - stream_offset, - elem->size, "stream", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + stream_offset, + elem->size, "stream", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_REGS: - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - window_offset + - elem->offset, - elem->size, "regs", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "regs", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_EXCEPTION: sdev->dsp_oops_offset = window_offset + elem->offset; - snd_sof_debugfs_io_item(sdev, - sdev->bar[bar] + - window_offset + - elem->offset, - elem->size, "exception", - SOF_DEBUGFS_ACCESS_D0_ONLY); + snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM, + window_offset + elem->offset, + elem->size, "exception", + SOF_DEBUGFS_ACCESS_D0_ONLY); break; default: dev_err(sdev->dev, "error: get illegal window info\n"); @@ -470,8 +453,12 @@ static void sof_get_windows(struct snd_sof_dev *sdev) return; } - snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, - outbox_offset, outbox_size); + sdev->dsp_box.offset = inbox_offset; + sdev->dsp_box.size = inbox_size; + + sdev->host_box.offset = outbox_offset; + sdev->host_box.size = outbox_size; + sdev->stream_box.offset = stream_offset; sdev->stream_box.size = stream_size; @@ -493,7 +480,6 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) { struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; int offset; - int bar; int ret; /* mailbox must be on 4k boundary */ @@ -503,12 +489,6 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) return offset; } - bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); - if (bar < 0) { - dev_err(sdev->dev, "error: have no bar mapping\n"); - return -EINVAL; - } - dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", msg_id, offset); @@ -516,8 +496,17 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) if (!sdev->first_boot) return 0; - /* copy data from the DSP FW ready offset */ - snd_sof_dsp_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready)); + /* + * copy data from the DSP FW ready offset + * Subsequent error handling is not needed for BLK_TYPE_SRAM + */ + ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready, + sizeof(*fw_ready)); + if (ret) { + dev_err(sdev->dev, + "error: unable to read fw_ready, read from TYPE_SRAM failed\n"); + return ret; + } /* make sure ABI version is compatible */ ret = snd_sof_ipc_valid(sdev); @@ -525,12 +514,11 @@ int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) return ret; /* now check for extended data */ - snd_sof_fw_parse_ext_data(sdev, bar, offset + - sizeof(struct sof_ipc_fw_ready)); + snd_sof_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready)); sof_get_windows(sdev); - return 0; + return sof_ipc_init_msg_memory(sdev); } EXPORT_SYMBOL(sof_fw_ready); @@ -539,7 +527,7 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, struct snd_sof_mod_hdr *module) { struct snd_sof_blk_hdr *block; - int count, bar; + int count, ret; u32 offset; size_t remaining; @@ -576,13 +564,6 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, case SOF_FW_BLK_TYPE_DRAM: case SOF_FW_BLK_TYPE_SRAM: offset = block->offset; - bar = snd_sof_dsp_get_bar_index(sdev, block->type); - if (bar < 0) { - dev_err(sdev->dev, - "error: no BAR mapping for block type 0x%x\n", - block->type); - return bar; - } break; default: dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", @@ -600,8 +581,13 @@ int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, block->size); return -EINVAL; } - snd_sof_dsp_block_write(sdev, bar, offset, - block + 1, block->size); + ret = snd_sof_dsp_block_write(sdev, block->type, offset, + block + 1, block->size); + if (ret < 0) { + dev_err(sdev->dev, "error: write to block type 0x%x failed\n", + block->type); + return ret; + } if (remaining < block->size) { dev_err(sdev->dev, "error: not enough data remaining\n"); @@ -800,22 +786,16 @@ int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); -int snd_sof_load_firmware(struct snd_sof_dev *sdev) -{ - dev_dbg(sdev->dev, "loading firmware\n"); - - if (sof_ops(sdev)->load_firmware) - return sof_ops(sdev)->load_firmware(sdev); - return 0; -} -EXPORT_SYMBOL(snd_sof_load_firmware); - int snd_sof_run_firmware(struct snd_sof_dev *sdev) { int ret; init_waitqueue_head(&sdev->boot_wait); + /* (re-)enable dsp dump */ + sdev->dbg_dump_printed = false; + sdev->ipc_dump_printed = false; + /* create read-only fw_version debugfs to store boot version info */ if (sdev->first_boot) { ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, @@ -840,7 +820,8 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) /* boot the firmware on the DSP */ ret = snd_sof_dsp_run(sdev); if (ret < 0) { - dev_err(sdev->dev, "error: failed to reset DSP\n"); + snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP", + SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI); return ret; } @@ -854,16 +835,13 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, msecs_to_jiffies(sdev->boot_timeout)); if (ret == 0) { - dev_err(sdev->dev, "error: firmware boot failure\n"); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | - SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_FORCE_ERR_LEVEL); - sdev->fw_state = SOF_FW_BOOT_FAILED; + snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX | + SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI); return -EIO; } - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) - dev_dbg(sdev->dev, "firmware boot complete\n"); - else + if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) return -EIO; /* FW boots but fw_ready op failed */ /* perform post fw run operations */ @@ -873,6 +851,9 @@ int snd_sof_run_firmware(struct snd_sof_dev *sdev) return ret; } + dev_dbg(sdev->dev, "firmware boot complete\n"); + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); + return 0; } EXPORT_SYMBOL(snd_sof_run_firmware); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 11ecebd079..235e2ef721 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -142,22 +142,46 @@ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, } EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset) +/** + * snd_sof_dsp_panic - handle a received DSP panic message + * @sdev: Pointer to the device's sdev + * @offset: offset of panic information + * @non_recoverable: the panic is fatal, no recovery will be done by the caller + */ +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable) { - dev_err(sdev->dev, "error : DSP panic!\n"); - /* - * check if DSP is not ready and did not set the dsp_oops_offset. - * if the dsp_oops_offset is not set, set it from the panic message. - * Also add a check to memory window setting with panic message. + * if DSP is not ready and the dsp_oops_offset is not yet set, use the + * offset from the panic message. */ if (!sdev->dsp_oops_offset) sdev->dsp_oops_offset = offset; - else - dev_dbg(sdev->dev, "panic: dsp_oops_offset %zu offset %d\n", - sdev->dsp_oops_offset, offset); - snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - snd_sof_trace_notify_for_error(sdev); + /* + * Print warning if the offset from the panic message differs from + * dsp_oops_offset + */ + if (sdev->dsp_oops_offset != offset) + dev_warn(sdev->dev, + "%s: dsp_oops_offset %zu differs from panic offset %u\n", + __func__, sdev->dsp_oops_offset, offset); + + /* + * Set the fw_state to crashed only in case of non recoverable DSP panic + * event. + * Use different message within the snd_sof_dsp_dbg_dump() depending on + * the non_recoverable flag. + */ + sdev->dbg_dump_printed = false; + if (non_recoverable) { + snd_sof_dsp_dbg_dump(sdev, "DSP panic!", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + sof_set_fw_state(sdev, SOF_FW_CRASHED); + snd_sof_trace_notify_for_error(sdev); + } else { + snd_sof_dsp_dbg_dump(sdev, + "DSP panic (recovery will be attempted)", + SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); + } } EXPORT_SYMBOL(snd_sof_dsp_panic); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 4a5d6e497f..ffe7456e77 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -72,35 +72,68 @@ static inline int snd_sof_dsp_reset(struct snd_sof_dev *sdev) return 0; } -/* dsp core power up/power down */ -static inline int snd_sof_dsp_core_power_up(struct snd_sof_dev *sdev, - unsigned int core_mask) +/* dsp core get/put */ +static inline int snd_sof_dsp_core_get(struct snd_sof_dev *sdev, int core) { - int ret = 0; - - core_mask &= ~sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_up && core_mask) { - ret = sof_ops(sdev)->core_power_up(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask |= core_mask; + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; } - return ret; + if (sof_ops(sdev)->core_get) { + int ret; + + /* if current ref_count is > 0, increment it and return */ + if (sdev->dsp_core_ref_count[core] > 0) { + sdev->dsp_core_ref_count[core]++; + return 0; + } + + /* power up the core */ + ret = sof_ops(sdev)->core_get(sdev, core); + if (ret < 0) + return ret; + + /* increment ref_count */ + sdev->dsp_core_ref_count[core]++; + + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask |= BIT(core); + + dev_dbg(sdev->dev, "Core %d powered up\n", core); + } + + return 0; } -static inline int snd_sof_dsp_core_power_down(struct snd_sof_dev *sdev, - unsigned int core_mask) +static inline int snd_sof_dsp_core_put(struct snd_sof_dev *sdev, int core) { - int ret = 0; - - core_mask &= sdev->enabled_cores_mask; - if (sof_ops(sdev)->core_power_down && core_mask) { - ret = sof_ops(sdev)->core_power_down(sdev, core_mask); - if (!ret) - sdev->enabled_cores_mask &= ~core_mask; + if (core > sdev->num_cores - 1) { + dev_err(sdev->dev, "invalid core id: %d for num_cores: %d\n", core, + sdev->num_cores); + return -EINVAL; } - return ret; + if (sof_ops(sdev)->core_put) { + int ret; + + /* decrement ref_count and return if it is > 0 */ + if (--(sdev->dsp_core_ref_count[core]) > 0) + return 0; + + /* power down the core */ + ret = sof_ops(sdev)->core_put(sdev, core); + if (ret < 0) + return ret; + + /* and update enabled_cores_mask */ + sdev->enabled_cores_mask &= ~BIT(core); + + dev_dbg(sdev->dev, "Core %d powered down\n", core); + } + + return 0; } /* pre/post fw load */ @@ -241,16 +274,17 @@ snd_sof_dsp_set_power_state(struct snd_sof_dev *sdev, } /* debug */ -static inline void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, u32 flags) -{ - if (sof_ops(sdev)->dbg_dump) - sof_ops(sdev)->dbg_dump(sdev, flags); -} +void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags); -static inline void snd_sof_ipc_dump(struct snd_sof_dev *sdev) +static inline int snd_sof_debugfs_add_region_item(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, + const char *name, enum sof_debugfs_access_type access_type) { - if (sof_ops(sdev)->ipc_dump) - sof_ops(sdev)->ipc_dump(sdev); + if (sof_ops(sdev) && sof_ops(sdev)->debugfs_add_region_item) + return sof_ops(sdev)->debugfs_add_region_item(sdev, blk_type, offset, + size, name, access_type); + + return 0; } /* register IO */ @@ -297,16 +331,33 @@ static inline u64 snd_sof_dsp_read64(struct snd_sof_dev *sdev, u32 bar, } /* block IO */ -static inline void snd_sof_dsp_block_read(struct snd_sof_dev *sdev, u32 bar, - u32 offset, void *dest, size_t bytes) +static inline int snd_sof_dsp_block_read(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t bytes) { - sof_ops(sdev)->block_read(sdev, bar, offset, dest, bytes); + return sof_ops(sdev)->block_read(sdev, blk_type, offset, dest, bytes); } -static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, u32 bar, - u32 offset, void *src, size_t bytes) +static inline int snd_sof_dsp_block_write(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t bytes) { - sof_ops(sdev)->block_write(sdev, bar, offset, src, bytes); + return sof_ops(sdev)->block_write(sdev, blk_type, offset, src, bytes); +} + +/* mailbox IO */ +static inline void snd_sof_dsp_mailbox_read(struct snd_sof_dev *sdev, + u32 offset, void *dest, size_t bytes) +{ + if (sof_ops(sdev)->mailbox_read) + sof_ops(sdev)->mailbox_read(sdev, offset, dest, bytes); +} + +static inline void snd_sof_dsp_mailbox_write(struct snd_sof_dev *sdev, + u32 offset, void *src, size_t bytes) +{ + if (sof_ops(sdev)->mailbox_write) + sof_ops(sdev)->mailbox_write(sdev, offset, src, bytes); } /* ipc */ @@ -400,12 +451,20 @@ snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, return 0; } -/* host DSP message data */ -static inline void snd_sof_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz) +/* Firmware loading */ +static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev) { - sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz); + dev_dbg(sdev->dev, "loading firmware\n"); + + return sof_ops(sdev)->load_firmware(sdev); +} + +/* host DSP message data */ +static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz) +{ + return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz); } /* host configure DSP HW parameters */ @@ -428,6 +487,16 @@ snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, return 0; } +/* pcm ack */ +static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) + return sof_ops(sdev)->pcm_ack(sdev, substream); + + return 0; +} + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) static inline int snd_sof_probe_compr_assign(struct snd_sof_dev *sdev, @@ -488,36 +557,23 @@ snd_sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) sof_ops(sdev)->machine_unregister(sdev, pdata); } -static inline void +static inline struct snd_soc_acpi_mach * snd_sof_machine_select(struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->machine_select) - sof_ops(sdev)->machine_select(sdev); + return sof_ops(sdev)->machine_select(sdev); + + return NULL; } static inline void -snd_sof_set_mach_params(const struct snd_soc_acpi_mach *mach, +snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev) { if (sof_ops(sdev) && sof_ops(sdev)->set_mach_params) sof_ops(sdev)->set_mach_params(mach, sdev); } -static inline const struct snd_sof_dsp_ops -*sof_get_ops(const struct sof_dev_desc *d, - const struct sof_ops_table mach_ops[], int asize) -{ - int i; - - for (i = 0; i < asize; i++) { - if (d == mach_ops[i].desc) - return mach_ops[i].ops; - } - - /* not found */ - return NULL; -} - /** * snd_sof_dsp_register_poll_timeout - Periodically poll an address * until a condition is met or a timeout occurs @@ -587,5 +643,5 @@ int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 target, u32 timeout_ms, u32 interval_us); -void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset); +void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverable); #endif diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 9893b182da..37fb8e6cd4 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -17,7 +17,7 @@ #include "sof-audio.h" #include "ops.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) -#include "compress.h" +#include "sof-probes.h" #endif /* Create DMA buffer page table for DSP */ @@ -57,7 +57,7 @@ static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream /* * sof pcm period elapse work */ -void snd_sof_pcm_period_elapsed_work(struct work_struct *work) +static void snd_sof_pcm_period_elapsed_work(struct work_struct *work) { struct snd_sof_pcm_stream *sps = container_of(work, struct snd_sof_pcm_stream, @@ -66,6 +66,11 @@ void snd_sof_pcm_period_elapsed_work(struct work_struct *work) snd_pcm_period_elapsed(sps->substream); } +void snd_sof_pcm_init_elapsed_work(struct work_struct *work) +{ + INIT_WORK(work, snd_sof_pcm_period_elapsed_work); +} + /* * sof pcm period elapse, this could be called at irq thread context. */ @@ -95,14 +100,16 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); -static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, - struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm) { struct sof_ipc_stream stream; struct sof_ipc_reply reply; int ret; + if (!spcm->prepared[substream->stream]) + return 0; + stream.hdr.size = sizeof(stream); stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; stream.comp_id = spcm->stream[substream->stream].comp_id; @@ -116,6 +123,40 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, return ret; } +static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dai *dai; + int ret, j; + + /* query DAPM for list of connected widgets and set them up */ + for_each_rtd_cpu_dais(rtd, j, dai) { + struct snd_soc_dapm_widget_list *list; + + ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list, + dpcm_end_walk_at_be); + if (ret < 0) { + dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name, + dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + return ret; + } + + spcm->stream[dir].list = list; + + ret = sof_widget_list_setup(sdev, spcm, dir); + if (ret < 0) { + dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n", + spcm->pcm.pcm_id, dir); + spcm->stream[dir].list = NULL; + snd_soc_dapm_dai_free_widgets(&list); + return ret; + } + } + + return 0; +} + static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -140,11 +181,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; - } + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + return ret; dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -213,7 +252,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); - /* send IPC to the DSP */ + /* if this is a repeated hw_params without hw_free, skip setting up widgets */ + if (!spcm->stream[substream->stream].list) { + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream); + if (ret < 0) + return ret; + } + + /* send hw_params IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), &ipc_params_reply, sizeof(ipc_params_reply)); if (ret < 0) { @@ -253,20 +299,26 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: free stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); - if (spcm->prepared[substream->stream]) { - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; - } + /* free PCM in the DSP */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (ret < 0) + err = ret; - cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + /* stop DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); if (ret < 0) { dev_err(component->dev, "error: platform hw free failed\n"); err = ret; } + /* free the DAPM widget list */ + ret = sof_widget_list_free(sdev, spcm, substream->stream); + if (ret < 0) + err = ret; + + cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); + return err; } @@ -316,6 +368,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, struct sof_ipc_stream stream; struct sof_ipc_reply reply; bool reset_hw_params = false; + bool free_widget_list = false; bool ipc_first = false; int ret; @@ -342,26 +395,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; - case SNDRV_PCM_TRIGGER_RESUME: - if (spcm->stream[substream->stream].suspend_ignored) { - /* - * this case will be triggered when INFO_RESUME is - * supported, no need to resume streams that remained - * enabled in D0ix. - */ - spcm->stream[substream->stream].suspend_ignored = false; - return 0; - } - - /* set up hw_params */ - ret = sof_pcm_prepare(component, substream); - if (ret < 0) { - dev_err(component->dev, - "error: failed to set up hw_params upon resume\n"); - return ret; - } - - fallthrough; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { /* @@ -386,6 +419,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = true; return 0; } + free_widget_list = true; fallthrough; case SNDRV_PCM_TRIGGER_STOP: stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; @@ -414,8 +448,12 @@ static int sof_pcm_trigger(struct snd_soc_component *component, snd_sof_pcm_platform_trigger(sdev, substream, cmd); /* free PCM if reset_hw_params is set and the STOP IPC is successful */ - if (!ret && reset_hw_params) - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); + if (!ret && reset_hw_params) { + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, + free_widget_list); + if (ret < 0) + return ret; + } return ret; } @@ -749,6 +787,18 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa channels->min = dai->dai_config->esai.tdm_slots; channels->max = dai->dai_config->esai.tdm_slots; + dev_dbg(component->dev, + "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = dai->dai_config->afe.rate; + rate->max = dai->dai_config->afe.rate; + channels->min = dai->dai_config->afe.channels; + channels->max = dai->dai_config->afe.channels; + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); dev_dbg(component->dev, @@ -767,6 +817,42 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa "channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_BT: + rate->min = dai->dai_config->acpbt.fsync_rate; + rate->max = dai->dai_config->acpbt.fsync_rate; + channels->min = dai->dai_config->acpbt.tdm_slots; + channels->max = dai->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = dai->dai_config->acpsp.fsync_rate; + rate->max = dai->dai_config->acpsp.fsync_rate; + channels->min = dai->dai_config->acpsp.tdm_slots; + channels->max = dai->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = dai->dai_config->acpdmic.fsync_rate; + rate->max = dai->dai_config->acpdmic.fsync_rate; + channels->min = dai->dai_config->acpdmic.tdm_slots; + channels->max = dai->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, + "AMD_DMIC channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; default: dev_err(component->dev, "error: invalid DAI type %d\n", dai->dai_config->type); @@ -810,6 +896,14 @@ static void sof_pcm_remove(struct snd_soc_component *component) snd_soc_tplg_component_remove(component); } +static int sof_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + + return snd_sof_pcm_platform_ack(sdev, substream); +} + void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { struct snd_soc_component_driver *pd = &sdev->plat_drv; @@ -828,12 +922,9 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->hw_free = sof_pcm_hw_free; pd->trigger = sof_pcm_trigger; pd->pointer = sof_pcm_pointer; + pd->ack = sof_pcm_ack; -#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) - pd->compress_ops = &sof_compressed_ops; -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) - /* override cops when probe support is enabled */ pd->compress_ops = &sof_probe_compressed_ops; #endif pd->pcm_construct = sof_pcm_new; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index c83fb62559..197a88695f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -122,7 +122,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) old_state == SOF_DSP_PM_D0) return 0; - sdev->fw_state = SOF_FW_BOOT_PREPARE; + sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE); /* load the firmware */ ret = snd_sof_load_firmware(sdev); @@ -130,10 +130,11 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to load DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } - sdev->fw_state = SOF_FW_BOOT_IN_PROGRESS; + sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS); /* * Boot the firmware. The FW boot status will be modified @@ -144,6 +145,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) dev_err(sdev->dev, "error: failed to boot DSP firmware after resume %d\n", ret); + sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED); return ret; } @@ -157,7 +159,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) } /* restore pipelines */ - ret = sof_restore_pipelines(sdev->dev); + ret = sof_set_up_pipelines(sdev, false); if (ret < 0) { dev_err(sdev->dev, "error: failed to restore pipeline after resume %d\n", @@ -191,7 +193,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) goto suspend; - /* set restore_stream for all streams during system suspend */ + /* prepare for streams to be resumed properly upon resume */ if (!runtime_suspend) { ret = sof_set_hw_params_upon_resume(sdev->dev); if (ret < 0) { @@ -208,6 +210,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (target_state == SOF_DSP_PM_D0) goto suspend; + sof_tear_down_pipelines(sdev, false); + /* release trace */ snd_sof_release_trace(sdev); @@ -255,7 +259,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) return ret; /* reset FW state */ - sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED); sdev->enabled_cores_mask = 0; return ret; @@ -310,6 +314,14 @@ int snd_sof_prepare(struct device *dev) /* will suspend to S3 by default */ sdev->system_suspend_target = SOF_SUSPEND_S3; + /* + * if the firmware is crashed or boot failed then we try to aim for S3 + * to reboot the firmware + */ + if (sdev->fw_state == SOF_FW_CRASHED || + sdev->fw_state == SOF_FW_BOOT_FAILED) + return 0; + if (!desc->use_acpi_target_states) return 0; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 989912f2b7..9e76b79650 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -8,9 +8,540 @@ // Author: Ranjani Sridharan // +#include #include "sof-audio.h" #include "ops.h" +static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + int ret; + + /* reset readback offset for scontrol */ + scontrol->readback_offset = 0; + + ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + if (ret < 0) + dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; +} + +static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai) +{ + struct sof_ipc_dai_config *config; + struct sof_ipc_reply reply; + int ret; + + config = &dai->dai_config[dai->current_config]; + if (!config) { + dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name); + return -EINVAL; + } + + /* set NONE flag to clear all previous settings */ + config->flags = SOF_DAI_CONFIG_FLAGS_NONE; + + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + + if (ret < 0) + dev_err(sdev->dev, "error: failed to set dai config for %s\n", dai->name); + + return ret; +} + +static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct snd_sof_control *scontrol; + int ret; + + /* set up all controls for the widget */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + if (scontrol->comp_id == swidget->comp_id) { + /* set kcontrol data in DSP */ + ret = sof_kcontrol_setup(sdev, scontrol); + if (ret < 0) { + dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n", + swidget->widget->name); + return ret; + } + + /* + * Read back the data from the DSP for static widgets. This is particularly + * useful for binary kcontrols associated with static pipeline widgets to + * initialize the data size to match that in the DSP. + */ + if (swidget->dynamic_pipeline_widget) + continue; + + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) + dev_warn(sdev->dev, "Failed kcontrol get for control in widget %s\n", + swidget->widget->name); + } + + return 0; +} + +static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) +{ + struct snd_sof_route *sroute; + + list_for_each_entry(sroute, &sdev->route_list, list) + if (sroute->src_widget == widget || sroute->sink_widget == widget) + sroute->setup = false; +} + +int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_free ipc_free = { + .hdr = { + .size = sizeof(ipc_free), + .cmd = SOF_IPC_GLB_TPLG_MSG, + }, + .id = swidget->comp_id, + }; + struct sof_ipc_reply reply; + int ret, ret1, core; + + if (!swidget->private) + return 0; + + /* only free when use_count is 0 */ + if (--swidget->use_count) + return 0; + + core = swidget->core; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + { + const struct sof_ipc_pipe_new *pipeline = swidget->private; + + core = pipeline->core; + ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; + break; + } + case snd_soc_dapm_buffer: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; + break; + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + + dai->configured = false; + fallthrough; + } + default: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; + break; + } + + /* continue to disable core even if IPC fails */ + ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); + + /* + * disable widget core. continue to route setup status and complete flag + * even if this fails and return the appropriate error + */ + ret1 = snd_sof_dsp_core_put(sdev, core); + if (ret1 < 0) { + dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", + core, swidget->widget->name); + if (!ret) + ret = ret1; + } + + /* reset route setup status for all routes that contain this widget */ + sof_reset_route_setup_status(sdev, swidget); + swidget->complete = 0; + + if (!ret) + dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + + return ret; +} +EXPORT_SYMBOL(sof_widget_free); + +int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_pipe_new *pipeline; + struct sof_ipc_comp_reply r; + struct sof_ipc_cmd_hdr *hdr; + struct sof_ipc_comp *comp; + struct snd_sof_dai *dai; + size_t ipc_size; + int ret; + int core; + + /* skip if there is no private data */ + if (!swidget->private) + return 0; + + /* widget already set up */ + if (++swidget->use_count > 1) + return 0; + + /* set core ID */ + core = swidget->core; + if (swidget->id == snd_soc_dapm_scheduler) { + pipeline = swidget->private; + core = pipeline->core; + } + + /* enable widget core */ + ret = snd_sof_dsp_core_get(sdev, core); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", + swidget->widget->name); + goto use_count_dec; + } + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext); + comp = kzalloc(ipc_size, GFP_KERNEL); + if (!comp) { + ret = -ENOMEM; + goto core_put; + } + + dai = swidget->private; + dai->configured = false; + memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai)); + + /* append extended data to the end of the component */ + memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext, + sizeof(swidget->comp_ext)); + + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r)); + kfree(comp); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load widget %s\n", + swidget->widget->name); + goto core_put; + } + + ret = sof_dai_config_setup(sdev, dai); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n", + swidget->widget->name); + + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ + sof_widget_free(sdev, swidget); + return ret; + } + break; + case snd_soc_dapm_scheduler: + pipeline = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), &r, sizeof(r)); + break; + default: + hdr = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, + &r, sizeof(r)); + break; + } + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); + goto core_put; + } + + /* restore kcontrols for widget */ + ret = sof_widget_kcontrol_setup(sdev, swidget); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", + swidget->widget->name); + /* + * widget use_count and core ref_count will both be decremented by + * sof_widget_free() + */ + sof_widget_free(sdev, swidget); + return ret; + } + + dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); + + return 0; + +core_put: + snd_sof_dsp_core_put(sdev, core); +use_count_dec: + swidget->use_count--; + return ret; +} +EXPORT_SYMBOL(sof_widget_setup); + +static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) +{ + struct sof_ipc_pipe_comp_connect *connect; + struct sof_ipc_reply reply; + int ret; + + /* skip if there's no private data */ + if (!sroute->private) + return 0; + + /* nothing to do if route is already set up */ + if (sroute->setup) + return 0; + + connect = sroute->private; + + dev_dbg(sdev->dev, "setting up route %s -> %s\n", + sroute->src_widget->widget->name, + sroute->sink_widget->widget->name); + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret); + return ret; + } + + sroute->setup = true; + + return 0; +} + +static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) +{ + struct snd_sof_widget *src_widget = wsource->dobj.private; + struct snd_sof_widget *sink_widget = wsink->dobj.private; + struct snd_sof_route *sroute; + bool route_found = false; + + /* ignore routes involving virtual widgets in topology */ + switch (src_widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + return 0; + default: + break; + } + + switch (sink_widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + return 0; + default: + break; + } + + /* find route matching source and sink widgets */ + list_for_each_entry(sroute, &sdev->route_list, list) + if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { + route_found = true; + break; + } + + if (!route_found) { + dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n", + wsource->name, wsink->name); + return -EINVAL; + } + + return sof_route_setup_ipc(sdev, sroute); +} + +static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget_list *list, int dir) +{ + struct snd_soc_dapm_widget *widget; + struct snd_soc_dapm_path *p; + int ret; + int i; + + /* + * Set up connections between widgets in the sink/source paths based on direction. + * Some non-SOF widgets exist in topology either for compatibility or for the + * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM + * events. But they are not handled by the firmware. So ignore them. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + for_each_dapm_widgets(list, i, widget) { + if (!widget->dobj.private) + continue; + + snd_soc_dapm_widget_for_each_sink_path(widget, p) + if (p->sink->dobj.private) { + ret = sof_route_setup(sdev, widget, p->sink); + if (ret < 0) + return ret; + } + } + } else { + for_each_dapm_widgets(list, i, widget) { + if (!widget->dobj.private) + continue; + + snd_soc_dapm_widget_for_each_source_path(widget, p) + if (p->source->dobj.private) { + ret = sof_route_setup(sdev, p->source, widget); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_soc_dapm_widget *widget; + int i, ret, num_widgets; + + /* nothing to set up */ + if (!list) + return 0; + + /* set up widgets in the list */ + for_each_dapm_widgets(list, num_widgets, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_sof_widget *pipe_widget; + + if (!swidget) + continue; + + /* + * The scheduler widget for a pipeline is not part of the connected DAPM + * widget list and it needs to be set up before the widgets in the pipeline + * are set up. The use_count for the scheduler widget is incremented for every + * widget in a given pipeline to ensure that it is freed only after the last + * widget in the pipeline is freed. + */ + pipe_widget = swidget->pipe_widget; + if (!pipe_widget) { + dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto widget_free; + } + + ret = sof_widget_setup(sdev, pipe_widget); + if (ret < 0) + goto widget_free; + + /* set up the widget */ + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) { + sof_widget_free(sdev, pipe_widget); + goto widget_free; + } + } + + /* + * error in setting pipeline connections will result in route status being reset for + * routes that were successfully set up when the widgets are freed. + */ + ret = sof_setup_pipeline_connections(sdev, list, dir); + if (ret < 0) + goto widget_free; + + /* complete pipelines */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_sof_widget *pipe_widget; + + if (!swidget) + continue; + + pipe_widget = swidget->pipe_widget; + if (!pipe_widget) { + dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto widget_free; + } + + if (pipe_widget->complete) + continue; + + pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget); + if (pipe_widget->complete < 0) { + ret = pipe_widget->complete; + goto widget_free; + } + } + + return 0; + +widget_free: + /* free all widgets that have been set up successfully */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + if (!num_widgets--) + break; + + sof_widget_free(sdev, swidget); + sof_widget_free(sdev, swidget->pipe_widget); + } + + return ret; +} + +int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_soc_dapm_widget *widget; + int i, ret; + int ret1 = 0; + + /* nothing to free */ + if (!list) + return 0; + + /* + * Free widgets in the list. This can fail but continue freeing other widgets to keep + * use_counts balanced. + */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + /* + * free widget and its pipe_widget. Either of these can fail, but free as many as + * possible before freeing the list and returning the error. + */ + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + ret1 = ret; + + ret = sof_widget_free(sdev, swidget->pipe_widget); + if (ret < 0) + ret1 = ret; + } + + snd_soc_dapm_dai_free_widgets(&list); + spcm->stream[dir].list = NULL; + + return ret1; +} + /* * helper to determine if there are only D0i3 compatible * streams active @@ -93,55 +624,6 @@ int sof_set_hw_params_upon_resume(struct device *dev) return snd_sof_dsp_hw_params_upon_resume(sdev); } -static int sof_restore_kcontrols(struct device *dev) -{ - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_control *scontrol; - int ipc_cmd, ctrl_type; - int ret = 0; - - /* restore kcontrol values */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - /* reset readback offset for scontrol after resuming */ - scontrol->readback_offset = 0; - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_SET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - - default: - break; - } - - if (ret < 0) { - dev_err(dev, - "error: failed kcontrol value set for widget: %d\n", - scontrol->comp_id); - - return ret; - } - } - - return 0; -} - const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, int pipeline_id) { @@ -158,142 +640,62 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, return NULL; } -int sof_restore_pipelines(struct device *dev) +int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; - struct sof_ipc_pipe_new *pipeline; - struct snd_sof_dai *dai; - struct sof_ipc_cmd_hdr *hdr; - struct sof_ipc_comp *comp; - size_t ipc_size; int ret; /* restore pipeline components */ - list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { - struct sof_ipc_comp_reply r; - - /* skip if there is no private data */ - if (!swidget->private) + list_for_each_entry(swidget, &sdev->widget_list, list) { + /* only set up the widgets belonging to static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) continue; - ret = sof_pipeline_core_enable(sdev, swidget); - if (ret < 0) { - dev_err(dev, - "error: failed to enable target core: %d\n", - ret); + /* + * For older firmware, skip scheduler widgets in this loop, + * sof_widget_setup() will be called in the 'complete pipeline' loop + */ + if (v->abi_version < SOF_ABI_VER(3, 19, 0) && + swidget->id == snd_soc_dapm_scheduler) + continue; - return ret; - } + /* update DAI config. The IPC will be sent in sof_widget_setup() */ + if (WIDGET_IS_DAI(swidget->id)) { + struct snd_sof_dai *dai = swidget->private; + struct sof_ipc_dai_config *config; - switch (swidget->id) { - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - ipc_size = sizeof(struct sof_ipc_comp_dai) + - sizeof(struct sof_ipc_comp_ext); - comp = kzalloc(ipc_size, GFP_KERNEL); - if (!comp) - return -ENOMEM; - - dai = swidget->private; - memcpy(comp, &dai->comp_dai, - sizeof(struct sof_ipc_comp_dai)); - - /* append extended data to the end of the component */ - memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), - &swidget->comp_ext, sizeof(swidget->comp_ext)); - - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, - comp, ipc_size, - &r, sizeof(r)); - kfree(comp); - break; - case snd_soc_dapm_scheduler: + if (!dai || !dai->dai_config) + continue; + config = dai->dai_config; /* - * During suspend, all DSP cores are powered off. - * Therefore upon resume, create the pipeline comp - * and power up the core that the pipeline is - * scheduled on. + * The link DMA channel would be invalidated for running + * streams but not for streams that were in the PAUSED + * state during suspend. So invalidate it here before setting + * the dai config in the DSP. */ - pipeline = swidget->private; - ret = sof_load_pipeline_ipc(dev, pipeline, &r); - break; - default: - hdr = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, - swidget->private, hdr->size, - &r, sizeof(r)); - break; + if (config->type == SOF_DAI_INTEL_HDA) + config->hda.link_dma_ch = DMA_CHAN_INVALID; } - if (ret < 0) { - dev_err(dev, - "error: failed to load widget type %d with ID: %d\n", - swidget->widget->id, swidget->comp_id); + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) return ret; - } } /* restore pipeline connections */ - list_for_each_entry_reverse(sroute, &sdev->route_list, list) { - struct sof_ipc_pipe_comp_connect *connect; - struct sof_ipc_reply reply; + list_for_each_entry(sroute, &sdev->route_list, list) { - /* skip if there's no private data */ - if (!sroute->private) + /* only set up routes belonging to static pipelines */ + if (!verify && (sroute->src_widget->dynamic_pipeline_widget || + sroute->sink_widget->dynamic_pipeline_widget)) continue; - connect = sroute->private; - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), - &reply, sizeof(reply)); + ret = sof_route_setup_ipc(sdev, sroute); if (ret < 0) { - dev_err(dev, - "error: failed to load route sink %s control %s source %s\n", - sroute->route->sink, - sroute->route->control ? sroute->route->control - : "none", - sroute->route->source); - - return ret; - } - } - - /* restore dai links */ - list_for_each_entry_reverse(dai, &sdev->dai_list, list) { - struct sof_ipc_reply reply; - struct sof_ipc_dai_config *config = &dai->dai_config[dai->current_config]; - - if (!config) { - dev_err(dev, "error: no config for DAI %s\n", - dai->name); - continue; - } - - /* - * The link DMA channel would be invalidated for running - * streams but not for streams that were in the PAUSED - * state during suspend. So invalidate it here before setting - * the dai config in the DSP. - */ - if (config->type == SOF_DAI_INTEL_HDA) - config->hda.link_dma_ch = DMA_CHAN_INVALID; - - ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, - config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) { - dev_err(dev, - "error: failed to set dai config for %s\n", - dai->name); - + dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); return ret; } } @@ -302,23 +704,148 @@ int sof_restore_pipelines(struct device *dev) list_for_each_entry(swidget, &sdev->widget_list, list) { switch (swidget->id) { case snd_soc_dapm_scheduler: + /* only complete static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) + continue; + + if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + swidget->complete = - snd_sof_complete_pipeline(dev, swidget); + snd_sof_complete_pipeline(sdev, swidget); break; default: break; } } - /* restore pipeline kcontrols */ - ret = sof_restore_kcontrols(dev); + return 0; +} + +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list) +{ + int ret; + + /* Send PCM_FREE IPC to reset pipeline */ + ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); if (ret < 0) - dev_err(dev, - "error: restoring kcontrols after resume\n"); + return ret; + + /* stop the DMA */ + ret = snd_sof_pcm_platform_hw_free(sdev, substream); + if (ret < 0) + return ret; + + /* free widget list */ + if (free_widget_list) { + ret = sof_widget_list_free(sdev, spcm, dir); + if (ret < 0) + dev_err(sdev->dev, "failed to free widgets during suspend\n"); + } return ret; } +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); + if (ret < 0) + return ret; + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * For older firmware, this function doesn't free widgets for static pipelines during suspend. + * It only resets use_count for all widgets. + */ +int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + int ret; + + /* + * This function is called during suspend and for one-time topology verification during + * first boot. In both cases, there is no need to protect swidget->use_count and + * sroute->setup because during suspend all running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->dynamic_pipeline_widget) + continue; + + /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ + if (!verify && !swidget->dynamic_pipeline_widget && + v->abi_version < SOF_ABI_VER(3, 19, 0)) { + swidget->use_count = 0; + swidget->complete = 0; + continue; + } + + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + * Skip this step for older firmware. + */ + if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + + list_for_each_entry(sroute, &sdev->route_list, list) + sroute->setup = false; + + return 0; +} + /* * Generic object lookup APIs. */ @@ -500,9 +1027,10 @@ int sof_machine_check(struct snd_sof_dev *sdev) if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) { /* find machine */ - snd_sof_machine_select(sdev); - if (sof_pdata->machine) { - snd_sof_set_mach_params(sof_pdata->machine, sdev); + mach = snd_sof_machine_select(sdev); + if (mach) { + sof_pdata->machine = mach; + snd_sof_set_mach_params(mach, sdev); return 0; } @@ -524,7 +1052,7 @@ int sof_machine_check(struct snd_sof_dev *sdev) sof_pdata->tplg_filename = desc->nocodec_tplg_filename; sof_pdata->machine = mach; - snd_sof_set_mach_params(sof_pdata->machine, sdev); + snd_sof_set_mach_params(mach, sdev); return 0; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index dc274e63ed..f3009e6b91 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -28,13 +28,17 @@ #define DMA_CHAN_INVALID 0xFFFFFFFF +#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; struct snd_dma_buffer page_table; struct sof_ipc_stream_posn posn; struct snd_pcm_substream *substream; + struct snd_compr_stream *cstream; struct work_struct period_elapsed_work; + struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */ bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */ /* * flag to indicate that the DSP pipelines should be kept @@ -66,28 +70,45 @@ struct snd_sof_control { int min_volume_step; /* min volume step for volume_table */ int max_volume_step; /* max volume step for volume_table */ int num_channels; + unsigned int access; u32 readback_offset; /* offset to mmapped data if used */ struct sof_ipc_ctrl_data *control_data; u32 size; /* cdata size */ - enum sof_ipc_ctrl_cmd cmd; u32 *volume_table; /* volume table computed from tlv data*/ struct list_head list; /* list in sdev control list */ struct snd_sof_led_control led_ctl; + + /* if true, the control's data needs to be updated from Firmware */ + bool comp_data_dirty; }; +struct snd_sof_widget; + /* ASoC SOF DAPM widget */ struct snd_sof_widget { struct snd_soc_component *scomp; int comp_id; int pipeline_id; int complete; + int use_count; /* use_count will be protected by the PCM mutex held by the core */ int core; int id; + /* + * Flag indicating if the widget should be set up dynamically when a PCM is opened. + * This flag is only set for the scheduler type widget in topology. During topology + * loading, this flag is propagated to all the widgets belonging to the same pipeline. + * When this flag is not set, a widget is set up at the time of topology loading + * and retained until the DSP enters D3. It will need to be set up again when resuming + * from D3. + */ + bool dynamic_pipeline_widget; + struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ + struct snd_sof_widget *pipe_widget; /* extended data for UUID components */ struct sof_ipc_comp_ext comp_ext; @@ -101,6 +122,9 @@ struct snd_sof_route { struct snd_soc_dapm_route *route; struct list_head list; /* list in sdev route list */ + struct snd_sof_widget *src_widget; + struct snd_sof_widget *sink_widget; + bool setup; void *private; }; @@ -109,11 +133,11 @@ struct snd_sof_route { struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; - const char *cpu_dai_name; struct sof_ipc_comp_dai comp_dai; int number_configs; int current_config; + bool configured; /* DAI configured during BE hw_params */ struct sof_ipc_dai_config *dai_config; struct list_head list; /* list in sdev dai list */ }; @@ -148,6 +172,8 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, unsigned int size); int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, unsigned int size); +void snd_sof_control_notify(struct snd_sof_dev *sdev, + struct sof_ipc_ctrl_data *cdata); /* * Topology. @@ -155,15 +181,9 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ * be freed by snd_soc_unregister_component, */ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); -int snd_sof_complete_pipeline(struct device *dev, +int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); -int sof_load_pipeline_ipc(struct device *dev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r); -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget); - /* * Stream IPC */ @@ -205,22 +225,27 @@ struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, int pipeline_id); void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); -void snd_sof_pcm_period_elapsed_work(struct work_struct *work); +void snd_sof_pcm_init_elapsed_work(struct work_struct *work); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) +void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream); +void snd_sof_compr_init_elapsed_work(struct work_struct *work); +#else +static inline void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) { } +static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } +#endif /* * Mixer IPC */ -int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, - u32 ipc_cmd, - enum sof_ipc_ctrl_type ctrl_type, - enum sof_ipc_ctrl_cmd ctrl_cmd, - bool send); +int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); /* PM */ -int sof_restore_pipelines(struct device *dev); +int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify); +int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify); int sof_set_hw_params_upon_resume(struct device *dev); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); @@ -229,4 +254,14 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); +int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); +int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + +/* PCM */ +int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm); +int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, + struct snd_sof_pcm *spcm, int dir, bool free_widget_list); #endif diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index d1a21edfa0..e3718638f9 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -7,65 +7,41 @@ #include #include +#include #include #include +#include "sof-of-dev.h" #include "ops.h" -extern struct snd_sof_dsp_ops sof_imx8_ops; -extern struct snd_sof_dsp_ops sof_imx8x_ops; -extern struct snd_sof_dsp_ops sof_imx8m_ops; +static char *fw_path; +module_param(fw_path, charp, 0444); +MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware."); -/* platform specific devices */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) -static struct sof_dev_desc sof_of_imx8qxp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8x.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8x_ops, -}; +static char *tplg_path; +module_param(tplg_path, charp, 0444); +MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); -static struct sof_dev_desc sof_of_imx8qm_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8_ops, -}; -#endif - -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) -static struct sof_dev_desc sof_of_imx8mp_desc = { - .default_fw_path = "imx/sof", - .default_tplg_path = "imx/sof-tplg", - .default_fw_filename = "sof-imx8m.ri", - .nocodec_tplg_filename = "sof-imx8-nocodec.tplg", - .ops = &sof_imx8m_ops, -}; -#endif - -static const struct dev_pm_ops sof_of_pm = { +const struct dev_pm_ops sof_of_pm = { .prepare = snd_sof_prepare, .complete = snd_sof_complete, SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) }; +EXPORT_SYMBOL(sof_of_pm); static void sof_of_probe_complete(struct device *dev) { /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - - pm_runtime_mark_last_busy(dev); - pm_runtime_put_autosuspend(dev); } -static int sof_of_probe(struct platform_device *pdev) +int sof_of_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct sof_dev_desc *desc; @@ -90,9 +66,15 @@ static int sof_of_probe(struct platform_device *pdev) sof_pdata->dev = &pdev->dev; sof_pdata->fw_filename = desc->default_fw_filename; - /* TODO: read alternate fw and tplg filenames from DT */ - sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; - sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; + if (fw_path) + sof_pdata->fw_filename_prefix = fw_path; + else + sof_pdata->fw_filename_prefix = sof_pdata->desc->default_fw_path; + + if (tplg_path) + sof_pdata->tplg_filename_prefix = tplg_path; + else + sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; /* set callback to be called on successful device probe to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_of_probe_complete; @@ -100,8 +82,9 @@ static int sof_of_probe(struct platform_device *pdev) /* call sof helper for DSP hardware probe */ return snd_sof_device_probe(dev, sof_pdata); } +EXPORT_SYMBOL(sof_of_probe); -static int sof_of_remove(struct platform_device *pdev) +int sof_of_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -110,29 +93,6 @@ static int sof_of_remove(struct platform_device *pdev) return 0; } - -static const struct of_device_id sof_of_ids[] = { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8) - { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc}, - { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc}, -#endif -#if IS_ENABLED(CONFIG_SND_SOC_SOF_IMX8M) - { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc}, -#endif - { } -}; -MODULE_DEVICE_TABLE(of, sof_of_ids); - -/* DT driver definition */ -static struct platform_driver snd_sof_of_driver = { - .probe = sof_of_probe, - .remove = sof_of_remove, - .driver = { - .name = "sof-audio-of", - .pm = &sof_of_pm, - .of_match_table = sof_of_ids, - }, -}; -module_platform_driver(snd_sof_of_driver); +EXPORT_SYMBOL(sof_of_remove); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index bc9e707656..20c6ca37db 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -59,22 +59,23 @@ static const struct dmi_system_id sof_tplg_table[] = { }, .driver_data = "sof-adl-rt5682-ssp0-max98373-ssp2.tplg", }, + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S"), + }, + .driver_data = "sof-adl-max98390-ssp2-rt5682-ssp0.tplg", + }, + {} }; static const struct dmi_system_id community_key_platforms[] = { { - .ident = "Up Squared", + .ident = "Up boards", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), - } - }, - { - .ident = "Up Extreme", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_BOARD_NAME, "UP-WHL01"), } }, { diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index fd8423172d..087935192c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -20,19 +20,31 @@ #include #include -/* debug flags */ +/* Flag definitions used in sof_core_debug (sof_debug module parameter) */ #define SOF_DBG_ENABLE_TRACE BIT(0) #define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */ +#define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */ +#define SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE BIT(3) /* 0: use topology token + * 1: override topology + */ +#define SOF_DBG_DYNAMIC_PIPELINES_ENABLE BIT(4) /* 0: use static pipelines + * 1: use dynamic pipelines + */ +#define SOF_DBG_DISABLE_MULTICORE BIT(5) /* schedule all pipelines/widgets + * on primary core + */ +#define SOF_DBG_PRINT_ALL_DUMPS BIT(6) /* Print all ipc and dsp dumps */ +/* Flag definitions used for controlling the DSP dump behavior */ #define SOF_DBG_DUMP_REGS BIT(0) #define SOF_DBG_DUMP_MBOX BIT(1) #define SOF_DBG_DUMP_TEXT BIT(2) #define SOF_DBG_DUMP_PCI BIT(3) -#define SOF_DBG_DUMP_FORCE_ERR_LEVEL BIT(4) /* used to dump dsp status with error log level */ - +/* Output this dump (at the DEBUG level) only when SOF_DBG_PRINT_ALL_DUMPS is set */ +#define SOF_DBG_DUMP_OPTIONAL BIT(4) /* global debug state set by SOF_DBG_ flags */ -extern int sof_core_debug; +bool sof_debug_check_flag(int mask); /* max BARs mmaped devices can use */ #define SND_SOF_BARS 8 @@ -61,6 +73,9 @@ extern int sof_core_debug; /* So far the primary core on all DSPs has ID 0 */ #define SOF_DSP_PRIMARY_CORE 0 +/* max number of DSP cores */ +#define SOF_MAX_DSP_NUM_CORES 8 + /* DSP power state */ enum sof_dsp_power_states { SOF_DSP_PM_D0, @@ -83,6 +98,16 @@ enum sof_system_suspend_state { SOF_SUSPEND_S3, }; +enum sof_dfsentry_type { + SOF_DFSENTRY_TYPE_IOMEM = 0, + SOF_DFSENTRY_TYPE_BUF, +}; + +enum sof_debugfs_access_type { + SOF_DEBUGFS_ACCESS_ALWAYS = 0, + SOF_DEBUGFS_ACCESS_D0_ONLY, +}; + struct snd_sof_dev; struct snd_sof_ipc_msg; struct snd_sof_ipc; @@ -107,10 +132,8 @@ struct snd_sof_dsp_ops { int (*run)(struct snd_sof_dev *sof_dev); /* mandatory */ int (*stall)(struct snd_sof_dev *sof_dev, unsigned int core_mask); /* optional */ int (*reset)(struct snd_sof_dev *sof_dev); /* optional */ - int (*core_power_up)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ - int (*core_power_down)(struct snd_sof_dev *sof_dev, - unsigned int core_mask); /* optional */ + int (*core_get)(struct snd_sof_dev *sof_dev, int core); /* optional */ + int (*core_put)(struct snd_sof_dev *sof_dev, int core); /* optional */ /* * Register IO: only used by respective drivers themselves, @@ -127,12 +150,20 @@ struct snd_sof_dsp_ops { void __iomem *addr); /* optional */ /* memcpy IO */ - void (*block_read)(struct snd_sof_dev *sof_dev, u32 bar, - u32 offset, void *dest, - size_t size); /* mandatory */ - void (*block_write)(struct snd_sof_dev *sof_dev, u32 bar, - u32 offset, void *src, - size_t size); /* mandatory */ + int (*block_read)(struct snd_sof_dev *sof_dev, + enum snd_sof_fw_blk_type type, u32 offset, + void *dest, size_t size); /* mandatory */ + int (*block_write)(struct snd_sof_dev *sof_dev, + enum snd_sof_fw_blk_type type, u32 offset, + void *src, size_t size); /* mandatory */ + + /* Mailbox IO */ + void (*mailbox_read)(struct snd_sof_dev *sof_dev, + u32 offset, void *dest, + size_t size); /* optional */ + void (*mailbox_write)(struct snd_sof_dev *sof_dev, + u32 offset, void *src, + size_t size); /* optional */ /* doorbell */ irqreturn_t (*irq_handler)(int irq, void *context); /* optional */ @@ -178,6 +209,9 @@ struct snd_sof_dsp_ops { snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + /* pcm ack */ + int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) /* Except for probe_pointer, all probe ops are mandatory */ int (*probe_assign)(struct snd_sof_dev *sdev, @@ -200,9 +234,9 @@ struct snd_sof_dsp_ops { #endif /* host read DSP stream data */ - void (*ipc_msg_data)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz); /* mandatory */ + int (*ipc_msg_data)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz); /* mandatory */ /* host configure DSP HW parameters */ int (*ipc_pcm_params)(struct snd_sof_dev *sdev, @@ -237,6 +271,10 @@ struct snd_sof_dsp_ops { void (*dbg_dump)(struct snd_sof_dev *sof_dev, u32 flags); /* optional */ void (*ipc_dump)(struct snd_sof_dev *sof_dev); /* optional */ + int (*debugfs_add_region_item)(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, u32 offset, + size_t size, const char *name, + enum sof_debugfs_access_type access_type); /* optional */ /* host DMA trace initialization */ int (*trace_init)(struct snd_sof_dev *sdev, @@ -257,8 +295,8 @@ struct snd_sof_dsp_ops { void *pdata); /* optional */ void (*machine_unregister)(struct snd_sof_dev *sdev, void *pdata); /* optional */ - void (*machine_select)(struct snd_sof_dev *sdev); /* optional */ - void (*set_mach_params)(const struct snd_soc_acpi_mach *mach, + struct snd_soc_acpi_mach * (*machine_select)(struct snd_sof_dev *sdev); /* optional */ + void (*set_mach_params)(struct snd_soc_acpi_mach *mach, struct snd_sof_dev *sdev); /* optional */ /* DAI ops */ @@ -268,33 +306,17 @@ struct snd_sof_dsp_ops { /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ u32 hw_info; - const struct sof_arch_ops *arch_ops; + const struct dsp_arch_ops *dsp_arch_ops; }; /* DSP architecture specific callbacks for oops and stack dumps */ -struct sof_arch_ops { - void (*dsp_oops)(struct snd_sof_dev *sdev, void *oops); - void (*dsp_stack)(struct snd_sof_dev *sdev, void *oops, +struct dsp_arch_ops { + void (*dsp_oops)(struct snd_sof_dev *sdev, const char *level, void *oops); + void (*dsp_stack)(struct snd_sof_dev *sdev, const char *level, void *oops, u32 *stack, u32 stack_words); }; -#define sof_arch_ops(sdev) ((sdev)->pdata->desc->ops->arch_ops) - -/* DSP device HW descriptor mapping between bus ID and ops */ -struct sof_ops_table { - const struct sof_dev_desc *desc; - const struct snd_sof_dsp_ops *ops; -}; - -enum sof_dfsentry_type { - SOF_DFSENTRY_TYPE_IOMEM = 0, - SOF_DFSENTRY_TYPE_BUF, -}; - -enum sof_debugfs_access_type { - SOF_DEBUGFS_ACCESS_ALWAYS = 0, - SOF_DEBUGFS_ACCESS_D0_ONLY, -}; +#define sof_dsp_arch_ops(sdev) ((sdev)->pdata->desc->ops->dsp_arch_ops) /* FS entry for debug files that can expose DSP memories, registers */ struct snd_sof_dfsentry { @@ -309,6 +331,10 @@ struct snd_sof_dfsentry { enum sof_debugfs_access_type access_type; #if ENABLE_DEBUGFS_CACHEBUF char *cache_buf; /* buffer to cache the contents of debugfs memory */ +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) + void *msg_inject_tx; + void *msg_inject_rx; #endif struct snd_sof_dev *sdev; struct list_head list; /* list in sdev dfsentry list */ @@ -351,15 +377,6 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; -enum snd_sof_fw_state { - SOF_FW_BOOT_NOT_STARTED = 0, - SOF_FW_BOOT_PREPARE, - SOF_FW_BOOT_IN_PROGRESS, - SOF_FW_BOOT_FAILED, - SOF_FW_BOOT_READY_FAILED, /* firmware booted but fw_ready op failed */ - SOF_FW_BOOT_COMPLETE, -}; - /* * SOF Device Level. */ @@ -384,7 +401,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; - enum snd_sof_fw_state fw_state; + enum sof_fw_state fw_state; bool first_boot; /* work queue in case the probe is implemented in two steps */ @@ -413,6 +430,8 @@ struct snd_sof_dev { /* debug */ struct dentry *debugfs_root; struct list_head dfsentry_list; + bool dbg_dump_printed; + bool ipc_dump_printed; /* firmware loader */ struct snd_dma_buffer dmab; @@ -455,6 +474,18 @@ struct snd_sof_dev { bool msi_enabled; + /* DSP core context */ + u32 num_cores; + + /* + * ref count per core that will be modified during system suspend/resume and during pcm + * hw_params/hw_free. This doesn't need to be protected with a mutex because pcm + * hw_params/hw_free are already protected by the PCM mutex in the ALSA framework in + * sound/core/ when streams are active and during system suspend/resume, streams are + * already suspended. + */ + int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES]; + void *private; /* core does not touch this */ }; @@ -485,27 +516,23 @@ int snd_sof_create_page_table(struct device *dev, /* * Firmware loading. */ -int snd_sof_load_firmware(struct snd_sof_dev *sdev); int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev); int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev); int snd_sof_run_firmware(struct snd_sof_dev *sdev); int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, struct snd_sof_mod_hdr *module); void snd_sof_fw_unload(struct snd_sof_dev *sdev); -int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset); /* * IPC low level APIs. */ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev); void snd_sof_ipc_free(struct snd_sof_dev *sdev); +void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev); void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id); void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev); int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev, struct sof_ipc_pcm_params *params); -int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox, - size_t dspbox_size, u32 hostbox, - size_t hostbox_size); int snd_sof_ipc_valid(struct snd_sof_dev *sdev); int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, @@ -513,6 +540,12 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); +int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev); +static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id) +{ + snd_sof_ipc_get_reply(sdev); + snd_sof_ipc_reply(sdev, msg_id); +} /* * Trace/debug @@ -522,45 +555,53 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev); void snd_sof_free_trace(struct snd_sof_dev *sdev); int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); -int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, - void __iomem *base, size_t size, - const char *name, - enum sof_debugfs_access_type access_type); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); -void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code, - u32 tracep_code, void *oops, - struct sof_ipc_panic_info *panic_info, - void *stack, size_t stack_words); +void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, + u32 panic_code, u32 tracep_code, void *oops, + struct sof_ipc_panic_info *panic_info, + void *stack, size_t stack_words); int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); - -/* - * Platform specific ops. - */ -extern struct snd_compress_ops sof_compressed_ops; +int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, + enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, + const char *name, enum sof_debugfs_access_type access_type); /* * DSP Architectures. */ -static inline void sof_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static inline void sof_stack(struct snd_sof_dev *sdev, const char *level, + void *oops, u32 *stack, u32 stack_words) { - sof_arch_ops(sdev)->dsp_stack(sdev, oops, stack, stack_words); + sof_dsp_arch_ops(sdev)->dsp_stack(sdev, level, oops, stack, + stack_words); } -static inline void sof_oops(struct snd_sof_dev *sdev, void *oops) +static inline void sof_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { - if (sof_arch_ops(sdev)->dsp_oops) - sof_arch_ops(sdev)->dsp_oops(sdev, oops); + if (sof_dsp_arch_ops(sdev)->dsp_oops) + sof_dsp_arch_ops(sdev)->dsp_oops(sdev, level, oops); } -extern const struct sof_arch_ops sof_xtensa_arch_ops; +extern const struct dsp_arch_ops sof_xtensa_arch_ops; + +/* + * Firmware state tracking + */ +static inline void sof_set_fw_state(struct snd_sof_dev *sdev, + enum sof_fw_state new_state) +{ + if (sdev->fw_state == new_state) + return; + + dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state); + sdev->fw_state = new_state; +} /* * Utilities @@ -573,33 +614,24 @@ void sof_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); void sof_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes); -void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src, - size_t size); -void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest, - size_t size); +int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size); +int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size); int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id); -void intel_ipc_msg_data(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - void *p, size_t sz); -int intel_ipc_pcm_params(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - const struct sof_ipc_pcm_params_reply *reply); +int sof_ipc_msg_data(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + void *p, size_t sz); +int sof_ipc_pcm_params(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + const struct sof_ipc_pcm_params_reply *reply); -int intel_pcm_open(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); -int intel_pcm_close(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); +int sof_stream_pcm_open(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); +int sof_stream_pcm_close(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); int sof_machine_check(struct snd_sof_dev *sdev); - -#define sof_dev_dbg_or_err(dev, is_err, fmt, ...) \ - do { \ - if (is_err) \ - dev_err(dev, "error: " fmt, __VA_ARGS__); \ - else \ - dev_dbg(dev, fmt, __VA_ARGS__); \ - } while (0) - #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 1bb2dcf37f..e72dcae5e7 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -376,6 +376,10 @@ static const struct sof_dai_types sof_dais[] = { {"ALH", SOF_DAI_INTEL_ALH}, {"SAI", SOF_DAI_IMX_SAI}, {"ESAI", SOF_DAI_IMX_ESAI}, + {"ACP", SOF_DAI_AMD_BT}, + {"ACPSP", SOF_DAI_AMD_SP}, + {"ACPDMIC", SOF_DAI_AMD_DMIC}, + {"AFE", SOF_DAI_MEDIATEK_AFE}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -572,6 +576,12 @@ static const struct sof_topology_token sched_tokens[] = { offsetof(struct sof_ipc_pipe_new, time_domain), 0}, }; +static const struct sof_topology_token pipeline_tokens[] = { + {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0}, + +}; + /* volume */ static const struct sof_topology_token volume_tokens[] = { {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, @@ -797,6 +807,19 @@ static const struct sof_topology_token led_tokens[] = { get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, }; +/* AFE */ +static const struct sof_topology_token afe_tokens[] = { + {SOF_TKN_MEDIATEK_AFE_RATE, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate), 0}, + {SOF_TKN_MEDIATEK_AFE_CH, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels), 0}, + {SOF_TKN_MEDIATEK_AFE_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_dai_mtk_afe_params, format), 0}, +}; + static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -1067,11 +1090,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, /* set cmd for mixer control */ if (le32_to_cpu(mc->max) == 1) { - scontrol->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH; goto skip; } - scontrol->cmd = SOF_CTRL_CMD_VOLUME; + scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { @@ -1142,7 +1165,7 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); scontrol->control_data->index = kc->index; - scontrol->cmd = SOF_CTRL_CMD_ENUM; + scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1188,7 +1211,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, } scontrol->comp_id = sdev->next_comp_id; - scontrol->cmd = SOF_CTRL_CMD_BINARY; + scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY; scontrol->control_data->index = kc->index; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", @@ -1250,6 +1273,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, return -ENOMEM; scontrol->scomp = scomp; + scontrol->access = kc->access; switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1322,69 +1346,6 @@ static int sof_control_unload(struct snd_soc_component *scomp, * DAI Topology */ -/* Static DSP core power management so far, should be extended in the future */ -static int sof_core_enable(struct snd_sof_dev *sdev, int core) -{ - struct sof_ipc_pm_core_config pm_core_config = { - .hdr = { - .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, - .size = sizeof(pm_core_config), - }, - .enable_mask = sdev->enabled_cores_mask | BIT(core), - }; - int ret; - - if (sdev->enabled_cores_mask & BIT(core)) - return 0; - - /* power up the core if it is host managed */ - ret = snd_sof_dsp_core_power_up(sdev, BIT(core)); - if (ret < 0) { - dev_err(sdev->dev, "error: %d powering up core %d\n", - ret, core); - return ret; - } - - /* Now notify DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), - &pm_core_config, sizeof(pm_core_config)); - if (ret < 0) { - dev_err(sdev->dev, "error: core %d enable ipc failure %d\n", - core, ret); - goto err; - } - return ret; -err: - /* power down core if it is host managed and return the original error if this fails too */ - if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0) - dev_err(sdev->dev, "error: powering down core %d\n", core); - - return ret; -} - -int sof_pipeline_core_enable(struct snd_sof_dev *sdev, - const struct snd_sof_widget *swidget) -{ - const struct sof_ipc_pipe_new *pipeline; - int ret; - - if (swidget->id == snd_soc_dapm_scheduler) { - pipeline = swidget->private; - } else { - pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id); - if (!pipeline) - return -ENOENT; - } - - /* First enable the pipeline core */ - ret = sof_core_enable(sdev, pipeline->core); - if (ret < 0) - return ret; - - return sof_core_enable(sdev, swidget->core); -} - static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw, @@ -1512,10 +1473,8 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_dai *comp_dai; size_t ipc_size = sizeof(*comp_dai); @@ -1552,10 +1511,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, swidget->widget->name, comp_dai->type, comp_dai->dai_index); sof_dbg_comp_config(scomp, &comp_dai->config); - ret = sof_ipc_tx_message(sdev->ipc, comp_dai->comp.hdr.cmd, - comp_dai, ipc_size, r, sizeof(*r)); - - if (ret == 0 && dai) { + if (dai) { dai->scomp = scomp; /* @@ -1577,10 +1533,8 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_buffer *buffer; int ret; @@ -1612,15 +1566,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, swidget->private = buffer; - ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer, - sizeof(*buffer), r, sizeof(*r)); - if (ret < 0) { - dev_err(scomp->dev, "error: buffer %s load failed\n", - swidget->widget->name); - kfree(buffer); - } - - return ret; + return 0; } /* bind PCM ID to host component ID */ @@ -1649,10 +1595,8 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, enum sof_ipc_stream_direction dir, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_host *host; size_t ipc_size = sizeof(*host); @@ -1691,10 +1635,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, swidget->private = host; - ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(host); return ret; @@ -1703,28 +1644,9 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, /* * Pipeline Topology */ -int sof_load_pipeline_ipc(struct device *dev, - struct sof_ipc_pipe_new *pipeline, - struct sof_ipc_comp_reply *r) -{ - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - int ret = sof_core_enable(sdev, pipeline->core); - - if (ret < 0) - return ret; - - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), r, sizeof(*r)); - if (ret < 0) - dev_err(dev, "error: load pipeline ipc failure\n"); - - return ret; -} - static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_pipe_new *pipeline; @@ -1764,16 +1686,30 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, goto err; } - dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n", + ret = sof_parse_tokens(scomp, swidget, pipeline_tokens, + ARRAY_SIZE(pipeline_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n", + private->size); + goto err; + } + + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) + pipeline->core = SOF_DSP_PRIMARY_CORE; + + if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) + swidget->dynamic_pipeline_widget = + sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); + + dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", swidget->widget->name, pipeline->period, pipeline->priority, - pipeline->period_mips, pipeline->core, pipeline->frames_per_sched); + pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, + swidget->dynamic_pipeline_widget); swidget->private = pipeline; - /* send ipc's to create pipeline comp and power up schedule core */ - ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r); - if (ret >= 0) - return ret; + return 0; err: kfree(pipeline); return ret; @@ -1785,10 +1721,8 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mixer *mixer; size_t ipc_size = sizeof(*mixer); @@ -1817,12 +1751,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, swidget->private = mixer; - ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer, - ipc_size, r, sizeof(*r)); - if (ret < 0) - kfree(mixer); - - return ret; + return 0; } /* @@ -1830,10 +1759,8 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, */ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mux *mux; size_t ipc_size = sizeof(*mux); @@ -1862,12 +1789,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, swidget->private = mux; - ret = sof_ipc_tx_message(sdev->ipc, mux->comp.hdr.cmd, mux, - ipc_size, r, sizeof(*r)); - if (ret < 0) - kfree(mux); - - return ret; + return 0; } /* @@ -1876,8 +1798,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; @@ -1937,10 +1858,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } } - ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(volume); return ret; @@ -1952,10 +1870,8 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, static int sof_widget_load_src(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_src *src; size_t ipc_size = sizeof(*src); @@ -1994,10 +1910,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, swidget->private = src; - ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(src); return ret; @@ -2009,10 +1922,8 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_asrc *asrc; size_t ipc_size = sizeof(*asrc); @@ -2053,10 +1964,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, swidget->private = asrc; - ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(asrc); return ret; @@ -2068,10 +1976,8 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_tone *tone; size_t ipc_size = sizeof(*tone); @@ -2110,10 +2016,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, swidget->private = tone; - ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(tone); return ret; @@ -2173,7 +2076,7 @@ static int sof_get_control_data(struct snd_soc_component *scomp, *size += wdata[i].pdata->size; /* get data type */ - switch (wdata[i].control->cmd) { + switch (wdata[i].control->control_data->cmd) { case SOF_CTRL_CMD_VOLUME: case SOF_CTRL_CMD_ENUM: case SOF_CTRL_CMD_SWITCH: @@ -2195,10 +2098,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp, static int sof_process_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r, int type) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_dapm_widget *widget = swidget->widget; struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_process *process; @@ -2272,33 +2173,6 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, process->size = ipc_data_size; swidget->private = process; - - ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process, - ipc_size, r, sizeof(*r)); - - if (ret < 0) { - dev_err(scomp->dev, "error: create process failed\n"); - goto err; - } - - /* we sent the data in single message so return */ - if (ipc_data_size) - goto out; - - /* send control data with large message supported method */ - for (i = 0; i < widget->num_kcontrols; i++) { - wdata[i].control->readback_offset = 0; - ret = snd_sof_ipc_set_get_comp_data(wdata[i].control, - wdata[i].ipc_cmd, - wdata[i].ctrl_type, - wdata[i].control->cmd, - true); - if (ret != 0) { - dev_err(scomp->dev, "error: send control failed\n"); - break; - } - } - err: if (ret < 0) kfree(process); @@ -2314,8 +2188,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, static int sof_widget_load_process(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_process config; @@ -2341,8 +2214,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, } /* now load process specific data and send IPC */ - ret = sof_process_load(scomp, index, swidget, tw, r, - find_process_comp_type(config.type)); + ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type)); if (ret < 0) { dev_err(scomp->dev, "error: process loading failed\n"); return ret; @@ -2391,8 +2263,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *dai; - struct sof_ipc_comp_reply reply; - struct snd_sof_control *scontrol; struct sof_ipc_comp comp = { .core = SOF_DSP_PRIMARY_CORE, }; @@ -2409,7 +2279,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->id = w->id; swidget->pipeline_id = index; swidget->private = NULL; - memset(&reply, 0, sizeof(reply)); dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", swidget->comp_id, index, swidget->id, tw->name, @@ -2426,15 +2295,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return ret; } - swidget->core = comp.core; + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) + comp.core = SOF_DSP_PRIMARY_CORE; - /* default is primary core, safe to call for already enabled cores */ - ret = sof_core_enable(sdev, comp.core); - if (ret < 0) { - dev_err(scomp->dev, "error: enable core: %d\n", ret); - kfree(swidget); - return ret; - } + swidget->core = comp.core; ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), tw->priv.array, @@ -2456,57 +2320,51 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return -ENOMEM; } - ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, dai); - if (ret == 0) { - sof_connect_dai_widget(scomp, w, tw, dai); - list_add(&dai->list, &sdev->dai_list); - swidget->private = dai; - } else { + ret = sof_widget_load_dai(scomp, index, swidget, tw, dai); + if (!ret) + ret = sof_connect_dai_widget(scomp, w, tw, dai); + if (ret < 0) { kfree(dai); + break; } + list_add(&dai->list, &sdev->dai_list); + swidget->private = dai; break; case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mixer(scomp, index, swidget, tw); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); - /* Find scontrol for this pga and set readback offset*/ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - if (scontrol->comp_id == swidget->comp_id) { - scontrol->readback_offset = reply.offset; - break; - } - } + ret = sof_widget_load_pga(scomp, index, swidget, tw); break; case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_buffer(scomp, index, swidget, tw); break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_pipeline(scomp, index, swidget, tw); break; case snd_soc_dapm_aif_out: ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_CAPTURE, tw, &reply); + SOF_IPC_STREAM_CAPTURE, tw); break; case snd_soc_dapm_aif_in: ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_PLAYBACK, tw, &reply); + SOF_IPC_STREAM_PLAYBACK, tw); break; case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_src(scomp, index, swidget, tw); break; case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_asrc(scomp, index, swidget, tw); break; case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_siggen(scomp, index, swidget, tw); break; case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_process(scomp, index, swidget, tw); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mux(scomp, index, swidget, tw); break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: @@ -2517,12 +2375,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } /* check IPC reply */ - if (ret < 0 || reply.rhdr.error < 0) { + if (ret < 0) { dev_err(scomp->dev, - "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", + "error: failed to add widget id %d type %d name : %s stream %s\n", tw->shift, swidget->id, tw->name, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 - ? tw->sname : "none", reply.rhdr.error); + ? tw->sname : "none"); kfree(swidget); return ret; } @@ -2564,10 +2422,8 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; - struct sof_ipc_pipe_new *pipeline; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; struct soc_mixer_control *sm; @@ -2594,24 +2450,6 @@ static int sof_widget_unload(struct snd_soc_component *scomp, list_del(&dai->list); } break; - case snd_soc_dapm_scheduler: - - /* power down the pipeline schedule core */ - pipeline = swidget->private; - - /* - * Runtime PM should still function normally if topology loading fails and - * it's components are unloaded. Do not power down the primary core so that the - * CTX_SAVE IPC can succeed during runtime suspend. - */ - if (pipeline->core == SOF_DSP_PRIMARY_CORE) - break; - - ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core); - if (ret < 0) - dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n", - pipeline->core); - break; default: break; } @@ -2680,8 +2518,10 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, for_each_pcm_streams(stream) { spcm->stream[stream].comp_id = COMP_ID_UNASSIGNED; - INIT_WORK(&spcm->stream[stream].period_elapsed_work, - snd_sof_pcm_period_elapsed_work); + if (pcm->compress) + snd_sof_compr_init_elapsed_work(&spcm->stream[stream].period_elapsed_work); + else + snd_sof_pcm_init_elapsed_work(&spcm->stream[stream].period_elapsed_work); } spcm->pcm = *pcm; @@ -2837,9 +2677,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, continue; if (strcmp(link->name, dai->name) == 0) { - struct sof_ipc_reply reply; - int ret; - /* * the same dai config will be applied to all DAIs in * the same dai link. We have to ensure that the ipc @@ -2851,18 +2688,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, dev_dbg(sdev->dev, "set DAI config for %s index %d\n", dai->name, config[curr_conf].dai_index); - /* send message to DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - config[curr_conf].hdr.cmd, - &config[curr_conf], size, - &reply, sizeof(reply)); - - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to set DAI config for %s index %d\n", - dai->name, config[curr_conf].dai_index); - return ret; - } dai->number_configs = num_conf; dai->current_config = curr_conf; @@ -2870,9 +2695,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, if (!dai->dai_config) return -ENOMEM; - /* set cpu_dai_name */ - dai->cpu_dai_name = link->cpus->dai_name; - found = 1; } } @@ -2942,12 +2764,12 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); - dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n", + dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n", config[i].dai_index, config[i].format, config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, - config[i].ssp.mclk_id, config[i].ssp.quirks); + config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); /* validate SSP fsync rate and channel count */ if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { @@ -3087,6 +2909,144 @@ static int sof_link_esai_load(struct snd_soc_component *scomp, int index, return ret; } +static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", + config->dai_index, config->acpdmic.tdm_slots, + config->acpdmic.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + config->dai_index, config->acpbt.tdm_slots, + config->acpbt.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params)); + config->hdr.size = size; + + config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", + config->dai_index, config->acpsp.tdm_slots, + config->acpsp.fsync_rate); + + /* set config for all DAI's with name matching the link name */ + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n", + config->dai_index); + return ret; +} + +static int sof_link_afe_load(struct snd_soc_component *scomp, int index, + struct snd_soc_dai_link *link, + struct snd_soc_tplg_link_config *cfg, + struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_private *private = &cfg->priv; + struct snd_soc_dai *dai; + u32 size = sizeof(*config); + int ret; + + config->hdr.size = size; + + /* get any bespoke DAI tokens */ + ret = sof_parse_tokens(scomp, &config->afe, afe_tokens, + ARRAY_SIZE(afe_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "parse afe tokens failed %d\n", + le32_to_cpu(private->size)); + return ret; + } + + dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", + config->afe.rate, config->afe.channels, config->afe.format); + + dai = snd_soc_find_dai(link->cpus); + if (!dai) { + dev_err(scomp->dev, "%s: failed to find dai %s", __func__, link->cpus->dai_name); + return -EINVAL; + } + + config->afe.stream_id = DMA_CHAN_INVALID; + + ret = sof_set_dai_config(sdev, size, link, config); + if (ret < 0) + dev_err(scomp->dev, "failed to process afe dai link %s", link->name); + + return ret; +} + static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg, @@ -3372,6 +3332,19 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, case SOF_DAI_IMX_ESAI: ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); break; + case SOF_DAI_AMD_BT: + ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_SP: + ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; + case SOF_DAI_AMD_DMIC: + ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, + config); + break; + case SOF_DAI_MEDIATEK_AFE: + ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config); + break; default: dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); ret = -EINVAL; @@ -3392,7 +3365,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; - struct sof_ipc_reply reply; int ret = 0; /* allocate memory for sroute and connect */ @@ -3467,33 +3439,11 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, route->source, route->sink); goto err; } else { - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), - &reply, sizeof(reply)); - - /* check IPC return value */ - if (ret < 0) { - dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n", - route->sink, - route->control ? route->control : "none", - route->source); - goto err; - } - - /* check IPC reply */ - if (reply.error < 0) { - dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", - route->sink, - route->control ? route->control : "none", - route->source, reply.error); - ret = reply.error; - goto err; - } - sroute->route = route; dobj->private = sroute; sroute->private = connect; + sroute->src_widget = source_swidget; + sroute->sink_widget = sink_swidget; /* add route to route list */ list_add(&sroute->list, &sdev->route_list); @@ -3507,59 +3457,14 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, return ret; } -/* Function to set the initial value of SOF kcontrols. - * The value will be stored in scontrol->control_data - */ -static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_control *scontrol = NULL; - int ipc_cmd, ctrl_type; - int ret = 0; - - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_GET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET; - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_GET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_GET; - break; - default: - dev_err(scomp->dev, - "error: Invalid scontrol->cmd: %d\n", - scontrol->cmd); - return -EINVAL; - } - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - false); - if (ret < 0) { - dev_warn(scomp->dev, - "error: kcontrol value get for widget: %d\n", - scontrol->comp_id); - } - } - - return ret; -} - -int snd_sof_complete_pipeline(struct device *dev, +int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct sof_ipc_pipe_ready ready; struct sof_ipc_reply reply; int ret; - dev_dbg(dev, "tplg: complete pipeline %s id %d\n", + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", swidget->widget->name, swidget->comp_id); memset(&ready, 0, sizeof(ready)); @@ -3575,31 +3480,84 @@ int snd_sof_complete_pipeline(struct device *dev, return 1; } +/** + * sof_set_pipe_widget - Set pipe_widget for a component + * @sdev: pointer to struct snd_sof_dev + * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler + * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget + * + * Return: 0 if successful, -EINVAL on error. + * The function checks if @swidget is associated with any volatile controls. If so, setting + * the dynamic_pipeline_widget is disallowed. + */ +static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget, + struct snd_sof_widget *swidget) +{ + struct snd_sof_control *scontrol; + + if (pipe_widget->dynamic_pipeline_widget) { + /* dynamic widgets cannot have volatile kcontrols */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + if (scontrol->comp_id == swidget->comp_id && + (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) { + dev_err(sdev->dev, + "error: volatile control found for dynamic widget %s\n", + swidget->widget->name); + return -EINVAL; + } + } + + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ + swidget->pipe_widget = pipe_widget; + swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; + + return 0; +} + /* completion - called at completion of firmware loading */ -static void sof_complete(struct snd_soc_component *scomp) +static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_widget *swidget; + struct snd_sof_widget *swidget, *comp_swidget; + int ret; - /* some widget types require completion notificattion */ + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->complete) - continue; - switch (swidget->id) { case snd_soc_dapm_scheduler: - swidget->complete = - snd_sof_complete_pipeline(scomp->dev, swidget); + /* + * Apply the dynamic_pipeline_widget flag and set the pipe_widget field + * for all widgets that have the same pipeline ID as the scheduler widget + */ + list_for_each_entry(comp_swidget, &sdev->widget_list, list) + if (comp_swidget->pipeline_id == swidget->pipeline_id) { + ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); + if (ret < 0) + return ret; + } break; default: break; } } - /* - * cache initial values of SOF kcontrols by reading DSP value over - * IPC. It may be overwritten by alsa-mixer after booting up - */ - snd_sof_cache_kcontrol_val(scomp); + + /* verify topology components loading including dynamic pipelines */ + if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { + ret = sof_set_up_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "error: topology verification failed %d\n", ret); + return ret; + } + + ret = sof_tear_down_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret); + return ret; + } + } + + /* set up static pipelines */ + return sof_set_up_pipelines(sdev, false); } /* manifest - optional to inform component of manifest */ diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 58f6ca5cf4..f13024c8eb 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -417,7 +417,7 @@ int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev) "error: fail in snd_sof_dma_trace_init %d\n", ret); return ret; } - dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag); + dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); /* send IPC to the DSP */ ret = sof_ipc_tx_message(sdev->ipc, @@ -480,7 +480,8 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev) goto table_err; sdev->dma_trace_pages = ret; - dev_dbg(sdev->dev, "dma_trace_pages: %d\n", sdev->dma_trace_pages); + dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", + __func__, sdev->dma_trace_pages); if (sdev->first_boot) { ret = trace_debugfs_create(sdev); @@ -538,6 +539,10 @@ EXPORT_SYMBOL(snd_sof_trace_notify_for_error); void snd_sof_release_trace(struct snd_sof_dev *sdev) { + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply ipc_reply; int ret; if (!sdev->dtrace_is_supported || !sdev->dtrace_is_enabled) @@ -548,6 +553,20 @@ void snd_sof_release_trace(struct snd_sof_dev *sdev) dev_err(sdev->dev, "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); + /* + * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from + * ABI 3.20.0 onwards + */ + if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { + hdr.size = sizeof(hdr); + hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; + + ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) + dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); + } + ret = snd_sof_dma_trace_release(sdev); if (ret < 0) dev_err(sdev->dev, diff --git a/sound/soc/sof/utils.c b/sound/soc/sof/utils.c index 5539d3afbe..66fa6602fb 100644 --- a/sound/soc/sof/utils.c +++ b/sound/soc/sof/utils.c @@ -14,6 +14,7 @@ #include #include #include "sof-priv.h" +#include "ops.h" /* * Register IO @@ -72,15 +73,21 @@ EXPORT_SYMBOL(sof_mailbox_read); * Memory copy. */ -void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src, - size_t size) +int sof_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *src, size_t size) { - void __iomem *dest = sdev->bar[bar] + offset; + int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); const u8 *src_byte = src; + void __iomem *dest; u32 affected_mask; u32 tmp; int m, n; + if (bar < 0) + return bar; + + dest = sdev->bar[bar] + offset; + m = size / 4; n = size % 4; @@ -100,15 +107,22 @@ void sof_block_write(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *src, tmp |= *(u32 *)(src_byte + m * 4) & affected_mask; iowrite32(tmp, dest + m * 4); } + + return 0; } EXPORT_SYMBOL(sof_block_write); -void sof_block_read(struct snd_sof_dev *sdev, u32 bar, u32 offset, void *dest, - size_t size) +int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, + u32 offset, void *dest, size_t size) { - void __iomem *src = sdev->bar[bar] + offset; + int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); - memcpy_fromio(dest, src, size); + if (bar < 0) + return bar; + + memcpy_fromio(dest, sdev->bar[bar] + offset, size); + + return 0; } EXPORT_SYMBOL(sof_block_read); diff --git a/sound/soc/sof/xtensa/core.c b/sound/soc/sof/xtensa/core.c index f6e3411b33..bebbe3a286 100644 --- a/sound/soc/sof/xtensa/core.c +++ b/sound/soc/sof/xtensa/core.c @@ -81,33 +81,39 @@ static const struct xtensa_exception_cause xtensa_exception_causes[] = { }; /* only need xtensa atm */ -static void xtensa_dsp_oops(struct snd_sof_dev *sdev, void *oops) +static void xtensa_dsp_oops(struct snd_sof_dev *sdev, const char *level, void *oops) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; int i; - dev_err(sdev->dev, "error: DSP Firmware Oops\n"); + dev_printk(level, sdev->dev, "error: DSP Firmware Oops\n"); for (i = 0; i < ARRAY_SIZE(xtensa_exception_causes); i++) { if (xtensa_exception_causes[i].id == xoops->exccause) { - dev_err(sdev->dev, "error: Exception Cause: %s, %s\n", - xtensa_exception_causes[i].msg, - xtensa_exception_causes[i].description); + dev_printk(level, sdev->dev, + "error: Exception Cause: %s, %s\n", + xtensa_exception_causes[i].msg, + xtensa_exception_causes[i].description); } } - dev_err(sdev->dev, "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", - xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); - dev_err(sdev->dev, "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", - xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); - dev_err(sdev->dev, "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", - xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); - dev_err(sdev->dev, "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", - xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); - dev_err(sdev->dev, "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", - xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); + dev_printk(level, sdev->dev, + "EXCCAUSE 0x%8.8x EXCVADDR 0x%8.8x PS 0x%8.8x SAR 0x%8.8x\n", + xoops->exccause, xoops->excvaddr, xoops->ps, xoops->sar); + dev_printk(level, sdev->dev, + "EPC1 0x%8.8x EPC2 0x%8.8x EPC3 0x%8.8x EPC4 0x%8.8x", + xoops->epc1, xoops->epc2, xoops->epc3, xoops->epc4); + dev_printk(level, sdev->dev, + "EPC5 0x%8.8x EPC6 0x%8.8x EPC7 0x%8.8x DEPC 0x%8.8x", + xoops->epc5, xoops->epc6, xoops->epc7, xoops->depc); + dev_printk(level, sdev->dev, + "EPS2 0x%8.8x EPS3 0x%8.8x EPS4 0x%8.8x EPS5 0x%8.8x", + xoops->eps2, xoops->eps3, xoops->eps4, xoops->eps5); + dev_printk(level, sdev->dev, + "EPS6 0x%8.8x EPS7 0x%8.8x INTENABL 0x%8.8x INTERRU 0x%8.8x", + xoops->eps6, xoops->eps7, xoops->intenable, xoops->interrupt); } -static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, - u32 stack_words) +static void xtensa_stack(struct snd_sof_dev *sdev, const char *level, void *oops, + u32 *stack, u32 stack_words) { struct sof_ipc_dsp_oops_xtensa *xoops = oops; u32 stack_ptr = xoops->plat_hdr.stackptr; @@ -115,7 +121,7 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, unsigned char buf[4 * 8 + 3 + 1]; int i; - dev_err(sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); + dev_printk(level, sdev->dev, "stack dump from 0x%8.8x\n", stack_ptr); /* * example output: @@ -124,11 +130,11 @@ static void xtensa_stack(struct snd_sof_dev *sdev, void *oops, u32 *stack, for (i = 0; i < stack_words; i += 4) { hex_dump_to_buffer(stack + i, 16, 16, 4, buf, sizeof(buf), false); - dev_err(sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); + dev_printk(level, sdev->dev, "0x%08x: %s\n", stack_ptr + i * 4, buf); } } -const struct sof_arch_ops sof_xtensa_arch_ops = { +const struct dsp_arch_ops sof_xtensa_arch_ops = { .dsp_oops = xtensa_dsp_oops, .dsp_stack = xtensa_stack, }; diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c index 2ed92c990b..dd9013c476 100644 --- a/sound/soc/sti/uniperif_player.c +++ b/sound/soc/sti/uniperif_player.c @@ -91,7 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); } ret = IRQ_HANDLED; @@ -105,7 +105,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } @@ -138,7 +138,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ - snd_pcm_stop_xrun(player->substream); + snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c index 1360593312..065c5f0d1f 100644 --- a/sound/soc/sti/uniperif_reader.c +++ b/sound/soc/sti/uniperif_reader.c @@ -65,7 +65,7 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { dev_err(reader->dev, "FIFO error detected\n"); - snd_pcm_stop_xrun(reader->substream); + snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); ret = IRQ_HANDLED; } diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index e6078f50e5..6ee714542b 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -12,7 +12,7 @@ #include #include #include - +#include #include #include #include @@ -334,6 +334,8 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, priv); + pm_runtime_enable(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &stm32_adfsdm_dai_component, &priv->dai_drv, 1); @@ -373,6 +375,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) static int stm32_adfsdm_remove(struct platform_device *pdev) { snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 717f45a834..ac5dff4d16 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1044,36 +1045,24 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Get clocks */ i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(i2s->pclk)) { - if (PTR_ERR(i2s->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get pclk: %ld\n", - PTR_ERR(i2s->pclk)); - return PTR_ERR(i2s->pclk); - } + if (IS_ERR(i2s->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->pclk), + "Could not get pclk\n"); i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); - if (IS_ERR(i2s->i2sclk)) { - if (PTR_ERR(i2s->i2sclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get i2sclk: %ld\n", - PTR_ERR(i2s->i2sclk)); - return PTR_ERR(i2s->i2sclk); - } + if (IS_ERR(i2s->i2sclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->i2sclk), + "Could not get i2sclk\n"); i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(i2s->x8kclk)) { - if (PTR_ERR(i2s->x8kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x8k parent clock: %ld\n", - PTR_ERR(i2s->x8kclk)); - return PTR_ERR(i2s->x8kclk); - } + if (IS_ERR(i2s->x8kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x8kclk), + "Could not get x8k parent clock\n"); i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(i2s->x11kclk)) { - if (PTR_ERR(i2s->x11kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get x11k parent clock: %ld\n", - PTR_ERR(i2s->x11kclk)); - return PTR_ERR(i2s->x11kclk); - } + if (IS_ERR(i2s->x11kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->x11kclk), + "Could not get x11k parent clock\n"); /* Register mclk provider if requested */ if (of_find_property(np, "#clock-cells", NULL)) { @@ -1096,12 +1085,10 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, /* Reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); @@ -1113,6 +1100,7 @@ static int stm32_i2s_remove(struct platform_device *pdev) { snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -1143,19 +1131,15 @@ static int stm32_i2s_probe(struct platform_device *pdev) i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", i2s->base, i2s->regmap_conf); - if (IS_ERR(i2s->regmap)) { - if (PTR_ERR(i2s->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(i2s->regmap)); - return PTR_ERR(i2s->regmap); - } + if (IS_ERR(i2s->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2s->regmap), + "Regmap init error\n"); + + pm_runtime_enable(&pdev->dev); ret = snd_dmaengine_pcm_register(&pdev->dev, &stm32_i2s_pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); ret = snd_soc_register_component(&pdev->dev, &stm32_i2s_component, i2s->dai_drv, 1); diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 058757c721..8e21e6f886 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -173,29 +173,20 @@ static int stm32_sai_probe(struct platform_device *pdev) if (!STM_SAI_IS_F4(sai)) { sai->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(sai->pclk)) { - if (PTR_ERR(sai->pclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing bus clock pclk: %ld\n", - PTR_ERR(sai->pclk)); - return PTR_ERR(sai->pclk); - } + if (IS_ERR(sai->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->pclk), + "missing bus clock pclk\n"); } sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); - if (IS_ERR(sai->clk_x8k)) { - if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x8k parent clock: %ld\n", - PTR_ERR(sai->clk_x8k)); - return PTR_ERR(sai->clk_x8k); - } + if (IS_ERR(sai->clk_x8k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x8k), + "missing x8k parent clock\n"); sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k"); - if (IS_ERR(sai->clk_x11k)) { - if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER) - dev_err(&pdev->dev, "missing x11k parent clock: %ld\n", - PTR_ERR(sai->clk_x11k)); - return PTR_ERR(sai->clk_x11k); - } + if (IS_ERR(sai->clk_x11k)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->clk_x11k), + "missing x11k parent clock\n"); /* init irqs */ sai->irq = platform_get_irq(pdev, 0); @@ -204,12 +195,10 @@ static int stm32_sai_probe(struct platform_device *pdev) /* reset */ rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 9c3b8e2096..dd636af81c 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1294,7 +1294,7 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, @@ -1312,7 +1312,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 16, .rate_min = 8000, .rate_max = 192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, @@ -1379,12 +1379,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, */ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, sai->regmap_config); - if (IS_ERR(sai->regmap)) { - if (PTR_ERR(sai->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(sai->regmap)); - return PTR_ERR(sai->regmap); - } + if (IS_ERR(sai->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap), + "Regmap init error\n"); /* Get direction property */ if (of_property_match_string(np, "dma-names", "tx") >= 0) { @@ -1472,12 +1469,9 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); - if (IS_ERR(sai->sai_ck)) { - if (PTR_ERR(sai->sai_ck) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Missing kernel clock sai_ck: %ld\n", - PTR_ERR(sai->sai_ck)); - return PTR_ERR(sai->sai_ck); - } + if (IS_ERR(sai->sai_ck)) + return dev_err_probe(&pdev->dev, PTR_ERR(sai->sai_ck), + "Missing kernel clock sai_ck\n"); ret = clk_prepare(sai->pdata->pclk); if (ret < 0) @@ -1551,11 +1545,8 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) conf = &stm32_sai_pcm_config_spdif; ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not register pcm dma\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Could not register pcm dma\n"); 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 index 48145f5535..6f7882c4fe 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -405,12 +406,9 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, int ret; spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (IS_ERR(spdifrx->ctrl_chan)) { - if (PTR_ERR(spdifrx->ctrl_chan) != -EPROBE_DEFER) - dev_err(dev, "dma_request_slave_channel error %ld\n", - PTR_ERR(spdifrx->ctrl_chan)); - return PTR_ERR(spdifrx->ctrl_chan); - } + if (IS_ERR(spdifrx->ctrl_chan)) + return dev_err_probe(dev, PTR_ERR(spdifrx->ctrl_chan), + "dma_request_slave_channel error\n"); spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); @@ -929,12 +927,9 @@ static int stm32_spdifrx_parse_of(struct platform_device *pdev, spdifrx->phys_addr = res->start; spdifrx->kclk = devm_clk_get(&pdev->dev, "kclk"); - if (IS_ERR(spdifrx->kclk)) { - if (PTR_ERR(spdifrx->kclk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Could not get kclk: %ld\n", - PTR_ERR(spdifrx->kclk)); - return PTR_ERR(spdifrx->kclk); - } + if (IS_ERR(spdifrx->kclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->kclk), + "Could not get kclk\n"); spdifrx->irq = platform_get_irq(pdev, 0); if (spdifrx->irq < 0) @@ -955,6 +950,7 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } @@ -985,12 +981,9 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) spdifrx->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "kclk", spdifrx->base, spdifrx->regmap_conf); - if (IS_ERR(spdifrx->regmap)) { - if (PTR_ERR(spdifrx->regmap) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Regmap init error %ld\n", - PTR_ERR(spdifrx->regmap)); - return PTR_ERR(spdifrx->regmap); - } + if (IS_ERR(spdifrx->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(spdifrx->regmap), + "Regmap init error\n"); ret = devm_request_irq(&pdev->dev, spdifrx->irq, stm32_spdifrx_isr, 0, dev_name(&pdev->dev), spdifrx); @@ -1000,23 +993,20 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) } rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(rst)) { - if (PTR_ERR(rst) != -EPROBE_DEFER) - dev_err(&pdev->dev, "Reset controller error %ld\n", - PTR_ERR(rst)); - return PTR_ERR(rst); - } + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), + "Reset controller error\n"); + reset_control_assert(rst); udelay(2); reset_control_deassert(rst); + pm_runtime_enable(&pdev->dev); + pcm_config = &stm32_spdifrx_pcm_config; ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "PCM DMA register error %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "PCM DMA register error\n"); 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 index da597e456b..60712f24ad 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1752,8 +1752,7 @@ static int sun4i_codec_probe(struct platform_device *pdev) GPIOD_OUT_LOW); if (IS_ERR(scodec->gpio_pa)) { ret = PTR_ERR(scodec->gpio_pa); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "Failed to get pa gpio\n"); return ret; } diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index a10949bf0c..17090f4315 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -186,6 +188,7 @@ struct sun4i_spdif_dev { struct regmap *regmap; struct snd_dmaengine_dai_dma_data dma_params_tx; const struct sun4i_spdif_quirks *quirks; + spinlock_t lock; }; static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) @@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int sun4i_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 *status = ucontrol->value.iec958.status; + + status[0] = 0xff; + status[1] = 0xff; + status[2] = 0xff; + status[3] = 0xff; + status[4] = 0xff; + status[5] = 0x03; + + return 0; +} + +static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + + spin_lock_irqsave(&host->lock, flags); + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®); + + status[0] = reg & 0xff; + status[1] = (reg >> 8) & 0xff; + status[2] = (reg >> 16) & 0xff; + status[3] = (reg >> 24) & 0xff; + + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, ®); + + status[4] = reg & 0xff; + status[5] = (reg >> 8) & 0x3; + + spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + +static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + u8 *status = ucontrol->value.iec958.status; + unsigned long flags; + unsigned int reg; + bool chg0, chg1; + + spin_lock_irqsave(&host->lock, flags); + + reg = (u32)status[3] << 24; + reg |= (u32)status[2] << 16; + reg |= (u32)status[1] << 8; + reg |= (u32)status[0]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0, + GENMASK(31,0), reg, &chg0); + + reg = (u32)status[5] << 8; + reg |= (u32)status[4]; + + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1, + GENMASK(9,0), reg, &chg1); + + reg = SUN4I_SPDIF_TXCFG_CHSTMODE; + if (status[0] & IEC958_AES0_NONAUDIO) + reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; + + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, + SUN4I_SPDIF_TXCFG_CHSTMODE | + SUN4I_SPDIF_TXCFG_NONAUDIO, reg); + + spin_unlock_irqrestore(&host->lock, flags); + + return chg0 || chg1; +} + +static struct snd_kcontrol_new sun4i_spdif_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status_mask + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = sun4i_spdif_info, + .get = sun4i_spdif_get_status, + .put = sun4i_spdif_set_status + } +}; + static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) { struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); + snd_soc_add_dai_controls(dai, sun4i_spdif_controls, + ARRAY_SIZE(sun4i_spdif_controls)); + return 0; } @@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev) return -ENOMEM; host->pdev = pdev; + spin_lock_init(&host->lock); /* Initialize this copy of the CPU DAI driver structure */ memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 518bfb724a..0bea2162f6 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -21,6 +21,7 @@ #include #include #include +#include #define SUN8I_SYSCLK_CTL 0x00c #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 @@ -72,6 +73,12 @@ #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 +#define SUN8I_AIF1_VOL_CTRL1 0x050 +#define SUN8I_AIF1_VOL_CTRL1_AD0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL1_AD0R_VOL 0 +#define SUN8I_AIF1_VOL_CTRL3 0x058 +#define SUN8I_AIF1_VOL_CTRL3_DA0L_VOL 8 +#define SUN8I_AIF1_VOL_CTRL3_DA0R_VOL 0 #define SUN8I_AIF2_ADCDAT_CTRL 0x084 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCL_ENA 15 #define SUN8I_AIF2_ADCDAT_CTRL_AIF2_ADCR_ENA 14 @@ -91,6 +98,12 @@ #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF1DA1R 10 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_AIF2DACL 9 #define SUN8I_AIF2_MXR_SRC_ADCR_MXR_SRC_ADCR 8 +#define SUN8I_AIF2_VOL_CTRL1 0x090 +#define SUN8I_AIF2_VOL_CTRL1_ADCL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL1_ADCR_VOL 0 +#define SUN8I_AIF2_VOL_CTRL2 0x098 +#define SUN8I_AIF2_VOL_CTRL2_DACL_VOL 8 +#define SUN8I_AIF2_VOL_CTRL2_DACR_VOL 0 #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1 (0x0 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF2 (0x1 << 0) #define SUN8I_AIF3_CLK_CTRL_AIF3_CLK_SRC_AIF1CLK (0x2 << 0) @@ -102,8 +115,14 @@ #define SUN8I_ADC_DIG_CTRL_ENAD 15 #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 +#define SUN8I_ADC_VOL_CTRL 0x104 +#define SUN8I_ADC_VOL_CTRL_ADCL_VOL 8 +#define SUN8I_ADC_VOL_CTRL_ADCR_VOL 0 #define SUN8I_DAC_DIG_CTRL 0x120 #define SUN8I_DAC_DIG_CTRL_ENDA 15 +#define SUN8I_DAC_VOL_CTRL 0x124 +#define SUN8I_DAC_VOL_CTRL_DACL_VOL 8 +#define SUN8I_DAC_VOL_CTRL_DACR_VOL 0 #define SUN8I_DAC_MXR_SRC 0x130 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 @@ -696,6 +715,41 @@ static struct snd_soc_dai_driver sun8i_codec_dais[] = { }, }; +static const DECLARE_TLV_DB_SCALE(sun8i_codec_vol_scale, -12000, 75, 1); + +static const struct snd_kcontrol_new sun8i_codec_controls[] = { + SOC_DOUBLE_TLV("AIF1 AD0 Capture Volume", + SUN8I_AIF1_VOL_CTRL1, + SUN8I_AIF1_VOL_CTRL1_AD0L_VOL, + SUN8I_AIF1_VOL_CTRL1_AD0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF1 DA0 Playback Volume", + SUN8I_AIF1_VOL_CTRL3, + SUN8I_AIF1_VOL_CTRL3_DA0L_VOL, + SUN8I_AIF1_VOL_CTRL3_DA0R_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 ADC Capture Volume", + SUN8I_AIF2_VOL_CTRL1, + SUN8I_AIF2_VOL_CTRL1_ADCL_VOL, + SUN8I_AIF2_VOL_CTRL1_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("AIF2 DAC Playback Volume", + SUN8I_AIF2_VOL_CTRL2, + SUN8I_AIF2_VOL_CTRL2_DACL_VOL, + SUN8I_AIF2_VOL_CTRL2_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("ADC Capture Volume", + SUN8I_ADC_VOL_CTRL, + SUN8I_ADC_VOL_CTRL_ADCL_VOL, + SUN8I_ADC_VOL_CTRL_ADCR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), + SOC_DOUBLE_TLV("DAC Playback Volume", + SUN8I_DAC_VOL_CTRL, + SUN8I_DAC_VOL_CTRL_DACL_VOL, + SUN8I_DAC_VOL_CTRL_DACR_VOL, + 0xc0, 0, sun8i_codec_vol_scale), +}; + static int sun8i_codec_aif_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1215,6 +1269,8 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component) } static const struct snd_soc_component_driver sun8i_soc_component = { + .controls = sun8i_codec_controls, + .num_controls = ARRAY_SIZE(sun8i_codec_controls), .dapm_widgets = sun8i_codec_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), .dapm_routes = sun8i_codec_dapm_routes, diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 83c87f35a7..cd454871d6 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -108,6 +108,54 @@ config SND_SOC_TEGRA210_ADMAIF channel. Buffer size is configurable for each ADMAIIF channel. Say Y or M if you want to add support for Tegra210 ADMAIF module. +config SND_SOC_TEGRA210_MVC + tristate "Tegra210 MVC module" + help + Config to enable the digital Master Volume Controller (MVC) which + provides gain or attenuation to a digital signal path. It can be + used in input or output signal path. It can be used either for + per-stream volume control or for master volume control. + Say Y or M if you want to add support for Tegra210 MVC module. + +config SND_SOC_TEGRA210_SFC + tristate "Tegra210 SFC module" + help + Config to enable the Sampling Frequency Converter (SFC) which + converts the sampling frequency of input signal to another + frequency. It supports sampling frequency conversion of streams + upto 2 channels (stereo). + Say Y or M if you want to add support for Tegra210 SFC module. + +config SND_SOC_TEGRA210_AMX + tristate "Tegra210 AMX module" + help + Config to enable the Audio Multiplexer (AMX) which can multiplex + four input streams (each of up to 16 channels) and generate + output stream (of up to 16 channels). A byte RAM helps to form an + output frame by any combination of bytes from the input frames. + Say Y or M if you want to add support for Tegra210 AMX module. + +config SND_SOC_TEGRA210_ADX + tristate "Tegra210 ADX module" + help + Config to enable the Audio Demultiplexer (ADX) which takes an + input stream (up to 16 channels) and demultiplexes it into four + output streams (each of up to 16 channels). A byte RAM helps to + form output frames by any combination of bytes from the input + frame. Its design is identical to that of byte RAM in the AMX + except that the data flow direction is reversed. + Say Y or M if you want to add support for Tegra210 ADX module. + +config SND_SOC_TEGRA210_MIXER + tristate "Tegra210 Mixer module" + help + Config to enable the Mixer module which can help to mix multiple + audio streams. It supports mixing of upto 10 input streams, + where each stream can contain maximum of 8 channels. It supports + 5 output each of which can be a mix of any combination of 10 + input streams. + Say Y or M if you want to add support for Tegra210 Mixer module. + config SND_SOC_TEGRA_AUDIO_GRAPH_CARD tristate "Audio Graph Card based Tegra driver" depends on SND_AUDIO_GRAPH_CARD diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index e2cec9ae31..f19d56690a 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -13,6 +13,11 @@ snd-soc-tegra210-dmic-objs := tegra210_dmic.o snd-soc-tegra210-i2s-objs := tegra210_i2s.o snd-soc-tegra186-dspk-objs := tegra186_dspk.o snd-soc-tegra210-admaif-objs := tegra210_admaif.o +snd-soc-tegra210-mvc-objs := tegra210_mvc.o +snd-soc-tegra210-sfc-objs := tegra210_sfc.o +snd-soc-tegra210-amx-objs := tegra210_amx.o +snd-soc-tegra210-adx-objs := tegra210_adx.o +snd-soc-tegra210-mixer-objs := tegra210_mixer.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o @@ -26,6 +31,11 @@ obj-$(CONFIG_SND_SOC_TEGRA210_AHUB) += snd-soc-tegra210-ahub.o obj-$(CONFIG_SND_SOC_TEGRA210_I2S) += snd-soc-tegra210-i2s.o obj-$(CONFIG_SND_SOC_TEGRA186_DSPK) += snd-soc-tegra186-dspk.o obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o +obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o +obj-$(CONFIG_SND_SOC_TEGRA210_SFC) += snd-soc-tegra210-sfc.o +obj-$(CONFIG_SND_SOC_TEGRA210_AMX) += snd-soc-tegra210-amx.o +obj-$(CONFIG_SND_SOC_TEGRA210_ADX) += snd-soc-tegra210-adx.o +obj-$(CONFIG_SND_SOC_TEGRA210_MIXER) += snd-soc-tegra210-mixer.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 266d2cab9f..27365a877e 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -262,10 +262,59 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) return 0; } +static const unsigned int tegra20_i2s_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000 +}; + +static int tegra20_i2s_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_i2s *i2s = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(i2s->clk_i2s); + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(tegra20_i2s_rates); i++) { + if (parent_rate % (tegra20_i2s_rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(tegra20_i2s_rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(tegra20_i2s_rates), + tegra20_i2s_rates, valid_rates); +} + +static int tegra20_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_i2s_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, + .startup = tegra20_i2s_startup, }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 7751575cd6..d09cd7ee68 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -7,12 +7,15 @@ */ #include +#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -22,12 +25,12 @@ #include "tegra20_spdif.h" -#define DRV_NAME "tegra20-spdif" - static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) { struct tegra20_spdif *spdif = dev_get_drvdata(dev); + regcache_cache_only(spdif->regmap, true); + clk_disable_unprepare(spdif->clk_spdif_out); return 0; @@ -38,23 +41,45 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) struct tegra20_spdif *spdif = dev_get_drvdata(dev); int ret; + ret = reset_control_assert(spdif->reset); + if (ret) + return ret; + ret = clk_prepare_enable(spdif->clk_spdif_out); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); return ret; } + usleep_range(10, 100); + + ret = reset_control_deassert(spdif->reset); + if (ret) + goto disable_clocks; + + regcache_cache_only(spdif->regmap, false); + regcache_mark_dirty(spdif->regmap); + + ret = regcache_sync(spdif->regmap); + if (ret) + goto disable_clocks; + return 0; + +disable_clocks: + clk_disable_unprepare(spdif->clk_spdif_out); + + return ret; } static int tegra20_spdif_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 device *dev = dai->dev; - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); unsigned int mask = 0, val = 0; int ret, spdifclock; + long rate; mask |= TEGRA20_SPDIF_CTRL_PACK | TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; @@ -69,6 +94,14 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); + /* + * FIFO trigger level must be bigger than DMA burst or equal to it, + * otherwise data is discarded on overflow. + */ + regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK, + TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL); + switch (params_rate(params)) { case 32000: spdifclock = 4096000; @@ -97,10 +130,16 @@ static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); if (ret) { - dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); + dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret); return ret; } + rate = clk_get_rate(spdif->clk_spdif_out); + if (rate != spdifclock) + dev_warn_once(dai->dev, + "SPDIF clock rate %d doesn't match requested rate %lu\n", + spdifclock, rate); + return 0; } @@ -118,9 +157,9 @@ static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) } static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) + struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -140,9 +179,62 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } +static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, rule->var); + struct snd_soc_dai *dai = rule->private; + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); + struct clk *parent = clk_get_parent(spdif->clk_spdif_out); + const unsigned int rates[] = { 32000, 44100, 48000 }; + long i, parent_rate, valid_rates = 0; + + parent_rate = clk_get_rate(parent); + if (parent_rate <= 0) { + dev_err(dai->dev, "Can't get parent clock rate: %ld\n", + parent_rate); + return parent_rate ?: -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rates); i++) { + if (parent_rate % (rates[i] * 128) == 0) + valid_rates |= BIT(i); + } + + /* + * At least one rate must be valid, otherwise the parent clock isn't + * audio PLL. Nothing should be filtered in this case. + */ + if (!valid_rates) + valid_rates = BIT(ARRAY_SIZE(rates)) - 1; + + return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates); +} + +static int tegra20_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) + return 0; + + /* + * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF + * and audio may not work on some TVs if clock rate isn't precise. + * + * PLL rate is controlled by I2S side. Filter out audio rates that + * don't match PLL rate at the start of stream to allow both SPDIF + * and I2S work simultaneously, assuming that PLL rate won't be + * changed later on. + */ + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + tegra20_spdif_filter_rates, dai, + SNDRV_PCM_HW_PARAM_RATE, -1); +} + static int tegra20_spdif_probe(struct snd_soc_dai *dai) { - struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); dai->capture_dma_data = NULL; dai->playback_dma_data = &spdif->playback_dma_data; @@ -151,26 +243,27 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { - .hw_params = tegra20_spdif_hw_params, - .trigger = tegra20_spdif_trigger, + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, + .startup = tegra20_spdif_startup, }; static struct snd_soc_dai_driver tegra20_spdif_dai = { - .name = DRV_NAME, + .name = "tegra20-spdif", .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &tegra20_spdif_dai_ops, }; static const struct snd_soc_component_driver tegra20_spdif_component = { - .name = DRV_NAME, + .name = "tegra20-spdif", }; static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) @@ -251,7 +344,7 @@ static const struct regmap_config tegra20_spdif_regmap_config = { static int tegra20_spdif_platform_probe(struct platform_device *pdev) { struct tegra20_spdif *spdif; - struct resource *mem, *dmareq; + struct resource *mem; void __iomem *regs; int ret; @@ -262,89 +355,77 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, spdif); - spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); + spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(spdif->reset)) { + dev_err(&pdev->dev, "Can't retrieve spdif reset\n"); + return PTR_ERR(spdif->reset); + } + + spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); if (IS_ERR(spdif->clk_spdif_out)) { - pr_err("Can't retrieve spdif clock\n"); - ret = PTR_ERR(spdif->clk_spdif_out); - return ret; + dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); + return PTR_ERR(spdif->clk_spdif_out); } regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(regs)) return PTR_ERR(regs); - dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmareq) { - dev_err(&pdev->dev, "No DMA resource\n"); - return -ENODEV; - } - spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, - &tegra20_spdif_regmap_config); + &tegra20_spdif_regmap_config); if (IS_ERR(spdif->regmap)) { dev_err(&pdev->dev, "regmap init failed\n"); - ret = PTR_ERR(spdif->regmap); - return ret; + return PTR_ERR(spdif->regmap); } spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; spdif->playback_dma_data.maxburst = 4; - spdif->playback_dma_data.slave_id = dmareq->start; - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; - ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, - &tegra20_spdif_dai, 1); + ret = devm_snd_soc_register_component(&pdev->dev, + &tegra20_spdif_component, + &tegra20_spdif_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); - ret = -ENOMEM; - goto err_pm_disable; + return ret; } - ret = tegra_pcm_platform_register(&pdev->dev); + ret = devm_tegra_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); - goto err_unregister_component; + return ret; } - return 0; - -err_unregister_component: - snd_soc_unregister_component(&pdev->dev); -err_pm_disable: - pm_runtime_disable(&pdev->dev); - - return ret; -} - -static int tegra20_spdif_platform_remove(struct platform_device *pdev) -{ - tegra_pcm_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); - - pm_runtime_disable(&pdev->dev); - return 0; } static const struct dev_pm_ops tegra20_spdif_pm_ops = { SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, tegra20_spdif_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; +static const struct of_device_id tegra20_spdif_of_match[] = { + { .compatible = "nvidia,tegra20-spdif", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); + static struct platform_driver tegra20_spdif_driver = { .driver = { - .name = DRV_NAME, + .name = "tegra20-spdif", .pm = &tegra20_spdif_pm_ops, + .of_match_table = tegra20_spdif_of_match, }, .probe = tegra20_spdif_platform_probe, - .remove = tegra20_spdif_platform_remove, }; - module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren "); MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index 1973ffc2d5..ff4b79e205 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -451,6 +451,7 @@ struct tegra20_spdif { struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data; struct regmap *regmap; + struct reset_control *reset; }; #endif diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c index 1b2f7cb8c6..388b815443 100644 --- a/sound/soc/tegra/tegra210_ahub.c +++ b/sound/soc/tegra/tegra210_ahub.c @@ -108,14 +108,68 @@ static struct snd_soc_dai_driver tegra210_ahub_dais[] = { DAI(ADMAIF8), DAI(ADMAIF9), DAI(ADMAIF10), + /* XBAR <-> I2S <-> Codec */ DAI(I2S1), DAI(I2S2), DAI(I2S3), DAI(I2S4), DAI(I2S5), + /* XBAR <- DMIC <- Codec */ DAI(DMIC1), DAI(DMIC2), DAI(DMIC3), + /* XBAR -> SFC -> XBAR */ + DAI(SFC1 RX), + DAI(SFC1 TX), + DAI(SFC2 RX), + DAI(SFC2 TX), + DAI(SFC3 RX), + DAI(SFC3 TX), + DAI(SFC4 RX), + DAI(SFC4 TX), + /* XBAR -> MVC -> XBAR */ + DAI(MVC1 RX), + DAI(MVC1 TX), + DAI(MVC2 RX), + DAI(MVC2 TX), + /* XBAR -> AMX(4:1) -> XBAR */ + DAI(AMX1 RX1), + DAI(AMX1 RX2), + DAI(AMX1 RX3), + DAI(AMX1 RX4), + DAI(AMX1), + DAI(AMX2 RX1), + DAI(AMX2 RX2), + DAI(AMX2 RX3), + DAI(AMX2 RX4), + DAI(AMX2), + /* XBAR -> ADX(1:4) -> XBAR */ + DAI(ADX1), + DAI(ADX1 TX1), + DAI(ADX1 TX2), + DAI(ADX1 TX3), + DAI(ADX1 TX4), + DAI(ADX2), + DAI(ADX2 TX1), + DAI(ADX2 TX2), + DAI(ADX2 TX3), + DAI(ADX2 TX4), + /* XBAR -> MIXER(10:5) -> XBAR */ + DAI(MIXER1 RX1), + DAI(MIXER1 RX2), + DAI(MIXER1 RX3), + DAI(MIXER1 RX4), + DAI(MIXER1 RX5), + DAI(MIXER1 RX6), + DAI(MIXER1 RX7), + DAI(MIXER1 RX8), + DAI(MIXER1 RX9), + DAI(MIXER1 RX10), + DAI(MIXER1 TX1), + DAI(MIXER1 TX2), + DAI(MIXER1 TX3), + DAI(MIXER1 TX4), + DAI(MIXER1 TX5), }; static struct snd_soc_dai_driver tegra186_ahub_dais[] = { @@ -139,18 +193,93 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = { DAI(ADMAIF18), DAI(ADMAIF19), DAI(ADMAIF20), + /* XBAR <-> I2S <-> Codec */ DAI(I2S1), DAI(I2S2), DAI(I2S3), DAI(I2S4), DAI(I2S5), DAI(I2S6), + /* XBAR <- DMIC <- Codec */ DAI(DMIC1), DAI(DMIC2), DAI(DMIC3), DAI(DMIC4), + /* XBAR -> DSPK -> Codec */ DAI(DSPK1), DAI(DSPK2), + /* XBAR -> SFC -> XBAR */ + DAI(SFC1 RX), + DAI(SFC1 TX), + DAI(SFC2 RX), + DAI(SFC2 TX), + DAI(SFC3 RX), + DAI(SFC3 TX), + DAI(SFC4 RX), + DAI(SFC4 TX), + /* XBAR -> MVC -> XBAR */ + DAI(MVC1 RX), + DAI(MVC1 TX), + DAI(MVC2 RX), + DAI(MVC2 TX), + /* XBAR -> AMX(4:1) -> XBAR */ + DAI(AMX1 RX1), + DAI(AMX1 RX2), + DAI(AMX1 RX3), + DAI(AMX1 RX4), + DAI(AMX1), + DAI(AMX2 RX1), + DAI(AMX2 RX2), + DAI(AMX2 RX3), + DAI(AMX2 RX4), + DAI(AMX2), + DAI(AMX3 RX1), + DAI(AMX3 RX2), + DAI(AMX3 RX3), + DAI(AMX3 RX4), + DAI(AMX3), + DAI(AMX4 RX1), + DAI(AMX4 RX2), + DAI(AMX4 RX3), + DAI(AMX4 RX4), + DAI(AMX4), + /* XBAR -> ADX(1:4) -> XBAR */ + DAI(ADX1), + DAI(ADX1 TX1), + DAI(ADX1 TX2), + DAI(ADX1 TX3), + DAI(ADX1 TX4), + DAI(ADX2), + DAI(ADX2 TX1), + DAI(ADX2 TX2), + DAI(ADX2 TX3), + DAI(ADX2 TX4), + DAI(ADX3), + DAI(ADX3 TX1), + DAI(ADX3 TX2), + DAI(ADX3 TX3), + DAI(ADX3 TX4), + DAI(ADX4), + DAI(ADX4 TX1), + DAI(ADX4 TX2), + DAI(ADX4 TX3), + DAI(ADX4 TX4), + /* XBAR -> MIXER1(10:5) -> XBAR */ + DAI(MIXER1 RX1), + DAI(MIXER1 RX2), + DAI(MIXER1 RX3), + DAI(MIXER1 RX4), + DAI(MIXER1 RX5), + DAI(MIXER1 RX6), + DAI(MIXER1 RX7), + DAI(MIXER1 RX8), + DAI(MIXER1 RX9), + DAI(MIXER1 RX10), + DAI(MIXER1 TX1), + DAI(MIXER1 TX2), + DAI(MIXER1 TX3), + DAI(MIXER1 TX4), + DAI(MIXER1 TX5), }; static const char * const tegra210_ahub_mux_texts[] = { @@ -173,6 +302,27 @@ static const char * const tegra210_ahub_mux_texts[] = { "DMIC1", "DMIC2", "DMIC3", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + "MVC1", + "MVC2", + "AMX1", + "AMX2", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", }; static const char * const tegra186_ahub_mux_texts[] = { @@ -207,10 +357,42 @@ static const char * const tegra186_ahub_mux_texts[] = { "DMIC2", "DMIC3", "DMIC4", + "SFC1", + "SFC2", + "SFC3", + "SFC4", + "MVC1", + "MVC2", + "AMX1", + "AMX2", + "AMX3", + "AMX4", + "ADX1 TX1", + "ADX1 TX2", + "ADX1 TX3", + "ADX1 TX4", + "ADX2 TX1", + "ADX2 TX2", + "ADX2 TX3", + "ADX2 TX4", + "ADX3 TX1", + "ADX3 TX2", + "ADX3 TX3", + "ADX3 TX4", + "ADX4 TX1", + "ADX4 TX2", + "ADX4 TX3", + "ADX4 TX4", + "MIXER1 TX1", + "MIXER1 TX2", + "MIXER1 TX3", + "MIXER1 TX4", + "MIXER1 TX5", }; static const unsigned int tegra210_ahub_mux_values[] = { 0, + /* ADMAIF */ MUX_VALUE(0, 0), MUX_VALUE(0, 1), MUX_VALUE(0, 2), @@ -221,18 +403,47 @@ static const unsigned int tegra210_ahub_mux_values[] = { MUX_VALUE(0, 7), MUX_VALUE(0, 8), MUX_VALUE(0, 9), + /* I2S */ MUX_VALUE(0, 16), MUX_VALUE(0, 17), MUX_VALUE(0, 18), MUX_VALUE(0, 19), MUX_VALUE(0, 20), + /* DMIC */ MUX_VALUE(2, 18), MUX_VALUE(2, 19), MUX_VALUE(2, 20), + /* SFC */ + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* MVC */ + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + /* AMX */ + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + /* ADX */ + MUX_VALUE(2, 24), + MUX_VALUE(2, 25), + MUX_VALUE(2, 26), + MUX_VALUE(2, 27), + MUX_VALUE(2, 28), + MUX_VALUE(2, 29), + MUX_VALUE(2, 30), + MUX_VALUE(2, 31), + /* MIXER */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), }; static const unsigned int tegra186_ahub_mux_values[] = { 0, + /* ADMAIF */ MUX_VALUE(0, 0), MUX_VALUE(0, 1), MUX_VALUE(0, 2), @@ -249,20 +460,59 @@ static const unsigned int tegra186_ahub_mux_values[] = { MUX_VALUE(0, 13), MUX_VALUE(0, 14), MUX_VALUE(0, 15), + /* I2S */ MUX_VALUE(0, 16), MUX_VALUE(0, 17), MUX_VALUE(0, 18), MUX_VALUE(0, 19), MUX_VALUE(0, 20), MUX_VALUE(0, 21), + /* ADMAIF */ MUX_VALUE(3, 16), MUX_VALUE(3, 17), MUX_VALUE(3, 18), MUX_VALUE(3, 19), + /* DMIC */ MUX_VALUE(2, 18), MUX_VALUE(2, 19), MUX_VALUE(2, 20), MUX_VALUE(2, 21), + /* SFC */ + MUX_VALUE(0, 24), + MUX_VALUE(0, 25), + MUX_VALUE(0, 26), + MUX_VALUE(0, 27), + /* MVC */ + MUX_VALUE(2, 8), + MUX_VALUE(2, 9), + /* AMX */ + MUX_VALUE(1, 8), + MUX_VALUE(1, 9), + MUX_VALUE(1, 10), + MUX_VALUE(1, 11), + /* ADX */ + MUX_VALUE(2, 24), + MUX_VALUE(2, 25), + MUX_VALUE(2, 26), + MUX_VALUE(2, 27), + MUX_VALUE(2, 28), + MUX_VALUE(2, 29), + MUX_VALUE(2, 30), + MUX_VALUE(2, 31), + MUX_VALUE(3, 0), + MUX_VALUE(3, 1), + MUX_VALUE(3, 2), + MUX_VALUE(3, 3), + MUX_VALUE(3, 4), + MUX_VALUE(3, 5), + MUX_VALUE(3, 6), + MUX_VALUE(3, 7), + /* MIXER */ + MUX_VALUE(1, 0), + MUX_VALUE(1, 1), + MUX_VALUE(1, 2), + MUX_VALUE(1, 3), + MUX_VALUE(1, 4), }; /* Controls for t210 */ @@ -281,6 +531,32 @@ MUX_ENUM_CTRL_DECL(t210_i2s2_tx, 0x11); MUX_ENUM_CTRL_DECL(t210_i2s3_tx, 0x12); MUX_ENUM_CTRL_DECL(t210_i2s4_tx, 0x13); MUX_ENUM_CTRL_DECL(t210_i2s5_tx, 0x14); +MUX_ENUM_CTRL_DECL(t210_sfc1_tx, 0x18); +MUX_ENUM_CTRL_DECL(t210_sfc2_tx, 0x19); +MUX_ENUM_CTRL_DECL(t210_sfc3_tx, 0x1a); +MUX_ENUM_CTRL_DECL(t210_sfc4_tx, 0x1b); +MUX_ENUM_CTRL_DECL(t210_mvc1_tx, 0x48); +MUX_ENUM_CTRL_DECL(t210_mvc2_tx, 0x49); +MUX_ENUM_CTRL_DECL(t210_amx11_tx, 0x50); +MUX_ENUM_CTRL_DECL(t210_amx12_tx, 0x51); +MUX_ENUM_CTRL_DECL(t210_amx13_tx, 0x52); +MUX_ENUM_CTRL_DECL(t210_amx14_tx, 0x53); +MUX_ENUM_CTRL_DECL(t210_amx21_tx, 0x54); +MUX_ENUM_CTRL_DECL(t210_amx22_tx, 0x55); +MUX_ENUM_CTRL_DECL(t210_amx23_tx, 0x56); +MUX_ENUM_CTRL_DECL(t210_amx24_tx, 0x57); +MUX_ENUM_CTRL_DECL(t210_adx1_tx, 0x58); +MUX_ENUM_CTRL_DECL(t210_adx2_tx, 0x59); +MUX_ENUM_CTRL_DECL(t210_mixer11_tx, 0x20); +MUX_ENUM_CTRL_DECL(t210_mixer12_tx, 0x21); +MUX_ENUM_CTRL_DECL(t210_mixer13_tx, 0x22); +MUX_ENUM_CTRL_DECL(t210_mixer14_tx, 0x23); +MUX_ENUM_CTRL_DECL(t210_mixer15_tx, 0x24); +MUX_ENUM_CTRL_DECL(t210_mixer16_tx, 0x25); +MUX_ENUM_CTRL_DECL(t210_mixer17_tx, 0x26); +MUX_ENUM_CTRL_DECL(t210_mixer18_tx, 0x27); +MUX_ENUM_CTRL_DECL(t210_mixer19_tx, 0x28); +MUX_ENUM_CTRL_DECL(t210_mixer110_tx, 0x29); /* Controls for t186 */ MUX_ENUM_CTRL_DECL_186(t186_admaif1_tx, 0x00); @@ -311,6 +587,42 @@ MUX_ENUM_CTRL_DECL_186(t186_admaif17_tx, 0x68); MUX_ENUM_CTRL_DECL_186(t186_admaif18_tx, 0x69); MUX_ENUM_CTRL_DECL_186(t186_admaif19_tx, 0x6a); MUX_ENUM_CTRL_DECL_186(t186_admaif20_tx, 0x6b); +MUX_ENUM_CTRL_DECL_186(t186_sfc1_tx, 0x18); +MUX_ENUM_CTRL_DECL_186(t186_sfc2_tx, 0x19); +MUX_ENUM_CTRL_DECL_186(t186_sfc3_tx, 0x1a); +MUX_ENUM_CTRL_DECL_186(t186_sfc4_tx, 0x1b); +MUX_ENUM_CTRL_DECL_186(t186_mvc1_tx, 0x48); +MUX_ENUM_CTRL_DECL_186(t186_mvc2_tx, 0x49); +MUX_ENUM_CTRL_DECL_186(t186_amx11_tx, 0x50); +MUX_ENUM_CTRL_DECL_186(t186_amx12_tx, 0x51); +MUX_ENUM_CTRL_DECL_186(t186_amx13_tx, 0x52); +MUX_ENUM_CTRL_DECL_186(t186_amx14_tx, 0x53); +MUX_ENUM_CTRL_DECL_186(t186_amx21_tx, 0x54); +MUX_ENUM_CTRL_DECL_186(t186_amx22_tx, 0x55); +MUX_ENUM_CTRL_DECL_186(t186_amx23_tx, 0x56); +MUX_ENUM_CTRL_DECL_186(t186_amx24_tx, 0x57); +MUX_ENUM_CTRL_DECL_186(t186_amx31_tx, 0x58); +MUX_ENUM_CTRL_DECL_186(t186_amx32_tx, 0x59); +MUX_ENUM_CTRL_DECL_186(t186_amx33_tx, 0x5a); +MUX_ENUM_CTRL_DECL_186(t186_amx34_tx, 0x5b); +MUX_ENUM_CTRL_DECL_186(t186_amx41_tx, 0x64); +MUX_ENUM_CTRL_DECL_186(t186_amx42_tx, 0x65); +MUX_ENUM_CTRL_DECL_186(t186_amx43_tx, 0x66); +MUX_ENUM_CTRL_DECL_186(t186_amx44_tx, 0x67); +MUX_ENUM_CTRL_DECL_186(t186_adx1_tx, 0x60); +MUX_ENUM_CTRL_DECL_186(t186_adx2_tx, 0x61); +MUX_ENUM_CTRL_DECL_186(t186_adx3_tx, 0x62); +MUX_ENUM_CTRL_DECL_186(t186_adx4_tx, 0x63); +MUX_ENUM_CTRL_DECL_186(t186_mixer11_tx, 0x20); +MUX_ENUM_CTRL_DECL_186(t186_mixer12_tx, 0x21); +MUX_ENUM_CTRL_DECL_186(t186_mixer13_tx, 0x22); +MUX_ENUM_CTRL_DECL_186(t186_mixer14_tx, 0x23); +MUX_ENUM_CTRL_DECL_186(t186_mixer15_tx, 0x24); +MUX_ENUM_CTRL_DECL_186(t186_mixer16_tx, 0x25); +MUX_ENUM_CTRL_DECL_186(t186_mixer17_tx, 0x26); +MUX_ENUM_CTRL_DECL_186(t186_mixer18_tx, 0x27); +MUX_ENUM_CTRL_DECL_186(t186_mixer19_tx, 0x28); +MUX_ENUM_CTRL_DECL_186(t186_mixer110_tx, 0x29); /* * The number of entries in, and order of, this array is closely tied to the @@ -336,6 +648,47 @@ static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = { TX_WIDGETS("DMIC1"), TX_WIDGETS("DMIC2"), TX_WIDGETS("DMIC3"), + WIDGETS("SFC1", t210_sfc1_tx), + WIDGETS("SFC2", t210_sfc2_tx), + WIDGETS("SFC3", t210_sfc3_tx), + WIDGETS("SFC4", t210_sfc4_tx), + WIDGETS("MVC1", t210_mvc1_tx), + WIDGETS("MVC2", t210_mvc2_tx), + WIDGETS("AMX1 RX1", t210_amx11_tx), + WIDGETS("AMX1 RX2", t210_amx12_tx), + WIDGETS("AMX1 RX3", t210_amx13_tx), + WIDGETS("AMX1 RX4", t210_amx14_tx), + WIDGETS("AMX2 RX1", t210_amx21_tx), + WIDGETS("AMX2 RX2", t210_amx22_tx), + WIDGETS("AMX2 RX3", t210_amx23_tx), + WIDGETS("AMX2 RX4", t210_amx24_tx), + TX_WIDGETS("AMX1"), + TX_WIDGETS("AMX2"), + WIDGETS("ADX1", t210_adx1_tx), + WIDGETS("ADX2", t210_adx2_tx), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + WIDGETS("MIXER1 RX1", t210_mixer11_tx), + WIDGETS("MIXER1 RX2", t210_mixer12_tx), + WIDGETS("MIXER1 RX3", t210_mixer13_tx), + WIDGETS("MIXER1 RX4", t210_mixer14_tx), + WIDGETS("MIXER1 RX5", t210_mixer15_tx), + WIDGETS("MIXER1 RX6", t210_mixer16_tx), + WIDGETS("MIXER1 RX7", t210_mixer17_tx), + WIDGETS("MIXER1 RX8", t210_mixer18_tx), + WIDGETS("MIXER1 RX9", t210_mixer19_tx), + WIDGETS("MIXER1 RX10", t210_mixer110_tx), + TX_WIDGETS("MIXER1 TX1"), + TX_WIDGETS("MIXER1 TX2"), + TX_WIDGETS("MIXER1 TX3"), + TX_WIDGETS("MIXER1 TX4"), + TX_WIDGETS("MIXER1 TX5"), }; static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { @@ -371,6 +724,67 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { TX_WIDGETS("DMIC4"), WIDGETS("DSPK1", t186_dspk1_tx), WIDGETS("DSPK2", t186_dspk2_tx), + WIDGETS("SFC1", t186_sfc1_tx), + WIDGETS("SFC2", t186_sfc2_tx), + WIDGETS("SFC3", t186_sfc3_tx), + WIDGETS("SFC4", t186_sfc4_tx), + WIDGETS("MVC1", t186_mvc1_tx), + WIDGETS("MVC2", t186_mvc2_tx), + WIDGETS("AMX1 RX1", t186_amx11_tx), + WIDGETS("AMX1 RX2", t186_amx12_tx), + WIDGETS("AMX1 RX3", t186_amx13_tx), + WIDGETS("AMX1 RX4", t186_amx14_tx), + WIDGETS("AMX2 RX1", t186_amx21_tx), + WIDGETS("AMX2 RX2", t186_amx22_tx), + WIDGETS("AMX2 RX3", t186_amx23_tx), + WIDGETS("AMX2 RX4", t186_amx24_tx), + WIDGETS("AMX3 RX1", t186_amx31_tx), + WIDGETS("AMX3 RX2", t186_amx32_tx), + WIDGETS("AMX3 RX3", t186_amx33_tx), + WIDGETS("AMX3 RX4", t186_amx34_tx), + WIDGETS("AMX4 RX1", t186_amx41_tx), + WIDGETS("AMX4 RX2", t186_amx42_tx), + WIDGETS("AMX4 RX3", t186_amx43_tx), + WIDGETS("AMX4 RX4", t186_amx44_tx), + TX_WIDGETS("AMX1"), + TX_WIDGETS("AMX2"), + TX_WIDGETS("AMX3"), + TX_WIDGETS("AMX4"), + WIDGETS("ADX1", t186_adx1_tx), + WIDGETS("ADX2", t186_adx2_tx), + WIDGETS("ADX3", t186_adx3_tx), + WIDGETS("ADX4", t186_adx4_tx), + TX_WIDGETS("ADX1 TX1"), + TX_WIDGETS("ADX1 TX2"), + TX_WIDGETS("ADX1 TX3"), + TX_WIDGETS("ADX1 TX4"), + TX_WIDGETS("ADX2 TX1"), + TX_WIDGETS("ADX2 TX2"), + TX_WIDGETS("ADX2 TX3"), + TX_WIDGETS("ADX2 TX4"), + TX_WIDGETS("ADX3 TX1"), + TX_WIDGETS("ADX3 TX2"), + TX_WIDGETS("ADX3 TX3"), + TX_WIDGETS("ADX3 TX4"), + TX_WIDGETS("ADX4 TX1"), + TX_WIDGETS("ADX4 TX2"), + TX_WIDGETS("ADX4 TX3"), + TX_WIDGETS("ADX4 TX4"), + WIDGETS("MIXER1 RX1", t186_mixer11_tx), + WIDGETS("MIXER1 RX2", t186_mixer12_tx), + WIDGETS("MIXER1 RX3", t186_mixer13_tx), + WIDGETS("MIXER1 RX4", t186_mixer14_tx), + WIDGETS("MIXER1 RX5", t186_mixer15_tx), + WIDGETS("MIXER1 RX6", t186_mixer16_tx), + WIDGETS("MIXER1 RX7", t186_mixer17_tx), + WIDGETS("MIXER1 RX8", t186_mixer18_tx), + WIDGETS("MIXER1 RX9", t186_mixer19_tx), + WIDGETS("MIXER1 RX10", t186_mixer110_tx), + TX_WIDGETS("MIXER1 TX1"), + TX_WIDGETS("MIXER1 TX2"), + TX_WIDGETS("MIXER1 TX3"), + TX_WIDGETS("MIXER1 TX4"), + TX_WIDGETS("MIXER1 TX5"), }; #define TEGRA_COMMON_MUX_ROUTES(name) \ @@ -392,7 +806,28 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { { name " Mux", "I2S5", "I2S5 XBAR-RX" }, \ { name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \ { name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \ - { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, + { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \ + { name " Mux", "SFC1", "SFC1 XBAR-RX" }, \ + { name " Mux", "SFC2", "SFC2 XBAR-RX" }, \ + { name " Mux", "SFC3", "SFC3 XBAR-RX" }, \ + { name " Mux", "SFC4", "SFC4 XBAR-RX" }, \ + { name " Mux", "MVC1", "MVC1 XBAR-RX" }, \ + { name " Mux", "MVC2", "MVC2 XBAR-RX" }, \ + { name " Mux", "AMX1", "AMX1 XBAR-RX" }, \ + { name " Mux", "AMX2", "AMX2 XBAR-RX" }, \ + { name " Mux", "ADX1 TX1", "ADX1 TX1 XBAR-RX" }, \ + { name " Mux", "ADX1 TX2", "ADX1 TX2 XBAR-RX" }, \ + { name " Mux", "ADX1 TX3", "ADX1 TX3 XBAR-RX" }, \ + { name " Mux", "ADX1 TX4", "ADX1 TX4 XBAR-RX" }, \ + { name " Mux", "ADX2 TX1", "ADX2 TX1 XBAR-RX" }, \ + { name " Mux", "ADX2 TX2", "ADX2 TX2 XBAR-RX" }, \ + { name " Mux", "ADX2 TX3", "ADX2 TX3 XBAR-RX" }, \ + { name " Mux", "ADX2 TX4", "ADX2 TX4 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX1", "MIXER1 TX1 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX2", "MIXER1 TX2 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX3", "MIXER1 TX3 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX4", "MIXER1 TX4 XBAR-RX" }, \ + { name " Mux", "MIXER1 TX5", "MIXER1 TX5 XBAR-RX" }, #define TEGRA186_ONLY_MUX_ROUTES(name) \ { name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \ @@ -406,7 +841,17 @@ static const struct snd_soc_dapm_widget tegra186_ahub_widgets[] = { { name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \ { name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \ { name " Mux", "I2S6", "I2S6 XBAR-RX" }, \ - { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, + { name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \ + { name " Mux", "AMX3", "AMX3 XBAR-RX" }, \ + { name " Mux", "AMX4", "AMX4 XBAR-RX" }, \ + { name " Mux", "ADX3 TX1", "ADX3 TX1 XBAR-RX" }, \ + { name " Mux", "ADX3 TX2", "ADX3 TX2 XBAR-RX" }, \ + { name " Mux", "ADX3 TX3", "ADX3 TX3 XBAR-RX" }, \ + { name " Mux", "ADX3 TX4", "ADX3 TX4 XBAR-RX" }, \ + { name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \ + { name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \ + { name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \ + { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, #define TEGRA210_MUX_ROUTES(name) \ TEGRA_COMMON_MUX_ROUTES(name) @@ -453,6 +898,32 @@ static const struct snd_soc_dapm_route tegra210_ahub_routes[] = { TEGRA210_MUX_ROUTES("I2S3") TEGRA210_MUX_ROUTES("I2S4") TEGRA210_MUX_ROUTES("I2S5") + TEGRA210_MUX_ROUTES("SFC1") + TEGRA210_MUX_ROUTES("SFC2") + TEGRA210_MUX_ROUTES("SFC3") + TEGRA210_MUX_ROUTES("SFC4") + TEGRA210_MUX_ROUTES("MVC1") + TEGRA210_MUX_ROUTES("MVC2") + TEGRA210_MUX_ROUTES("AMX1 RX1") + TEGRA210_MUX_ROUTES("AMX1 RX2") + TEGRA210_MUX_ROUTES("AMX1 RX3") + TEGRA210_MUX_ROUTES("AMX1 RX4") + TEGRA210_MUX_ROUTES("AMX2 RX1") + TEGRA210_MUX_ROUTES("AMX2 RX2") + TEGRA210_MUX_ROUTES("AMX2 RX3") + TEGRA210_MUX_ROUTES("AMX2 RX4") + TEGRA210_MUX_ROUTES("ADX1") + TEGRA210_MUX_ROUTES("ADX2") + TEGRA210_MUX_ROUTES("MIXER1 RX1") + TEGRA210_MUX_ROUTES("MIXER1 RX2") + TEGRA210_MUX_ROUTES("MIXER1 RX3") + TEGRA210_MUX_ROUTES("MIXER1 RX4") + TEGRA210_MUX_ROUTES("MIXER1 RX5") + TEGRA210_MUX_ROUTES("MIXER1 RX6") + TEGRA210_MUX_ROUTES("MIXER1 RX7") + TEGRA210_MUX_ROUTES("MIXER1 RX8") + TEGRA210_MUX_ROUTES("MIXER1 RX9") + TEGRA210_MUX_ROUTES("MIXER1 RX10") }; static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { @@ -504,6 +975,42 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = { TEGRA186_MUX_ROUTES("I2S6") TEGRA186_MUX_ROUTES("DSPK1") TEGRA186_MUX_ROUTES("DSPK2") + TEGRA186_MUX_ROUTES("SFC1") + TEGRA186_MUX_ROUTES("SFC2") + TEGRA186_MUX_ROUTES("SFC3") + TEGRA186_MUX_ROUTES("SFC4") + TEGRA186_MUX_ROUTES("MVC1") + TEGRA186_MUX_ROUTES("MVC2") + TEGRA186_MUX_ROUTES("AMX1 RX1") + TEGRA186_MUX_ROUTES("AMX1 RX2") + TEGRA186_MUX_ROUTES("AMX1 RX3") + TEGRA186_MUX_ROUTES("AMX1 RX4") + TEGRA186_MUX_ROUTES("AMX2 RX1") + TEGRA186_MUX_ROUTES("AMX2 RX2") + TEGRA186_MUX_ROUTES("AMX2 RX3") + TEGRA186_MUX_ROUTES("AMX2 RX4") + TEGRA186_MUX_ROUTES("AMX3 RX1") + TEGRA186_MUX_ROUTES("AMX3 RX2") + TEGRA186_MUX_ROUTES("AMX3 RX3") + TEGRA186_MUX_ROUTES("AMX3 RX4") + TEGRA186_MUX_ROUTES("AMX4 RX1") + TEGRA186_MUX_ROUTES("AMX4 RX2") + TEGRA186_MUX_ROUTES("AMX4 RX3") + TEGRA186_MUX_ROUTES("AMX4 RX4") + TEGRA186_MUX_ROUTES("ADX1") + TEGRA186_MUX_ROUTES("ADX2") + TEGRA186_MUX_ROUTES("ADX3") + TEGRA186_MUX_ROUTES("ADX4") + TEGRA186_MUX_ROUTES("MIXER1 RX1") + TEGRA186_MUX_ROUTES("MIXER1 RX2") + TEGRA186_MUX_ROUTES("MIXER1 RX3") + TEGRA186_MUX_ROUTES("MIXER1 RX4") + TEGRA186_MUX_ROUTES("MIXER1 RX5") + TEGRA186_MUX_ROUTES("MIXER1 RX6") + TEGRA186_MUX_ROUTES("MIXER1 RX7") + TEGRA186_MUX_ROUTES("MIXER1 RX8") + TEGRA186_MUX_ROUTES("MIXER1 RX9") + TEGRA186_MUX_ROUTES("MIXER1 RX10") }; static const struct snd_soc_component_driver tegra210_ahub_component = { diff --git a/sound/soc/tegra/tegra_asoc_machine.c b/sound/soc/tegra/tegra_asoc_machine.c index 2e549b6906..a73404879a 100644 --- a/sound/soc/tegra/tegra_asoc_machine.c +++ b/sound/soc/tegra/tegra_asoc_machine.c @@ -321,7 +321,7 @@ static int tegra_machine_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops tegra_machine_snd_ops = { +static const struct snd_soc_ops tegra_machine_snd_ops = { .hw_params = tegra_machine_hw_params, }; diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index ef1e74d952..468c8e77de 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -48,6 +48,12 @@ int tegra_pcm_platform_register(struct device *dev) } EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); +int devm_tegra_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &tegra_dmaengine_pcm_config, 0); +} +EXPORT_SYMBOL_GPL(devm_tegra_pcm_platform_register); + int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan) diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h index d602126c65..2a36eea174 100644 --- a/sound/soc/tegra/tegra_pcm.h +++ b/sound/soc/tegra/tegra_pcm.h @@ -32,6 +32,7 @@ int tegra_pcm_hw_params(struct snd_soc_component *component, snd_pcm_uframes_t tegra_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream); int tegra_pcm_platform_register(struct device *dev); +int devm_tegra_pcm_platform_register(struct device *dev); int tegra_pcm_platform_register_with_chan_names(struct device *dev, struct snd_dmaengine_pcm_config *config, char *txdmachan, char *rxdmachan); diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index 1d9fe3fca1..40110e9a9e 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -212,7 +212,7 @@ config SND_SOC_DM365_VOICE_CODEC Say Y if you want to add support for SoC On-chip voice codec endchoice -config SND_SOC_DM365_VOICE_CODEC_MODULE +config SND_SOC_DM365_SELECT_VOICE_CODECS def_tristate y depends on SND_SOC_DM365_VOICE_CODEC && SND_SOC select MFD_DAVINCI_VOICECODEC diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c index ecd24d412a..b1a32545ba 100644 --- a/sound/soc/ti/ams-delta.c +++ b/sound/soc/ti/ams-delta.c @@ -330,10 +330,9 @@ static void cx81801_close(struct tty_struct *tty) } /* Line discipline .hangup() */ -static int cx81801_hangup(struct tty_struct *tty) +static void cx81801_hangup(struct tty_struct *tty) { cx81801_close(tty); - return 0; } /* Line discipline .receive_buf() */ diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index b043a0070d..68d69e3268 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -73,7 +73,7 @@ static int evm_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops evm_ops = { +static const struct snd_soc_ops evm_ops = { .startup = evm_startup, .shutdown = evm_shutdown, .hw_params = evm_hw_params, diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 56a19eeec5..2c146b91fc 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1870,12 +1870,10 @@ static int mcasp_reparent_fck(struct platform_device *pdev) static bool davinci_mcasp_have_gpiochip(struct davinci_mcasp *mcasp) { #ifdef CONFIG_OF_GPIO - if (mcasp->dev->of_node && - of_property_read_bool(mcasp->dev->of_node, "gpio-controller")) - return true; -#endif - + return of_property_read_bool(mcasp->dev->of_node, "gpio-controller"); +#else return false; +#endif } static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, @@ -2026,13 +2024,9 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; chan = dma_request_chan(mcasp->dev, tmp); - if (IS_ERR(chan)) { - if (PTR_ERR(chan) != -EPROBE_DEFER) - dev_err(mcasp->dev, - "Can't verify DMA configuration (%ld)\n", - PTR_ERR(chan)); - return PTR_ERR(chan); - } + if (IS_ERR(chan)) + return dev_err_probe(mcasp->dev, PTR_ERR(chan), + "Can't verify DMA configuration\n"); if (WARN_ON(!chan->device || !chan->device->dev)) { dma_release_channel(chan); return -EINVAL; @@ -2230,9 +2224,6 @@ static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp) mcasp->gpio_chip = davinci_mcasp_template_chip; mcasp->gpio_chip.label = dev_name(mcasp->dev); mcasp->gpio_chip.parent = mcasp->dev; -#ifdef CONFIG_OF_GPIO - mcasp->gpio_chip.of_node = mcasp->dev->of_node; -#endif return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp); } diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index 9347f982c3..4077e15ec4 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -464,13 +464,9 @@ static int j721e_get_clocks(struct device *dev, int ret; clocks->target = devm_clk_get(dev, prefix); - if (IS_ERR(clocks->target)) { - ret = PTR_ERR(clocks->target); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to acquire %s: %d\n", - prefix, ret); - return ret; - } + if (IS_ERR(clocks->target)) + return dev_err_probe(dev, PTR_ERR(clocks->target), + "failed to acquire %s\n", prefix); clk_name = kasprintf(GFP_KERNEL, "%s-48000", prefix); if (clk_name) { diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c index 2e3d1eea77..da809c7f25 100644 --- a/sound/soc/ti/omap-abe-twl6040.c +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -96,7 +96,7 @@ static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream, return 0; } -static struct snd_soc_ops omap_abe_dmic_ops = { +static const struct snd_soc_ops omap_abe_dmic_ops = { .hw_params = omap_abe_dmic_hw_params, }; diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 2c39c7a2fd..3e654e708f 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -348,7 +348,7 @@ static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream) return 0; } -struct snd_soc_ops mop500_ab8500_ops[] = { +const struct snd_soc_ops mop500_ab8500_ops[] = { { .hw_params = mop500_ab8500_hw_params, .hw_free = mop500_ab8500_hw_free, diff --git a/sound/soc/ux500/mop500_ab8500.h b/sound/soc/ux500/mop500_ab8500.h index 8138a4e9aa..087ef246d8 100644 --- a/sound/soc/ux500/mop500_ab8500.h +++ b/sound/soc/ux500/mop500_ab8500.h @@ -11,7 +11,7 @@ #ifndef MOP500_AB8500_H #define MOP500_AB8500_H -extern struct snd_soc_ops mop500_ab8500_ops[]; +extern const struct snd_soc_ops mop500_ab8500_ops[]; int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd); void mop500_ab8500_remove(struct snd_soc_card *card); diff --git a/sound/soc/xilinx/xlnx_spdif.c b/sound/soc/xilinx/xlnx_spdif.c index e2ca087ade..cba0e868a7 100644 --- a/sound/soc/xilinx/xlnx_spdif.c +++ b/sound/soc/xilinx/xlnx_spdif.c @@ -237,7 +237,6 @@ MODULE_DEVICE_TABLE(of, xlnx_spdif_of_match); static int xlnx_spdif_probe(struct platform_device *pdev) { int ret; - struct resource *res; struct snd_soc_dai_driver *dai_drv; struct spdif_dev_data *ctx; @@ -273,13 +272,10 @@ static int xlnx_spdif_probe(struct platform_device *pdev) if (ctx->mode) { dai_drv = &xlnx_spdif_tx_dai; } else { - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "No IRQ resource found\n"); - ret = -ENODEV; + ret = platform_get_irq(pdev, 0); + if (ret < 0) goto clk_err; - } - ret = devm_request_irq(dev, res->start, + ret = devm_request_irq(dev, ret, xlnx_spdifrx_irq_handler, 0, "XLNX_SPDIF_RX", ctx); if (ret) { diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 6b84f66e4a..3881e1c1b0 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -688,7 +688,7 @@ static void dbri_cmdsend(struct snd_dbri *dbri, s32 *cmd, int len) { u32 dvma_addr = (u32)dbri->dma_dvma; s32 tmp, addr; - static int wait_id = 0; + static int wait_id; wait_id++; wait_id &= 0xffff; /* restrict it to a 16 bit counter. */ @@ -1926,7 +1926,7 @@ static void dbri_process_interrupt_buffer(struct snd_dbri *dbri) static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id) { struct snd_dbri *dbri = dev_id; - static int errcnt = 0; + static int errcnt; int x; if (dbri == NULL) @@ -2591,7 +2591,7 @@ static int dbri_probe(struct platform_device *op) struct snd_dbri *dbri; struct resource *rp; struct snd_card *card; - static int dev = 0; + static int dev; int irq; int err; diff --git a/sound/usb/card.c b/sound/usb/card.c index fbbe330a52..2a1f76ff85 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -993,8 +993,6 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) wake_up(&chip->shutdown_wait); } -#ifdef CONFIG_PM - int snd_usb_autoresume(struct snd_usb_audio *chip) { int i, err; @@ -1106,11 +1104,6 @@ static int usb_audio_resume(struct usb_interface *intf) atomic_dec(&chip->active); /* allow autopm after this point */ return err; } -#else -#define usb_audio_suspend NULL -#define usb_audio_resume NULL -#define usb_audio_resume NULL -#endif /* CONFIG_PM */ static const struct usb_device_id usb_audio_ids [] = { #include "quirks-table.h" diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 98345a695d..4dfe764167 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -271,7 +271,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, return -EINVAL; } - /* first, see if the ID we're looking for is a clock source already */ + /* first, see if the ID we're looking at is a clock source already */ source = snd_usb_find_clock_source(chip, entity_id, proto); if (source) { entity_id = GET_VAL(source, proto, bClockID); @@ -297,7 +297,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, goto find_source; } - /* the entity ID we are looking for is a selector. + /* the entity ID we are looking at is a selector. * find out what it currently selects */ ret = uac_clock_selector_get_val(chip, clock_id); if (ret < 0) { diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 567514832b..a5641956ef 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -145,6 +145,7 @@ static inline void check_mapped_dB(const struct usbmix_name_map *p, if (p && p->dB) { cval->dBmin = p->dB->min; cval->dBmax = p->dB->max; + cval->min_mute = p->dB->min_mute; cval->initialized = 1; } } @@ -361,9 +362,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, memset(buf, 0, sizeof(buf)); - ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; - if (ret) - goto error; + if (snd_usb_lock_shutdown(chip)) + return -EIO; idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, @@ -372,8 +372,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, snd_usb_unlock_shutdown(chip); if (ret < 0) { -error: - usb_audio_err(chip, + usb_audio_dbg(chip, "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", request, validx, idx, cval->val_type); return ret; @@ -1208,12 +1207,32 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, } } +/* forcibly initialize the current mixer value; if GET_CUR fails, set to + * the minimum as default + */ +static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) +{ + int val, err; + + err = snd_usb_get_cur_mix_value(cval, ch, idx, &val); + if (!err) + return; + if (!cval->head.mixer->ignore_ctl_error) + usb_audio_warn(cval->head.mixer->chip, + "%d:%d: failed to get current value for ch %d (%d)\n", + cval->head.id, mixer_ctrl_intf(cval->head.mixer), + ch, err); + snd_usb_set_cur_mix_value(cval, ch, idx, cval->min); +} + /* * retrieve the minimum and maximum values for the specified control */ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, int default_min, struct snd_kcontrol *kctl) { + int i, idx; + /* for failsafe */ cval->min = default_min; cval->max = cval->min + 1; @@ -1226,7 +1245,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } else { int minchn = 0; if (cval->cmask) { - int i; for (i = 0; i < MAX_CHANNELS; i++) if (cval->cmask & (1 << i)) { minchn = i + 1; @@ -1327,6 +1345,19 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } } + /* initialize all elements */ + if (!cval->cmask) { + init_cur_mix_raw(cval, 0, 0); + } else { + idx = 0; + for (i = 0; i < MAX_CHANNELS; i++) { + if (cval->cmask & (1 << i)) { + init_cur_mix_raw(cval, i + 1, idx); + idx++; + } + } + } + return 0; } @@ -3602,7 +3633,6 @@ void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) mixer->disconnected = true; } -#ifdef CONFIG_PM /* stop any bus activity of a mixer */ static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) { @@ -3648,17 +3678,14 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list) err = snd_usb_set_cur_mix_value(cval, c + 1, idx, cval->cache_val[idx]); if (err < 0) - return err; + break; } idx++; } } else { /* master */ - if (cval->cached) { - err = snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); - if (err < 0) - return err; - } + if (cval->cached) + snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); } return 0; @@ -3684,7 +3711,6 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer) return snd_usb_mixer_activate(mixer); } -#endif void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, struct usb_mixer_interface *mixer, @@ -3693,7 +3719,5 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, list->mixer = mixer; list->id = unitid; list->dump = snd_usb_mixer_dump_cval; -#ifdef CONFIG_PM list->resume = restore_mixer_value; -#endif } diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 98ea24d91d..d43895c1ae 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -118,10 +118,8 @@ void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); -#ifdef CONFIG_PM int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer); -#endif int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value); diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 55eea90ee9..64f5544d0a 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -6,8 +6,9 @@ */ struct usbmix_dB_map { - u32 min; - u32 max; + int min; + int max; + bool min_mute; }; struct usbmix_name_map { @@ -336,6 +337,13 @@ static const struct usbmix_name_map bose_companion5_map[] = { { 0 } /* terminator */ }; +/* Bose Revolve+ SoundLink, correction of dB maps */ +static const struct usbmix_dB_map bose_soundlink_dB = {-8283, -0, true}; +static const struct usbmix_name_map bose_soundlink_map[] = { + { 2, NULL, .dB = &bose_soundlink_dB }, + { 0 } /* terminator */ +}; + /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */ static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0}; static const struct usbmix_name_map sennheiser_pc8_map[] = { @@ -423,6 +431,14 @@ static const struct usbmix_name_map aorus_master_alc1220vb_map[] = { {} }; +/* MSI MPG X570S Carbon Max Wifi with ALC4080 */ +static const struct usbmix_name_map msi_mpg_x570s_carbon_max_wifi_alc4080_map[] = { + { 29, "Speaker Playback" }, + { 30, "Front Headphone Playback" }, + { 32, "IEC958 Playback" }, + {} +}; + /* * Control map entries */ @@ -521,6 +537,21 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x05a7, 0x1020), .map = bose_companion5_map, }, + { + /* Bose Revolve+ SoundLink */ + .id = USB_ID(0x05a7, 0x40fa), + .map = bose_soundlink_map, + }, + { + /* Corsair Virtuoso SE Latest (wired mode) */ + .id = USB_ID(0x1b1c, 0x0a3f), + .map = corsair_virtuoso_map, + }, + { + /* Corsair Virtuoso SE Latest (wireless mode) */ + .id = USB_ID(0x1b1c, 0x0a40), + .map = corsair_virtuoso_map, + }, { /* Corsair Virtuoso SE (wired mode) */ .id = USB_ID(0x1b1c, 0x0a3d), @@ -564,6 +595,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = trx40_mobo_map, .connector_map = trx40_mobo_connector_map, }, + { /* MSI MPG X570S Carbon Max Wifi */ + .id = USB_ID(0x0db0, 0x419c), + .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/mixer_quirks.c b/sound/usb/mixer_quirks.c index d48729e6a3..d35cf54cab 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3280,7 +3280,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) return err; } -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) { switch (mixer->chip->usb_id) { @@ -3289,7 +3288,6 @@ void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer) break; } } -#endif void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, int unitid) @@ -3362,9 +3360,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, if (unitid == 7 && cval->control == UAC_FU_VOLUME) snd_dragonfly_quirk_db_scale(mixer, cval, kctl); break; - /* lowest playback value is muted on C-Media devices */ - case USB_ID(0x0d8c, 0x000c): - case USB_ID(0x0d8c, 0x0014): + /* lowest playback value is muted on some devices */ + case USB_ID(0x0d8c, 0x000c): /* C-Media */ + case USB_ID(0x0d8c, 0x0014): /* C-Media */ + case USB_ID(0x19f7, 0x0003): /* RODE NT-USB */ if (strstr(kctl->id.name, "Playback")) cval->min_mute = 1; break; diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h index 52be26db55..4ba01ba3fe 100644 --- a/sound/usb/mixer_quirks.h +++ b/sound/usb/mixer_quirks.h @@ -14,9 +14,7 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer, struct usb_mixer_elem_info *cval, int unitid, struct snd_kcontrol *kctl); -#ifdef CONFIG_PM void snd_usb_mixer_resume_quirk(struct usb_mixer_interface *mixer); -#endif #endif /* SND_USB_MIXER_QUIRKS_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 2e51fb031a..cec6e91afe 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1086,6 +1086,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (err < 0) return err; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->implicit_fb) { + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + break; + } + } + return 0; } diff --git a/sound/usb/power.h b/sound/usb/power.h index 6004231a7c..396e3e5144 100644 --- a/sound/usb/power.h +++ b/sound/usb/power.h @@ -21,17 +21,7 @@ struct snd_usb_power_domain * snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface, unsigned char id); -#ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip); void snd_usb_autosuspend(struct snd_usb_audio *chip); -#else -static inline int snd_usb_autoresume(struct snd_usb_audio *chip) -{ - return 0; -} -static inline void snd_usb_autosuspend(struct snd_usb_audio *chip) -{ -} -#endif #endif /* __USBAUDIO_POWER_H */ diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index c39cc6851e..cfc1ea5397 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -668,14 +668,15 @@ static void i_usx2y_04int(struct urb *urb) static int usx2y_rate_set(struct usx2ydev *usx2y, int rate) { - int err = 0, i; - struct snd_usx2y_urb_seq *us = NULL; - int *usbdata = NULL; - const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100; + int err = 0, i; + struct snd_usx2y_urb_seq *us = NULL; + int *usbdata = NULL; + const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100; struct urb *urb; if (usx2y->rate != rate) { - us = kzalloc(sizeof(*us) + sizeof(struct urb *) * NOOF_SETRATE_URBS, GFP_KERNEL); + us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS), + GFP_KERNEL); if (!us) { err = -ENOMEM; goto cleanup; diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c index f88c8f29cb..aca2dc1989 100644 --- a/sound/virtio/virtio_pcm_msg.c +++ b/sound/virtio/virtio_pcm_msg.c @@ -20,7 +20,7 @@ struct virtio_pcm_msg { struct virtio_snd_pcm_xfer xfer; struct virtio_snd_pcm_status status; size_t length; - struct scatterlist sgs[0]; + struct scatterlist sgs[]; }; /** @@ -146,8 +146,7 @@ int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss, int sg_num = virtsnd_pcm_sg_num(data, period_bytes); struct virtio_pcm_msg *msg; - msg = kzalloc(sizeof(*msg) + sizeof(*msg->sgs) * (sg_num + 2), - GFP_KERNEL); + msg = kzalloc(struct_size(msg, sgs, sg_num + 2), GFP_KERNEL); if (!msg) return -ENOMEM; diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 7aa9472749..4a3ff6468a 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1750,7 +1750,9 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) card_ctx->irq = irq; /* only 32bit addressable */ - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; init_channel_allocations(); diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index 2cb0a19be2..4041748c12 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -358,6 +358,7 @@ static struct xenbus_driver xen_driver = { .probe = xen_drv_probe, .remove = xen_drv_remove, .otherend_changed = sndback_changed, + .not_essential = true, }; static int __init xen_drv_init(void)