* Fri Jul 24 2026 Augusto Caringi <acaringi@redhat.com> [7.1.5-0] - redhat/configs: fix new config items for aarch64 and s390x (Augusto Caringi) - Revert some RHEL only rng patches (Justin M. Forbes) - Linux v7.1.5 Resolves: Signed-off-by: Augusto Caringi <acaringi@redhat.com>
3141 lines
97 KiB
Diff
3141 lines
97 KiB
Diff
MAINTAINERS | 6 +
|
|
Makefile | 30 +
|
|
arch/arm/Kconfig | 4 +-
|
|
arch/arm64/Kconfig | 2 +-
|
|
arch/arm64/kernel/setup.c | 27 +
|
|
arch/s390/include/asm/ipl.h | 1 +
|
|
arch/s390/kernel/ipl.c | 5 +
|
|
arch/s390/kernel/setup.c | 4 +
|
|
arch/x86/kernel/setup.c | 22 +-
|
|
arch/x86/kvm/lapic.c | 2 +-
|
|
arch/x86/kvm/mmu/mmu.c | 12 +-
|
|
arch/x86/kvm/mmu/paging_tmpl.h | 10 +-
|
|
arch/x86/kvm/vmx/nested.c | 11 +-
|
|
crypto/akcipher.c | 3 +-
|
|
crypto/dh.c | 25 +
|
|
crypto/seqiv.c | 15 +-
|
|
crypto/sig.c | 3 +-
|
|
crypto/testmgr.c | 6 +-
|
|
drivers/acpi/apei/hest.c | 8 +
|
|
drivers/acpi/irq.c | 17 +-
|
|
drivers/acpi/scan.c | 9 +
|
|
drivers/ata/libahci.c | 18 +
|
|
drivers/char/ipmi/ipmi_dmi.c | 15 +
|
|
drivers/char/ipmi/ipmi_msghandler.c | 16 +-
|
|
drivers/char/random.c | 126 ++-
|
|
drivers/firmware/efi/Makefile | 1 +
|
|
drivers/firmware/efi/efi.c | 124 ++-
|
|
drivers/firmware/efi/libstub/fdt.c | 5 +
|
|
drivers/firmware/efi/libstub/secureboot.c | 14 +-
|
|
drivers/firmware/efi/secureboot.c | 38 +
|
|
drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 12 +
|
|
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 11 +
|
|
.../gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 45 +
|
|
drivers/gpu/drm/i915/display/intel_ddi.c | 11 +-
|
|
drivers/hwtracing/coresight/coresight-etm4x-core.c | 19 +
|
|
drivers/iommu/iommu.c | 22 +
|
|
drivers/media/i2c/Kconfig | 10 +
|
|
drivers/media/i2c/Makefile | 1 +
|
|
drivers/media/i2c/imx471.c | 957 +++++++++++++++++++++
|
|
drivers/media/pci/intel/ipu-bridge.c | 41 +
|
|
drivers/pci/quirks.c | 24 +
|
|
drivers/platform/x86/intel/int3472/discrete.c | 18 +
|
|
drivers/usb/core/hub.c | 7 +
|
|
include/linux/crypto.h | 2 +
|
|
include/linux/efi.h | 22 +-
|
|
include/linux/lsm_hook_defs.h | 1 +
|
|
include/linux/random.h | 10 +
|
|
include/linux/security.h | 9 +
|
|
kernel/module/signing.c | 9 +-
|
|
net/packet/af_packet.c | 6 +-
|
|
scripts/Makefile.lib | 3 +
|
|
scripts/tags.sh | 2 +
|
|
security/integrity/platform_certs/load_uefi.c | 6 +-
|
|
security/lockdown/Kconfig | 13 +
|
|
security/lockdown/lockdown.c | 11 +
|
|
sound/soc/codecs/rt722-sdca.c | 77 +-
|
|
sound/soc/codecs/rt722-sdca.h | 4 +
|
|
tools/testing/selftests/bpf/DENYLIST.rhel | 76 ++
|
|
tools/testing/selftests/bpf/Makefile | 2 +-
|
|
tools/testing/selftests/bpf/prog_tests/ksyms_btf.c | 31 -
|
|
60 files changed, 1906 insertions(+), 135 deletions(-)
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index f74da642290a..06c8bb2075e6 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -24964,6 +24964,12 @@ T: git git://linuxtv.org/media.git
|
|
F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
|
|
F: drivers/media/i2c/imx415.c
|
|
|
|
+SONY IMX471 SENSOR DRIVER
|
|
+M: Kate Hsuan <hpa@redhat.com>
|
|
+L: linux-media@vger.kernel.org
|
|
+S: Maintained
|
|
+F: drivers/media/i2c/imx471.c
|
|
+
|
|
SONY MEMORYSTICK SUBSYSTEM
|
|
M: Maxim Levitsky <maximlevitsky@gmail.com>
|
|
M: Alex Dubov <oakad@yahoo.com>
|
|
diff --git a/Makefile b/Makefile
|
|
index 58f3b3e2dc1c..c57ee1ced308 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -356,6 +356,17 @@ ifneq ($(filter install,$(MAKECMDGOALS)),)
|
|
endif
|
|
endif
|
|
|
|
+# CKI/cross compilation hack
|
|
+# Do we need to rebuild scripts after cross compilation?
|
|
+# If kernel was cross-compiled, these scripts have arch of build host.
|
|
+REBUILD_SCRIPTS_FOR_CROSS:=0
|
|
+
|
|
+# Regenerating config with incomplete source tree will produce different
|
|
+# config options. Disable it.
|
|
+ifeq ($(REBUILD_SCRIPTS_FOR_CROSS),1)
|
|
+may-sync-config:=
|
|
+endif
|
|
+
|
|
ifdef mixed-build
|
|
# ===========================================================================
|
|
# We're called with mixed targets (*config and build targets).
|
|
@@ -1418,6 +1429,8 @@ uapi-asm-generic:
|
|
# Generate some files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
+include $(srctree)/Makefile.rhelver
|
|
+
|
|
# KERNELRELEASE can change from a few different places, meaning version.h
|
|
# needs to be updated, so this check is forced on all builds
|
|
|
|
@@ -2083,6 +2096,23 @@ endif
|
|
|
|
ifdef CONFIG_MODULES
|
|
|
|
+scripts_build:
|
|
+ $(MAKE) $(build)=scripts/basic
|
|
+ $(MAKE) $(build)=scripts/mod
|
|
+ $(MAKE) $(build)=scripts scripts/module.lds
|
|
+ $(MAKE) $(build)=scripts scripts/unifdef
|
|
+ $(MAKE) $(build)=scripts
|
|
+
|
|
+prepare_after_cross:
|
|
+ # disable STACK_VALIDATION to avoid building objtool
|
|
+ sed -i '/^CONFIG_STACK_VALIDATION/d' ./include/config/auto.conf || true
|
|
+ # build minimum set of scripts and resolve_btfids to allow building
|
|
+ # external modules
|
|
+ $(MAKE) KBUILD_EXTMOD="" M="" scripts_build V=1
|
|
+ $(MAKE) -C tools/bpf/resolve_btfids
|
|
+
|
|
+PHONY += prepare_after_cross scripts_build
|
|
+
|
|
modules.order: $(build-dir)
|
|
@:
|
|
|
|
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
|
|
index 73e6647bea46..32b90d43664c 100644
|
|
--- a/arch/arm/Kconfig
|
|
+++ b/arch/arm/Kconfig
|
|
@@ -1214,9 +1214,9 @@ config HIGHMEM
|
|
If unsure, say n.
|
|
|
|
config HIGHPTE
|
|
- bool "Allocate 2nd-level pagetables from highmem" if EXPERT
|
|
+ bool "Allocate 2nd-level pagetables from highmem"
|
|
depends on HIGHMEM && !PREEMPT_RT
|
|
- default y
|
|
+ default n
|
|
help
|
|
The VM uses one page of physical memory for each page table.
|
|
For systems with a lot of processes, this can use a lot of
|
|
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
|
|
index 10c69474f276..96190d176bc8 100644
|
|
--- a/arch/arm64/Kconfig
|
|
+++ b/arch/arm64/Kconfig
|
|
@@ -1470,7 +1470,7 @@ endchoice
|
|
|
|
config ARM64_FORCE_52BIT
|
|
bool "Force 52-bit virtual addresses for userspace"
|
|
- depends on ARM64_VA_BITS_52 && EXPERT
|
|
+ depends on ARM64_VA_BITS_52
|
|
help
|
|
For systems with 52-bit userspace VAs enabled, the kernel will attempt
|
|
to maintain compatibility with older software by providing 48-bit VAs
|
|
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
|
|
index 23c05dc7a8f2..d7b7b2f39e16 100644
|
|
--- a/arch/arm64/kernel/setup.c
|
|
+++ b/arch/arm64/kernel/setup.c
|
|
@@ -32,6 +32,8 @@
|
|
#include <linux/sched/task.h>
|
|
#include <linux/scs.h>
|
|
#include <linux/mm.h>
|
|
+#include <linux/security.h>
|
|
+#include <linux/libfdt.h>
|
|
|
|
#include <asm/acpi.h>
|
|
#include <asm/fixmap.h>
|
|
@@ -207,6 +209,24 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys)
|
|
dump_stack_set_arch_desc("%s (DT)", name);
|
|
}
|
|
|
|
+static void __init init_secureboot_mode(void)
|
|
+{
|
|
+ void *fdt = initial_boot_params;
|
|
+ u64 chosen;
|
|
+ const __be32 *prop;
|
|
+ int len;
|
|
+
|
|
+ chosen = fdt_path_offset(fdt, "/chosen");
|
|
+ if (chosen < 0)
|
|
+ return;
|
|
+
|
|
+ prop = fdt_getprop(fdt, chosen, "secure-boot-mode", &len);
|
|
+ if (!prop || len != sizeof(u32))
|
|
+ return;
|
|
+
|
|
+ efi_set_secure_boot((enum efi_secureboot_mode)fdt32_to_cpu(*prop));
|
|
+}
|
|
+
|
|
static void __init request_standard_resources(void)
|
|
{
|
|
struct memblock_region *region;
|
|
@@ -327,6 +347,13 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
|
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
|
|
WARN_TAINT(mmu_enabled_at_boot, TAINT_FIRMWARE_WORKAROUND,
|
|
FW_BUG "Booted with MMU enabled!");
|
|
+ } else {
|
|
+ init_secureboot_mode();
|
|
+
|
|
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ if (efi_enabled(EFI_SECURE_BOOT))
|
|
+ security_lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+#endif
|
|
}
|
|
|
|
arm64_memblock_init();
|
|
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h
|
|
index b0d00032479d..afb9544fb007 100644
|
|
--- a/arch/s390/include/asm/ipl.h
|
|
+++ b/arch/s390/include/asm/ipl.h
|
|
@@ -139,6 +139,7 @@ int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf,
|
|
unsigned char flags, unsigned short cert);
|
|
int ipl_report_add_certificate(struct ipl_report *report, void *key,
|
|
unsigned long addr, unsigned long len);
|
|
+bool ipl_get_secureboot(void);
|
|
|
|
/*
|
|
* DIAG 308 support
|
|
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
|
|
index 3c346b02ceb9..30e16a0d4490 100644
|
|
--- a/arch/s390/kernel/ipl.c
|
|
+++ b/arch/s390/kernel/ipl.c
|
|
@@ -2527,3 +2527,8 @@ int ipl_report_free(struct ipl_report *report)
|
|
}
|
|
|
|
#endif
|
|
+
|
|
+bool ipl_get_secureboot(void)
|
|
+{
|
|
+ return !!ipl_secure_flag;
|
|
+}
|
|
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
|
|
index b60284328fe3..e4d37b08ca29 100644
|
|
--- a/arch/s390/kernel/setup.c
|
|
+++ b/arch/s390/kernel/setup.c
|
|
@@ -47,6 +47,7 @@
|
|
#include <linux/crash_dump.h>
|
|
#include <linux/memory.h>
|
|
#include <linux/start_kernel.h>
|
|
+#include <linux/security.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/kmemleak.h>
|
|
|
|
@@ -917,6 +918,9 @@ void __init setup_arch(char **cmdline_p)
|
|
|
|
log_component_list();
|
|
|
|
+ if (ipl_get_secureboot())
|
|
+ security_lock_kernel_down("Secure IPL mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+
|
|
/* Have one command line that is parsed and saved in /proc/cmdline */
|
|
/* boot_command_line has been already set up in early.c */
|
|
*cmdline_p = boot_command_line;
|
|
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
|
|
index 46882ce79c3a..9c73814c31e0 100644
|
|
--- a/arch/x86/kernel/setup.c
|
|
+++ b/arch/x86/kernel/setup.c
|
|
@@ -21,6 +21,7 @@
|
|
#include <linux/pci.h>
|
|
#include <linux/random.h>
|
|
#include <linux/root_dev.h>
|
|
+#include <linux/security.h>
|
|
#include <linux/static_call.h>
|
|
#include <linux/sysfb.h>
|
|
#include <linux/swiotlb.h>
|
|
@@ -995,6 +996,13 @@ void __init setup_arch(char **cmdline_p)
|
|
if (efi_enabled(EFI_BOOT))
|
|
efi_init();
|
|
|
|
+ efi_set_secure_boot(boot_params.secure_boot);
|
|
+
|
|
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ if (efi_enabled(EFI_SECURE_BOOT))
|
|
+ security_lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+#endif
|
|
+
|
|
reserve_ibft_region();
|
|
x86_init.resources.dmi_setup();
|
|
|
|
@@ -1156,19 +1164,7 @@ void __init setup_arch(char **cmdline_p)
|
|
/* Allocate bigger log buffer */
|
|
setup_log_buf(1);
|
|
|
|
- if (efi_enabled(EFI_BOOT)) {
|
|
- switch (boot_params.secure_boot) {
|
|
- case efi_secureboot_mode_disabled:
|
|
- pr_info("Secure boot disabled\n");
|
|
- break;
|
|
- case efi_secureboot_mode_enabled:
|
|
- pr_info("Secure boot enabled\n");
|
|
- break;
|
|
- default:
|
|
- pr_info("Secure boot could not be determined\n");
|
|
- break;
|
|
- }
|
|
- }
|
|
+ efi_set_secure_boot(boot_params.secure_boot);
|
|
|
|
reserve_initrd();
|
|
|
|
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
|
|
index d806abaeda3d..3a0eef95e905 100644
|
|
--- a/arch/x86/kvm/lapic.c
|
|
+++ b/arch/x86/kvm/lapic.c
|
|
@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)
|
|
if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
|
|
ktimer->expired_tscdeadline = ktimer->tscdeadline;
|
|
|
|
- if (!from_timer_fn && apic->apicv_active) {
|
|
+ if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
|
|
WARN_ON(kvm_get_running_vcpu() != vcpu);
|
|
kvm_apic_inject_pending_timer_irqs(apic);
|
|
return;
|
|
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
|
|
index 0c2e35e3ffbd..dc4fda3b9769 100644
|
|
--- a/arch/x86/kvm/mmu/mmu.c
|
|
+++ b/arch/x86/kvm/mmu/mmu.c
|
|
@@ -2635,6 +2635,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp,
|
|
*/
|
|
if (tdp_enabled && invalid_list &&
|
|
child->role.guest_mode &&
|
|
+ !child->root_count &&
|
|
!atomic_long_read(&child->parent_ptes.val))
|
|
return kvm_mmu_prepare_zap_page(kvm, child,
|
|
invalid_list);
|
|
@@ -4837,16 +4838,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
|
if (r != RET_PF_CONTINUE)
|
|
return r;
|
|
|
|
- r = RET_PF_RETRY;
|
|
write_lock(&vcpu->kvm->mmu_lock);
|
|
|
|
- if (is_page_fault_stale(vcpu, fault))
|
|
- goto out_unlock;
|
|
-
|
|
r = make_mmu_pages_available(vcpu);
|
|
if (r)
|
|
goto out_unlock;
|
|
|
|
+ if (is_page_fault_stale(vcpu, fault)) {
|
|
+ r = RET_PF_RETRY;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
r = direct_map(vcpu, fault);
|
|
|
|
out_unlock:
|
|
@@ -7501,7 +7503,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
|
|
static void mmu_destroy_caches(void)
|
|
{
|
|
kmem_cache_destroy(pte_list_desc_cache);
|
|
+ pte_list_desc_cache = NULL;
|
|
kmem_cache_destroy(mmu_page_header_cache);
|
|
+ mmu_page_header_cache = NULL;
|
|
}
|
|
|
|
static void kvm_wake_nx_recovery_thread(struct kvm *kvm)
|
|
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
|
|
index 901cd2bd40b8..6465de820e70 100644
|
|
--- a/arch/x86/kvm/mmu/paging_tmpl.h
|
|
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
|
|
@@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault
|
|
}
|
|
#endif
|
|
|
|
- r = RET_PF_RETRY;
|
|
write_lock(&vcpu->kvm->mmu_lock);
|
|
|
|
- if (is_page_fault_stale(vcpu, fault))
|
|
- goto out_unlock;
|
|
-
|
|
r = make_mmu_pages_available(vcpu);
|
|
if (r)
|
|
goto out_unlock;
|
|
+
|
|
+ if (is_page_fault_stale(vcpu, fault)) {
|
|
+ r = RET_PF_RETRY;
|
|
+ goto out_unlock;
|
|
+ }
|
|
+
|
|
r = FNAME(fetch)(vcpu, fault, &walker);
|
|
|
|
out_unlock:
|
|
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
|
|
index 52ab52b0e1cc..170b0c4d865f 100644
|
|
--- a/arch/x86/kvm/vmx/nested.c
|
|
+++ b/arch/x86/kvm/vmx/nested.c
|
|
@@ -336,6 +336,7 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
|
|
static void free_nested(struct kvm_vcpu *vcpu)
|
|
{
|
|
struct vcpu_vmx *vmx = to_vmx(vcpu);
|
|
+ struct vmcs *shadow_vmcs;
|
|
|
|
if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
|
|
vmx_switch_vmcs(vcpu, &vmx->vmcs01);
|
|
@@ -353,9 +354,15 @@ static void free_nested(struct kvm_vcpu *vcpu)
|
|
vmx->nested.current_vmptr = INVALID_GPA;
|
|
if (enable_shadow_vmcs) {
|
|
vmx_disable_shadow_vmcs(vmx);
|
|
- vmcs_clear(vmx->vmcs01.shadow_vmcs);
|
|
- free_vmcs(vmx->vmcs01.shadow_vmcs);
|
|
+
|
|
+ /*
|
|
+ * Keep the pointer visible until after VMCLEAR, so migration
|
|
+ * can clear an active shadow VMCS on the old CPU.
|
|
+ */
|
|
+ shadow_vmcs = vmx->vmcs01.shadow_vmcs;
|
|
+ vmcs_clear(shadow_vmcs);
|
|
vmx->vmcs01.shadow_vmcs = NULL;
|
|
+ free_vmcs(shadow_vmcs);
|
|
}
|
|
kfree(vmx->nested.cached_vmcs12);
|
|
vmx->nested.cached_vmcs12 = NULL;
|
|
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
|
|
index dfe87b3ce183..cd1302ca3b36 100644
|
|
--- a/crypto/akcipher.c
|
|
+++ b/crypto/akcipher.c
|
|
@@ -140,8 +140,7 @@ int crypto_register_akcipher(struct akcipher_alg *alg)
|
|
|
|
if (!alg->encrypt)
|
|
alg->encrypt = akcipher_default_op;
|
|
- if (!alg->decrypt)
|
|
- alg->decrypt = akcipher_default_op;
|
|
+ alg->decrypt = akcipher_default_op;
|
|
if (!alg->set_priv_key)
|
|
alg->set_priv_key = akcipher_default_set_key;
|
|
|
|
diff --git a/crypto/dh.c b/crypto/dh.c
|
|
index 7ad4768716c8..01ccc24db074 100644
|
|
--- a/crypto/dh.c
|
|
+++ b/crypto/dh.c
|
|
@@ -227,10 +227,35 @@ static int dh_compute_value(struct kpp_request *req)
|
|
|
|
/* SP800-56A rev 3 5.6.2.1.3 key check */
|
|
} else {
|
|
+ MPI val_pct;
|
|
+
|
|
if (dh_is_pubkey_valid(ctx, val)) {
|
|
ret = -EAGAIN;
|
|
goto err_free_val;
|
|
}
|
|
+
|
|
+ /*
|
|
+ * SP800-56Arev3, 5.6.2.1.4: ("Owner Assurance
|
|
+ * of Pair-wise Consistency"): recompute the
|
|
+ * public key and check if the results match.
|
|
+ */
|
|
+ val_pct = mpi_alloc(0);
|
|
+ if (!val_pct) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_free_val;
|
|
+ }
|
|
+
|
|
+ ret = _compute_val(ctx, base, val_pct);
|
|
+ if (ret) {
|
|
+ mpi_free(val_pct);
|
|
+ goto err_free_val;
|
|
+ }
|
|
+
|
|
+ if (mpi_cmp(val, val_pct) != 0) {
|
|
+ fips_fail_notify();
|
|
+ panic("dh: pair-wise consistency test failed\n");
|
|
+ }
|
|
+ mpi_free(val_pct);
|
|
}
|
|
}
|
|
|
|
diff --git a/crypto/seqiv.c b/crypto/seqiv.c
|
|
index 678bb4145d78..fcc059e7d3db 100644
|
|
--- a/crypto/seqiv.c
|
|
+++ b/crypto/seqiv.c
|
|
@@ -123,6 +123,19 @@ static int seqiv_aead_decrypt(struct aead_request *req)
|
|
return crypto_aead_decrypt(subreq);
|
|
}
|
|
|
|
+static int aead_init_seqiv(struct crypto_aead *aead)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = aead_init_geniv(aead);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ crypto_aead_set_flags(aead, CRYPTO_TFM_FIPS_COMPLIANCE);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
|
|
{
|
|
struct aead_instance *inst;
|
|
@@ -140,7 +153,7 @@ static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
|
|
inst->alg.encrypt = seqiv_aead_encrypt;
|
|
inst->alg.decrypt = seqiv_aead_decrypt;
|
|
|
|
- inst->alg.init = aead_init_geniv;
|
|
+ inst->alg.init = aead_init_seqiv;
|
|
inst->alg.exit = aead_exit_geniv;
|
|
|
|
inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
|
|
diff --git a/crypto/sig.c b/crypto/sig.c
|
|
index beba745b6405..fd41f6d3abf9 100644
|
|
--- a/crypto/sig.c
|
|
+++ b/crypto/sig.c
|
|
@@ -112,8 +112,7 @@ static int sig_prepare_alg(struct sig_alg *alg)
|
|
{
|
|
struct crypto_alg *base = &alg->base;
|
|
|
|
- if (!alg->sign)
|
|
- alg->sign = sig_default_sign;
|
|
+ alg->sign = sig_default_sign;
|
|
if (!alg->verify)
|
|
alg->verify = sig_default_verify;
|
|
if (!alg->set_priv_key)
|
|
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
|
|
index 4d86efae65b2..31c6372c50dd 100644
|
|
--- a/crypto/testmgr.c
|
|
+++ b/crypto/testmgr.c
|
|
@@ -3843,7 +3843,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
|
|
* Don't invoke decrypt test which requires a private key
|
|
* for vectors with only a public key.
|
|
*/
|
|
- if (vecs->public_key_vec) {
|
|
+ if (1 || vecs->public_key_vec) {
|
|
err = 0;
|
|
goto free_all;
|
|
}
|
|
@@ -3979,7 +3979,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs)
|
|
* Don't invoke sign test (which requires a private key)
|
|
* for vectors with only a public key.
|
|
*/
|
|
- if (vecs->public_key_vec)
|
|
+ if (1 || vecs->public_key_vec)
|
|
return 0;
|
|
|
|
sig_size = crypto_sig_maxsize(tfm);
|
|
@@ -4909,14 +4909,12 @@ static const struct alg_test_desc alg_test_descs[] = {
|
|
}, {
|
|
.alg = "ecdh-nist-p256",
|
|
.test = alg_test_kpp,
|
|
- .fips_allowed = 1,
|
|
.suite = {
|
|
.kpp = __VECS(ecdh_p256_tv_template)
|
|
}
|
|
}, {
|
|
.alg = "ecdh-nist-p384",
|
|
.test = alg_test_kpp,
|
|
- .fips_allowed = 1,
|
|
.suite = {
|
|
.kpp = __VECS(ecdh_p384_tv_template)
|
|
}
|
|
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
|
|
index 20d757687e3d..90a13f20f052 100644
|
|
--- a/drivers/acpi/apei/hest.c
|
|
+++ b/drivers/acpi/apei/hest.c
|
|
@@ -142,6 +142,14 @@ static int apei_hest_parse(apei_hest_func_t func, void *data)
|
|
if (hest_disable || !hest_tab)
|
|
return -EINVAL;
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* Ignore broken firmware */
|
|
+ if (!strncmp(hest_tab->header.oem_id, "HPE ", 6) &&
|
|
+ !strncmp(hest_tab->header.oem_table_id, "ProLiant", 8) &&
|
|
+ MIDR_IMPLEMENTOR(read_cpuid_id()) == ARM_CPU_IMP_APM)
|
|
+ return -EINVAL;
|
|
+#endif
|
|
+
|
|
hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
|
|
for (i = 0; i < hest_tab->error_source_count; i++) {
|
|
len = hest_esrc_len(hest_hdr);
|
|
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
|
|
index d1595156c86a..9485212f1a6d 100644
|
|
--- a/drivers/acpi/irq.c
|
|
+++ b/drivers/acpi/irq.c
|
|
@@ -143,6 +143,7 @@ struct acpi_irq_parse_one_ctx {
|
|
unsigned int index;
|
|
unsigned long *res_flags;
|
|
struct irq_fwspec *fwspec;
|
|
+ bool skip_producer_check;
|
|
};
|
|
|
|
/**
|
|
@@ -216,7 +217,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
|
return AE_CTRL_TERMINATE;
|
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
|
eirq = &ares->data.extended_irq;
|
|
- if (eirq->producer_consumer == ACPI_PRODUCER)
|
|
+ if (!ctx->skip_producer_check &&
|
|
+ eirq->producer_consumer == ACPI_PRODUCER)
|
|
return AE_OK;
|
|
if (ctx->index >= eirq->interrupt_count) {
|
|
ctx->index -= eirq->interrupt_count;
|
|
@@ -252,8 +254,19 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
|
static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
|
|
struct irq_fwspec *fwspec, unsigned long *flags)
|
|
{
|
|
- struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
|
|
+ struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec, false };
|
|
|
|
+ /*
|
|
+ * Firmware on arm64-based HPE m400 platform incorrectly marks
|
|
+ * its UART interrupt as ACPI_PRODUCER rather than ACPI_CONSUMER.
|
|
+ * Don't do the producer/consumer check for that device.
|
|
+ */
|
|
+ if (IS_ENABLED(CONFIG_ARM64)) {
|
|
+ struct acpi_device *adev = acpi_get_acpi_dev(handle);
|
|
+
|
|
+ if (adev && !strcmp(acpi_device_hid(adev), "APMC0D08"))
|
|
+ ctx.skip_producer_check = true;
|
|
+ }
|
|
acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
|
|
return ctx.rc;
|
|
}
|
|
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
|
|
index 530547cda8b2..dc046780c74d 100644
|
|
--- a/drivers/acpi/scan.c
|
|
+++ b/drivers/acpi/scan.c
|
|
@@ -1792,6 +1792,15 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
|
|
if (!acpi_match_device_ids(device, ignore_serial_bus_ids))
|
|
return false;
|
|
|
|
+ /*
|
|
+ * Firmware on some arm64 X-Gene platforms will make the UART
|
|
+ * device appear as both a UART and a slave of that UART. Just
|
|
+ * bail out here for X-Gene UARTs.
|
|
+ */
|
|
+ if (IS_ENABLED(CONFIG_ARM64) &&
|
|
+ !strcmp(acpi_device_hid(device), "APMC0D08"))
|
|
+ return false;
|
|
+
|
|
INIT_LIST_HEAD(&resource_list);
|
|
acpi_dev_get_resources(device, &resource_list,
|
|
acpi_check_serial_bus_slave,
|
|
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
|
|
index c79abdfcd7a9..e23bfb7f94c7 100644
|
|
--- a/drivers/ata/libahci.c
|
|
+++ b/drivers/ata/libahci.c
|
|
@@ -731,6 +731,24 @@ int ahci_stop_engine(struct ata_port *ap)
|
|
tmp &= ~PORT_CMD_START;
|
|
writel(tmp, port_mmio + PORT_CMD);
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* Rev Ax of Cavium CN99XX needs a hack for port stop */
|
|
+ if (dev_is_pci(ap->host->dev) &&
|
|
+ to_pci_dev(ap->host->dev)->vendor == 0x14e4 &&
|
|
+ to_pci_dev(ap->host->dev)->device == 0x9027 &&
|
|
+ midr_is_cpu_model_range(read_cpuid_id(),
|
|
+ MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN),
|
|
+ MIDR_CPU_VAR_REV(0, 0),
|
|
+ MIDR_CPU_VAR_REV(0, MIDR_REVISION_MASK))) {
|
|
+ tmp = readl(hpriv->mmio + 0x8000);
|
|
+ udelay(100);
|
|
+ writel(tmp | (1 << 26), hpriv->mmio + 0x8000);
|
|
+ udelay(100);
|
|
+ writel(tmp & ~(1 << 26), hpriv->mmio + 0x8000);
|
|
+ dev_warn(ap->host->dev, "CN99XX SATA reset workaround applied\n");
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* wait for engine to stop. This could be as long as 500 msec */
|
|
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
|
|
PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
|
|
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
|
|
index 505e32911c34..26f7ee0ccf40 100644
|
|
--- a/drivers/char/ipmi/ipmi_dmi.c
|
|
+++ b/drivers/char/ipmi/ipmi_dmi.c
|
|
@@ -215,6 +215,21 @@ static int __init scan_for_dmi_ipmi(void)
|
|
{
|
|
const struct dmi_device *dev = NULL;
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* RHEL-only
|
|
+ * If this is ARM-based HPE m400, return now, because that platform
|
|
+ * reports the host-side ipmi address as intel port-io space, which
|
|
+ * does not exist in the ARM architecture.
|
|
+ */
|
|
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
+
|
|
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
|
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
|
+ return 0;
|
|
+ }
|
|
+ /* END RHEL-only */
|
|
+#endif
|
|
+
|
|
while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
|
|
dmi_decode_ipmi((const struct dmi_header *) dev->device_data);
|
|
|
|
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
|
|
index 3e0b7d60269d..8317d264426a 100644
|
|
--- a/drivers/char/ipmi/ipmi_msghandler.c
|
|
+++ b/drivers/char/ipmi/ipmi_msghandler.c
|
|
@@ -34,6 +34,7 @@
|
|
#include <linux/uuid.h>
|
|
#include <linux/nospec.h>
|
|
#include <linux/vmalloc.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/delay.h>
|
|
|
|
#define IPMI_DRIVER_VERSION "39.2"
|
|
@@ -5649,8 +5650,21 @@ static int __init ipmi_init_msghandler_mod(void)
|
|
{
|
|
int rv;
|
|
|
|
- pr_info("version " IPMI_DRIVER_VERSION "\n");
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* RHEL-only
|
|
+ * If this is ARM-based HPE m400, return now, because that platform
|
|
+ * reports the host-side ipmi address as intel port-io space, which
|
|
+ * does not exist in the ARM architecture.
|
|
+ */
|
|
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
|
|
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
|
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+ /* END RHEL-only */
|
|
+#endif
|
|
+ pr_info("version " IPMI_DRIVER_VERSION "\n");
|
|
mutex_lock(&ipmi_interfaces_mutex);
|
|
rv = ipmi_register_driver();
|
|
mutex_unlock(&ipmi_interfaces_mutex);
|
|
diff --git a/drivers/char/random.c b/drivers/char/random.c
|
|
index b4da1fb976c1..1e112e2b405b 100644
|
|
--- a/drivers/char/random.c
|
|
+++ b/drivers/char/random.c
|
|
@@ -51,9 +51,11 @@
|
|
#include <linux/completion.h>
|
|
#include <linux/uuid.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/rcupdate.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/siphash.h>
|
|
#include <linux/sched/isolation.h>
|
|
+#include <linux/fips.h>
|
|
#include <crypto/chacha.h>
|
|
#include <crypto/blake2s.h>
|
|
#include <vdso/datapage.h>
|
|
@@ -320,6 +322,11 @@ static void crng_fast_key_erasure(u8 key[CHACHA_KEY_SIZE],
|
|
memzero_explicit(first_block, sizeof(first_block));
|
|
}
|
|
|
|
+/*
|
|
+ * Hook for external RNG.
|
|
+ */
|
|
+static const struct random_extrng __rcu *extrng;
|
|
+
|
|
/*
|
|
* This function returns a ChaCha state that you may use for generating
|
|
* random data. It also returns up to 32 bytes on its own of random data
|
|
@@ -732,7 +739,8 @@ static void __cold _credit_init_bits(size_t bits)
|
|
queue_work(system_dfl_wq, &set_ready);
|
|
atomic_notifier_call_chain(&random_ready_notifier, 0, NULL);
|
|
if (IS_ENABLED(CONFIG_VDSO_GETRANDOM))
|
|
- WRITE_ONCE(vdso_k_rng_data->is_ready, true);
|
|
+ if (!fips_enabled)
|
|
+ WRITE_ONCE(vdso_k_rng_data->is_ready, true);
|
|
wake_up_interruptible(&crng_init_wait);
|
|
kill_fasync(&fasync, SIGIO, POLL_IN);
|
|
pr_notice("crng init done\n");
|
|
@@ -751,6 +759,9 @@ static void __cold _credit_init_bits(size_t bits)
|
|
}
|
|
|
|
|
|
+static const struct file_operations extrng_random_fops;
|
|
+static const struct file_operations extrng_urandom_fops;
|
|
+
|
|
/**********************************************************************
|
|
*
|
|
* Entropy collection routines.
|
|
@@ -967,6 +978,19 @@ void __init add_bootloader_randomness(const void *buf, size_t len)
|
|
credit_init_bits(len * 8);
|
|
}
|
|
|
|
+void random_register_extrng(const struct random_extrng *rng)
|
|
+{
|
|
+ rcu_assign_pointer(extrng, rng);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(random_register_extrng);
|
|
+
|
|
+void random_unregister_extrng(void)
|
|
+{
|
|
+ RCU_INIT_POINTER(extrng, NULL);
|
|
+ synchronize_rcu();
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(random_unregister_extrng);
|
|
+
|
|
#if IS_ENABLED(CONFIG_VMGENID)
|
|
static BLOCKING_NOTIFIER_HEAD(vmfork_chain);
|
|
|
|
@@ -1381,6 +1405,7 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags
|
|
{
|
|
struct iov_iter iter;
|
|
int ret;
|
|
+ const struct random_extrng *rng;
|
|
|
|
if (flags & ~(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE))
|
|
return -EINVAL;
|
|
@@ -1392,6 +1417,21 @@ SYSCALL_DEFINE3(getrandom, char __user *, ubuf, size_t, len, unsigned int, flags
|
|
if ((flags & (GRND_INSECURE | GRND_RANDOM)) == (GRND_INSECURE | GRND_RANDOM))
|
|
return -EINVAL;
|
|
|
|
+ rcu_read_lock();
|
|
+ rng = rcu_dereference(extrng);
|
|
+ if (rng && !try_module_get(rng->owner))
|
|
+ rng = NULL;
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ if (rng) {
|
|
+ ret = import_ubuf(ITER_DEST, ubuf, len, &iter);
|
|
+ if (unlikely(ret))
|
|
+ return ret;
|
|
+ ret = rng->extrng_read_iter(&iter, !!(flags & GRND_RANDOM));
|
|
+ module_put(rng->owner);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
if (!crng_ready() && !(flags & GRND_INSECURE)) {
|
|
if (flags & GRND_NONBLOCK)
|
|
return -EAGAIN;
|
|
@@ -1412,6 +1452,12 @@ static __poll_t random_poll(struct file *file, poll_table *wait)
|
|
return crng_ready() ? EPOLLIN | EPOLLRDNORM : EPOLLOUT | EPOLLWRNORM;
|
|
}
|
|
|
|
+static __poll_t extrng_poll(struct file *file, poll_table * wait)
|
|
+{
|
|
+ /* extrng pool is always full, always read, no writes */
|
|
+ return EPOLLIN | EPOLLRDNORM;
|
|
+}
|
|
+
|
|
static ssize_t write_pool_user(struct iov_iter *iter)
|
|
{
|
|
u8 block[BLAKE2S_BLOCK_SIZE];
|
|
@@ -1552,7 +1598,58 @@ static int random_fasync(int fd, struct file *filp, int on)
|
|
return fasync_helper(fd, filp, on, &fasync);
|
|
}
|
|
|
|
+static int random_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ const struct random_extrng *rng;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ rng = rcu_dereference(extrng);
|
|
+ if (rng && !try_module_get(rng->owner))
|
|
+ rng = NULL;
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ if (!rng)
|
|
+ return 0;
|
|
+
|
|
+ filp->f_op = &extrng_random_fops;
|
|
+ filp->private_data = rng->owner;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int urandom_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ const struct random_extrng *rng;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ rng = rcu_dereference(extrng);
|
|
+ if (rng && !try_module_get(rng->owner))
|
|
+ rng = NULL;
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ if (!rng)
|
|
+ return 0;
|
|
+
|
|
+ filp->f_op = &extrng_urandom_fops;
|
|
+ filp->private_data = rng->owner;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int extrng_release(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ module_put(filp->private_data);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t
|
|
+extrng_read_iter(struct kiocb *kiocb, struct iov_iter *iter)
|
|
+{
|
|
+ return rcu_dereference_raw(extrng)->extrng_read_iter(iter, false);
|
|
+}
|
|
+
|
|
const struct file_operations random_fops = {
|
|
+ .open = random_open,
|
|
.read_iter = random_read_iter,
|
|
.write_iter = random_write_iter,
|
|
.poll = random_poll,
|
|
@@ -1565,6 +1662,7 @@ const struct file_operations random_fops = {
|
|
};
|
|
|
|
const struct file_operations urandom_fops = {
|
|
+ .open = urandom_open,
|
|
.read_iter = urandom_read_iter,
|
|
.write_iter = random_write_iter,
|
|
.unlocked_ioctl = random_ioctl,
|
|
@@ -1575,6 +1673,32 @@ const struct file_operations urandom_fops = {
|
|
.splice_write = iter_file_splice_write,
|
|
};
|
|
|
|
+static const struct file_operations extrng_random_fops = {
|
|
+ .open = random_open,
|
|
+ .read_iter = extrng_read_iter,
|
|
+ .write_iter = random_write_iter,
|
|
+ .poll = extrng_poll,
|
|
+ .unlocked_ioctl = random_ioctl,
|
|
+ .compat_ioctl = compat_ptr_ioctl,
|
|
+ .fasync = random_fasync,
|
|
+ .llseek = noop_llseek,
|
|
+ .release = extrng_release,
|
|
+ .splice_read = copy_splice_read,
|
|
+ .splice_write = iter_file_splice_write,
|
|
+};
|
|
+
|
|
+static const struct file_operations extrng_urandom_fops = {
|
|
+ .open = urandom_open,
|
|
+ .read_iter = extrng_read_iter,
|
|
+ .write_iter = random_write_iter,
|
|
+ .unlocked_ioctl = random_ioctl,
|
|
+ .compat_ioctl = compat_ptr_ioctl,
|
|
+ .fasync = random_fasync,
|
|
+ .llseek = noop_llseek,
|
|
+ .release = extrng_release,
|
|
+ .splice_read = copy_splice_read,
|
|
+ .splice_write = iter_file_splice_write,
|
|
+};
|
|
|
|
/********************************************************************
|
|
*
|
|
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
|
|
index 8efbcf699e4f..96d5a1ca981d 100644
|
|
--- a/drivers/firmware/efi/Makefile
|
|
+++ b/drivers/firmware/efi/Makefile
|
|
@@ -25,6 +25,7 @@ subdir-$(CONFIG_EFI_STUB) += libstub
|
|
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
|
|
obj-$(CONFIG_EFI_TEST) += test/
|
|
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
|
|
+obj-$(CONFIG_EFI) += secureboot.o
|
|
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
|
|
obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o
|
|
obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o
|
|
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
|
|
index 318d1cc9a066..eb41434d4f5e 100644
|
|
--- a/drivers/firmware/efi/efi.c
|
|
+++ b/drivers/firmware/efi/efi.c
|
|
@@ -33,6 +33,7 @@
|
|
#include <linux/memblock.h>
|
|
#include <linux/security.h>
|
|
#include <linux/notifier.h>
|
|
+#include <linux/bsearch.h>
|
|
|
|
#include <asm/early_ioremap.h>
|
|
|
|
@@ -1018,40 +1019,101 @@ int efi_mem_type(unsigned long phys_addr)
|
|
return md.type;
|
|
}
|
|
|
|
+struct efi_error_code {
|
|
+ efi_status_t status;
|
|
+ int errno;
|
|
+ const char *description;
|
|
+};
|
|
+
|
|
+static const struct efi_error_code efi_error_codes[] = {
|
|
+ { EFI_SUCCESS, 0, "Success"},
|
|
+#if 0
|
|
+ { EFI_LOAD_ERROR, -EPICK_AN_ERRNO, "Load Error"},
|
|
+#endif
|
|
+ { EFI_INVALID_PARAMETER, -EINVAL, "Invalid Parameter"},
|
|
+ { EFI_UNSUPPORTED, -ENOSYS, "Unsupported"},
|
|
+ { EFI_BAD_BUFFER_SIZE, -ENOSPC, "Bad Buffer Size"},
|
|
+ { EFI_BUFFER_TOO_SMALL, -ENOSPC, "Buffer Too Small"},
|
|
+ { EFI_NOT_READY, -EAGAIN, "Not Ready"},
|
|
+ { EFI_DEVICE_ERROR, -EIO, "Device Error"},
|
|
+ { EFI_WRITE_PROTECTED, -EROFS, "Write Protected"},
|
|
+ { EFI_OUT_OF_RESOURCES, -ENOMEM, "Out of Resources"},
|
|
+#if 0
|
|
+ { EFI_VOLUME_CORRUPTED, -EPICK_AN_ERRNO, "Volume Corrupt"},
|
|
+ { EFI_VOLUME_FULL, -EPICK_AN_ERRNO, "Volume Full"},
|
|
+ { EFI_NO_MEDIA, -EPICK_AN_ERRNO, "No Media"},
|
|
+ { EFI_MEDIA_CHANGED, -EPICK_AN_ERRNO, "Media changed"},
|
|
+#endif
|
|
+ { EFI_NOT_FOUND, -ENOENT, "Not Found"},
|
|
+#if 0
|
|
+ { EFI_ACCESS_DENIED, -EPICK_AN_ERRNO, "Access Denied"},
|
|
+ { EFI_NO_RESPONSE, -EPICK_AN_ERRNO, "No Response"},
|
|
+ { EFI_NO_MAPPING, -EPICK_AN_ERRNO, "No mapping"},
|
|
+ { EFI_TIMEOUT, -EPICK_AN_ERRNO, "Time out"},
|
|
+ { EFI_NOT_STARTED, -EPICK_AN_ERRNO, "Not started"},
|
|
+ { EFI_ALREADY_STARTED, -EPICK_AN_ERRNO, "Already started"},
|
|
+#endif
|
|
+ { EFI_ABORTED, -EINTR, "Aborted"},
|
|
+#if 0
|
|
+ { EFI_ICMP_ERROR, -EPICK_AN_ERRNO, "ICMP Error"},
|
|
+ { EFI_TFTP_ERROR, -EPICK_AN_ERRNO, "TFTP Error"},
|
|
+ { EFI_PROTOCOL_ERROR, -EPICK_AN_ERRNO, "Protocol Error"},
|
|
+ { EFI_INCOMPATIBLE_VERSION, -EPICK_AN_ERRNO, "Incompatible Version"},
|
|
+#endif
|
|
+ { EFI_SECURITY_VIOLATION, -EACCES, "Security Policy Violation"},
|
|
+#if 0
|
|
+ { EFI_CRC_ERROR, -EPICK_AN_ERRNO, "CRC Error"},
|
|
+ { EFI_END_OF_MEDIA, -EPICK_AN_ERRNO, "End of Media"},
|
|
+ { EFI_END_OF_FILE, -EPICK_AN_ERRNO, "End of File"},
|
|
+ { EFI_INVALID_LANGUAGE, -EPICK_AN_ERRNO, "Invalid Languages"},
|
|
+ { EFI_COMPROMISED_DATA, -EPICK_AN_ERRNO, "Compromised Data"},
|
|
+
|
|
+ // warnings
|
|
+ { EFI_WARN_UNKOWN_GLYPH, -EPICK_AN_ERRNO, "Warning Unknown Glyph"},
|
|
+ { EFI_WARN_DELETE_FAILURE, -EPICK_AN_ERRNO, "Warning Delete Failure"},
|
|
+ { EFI_WARN_WRITE_FAILURE, -EPICK_AN_ERRNO, "Warning Write Failure"},
|
|
+ { EFI_WARN_BUFFER_TOO_SMALL, -EPICK_AN_ERRNO, "Warning Buffer Too Small"},
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int
|
|
+efi_status_cmp_bsearch(const void *key, const void *item)
|
|
+{
|
|
+ u64 status = (u64)(uintptr_t)key;
|
|
+ struct efi_error_code *code = (struct efi_error_code *)item;
|
|
+
|
|
+ if (status < code->status)
|
|
+ return -1;
|
|
+ if (status > code->status)
|
|
+ return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int efi_status_to_err(efi_status_t status)
|
|
{
|
|
- int err;
|
|
-
|
|
- switch (status) {
|
|
- case EFI_SUCCESS:
|
|
- err = 0;
|
|
- break;
|
|
- case EFI_INVALID_PARAMETER:
|
|
- err = -EINVAL;
|
|
- break;
|
|
- case EFI_OUT_OF_RESOURCES:
|
|
- err = -ENOSPC;
|
|
- break;
|
|
- case EFI_DEVICE_ERROR:
|
|
- err = -EIO;
|
|
- break;
|
|
- case EFI_WRITE_PROTECTED:
|
|
- err = -EROFS;
|
|
- break;
|
|
- case EFI_SECURITY_VIOLATION:
|
|
- err = -EACCES;
|
|
- break;
|
|
- case EFI_NOT_FOUND:
|
|
- err = -ENOENT;
|
|
- break;
|
|
- case EFI_ABORTED:
|
|
- err = -EINTR;
|
|
- break;
|
|
- default:
|
|
- err = -EINVAL;
|
|
- }
|
|
+ struct efi_error_code *found;
|
|
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
|
|
|
- return err;
|
|
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
|
+ num, sizeof(struct efi_error_code),
|
|
+ efi_status_cmp_bsearch);
|
|
+ if (!found)
|
|
+ return -EINVAL;
|
|
+ return found->errno;
|
|
+}
|
|
+
|
|
+const char *
|
|
+efi_status_to_str(efi_status_t status)
|
|
+{
|
|
+ struct efi_error_code *found;
|
|
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
|
+
|
|
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
|
+ num, sizeof(struct efi_error_code),
|
|
+ efi_status_cmp_bsearch);
|
|
+ if (!found)
|
|
+ return "Unknown error code";
|
|
+ return found->description;
|
|
}
|
|
EXPORT_SYMBOL_GPL(efi_status_to_err);
|
|
|
|
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
|
|
index 23b3543d3041..0d08a549a10b 100644
|
|
--- a/drivers/firmware/efi/libstub/fdt.c
|
|
+++ b/drivers/firmware/efi/libstub/fdt.c
|
|
@@ -132,6 +132,11 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
|
|
}
|
|
}
|
|
|
|
+ fdt_val32 = cpu_to_fdt32((u32)efi_get_secureboot());
|
|
+ status = fdt_setprop_var(fdt, node, "secure-boot-mode", fdt_val32);
|
|
+ if (status)
|
|
+ goto fdt_set_fail;
|
|
+
|
|
/* Shrink the FDT back to its minimum size: */
|
|
fdt_pack(fdt);
|
|
|
|
diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c
|
|
index 516f4f0069bd..380354755108 100644
|
|
--- a/drivers/firmware/efi/libstub/secureboot.c
|
|
+++ b/drivers/firmware/efi/libstub/secureboot.c
|
|
@@ -29,10 +29,13 @@ enum efi_secureboot_mode efi_get_secureboot(void)
|
|
{
|
|
u32 attr;
|
|
unsigned long size;
|
|
- enum efi_secureboot_mode mode;
|
|
+ static enum efi_secureboot_mode mode;
|
|
efi_status_t status;
|
|
u8 moksbstate;
|
|
|
|
+ if (mode != efi_secureboot_mode_unset)
|
|
+ return mode;
|
|
+
|
|
mode = efi_get_secureboot_mode(get_var);
|
|
if (mode == efi_secureboot_mode_unknown) {
|
|
efi_err("Could not determine UEFI Secure Boot status.\n");
|
|
@@ -53,10 +56,13 @@ enum efi_secureboot_mode efi_get_secureboot(void)
|
|
/* If it fails, we don't care why. Default to secure */
|
|
if (status != EFI_SUCCESS)
|
|
goto secure_boot_enabled;
|
|
- if (!(attr & EFI_VARIABLE_NON_VOLATILE) && moksbstate == 1)
|
|
- return efi_secureboot_mode_disabled;
|
|
+ if (!(attr & EFI_VARIABLE_NON_VOLATILE) && moksbstate == 1) {
|
|
+ mode = efi_secureboot_mode_disabled;
|
|
+ return mode;
|
|
+ }
|
|
|
|
secure_boot_enabled:
|
|
efi_info("UEFI Secure Boot is enabled.\n");
|
|
- return efi_secureboot_mode_enabled;
|
|
+ mode = efi_secureboot_mode_enabled;
|
|
+ return mode;
|
|
}
|
|
diff --git a/drivers/firmware/efi/secureboot.c b/drivers/firmware/efi/secureboot.c
|
|
new file mode 100644
|
|
index 000000000000..de0a3714a5d4
|
|
--- /dev/null
|
|
+++ b/drivers/firmware/efi/secureboot.c
|
|
@@ -0,0 +1,38 @@
|
|
+/* Core kernel secure boot support.
|
|
+ *
|
|
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/efi.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/printk.h>
|
|
+
|
|
+/*
|
|
+ * Decide what to do when UEFI secure boot mode is enabled.
|
|
+ */
|
|
+void __init efi_set_secure_boot(enum efi_secureboot_mode mode)
|
|
+{
|
|
+ if (efi_enabled(EFI_BOOT)) {
|
|
+ switch (mode) {
|
|
+ case efi_secureboot_mode_disabled:
|
|
+ pr_info("Secure boot disabled\n");
|
|
+ break;
|
|
+ case efi_secureboot_mode_enabled:
|
|
+ set_bit(EFI_SECURE_BOOT, &efi.flags);
|
|
+ pr_info("Secure boot enabled\n");
|
|
+ break;
|
|
+ default:
|
|
+ pr_warn("Secure boot could not be determined (mode %u)\n",
|
|
+ mode);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
|
|
index 8faf7de7aaa9..f65bb1dc44ac 100644
|
|
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
|
|
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
|
|
@@ -1365,6 +1365,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
|
|
bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
|
|
}
|
|
|
|
+static int smu_set_power_dep(struct smu_context *smu, bool enable)
|
|
+{
|
|
+ if (!smu->ppt_funcs->set_power_dep)
|
|
+ return 0;
|
|
+
|
|
+ return smu->ppt_funcs->set_power_dep(smu, enable);
|
|
+}
|
|
+
|
|
static int smu_sw_init(struct amdgpu_ip_block *ip_block)
|
|
{
|
|
struct amdgpu_device *adev = ip_block->adev;
|
|
@@ -1426,6 +1434,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
|
|
if (!smu->ppt_funcs->get_fan_control_mode)
|
|
smu->adev->pm.no_fan = true;
|
|
|
|
+ smu_set_power_dep(smu, true);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1448,6 +1458,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)
|
|
|
|
smu_fini_microcode(smu);
|
|
|
|
+ smu_set_power_dep(smu, false);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
|
|
index d76e0b005308..e14bcc3f937c 100644
|
|
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
|
|
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
|
|
@@ -749,6 +749,9 @@ struct smu_context {
|
|
bool pm_enabled;
|
|
bool is_apu;
|
|
|
|
+ /* Power dependency link from an integrated xHCI controller to the GPU */
|
|
+ struct device_link *usb_power_link;
|
|
+
|
|
uint32_t smc_driver_if_version;
|
|
uint32_t smc_fw_if_version;
|
|
uint32_t smc_fw_version;
|
|
@@ -1654,6 +1657,14 @@ struct pptable_funcs {
|
|
* Return: ras_smu_drv *
|
|
*/
|
|
int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
|
|
+
|
|
+ /**
|
|
+ * @set_power_dep: Create or destroy a power dependency link
|
|
+ * from an integrated xHCI controller to the GPU so that the GPU is
|
|
+ * resumed before the USB controller during PM resume. @enable is true
|
|
+ * to create the link and false to tear it down.
|
|
+ */
|
|
+ int (*set_power_dep)(struct smu_context *smu, bool enable);
|
|
};
|
|
|
|
typedef enum {
|
|
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
|
|
index 75719c47a41e..3d73f2050bbe 100644
|
|
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
|
|
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
|
|
@@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * Link any xHCI controller sharing the GPU's PCIe root port as a consumer
|
|
+ * of the GPU so the GPU resumes first, avoiding an xHCI resume race.
|
|
+ */
|
|
+static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
|
|
+{
|
|
+ struct amdgpu_device *adev = smu->adev;
|
|
+ struct pci_dev *gpu_pdev = adev->pdev;
|
|
+ struct pci_dev *root_port, *usb_pdev = NULL;
|
|
+ struct device_link *link;
|
|
+
|
|
+ if (!enable) {
|
|
+ if (smu->usb_power_link) {
|
|
+ device_link_del(smu->usb_power_link);
|
|
+ smu->usb_power_link = NULL;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ root_port = pcie_find_root_port(gpu_pdev);
|
|
+ while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
|
|
+ struct pci_dev *usb_root;
|
|
+
|
|
+ usb_root = pcie_find_root_port(usb_pdev);
|
|
+ if (usb_root != root_port)
|
|
+ continue;
|
|
+
|
|
+ /* Create device link: USB (consumer) depends on GPU (supplier) */
|
|
+ link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
|
|
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
|
|
+ if (link) {
|
|
+ smu->usb_power_link = link;
|
|
+ drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
|
|
+ pci_name(usb_pdev), pci_name(gpu_pdev));
|
|
+ /* Only create one link for the first USB controller found */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pci_dev_put(usb_pdev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
|
|
.check_fw_status = smu_v14_0_check_fw_status,
|
|
.check_fw_version = smu_cmn_check_fw_version,
|
|
@@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
|
|
.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
|
|
.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
|
|
.set_mall_enable = smu_v14_0_common_set_mall_enable,
|
|
+ .set_power_dep = smu_v14_0_0_set_power_dep,
|
|
};
|
|
|
|
static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)
|
|
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
|
|
index ebefa889bc8c..0987eee38bd1 100644
|
|
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
|
|
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
|
|
@@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state,
|
|
/* 3. Select Thunderbolt */
|
|
mtl_port_buf_ctl_io_selection(encoder);
|
|
|
|
- /* 4. Enable Panel Power if PPS is required */
|
|
- intel_pps_on(intel_dp);
|
|
-
|
|
/* 5. Enable the port PLL */
|
|
intel_ddi_enable_clock(encoder, crtc_state);
|
|
|
|
@@ -3710,6 +3707,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state,
|
|
else if (display->platform.geminilake || display->platform.broxton)
|
|
bxt_dpio_phy_set_lane_optim_mask(encoder,
|
|
crtc_state->lane_lat_optim_mask);
|
|
+
|
|
+ /*
|
|
+ * There is no direct connection between the PLL and PPS, however
|
|
+ * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts
|
|
+ * during system resume. Do that matching the Bspec order as well.
|
|
+ */
|
|
+ if (DISPLAY_VER(display) >= 14)
|
|
+ intel_pps_on(&dig_port->dp);
|
|
}
|
|
|
|
static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder)
|
|
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
index a251375db24b..2b5337eaeb6d 100644
|
|
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
@@ -12,6 +12,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/io.h>
|
|
#include <linux/err.h>
|
|
#include <linux/fs.h>
|
|
@@ -2547,6 +2548,16 @@ static const struct amba_id etm4_ids[] = {
|
|
{},
|
|
};
|
|
|
|
+static const struct dmi_system_id broken_coresight[] = {
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "HPE"),
|
|
+ DMI_MATCH(DMI_PRODUCT_NAME, "Apollo 70"),
|
|
+ },
|
|
+ },
|
|
+ { } /* terminating entry */
|
|
+};
|
|
+
|
|
MODULE_DEVICE_TABLE(amba, etm4_ids);
|
|
|
|
static struct amba_driver etm4x_amba_driver = {
|
|
@@ -2621,6 +2632,11 @@ static int __init etm4x_init(void)
|
|
{
|
|
int ret;
|
|
|
|
+ if (dmi_check_system(broken_coresight)) {
|
|
+ pr_info("ETM4 disabled due to firmware bug\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
ret = etm4_pm_setup();
|
|
|
|
/* etm4_pm_setup() does its own cleanup - exit on error */
|
|
@@ -2647,6 +2663,9 @@ static int __init etm4x_init(void)
|
|
|
|
static void __exit etm4x_exit(void)
|
|
{
|
|
+ if (dmi_check_system(broken_coresight))
|
|
+ return;
|
|
+
|
|
amba_driver_unregister(&etm4x_amba_driver);
|
|
platform_driver_unregister(&etm4_platform_driver);
|
|
etm4_pm_clear();
|
|
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
|
|
index d1a9e713d3a0..940458370730 100644
|
|
--- a/drivers/iommu/iommu.c
|
|
+++ b/drivers/iommu/iommu.c
|
|
@@ -8,6 +8,7 @@
|
|
|
|
#include <linux/amba/bus.h>
|
|
#include <linux/device.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/bug.h>
|
|
@@ -3136,6 +3137,27 @@ int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids)
|
|
}
|
|
EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+static int __init iommu_quirks(void)
|
|
+{
|
|
+ const char *vendor, *name;
|
|
+
|
|
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
|
+ name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
+
|
|
+ if (vendor &&
|
|
+ (strncmp(vendor, "GIGABYTE", 8) == 0 && name &&
|
|
+ (strncmp(name, "R120", 4) == 0 ||
|
|
+ strncmp(name, "R270", 4) == 0))) {
|
|
+ pr_warn("Gigabyte %s detected, force iommu passthrough mode", name);
|
|
+ iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+arch_initcall(iommu_quirks);
|
|
+#endif
|
|
+
|
|
/**
|
|
* iommu_setup_default_domain - Set the default_domain for the group
|
|
* @group: Group to change
|
|
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
|
|
index 8f2ba4121586..e1143579ae3d 100644
|
|
--- a/drivers/media/i2c/Kconfig
|
|
+++ b/drivers/media/i2c/Kconfig
|
|
@@ -287,6 +287,16 @@ config VIDEO_IMX415
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called imx415.
|
|
|
|
+config VIDEO_IMX471
|
|
+ tristate "Sony IMX471 sensor support"
|
|
+ select V4L2_CCI_I2C
|
|
+ help
|
|
+ This is a Video4Linux2 sensor driver for the Sony
|
|
+ IMX471 camera.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called imx471.
|
|
+
|
|
config VIDEO_MAX9271_LIB
|
|
tristate
|
|
|
|
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
|
|
index 90b276a7417a..f5c9fa93dc47 100644
|
|
--- a/drivers/media/i2c/Makefile
|
|
+++ b/drivers/media/i2c/Makefile
|
|
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o
|
|
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
|
|
obj-$(CONFIG_VIDEO_IMX412) += imx412.o
|
|
obj-$(CONFIG_VIDEO_IMX415) += imx415.o
|
|
+obj-$(CONFIG_VIDEO_IMX471) += imx471.o
|
|
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
|
|
obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
|
|
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
|
|
diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c
|
|
new file mode 100644
|
|
index 000000000000..6d358b11e96d
|
|
--- /dev/null
|
|
+++ b/drivers/media/i2c/imx471.c
|
|
@@ -0,0 +1,957 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * imx471.c - imx471 sensor driver
|
|
+ *
|
|
+ * Copyright (C) 2025 Intel Corporation
|
|
+ * Copyright (C) 2026 Kate Hsuan <hpa@redhat.com>
|
|
+ */
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <media/v4l2-cci.h>
|
|
+#include <media/v4l2-ctrls.h>
|
|
+#include <media/v4l2-subdev.h>
|
|
+#include <media/v4l2-fwnode.h>
|
|
+
|
|
+#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100)
|
|
+#define IMX471_MODE_STANDBY 0x00
|
|
+#define IMX471_MODE_STREAMING 0x01
|
|
+
|
|
+/* Chip ID */
|
|
+#define IMX471_REG_CHIP_ID CCI_REG16(0x0016)
|
|
+#define IMX471_CHIP_ID 0x0471
|
|
+
|
|
+/* V_TIMING internal */
|
|
+#define IMX471_REG_FLL CCI_REG16(0x0340)
|
|
+#define IMX471_FLL_MAX 0xffff
|
|
+
|
|
+/* Exposure control */
|
|
+#define IMX471_REG_EXPOSURE CCI_REG16(0x0202)
|
|
+#define IMX471_EXPOSURE_MIN 1
|
|
+#define IMX471_EXPOSURE_STEP 1
|
|
+#define IMX471_EXPOSURE_DEFAULT 1270
|
|
+
|
|
+/* Default exposure margin */
|
|
+#define IMX471_EXPOSURE_MARGIN 18
|
|
+
|
|
+/* Analog gain control */
|
|
+#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204)
|
|
+#define IMX471_ANA_GAIN_MIN 0
|
|
+#define IMX471_ANA_GAIN_MAX 800
|
|
+#define IMX471_ANA_GAIN_STEP 1
|
|
+#define IMX471_ANA_GAIN_DEFAULT 0
|
|
+
|
|
+/* Digital gain control */
|
|
+#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9)
|
|
+#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e)
|
|
+#define IMX471_DGTL_GAIN_MIN 256
|
|
+#define IMX471_DGTL_GAIN_MAX 4095
|
|
+#define IMX471_DGTL_GAIN_STEP 1
|
|
+#define IMX471_DGTL_GAIN_DEFAULT 256
|
|
+
|
|
+/* HFLIP and VFLIP control */
|
|
+#define IMX471_REG_ORIENTATION CCI_REG8(0x0101)
|
|
+
|
|
+/* Test Pattern Control */
|
|
+#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600)
|
|
+
|
|
+/* default link frequency and external clock */
|
|
+#define IMX471_LINK_FREQ_DEFAULT 200000000LL
|
|
+#define IMX471_EXT_CLK 19200000
|
|
+
|
|
+/* PLL */
|
|
+#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301)
|
|
+#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303)
|
|
+#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305)
|
|
+#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306)
|
|
+#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309)
|
|
+#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b)
|
|
+#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310)
|
|
+#define IMX471_PLL_SINGLE 0
|
|
+#define IMX471_PLL_DUAL 1
|
|
+
|
|
+/* IMX471 native and active pixel array size */
|
|
+#define IMX471_NATIVE_WIDTH 4672
|
|
+#define IMX471_NATIVE_HEIGHT 3512
|
|
+#define IMX471_PIXEL_ARRAY_LEFT 8
|
|
+#define IMX471_PIXEL_ARRAY_TOP 8
|
|
+#define IMX471_PIXEL_ARRAY_WIDTH 4656
|
|
+#define IMX471_PIXEL_ARRAY_HEIGHT 3496
|
|
+
|
|
+#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136)
|
|
+
|
|
+#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112)
|
|
+#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a
|
|
+
|
|
+#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114)
|
|
+#define IMX471_CSI_2_LANE_MODE 1
|
|
+#define IMX471_CSI_4_LANE_MODE 3
|
|
+
|
|
+#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344)
|
|
+#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346)
|
|
+#define IMX471_REG_X_ADD_END CCI_REG16(0x0348)
|
|
+#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a)
|
|
+#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c)
|
|
+#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e)
|
|
+#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381)
|
|
+#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383)
|
|
+#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385)
|
|
+#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387)
|
|
+
|
|
+#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408)
|
|
+#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a)
|
|
+#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c)
|
|
+#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e)
|
|
+
|
|
+/* Binning mode */
|
|
+#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900)
|
|
+#define IMX471_BINNING_NONE 0
|
|
+#define IMX471_BINNING_ENABLE 1
|
|
+#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901)
|
|
+#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902)
|
|
+
|
|
+#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd)
|
|
+
|
|
+static const char * const imx471_supply_name[] = {
|
|
+ "vana",
|
|
+};
|
|
+
|
|
+struct imx471_mode {
|
|
+ u32 width;
|
|
+ u32 height;
|
|
+
|
|
+ /* V-timing */
|
|
+ u32 fll_def;
|
|
+ u32 fll_min;
|
|
+
|
|
+ /* H-timing */
|
|
+ u32 llp;
|
|
+
|
|
+ const struct cci_reg_sequence *default_mode_regs;
|
|
+ unsigned int default_mode_regs_length;
|
|
+};
|
|
+
|
|
+struct imx471 {
|
|
+ struct v4l2_subdev sd;
|
|
+ struct media_pad pad;
|
|
+
|
|
+ struct v4l2_ctrl_handler ctrl_handler;
|
|
+ struct v4l2_ctrl *vblank;
|
|
+ struct v4l2_ctrl *hblank;
|
|
+ struct v4l2_ctrl *vflip;
|
|
+ struct v4l2_ctrl *hflip;
|
|
+ struct v4l2_ctrl *exposure;
|
|
+
|
|
+ struct gpio_desc *reset_gpio;
|
|
+ struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)];
|
|
+ struct clk *img_clk;
|
|
+
|
|
+ struct device *dev;
|
|
+ struct regmap *regmap;
|
|
+};
|
|
+
|
|
+static const struct cci_reg_sequence imx471_global_regs[] = {
|
|
+ { IMX471_REG_EXCK_FREQ, 0x1333 },
|
|
+ { CCI_REG8(0x3c7e), 0x08 },
|
|
+ { CCI_REG8(0x3c7f), 0x05 },
|
|
+ { CCI_REG8(0x3e35), 0x00 },
|
|
+ { CCI_REG8(0x3e36), 0x00 },
|
|
+ { CCI_REG8(0x3e37), 0x00 },
|
|
+ { CCI_REG8(0x3f7f), 0x01 },
|
|
+ { CCI_REG8(0x4431), 0x04 },
|
|
+ { CCI_REG8(0x531c), 0x01 },
|
|
+ { CCI_REG8(0x531d), 0x02 },
|
|
+ { CCI_REG8(0x531e), 0x04 },
|
|
+ { CCI_REG8(0x5928), 0x00 },
|
|
+ { CCI_REG8(0x5929), 0x2f },
|
|
+ { CCI_REG8(0x592a), 0x00 },
|
|
+ { CCI_REG8(0x592b), 0x85 },
|
|
+ { CCI_REG8(0x592c), 0x00 },
|
|
+ { CCI_REG8(0x592d), 0x32 },
|
|
+ { CCI_REG8(0x592e), 0x00 },
|
|
+ { CCI_REG8(0x592f), 0x88 },
|
|
+ { CCI_REG8(0x5930), 0x00 },
|
|
+ { CCI_REG8(0x5931), 0x3d },
|
|
+ { CCI_REG8(0x5932), 0x00 },
|
|
+ { CCI_REG8(0x5933), 0x93 },
|
|
+ { CCI_REG8(0x5938), 0x00 },
|
|
+ { CCI_REG8(0x5939), 0x24 },
|
|
+ { CCI_REG8(0x593a), 0x00 },
|
|
+ { CCI_REG8(0x593b), 0x7a },
|
|
+ { CCI_REG8(0x593c), 0x00 },
|
|
+ { CCI_REG8(0x593d), 0x24 },
|
|
+ { CCI_REG8(0x593e), 0x00 },
|
|
+ { CCI_REG8(0x593f), 0x7a },
|
|
+ { CCI_REG8(0x5940), 0x00 },
|
|
+ { CCI_REG8(0x5941), 0x2f },
|
|
+ { CCI_REG8(0x5942), 0x00 },
|
|
+ { CCI_REG8(0x5943), 0x85 },
|
|
+ { CCI_REG8(0x5f0e), 0x6e },
|
|
+ { CCI_REG8(0x5f11), 0xc6 },
|
|
+ { CCI_REG8(0x5f17), 0x5e },
|
|
+ { CCI_REG8(0x7990), 0x01 },
|
|
+ { CCI_REG8(0x7993), 0x5d },
|
|
+ { CCI_REG8(0x7994), 0x5d },
|
|
+ { CCI_REG8(0x7995), 0xa1 },
|
|
+ { CCI_REG8(0x799a), 0x01 },
|
|
+ { CCI_REG8(0x799d), 0x00 },
|
|
+ { CCI_REG8(0x8169), 0x01 },
|
|
+ { CCI_REG8(0x8359), 0x01 },
|
|
+ { CCI_REG8(0x9302), 0x1e },
|
|
+ { CCI_REG8(0x9306), 0x1f },
|
|
+ { CCI_REG8(0x930a), 0x26 },
|
|
+ { CCI_REG8(0x930e), 0x23 },
|
|
+ { CCI_REG8(0x9312), 0x23 },
|
|
+ { CCI_REG8(0x9316), 0x2c },
|
|
+ { CCI_REG8(0x9317), 0x19 },
|
|
+ { CCI_REG8(0xb046), 0x01 },
|
|
+ { CCI_REG8(0xb048), 0x01 },
|
|
+};
|
|
+
|
|
+static const struct cci_reg_sequence mode_1928x1088_regs[] = {
|
|
+ { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 },
|
|
+ { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE },
|
|
+ { IMX471_REG_X_ADD_STA, 8 },
|
|
+ { IMX471_REG_Y_ADD_STA, 408 },
|
|
+ { IMX471_REG_X_ADD_END, 4647 },
|
|
+ { IMX471_REG_Y_ADD_END, 3051 },
|
|
+ { IMX471_REG_X_EVEN_INC, 1 },
|
|
+ { IMX471_REG_X_ODD_INC, 1 },
|
|
+ { IMX471_REG_Y_EVEN_INC, 1 },
|
|
+ { IMX471_REG_Y_ODD_INC, 1 },
|
|
+ { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE },
|
|
+ { IMX471_REG_BINNING_TYPE, 0x22 },
|
|
+ { IMX471_REG_BINNING_WEIGHTING, 0x08 },
|
|
+ { IMX471_REG_DIG_CROP_X_OFFSET, 208 },
|
|
+ { IMX471_REG_DIG_CROP_Y_OFFSET, 108 },
|
|
+ { IMX471_REG_DIG_CROP_WIDTH, 1928 },
|
|
+ { IMX471_REG_DIG_CROP_HEIGHT, 1088 },
|
|
+ { IMX471_REG_X_OUTPUT_SIZE, 1928 },
|
|
+ { IMX471_REG_Y_OUTPUT_SIZE, 1088 },
|
|
+ { IMX471_REG_VTPXCK_DIV, 0x06 },
|
|
+ { IMX471_REG_VTSYCK_DIV, 0x02 },
|
|
+ { IMX471_REG_PREPLLCK_VT_DIV, 0x02 },
|
|
+ { IMX471_REG_PLL_VT_MPY, 0x0079 },
|
|
+ { IMX471_REG_OPSYCK_DIV, 0x01 },
|
|
+ { CCI_REG8(0x030d), 0x02 },
|
|
+ { CCI_REG8(0x030e), 0x00 },
|
|
+ { CCI_REG8(0x030f), 0x53 },
|
|
+ { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL },
|
|
+ { CCI_REG8(0x3f4c), 0x81 },
|
|
+ { CCI_REG8(0x3f4d), 0x81 },
|
|
+ { CCI_REG8(0x3f78), 0x01 },
|
|
+ { CCI_REG8(0x3f79), 0x31 },
|
|
+ { CCI_REG8(0x3ffe), 0x00 },
|
|
+ { CCI_REG8(0x3fff), 0x8a },
|
|
+ { CCI_REG8(0x5f0a), 0xb6 },
|
|
+};
|
|
+
|
|
+static const char * const imx471_test_pattern_menu[] = {
|
|
+ "Disabled",
|
|
+ "Solid Colour",
|
|
+ "Eight Vertical Colour Bars",
|
|
+ "Colour Bars With Fade to Grey",
|
|
+ "Pseudorandom Sequence (PN9)",
|
|
+};
|
|
+
|
|
+static const s64 link_freq_menu_items[] = {
|
|
+ IMX471_LINK_FREQ_DEFAULT,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * The Bayer formats for the flipping.
|
|
+ * - no flip
|
|
+ * - h flip
|
|
+ * - v flip
|
|
+ * - h and v flips
|
|
+ */
|
|
+static const u32 imx471_hv_flips_bayer_order[] = {
|
|
+ MEDIA_BUS_FMT_SRGGB10_1X10,
|
|
+ MEDIA_BUS_FMT_SGRBG10_1X10,
|
|
+ MEDIA_BUS_FMT_SGBRG10_1X10,
|
|
+ MEDIA_BUS_FMT_SBGGR10_1X10,
|
|
+};
|
|
+
|
|
+static const struct imx471_mode imx471_modes[] = {
|
|
+ {
|
|
+ .width = 1928,
|
|
+ .height = 1088,
|
|
+ .fll_def = 1308,
|
|
+ .fll_min = 1308,
|
|
+ .llp = 2328,
|
|
+ .default_mode_regs = mode_1928x1088_regs,
|
|
+ .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs),
|
|
+ },
|
|
+};
|
|
+
|
|
+static int imx471_get_regulators(struct device *dev, struct imx471 *sensor)
|
|
+{
|
|
+ for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++)
|
|
+ sensor->supplies[i].supply = imx471_supply_name[i];
|
|
+
|
|
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name),
|
|
+ sensor->supplies);
|
|
+}
|
|
+
|
|
+static int imx471_set_ctrl(struct v4l2_ctrl *ctrl)
|
|
+{
|
|
+ struct imx471 *sensor = container_of_const(ctrl->handler,
|
|
+ struct imx471,
|
|
+ ctrl_handler);
|
|
+ struct v4l2_subdev_state *state =
|
|
+ v4l2_subdev_get_locked_active_state(&sensor->sd);
|
|
+ const struct v4l2_mbus_framefmt *format =
|
|
+ v4l2_subdev_state_get_format(state, 0);
|
|
+ int ret;
|
|
+
|
|
+ if (ctrl->id == V4L2_CID_VBLANK) {
|
|
+ s64 exposure_max = format->height + ctrl->val -
|
|
+ IMX471_EXPOSURE_MARGIN;
|
|
+ ret = __v4l2_ctrl_modify_range(sensor->exposure,
|
|
+ sensor->exposure->minimum,
|
|
+ exposure_max,
|
|
+ sensor->exposure->step,
|
|
+ exposure_max);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (!pm_runtime_get_if_in_use(sensor->dev))
|
|
+ return 0;
|
|
+
|
|
+ switch (ctrl->id) {
|
|
+ case V4L2_CID_ANALOGUE_GAIN:
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN,
|
|
+ ctrl->val, NULL);
|
|
+ break;
|
|
+ case V4L2_CID_DIGITAL_GAIN:
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL,
|
|
+ ctrl->val, NULL);
|
|
+ break;
|
|
+ case V4L2_CID_EXPOSURE:
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE,
|
|
+ ctrl->val, &ret);
|
|
+ break;
|
|
+ case V4L2_CID_VBLANK:
|
|
+ /* Update FLL that meets expected vertical blanking */
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_FLL,
|
|
+ format->height + ctrl->val, &ret);
|
|
+ break;
|
|
+ case V4L2_CID_TEST_PATTERN:
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN,
|
|
+ ctrl->val, NULL);
|
|
+ break;
|
|
+ case V4L2_CID_HFLIP:
|
|
+ case V4L2_CID_VFLIP:
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION,
|
|
+ sensor->hflip->val | sensor->vflip->val << 1,
|
|
+ NULL);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n",
|
|
+ ctrl->id, ctrl->val);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ pm_runtime_put(sensor->dev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct v4l2_ctrl_ops imx471_ctrl_ops = {
|
|
+ .s_ctrl = imx471_set_ctrl,
|
|
+};
|
|
+
|
|
+static u32 imx471_get_format_code(struct imx471 *sensor)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0);
|
|
+
|
|
+ return imx471_hv_flips_bayer_order[i];
|
|
+}
|
|
+
|
|
+static int imx471_enum_mbus_code(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *sd_state,
|
|
+ struct v4l2_subdev_mbus_code_enum *code)
|
|
+{
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+
|
|
+ if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4))
|
|
+ return -EINVAL;
|
|
+
|
|
+ code->code = imx471_get_format_code(sensor);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int imx471_enum_frame_size(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *sd_state,
|
|
+ struct v4l2_subdev_frame_size_enum *fse)
|
|
+{
|
|
+ if (fse->index >= ARRAY_SIZE(imx471_modes))
|
|
+ return -EINVAL;
|
|
+
|
|
+ fse->min_width = imx471_modes[fse->index].width;
|
|
+ fse->max_width = fse->min_width;
|
|
+ fse->min_height = imx471_modes[fse->index].height;
|
|
+ fse->max_height = fse->min_height;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void imx471_update_pad_format(struct imx471 *sensor,
|
|
+ const struct imx471_mode *mode,
|
|
+ struct v4l2_subdev_format *fmt)
|
|
+{
|
|
+ fmt->format.code = imx471_get_format_code(sensor);
|
|
+ fmt->format.width = mode->width;
|
|
+ fmt->format.height = mode->height;
|
|
+ fmt->format.field = V4L2_FIELD_NONE;
|
|
+}
|
|
+
|
|
+static int imx471_set_pad_format(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *sd_state,
|
|
+ struct v4l2_subdev_format *fmt)
|
|
+{
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+ const struct imx471_mode *mode;
|
|
+ int h_blank, ret;
|
|
+
|
|
+ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes),
|
|
+ width, height, fmt->format.width,
|
|
+ fmt->format.height);
|
|
+
|
|
+ imx471_update_pad_format(sensor, mode, fmt);
|
|
+
|
|
+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
|
|
+
|
|
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
|
|
+ return 0;
|
|
+
|
|
+ if (media_entity_is_streaming(&sensor->sd.entity))
|
|
+ return -EBUSY;
|
|
+
|
|
+ ret = __v4l2_ctrl_modify_range(sensor->vblank,
|
|
+ mode->fll_min - mode->height,
|
|
+ IMX471_FLL_MAX - mode->height,
|
|
+ 1,
|
|
+ mode->fll_def - mode->height);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ h_blank = mode->llp - mode->width;
|
|
+ /*
|
|
+ * Currently hblank is not changeable.
|
|
+ * So FPS control is done only by vblank.
|
|
+ */
|
|
+ return __v4l2_ctrl_modify_range(sensor->hblank, h_blank,
|
|
+ h_blank, 1, h_blank);
|
|
+}
|
|
+
|
|
+static int imx471_get_selection(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *sd_state,
|
|
+ struct v4l2_subdev_selection *sel)
|
|
+{
|
|
+ switch (sel->target) {
|
|
+ case V4L2_SEL_TGT_CROP:
|
|
+ sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad);
|
|
+ break;
|
|
+
|
|
+ case V4L2_SEL_TGT_NATIVE_SIZE:
|
|
+ sel->r.top = 0;
|
|
+ sel->r.left = 0;
|
|
+ sel->r.width = IMX471_NATIVE_WIDTH;
|
|
+ sel->r.height = IMX471_NATIVE_HEIGHT;
|
|
+ return 0;
|
|
+
|
|
+ case V4L2_SEL_TGT_CROP_DEFAULT:
|
|
+ case V4L2_SEL_TGT_CROP_BOUNDS:
|
|
+ sel->r.top = IMX471_PIXEL_ARRAY_TOP;
|
|
+ sel->r.left = IMX471_PIXEL_ARRAY_LEFT;
|
|
+ sel->r.width = IMX471_PIXEL_ARRAY_WIDTH;
|
|
+ sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int imx471_init_state(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *sd_state)
|
|
+{
|
|
+ struct v4l2_subdev_format fmt = {
|
|
+ .which = V4L2_SUBDEV_FORMAT_TRY,
|
|
+ .format = {
|
|
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
|
+ .width = imx471_modes[0].width,
|
|
+ .height = imx471_modes[0].height,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ return imx471_set_pad_format(sd, sd_state, &fmt);
|
|
+}
|
|
+
|
|
+static int imx471_identify_module(struct imx471 *sensor)
|
|
+{
|
|
+ int ret;
|
|
+ u64 val;
|
|
+
|
|
+ ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL);
|
|
+ if (ret)
|
|
+ return dev_err_probe(sensor->dev, ret,
|
|
+ "failed to read chip id\n");
|
|
+
|
|
+ if (val != IMX471_CHIP_ID)
|
|
+ return dev_err_probe(sensor->dev, -EIO,
|
|
+ "chip id mismatch: %x!=%llx\n",
|
|
+ IMX471_CHIP_ID, val);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int imx471_power_off(struct device *dev)
|
|
+{
|
|
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+
|
|
+ clk_disable_unprepare(sensor->img_clk);
|
|
+ gpiod_set_value_cansleep(sensor->reset_gpio, 1);
|
|
+
|
|
+ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name),
|
|
+ sensor->supplies);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int imx471_power_on(struct device *dev)
|
|
+{
|
|
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+ int ret;
|
|
+
|
|
+ ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name),
|
|
+ sensor->supplies);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "failed to enable regulators: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(sensor->img_clk);
|
|
+ if (ret < 0) {
|
|
+ regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name),
|
|
+ sensor->supplies);
|
|
+ dev_err(dev, "failed to enable imaging clock: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ gpiod_set_value_cansleep(sensor->reset_gpio, 0);
|
|
+
|
|
+ usleep_range(10000, 15000);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int imx471_enable_stream(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *state,
|
|
+ u32 pad, u64 streams_mask)
|
|
+{
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+ const struct imx471_mode *mode;
|
|
+ struct v4l2_mbus_framefmt *fmt;
|
|
+ int ret;
|
|
+
|
|
+ ret = pm_runtime_resume_and_get(sensor->dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = imx471_identify_module(sensor);
|
|
+ if (ret)
|
|
+ goto error_powerdown;
|
|
+
|
|
+ ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs,
|
|
+ ARRAY_SIZE(imx471_global_regs), NULL);
|
|
+ if (ret) {
|
|
+ dev_err(sensor->dev, "failed to set global settings: %d\n",
|
|
+ ret);
|
|
+ goto error_powerdown;
|
|
+ }
|
|
+
|
|
+ fmt = v4l2_subdev_state_get_format(state, 0);
|
|
+ mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes),
|
|
+ width, height, fmt->width, fmt->height);
|
|
+
|
|
+ ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs,
|
|
+ mode->default_mode_regs_length, NULL);
|
|
+ if (ret) {
|
|
+ dev_err(sensor->dev, "failed to set mode: %d\n", ret);
|
|
+ goto error_powerdown;
|
|
+ }
|
|
+
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1,
|
|
+ NULL);
|
|
+ if (ret)
|
|
+ goto error_powerdown;
|
|
+
|
|
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler);
|
|
+ if (ret)
|
|
+ goto error_powerdown;
|
|
+
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT,
|
|
+ IMX471_MODE_STREAMING, NULL);
|
|
+ if (ret)
|
|
+ goto error_powerdown;
|
|
+
|
|
+ __v4l2_ctrl_grab(sensor->vflip, true);
|
|
+ __v4l2_ctrl_grab(sensor->hflip, true);
|
|
+
|
|
+ return ret;
|
|
+
|
|
+error_powerdown:
|
|
+ pm_runtime_put(sensor->dev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int imx471_disable_stream(struct v4l2_subdev *sd,
|
|
+ struct v4l2_subdev_state *state,
|
|
+ u32 pad, u64 streams_mask)
|
|
+{
|
|
+ struct imx471 *sensor = to_imx471(sd);
|
|
+ int ret;
|
|
+
|
|
+ ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT,
|
|
+ IMX471_MODE_STANDBY, NULL);
|
|
+ pm_runtime_put(sensor->dev);
|
|
+
|
|
+ if (ret)
|
|
+ dev_err(sensor->dev,
|
|
+ "failed to disable stream with return value: %d\n",
|
|
+ ret);
|
|
+
|
|
+ __v4l2_ctrl_grab(sensor->vflip, false);
|
|
+ __v4l2_ctrl_grab(sensor->hflip, false);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct v4l2_subdev_video_ops imx471_video_ops = {
|
|
+ .s_stream = v4l2_subdev_s_stream_helper,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_pad_ops imx471_pad_ops = {
|
|
+ .enum_mbus_code = imx471_enum_mbus_code,
|
|
+ .get_fmt = v4l2_subdev_get_fmt,
|
|
+ .set_fmt = imx471_set_pad_format,
|
|
+ .get_selection = imx471_get_selection,
|
|
+ .enum_frame_size = imx471_enum_frame_size,
|
|
+ .enable_streams = imx471_enable_stream,
|
|
+ .disable_streams = imx471_disable_stream,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_ops imx471_subdev_ops = {
|
|
+ .video = &imx471_video_ops,
|
|
+ .pad = &imx471_pad_ops,
|
|
+};
|
|
+
|
|
+static const struct v4l2_subdev_internal_ops imx471_internal_ops = {
|
|
+ .init_state = imx471_init_state,
|
|
+};
|
|
+
|
|
+static int imx471_init_controls(struct imx471 *sensor)
|
|
+{
|
|
+ const struct imx471_mode *mode = &imx471_modes[0];
|
|
+ struct v4l2_fwnode_device_properties props;
|
|
+ struct v4l2_ctrl_handler *ctrl_hdlr;
|
|
+ struct v4l2_ctrl *link_freq;
|
|
+ s64 exposure_max, hblank;
|
|
+ u64 pixel_rate;
|
|
+ int ret;
|
|
+
|
|
+ ret = v4l2_fwnode_device_parse(sensor->dev, &props);
|
|
+ if (ret) {
|
|
+ dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ctrl_hdlr = &sensor->ctrl_handler;
|
|
+ v4l2_ctrl_handler_init(ctrl_hdlr, 12);
|
|
+
|
|
+ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props);
|
|
+
|
|
+ link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
|
|
+ &imx471_ctrl_ops,
|
|
+ V4L2_CID_LINK_FREQ,
|
|
+ ARRAY_SIZE(link_freq_menu_items) - 1,
|
|
+ 0,
|
|
+ link_freq_menu_items);
|
|
+
|
|
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
|
|
+ pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10);
|
|
+
|
|
+ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_PIXEL_RATE, pixel_rate,
|
|
+ pixel_rate, 1, pixel_rate);
|
|
+
|
|
+ sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr,
|
|
+ &imx471_ctrl_ops,
|
|
+ V4L2_CID_VBLANK,
|
|
+ mode->fll_min - mode->height,
|
|
+ IMX471_FLL_MAX - mode->height,
|
|
+ 1,
|
|
+ mode->fll_def - mode->height);
|
|
+
|
|
+ hblank = mode->llp - mode->width;
|
|
+ sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_HBLANK, hblank, hblank,
|
|
+ 1, hblank);
|
|
+
|
|
+ /* fll >= exposure time + adjust parameter (default value is 18) */
|
|
+ exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN;
|
|
+ sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_EXPOSURE,
|
|
+ IMX471_EXPOSURE_MIN, exposure_max,
|
|
+ IMX471_EXPOSURE_STEP,
|
|
+ IMX471_EXPOSURE_DEFAULT);
|
|
+
|
|
+ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
|
|
+ IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX,
|
|
+ IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT);
|
|
+
|
|
+ v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
|
|
+ IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX,
|
|
+ IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT);
|
|
+
|
|
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_TEST_PATTERN,
|
|
+ ARRAY_SIZE(imx471_test_pattern_menu) - 1,
|
|
+ 0, 0, imx471_test_pattern_menu);
|
|
+
|
|
+ sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
+
|
|
+ sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops,
|
|
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
+
|
|
+ if (ctrl_hdlr->error) {
|
|
+ dev_err(sensor->dev, "%s control init failed: %d\n",
|
|
+ __func__, ctrl_hdlr->error);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
|
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
|
+ sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
|
|
+ sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
|
|
+
|
|
+ sensor->sd.ctrl_handler = ctrl_hdlr;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ v4l2_ctrl_handler_free(ctrl_hdlr);
|
|
+
|
|
+ return ctrl_hdlr->error;
|
|
+}
|
|
+
|
|
+static int imx471_check_hwcfg(struct imx471 *sensor)
|
|
+{
|
|
+ struct v4l2_fwnode_endpoint bus_cfg = {
|
|
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
|
|
+ };
|
|
+ struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev);
|
|
+ unsigned long link_freq_bitmap;
|
|
+ struct clk *clk;
|
|
+ int ret;
|
|
+
|
|
+ clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL);
|
|
+ if (IS_ERR(clk))
|
|
+ return dev_err_probe(sensor->dev, PTR_ERR(clk),
|
|
+ "can't get clock frequency\n");
|
|
+
|
|
+ if (clk_get_rate(clk) != IMX471_EXT_CLK)
|
|
+ return dev_err_probe(sensor->dev, -EINVAL,
|
|
+ "external clock %lu is not supported\n",
|
|
+ clk_get_rate(clk));
|
|
+
|
|
+ ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0);
|
|
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
|
|
+ fwnode_handle_put(ep);
|
|
+ if (ret)
|
|
+ return dev_err_probe(sensor->dev, ret,
|
|
+ "parsing endpoint failed\n");
|
|
+
|
|
+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
|
|
+ ret = dev_err_probe(sensor->dev, -EINVAL,
|
|
+ "number of CSI2 data lanes %u is not supported\n",
|
|
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
|
|
+ goto done_endpoint_free;
|
|
+ }
|
|
+
|
|
+ ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies,
|
|
+ bus_cfg.nr_of_link_frequencies,
|
|
+ link_freq_menu_items,
|
|
+ ARRAY_SIZE(link_freq_menu_items),
|
|
+ &link_freq_bitmap);
|
|
+
|
|
+done_endpoint_free:
|
|
+ v4l2_fwnode_endpoint_free(&bus_cfg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int imx471_probe(struct i2c_client *client)
|
|
+{
|
|
+ struct imx471 *sensor;
|
|
+ int ret;
|
|
+
|
|
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
|
|
+ if (!sensor)
|
|
+ return dev_err_probe(&client->dev, -ENOMEM,
|
|
+ "failed to allocate memory\n");
|
|
+
|
|
+ sensor->dev = &client->dev;
|
|
+
|
|
+ ret = imx471_check_hwcfg(sensor);
|
|
+ if (ret)
|
|
+ return dev_err_probe(sensor->dev, ret,
|
|
+ "failed to check hwcfg: %d\n", ret);
|
|
+
|
|
+ ret = imx471_get_regulators(sensor->dev, sensor);
|
|
+ if (ret)
|
|
+ return dev_err_probe(sensor->dev, ret,
|
|
+ "failed to get regulators\n");
|
|
+
|
|
+ sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset",
|
|
+ GPIOD_OUT_HIGH);
|
|
+ if (IS_ERR(sensor->reset_gpio))
|
|
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio),
|
|
+ "failed to get reset gpio\n");
|
|
+
|
|
+ sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL);
|
|
+ if (IS_ERR(sensor->img_clk))
|
|
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk),
|
|
+ "failed to get imaging clock\n");
|
|
+
|
|
+ v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops);
|
|
+
|
|
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
|
|
+ if (IS_ERR(sensor->regmap))
|
|
+ return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap),
|
|
+ "failed to initialize CCI\n");
|
|
+
|
|
+ ret = imx471_power_on(sensor->dev);
|
|
+ if (ret)
|
|
+ return dev_err_probe(sensor->dev, ret,
|
|
+ "failed to power on\n");
|
|
+
|
|
+ ret = imx471_identify_module(sensor);
|
|
+ if (ret) {
|
|
+ dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n",
|
|
+ ret);
|
|
+ goto error_power_off;
|
|
+ }
|
|
+
|
|
+ ret = imx471_init_controls(sensor);
|
|
+ if (ret) {
|
|
+ dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n",
|
|
+ ret);
|
|
+ goto error_power_off;
|
|
+ }
|
|
+
|
|
+ sensor->sd.internal_ops = &imx471_internal_ops;
|
|
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
|
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
|
+
|
|
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
|
|
+ if (ret) {
|
|
+ dev_err_probe(sensor->dev, ret,
|
|
+ "failed to init entity pads: %d\n", ret);
|
|
+ goto error_v4l2_ctrl_handler_free;
|
|
+ }
|
|
+
|
|
+ sensor->sd.state_lock = sensor->ctrl_handler.lock;
|
|
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
|
|
+ if (ret < 0) {
|
|
+ dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n",
|
|
+ ret);
|
|
+ goto error_media_entity_pm;
|
|
+ }
|
|
+
|
|
+ pm_runtime_set_active(sensor->dev);
|
|
+ pm_runtime_enable(sensor->dev);
|
|
+
|
|
+ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
|
|
+ if (ret < 0)
|
|
+ goto error_v4l2_subdev_cleanup;
|
|
+
|
|
+ pm_runtime_idle(sensor->dev);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error_v4l2_subdev_cleanup:
|
|
+ pm_runtime_disable(sensor->dev);
|
|
+ pm_runtime_set_suspended(sensor->dev);
|
|
+ v4l2_subdev_cleanup(&sensor->sd);
|
|
+
|
|
+error_media_entity_pm:
|
|
+ media_entity_cleanup(&sensor->sd.entity);
|
|
+
|
|
+error_v4l2_ctrl_handler_free:
|
|
+ v4l2_ctrl_handler_free(sensor->sd.ctrl_handler);
|
|
+
|
|
+error_power_off:
|
|
+ imx471_power_off(sensor->dev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void imx471_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
|
|
+
|
|
+ v4l2_async_unregister_subdev(sd);
|
|
+ v4l2_subdev_cleanup(sd);
|
|
+ media_entity_cleanup(&sd->entity);
|
|
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
|
|
+
|
|
+ pm_runtime_disable(&client->dev);
|
|
+
|
|
+ if (!pm_runtime_status_suspended(&client->dev)) {
|
|
+ imx471_power_off(&client->dev);
|
|
+ pm_runtime_set_suspended(&client->dev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off,
|
|
+ imx471_power_on, NULL);
|
|
+
|
|
+static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = {
|
|
+ { "SONY471A" },
|
|
+ { "TBE20A0" },
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids);
|
|
+
|
|
+static struct i2c_driver imx471_i2c_driver = {
|
|
+ .driver = {
|
|
+ .name = "imx471",
|
|
+ .acpi_match_table = ACPI_PTR(imx471_acpi_ids),
|
|
+ .pm = pm_sleep_ptr(&imx471_pm_ops),
|
|
+ },
|
|
+ .probe = imx471_probe,
|
|
+ .remove = imx471_remove,
|
|
+};
|
|
+module_i2c_driver(imx471_i2c_driver);
|
|
+
|
|
+MODULE_AUTHOR("Jimmy Su <jimmy.su@intel.com>");
|
|
+MODULE_AUTHOR("Serin Yeh <serin.yeh@intel.com>");
|
|
+MODULE_AUTHOR("Kate Hsuan <hpa@redhat.com>");
|
|
+MODULE_DESCRIPTION("Sony imx471 sensor driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
|
|
index fc6608e33de4..8cfa3282bf08 100644
|
|
--- a/drivers/media/pci/intel/ipu-bridge.c
|
|
+++ b/drivers/media/pci/intel/ipu-bridge.c
|
|
@@ -97,6 +97,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
|
|
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
|
|
/* Sony IMX471 */
|
|
IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
|
|
+ /* Sony IMX471 (found on Lenovo X1 Carbon G14) */
|
|
+ IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000),
|
|
/* Toshiba T4KA3 */
|
|
IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000),
|
|
};
|
|
@@ -134,6 +136,45 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
|
|
},
|
|
.driver_data = "OVTI02C1",
|
|
},
|
|
+ /*
|
|
+ * The first four characters of DMI_BOARD_NAME identify the Lenovo
|
|
+ * machine type/model. For example, a DMI_BOARD_NAME starting with
|
|
+ * "21Q6" indicates a ThinkPad X9-15.
|
|
+ *
|
|
+ * Reference: https://psref.lenovo.com/
|
|
+ */
|
|
+ {
|
|
+ /* Lenovo X9-14 */
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "21QA"),
|
|
+ },
|
|
+ .driver_data = "SONY471A",
|
|
+ },
|
|
+ {
|
|
+ /* Lenovo X9-14 */
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "21QB"),
|
|
+ },
|
|
+ .driver_data = "SONY471A",
|
|
+ },
|
|
+ {
|
|
+ /* Lenovo X9-15 */
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "21Q6"),
|
|
+ },
|
|
+ .driver_data = "SONY471A",
|
|
+ },
|
|
+ {
|
|
+ /* Lenovo X9-15 */
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
+ DMI_MATCH(DMI_BOARD_NAME, "21Q7"),
|
|
+ },
|
|
+ .driver_data = "SONY471A",
|
|
+ },
|
|
{} /* Terminating entry */
|
|
};
|
|
|
|
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
|
|
index dd0025d3914e..e57d10dc843c 100644
|
|
--- a/drivers/pci/quirks.c
|
|
+++ b/drivers/pci/quirks.c
|
|
@@ -4491,6 +4491,30 @@ static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev)
|
|
}
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASPEED, 0x1150, quirk_aspeed_pci_bridge_no_alias);
|
|
|
|
+/*
|
|
+ * PCI BAR 5 is not setup correctly for the on-board AHCI controller
|
|
+ * on Broadcom's Vulcan processor. Added a quirk to fix BAR 5 by
|
|
+ * using BAR 4's resources which are populated correctly and NOT
|
|
+ * actually used by the AHCI controller.
|
|
+ */
|
|
+static void quirk_fix_vulcan_ahci_bars(struct pci_dev *dev)
|
|
+{
|
|
+ struct resource *r = &dev->resource[4];
|
|
+
|
|
+ if (!(r->flags & IORESOURCE_MEM) || (r->start == 0))
|
|
+ return;
|
|
+
|
|
+ /* Set BAR5 resource to BAR4 */
|
|
+ dev->resource[5] = *r;
|
|
+
|
|
+ /* Update BAR5 in pci config space */
|
|
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, r->start);
|
|
+
|
|
+ /* Clear BAR4's resource */
|
|
+ memset(r, 0, sizeof(*r));
|
|
+}
|
|
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9027, quirk_fix_vulcan_ahci_bars);
|
|
+
|
|
/*
|
|
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
|
|
* class code. Fix it.
|
|
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
|
|
index 115bb37577a1..adff564bf3fd 100644
|
|
--- a/drivers/platform/x86/intel/int3472/discrete.c
|
|
+++ b/drivers/platform/x86/intel/int3472/discrete.c
|
|
@@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
|
|
.con_id = "dvdd",
|
|
.enable_time_us = 45 * USEC_PER_MSEC,
|
|
},
|
|
+ { /* imx471 expects "vana" as con_id for power enable */
|
|
+ .hid = "SONY471A",
|
|
+ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
|
|
+ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
|
|
+ .con_id = "vana",
|
|
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
|
|
+ },
|
|
+ {
|
|
+ /*
|
|
+ * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id
|
|
+ * for power enable
|
|
+ */
|
|
+ .hid = "TBE20A0",
|
|
+ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
|
|
+ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
|
|
+ .con_id = "vana",
|
|
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
|
|
+ },
|
|
};
|
|
|
|
static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
|
|
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
|
index 24960ba9caa9..32597cdb72ef 100644
|
|
--- a/drivers/usb/core/hub.c
|
|
+++ b/drivers/usb/core/hub.c
|
|
@@ -5894,6 +5894,13 @@ static void hub_event(struct work_struct *work)
|
|
(u16) hub->change_bits[0],
|
|
(u16) hub->event_bits[0]);
|
|
|
|
+ /* Don't disconnect USB-SATA on TrimSlice */
|
|
+ if (strcmp(dev_name(hdev->bus->controller), "tegra-ehci.0") == 0) {
|
|
+ if ((hdev->state == 7) && (hub->change_bits[0] == 0) &&
|
|
+ (hub->event_bits[0] == 0x2))
|
|
+ hub->event_bits[0] = 0;
|
|
+ }
|
|
+
|
|
/* Lock the device, then check to see if we were
|
|
* disconnected while waiting for the lock to succeed. */
|
|
usb_lock_device(hdev);
|
|
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
|
|
index a2137e19be7d..79ccb6fabc10 100644
|
|
--- a/include/linux/crypto.h
|
|
+++ b/include/linux/crypto.h
|
|
@@ -152,6 +152,8 @@
|
|
#define CRYPTO_TFM_REQ_MAY_BACKLOG 0x00000400
|
|
#define CRYPTO_TFM_REQ_ON_STACK 0x00000800
|
|
|
|
+#define CRYPTO_TFM_FIPS_COMPLIANCE 0x80000000
|
|
+
|
|
/*
|
|
* Miscellaneous stuff.
|
|
*/
|
|
diff --git a/include/linux/efi.h b/include/linux/efi.h
|
|
index ccbc35479684..8eb9a1276ba7 100644
|
|
--- a/include/linux/efi.h
|
|
+++ b/include/linux/efi.h
|
|
@@ -45,6 +45,8 @@ struct screen_info;
|
|
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
|
|
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
|
|
|
|
+#define EFI_IS_ERROR(x) ((x) & (1UL << (BITS_PER_LONG-1)))
|
|
+
|
|
typedef unsigned long efi_status_t;
|
|
typedef u8 efi_bool_t;
|
|
typedef u16 efi_char16_t; /* UNICODE character */
|
|
@@ -847,6 +849,14 @@ extern int __init parse_efi_signature_list(
|
|
#define EFI_MEM_ATTR 9 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
|
|
#define EFI_MEM_NO_SOFT_RESERVE 10 /* Is the kernel configured to ignore soft reservations? */
|
|
#define EFI_PRESERVE_BS_REGIONS 11 /* Are EFI boot-services memory segments available? */
|
|
+#define EFI_SECURE_BOOT 12 /* Are we in Secure Boot mode? */
|
|
+
|
|
+enum efi_secureboot_mode {
|
|
+ efi_secureboot_mode_unset,
|
|
+ efi_secureboot_mode_unknown,
|
|
+ efi_secureboot_mode_disabled,
|
|
+ efi_secureboot_mode_enabled,
|
|
+};
|
|
|
|
#ifdef CONFIG_EFI
|
|
/*
|
|
@@ -858,6 +868,8 @@ static inline bool efi_enabled(int feature)
|
|
}
|
|
extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused);
|
|
|
|
+extern void __init efi_set_secure_boot(enum efi_secureboot_mode mode);
|
|
+
|
|
bool __pure __efi_soft_reserve_enabled(void);
|
|
|
|
static inline bool __pure efi_soft_reserve_enabled(void)
|
|
@@ -879,6 +891,8 @@ static inline bool efi_enabled(int feature)
|
|
static inline void
|
|
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
|
|
|
|
+static inline void efi_set_secure_boot(enum efi_secureboot_mode mode) {}
|
|
+
|
|
static inline bool efi_soft_reserve_enabled(void)
|
|
{
|
|
return false;
|
|
@@ -893,6 +907,7 @@ static inline void efi_find_mirror(void) {}
|
|
#endif
|
|
|
|
extern int efi_status_to_err(efi_status_t status);
|
|
+extern const char *efi_status_to_str(efi_status_t status);
|
|
|
|
/*
|
|
* Variable Attributes
|
|
@@ -1110,13 +1125,6 @@ extern unsigned long efi_call_virt_save_flags(void);
|
|
|
|
void efi_runtime_assert_lock_held(void);
|
|
|
|
-enum efi_secureboot_mode {
|
|
- efi_secureboot_mode_unset,
|
|
- efi_secureboot_mode_unknown,
|
|
- efi_secureboot_mode_disabled,
|
|
- efi_secureboot_mode_enabled,
|
|
-};
|
|
-
|
|
static inline
|
|
enum efi_secureboot_mode efi_get_secureboot_mode(efi_get_variable_t *get_var)
|
|
{
|
|
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
|
|
index 2b8dfb35caed..47274c0e7900 100644
|
|
--- a/include/linux/lsm_hook_defs.h
|
|
+++ b/include/linux/lsm_hook_defs.h
|
|
@@ -456,6 +456,7 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap)
|
|
|
|
LSM_HOOK(int, 0, locked_down, enum lockdown_reason what)
|
|
|
|
+
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
LSM_HOOK(int, 0, perf_event_open, int type)
|
|
LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event)
|
|
diff --git a/include/linux/random.h b/include/linux/random.h
|
|
index 8a8064dc3970..3238fab8f749 100644
|
|
--- a/include/linux/random.h
|
|
+++ b/include/linux/random.h
|
|
@@ -9,6 +9,13 @@
|
|
|
|
#include <uapi/linux/random.h>
|
|
|
|
+struct iov_iter;
|
|
+
|
|
+struct random_extrng {
|
|
+ ssize_t (*extrng_read_iter)(struct iov_iter *iter, bool reseed);
|
|
+ struct module *owner;
|
|
+};
|
|
+
|
|
struct notifier_block;
|
|
|
|
void add_device_randomness(const void *buf, size_t len);
|
|
@@ -135,6 +142,9 @@ int random_prepare_cpu(unsigned int cpu);
|
|
int random_online_cpu(unsigned int cpu);
|
|
#endif
|
|
|
|
+void random_register_extrng(const struct random_extrng *rng);
|
|
+void random_unregister_extrng(void);
|
|
+
|
|
#ifndef MODULE
|
|
extern const struct file_operations random_fops, urandom_fops;
|
|
#endif
|
|
diff --git a/include/linux/security.h b/include/linux/security.h
|
|
index 41d7367cf403..ad6f69c6fcff 100644
|
|
--- a/include/linux/security.h
|
|
+++ b/include/linux/security.h
|
|
@@ -2438,4 +2438,13 @@ static inline void security_initramfs_populated(void)
|
|
}
|
|
#endif /* CONFIG_SECURITY */
|
|
|
|
+#ifdef CONFIG_SECURITY_LOCKDOWN_LSM
|
|
+extern int security_lock_kernel_down(const char *where, enum lockdown_reason level);
|
|
+#else
|
|
+static inline int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_SECURITY_LOCKDOWN_LSM */
|
|
+
|
|
#endif /* ! __LINUX_SECURITY_H */
|
|
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
|
|
index 590ba29c85ab..02153d857531 100644
|
|
--- a/kernel/module/signing.c
|
|
+++ b/kernel/module/signing.c
|
|
@@ -61,10 +61,17 @@ int mod_verify_sig(const void *mod, struct load_info *info)
|
|
modlen -= sig_len + sizeof(ms);
|
|
info->len = modlen;
|
|
|
|
- return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
VERIFY_USE_SECONDARY_KEYRING,
|
|
VERIFYING_MODULE_SIGNATURE,
|
|
NULL, NULL);
|
|
+ if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) {
|
|
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
+ VERIFY_USE_PLATFORM_KEYRING,
|
|
+ VERIFYING_MODULE_SIGNATURE,
|
|
+ NULL, NULL);
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
|
|
int module_sig_check(struct load_info *info, int flags)
|
|
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
|
|
index 8e6f3a734ba0..e75d2932475a 100644
|
|
--- a/net/packet/af_packet.c
|
|
+++ b/net/packet/af_packet.c
|
|
@@ -4561,7 +4561,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
|
|
|
|
spin_lock(&po->bind_lock);
|
|
WRITE_ONCE(po->num, num);
|
|
- if (was_running)
|
|
+ /*
|
|
+ * NETDEV_UNREGISTER may have invalidated the binding while bind_lock
|
|
+ * was dropped above. Do not re-add a fanout hook to a dead device.
|
|
+ */
|
|
+ if (was_running && READ_ONCE(po->ifindex) != -1)
|
|
register_prot_hook(sk);
|
|
|
|
spin_unlock(&po->bind_lock);
|
|
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
|
|
index 0718e39cedda..10e94a1b8b6e 100644
|
|
--- a/scripts/Makefile.lib
|
|
+++ b/scripts/Makefile.lib
|
|
@@ -201,7 +201,10 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
|
|
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
|
|
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
|
|
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
|
+# RHEL-only: don't enforce OBJTOOL_WERROR for out of tree modules
|
|
+ifeq ($(KBUILD_EXTMOD),)
|
|
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
|
|
+endif
|
|
|
|
objtool-args = $(objtool-args-y) \
|
|
$(if $(delay-objtool), --link) \
|
|
diff --git a/scripts/tags.sh b/scripts/tags.sh
|
|
index 243373683f98..2affd5e58ee8 100755
|
|
--- a/scripts/tags.sh
|
|
+++ b/scripts/tags.sh
|
|
@@ -16,6 +16,8 @@ fi
|
|
ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )"
|
|
# tags and cscope files should also ignore MODVERSION *.mod.c files
|
|
ignore="$ignore ( -name *.mod.c ) -prune -o"
|
|
+# RHEL tags and cscope should also ignore redhat/rpm
|
|
+ignore="$ignore ( -path redhat/rpm ) -prune -o"
|
|
|
|
# ignore arbitrary directories
|
|
if [ -n "${IGNORE_DIRS}" ]; then
|
|
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
|
|
index c0d6948446c3..bed56532e206 100644
|
|
--- a/security/integrity/platform_certs/load_uefi.c
|
|
+++ b/security/integrity/platform_certs/load_uefi.c
|
|
@@ -74,7 +74,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
|
return NULL;
|
|
|
|
if (*status != EFI_BUFFER_TOO_SMALL) {
|
|
- pr_err("Couldn't get size: 0x%lx\n", *status);
|
|
+ pr_err("Couldn't get size: %s (0x%lx)\n",
|
|
+ efi_status_to_str(*status), *status);
|
|
return NULL;
|
|
}
|
|
|
|
@@ -85,7 +86,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
|
*status = efi.get_variable(name, guid, NULL, &lsize, db);
|
|
if (*status != EFI_SUCCESS) {
|
|
kfree(db);
|
|
- pr_err("Error reading db var: 0x%lx\n", *status);
|
|
+ pr_err("Error reading db var: %s (0x%lx)\n",
|
|
+ efi_status_to_str(*status), *status);
|
|
return NULL;
|
|
}
|
|
|
|
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
|
|
index e84ddf484010..d0501353a4b9 100644
|
|
--- a/security/lockdown/Kconfig
|
|
+++ b/security/lockdown/Kconfig
|
|
@@ -16,6 +16,19 @@ config SECURITY_LOCKDOWN_LSM_EARLY
|
|
subsystem is fully initialised. If enabled, lockdown will
|
|
unconditionally be called before any other LSMs.
|
|
|
|
+config LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ bool "Lock down the kernel in EFI Secure Boot mode"
|
|
+ default n
|
|
+ depends on EFI && SECURITY_LOCKDOWN_LSM_EARLY
|
|
+ help
|
|
+ UEFI Secure Boot provides a mechanism for ensuring that the firmware
|
|
+ will only load signed bootloaders and kernels. Secure boot mode may
|
|
+ be determined from EFI variables provided by the system firmware if
|
|
+ not indicated by the boot parameters.
|
|
+
|
|
+ Enabling this option results in kernel lockdown being triggered if
|
|
+ EFI Secure Boot is set.
|
|
+
|
|
choice
|
|
prompt "Kernel default lockdown mode"
|
|
default LOCK_DOWN_KERNEL_FORCE_NONE
|
|
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
|
|
index 8d46886d2cca..14a9cdff942b 100644
|
|
--- a/security/lockdown/lockdown.c
|
|
+++ b/security/lockdown/lockdown.c
|
|
@@ -72,6 +72,17 @@ static int lockdown_is_locked_down(enum lockdown_reason what)
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * security_lock_kernel_down() - Put the kernel into lock-down mode.
|
|
+ *
|
|
+ * @where: Where the lock-down is originating from (e.g. command line option)
|
|
+ * @level: The lock-down level (can only increase)
|
|
+ */
|
|
+int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
|
+{
|
|
+ return lock_kernel_down(where, level);
|
|
+}
|
|
+
|
|
static struct security_hook_list lockdown_hooks[] __ro_after_init = {
|
|
LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
|
|
};
|
|
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
|
|
index 79b8b7e70a33..23d2f63d68ef 100644
|
|
--- a/sound/soc/codecs/rt722-sdca.c
|
|
+++ b/sound/soc/codecs/rt722-sdca.c
|
|
@@ -517,6 +517,61 @@ static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
|
|
return changed;
|
|
}
|
|
|
|
+static int rt722_sdca_set_fu06_playback_ctl(struct rt722_sdca_priv *rt722)
|
|
+{
|
|
+ int err;
|
|
+ unsigned int ch_l, ch_r;
|
|
+
|
|
+ ch_l = (rt722->fu06_dapm_mute || rt722->fu06_mixer_l_mute) ? 0x01 : 0x00;
|
|
+ ch_r = (rt722->fu06_dapm_mute || rt722->fu06_mixer_r_mute) ? 0x01 : 0x00;
|
|
+
|
|
+ err = regmap_write(rt722->regmap,
|
|
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
+ RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ err = regmap_write(rt722->regmap,
|
|
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
+ RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rt722_sdca_fu06_playback_get(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
|
|
+
|
|
+ ucontrol->value.integer.value[0] = !rt722->fu06_mixer_l_mute;
|
|
+ ucontrol->value.integer.value[1] = !rt722->fu06_mixer_r_mute;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int rt722_sdca_fu06_playback_put(struct snd_kcontrol *kcontrol,
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
+{
|
|
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
|
|
+ int err, changed = 0;
|
|
+
|
|
+ if (rt722->fu06_mixer_l_mute != !ucontrol->value.integer.value[0] ||
|
|
+ rt722->fu06_mixer_r_mute != !ucontrol->value.integer.value[1])
|
|
+ changed = 1;
|
|
+
|
|
+ rt722->fu06_mixer_l_mute = !ucontrol->value.integer.value[0];
|
|
+ rt722->fu06_mixer_r_mute = !ucontrol->value.integer.value[1];
|
|
+
|
|
+ err = rt722_sdca_set_fu06_playback_ctl(rt722);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ return changed;
|
|
+}
|
|
+
|
|
static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722)
|
|
{
|
|
int err;
|
|
@@ -718,6 +773,8 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = {
|
|
RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0,
|
|
rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv),
|
|
/* AMP playback settings */
|
|
+ SOC_DOUBLE_EXT("FU06 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
|
|
+ rt722_sdca_fu06_playback_get, rt722_sdca_fu06_playback_put),
|
|
SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
|
|
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
RT722_SDCA_CTL_FU_VOLUME, CH_L),
|
|
@@ -807,27 +864,17 @@ static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_soc_component *component =
|
|
snd_soc_dapm_to_component(w->dapm);
|
|
struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
|
|
- unsigned char unmute = 0x0, mute = 0x1;
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_POST_PMU:
|
|
- regmap_write(rt722->regmap,
|
|
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
- RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
|
|
- regmap_write(rt722->regmap,
|
|
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
- RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
|
|
+ rt722->fu06_dapm_mute = false;
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD:
|
|
- regmap_write(rt722->regmap,
|
|
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
- RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
|
|
- regmap_write(rt722->regmap,
|
|
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
|
|
- RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
|
|
+ rt722->fu06_dapm_mute = true;
|
|
break;
|
|
}
|
|
- return 0;
|
|
+
|
|
+ return rt722_sdca_set_fu06_playback_ctl(rt722);
|
|
}
|
|
|
|
static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w,
|
|
@@ -1324,6 +1371,8 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave
|
|
rt722->first_hw_init = false;
|
|
rt722->fu1e_dapm_mute = true;
|
|
rt722->fu0f_dapm_mute = true;
|
|
+ rt722->fu06_dapm_mute = true;
|
|
+ rt722->fu06_mixer_l_mute = rt722->fu06_mixer_r_mute = false;
|
|
rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true;
|
|
rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] =
|
|
rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true;
|
|
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
|
|
index 823abee9ab76..fc50beff9424 100644
|
|
--- a/sound/soc/codecs/rt722-sdca.h
|
|
+++ b/sound/soc/codecs/rt722-sdca.h
|
|
@@ -36,6 +36,10 @@ struct rt722_sdca_priv {
|
|
bool fu0f_dapm_mute;
|
|
bool fu0f_mixer_l_mute;
|
|
bool fu0f_mixer_r_mute;
|
|
+ /* For AMP */
|
|
+ bool fu06_dapm_mute;
|
|
+ bool fu06_mixer_l_mute;
|
|
+ bool fu06_mixer_r_mute;
|
|
/* For DMIC */
|
|
bool fu1e_dapm_mute;
|
|
bool fu1e_mixer_mute[4];
|
|
diff --git a/tools/testing/selftests/bpf/DENYLIST.rhel b/tools/testing/selftests/bpf/DENYLIST.rhel
|
|
new file mode 100644
|
|
index 000000000000..9424a83c6f48
|
|
--- /dev/null
|
|
+++ b/tools/testing/selftests/bpf/DENYLIST.rhel
|
|
@@ -0,0 +1,76 @@
|
|
+# Tests that need CONFIG_FUNCTION_ERROR_INJECTION
|
|
+# Typical observed errors are:
|
|
+# <...some function...> is not sleepable
|
|
+# <...some function...> is not modifiable
|
|
+cgrp_local_storage/yes_rcu_lock
|
|
+cgrp_local_storage/no_rcu_lock
|
|
+cgrp_local_storage/cgrp1_yes_rcu_lock
|
|
+cgrp_local_storage/cgrp1_no_rcu_lock
|
|
+compute_live_registers/addr_space_cast
|
|
+dynptr/test_read_write
|
|
+dynptr/test_dynptr_data
|
|
+dynptr/test_dynptr_copy
|
|
+dynptr/test_dynptr_copy_xdp
|
|
+dynptr/test_dynptr_memset_zero
|
|
+dynptr/test_dynptr_memset_notzero
|
|
+dynptr/test_dynptr_memset_zero_offset
|
|
+dynptr/test_dynptr_memset_zero_adjusted
|
|
+dynptr/test_dynptr_memset_overflow
|
|
+dynptr/test_dynptr_memset_overflow_offset
|
|
+dynptr/test_dynptr_memset_readonly
|
|
+dynptr/test_dynptr_memset_xdp_chunks
|
|
+dynptr/test_dynptr_skb_meta_data
|
|
+dynptr/test_dynptr_skb_meta_flags
|
|
+dynptr/test_ringbuf
|
|
+dynptr/test_skb_readonly
|
|
+dynptr/test_dynptr_skb_data
|
|
+dynptr/test_adjust
|
|
+dynptr/test_adjust_err
|
|
+dynptr/test_zero_size_dynptr
|
|
+dynptr/test_dynptr_is_null
|
|
+dynptr/test_dynptr_is_rdonly
|
|
+dynptr/test_dynptr_clone
|
|
+dynptr/test_dynptr_skb_no_buff
|
|
+dynptr/test_dynptr_skb_strcmp
|
|
+dynptr/test_dynptr_skb_tp_btf
|
|
+dynptr/test_probe_read_user_dynptr
|
|
+dynptr/test_probe_read_kernel_dynptr
|
|
+dynptr/test_probe_read_user_str_dynptr
|
|
+dynptr/test_probe_read_kernel_str_dynptr
|
|
+dynptr/test_copy_from_user_dynptr
|
|
+dynptr/test_copy_from_user_str_dynptr
|
|
+dynptr/test_copy_from_user_task_dynptr
|
|
+dynptr/test_copy_from_user_task_str_dynptr
|
|
+irq/irq_sleepable_helper
|
|
+irq/irq_sleepable_kfunc
|
|
+iters/iter_css_task_for_each_sleep
|
|
+iters/iter_ret_rcu_test_protected
|
|
+iters/iter_ret_rcu_test_type
|
|
+iters/iter_ret_rcu_test_protected_nostruct
|
|
+iters/iter_ret_rcu_test_type_nostruct
|
|
+iters/task
|
|
+iters/css
|
|
+iters/iter_tasks_without_lock
|
|
+iters/iter_css_without_lock
|
|
+iters/iter_tasks_lock_and_unlock
|
|
+iters/iter_css_lock_and_unlock
|
|
+map_excl/map_excl_allowed
|
|
+map_in_map/sleepable_acc_map_in_array
|
|
+map_in_map/sleepable_acc_map_in_htab
|
|
+mem_rdonly_untrusted/helper_param_not_ok
|
|
+module_attach
|
|
+preempt_lock/preempt_sleepable_helper
|
|
+preempt_lock/preempt_sleepable_kfunc
|
|
+rcu_read_lock/success
|
|
+rcu_read_lock/rcuptr_acquire
|
|
+read_vsyscall
|
|
+refcounted_kptr/rbtree_sleepable_rcu
|
|
+refcounted_kptr/rbtree_sleepable_rcu_no_explicit_rcu_lock
|
|
+refcounted_kptr_fail/rbtree_fail_sleepable_lock_across_rcu
|
|
+test_lsm/lsm_basic
|
|
+verifier_global_subprogs/arg_tag_ctx_fmod_ret
|
|
+
|
|
+kprobe_multi_test/attach_override # needs CONFIG_BPF_KPROBE_OVERRIDE
|
|
+libbpf_probe_prog_types/BPF_PROG_TYPE_LIRC_MODE2 # needs CONFIG_BPF_LIRC_MODE2
|
|
+test_bpffs # needs CONFIG_BPF_PRELOAD
|
|
+tunnel # needs CONFIG_NET_FOU
|
|
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
|
|
index 6ef6872adbc3..bd76422a1f13 100644
|
|
--- a/tools/testing/selftests/bpf/Makefile
|
|
+++ b/tools/testing/selftests/bpf/Makefile
|
|
@@ -504,7 +504,7 @@ LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \
|
|
LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c
|
|
|
|
# Generate both light skeleton and libbpf skeleton for these
|
|
-LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \
|
|
+LSKELS_EXTRA := test_ksyms_module.c kfunc_call_test.c \
|
|
kfunc_call_test_subprog.c
|
|
SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED)
|
|
|
|
diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
index 1d7a2f1e0731..b22f3a9cb8b8 100644
|
|
--- a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
+++ b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
@@ -7,7 +7,6 @@
|
|
#include "test_ksyms_btf.skel.h"
|
|
#include "test_ksyms_btf_null_check.skel.h"
|
|
#include "test_ksyms_weak.skel.h"
|
|
-#include "test_ksyms_weak.lskel.h"
|
|
#include "test_ksyms_btf_write_check.skel.h"
|
|
|
|
static int duration;
|
|
@@ -111,33 +110,6 @@ static void test_weak_syms(void)
|
|
test_ksyms_weak__destroy(skel);
|
|
}
|
|
|
|
-static void test_weak_syms_lskel(void)
|
|
-{
|
|
- struct test_ksyms_weak_lskel *skel;
|
|
- struct test_ksyms_weak_lskel__data *data;
|
|
- int err;
|
|
-
|
|
- skel = test_ksyms_weak_lskel__open_and_load();
|
|
- if (!ASSERT_OK_PTR(skel, "test_ksyms_weak_lskel__open_and_load"))
|
|
- return;
|
|
-
|
|
- err = test_ksyms_weak_lskel__attach(skel);
|
|
- if (!ASSERT_OK(err, "test_ksyms_weak_lskel__attach"))
|
|
- goto cleanup;
|
|
-
|
|
- /* trigger tracepoint */
|
|
- usleep(1);
|
|
-
|
|
- data = skel->data;
|
|
- ASSERT_EQ(data->out__existing_typed, 0, "existing typed ksym");
|
|
- ASSERT_NEQ(data->out__existing_typeless, -1, "existing typeless ksym");
|
|
- ASSERT_EQ(data->out__non_existent_typeless, 0, "nonexistent typeless ksym");
|
|
- ASSERT_EQ(data->out__non_existent_typed, 0, "nonexistent typed ksym");
|
|
-
|
|
-cleanup:
|
|
- test_ksyms_weak_lskel__destroy(skel);
|
|
-}
|
|
-
|
|
static void test_write_check(bool test_handler1)
|
|
{
|
|
struct test_ksyms_btf_write_check *skel;
|
|
@@ -180,9 +152,6 @@ void test_ksyms_btf(void)
|
|
if (test__start_subtest("weak_ksyms"))
|
|
test_weak_syms();
|
|
|
|
- if (test__start_subtest("weak_ksyms_lskel"))
|
|
- test_weak_syms_lskel();
|
|
-
|
|
if (test__start_subtest("write_check1"))
|
|
test_write_check(true);
|
|
|