diff options
968 files changed, 24142 insertions, 6590 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl index 6350dd82b9a9..087f762ebfd5 100644 --- a/Documentation/ABI/testing/sysfs-bus-cxl +++ b/Documentation/ABI/testing/sysfs-bus-cxl @@ -82,7 +82,12 @@ Description: whether it resides in persistent capacity, volatile capacity, or the LSA, is made permanently unavailable by whatever means is appropriate for the media type. This functionality requires - the device to be not be actively decoding any HPA ranges. + the device to be disabled, that is, not actively decoding any + HPA ranges. This permits avoiding explicit global CPU cache + management, relying instead for it to be done when a region + transitions between software programmed and hardware committed + states. If this file is not present, then there is no hardware + support for the operation. What /sys/bus/cxl/devices/memX/security/erase @@ -92,7 +97,13 @@ Contact: linux-cxl@vger.kernel.org Description: (WO) Write a boolean 'true' string value to this attribute to secure erase user data by changing the media encryption keys for - all user data areas of the device. + all user data areas of the device. This functionality requires + the device to be disabled, that is, not actively decoding any + HPA ranges. This permits avoiding explicit global CPU cache + management, relying instead for it to be done when a region + transitions between software programmed and hardware committed + states. If this file is not present, then there is no hardware + support for the operation. What: /sys/bus/cxl/devices/memX/firmware/ diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index ecd585ca2d50..77942eedf4f6 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -513,17 +513,18 @@ Description: information about CPUs heterogeneity. cpu_capacity: capacity of cpuX. What: /sys/devices/system/cpu/vulnerabilities + /sys/devices/system/cpu/vulnerabilities/gather_data_sampling + /sys/devices/system/cpu/vulnerabilities/itlb_multihit + /sys/devices/system/cpu/vulnerabilities/l1tf + /sys/devices/system/cpu/vulnerabilities/mds /sys/devices/system/cpu/vulnerabilities/meltdown + /sys/devices/system/cpu/vulnerabilities/mmio_stale_data + /sys/devices/system/cpu/vulnerabilities/retbleed + /sys/devices/system/cpu/vulnerabilities/spec_store_bypass /sys/devices/system/cpu/vulnerabilities/spectre_v1 /sys/devices/system/cpu/vulnerabilities/spectre_v2 - /sys/devices/system/cpu/vulnerabilities/spec_store_bypass - /sys/devices/system/cpu/vulnerabilities/l1tf - /sys/devices/system/cpu/vulnerabilities/mds /sys/devices/system/cpu/vulnerabilities/srbds /sys/devices/system/cpu/vulnerabilities/tsx_async_abort - /sys/devices/system/cpu/vulnerabilities/itlb_multihit - /sys/devices/system/cpu/vulnerabilities/mmio_stale_data - /sys/devices/system/cpu/vulnerabilities/retbleed Date: January 2018 Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> Description: Information about CPU vulnerabilities diff --git a/Documentation/ABI/testing/sysfs-platform-hidma b/Documentation/ABI/testing/sysfs-platform-hidma index fca40a54df59..a80aeda85ef6 100644 --- a/Documentation/ABI/testing/sysfs-platform-hidma +++ b/Documentation/ABI/testing/sysfs-platform-hidma @@ -2,7 +2,7 @@ What: /sys/devices/platform/hidma-*/chid /sys/devices/platform/QCOM8061:*/chid Date: Dec 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains the ID of the channel within the HIDMA instance. It is used to associate a given HIDMA channel with the diff --git a/Documentation/ABI/testing/sysfs-platform-hidma-mgmt b/Documentation/ABI/testing/sysfs-platform-hidma-mgmt index 3b6c5c9eabdc..0373745b4e18 100644 --- a/Documentation/ABI/testing/sysfs-platform-hidma-mgmt +++ b/Documentation/ABI/testing/sysfs-platform-hidma-mgmt @@ -2,7 +2,7 @@ What: /sys/devices/platform/hidma-mgmt*/chanops/chan*/priority /sys/devices/platform/QCOM8060:*/chanops/chan*/priority Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains either 0 or 1 and indicates if the DMA channel is a low priority (0) or high priority (1) channel. @@ -11,7 +11,7 @@ What: /sys/devices/platform/hidma-mgmt*/chanops/chan*/weight /sys/devices/platform/QCOM8060:*/chanops/chan*/weight Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains 0..15 and indicates the weight of the channel among equal priority channels during round robin scheduling. @@ -20,7 +20,7 @@ What: /sys/devices/platform/hidma-mgmt*/chreset_timeout_cycles /sys/devices/platform/QCOM8060:*/chreset_timeout_cycles Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains the platform specific cycle value to wait after a reset command is issued. If the value is chosen too short, @@ -32,7 +32,7 @@ What: /sys/devices/platform/hidma-mgmt*/dma_channels /sys/devices/platform/QCOM8060:*/dma_channels Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains the number of dma channels supported by one instance of HIDMA hardware. The value may change from chip to chip. @@ -41,7 +41,7 @@ What: /sys/devices/platform/hidma-mgmt*/hw_version_major /sys/devices/platform/QCOM8060:*/hw_version_major Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Version number major for the hardware. @@ -49,7 +49,7 @@ What: /sys/devices/platform/hidma-mgmt*/hw_version_minor /sys/devices/platform/QCOM8060:*/hw_version_minor Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Version number minor for the hardware. @@ -57,7 +57,7 @@ What: /sys/devices/platform/hidma-mgmt*/max_rd_xactions /sys/devices/platform/QCOM8060:*/max_rd_xactions Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains a value between 0 and 31. Maximum number of read transactions that can be issued back to back. @@ -69,7 +69,7 @@ What: /sys/devices/platform/hidma-mgmt*/max_read_request /sys/devices/platform/QCOM8060:*/max_read_request Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Size of each read request. The value needs to be a power of two and can be between 128 and 1024. @@ -78,7 +78,7 @@ What: /sys/devices/platform/hidma-mgmt*/max_wr_xactions /sys/devices/platform/QCOM8060:*/max_wr_xactions Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Contains a value between 0 and 31. Maximum number of write transactions that can be issued back to back. @@ -91,7 +91,7 @@ What: /sys/devices/platform/hidma-mgmt*/max_write_request /sys/devices/platform/QCOM8060:*/max_write_request Date: Nov 2015 KernelVersion: 4.4 -Contact: "Sinan Kaya <okaya@codeaurora.org>" +Contact: "Sinan Kaya <okaya@kernel.org>" Description: Size of each write request. The value needs to be a power of two and can be between 128 and 1024. diff --git a/Documentation/admin-guide/hw-vuln/gather_data_sampling.rst b/Documentation/admin-guide/hw-vuln/gather_data_sampling.rst new file mode 100644 index 000000000000..264bfa937f7d --- /dev/null +++ b/Documentation/admin-guide/hw-vuln/gather_data_sampling.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0 + +GDS - Gather Data Sampling +========================== + +Gather Data Sampling is a hardware vulnerability which allows unprivileged +speculative access to data which was previously stored in vector registers. + +Problem +------- +When a gather instruction performs loads from memory, different data elements +are merged into the destination vector register. However, when a gather +instruction that is transiently executed encounters a fault, stale data from +architectural or internal vector registers may get transiently forwarded to the +destination vector register instead. This will allow a malicious attacker to +infer stale data using typical side channel techniques like cache timing +attacks. GDS is a purely sampling-based attack. + +The attacker uses gather instructions to infer the stale vector register data. +The victim does not need to do anything special other than use the vector +registers. The victim does not need to use gather instructions to be +vulnerable. + +Because the buffers are shared between Hyper-Threads cross Hyper-Thread attacks +are possible. + +Attack scenarios +---------------- +Without mitigation, GDS can infer stale data across virtually all +permission boundaries: + + Non-enclaves can infer SGX enclave data + Userspace can infer kernel data + Guests can infer data from hosts + Guest can infer guest from other guests + Users can infer data from other users + +Because of this, it is important to ensure that the mitigation stays enabled in +lower-privilege contexts like guests and when running outside SGX enclaves. + +The hardware enforces the mitigation for SGX. Likewise, VMMs should ensure +that guests are not allowed to disable the GDS mitigation. If a host erred and +allowed this, a guest could theoretically disable GDS mitigation, mount an +attack, and re-enable it. + +Mitigation mechanism +-------------------- +This issue is mitigated in microcode. The microcode defines the following new +bits: + + ================================ === ============================ + IA32_ARCH_CAPABILITIES[GDS_CTRL] R/O Enumerates GDS vulnerability + and mitigation support. + IA32_ARCH_CAPABILITIES[GDS_NO] R/O Processor is not vulnerable. + IA32_MCU_OPT_CTRL[GDS_MITG_DIS] R/W Disables the mitigation + 0 by default. + IA32_MCU_OPT_CTRL[GDS_MITG_LOCK] R/W Locks GDS_MITG_DIS=0. Writes + to GDS_MITG_DIS are ignored + Can't be cleared once set. + ================================ === ============================ + +GDS can also be mitigated on systems that don't have updated microcode by +disabling AVX. This can be done by setting gather_data_sampling="force" or +"clearcpuid=avx" on the kernel command-line. + +If used, these options will disable AVX use by turning off XSAVE YMM support. +However, the processor will still enumerate AVX support. Userspace that +does not follow proper AVX enumeration to check both AVX *and* XSAVE YMM +support will break. + +Mitigation control on the kernel command line +--------------------------------------------- +The mitigation can be disabled by setting "gather_data_sampling=off" or +"mitigations=off" on the kernel command line. Not specifying either will default +to the mitigation being enabled. Specifying "gather_data_sampling=force" will +use the microcode mitigation when available or disable AVX on affected systems +where the microcode hasn't been updated to include the mitigation. + +GDS System Information +------------------------ +The kernel provides vulnerability status information through sysfs. For +GDS this can be accessed by the following sysfs file: + +/sys/devices/system/cpu/vulnerabilities/gather_data_sampling + +The possible values contained in this file are: + + ============================== ============================================= + Not affected Processor not vulnerable. + Vulnerable Processor vulnerable and mitigation disabled. + Vulnerable: No microcode Processor vulnerable and microcode is missing + mitigation. + Mitigation: AVX disabled, + no microcode Processor is vulnerable and microcode is missing + mitigation. AVX disabled as mitigation. + Mitigation: Microcode Processor is vulnerable and mitigation is in + effect. + Mitigation: Microcode (locked) Processor is vulnerable and mitigation is in + effect and cannot be disabled. + Unknown: Dependent on + hypervisor status Running on a virtual guest processor that is + affected but with no way to know if host + processor is mitigated or vulnerable. + ============================== ============================================= + +GDS Default mitigation +---------------------- +The updated microcode will enable the mitigation by default. The kernel's +default action is to leave the mitigation enabled. diff --git a/Documentation/admin-guide/hw-vuln/index.rst b/Documentation/admin-guide/hw-vuln/index.rst index e0614760a99e..de99caabf65a 100644 --- a/Documentation/admin-guide/hw-vuln/index.rst +++ b/Documentation/admin-guide/hw-vuln/index.rst @@ -13,9 +13,11 @@ are configurable at compile, boot or run time. l1tf mds tsx_async_abort - multihit.rst - special-register-buffer-data-sampling.rst - core-scheduling.rst - l1d_flush.rst - processor_mmio_stale_data.rst - cross-thread-rsb.rst + multihit + special-register-buffer-data-sampling + core-scheduling + l1d_flush + processor_mmio_stale_data + cross-thread-rsb + srso + gather_data_sampling diff --git a/Documentation/admin-guide/hw-vuln/srso.rst b/Documentation/admin-guide/hw-vuln/srso.rst new file mode 100644 index 000000000000..af59a9395662 --- /dev/null +++ b/Documentation/admin-guide/hw-vuln/srso.rst @@ -0,0 +1,150 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Speculative Return Stack Overflow (SRSO) +======================================== + +This is a mitigation for the speculative return stack overflow (SRSO) +vulnerability found on AMD processors. The mechanism is by now the well +known scenario of poisoning CPU functional units - the Branch Target +Buffer (BTB) and Return Address Predictor (RAP) in this case - and then +tricking the elevated privilege domain (the kernel) into leaking +sensitive data. + +AMD CPUs predict RET instructions using a Return Address Predictor (aka +Return Address Stack/Return Stack Buffer). In some cases, a non-architectural +CALL instruction (i.e., an instruction predicted to be a CALL but is +not actually a CALL) can create an entry in the RAP which may be used +to predict the target of a subsequent RET instruction. + +The specific circumstances that lead to this varies by microarchitecture +but the concern is that an attacker can mis-train the CPU BTB to predict +non-architectural CALL instructions in kernel space and use this to +control the speculative target of a subsequent kernel RET, potentially +leading to information disclosure via a speculative side-channel. + +The issue is tracked under CVE-2023-20569. + +Affected processors +------------------- + +AMD Zen, generations 1-4. That is, all families 0x17 and 0x19. Older +processors have not been investigated. + +System information and options +------------------------------ + +First of all, it is required that the latest microcode be loaded for +mitigations to be effective. + +The sysfs file showing SRSO mitigation status is: + + /sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow + +The possible values in this file are: + + * 'Not affected': + + The processor is not vulnerable + + * 'Vulnerable: no microcode': + + The processor is vulnerable, no microcode extending IBPB + functionality to address the vulnerability has been applied. + + * 'Mitigation: microcode': + + Extended IBPB functionality microcode patch has been applied. It does + not address User->Kernel and Guest->Host transitions protection but it + does address User->User and VM->VM attack vectors. + + Note that User->User mitigation is controlled by how the IBPB aspect in + the Spectre v2 mitigation is selected: + + * conditional IBPB: + + where each process can select whether it needs an IBPB issued + around it PR_SPEC_DISABLE/_ENABLE etc, see :doc:`spectre` + + * strict: + + i.e., always on - by supplying spectre_v2_user=on on the kernel + command line + + (spec_rstack_overflow=microcode) + + * 'Mitigation: safe RET': + + Software-only mitigation. It complements the extended IBPB microcode + patch functionality by addressing User->Kernel and Guest->Host + transitions protection. + + Selected by default or by spec_rstack_overflow=safe-ret + + * 'Mitigation: IBPB': + + Similar protection as "safe RET" above but employs an IBPB barrier on + privilege domain crossings (User->Kernel, Guest->Host). + + (spec_rstack_overflow=ibpb) + + * 'Mitigation: IBPB on VMEXIT': + + Mitigation addressing the cloud provider scenario - the Guest->Host + transitions only. + + (spec_rstack_overflow=ibpb-vmexit) + + + +In order to exploit vulnerability, an attacker needs to: + + - gain local access on the machine + + - break kASLR + + - find gadgets in the running kernel in order to use them in the exploit + + - potentially create and pin an additional workload on the sibling + thread, depending on the microarchitecture (not necessary on fam 0x19) + + - run the exploit + +Considering the performance implications of each mitigation type, the +default one is 'Mitigation: safe RET' which should take care of most +attack vectors, including the local User->Kernel one. + +As always, the user is advised to keep her/his system up-to-date by +applying software updates regularly. + +The default setting will be reevaluated when needed and especially when +new attack vectors appear. + +As one can surmise, 'Mitigation: safe RET' does come at the cost of some +performance depending on the workload. If one trusts her/his userspace +and does not want to suffer the performance impact, one can always +disable the mitigation with spec_rstack_overflow=off. + +Similarly, 'Mitigation: IBPB' is another full mitigation type employing +an indrect branch prediction barrier after having applied the required +microcode patch for one's system. This mitigation comes also at +a performance cost. + +Mitigation: safe RET +-------------------- + +The mitigation works by ensuring all RET instructions speculate to +a controlled location, similar to how speculation is controlled in the +retpoline sequence. To accomplish this, the __x86_return_thunk forces +the CPU to mispredict every function return using a 'safe return' +sequence. + +To ensure the safety of this mitigation, the kernel must ensure that the +safe return sequence is itself free from attacker interference. In Zen3 +and Zen4, this is accomplished by creating a BTB alias between the +untraining function srso_untrain_ret_alias() and the safe return +function srso_safe_ret_alias() which results in evicting a potentially +poisoned BTB entry and using that safe one for all function returns. + +In older Zen1 and Zen2, this is accomplished using a reinterpretation +technique similar to Retbleed one: srso_untrain_ret() and +srso_safe_ret(). diff --git a/Documentation/admin-guide/kdump/vmcoreinfo.rst b/Documentation/admin-guide/kdump/vmcoreinfo.rst index c18d94fa6470..f8ebb63b6c5d 100644 --- a/Documentation/admin-guide/kdump/vmcoreinfo.rst +++ b/Documentation/admin-guide/kdump/vmcoreinfo.rst @@ -624,3 +624,9 @@ Used to get the correct ranges: * VMALLOC_START ~ VMALLOC_END : vmalloc() / ioremap() space. * VMEMMAP_START ~ VMEMMAP_END : vmemmap space, used for struct page array. * KERNEL_LINK_ADDR : start address of Kernel link and BPF + +va_kernel_pa_offset +------------------- + +Indicates the offset between the kernel virtual and physical mappings. +Used to translate virtual to physical addresses. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a1457995fd41..722b6eca2e93 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1623,6 +1623,26 @@ Format: off | on default: on + gather_data_sampling= + [X86,INTEL] Control the Gather Data Sampling (GDS) + mitigation. + + Gather Data Sampling is a hardware vulnerability which + allows unprivileged speculative access to data which was + previously stored in vector registers. + + This issue is mitigated by default in updated microcode. + The mitigation may have a performance impact but can be + disabled. On systems without the microcode mitigation + disabling AVX serves as a mitigation. + + force: Disable AVX to mitigate systems without + microcode mitigation. No effect if the microcode + mitigation is present. Known to cause crashes in + userspace with buggy AVX enumeration. + + off: Disable GDS mitigation. + gcov_persist= [GCOV] When non-zero (default), profiling data for kernel modules is saved and remains accessible via debugfs, even when the module is unloaded/reloaded. @@ -3273,24 +3293,25 @@ Disable all optional CPU mitigations. This improves system performance, but it may also expose users to several CPU vulnerabilities. - Equivalent to: nopti [X86,PPC] - if nokaslr then kpti=0 [ARM64] - nospectre_v1 [X86,PPC] - nobp=0 [S390] - nospectre_v2 [X86,PPC,S390,ARM64] - spectre_v2_user=off [X86] - spec_store_bypass_disable=off [X86,PPC] - ssbd=force-off [ARM64] - nospectre_bhb [ARM64] + Equivalent to: if nokaslr then kpti=0 [ARM64] + gather_data_sampling=off [X86] + kvm.nx_huge_pages=off [X86] l1tf=off [X86] mds=off [X86] - tsx_async_abort=off [X86] - kvm.nx_huge_pages=off [X86] - srbds=off [X86,INTEL] + mmio_stale_data=off [X86] no_entry_flush [PPC] no_uaccess_flush [PPC] - mmio_stale_data=off [X86] + nobp=0 [S390] + nopti [X86,PPC] + nospectre_bhb [ARM64] + nospectre_v1 [X86,PPC] + nospectre_v2 [X86,PPC,S390,ARM64] retbleed=off [X86] + spec_store_bypass_disable=off [X86,PPC] + spectre_v2_user=off [X86] + srbds=off [X86,INTEL] + ssbd=force-off [ARM64] + tsx_async_abort=off [X86] Exceptions: This does not have any effect on @@ -5875,6 +5896,17 @@ Not specifying this option is equivalent to spectre_v2_user=auto. + spec_rstack_overflow= + [X86] Control RAS overflow mitigation on AMD Zen CPUs + + off - Disable mitigation + microcode - Enable microcode mitigation only + safe-ret - Enable sw-only safe RET mitigation (default) + ibpb - Enable mitigation by issuing IBPB on + kernel entry + ibpb-vmexit - Issue IBPB only on VMEXIT + (cloud-specific mitigation) + spec_store_bypass_disable= [HW] Control Speculative Store Bypass (SSB) Disable mitigation (Speculative Store Bypass vulnerability) diff --git a/Documentation/core-api/kernel-api.rst b/Documentation/core-api/kernel-api.rst index a526fbe06f86..ae92a2571388 100644 --- a/Documentation/core-api/kernel-api.rst +++ b/Documentation/core-api/kernel-api.rst @@ -166,7 +166,6 @@ Integer log and power Functions ------------------------------- .. kernel-doc:: include/linux/int_log.h - :export: .. kernel-doc:: lib/math/int_pow.c :export: diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml index 72d2e910f206..2594fa192f93 100644 --- a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml @@ -216,7 +216,6 @@ properties: description: Whether to enable burnout current for EXT1. adi,ext1-burnout-current-nanoamp: - $ref: /schemas/types.yaml#/definitions/uint32 description: Burnout current in nanoamps to be applied to EXT1. enum: [0, 50, 500, 1000, 10000] @@ -233,7 +232,6 @@ properties: description: Whether to enable burnout current for EXT2. adi,ext2-burnout-current-nanoamp: - $ref: /schemas/types.yaml#/definitions/uint32 description: Burnout current in nanoamps to be applied to EXT2. enum: [0, 50, 500, 1000, 10000] default: 0 @@ -249,7 +247,6 @@ properties: description: Whether to enable burnout current for VIOUT. adi,viout-burnout-current-nanoamp: - $ref: /schemas/types.yaml#/definitions/uint32 description: Burnout current in nanoamps to be applied to VIOUT. enum: [0, 1000, 10000] default: 0 diff --git a/Documentation/devicetree/bindings/net/mediatek,net.yaml b/Documentation/devicetree/bindings/net/mediatek,net.yaml index acb2b2ac4fe1..31cc0c412805 100644 --- a/Documentation/devicetree/bindings/net/mediatek,net.yaml +++ b/Documentation/devicetree/bindings/net/mediatek,net.yaml @@ -293,7 +293,7 @@ allOf: patternProperties: "^mac@[0-1]$": type: object - additionalProperties: false + unevaluatedProperties: false allOf: - $ref: ethernet-controller.yaml# description: @@ -305,14 +305,9 @@ patternProperties: reg: maxItems: 1 - phy-handle: true - - phy-mode: true - required: - reg - compatible - - phy-handle required: - compatible diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml index 176ea5f90251..7f324c6da915 100644 --- a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml +++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml @@ -91,12 +91,18 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle tx_delay: - description: Delay value for TXD timing. Range value is 0~0x7F, 0x30 as default. + description: Delay value for TXD timing. $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0x7F + default: 0x30 rx_delay: - description: Delay value for RXD timing. Range value is 0~0x7F, 0x10 as default. + description: Delay value for RXD timing. $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0x7F + default: 0x10 phy-supply: description: PHY regulator diff --git a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml index 30b2131b5860..65cb2e5c5eee 100644 --- a/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml +++ b/Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml @@ -16,7 +16,6 @@ properties: - enum: - atmel,at91rm9200-usart - atmel,at91sam9260-usart - - microchip,sam9x60-usart - items: - const: atmel,at91rm9200-dbgu - const: atmel,at91rm9200-usart @@ -24,6 +23,9 @@ properties: - const: atmel,at91sam9260-dbgu - const: atmel,at91sam9260-usart - items: + - const: microchip,sam9x60-usart + - const: atmel,at91sam9260-usart + - items: - const: microchip,sam9x60-dbgu - const: microchip,sam9x60-usart - const: atmel,at91sam9260-dbgu diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml index bf1234550343..5db718e4d0e7 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml @@ -9,6 +9,9 @@ title: Amlogic AXG sound card maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: const: amlogic,axg-sound-card @@ -17,23 +20,12 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle-array description: list of auxiliary devices - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - description: - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. - audio-widgets: $ref: /schemas/types.yaml#/definitions/non-unique-string-array description: A list off component DAPM widget. Each entry is a pair of strings, the first being the widget type, the second being the widget name - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - patternProperties: "^dai-link-[0-9]+$": type: object @@ -108,7 +100,6 @@ patternProperties: - sound-dai required: - - model - dai-link-0 unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml index b358fd601ed3..d4277d342e69 100644 --- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml @@ -9,6 +9,9 @@ title: Amlogic GX sound card maintainers: - Jerome Brunet <jbrunet@baylibre.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: items: @@ -18,14 +21,6 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle-array description: list of auxiliary devices - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - minItems: 2 - description: |- - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. - audio-widgets: $ref: /schemas/types.yaml#/definitions/non-unique-string-array minItems: 2 @@ -33,10 +28,6 @@ properties: A list off component DAPM widget. Each entry is a pair of strings, the first being the widget type, the second being the widget name - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - patternProperties: "^dai-link-[0-9]+$": type: object @@ -86,7 +77,7 @@ required: - model - dai-link-0 -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 35eef7d818a2..4051c2538caf 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -19,7 +19,9 @@ allOf: properties: compatible: - const: awinic,aw88395 + enum: + - awinic,aw88395 + - awinic,aw88261 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml new file mode 100644 index 000000000000..7a6de938b11d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l43.yaml @@ -0,0 +1,313 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/cirrus,cs42l43.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic CS42L43 Audio CODEC + +maintainers: + - patches@opensource.cirrus.com + +description: | + The CS42L43 is an audio CODEC with integrated MIPI SoundWire interface + (Version 1.2.1 compliant), I2C, SPI, and I2S/TDM interfaces designed + for portable applications. It provides a high dynamic range, stereo + DAC for headphone output, two integrated Class D amplifiers for + loudspeakers, and two ADCs for wired headset microphone input or + stereo line input. PDM inputs are provided for digital microphones. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - cirrus,cs42l43 + + reg: + maxItems: 1 + + vdd-p-supply: + description: + Power supply for the high voltage interface. + + vdd-a-supply: + description: + Power supply for internal analog circuits. + + vdd-d-supply: + description: + Power supply for internal digital circuits. Can be internally supplied. + + vdd-io-supply: + description: + Power supply for external interface and internal digital logic. + + vdd-cp-supply: + description: + Power supply for the amplifier 3 and 4 charge pump. + + vdd-amp-supply: + description: + Power supply for amplifier 1 and 2. + + reset-gpios: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + interrupts: + maxItems: 1 + + "#sound-dai-cells": + const: 1 + + clocks: + items: + - description: Synchronous audio clock provided on mclk_in. + + clock-names: + const: mclk + + cirrus,bias-low: + type: boolean + description: + Select a 1.8V headset micbias rather than 2.8V. + + cirrus,bias-sense-microamp: + description: + Current at which the headset micbias sense clamp will engage, 0 to + disable. + enum: [ 0, 14, 23, 41, 50, 60, 68, 86, 95 ] + default: 0 + + cirrus,bias-ramp-ms: + description: + Time in milliseconds the hardware allows for the headset micbias to + ramp up. + enum: [ 10, 40, 90, 170 ] + default: 170 + + cirrus,detect-us: + description: + Time in microseconds the type detection will run for. Long values will + cause more audible effects, but give more accurate detection. + enum: [ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 ] + default: 10000 + + cirrus,button-automute: + type: boolean + description: + Enable the hardware automuting of decimator 1 when a headset button is + pressed. + + cirrus,buttons-ohms: + description: + Impedance in Ohms for each headset button, these should be listed in + ascending order. + minItems: 1 + maxItems: 6 + + cirrus,tip-debounce-ms: + description: + Software debounce on tip sense triggering in milliseconds. + default: 0 + + cirrus,tip-invert: + type: boolean + description: + Indicates tip detect polarity, inverted implies open-circuit whilst the + jack is inserted. + + cirrus,tip-disable-pullup: + type: boolean + description: + Indicates if the internal pullup on the tip detect should be disabled. + + cirrus,tip-fall-db-ms: + description: + Time in milliseconds a falling edge on the tip detect should be hardware + debounced for. Note the falling edge is considered after the invert. + enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ] + default: 500 + + cirrus,tip-rise-db-ms: + description: + Time in milliseconds a rising edge on the tip detect should be hardware + debounced for. Note the rising edge is considered after the invert. + enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ] + default: 500 + + cirrus,use-ring-sense: + type: boolean + description: + Indicates if the ring sense should be used. + + cirrus,ring-invert: + type: boolean + description: + Indicates ring detect polarity, inverted implies open-circuit whilst the + jack is inserted. + + cirrus,ring-disable-pullup: + type: boolean + description: + Indicates if the internal pullup on the ring detect should be disabled. + + cirrus,ring-fall-db-ms: + description: + Time in milliseconds a falling edge on the ring detect should be hardware + debounced for. Note the falling edge is considered after the invert. + enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ] + default: 500 + + cirrus,ring-rise-db-ms: + description: + Time in milliseconds a rising edge on the ring detect should be hardware + debounced for. Note the rising edge is considered after the invert. + enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ] + default: 500 + + pinctrl: + type: object + $ref: /schemas/pinctrl/pinctrl.yaml# + additionalProperties: false + + properties: + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-ranges: + items: + - description: A phandle to the CODEC pinctrl node + minimum: 0 + - const: 0 + - const: 0 + - const: 3 + + patternProperties: + "-state$": + oneOf: + - $ref: "#/$defs/cirrus-cs42l43-state" + - patternProperties: + "-pins$": + $ref: "#/$defs/cirrus-cs42l43-state" + additionalProperties: false + + spi: + type: object + $ref: /schemas/spi/spi-controller.yaml# + unevaluatedProperties: false + +$defs: + cirrus-cs42l43-state: + type: object + + allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml# + - $ref: /schemas/pinctrl/pinmux-node.yaml# + + oneOf: + - required: [ groups ] + - required: [ pins ] + + additionalProperties: false + + properties: + groups: + enum: [ gpio1, gpio2, gpio3, asp, pdmout2, pdmout1, i2c, spi ] + + pins: + enum: [ gpio1, gpio2, gpio3, + asp_dout, asp_fsync, asp_bclk, + pdmout2_clk, pdmout2_data, pdmout1_clk, pdmout1_data, + i2c_sda, i2c_scl, + spi_miso, spi_sck, spi_ssb ] + + function: + enum: [ gpio, spdif, irq, mic-shutter, spk-shutter ] + + drive-strength: + description: Set drive strength in mA + enum: [ 1, 2, 4, 8, 9, 10, 12, 16 ] + + input-debounce: + description: Set input debounce in uS + enum: [ 0, 85 ] + +required: + - compatible + - reg + - vdd-p-supply + - vdd-a-supply + - vdd-io-supply + - vdd-cp-supply + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + cs42l43: codec@1a { + compatible = "cirrus,cs42l43"; + reg = <0x1a>; + + vdd-p-supply = <&vdd5v0>; + vdd-a-supply = <&vdd1v8>; + vdd-io-supply = <&vdd1v8>; + vdd-cp-supply = <&vdd1v8>; + vdd-amp-supply = <&vdd5v0>; + + reset-gpios = <&gpio 0>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio>; + interrupts = <56 IRQ_TYPE_LEVEL_LOW>; + + #sound-dai-cells = <1>; + + clocks = <&clks 0>; + clock-names = "mclk"; + + cs42l43_pins: pinctrl { + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&cs42l43_pins 0 0 3>; + + pinctrl-names = "default"; + pinctrl-0 = <&pinsettings>; + + pinsettings: default-state { + shutter-pins { + groups = "gpio3"; + function = "mic-shutter"; + }; + }; + }; + + spi { + #address-cells = <1>; + #size-cells = <0>; + + cs-gpios = <&cs42l43_pins 1 0>; + + sensor@0 { + compatible = "bosch,bme680"; + reg = <0>; + spi-max-frequency = <1400000>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml index 6df0e03a1d4b..188f38baddec 100644 --- a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml @@ -17,6 +17,9 @@ description: | such as SAI, MICFIL, .etc through building rpmsg channels between Cortex-A and Cortex-M. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: @@ -27,10 +30,6 @@ properties: - fsl,imx8ulp-rpmsg-audio - fsl,imx93-rpmsg-audio - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - clocks: items: - description: Peripheral clock for register access @@ -66,13 +65,6 @@ properties: $ref: /schemas/types.yaml#/definitions/phandle description: The phandle to a node of audio codec - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - description: | - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. - fsl,enable-lpa: $ref: /schemas/types.yaml#/definitions/flag description: enable low power audio path. @@ -101,9 +93,8 @@ properties: required: - compatible - - model -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml index ba5b7728cf33..bac940553965 100644 --- a/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml +++ b/Documentation/devicetree/bindings/sound/google,sc7180-trogdor.yaml @@ -13,23 +13,15 @@ maintainers: description: This binding describes the SC7180 sound card which uses LPASS for audio. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: - google,sc7180-trogdor - google,sc7180-coachz - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - description: - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - "#address-cells": const: 1 @@ -86,11 +78,10 @@ patternProperties: required: - compatible - - model - "#address-cells" - "#size-cells" -additionalProperties: false +unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml index 0b1a01a4c14e..ec4b6e547ca6 100644 --- a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml +++ b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml @@ -13,22 +13,14 @@ maintainers: description: This binding describes the SC7280 sound card which uses LPASS for audio. +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: - google,sc7280-herobrine - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - description: - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. - - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - "#address-cells": const: 1 @@ -97,11 +89,10 @@ patternProperties: required: - compatible - - model - "#address-cells" - "#size-cells" -additionalProperties: false +unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/sound/gtm601.txt b/Documentation/devicetree/bindings/sound/gtm601.txt deleted file mode 100644 index efa32a486c4a..000000000000 --- a/Documentation/devicetree/bindings/sound/gtm601.txt +++ /dev/null @@ -1,19 +0,0 @@ -GTM601 UMTS modem audio interface CODEC - -This device has no configuration interface. The sample rate and channels are -based on the compatible string - "option,gtm601" = 8kHz mono - "broadmobi,bm818" = 48KHz stereo - -Required properties: - - - compatible : one of - "option,gtm601" - "broadmobi,bm818" - - -Example: - -codec: gtm601_codec { - compatible = "option,gtm601"; -}; diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml index b6f5d486600e..f7ad5ea2491e 100644 --- a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml +++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml @@ -9,23 +9,14 @@ title: NXP i.MX audio sound card. maintainers: - Shengjiu Wang <shengjiu.wang@nxp.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: - fsl,imx-audio-card - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array - description: - A list of the connections between audio components. Each entry is a - pair of strings, the first being the connection's sink, the second - being the connection's source. Valid names could be power supplies, - MicBias of codec and the jacks on the board. - patternProperties: ".*-dai-link$": description: @@ -84,9 +75,8 @@ patternProperties: required: - compatible - - model -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/sound/max9892x.txt b/Documentation/devicetree/bindings/sound/max9892x.txt deleted file mode 100644 index 98cb9ba5b328..000000000000 --- a/Documentation/devicetree/bindings/sound/max9892x.txt +++ /dev/null @@ -1,44 +0,0 @@ -Maxim Integrated MAX98925/MAX98926/MAX98927 Speaker Amplifier - -This device supports I2C. - -Required properties: - - - compatible : should be one of the following - - "maxim,max98925" - - "maxim,max98926" - - "maxim,max98927" - - - vmon-slot-no : slot number used to send voltage information - or in inteleave mode this will be used as - interleave slot. - MAX98925/MAX98926 slot range : 0 ~ 30, Default : 0 - MAX98927 slot range : 0 ~ 15, Default : 0 - - - imon-slot-no : slot number used to send current information - MAX98925/MAX98926 slot range : 0 ~ 30, Default : 0 - MAX98927 slot range : 0 ~ 15, Default : 0 - - - interleave-mode : When using two MAX9892X in a system it is - possible to create ADC data that that will - overflow the frame size. Digital Audio Interleave - mode provides a means to output VMON and IMON data - from two devices on a single DOUT line when running - smaller frames sizes such as 32 BCLKS per LRCLK or - 48 BCLKS per LRCLK. - Range : 0 (off), 1 (on), Default : 0 - - - reg : the I2C address of the device for I2C - -Optional properties: - - reset-gpios : GPIO to reset the device - -Example: - -codec: max98927@3a { - compatible = "maxim,max98927"; - vmon-slot-no = <0>; - imon-slot-no = <1>; - interleave-mode = <0>; - reg = <0x3a>; -}; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98925.yaml b/Documentation/devicetree/bindings/sound/maxim,max98925.yaml new file mode 100644 index 000000000000..32fd86204a7a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/maxim,max98925.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/maxim,max98925.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX98925/MAX98926/MAX98927 speaker amplifier + +maintainers: + - Ryan Lee <ryans.lee@maximintegrated.com> + +properties: + compatible: + enum: + - maxim,max98925 + - maxim,max98926 + - maxim,max98927 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + vmon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 30 + default: 0 + description: + Slot number used to send voltage information or in inteleave mode this + will be used as interleave slot. + + imon-slot-no: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 30 + default: 0 + description: + Slot number used to send current information. + + maxim,interleave-mode: + type: boolean + description: + When using two MAX9892X in a system it is possible to create ADC data + that will overflow the frame size. When enabled, the Digital Audio + Interleave mode provides a means to output VMON and IMON data from two + devices on a single DOUT line when running smaller frames sizes such as + 32 BCLKS per LRCLK or 48 BCLKS per LRCLK. + +required: + - compatible + - reg + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - maxim,max98927 + then: + properties: + vmon-slot-no: + minimum: 0 + maximum: 15 + + imon-slot-no: + minimum: 0 + maximum: 15 + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + #include <dt-bindings/gpio/gpio.h> + audio-codec@3a { + compatible = "maxim,max98927"; + reg = <0x3a>; + #sound-dai-cells = <0>; + + pinctrl-0 = <&speaker_default>; + pinctrl-names = "default"; + + reset-gpios = <&tlmm 69 GPIO_ACTIVE_LOW>; + + vmon-slot-no = <1>; + imon-slot-no = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt7986-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt7986-afe.yaml new file mode 100644 index 000000000000..398efdfe00f5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt7986-afe.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt7986-afe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek AFE PCM controller for MT7986 + +maintainers: + - Maso Huang <maso.huang@mediatek.com> + +properties: + compatible: + oneOf: + - const: mediatek,mt7986-afe + - items: + - enum: + - mediatek,mt7981-afe + - mediatek,mt7988-afe + - const: mediatek,mt7986-afe + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 5 + items: + - description: audio bus clock + - description: audio 26M clock + - description: audio intbus clock + - description: audio hopping clock + - description: audio pll clock + - description: mux for pcm_mck + - description: audio i2s/pcm mck + + clock-names: + minItems: 5 + items: + - const: bus_ck + - const: 26m_ck + - const: l_ck + - const: aud_ck + - const: eg2_ck + - const: sel + - const: i2s_m + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - if: + properties: + compatible: + contains: + const: mediatek,mt7986-afe + then: + properties: + clocks: + items: + - description: audio bus clock + - description: audio 26M clock + - description: audio intbus clock + - description: audio hopping clock + - description: audio pll clock + clock-names: + items: + - const: bus_ck + - const: 26m_ck + - const: l_ck + - const: aud_ck + - const: eg2_ck + + - if: + properties: + compatible: + contains: + const: mediatek,mt7981-afe + then: + properties: + clocks: + items: + - description: audio bus clock + - description: audio 26M clock + - description: audio intbus clock + - description: audio hopping clock + - description: audio pll clock + - description: mux for pcm_mck + clock-names: + items: + - const: bus_ck + - const: 26m_ck + - const: l_ck + - const: aud_ck + - const: eg2_ck + - const: sel + + - if: + properties: + compatible: + contains: + const: mediatek,mt7988-afe + then: + properties: + clocks: + items: + - description: audio bus clock + - description: audio 26M clock + - description: audio intbus clock + - description: audio hopping clock + - description: audio pll clock + - description: mux for pcm_mck + - description: audio i2s/pcm mck + clock-names: + items: + - const: bus_ck + - const: 26m_ck + - const: l_ck + - const: aud_ck + - const: eg2_ck + - const: sel + - const: i2s_m + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/clock/mt7986-clk.h> + + afe@11210000 { + compatible = "mediatek,mt7986-afe"; + reg = <0x11210000 0x9000>; + interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&infracfg_ao CLK_INFRA_AUD_BUS_CK>, + <&infracfg_ao CLK_INFRA_AUD_26M_CK>, + <&infracfg_ao CLK_INFRA_AUD_L_CK>, + <&infracfg_ao CLK_INFRA_AUD_AUD_CK>, + <&infracfg_ao CLK_INFRA_AUD_EG2_CK>; + clock-names = "bus_ck", + "26m_ck", + "l_ck", + "aud_ck", + "eg2_ck"; + assigned-clocks = <&topckgen CLK_TOP_A1SYS_SEL>, + <&topckgen CLK_TOP_AUD_L_SEL>, + <&topckgen CLK_TOP_A_TUNER_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_APLL2_D4>, + <&apmixedsys CLK_APMIXED_APLL2>, + <&topckgen CLK_TOP_APLL2_D4>; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml new file mode 100644 index 000000000000..09247ceea3f7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mediatek,mt7986-wm8960.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/mediatek,mt7986-wm8960.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT7986 sound card with WM8960 codec + +maintainers: + - Maso Huang <maso.huang@mediatek.com> + +allOf: + - $ref: sound-card-common.yaml# + +properties: + compatible: + const: mediatek,mt7986-wm8960-sound + + platform: + type: object + additionalProperties: false + properties: + sound-dai: + description: The phandle of MT7986 platform. + maxItems: 1 + required: + - sound-dai + + codec: + type: object + additionalProperties: false + properties: + sound-dai: + description: The phandle of wm8960 codec. + maxItems: 1 + required: + - sound-dai + +unevaluatedProperties: false + +required: + - compatible + - audio-routing + - platform + - codec + +examples: + - | + sound { + compatible = "mediatek,mt7986-wm8960-sound"; + model = "mt7986-wm8960"; + audio-routing = + "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + + platform { + sound-dai = <&afe>; + }; + + codec { + sound-dai = <&wm8960>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml index 05e532b5d50a..43b3b67bdf3b 100644 --- a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml +++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml @@ -9,23 +9,19 @@ title: MediaTek MT8188 ASoC sound card maintainers: - Trevor Wu <trevor.wu@mediatek.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: - mediatek,mt8188-mt6359-evb - mediatek,mt8188-nau8825 - model: - $ref: /schemas/types.yaml#/definitions/string - description: User specified audio sound card name - audio-routing: - $ref: /schemas/types.yaml#/definitions/non-unique-string-array description: - A list of the connections between audio components. Each entry is a - sink/source pair of strings. Valid names could be the input or output - widgets of audio components, power supplies, MicBias of codec and the - software switch. + Valid names could be the input or output widgets of audio components, + power supplies, MicBias of codec and the software switch. mediatek,platform: $ref: /schemas/types.yaml#/definitions/phandle @@ -86,7 +82,7 @@ patternProperties: required: - link-name -additionalProperties: false +unevaluatedProperties: false required: - compatible @@ -96,6 +92,7 @@ examples: - | sound { compatible = "mediatek,mt8188-mt6359-evb"; + model = "MT6359-EVB"; mediatek,platform = <&afe>; pinctrl-names = "default"; pinctrl-0 = <&aud_pins_default>; diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8821.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8821.yaml index fc2f4ce4db88..3e54abd4ca74 100644 --- a/Documentation/devicetree/bindings/sound/nuvoton,nau8821.yaml +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8821.yaml @@ -89,6 +89,12 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 default: 3072000 + nuvoton,left-input-single-end: + description: Enable left input with single-ended settings if set. + For the headset mic application, the single-ended control is + just limited to the left adc for design demand. + type: boolean + '#sound-dai-cells': const: 0 @@ -114,6 +120,7 @@ examples: nuvoton,jkdet-pull-enable; nuvoton,jkdet-pull-up; nuvoton,key-enable; + nuvoton,left-input-single-end; nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>; nuvoton,micbias-voltage = <6>; nuvoton,vref-impedance = <2>; diff --git a/Documentation/devicetree/bindings/sound/option,gtm601.yaml b/Documentation/devicetree/bindings/sound/option,gtm601.yaml new file mode 100644 index 000000000000..ff813d97fc59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/option,gtm601.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/option,gtm601.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GTM601 UMTS modem audio interface CODEC + +maintainers: + - kernel@puri.sm + +description: > + This device has no configuration interface. The sample rate and channels are + based on the compatible string + +properties: + compatible: + oneOf: + - description: Broadmobi BM818 (48Khz stereo) + items: + - const: broadmobi,bm818 + - const: option,gtm601 + - description: GTM601 (8kHz mono) + const: option,gtm601 + + '#sound-dai-cells': + const: 0 + +required: + - compatible + +allOf: + - $ref: dai-common.yaml# + +additionalProperties: false + +examples: + - | + codec { + compatible = "option,gtm601"; + #sound-dai-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..13a5a0a10fe6 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers: - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> +definitions: + port-def: + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: audio-graph-port.yaml#/definitions/endpoint-base + properties: + playback: + $ref: /schemas/types.yaml#/definitions/phandle-array + capture: + $ref: /schemas/types.yaml#/definitions/phandle-array + unevaluatedProperties: false + properties: compatible: @@ -77,6 +91,12 @@ properties: it must be 1 if your system has audio_clkout0/1/2/3 enum: [0, 1] + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + clock-frequency: description: for audio_clkout0/1/2/3 @@ -103,35 +123,9 @@ properties: description: List of necessary clock names. # details are defined below - ports: - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - '^port(@[0-9a-f]+)?$': - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - "^endpoint(@[0-9a-f]+)?": - $ref: audio-graph-port.yaml#/definitions/endpoint-base - properties: - playback: - $ref: /schemas/types.yaml#/definitions/phandle-array - capture: - $ref: /schemas/types.yaml#/definitions/phandle-array - unevaluatedProperties: false - + # ports is below port: - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - "^endpoint(@[0-9a-f]+)?": - $ref: audio-graph-port.yaml#/definitions/endpoint-base - properties: - playback: - $ref: /schemas/types.yaml#/definitions/phandle-array - capture: - $ref: /schemas/types.yaml#/definitions/phandle-array - unevaluatedProperties: false + $ref: "#/definitions/port-def" rcar_sound,dvc: description: DVC subnode. @@ -248,8 +242,9 @@ properties: - interrupts additionalProperties: false +patternProperties: # For DAI base - rcar_sound,dai: + 'rcar_sound,dai(@[0-9a-f]+)?$': description: DAI subnode. type: object patternProperties: @@ -269,6 +264,13 @@ properties: - capture additionalProperties: false + 'ports(@[0-9a-f]+)?$': + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + patternProperties: + '^port(@[0-9a-f]+)?$': + $ref: "#/definitions/port-def" + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml index 447e013f6e17..5ea0819a261a 100644 --- a/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,aries-wm8994.yaml @@ -9,6 +9,9 @@ title: Samsung Aries audio complex with WM8994 codec maintainers: - Jonathan Bakker <xc-racer2@live.ca> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: enum: @@ -17,10 +20,6 @@ properties: # Without FM radio and modem slave - samsung,fascinate4g-wm8994 - model: - $ref: /schemas/types.yaml#/definitions/string - description: The user-visible name of this sound complex. - cpu: type: object additionalProperties: false @@ -46,6 +45,7 @@ properties: samsung,audio-routing: $ref: /schemas/types.yaml#/definitions/non-unique-string-array + deprecated: true description: | List of the connections between audio components; each entry is a pair of strings, the first being the @@ -56,6 +56,7 @@ properties: or FM In For samsung,fascinate4g-wm8994: HP, SPK, RCV, LINE, Main Mic, or HeadsetMic + Deprecated, use audio-routing. extcon: description: Extcon phandle for dock detection @@ -87,10 +88,9 @@ properties: required: - compatible - - model - cpu - codec - - samsung,audio-routing + - audio-routing - extcon - main-micbias-supply - headset-micbias-supply @@ -98,7 +98,7 @@ required: - headset-detect-gpios - headset-key-gpios -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -121,7 +121,7 @@ examples: headset-detect-gpios = <&gph0 6 GPIO_ACTIVE_HIGH>; headset-key-gpios = <&gph3 6 GPIO_ACTIVE_HIGH>; - samsung,audio-routing = + audio-routing = "HP", "HPOUT1L", "HP", "HPOUT1R", diff --git a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml index 31095913e330..6ec80f529d84 100644 --- a/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,midas-audio.yaml @@ -9,14 +9,13 @@ title: Samsung Midas audio complex with WM1811 codec maintainers: - Sylwester Nawrocki <s.nawrocki@samsung.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: const: samsung,midas-audio - model: - $ref: /schemas/types.yaml#/definitions/string - description: The user-visible name of this sound complex. - cpu: type: object additionalProperties: false @@ -38,6 +37,7 @@ properties: - sound-dai samsung,audio-routing: + deprecated: true $ref: /schemas/types.yaml#/definitions/non-unique-string-array description: | List of the connections between audio components; each entry is @@ -45,6 +45,7 @@ properties: being the connection's source; valid names for sources and sinks are the WM1811's pins (as documented in its binding), and the jacks on the board: HP, SPK, Main Mic, Sub Mic, Headset Mic. + Deprecated, use audio-routing. mic-bias-supply: description: Supply for the micbias on the Main microphone @@ -62,14 +63,13 @@ properties: required: - compatible - - model - cpu - codec - - samsung,audio-routing + - audio-routing - mic-bias-supply - submic-bias-supply -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -84,7 +84,7 @@ examples: mic-bias-supply = <&mic_bias_reg>; submic-bias-supply = <&submic_bias_reg>; - samsung,audio-routing = + audio-routing = "HP", "HPOUT1L", "HP", "HPOUT1R", diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml index c6751c40e63f..b77284e3e26a 100644 --- a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml @@ -10,6 +10,9 @@ maintainers: - Krzysztof Kozlowski <krzk@kernel.org> - Sylwester Nawrocki <s.nawrocki@samsung.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: oneOf: @@ -24,10 +27,6 @@ properties: - const: samsung,odroid-xu4-audio deprecated: true - model: - $ref: /schemas/types.yaml#/definitions/string - description: The user-visible name of this sound complex. - assigned-clock-parents: true assigned-clock-rates: true assigned-clocks: true @@ -52,6 +51,7 @@ properties: samsung,audio-routing: $ref: /schemas/types.yaml#/definitions/non-unique-string-array + deprecated: true description: | List of the connections between audio components; each entry is a pair of strings, the first being the @@ -61,6 +61,7 @@ properties: For Odroid X2: "Headphone Jack", "Mic Jack", "DMIC" For Odroid U3, XU3: "Headphone Jack", "Speakers" For Odroid XU4: no entries + Deprecated, use audio-routing. samsung,audio-widgets: $ref: /schemas/types.yaml#/definitions/non-unique-string-array @@ -70,18 +71,17 @@ properties: required: - compatible - - model - cpu - codec -additionalProperties: false +unevaluatedProperties: false examples: - | sound { compatible = "hardkernel,odroid-xu3-audio"; model = "Odroid-XU3"; - samsung,audio-routing = + audio-routing = "Headphone Jack", "HPL", "Headphone Jack", "HPR", "IN1", "Mic Jack", diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml index 491e08019c04..760592599143 100644 --- a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml +++ b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml @@ -10,6 +10,9 @@ maintainers: - Krzysztof Kozlowski <krzk@kernel.org> - Sylwester Nawrocki <s.nawrocki@samsung.com> +allOf: + - $ref: sound-card-common.yaml# + properties: compatible: const: samsung,tm2-audio @@ -32,6 +35,8 @@ properties: being the connection's source; valid names for sources and sinks are the WM5110's and MAX98504's pins and the jacks on the board: HP, SPK, Main Mic, Sub Mic, Third Mic, Headset Mic. + Deprecated, use audio-routing. + deprecated: true $ref: /schemas/types.yaml#/definitions/non-unique-string-array i2s-controller: @@ -44,20 +49,15 @@ properties: mic-bias-gpios: description: GPIO pin that enables the Main Mic bias regulator. - model: - description: The user-visible name of this sound complex. - $ref: /schemas/types.yaml#/definitions/string - required: - compatible - audio-amplifier - audio-codec - - samsung,audio-routing + - audio-routing - i2s-controller - mic-bias-gpios - - model -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -70,7 +70,7 @@ examples: audio-amplifier = <&max98504>; mic-bias-gpios = <&gpr3 2 GPIO_ACTIVE_HIGH>; model = "wm5110"; - samsung,audio-routing = "HP", "HPOUT1L", + audio-routing = "HP", "HPOUT1L", "HP", "HPOUT1R", "SPK", "SPKOUT", "SPKOUT", "HPOUT2L", diff --git a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml index a970fd264b21..a48d040b0a4f 100644 --- a/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml +++ b/Documentation/devicetree/bindings/sound/snps,designware-i2s.yaml @@ -17,6 +17,9 @@ properties: - const: snps,designware-i2s - enum: - snps,designware-i2s + - starfive,jh7110-i2stx0 + - starfive,jh7110-i2stx1 + - starfive,jh7110-i2srx reg: maxItems: 1 @@ -29,15 +32,36 @@ properties: maxItems: 1 clocks: - description: Sampling rate reference clock - maxItems: 1 + items: + - description: Sampling rate reference clock + - description: APB clock + - description: Audio master clock + - description: Inner audio master clock source + - description: External audio master clock source + - description: Bit clock + - description: Left/right channel clock + - description: External bit clock + - description: External left/right channel clock + minItems: 1 clock-names: - const: i2sclk + items: + - const: i2sclk + - const: apb + - const: mclk + - const: mclk_inner + - const: mclk_ext + - const: bclk + - const: lrck + - const: bclk_ext + - const: lrck_ext + minItems: 1 resets: items: - description: Optional controller resets + - description: controller reset of Sampling rate + minItems: 1 dmas: items: @@ -51,6 +75,17 @@ properties: - const: rx minItems: 1 + starfive,syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller sys_syscon node. + - description: I2S-rx enabled control offset of SYS_SYSCONSAIF__SYSCFG register. + - description: I2S-rx enabled control mask + description: + The phandle to System Register Controller syscon node and the I2S-rx(ADC) + enabled control offset and mask of SYS_SYSCONSAIF__SYSCFG register. + allOf: - $ref: dai-common.yaml# - if: @@ -66,6 +101,73 @@ allOf: properties: "#sound-dai-cells": const: 0 + - if: + properties: + compatible: + contains: + const: snps,designware-i2s + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + resets: + maxItems: 1 + else: + properties: + resets: + minItems: 2 + maxItems: 2 + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2stx0 + then: + properties: + clocks: + minItems: 5 + maxItems: 5 + clock-names: + minItems: 5 + maxItems: 5 + required: + - resets + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2stx1 + then: + properties: + clocks: + minItems: 9 + maxItems: 9 + clock-names: + minItems: 9 + maxItems: 9 + required: + - resets + - if: + properties: + compatible: + contains: + const: starfive,jh7110-i2srx + then: + properties: + clocks: + minItems: 9 + maxItems: 9 + clock-names: + minItems: 9 + maxItems: 9 + required: + - resets + - starfive,syscon + else: + properties: + starfive,syscon: false required: - compatible diff --git a/Documentation/devicetree/bindings/sound/sound-card-common.yaml b/Documentation/devicetree/bindings/sound/sound-card-common.yaml new file mode 100644 index 000000000000..3a941177f684 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/sound-card-common.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/sound-card-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Board Sound Card Common Properties + +maintainers: + - Mark Brown <broonie@kernel.org> + +properties: + audio-routing: + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + description: | + A list of the connections between audio components. Each entry is a + pair of strings, the first being the connection's sink, the second + being the connection's source. + + model: + $ref: /schemas/types.yaml#/definitions/string + description: User specified audio sound card name + +required: + - model + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml index ee8eba7f0104..62e62c335d07 100644 --- a/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml +++ b/Documentation/devicetree/bindings/sound/wlf,wm8960.yaml @@ -26,6 +26,21 @@ properties: '#sound-dai-cells': const: 0 + AVDD-supply: + description: Analogue supply. + + DBVDD-supply: + description: Digital Buffer Supply. + + DCVDD-supply: + description: Digital Core Supply. + + SPKVDD1-supply: + description: Supply for speaker drivers 1. + + SPKVDD2-supply: + description: Supply for speaker drivers 2. + wlf,capless: type: boolean description: @@ -84,5 +99,10 @@ examples: wlf,hp-cfg = <3 2 3>; wlf,gpio-cfg = <1 3>; wlf,shared-lrclk; + DCVDD-supply = <®_audio>; + DBVDD-supply = <®_audio>; + AVDD-supply = <®_audio>; + SPKVDD1-supply = <®_audio>; + SPKVDD2-supply = <®_audio>; }; }; diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index ed148919e11a..0ca479dbb1cd 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -551,9 +551,8 @@ mutex or just to use i_size_read() instead. Note: this does not protect the file->f_pos against concurrent modifications since this is something the userspace has to take care about. -->iterate() is called with i_rwsem exclusive. - -->iterate_shared() is called with i_rwsem at least shared. +->iterate_shared() is called with i_rwsem held for reading, and with the +file f_pos_lock held exclusively ->fasync() is responsible for maintaining the FASYNC bit in filp->f_flags. Most instances call fasync_helper(), which does that maintenance, so it's diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index d2d684ae7798..0f5da78ef4f9 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -537,7 +537,7 @@ vfs_readdir() is gone; switch to iterate_dir() instead **mandatory** -->readdir() is gone now; switch to ->iterate() +->readdir() is gone now; switch to ->iterate_shared() **mandatory** @@ -693,24 +693,19 @@ parallel now. --- -**recommended** +**mandatory** -->iterate_shared() is added; it's a parallel variant of ->iterate(). +->iterate_shared() is added. Exclusion on struct file level is still provided (as well as that between it and lseek on the same struct file), but if your directory has been opened several times, you can get these called in parallel. Exclusion between that method and all directory-modifying ones is still provided, of course. -Often enough ->iterate() can serve as ->iterate_shared() without any -changes - it is a read-only operation, after all. If you have any -per-inode or per-dentry in-core data structures modified by ->iterate(), -you might need something to serialize the access to them. If you -do dcache pre-seeding, you'll need to switch to d_alloc_parallel() for -that; look for in-tree examples. - -Old method is only used if the new one is absent; eventually it will -be removed. Switch while you still can; the old one won't stay. +If you have any per-inode or per-dentry in-core data structures modified +by ->iterate_shared(), you might need something to serialize the access +to them. If you do dcache pre-seeding, you'll need to switch to +d_alloc_parallel() for that; look for in-tree examples. --- @@ -930,9 +925,9 @@ should be done by looking at FMODE_LSEEK in file->f_mode. filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: -callers never looked at specific -E... values anyway. ->iterate() and -->iterate_shared() instance require no changes at all, all filldir_t ones in -the tree converted. +callers never looked at specific -E... values anyway. -> iterate_shared() +instances require no changes at all, all filldir_t ones in the tree +converted. --- diff --git a/MAINTAINERS b/MAINTAINERS index 88174736be51..3aaec9532ff9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2339,7 +2339,7 @@ F: drivers/phy/mediatek/ ARM/MICROCHIP (ARM64) SoC support M: Conor Dooley <conor@kernel.org> M: Nicolas Ferre <nicolas.ferre@microchip.com> -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported T: git https://git.kernel.org/pub/scm/linux/kernel/git/at91/linux.git @@ -2348,7 +2348,7 @@ F: arch/arm64/boot/dts/microchip/ ARM/Microchip (AT91) SoC support M: Nicolas Ferre <nicolas.ferre@microchip.com> M: Alexandre Belloni <alexandre.belloni@bootlin.com> -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported W: http://www.linux4sam.org @@ -3250,7 +3250,7 @@ F: include/uapi/linux/atm* ATMEL MACB ETHERNET DRIVER M: Nicolas Ferre <nicolas.ferre@microchip.com> -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> S: Supported F: drivers/net/ethernet/cadence/ @@ -3262,9 +3262,8 @@ F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml F: drivers/input/touchscreen/atmel_mxt_ts.c ATMEL WIRELESS DRIVER -M: Simon Kelley <simon@thekelleys.org.uk> L: linux-wireless@vger.kernel.org -S: Maintained +S: Orphan W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ F: drivers/net/wireless/atmel/atmel* @@ -3394,7 +3393,7 @@ F: drivers/media/radio/radio-aztech* B43 WIRELESS DRIVER L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org -S: Odd Fixes +S: Orphan W: https://wireless.wiki.kernel.org/en/users/Drivers/b43 F: drivers/net/wireless/broadcom/b43/ @@ -4887,7 +4886,10 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: patches@opensource.cirrus.com S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* +F: drivers/mfd/cs42l43* +F: drivers/pinctrl/cirrus/pinctrl-cs42l43* F: include/dt-bindings/sound/cs* +F: include/linux/mfd/cs42l43* F: include/sound/cs* F: sound/pci/hda/cs* F: sound/pci/hda/hda_cs_dsp_ctl.* @@ -5462,8 +5464,7 @@ F: Documentation/devicetree/bindings/net/can/ctu,ctucanfd.yaml F: drivers/net/can/ctucanfd/ CW1200 WLAN driver -M: Solomon Peachy <pizza@shaftnet.org> -S: Maintained +S: Orphan F: drivers/net/wireless/st/cw1200/ CX18 VIDEO4LINUX DRIVER @@ -9377,7 +9378,6 @@ F: drivers/crypto/hisilicon/sgl.c F: include/linux/hisi_acc_qm.h HISILICON ROCE DRIVER -M: Haoyue Xu <xuhaoyue1@hisilicon.com> M: Junxian Huang <huangjunxian6@hisilicon.com> L: linux-rdma@vger.kernel.org S: Maintained @@ -9662,6 +9662,7 @@ F: tools/hv/ HYPERBUS SUPPORT M: Vignesh Raghavendra <vigneshr@ti.com> +R: Tudor Ambarus <tudor.ambarus@linaro.org> L: linux-mtd@lists.infradead.org S: Supported Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ @@ -12481,6 +12482,7 @@ F: net/mctp/ MAPLE TREE M: Liam R. Howlett <Liam.Howlett@oracle.com> +L: maple-tree@lists.infradead.org L: linux-mm@kvack.org S: Supported F: Documentation/core-api/maple_tree.rst @@ -12592,18 +12594,14 @@ F: Documentation/devicetree/bindings/net/marvell,pp2.yaml F: drivers/net/ethernet/marvell/mvpp2/ MARVELL MWIFIEX WIRELESS DRIVER -M: Amitkumar Karwar <amitkarwar@gmail.com> -M: Ganapathi Bhat <ganapathi017@gmail.com> -M: Sharvari Harisangam <sharvari.harisangam@nxp.com> -M: Xinming Hu <huxinming820@gmail.com> +M: Brian Norris <briannorris@chromium.org> L: linux-wireless@vger.kernel.org -S: Maintained +S: Odd Fixes F: drivers/net/wireless/marvell/mwifiex/ MARVELL MWL8K WIRELESS DRIVER -M: Lennert Buytenhek <buytenh@wantstofly.org> L: linux-wireless@vger.kernel.org -S: Odd Fixes +S: Orphan F: drivers/net/wireless/marvell/mwl8k.c MARVELL NAND CONTROLLER DRIVER @@ -13791,7 +13789,7 @@ F: Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml F: drivers/spi/spi-at91-usart.c MICROCHIP AUDIO ASOC DRIVERS -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/sound/atmel* @@ -13814,7 +13812,7 @@ S: Maintained F: drivers/crypto/atmel-ecc.* MICROCHIP EIC DRIVER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/interrupt-controller/microchip,sama7g5-eic.yaml @@ -13887,7 +13885,7 @@ F: drivers/video/fbdev/atmel_lcdfb.c F: include/video/atmel_lcdc.h MICROCHIP MCP16502 PMIC DRIVER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt @@ -13914,7 +13912,7 @@ F: Documentation/devicetree/bindings/mtd/atmel-nand.txt F: drivers/mtd/nand/raw/atmel/* MICROCHIP OTPC DRIVER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml @@ -13953,7 +13951,7 @@ F: Documentation/devicetree/bindings/fpga/microchip,mpf-spi-fpga-mgr.yaml F: drivers/fpga/microchip-spi.c MICROCHIP PWM DRIVER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-pwm@vger.kernel.org S: Supported @@ -13969,7 +13967,7 @@ F: drivers/iio/adc/at91-sama5d2_adc.c F: include/dt-bindings/iio/adc/at91-sama5d2_adc.h MICROCHIP SAMA5D2-COMPATIBLE SHUTDOWN CONTROLLER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> S: Supported F: Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml F: drivers/power/reset/at91-sama5d2_shdwc.c @@ -13986,7 +13984,7 @@ S: Supported F: drivers/spi/spi-atmel.* MICROCHIP SSC DRIVER -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -14015,7 +14013,7 @@ F: drivers/usb/gadget/udc/atmel_usba_udc.* MICROCHIP WILC1000 WIFI DRIVER M: Ajay Singh <ajay.kathat@microchip.com> -M: Claudiu Beznea <claudiu.beznea@microchip.com> +M: Claudiu Beznea <claudiu.beznea@tuxon.dev> L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/microchip/wilc1000/ @@ -16298,6 +16296,7 @@ F: drivers/pci/controller/dwc/pci-exynos.c PCI DRIVER FOR SYNOPSYS DESIGNWARE M: Jingoo Han <jingoohan1@gmail.com> M: Gustavo Pimentel <gustavo.pimentel@synopsys.com> +M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> L: linux-pci@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml @@ -17449,6 +17448,7 @@ F: drivers/media/tuners/qt1010* QUALCOMM ATH12K WIRELESS DRIVER M: Kalle Valo <kvalo@kernel.org> +M: Jeff Johnson <quic_jjohnson@quicinc.com> L: ath12k@lists.infradead.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git @@ -17456,6 +17456,7 @@ F: drivers/net/wireless/ath/ath12k/ QUALCOMM ATHEROS ATH10K WIRELESS DRIVER M: Kalle Valo <kvalo@kernel.org> +M: Jeff Johnson <quic_jjohnson@quicinc.com> L: ath10k@lists.infradead.org S: Supported W: https://wireless.wiki.kernel.org/en/users/Drivers/ath10k @@ -17465,6 +17466,7 @@ F: drivers/net/wireless/ath/ath10k/ QUALCOMM ATHEROS ATH11K WIRELESS DRIVER M: Kalle Valo <kvalo@kernel.org> +M: Jeff Johnson <quic_jjohnson@quicinc.com> L: ath11k@lists.infradead.org S: Supported W: https://wireless.wiki.kernel.org/en/users/Drivers/ath11k @@ -17985,7 +17987,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.g F: drivers/net/wireless/realtek/rtlwifi/ REALTEK WIRELESS DRIVER (rtw88) -M: Yan-Hsuan Chuang <tony0620emma@gmail.com> +M: Ping-Ke Shih <pkshih@realtek.com> L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/realtek/rtw88/ @@ -18510,17 +18512,14 @@ RTL8180 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan W: https://wireless.wiki.kernel.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git F: drivers/net/wireless/realtek/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER -M: Herton Ronaldo Krzesinski <herton@canonical.com> -M: Hin-Tak Leung <htl10@users.sourceforge.net> +M: Hin-Tak Leung <hintak.leung@gmail.com> M: Larry Finger <Larry.Finger@lwfinger.net> L: linux-wireless@vger.kernel.org S: Maintained W: https://wireless.wiki.kernel.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git F: drivers/net/wireless/realtek/rtl818x/rtl8187/ RTL8XXXU WIRELESS DRIVER (rtl8xxxu) @@ -20404,7 +20403,6 @@ F: drivers/pwm/pwm-stm32* F: include/linux/*/stm32-*tim* STMMAC ETHERNET DRIVER -M: Giuseppe Cavallaro <peppe.cavallaro@st.com> M: Alexandre Torgue <alexandre.torgue@foss.st.com> M: Jose Abreu <joabreu@synopsys.com> L: netdev@vger.kernel.org @@ -21714,11 +21712,14 @@ S: Orphan F: drivers/net/ethernet/dec/tulip/ TUN/TAP driver -M: Maxim Krasnyansky <maxk@qti.qualcomm.com> +M: Willem de Bruijn <willemdebruijn.kernel@gmail.com> +M: Jason Wang <jasowang@redhat.com> S: Maintained W: http://vtun.sourceforge.net/tun F: Documentation/networking/tuntap.rst F: arch/um/os-Linux/drivers/ +F: drivers/net/tap.c +F: drivers/net/tun.c TURBOCHANNEL SUBSYSTEM M: "Maciej W. Rozycki" <macro@orcam.me.uk> @@ -21941,9 +21942,8 @@ S: Maintained F: drivers/usb/misc/apple-mfi-fastcharge.c USB AR5523 WIRELESS DRIVER -M: Pontus Fuchs <pontus.fuchs@gmail.com> L: linux-wireless@vger.kernel.org -S: Maintained +S: Orphan F: drivers/net/wireless/ath/ar5523/ USB ATTACHED SCSI @@ -22220,9 +22220,8 @@ F: drivers/usb/gadget/legacy/webcam.c F: include/uapi/linux/usb/g_uvc.h USB WIRELESS RNDIS DRIVER (rndis_wlan) -M: Jussi Kivilinna <jussi.kivilinna@iki.fi> L: linux-wireless@vger.kernel.org -S: Maintained +S: Orphan F: drivers/net/wireless/legacy/rndis_wlan.c USB XHCI DRIVER @@ -22997,7 +22996,7 @@ F: drivers/input/misc/wistron_btns.c WL3501 WIRELESS PCMCIA CARD DRIVER L: linux-wireless@vger.kernel.org -S: Odd fixes +S: Orphan F: drivers/net/wireless/legacy/wl3501* WMI BINARY MOF DRIVER @@ -23568,11 +23567,8 @@ S: Maintained F: mm/zbud.c ZD1211RW WIRELESS DRIVER -M: Ulrich Kunitz <kune@deine-taler.de> L: linux-wireless@vger.kernel.org -L: zd1211-devs@lists.sourceforge.net (subscribers-only) -S: Maintained -W: http://zd1211.ath.cx/wiki/DriverRewrite +S: Orphan F: drivers/net/wireless/zydas/zd1211rw/ ZD1301 MEDIA DRIVER @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 5 SUBLEVEL = 0 -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc6 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* diff --git a/arch/alpha/include/asm/processor.h b/arch/alpha/include/asm/processor.h index 714abe494e5f..55bb1c09fd39 100644 --- a/arch/alpha/include/asm/processor.h +++ b/arch/alpha/include/asm/processor.h @@ -47,12 +47,6 @@ unsigned long __get_wchan(struct task_struct *p); #define ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCHW -#define ARCH_HAS_SPINLOCK_PREFETCH - -#ifndef CONFIG_SMP -/* Nothing to prefetch. */ -#define spin_lock_prefetch(lock) do { } while (0) -#endif extern inline void prefetch(const void *ptr) { @@ -64,11 +58,4 @@ extern inline void prefetchw(const void *ptr) __builtin_prefetch(ptr, 1, 3); } -#ifdef CONFIG_SMP -extern inline void spin_lock_prefetch(const void *ptr) -{ - __builtin_prefetch(ptr, 1, 3); -} -#endif - #endif /* __ASM_ALPHA_PROCESSOR_H */ diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index b650ff1cb022..3d7473531ab1 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -385,8 +385,7 @@ setup_memory(void *kernel_end) #endif /* CONFIG_BLK_DEV_INITRD */ } -int __init -page_is_ram(unsigned long pfn) +int page_is_ram(unsigned long pfn) { struct memclust_struct * cluster; struct memdesc_struct * memdesc; diff --git a/arch/arm/boot/dts/microchip/sam9x60.dtsi b/arch/arm/boot/dts/microchip/sam9x60.dtsi index 8b53997675e7..73d570a17269 100644 --- a/arch/arm/boot/dts/microchip/sam9x60.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x60.dtsi @@ -172,7 +172,7 @@ status = "disabled"; uart4: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <13 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -240,7 +240,7 @@ status = "disabled"; uart5: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; atmel,usart-mode = <AT91_USART_MODE_SERIAL>; interrupts = <14 IRQ_TYPE_LEVEL_HIGH 7>; @@ -370,7 +370,7 @@ status = "disabled"; uart11: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <32 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -419,7 +419,7 @@ status = "disabled"; uart12: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <33 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -576,7 +576,7 @@ status = "disabled"; uart6: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <9 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -625,7 +625,7 @@ status = "disabled"; uart7: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <10 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -674,7 +674,7 @@ status = "disabled"; uart8: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <11 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -723,7 +723,7 @@ status = "disabled"; uart0: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <5 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -791,7 +791,7 @@ status = "disabled"; uart1: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <6 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -859,7 +859,7 @@ status = "disabled"; uart2: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <7 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -927,7 +927,7 @@ status = "disabled"; uart3: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <8 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -1050,7 +1050,7 @@ status = "disabled"; uart9: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <15 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 @@ -1099,7 +1099,7 @@ status = "disabled"; uart10: serial@200 { - compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; reg = <0x200 0x200>; interrupts = <16 IRQ_TYPE_LEVEL_HIGH 7>; dmas = <&dma0 diff --git a/arch/arm/boot/dts/nspire/nspire.dtsi b/arch/arm/boot/dts/nspire/nspire.dtsi index bb240e6a3a6f..088bcc38589f 100644 --- a/arch/arm/boot/dts/nspire/nspire.dtsi +++ b/arch/arm/boot/dts/nspire/nspire.dtsi @@ -161,7 +161,7 @@ }; watchdog: watchdog@90060000 { - compatible = "arm,amba-primecell"; + compatible = "arm,primecell"; reg = <0x90060000 0x1000>; interrupts = <3>; }; diff --git a/arch/arm/boot/dts/nxp/imx/imx53-sk-imx53.dts b/arch/arm/boot/dts/nxp/imx/imx53-sk-imx53.dts index 103e73176e47..1a00d290092a 100644 --- a/arch/arm/boot/dts/nxp/imx/imx53-sk-imx53.dts +++ b/arch/arm/boot/dts/nxp/imx/imx53-sk-imx53.dts @@ -60,6 +60,16 @@ status = "okay"; }; +&cpu0 { + /* CPU rated to 800 MHz, not the default 1.2GHz. */ + operating-points = < + /* kHz uV */ + 166666 850000 + 400000 900000 + 800000 1050000 + >; +}; + &ecspi1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi1>; diff --git a/arch/arm/boot/dts/nxp/imx/imx6sll.dtsi b/arch/arm/boot/dts/nxp/imx/imx6sll.dtsi index 2873369a57c0..3659fd5ecfa6 100644 --- a/arch/arm/boot/dts/nxp/imx/imx6sll.dtsi +++ b/arch/arm/boot/dts/nxp/imx/imx6sll.dtsi @@ -552,7 +552,7 @@ reg = <0x020ca000 0x1000>; interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6SLL_CLK_USBPHY2>; - phy-reg_3p0-supply = <®_3p0>; + phy-3p0-supply = <®_3p0>; fsl,anatop = <&anatop>; }; diff --git a/arch/arm/mach-pxa/sharpsl_pm.h b/arch/arm/mach-pxa/sharpsl_pm.h index 20e4cab64d85..623167f30ec2 100644 --- a/arch/arm/mach-pxa/sharpsl_pm.h +++ b/arch/arm/mach-pxa/sharpsl_pm.h @@ -105,5 +105,4 @@ void sharpsl_pm_led(int val); #define MAX1111_ACIN_VOLT 6u int sharpsl_pm_pxa_read_max1111(int channel); -void corgi_lcd_limit_intensity(int limit); #endif diff --git a/arch/arm/mach-pxa/spitz_pm.c b/arch/arm/mach-pxa/spitz_pm.c index 1c021cef965f..8bc4ea51a0c1 100644 --- a/arch/arm/mach-pxa/spitz_pm.c +++ b/arch/arm/mach-pxa/spitz_pm.c @@ -15,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/apm-emulation.h> +#include <linux/spi/corgi_lcd.h> #include <asm/irq.h> #include <asm/mach-types.h> diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts index 38ae674f2f02..3037f58057c9 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk.dts @@ -145,7 +145,7 @@ status = "okay"; clock-frequency = <100000>; i2c-sda-falling-time-ns = <890>; /* hcnt */ - i2c-sdl-falling-time-ns = <890>; /* lcnt */ + i2c-scl-falling-time-ns = <890>; /* lcnt */ pinctrl-names = "default", "gpio"; pinctrl-0 = <&i2c1_pmx_func>; diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_nand.dts b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_nand.dts index ede99dcc0558..f4cf30bac557 100644 --- a/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_nand.dts +++ b/arch/arm64/boot/dts/altera/socfpga_stratix10_socdk_nand.dts @@ -141,7 +141,7 @@ status = "okay"; clock-frequency = <100000>; i2c-sda-falling-time-ns = <890>; /* hcnt */ - i2c-sdl-falling-time-ns = <890>; /* lcnt */ + i2c-scl-falling-time-ns = <890>; /* lcnt */ adc@14 { compatible = "lltc,ltc2497"; diff --git a/arch/arm64/boot/dts/arm/vexpress-v2m-rs1.dtsi b/arch/arm64/boot/dts/arm/vexpress-v2m-rs1.dtsi deleted file mode 120000 index 68fd0f8f1dee..000000000000 --- a/arch/arm64/boot/dts/arm/vexpress-v2m-rs1.dtsi +++ /dev/null @@ -1 +0,0 @@ -../../../../arm/boot/dts/vexpress-v2m-rs1.dtsi
\ No newline at end of file diff --git a/arch/arm64/boot/dts/freescale/imx8mm-phyboard-polis-rdk.dts b/arch/arm64/boot/dts/freescale/imx8mm-phyboard-polis-rdk.dts index 03e7679217b2..479948f8a4b7 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-phyboard-polis-rdk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-phyboard-polis-rdk.dts @@ -141,7 +141,7 @@ }; &gpio1 { - gpio-line-names = "nINT_ETHPHY", "LED_RED", "WDOG_INT", "X_RTC_INT", + gpio-line-names = "", "LED_RED", "WDOG_INT", "X_RTC_INT", "", "", "", "RESET_ETHPHY", "CAN_nINT", "CAN_EN", "nENABLE_FLATLINK", "", "USB_OTG_VBUS_EN", "", "LED_GREEN", "LED_BLUE"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-phycore-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-phycore-som.dtsi index 92616bc4f71f..847f08537b48 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-phycore-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-phycore-som.dtsi @@ -111,7 +111,7 @@ }; &gpio1 { - gpio-line-names = "nINT_ETHPHY", "", "WDOG_INT", "X_RTC_INT", + gpio-line-names = "", "", "WDOG_INT", "X_RTC_INT", "", "", "", "RESET_ETHPHY", "", "", "nENABLE_FLATLINK"; }; @@ -210,7 +210,7 @@ }; }; - reg_vdd_gpu: buck3 { + reg_vdd_vpu: buck3 { regulator-always-on; regulator-boot-on; regulator-max-microvolt = <1000000>; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts index 6f26914602c8..07b07dc954fd 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts @@ -567,6 +567,10 @@ status = "okay"; }; +&disp_blk_ctrl { + status = "disabled"; +}; + &pgc_mipi { status = "disabled"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts index 93088fa1c3b9..d5b716855812 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7904.dts @@ -628,6 +628,10 @@ status = "okay"; }; +&disp_blk_ctrl { + status = "disabled"; +}; + &pgc_mipi { status = "disabled"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi index d3a67109d55b..b8946edf317b 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn-var-som.dtsi @@ -358,7 +358,7 @@ MX8MN_IOMUXC_ENET_RXC_ENET1_RGMII_RXC 0x91 MX8MN_IOMUXC_ENET_RX_CTL_ENET1_RGMII_RX_CTL 0x91 MX8MN_IOMUXC_ENET_TX_CTL_ENET1_RGMII_TX_CTL 0x1f - MX8MN_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x19 + MX8MN_IOMUXC_GPIO1_IO09_GPIO1_IO9 0x159 >; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 1a2d2c04db32..01eec424f7f7 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -772,7 +772,7 @@ <&clk IMX8MQ_SYS1_PLL_800M>, <&clk IMX8MQ_VPU_PLL>; assigned-clock-rates = <600000000>, - <600000000>, + <300000000>, <800000000>, <0>; }; diff --git a/arch/arm64/boot/dts/renesas/r9a07g044.dtsi b/arch/arm64/boot/dts/renesas/r9a07g044.dtsi index 232910e07444..66f68fc2b241 100644 --- a/arch/arm64/boot/dts/renesas/r9a07g044.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a07g044.dtsi @@ -223,20 +223,20 @@ <GIC_SPI 212 IRQ_TYPE_EDGE_RISING>, <GIC_SPI 213 IRQ_TYPE_EDGE_RISING>; interrupt-names = "tgia0", "tgib0", "tgic0", "tgid0", - "tgiv0", "tgie0", "tgif0", - "tgia1", "tgib1", "tgiv1", "tgiu1", - "tgia2", "tgib2", "tgiv2", "tgiu2", + "tciv0", "tgie0", "tgif0", + "tgia1", "tgib1", "tciv1", "tciu1", + "tgia2", "tgib2", "tciv2", "tciu2", "tgia3", "tgib3", "tgic3", "tgid3", - "tgiv3", + "tciv3", "tgia4", "tgib4", "tgic4", "tgid4", - "tgiv4", + "tciv4", "tgiu5", "tgiv5", "tgiw5", "tgia6", "tgib6", "tgic6", "tgid6", - "tgiv6", + "tciv6", "tgia7", "tgib7", "tgic7", "tgid7", - "tgiv7", + "tciv7", "tgia8", "tgib8", "tgic8", "tgid8", - "tgiv8", "tgiu8"; + "tciv8", "tciu8"; clocks = <&cpg CPG_MOD R9A07G044_MTU_X_MCK_MTU3>; power-domains = <&cpg>; resets = <&cpg R9A07G044_MTU_X_PRESET_MTU3>; diff --git a/arch/arm64/boot/dts/renesas/r9a07g054.dtsi b/arch/arm64/boot/dts/renesas/r9a07g054.dtsi index 2eba3a8a100d..1f1d481dc783 100644 --- a/arch/arm64/boot/dts/renesas/r9a07g054.dtsi +++ b/arch/arm64/boot/dts/renesas/r9a07g054.dtsi @@ -223,20 +223,20 @@ <GIC_SPI 212 IRQ_TYPE_EDGE_RISING>, <GIC_SPI 213 IRQ_TYPE_EDGE_RISING>; interrupt-names = "tgia0", "tgib0", "tgic0", "tgid0", - "tgiv0", "tgie0", "tgif0", - "tgia1", "tgib1", "tgiv1", "tgiu1", - "tgia2", "tgib2", "tgiv2", "tgiu2", + "tciv0", "tgie0", "tgif0", + "tgia1", "tgib1", "tciv1", "tciu1", + "tgia2", "tgib2", "tciv2", "tciu2", "tgia3", "tgib3", "tgic3", "tgid3", - "tgiv3", + "tciv3", "tgia4", "tgib4", "tgic4", "tgid4", - "tgiv4", + "tciv4", "tgiu5", "tgiv5", "tgiw5", "tgia6", "tgib6", "tgic6", "tgid6", - "tgiv6", + "tciv6", "tgia7", "tgib7", "tgic7", "tgid7", - "tgiv7", + "tciv7", "tgia8", "tgib8", "tgic8", "tgid8", - "tgiv8", "tgiu8"; + "tciv8", "tciu8"; clocks = <&cpg CPG_MOD R9A07G054_MTU_X_MCK_MTU3>; power-domains = <&cpg>; resets = <&cpg R9A07G054_MTU_X_PRESET_MTU3>; diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index 8e5ffb58f83e..b7afaa026842 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -31,6 +31,13 @@ .Lskip_hcrx_\@: .endm +/* Check if running in host at EL2 mode, i.e., (h)VHE. Jump to fail if not. */ +.macro __check_hvhe fail, tmp + mrs \tmp, hcr_el2 + and \tmp, \tmp, #HCR_E2H + cbz \tmp, \fail +.endm + /* * Allow Non-secure EL1 and EL0 to access physical timer and counter. * This is not necessary for VHE, since the host kernel runs in EL2, @@ -43,9 +50,7 @@ */ .macro __init_el2_timers mov x0, #3 // Enable EL1 physical timers - mrs x1, hcr_el2 - and x1, x1, #HCR_E2H - cbz x1, .LnVHE_\@ + __check_hvhe .LnVHE_\@, x1 lsl x0, x0, #10 .LnVHE_\@: msr cnthctl_el2, x0 @@ -139,15 +144,14 @@ /* Coprocessor traps */ .macro __init_el2_cptr - mrs x1, hcr_el2 - and x1, x1, #HCR_E2H - cbz x1, .LnVHE_\@ + __check_hvhe .LnVHE_\@, x1 mov x0, #(CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN) - b .Lset_cptr_\@ + msr cpacr_el1, x0 + b .Lskip_set_cptr_\@ .LnVHE_\@: mov x0, #0x33ff -.Lset_cptr_\@: msr cptr_el2, x0 // Disable copro. traps to EL2 +.Lskip_set_cptr_\@: .endm /* Disable any fine grained traps */ @@ -268,19 +272,19 @@ check_override id_aa64pfr0, ID_AA64PFR0_EL1_SVE_SHIFT, .Linit_sve_\@, .Lskip_sve_\@, x1, x2 .Linit_sve_\@: /* SVE register access */ - mrs x0, cptr_el2 // Disable SVE traps - mrs x1, hcr_el2 - and x1, x1, #HCR_E2H - cbz x1, .Lcptr_nvhe_\@ + __check_hvhe .Lcptr_nvhe_\@, x1 - // VHE case + // (h)VHE case + mrs x0, cpacr_el1 // Disable SVE traps orr x0, x0, #(CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN) - b .Lset_cptr_\@ + msr cpacr_el1, x0 + b .Lskip_set_cptr_\@ .Lcptr_nvhe_\@: // nVHE case + mrs x0, cptr_el2 // Disable SVE traps bic x0, x0, #CPTR_EL2_TZ -.Lset_cptr_\@: msr cptr_el2, x0 +.Lskip_set_cptr_\@: isb mov x1, #ZCR_ELx_LEN_MASK // SVE: Enable full vector msr_s SYS_ZCR_EL2, x1 // length for EL1. @@ -289,9 +293,19 @@ check_override id_aa64pfr1, ID_AA64PFR1_EL1_SME_SHIFT, .Linit_sme_\@, .Lskip_sme_\@, x1, x2 .Linit_sme_\@: /* SME register access and priority mapping */ + __check_hvhe .Lcptr_nvhe_sme_\@, x1 + + // (h)VHE case + mrs x0, cpacr_el1 // Disable SME traps + orr x0, x0, #(CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN) + msr cpacr_el1, x0 + b .Lskip_set_cptr_sme_\@ + +.Lcptr_nvhe_sme_\@: // nVHE case mrs x0, cptr_el2 // Disable SME traps bic x0, x0, #CPTR_EL2_TSM msr cptr_el2, x0 +.Lskip_set_cptr_sme_\@: isb mrs x1, sctlr_el2 diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 7d170aaa2db4..24e28bb2d95b 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -278,7 +278,7 @@ asmlinkage void __noreturn hyp_panic_bad_stack(void); asmlinkage void kvm_unexpected_el2_exception(void); struct kvm_cpu_context; void handle_trap(struct kvm_cpu_context *host_ctxt); -asmlinkage void __noreturn kvm_host_psci_cpu_entry(bool is_cpu_on); +asmlinkage void __noreturn __kvm_host_psci_cpu_entry(bool is_cpu_on); void __noreturn __pkvm_init_finalise(void); void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc); void kvm_patch_vector_branch(struct alt_instr *alt, diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index efc0b45d79c3..3d6725ff0bf6 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -571,6 +571,14 @@ static inline bool vcpu_has_feature(struct kvm_vcpu *vcpu, int feature) return test_bit(feature, vcpu->arch.features); } +static __always_inline void kvm_write_cptr_el2(u64 val) +{ + if (has_vhe() || has_hvhe()) + write_sysreg(val, cpacr_el1); + else + write_sysreg(val, cptr_el2); +} + static __always_inline u64 kvm_get_reset_cptr_el2(struct kvm_vcpu *vcpu) { u64 val; @@ -578,8 +586,16 @@ static __always_inline u64 kvm_get_reset_cptr_el2(struct kvm_vcpu *vcpu) if (has_vhe()) { val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN | CPACR_EL1_ZEN_EL1EN); + if (cpus_have_final_cap(ARM64_SME)) + val |= CPACR_EL1_SMEN_EL1EN; } else if (has_hvhe()) { val = (CPACR_EL1_FPEN_EL0EN | CPACR_EL1_FPEN_EL1EN); + + if (!vcpu_has_sve(vcpu) || + (vcpu->arch.fp_state != FP_STATE_GUEST_OWNED)) + val |= CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN; + if (cpus_have_final_cap(ARM64_SME)) + val |= CPACR_EL1_SMEN_EL1EN | CPACR_EL1_SMEN_EL0EN; } else { val = CPTR_NVHE_EL2_RES1; @@ -597,9 +613,6 @@ static __always_inline void kvm_reset_cptr_el2(struct kvm_vcpu *vcpu) { u64 val = kvm_get_reset_cptr_el2(vcpu); - if (has_vhe() || has_hvhe()) - write_sysreg(val, cpacr_el1); - else - write_sysreg(val, cptr_el2); + kvm_write_cptr_el2(val); } #endif /* __ARM64_KVM_EMULATE_H__ */ diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 3918f2a67970..e5bc54522e71 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -359,14 +359,6 @@ static inline void prefetchw(const void *ptr) asm volatile("prfm pstl1keep, %a0\n" : : "p" (ptr)); } -#define ARCH_HAS_SPINLOCK_PREFETCH -static inline void spin_lock_prefetch(const void *ptr) -{ - asm volatile(ARM64_LSE_ATOMIC_INSN( - "prfm pstl1strm, %a0", - "nop") : : "p" (ptr)); -} - extern unsigned long __ro_after_init signal_minsigstksz; /* sigframe size */ extern void __init minsigstksz_setup(void); diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 520b681a07bb..75c37b1c55aa 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -679,7 +679,7 @@ static void fpsimd_to_sve(struct task_struct *task) void *sst = task->thread.sve_state; struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state; - if (!system_supports_sve()) + if (!system_supports_sve() && !system_supports_sme()) return; vq = sve_vq_from_vl(thread_get_cur_vl(&task->thread)); @@ -705,7 +705,7 @@ static void sve_to_fpsimd(struct task_struct *task) unsigned int i; __uint128_t const *p; - if (!system_supports_sve()) + if (!system_supports_sve() && !system_supports_sme()) return; vl = thread_get_cur_vl(&task->thread); @@ -835,7 +835,8 @@ void sve_sync_from_fpsimd_zeropad(struct task_struct *task) void *sst = task->thread.sve_state; struct user_fpsimd_state const *fst = &task->thread.uw.fpsimd_state; - if (!test_tsk_thread_flag(task, TIF_SVE)) + if (!test_tsk_thread_flag(task, TIF_SVE) && + !thread_sm_enabled(&task->thread)) return; vq = sve_vq_from_vl(thread_get_cur_vl(&task->thread)); @@ -909,7 +910,7 @@ int vec_set_vector_length(struct task_struct *task, enum vec_type type, */ task->thread.svcr &= ~(SVCR_SM_MASK | SVCR_ZA_MASK); - clear_thread_flag(TIF_SME); + clear_tsk_thread_flag(task, TIF_SME); free_sme = true; } } diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index d7f4f0d1ae12..5b9b4305248b 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -932,11 +932,13 @@ static int sve_set_common(struct task_struct *target, /* * Ensure target->thread.sve_state is up to date with target's * FPSIMD regs, so that a short copyin leaves trailing - * registers unmodified. Always enable SVE even if going into - * streaming mode. + * registers unmodified. Only enable SVE if we are + * configuring normal SVE, a system with streaming SVE may not + * have normal SVE. */ fpsimd_sync_to_sve(target); - set_tsk_thread_flag(target, TIF_SVE); + if (type == ARM64_VEC_SVE) + set_tsk_thread_flag(target, TIF_SVE); target->thread.fp_type = FP_STATE_SVE; BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); @@ -1180,6 +1182,8 @@ static int zt_set(struct task_struct *target, if (ret == 0) target->thread.svcr |= SVCR_ZA_MASK; + fpsimd_flush_task_state(target); + return ret; } diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 72dc53a75d1c..d1cb298a58a0 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -55,7 +55,7 @@ DECLARE_KVM_NVHE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt); static bool vgic_present, kvm_arm_initialised; -static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled); +static DEFINE_PER_CPU(unsigned char, kvm_hyp_initialized); DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use); bool is_kvm_arm_initialised(void) @@ -1864,18 +1864,24 @@ static void cpu_hyp_reinit(void) cpu_hyp_init_features(); } -static void _kvm_arch_hardware_enable(void *discard) +static void cpu_hyp_init(void *discard) { - if (!__this_cpu_read(kvm_arm_hardware_enabled)) { + if (!__this_cpu_read(kvm_hyp_initialized)) { cpu_hyp_reinit(); - __this_cpu_write(kvm_arm_hardware_enabled, 1); + __this_cpu_write(kvm_hyp_initialized, 1); } } -int kvm_arch_hardware_enable(void) +static void cpu_hyp_uninit(void *discard) { - int was_enabled; + if (__this_cpu_read(kvm_hyp_initialized)) { + cpu_hyp_reset(); + __this_cpu_write(kvm_hyp_initialized, 0); + } +} +int kvm_arch_hardware_enable(void) +{ /* * Most calls to this function are made with migration * disabled, but not with preemption disabled. The former is @@ -1884,36 +1890,23 @@ int kvm_arch_hardware_enable(void) */ preempt_disable(); - was_enabled = __this_cpu_read(kvm_arm_hardware_enabled); - _kvm_arch_hardware_enable(NULL); + cpu_hyp_init(NULL); - if (!was_enabled) { - kvm_vgic_cpu_up(); - kvm_timer_cpu_up(); - } + kvm_vgic_cpu_up(); + kvm_timer_cpu_up(); preempt_enable(); return 0; } -static void _kvm_arch_hardware_disable(void *discard) -{ - if (__this_cpu_read(kvm_arm_hardware_enabled)) { - cpu_hyp_reset(); - __this_cpu_write(kvm_arm_hardware_enabled, 0); - } -} - void kvm_arch_hardware_disable(void) { - if (__this_cpu_read(kvm_arm_hardware_enabled)) { - kvm_timer_cpu_down(); - kvm_vgic_cpu_down(); - } + kvm_timer_cpu_down(); + kvm_vgic_cpu_down(); if (!is_protected_kvm_enabled()) - _kvm_arch_hardware_disable(NULL); + cpu_hyp_uninit(NULL); } #ifdef CONFIG_CPU_PM @@ -1922,16 +1915,16 @@ static int hyp_init_cpu_pm_notifier(struct notifier_block *self, void *v) { /* - * kvm_arm_hardware_enabled is left with its old value over + * kvm_hyp_initialized is left with its old value over * PM_ENTER->PM_EXIT. It is used to indicate PM_EXIT should * re-enable hyp. */ switch (cmd) { case CPU_PM_ENTER: - if (__this_cpu_read(kvm_arm_hardware_enabled)) + if (__this_cpu_read(kvm_hyp_initialized)) /* - * don't update kvm_arm_hardware_enabled here - * so that the hardware will be re-enabled + * don't update kvm_hyp_initialized here + * so that the hyp will be re-enabled * when we resume. See below. */ cpu_hyp_reset(); @@ -1939,8 +1932,8 @@ static int hyp_init_cpu_pm_notifier(struct notifier_block *self, return NOTIFY_OK; case CPU_PM_ENTER_FAILED: case CPU_PM_EXIT: - if (__this_cpu_read(kvm_arm_hardware_enabled)) - /* The hardware was enabled before suspend. */ + if (__this_cpu_read(kvm_hyp_initialized)) + /* The hyp was enabled before suspend. */ cpu_hyp_reinit(); return NOTIFY_OK; @@ -2021,7 +2014,7 @@ static int __init init_subsystems(void) /* * Enable hardware so that subsystem initialisation can access EL2. */ - on_each_cpu(_kvm_arch_hardware_enable, NULL, 1); + on_each_cpu(cpu_hyp_init, NULL, 1); /* * Register CPU lower-power notifier @@ -2059,7 +2052,7 @@ out: hyp_cpu_pm_exit(); if (err || !is_protected_kvm_enabled()) - on_each_cpu(_kvm_arch_hardware_disable, NULL, 1); + on_each_cpu(cpu_hyp_uninit, NULL, 1); return err; } @@ -2097,7 +2090,7 @@ static int __init do_pkvm_init(u32 hyp_va_bits) * The stub hypercalls are now disabled, so set our local flag to * prevent a later re-init attempt in kvm_arch_hardware_enable(). */ - __this_cpu_write(kvm_arm_hardware_enabled, 1); + __this_cpu_write(kvm_hyp_initialized, 1); preempt_enable(); return ret; diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 4bddb8541bec..34f222af6165 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -457,6 +457,7 @@ static bool handle_ampere1_tcr(struct kvm_vcpu *vcpu) */ val &= ~(TCR_HD | TCR_HA); write_sysreg_el1(val, SYS_TCR); + __kvm_skip_instr(vcpu); return true; } diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 58dcd92bf346..ab4f5d160c58 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -705,7 +705,20 @@ int hyp_ffa_init(void *pages) if (res.a0 == FFA_RET_NOT_SUPPORTED) return 0; - if (res.a0 != FFA_VERSION_1_0) + /* + * Firmware returns the maximum supported version of the FF-A + * implementation. Check that the returned version is + * backwards-compatible with the hyp according to the rules in DEN0077A + * v1.1 REL0 13.2.1. + * + * Of course, things are never simple when dealing with firmware. v1.1 + * broke ABI with v1.0 on several structures, which is itself + * incompatible with the aforementioned versioning scheme. The + * expectation is that v1.x implementations that do not support the v1.0 + * ABI return NOT_SUPPORTED rather than a version number, according to + * DEN0077A v1.1 REL0 18.6.4. + */ + if (FFA_MAJOR_VERSION(res.a0) != 1) return -EOPNOTSUPP; arm_smccc_1_1_smc(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0, &res); diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 0a6271052def..e89a23153e85 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -63,7 +63,7 @@ static void __activate_traps(struct kvm_vcpu *vcpu) __activate_traps_fpsimd32(vcpu); } - write_sysreg(val, cptr_el2); + kvm_write_cptr_el2(val); write_sysreg(__this_cpu_read(kvm_hyp_vector), vbar_el2); if (cpus_have_final_cap(ARM64_WORKAROUND_SPECULATIVE_AT)) { diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h index d1978e004054..47e3801b526a 100644 --- a/arch/ia64/include/asm/processor.h +++ b/arch/ia64/include/asm/processor.h @@ -634,7 +634,6 @@ ia64_imva (void *addr) #define ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCHW -#define ARCH_HAS_SPINLOCK_PREFETCH #define PREFETCH_STRIDE L1_CACHE_BYTES static inline void @@ -649,8 +648,6 @@ prefetchw (const void *x) ia64_lfetch_excl(ia64_lfhint_none, x); } -#define spin_lock_prefetch(x) prefetchw(x) - extern unsigned long boot_option_idle_override; enum idle_boot_override {IDLE_NO_OVERRIDE=0, IDLE_HALT, IDLE_FORCE_MWAIT, diff --git a/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h b/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h index 9151dcd9d0d5..af9cea21c853 100644 --- a/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h +++ b/arch/mips/include/asm/mach-cavium-octeon/cpu-feature-overrides.h @@ -58,8 +58,6 @@ #define cpu_has_rixi (cpu_data[0].cputype != CPU_CAVIUM_OCTEON) -#define ARCH_HAS_SPINLOCK_PREFETCH 1 -#define spin_lock_prefetch(x) prefetch(x) #define PREFETCH_STRIDE 128 #ifdef __OCTEON__ diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug index 1401e4c5fe5f..bf2b21b96f0b 100644 --- a/arch/parisc/Kconfig.debug +++ b/arch/parisc/Kconfig.debug @@ -2,7 +2,7 @@ # config LIGHTWEIGHT_SPINLOCK_CHECK bool "Enable lightweight spinlock checks" - depends on SMP && !DEBUG_SPINLOCK + depends on DEBUG_KERNEL && SMP && !DEBUG_SPINLOCK default y help Add checks with low performance impact to the spinlock functions diff --git a/arch/parisc/boot/compressed/misc.c b/arch/parisc/boot/compressed/misc.c index 7ee49f5881d1..d389359e22ac 100644 --- a/arch/parisc/boot/compressed/misc.c +++ b/arch/parisc/boot/compressed/misc.c @@ -117,7 +117,7 @@ char *strchr(const char *s, int c) return NULL; } -int puts(const char *s) +static int puts(const char *s) { const char *nuline = s; @@ -172,7 +172,7 @@ static int print_num(unsigned long num, int base) return 0; } -int printf(const char *fmt, ...) +static int printf(const char *fmt, ...) { va_list args; int i = 0; @@ -204,13 +204,13 @@ void abort(void) } #undef malloc -void *malloc(size_t size) +static void *malloc(size_t size) { return malloc_gzip(size); } #undef free -void free(void *ptr) +static void free(void *ptr) { return free_gzip(ptr); } @@ -278,7 +278,7 @@ static void parse_elf(void *output) free(phdrs); } -unsigned long decompress_kernel(unsigned int started_wide, +asmlinkage unsigned long __visible decompress_kernel(unsigned int started_wide, unsigned int command_line, const unsigned int rd_start, const unsigned int rd_end) diff --git a/arch/parisc/include/asm/dma.h b/arch/parisc/include/asm/dma.h index 9e8c101de902..582fb5d1a5d5 100644 --- a/arch/parisc/include/asm/dma.h +++ b/arch/parisc/include/asm/dma.h @@ -14,6 +14,8 @@ #define dma_outb outb #define dma_inb inb +extern unsigned long pcxl_dma_start; + /* ** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up ** (or rather not merge) DMAs into manageable chunks. diff --git a/arch/parisc/include/asm/ftrace.h b/arch/parisc/include/asm/ftrace.h index a7cf0d05ccf4..f1cc1ee3a647 100644 --- a/arch/parisc/include/asm/ftrace.h +++ b/arch/parisc/include/asm/ftrace.h @@ -12,6 +12,10 @@ extern void mcount(void); extern unsigned long sys_call_table[]; extern unsigned long return_address(unsigned int); +struct ftrace_regs; +extern void ftrace_function_trampoline(unsigned long parent, + unsigned long self_addr, unsigned long org_sp_gr3, + struct ftrace_regs *fregs); #ifdef CONFIG_DYNAMIC_FTRACE extern void ftrace_caller(void); diff --git a/arch/parisc/include/asm/spinlock.h b/arch/parisc/include/asm/spinlock.h index edfcb9858bcb..0b326e52255e 100644 --- a/arch/parisc/include/asm/spinlock.h +++ b/arch/parisc/include/asm/spinlock.h @@ -7,8 +7,6 @@ #include <asm/processor.h> #include <asm/spinlock_types.h> -#define SPINLOCK_BREAK_INSN 0x0000c006 /* break 6,6 */ - static inline void arch_spin_val_check(int lock_val) { if (IS_ENABLED(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK)) diff --git a/arch/parisc/include/asm/spinlock_types.h b/arch/parisc/include/asm/spinlock_types.h index d65934079ebd..efd06a897c6a 100644 --- a/arch/parisc/include/asm/spinlock_types.h +++ b/arch/parisc/include/asm/spinlock_types.h @@ -4,6 +4,10 @@ #define __ARCH_SPIN_LOCK_UNLOCKED_VAL 0x1a46 +#define SPINLOCK_BREAK_INSN 0x0000c006 /* break 6,6 */ + +#ifndef __ASSEMBLY__ + typedef struct { #ifdef CONFIG_PA20 volatile unsigned int slock; @@ -27,6 +31,8 @@ typedef struct { volatile unsigned int counter; } arch_rwlock_t; +#endif /* __ASSEMBLY__ */ + #define __ARCH_RW_LOCK_UNLOCKED__ 0x01000000 #define __ARCH_RW_LOCK_UNLOCKED { .lock_mutex = __ARCH_SPIN_LOCK_UNLOCKED, \ .counter = __ARCH_RW_LOCK_UNLOCKED__ } diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index 6d1c781eb1db..8f37e75f2fb9 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -74,8 +74,8 @@ static DEFINE_SPINLOCK(pdc_lock); #endif -unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8); -unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); +static unsigned long pdc_result[NUM_PDC_RESULT] __aligned(8); +static unsigned long pdc_result2[NUM_PDC_RESULT] __aligned(8); #ifdef CONFIG_64BIT #define WIDE_FIRMWARE 0x1 @@ -334,7 +334,7 @@ int __pdc_cpu_rendezvous(void) /** * pdc_cpu_rendezvous_lock - Lock PDC while transitioning to rendezvous state */ -void pdc_cpu_rendezvous_lock(void) +void pdc_cpu_rendezvous_lock(void) __acquires(&pdc_lock) { spin_lock(&pdc_lock); } @@ -342,7 +342,7 @@ void pdc_cpu_rendezvous_lock(void) /** * pdc_cpu_rendezvous_unlock - Unlock PDC after reaching rendezvous state */ -void pdc_cpu_rendezvous_unlock(void) +void pdc_cpu_rendezvous_unlock(void) __releases(&pdc_lock) { spin_unlock(&pdc_lock); } diff --git a/arch/parisc/kernel/ftrace.c b/arch/parisc/kernel/ftrace.c index 4d392e4ed358..d1defb9ede70 100644 --- a/arch/parisc/kernel/ftrace.c +++ b/arch/parisc/kernel/ftrace.c @@ -53,7 +53,7 @@ static void __hot prepare_ftrace_return(unsigned long *parent, static ftrace_func_t ftrace_func; -void notrace __hot ftrace_function_trampoline(unsigned long parent, +asmlinkage void notrace __hot ftrace_function_trampoline(unsigned long parent, unsigned long self_addr, unsigned long org_sp_gr3, struct ftrace_regs *fregs) diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c index 00297e8e1c88..6f0c92e8149d 100644 --- a/arch/parisc/kernel/parisc_ksyms.c +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/syscalls.h> +#include <linux/libgcc.h> #include <linux/string.h> EXPORT_SYMBOL(memset); @@ -92,12 +93,6 @@ EXPORT_SYMBOL($$divI_12); EXPORT_SYMBOL($$divI_14); EXPORT_SYMBOL($$divI_15); -extern void __ashrdi3(void); -extern void __ashldi3(void); -extern void __lshrdi3(void); -extern void __muldi3(void); -extern void __ucmpdi2(void); - EXPORT_SYMBOL(__ashrdi3); EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__lshrdi3); diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c index d818ece23b4a..bf9f192c826e 100644 --- a/arch/parisc/kernel/pci-dma.c +++ b/arch/parisc/kernel/pci-dma.c @@ -39,7 +39,7 @@ static struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; static unsigned long pcxl_used_bytes __read_mostly; static unsigned long pcxl_used_pages __read_mostly; -extern unsigned long pcxl_dma_start; /* Start of pcxl dma mapping area */ +unsigned long pcxl_dma_start __ro_after_init; /* pcxl dma mapping area start */ static DEFINE_SPINLOCK(pcxl_res_lock); static char *pcxl_res_map; static int pcxl_res_hint; @@ -381,7 +381,7 @@ pcxl_dma_init(void) pcxl_res_map = (char *)__get_free_pages(GFP_KERNEL, get_order(pcxl_res_size)); memset(pcxl_res_map, 0, pcxl_res_size); - proc_gsc_root = proc_mkdir("gsc", NULL); + proc_gsc_root = proc_mkdir("bus/gsc", NULL); if (!proc_gsc_root) printk(KERN_WARNING "pcxl_dma_init: Unable to create gsc /proc dir entry\n"); @@ -417,14 +417,6 @@ void *arch_dma_alloc(struct device *dev, size_t size, map_uncached_pages(vaddr, size, paddr); *dma_handle = (dma_addr_t) paddr; -#if 0 -/* This probably isn't needed to support EISA cards. -** ISA cards will certainly only support 24-bit DMA addressing. -** Not clear if we can, want, or need to support ISA. -*/ - if (!dev || *dev->coherent_dma_mask < 0xffffffff) - gfp |= GFP_DMA; -#endif return (void *)vaddr; } diff --git a/arch/parisc/kernel/pdt.c b/arch/parisc/kernel/pdt.c index 0d24735bd918..0f9b3b5914cf 100644 --- a/arch/parisc/kernel/pdt.c +++ b/arch/parisc/kernel/pdt.c @@ -354,10 +354,8 @@ static int __init pdt_initcall(void) return -ENODEV; kpdtd_task = kthread_run(pdt_mainloop, NULL, "kpdtd"); - if (IS_ERR(kpdtd_task)) - return PTR_ERR(kpdtd_task); - return 0; + return PTR_ERR_OR_ZERO(kpdtd_task); } late_initcall(pdt_initcall); diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c index 90b04d8af212..b0f0816879df 100644 --- a/arch/parisc/kernel/perf.c +++ b/arch/parisc/kernel/perf.c @@ -57,7 +57,7 @@ struct rdr_tbl_ent { static int perf_processor_interface __read_mostly = UNKNOWN_INTF; static int perf_enabled __read_mostly; static DEFINE_SPINLOCK(perf_lock); -struct parisc_device *cpu_device __read_mostly; +static struct parisc_device *cpu_device __read_mostly; /* RDRs to write for PCX-W */ static const int perf_rdrs_W[] = diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c index 00b0df97afb1..762289b9984e 100644 --- a/arch/parisc/kernel/processor.c +++ b/arch/parisc/kernel/processor.c @@ -26,6 +26,7 @@ #include <asm/processor.h> #include <asm/page.h> #include <asm/pdc.h> +#include <asm/smp.h> #include <asm/pdcpat.h> #include <asm/irq.h> /* for struct irq_region */ #include <asm/parisc-device.h> diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index 573f8303e2b0..211a4afdd282 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -40,11 +40,6 @@ static char __initdata command_line[COMMAND_LINE_SIZE]; -/* Intended for ccio/sba/cpu statistics under /proc/bus/{runway|gsc} */ -struct proc_dir_entry * proc_runway_root __read_mostly = NULL; -struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; -struct proc_dir_entry * proc_mckinley_root __read_mostly = NULL; - static void __init setup_cmdline(char **cmdline_p) { extern unsigned int boot_args[]; @@ -196,48 +191,6 @@ const struct seq_operations cpuinfo_op = { .show = show_cpuinfo }; -static void __init parisc_proc_mkdir(void) -{ - /* - ** Can't call proc_mkdir() until after proc_root_init() has been - ** called by start_kernel(). In other words, this code can't - ** live in arch/.../setup.c because start_parisc() calls - ** start_kernel(). - */ - switch (boot_cpu_data.cpu_type) { - case pcxl: - case pcxl2: - if (NULL == proc_gsc_root) - { - proc_gsc_root = proc_mkdir("bus/gsc", NULL); - } - break; - case pcxt_: - case pcxu: - case pcxu_: - case pcxw: - case pcxw_: - case pcxw2: - if (NULL == proc_runway_root) - { - proc_runway_root = proc_mkdir("bus/runway", NULL); - } - break; - case mako: - case mako2: - if (NULL == proc_mckinley_root) - { - proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); - } - break; - default: - /* FIXME: this was added to prevent the compiler - * complaining about missing pcx, pcxs and pcxt - * I'm assuming they have neither gsc nor runway */ - break; - } -} - static struct resource central_bus = { .name = "Central Bus", .start = F_EXTEND(0xfff80000), @@ -294,7 +247,6 @@ static int __init parisc_init(void) { u32 osid = (OS_ID_LINUX << 16); - parisc_proc_mkdir(); parisc_init_resources(); do_device_inventory(); /* probe for hardware */ diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index f886ff0c75df..e8d27def6c52 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -423,7 +423,7 @@ static void check_syscallno_in_delay_branch(struct pt_regs *regs) regs->gr[31] -= 8; /* delayed branching */ /* Get assembler opcode of code in delay branch */ - uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4); + uaddr = (u32 __user *) ((regs->gr[31] & ~3) + 4); err = get_user(opcode, uaddr); if (err) return; diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index ca2d537e25b1..9915062d5243 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -27,17 +27,12 @@ #include <linux/elf-randomize.h> /* - * Construct an artificial page offset for the mapping based on the virtual + * Construct an artificial page offset for the mapping based on the physical * address of the kernel file mapping variable. - * If filp is zero the calculated pgoff value aliases the memory of the given - * address. This is useful for io_uring where the mapping shall alias a kernel - * address and a userspace adress where both the kernel and the userspace - * access the same memory region. */ -#define GET_FILP_PGOFF(filp, addr) \ - ((filp ? (((unsigned long) filp->f_mapping) >> 8) \ - & ((SHM_COLOUR-1) >> PAGE_SHIFT) : 0UL) \ - + (addr >> PAGE_SHIFT)) +#define GET_FILP_PGOFF(filp) \ + (filp ? (((unsigned long) filp->f_mapping) >> 8) \ + & ((SHM_COLOUR-1) >> PAGE_SHIFT) : 0UL) static unsigned long shared_align_offset(unsigned long filp_pgoff, unsigned long pgoff) @@ -117,7 +112,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, do_color_align = 0; if (filp || (flags & MAP_SHARED)) do_color_align = 1; - filp_pgoff = GET_FILP_PGOFF(filp, addr); + filp_pgoff = GET_FILP_PGOFF(filp); if (flags & MAP_FIXED) { /* Even MAP_FIXED mappings must reside within TASK_SIZE */ diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S index 1373e5129868..1f51aa9c8230 100644 --- a/arch/parisc/kernel/syscall.S +++ b/arch/parisc/kernel/syscall.S @@ -39,6 +39,7 @@ registers). #include <asm/assembly.h> #include <asm/processor.h> #include <asm/cache.h> +#include <asm/spinlock_types.h> #include <linux/linkage.h> @@ -66,6 +67,16 @@ registers). stw \reg1, 0(%sr2,\reg2) .endm + /* raise exception if spinlock content is not zero or + * __ARCH_SPIN_LOCK_UNLOCKED_VAL */ + .macro spinlock_check spin_val,tmpreg +#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, \tmpreg + andcm,= \spin_val, \tmpreg, %r0 + .word SPINLOCK_BREAK_INSN +#endif + .endm + .text .import syscall_exit,code @@ -508,7 +519,8 @@ lws_start: lws_exit_noerror: lws_pagefault_enable %r1,%r21 - stw,ma %r20, 0(%sr2,%r20) + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21 + stw,ma %r21, 0(%sr2,%r20) ssm PSW_SM_I, %r0 b lws_exit copy %r0, %r21 @@ -521,7 +533,8 @@ lws_wouldblock: lws_pagefault: lws_pagefault_enable %r1,%r21 - stw,ma %r20, 0(%sr2,%r20) + ldi __ARCH_SPIN_LOCK_UNLOCKED_VAL, %r21 + stw,ma %r21, 0(%sr2,%r20) ssm PSW_SM_I, %r0 ldo 3(%r0),%r28 b lws_exit @@ -619,6 +632,7 @@ lws_compare_and_swap: /* Try to acquire the lock */ LDCW 0(%sr2,%r20), %r28 + spinlock_check %r28, %r21 comclr,<> %r0, %r28, %r0 b,n lws_wouldblock @@ -772,6 +786,7 @@ cas2_lock_start: /* Try to acquire the lock */ LDCW 0(%sr2,%r20), %r28 + spinlock_check %r28, %r21 comclr,<> %r0, %r28, %r0 b,n lws_wouldblock @@ -1001,6 +1016,7 @@ atomic_xchg_start: /* Try to acquire the lock */ LDCW 0(%sr2,%r20), %r28 + spinlock_check %r28, %r21 comclr,<> %r0, %r28, %r0 b,n lws_wouldblock @@ -1199,6 +1215,7 @@ atomic_store_start: /* Try to acquire the lock */ LDCW 0(%sr2,%r20), %r28 + spinlock_check %r28, %r21 comclr,<> %r0, %r28, %r0 b,n lws_wouldblock @@ -1330,7 +1347,7 @@ ENTRY(lws_lock_start) /* lws locks */ .rept 256 /* Keep locks aligned at 16-bytes */ - .word 1 + .word __ARCH_SPIN_LOCK_UNLOCKED_VAL .word 0 .word 0 .word 0 diff --git a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c index 033b9e50b44a..170d0dda4213 100644 --- a/arch/parisc/kernel/unaligned.c +++ b/arch/parisc/kernel/unaligned.c @@ -11,6 +11,7 @@ #include <linux/signal.h> #include <linux/ratelimit.h> #include <linux/uaccess.h> +#include <linux/sysctl.h> #include <asm/unaligned.h> #include <asm/hardirq.h> #include <asm/traps.h> @@ -337,7 +338,7 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop) : "r19", "r20", "r21", "r22", "r1" ); #else { - unsigned long valh=(val>>32),vall=(val&0xffffffffl); + unsigned long valh = (val >> 32), vall = (val & 0xffffffffl); __asm__ __volatile__ ( " mtsp %4, %%sr1\n" " zdep %2, 29, 2, %%r19\n" @@ -473,7 +474,7 @@ void handle_unaligned(struct pt_regs *regs) case OPCODE_LDWA_I: case OPCODE_LDW_S: case OPCODE_LDWA_S: - ret = emulate_ldw(regs, R3(regs->iir),0); + ret = emulate_ldw(regs, R3(regs->iir), 0); break; case OPCODE_STH: @@ -482,7 +483,7 @@ void handle_unaligned(struct pt_regs *regs) case OPCODE_STW: case OPCODE_STWA: - ret = emulate_stw(regs, R2(regs->iir),0); + ret = emulate_stw(regs, R2(regs->iir), 0); break; #ifdef CONFIG_64BIT @@ -490,12 +491,12 @@ void handle_unaligned(struct pt_regs *regs) case OPCODE_LDDA_I: case OPCODE_LDD_S: case OPCODE_LDDA_S: - ret = emulate_ldd(regs, R3(regs->iir),0); + ret = emulate_ldd(regs, R3(regs->iir), 0); break; case OPCODE_STD: case OPCODE_STDA: - ret = emulate_std(regs, R2(regs->iir),0); + ret = emulate_std(regs, R2(regs->iir), 0); break; #endif @@ -503,24 +504,24 @@ void handle_unaligned(struct pt_regs *regs) case OPCODE_FLDWS: case OPCODE_FLDWXR: case OPCODE_FLDWSR: - ret = emulate_ldw(regs,FR3(regs->iir),1); + ret = emulate_ldw(regs, FR3(regs->iir), 1); break; case OPCODE_FLDDX: case OPCODE_FLDDS: - ret = emulate_ldd(regs,R3(regs->iir),1); + ret = emulate_ldd(regs, R3(regs->iir), 1); break; case OPCODE_FSTWX: case OPCODE_FSTWS: case OPCODE_FSTWXR: case OPCODE_FSTWSR: - ret = emulate_stw(regs,FR3(regs->iir),1); + ret = emulate_stw(regs, FR3(regs->iir), 1); break; case OPCODE_FSTDX: case OPCODE_FSTDS: - ret = emulate_std(regs,R3(regs->iir),1); + ret = emulate_std(regs, R3(regs->iir), 1); break; case OPCODE_LDCD_I: diff --git a/arch/parisc/lib/ucmpdi2.c b/arch/parisc/lib/ucmpdi2.c index 8e6014a142ef..9d8b4dbae273 100644 --- a/arch/parisc/lib/ucmpdi2.c +++ b/arch/parisc/lib/ucmpdi2.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/module.h> +#include <linux/libgcc.h> union ull_union { unsigned long long ull; @@ -9,7 +10,7 @@ union ull_union { } ui; }; -int __ucmpdi2(unsigned long long a, unsigned long long b) +word_type __ucmpdi2(unsigned long long a, unsigned long long b) { union ull_union au = {.ull = a}; union ull_union bu = {.ull = b}; diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index a4c7c7630f48..2fe5b44986e0 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c @@ -192,31 +192,31 @@ int fixup_exception(struct pt_regs *regs) * For implementation see handle_interruption() in traps.c */ static const char * const trap_description[] = { - [1] "High-priority machine check (HPMC)", - [2] "Power failure interrupt", - [3] "Recovery counter trap", - [5] "Low-priority machine check", - [6] "Instruction TLB miss fault", - [7] "Instruction access rights / protection trap", - [8] "Illegal instruction trap", - [9] "Break instruction trap", - [10] "Privileged operation trap", - [11] "Privileged register trap", - [12] "Overflow trap", - [13] "Conditional trap", - [14] "FP Assist Exception trap", - [15] "Data TLB miss fault", - [16] "Non-access ITLB miss fault", - [17] "Non-access DTLB miss fault", - [18] "Data memory protection/unaligned access trap", - [19] "Data memory break trap", - [20] "TLB dirty bit trap", - [21] "Page reference trap", - [22] "Assist emulation trap", - [25] "Taken branch trap", - [26] "Data memory access rights trap", - [27] "Data memory protection ID trap", - [28] "Unaligned data reference trap", + [1] = "High-priority machine check (HPMC)", + [2] = "Power failure interrupt", + [3] = "Recovery counter trap", + [5] = "Low-priority machine check", + [6] = "Instruction TLB miss fault", + [7] = "Instruction access rights / protection trap", + [8] = "Illegal instruction trap", + [9] = "Break instruction trap", + [10] = "Privileged operation trap", + [11] = "Privileged register trap", + [12] = "Overflow trap", + [13] = "Conditional trap", + [14] = "FP Assist Exception trap", + [15] = "Data TLB miss fault", + [16] = "Non-access ITLB miss fault", + [17] = "Non-access DTLB miss fault", + [18] = "Data memory protection/unaligned access trap", + [19] = "Data memory break trap", + [20] = "TLB dirty bit trap", + [21] = "Page reference trap", + [22] = "Assist emulation trap", + [25] = "Taken branch trap", + [26] = "Data memory access rights trap", + [27] = "Data memory protection ID trap", + [28] = "Unaligned data reference trap", }; const char *trap_name(unsigned long code) diff --git a/arch/parisc/mm/fixmap.c b/arch/parisc/mm/fixmap.c index cc15d737fda6..ae3493dae9dc 100644 --- a/arch/parisc/mm/fixmap.c +++ b/arch/parisc/mm/fixmap.c @@ -19,9 +19,6 @@ void notrace set_fixmap(enum fixed_addresses idx, phys_addr_t phys) pmd_t *pmd = pmd_offset(pud, vaddr); pte_t *pte; - if (pmd_none(*pmd)) - pte = pte_alloc_kernel(pmd, vaddr); - pte = pte_offset_kernel(pmd, vaddr); set_pte_at(&init_mm, vaddr, pte, __mk_pte(phys, PAGE_KERNEL_RWX)); flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE); diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 406c52fe23d5..a088c243edea 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -523,10 +523,6 @@ void mark_rodata_ro(void) void *parisc_vmalloc_start __ro_after_init; EXPORT_SYMBOL(parisc_vmalloc_start); -#ifdef CONFIG_PA11 -unsigned long pcxl_dma_start __ro_after_init; -#endif - void __init mem_init(void) { /* Do sanity checks on IPC (compat) structures */ @@ -669,6 +665,39 @@ static void __init gateway_init(void) PAGE_SIZE, PAGE_GATEWAY, 1); } +static void __init fixmap_init(void) +{ + unsigned long addr = FIXMAP_START; + unsigned long end = FIXMAP_START + FIXMAP_SIZE; + pgd_t *pgd = pgd_offset_k(addr); + p4d_t *p4d = p4d_offset(pgd, addr); + pud_t *pud = pud_offset(p4d, addr); + pmd_t *pmd; + + BUILD_BUG_ON(FIXMAP_SIZE > PMD_SIZE); + +#if CONFIG_PGTABLE_LEVELS == 3 + if (pud_none(*pud)) { + pmd = memblock_alloc(PAGE_SIZE << PMD_TABLE_ORDER, + PAGE_SIZE << PMD_TABLE_ORDER); + if (!pmd) + panic("fixmap: pmd allocation failed.\n"); + pud_populate(NULL, pud, pmd); + } +#endif + + pmd = pmd_offset(pud, addr); + do { + pte_t *pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (!pte) + panic("fixmap: pte allocation failed.\n"); + + pmd_populate_kernel(&init_mm, pmd, pte); + + addr += PAGE_SIZE; + } while (addr < end); +} + static void __init parisc_bootmem_free(void) { unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0, }; @@ -683,6 +712,7 @@ void __init paging_init(void) setup_bootmem(); pagetable_init(); gateway_init(); + fixmap_init(); flush_cache_all_local(); /* start with known state */ flush_tlb_all_local(NULL); diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index 345ff0b66499..d7ee1f43d997 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -27,7 +27,7 @@ */ void __iomem *ioremap(unsigned long phys_addr, unsigned long size) { - void __iomem *addr; + uintptr_t addr; struct vm_struct *area; unsigned long offset, last_addr; pgprot_t pgprot; @@ -79,10 +79,9 @@ void __iomem *ioremap(unsigned long phys_addr, unsigned long size) if (!area) return NULL; - addr = (void __iomem *) area->addr; - if (ioremap_page_range((unsigned long)addr, (unsigned long)addr + size, - phys_addr, pgprot)) { - vunmap(addr); + addr = (uintptr_t) area->addr; + if (ioremap_page_range(addr, addr + size, phys_addr, pgprot)) { + vunmap(area->addr); return NULL; } diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 8a6754ffdc7e..a6c7069bec5d 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -393,7 +393,6 @@ int validate_sp_size(unsigned long sp, struct task_struct *p, */ #define ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCHW -#define ARCH_HAS_SPINLOCK_PREFETCH static inline void prefetch(const void *x) { @@ -411,8 +410,6 @@ static inline void prefetchw(const void *x) __asm__ __volatile__ ("dcbtst 0,%0" : : "r" (x)); } -#define spin_lock_prefetch(x) prefetchw(x) - /* asm stubs */ extern unsigned long isa300_idle_stop_noloss(unsigned long psscr_val); extern unsigned long isa300_idle_stop_mayloss(unsigned long psscr_val); diff --git a/arch/powerpc/include/asm/word-at-a-time.h b/arch/powerpc/include/asm/word-at-a-time.h index 46c31fb8748d..30a12d208687 100644 --- a/arch/powerpc/include/asm/word-at-a-time.h +++ b/arch/powerpc/include/asm/word-at-a-time.h @@ -34,7 +34,7 @@ static inline long find_zero(unsigned long mask) return leading_zero_bits >> 3; } -static inline bool has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c) +static inline unsigned long has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c) { unsigned long rhs = val | c->low_bits; *data = rhs; diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index f132d8704263..6440b1bb332a 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -375,8 +375,7 @@ _GLOBAL(generic_secondary_smp_init) beq 20f /* start the specified thread */ - LOAD_REG_ADDR(r5, fsl_secondary_thread_init) - ld r4, 0(r5) + LOAD_REG_ADDR(r5, DOTSYM(fsl_secondary_thread_init)) bl book3e_start_thread /* stop the current thread */ diff --git a/arch/powerpc/kernel/trace/ftrace_mprofile.S b/arch/powerpc/kernel/trace/ftrace_mprofile.S index ffb1db386849..1f7d86de1538 100644 --- a/arch/powerpc/kernel/trace/ftrace_mprofile.S +++ b/arch/powerpc/kernel/trace/ftrace_mprofile.S @@ -33,6 +33,9 @@ * and then arrange for the ftrace function to be called. */ .macro ftrace_regs_entry allregs + /* Create a minimal stack frame for representing B */ + PPC_STLU r1, -STACK_FRAME_MIN_SIZE(r1) + /* Create our stack frame + pt_regs */ PPC_STLU r1,-SWITCH_FRAME_SIZE(r1) @@ -42,7 +45,7 @@ #ifdef CONFIG_PPC64 /* Save the original return address in A's stack frame */ - std r0, LRSAVE+SWITCH_FRAME_SIZE(r1) + std r0, LRSAVE+SWITCH_FRAME_SIZE+STACK_FRAME_MIN_SIZE(r1) /* Ok to continue? */ lbz r3, PACA_FTRACE_ENABLED(r13) cmpdi r3, 0 @@ -77,6 +80,8 @@ mflr r7 /* Save it as pt_regs->nip */ PPC_STL r7, _NIP(r1) + /* Also save it in B's stackframe header for proper unwind */ + PPC_STL r7, LRSAVE+SWITCH_FRAME_SIZE(r1) /* Save the read LR in pt_regs->link */ PPC_STL r0, _LINK(r1) @@ -142,7 +147,7 @@ #endif /* Pop our stack frame */ - addi r1, r1, SWITCH_FRAME_SIZE + addi r1, r1, SWITCH_FRAME_SIZE+STACK_FRAME_MIN_SIZE #ifdef CONFIG_LIVEPATCH_64 /* Based on the cmpd above, if the NIP was altered handle livepatch */ diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c index fe1b83020e0d..0ec5b45b1e86 100644 --- a/arch/powerpc/mm/init_64.c +++ b/arch/powerpc/mm/init_64.c @@ -314,8 +314,7 @@ void __ref vmemmap_free(unsigned long start, unsigned long end, start = ALIGN_DOWN(start, page_size); if (altmap) { alt_start = altmap->base_pfn; - alt_end = altmap->base_pfn + altmap->reserve + - altmap->free + altmap->alloc + altmap->align; + alt_end = altmap->base_pfn + altmap->reserve + altmap->free; } pr_debug("vmemmap_free %lx...%lx\n", start, end); diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 9c43cf32f4c9..40aa58206888 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -180,7 +180,7 @@ static void wake_hw_thread(void *info) unsigned long inia; int cpu = *(const int *)info; - inia = *(unsigned long *)fsl_secondary_thread_init; + inia = ppc_function_entry(fsl_secondary_thread_init); book3e_start_thread(cpu_thread_in_core(cpu), inia); } #endif diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c index 4c5790aff1b5..8633891b7aa5 100644 --- a/arch/powerpc/platforms/powermac/time.c +++ b/arch/powerpc/platforms/powermac/time.c @@ -26,8 +26,8 @@ #include <linux/rtc.h> #include <linux/of_address.h> +#include <asm/early_ioremap.h> #include <asm/sections.h> -#include <asm/io.h> #include <asm/machdep.h> #include <asm/time.h> #include <asm/nvram.h> @@ -182,7 +182,7 @@ static int __init via_calibrate_decr(void) return 0; } of_node_put(vias); - via = ioremap(rsrc.start, resource_size(&rsrc)); + via = early_ioremap(rsrc.start, resource_size(&rsrc)); if (via == NULL) { printk(KERN_ERR "Failed to map VIA for timer calibration !\n"); return 0; @@ -207,7 +207,7 @@ static int __init via_calibrate_decr(void) ppc_tb_freq = (dstart - dend) * 100 / 6; - iounmap(via); + early_iounmap((void *)via, resource_size(&rsrc)); return 1; } diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h index f71ce21ff684..d5604d2073bc 100644 --- a/arch/riscv/include/asm/acpi.h +++ b/arch/riscv/include/asm/acpi.h @@ -19,7 +19,7 @@ typedef u64 phys_cpuid_t; #define PHYS_CPUID_INVALID INVALID_HARTID /* ACPI table mapping after acpi_permanent_mmap is set */ -void *acpi_os_ioremap(acpi_physical_address phys, acpi_size size); +void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size); #define acpi_os_ioremap acpi_os_ioremap #define acpi_strict 1 /* No out-of-spec workarounds on RISC-V */ diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8091b8bf4883..b93ffddf8a61 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -37,6 +37,10 @@ static inline void flush_dcache_page(struct page *page) #define flush_icache_user_page(vma, pg, addr, len) \ flush_icache_mm(vma->vm_mm, 0) +#ifdef CONFIG_64BIT +#define flush_cache_vmap(start, end) flush_tlb_kernel_range(start, end) +#endif + #ifndef CONFIG_SMP #define flush_icache_all() local_flush_icache_all() diff --git a/arch/riscv/include/asm/mmio.h b/arch/riscv/include/asm/mmio.h index aff6c33ab0c0..4c58ee7f95ec 100644 --- a/arch/riscv/include/asm/mmio.h +++ b/arch/riscv/include/asm/mmio.h @@ -101,9 +101,9 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) * Relaxed I/O memory access primitives. These follow the Device memory * ordering rules but do not guarantee any ordering relative to Normal memory * accesses. These are defined to order the indicated access (either a read or - * write) with all other I/O memory accesses. Since the platform specification - * defines that all I/O regions are strongly ordered on channel 2, no explicit - * fences are required to enforce this ordering. + * write) with all other I/O memory accesses to the same peripheral. Since the + * platform specification defines that all I/O regions are strongly ordered on + * channel 0, no explicit fences are required to enforce this ordering. */ /* FIXME: These are now the same as asm-generic */ #define __io_rbr() do {} while (0) @@ -125,14 +125,14 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) #endif /* - * I/O memory access primitives. Reads are ordered relative to any - * following Normal memory access. Writes are ordered relative to any prior - * Normal memory access. The memory barriers here are necessary as RISC-V + * I/O memory access primitives. Reads are ordered relative to any following + * Normal memory read and delay() loop. Writes are ordered relative to any + * prior Normal memory write. The memory barriers here are necessary as RISC-V * doesn't define any ordering between the memory space and the I/O space. */ #define __io_br() do {} while (0) -#define __io_ar(v) __asm__ __volatile__ ("fence i,r" : : : "memory") -#define __io_bw() __asm__ __volatile__ ("fence w,o" : : : "memory") +#define __io_ar(v) ({ __asm__ __volatile__ ("fence i,ir" : : : "memory"); }) +#define __io_bw() ({ __asm__ __volatile__ ("fence w,o" : : : "memory"); }) #define __io_aw() mmiowb_set_pending() #define readb(c) ({ u8 __v; __io_br(); __v = readb_cpu(c); __io_ar(__v); __v; }) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 75970ee2bda2..b5680c940c1e 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -188,6 +188,8 @@ extern struct pt_alloc_ops pt_ops __initdata; #define PAGE_KERNEL_IO __pgprot(_PAGE_IOREMAP) extern pgd_t swapper_pg_dir[]; +extern pgd_t trampoline_pg_dir[]; +extern pgd_t early_pg_dir[]; #ifdef CONFIG_TRANSPARENT_HUGEPAGE static inline int pmd_present(pmd_t pmd) diff --git a/arch/riscv/include/asm/vmalloc.h b/arch/riscv/include/asm/vmalloc.h index 58d3e447f191..924d01b56c9a 100644 --- a/arch/riscv/include/asm/vmalloc.h +++ b/arch/riscv/include/asm/vmalloc.h @@ -3,12 +3,14 @@ #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP +extern bool pgtable_l4_enabled, pgtable_l5_enabled; + #define IOREMAP_MAX_ORDER (PUD_SHIFT) #define arch_vmap_pud_supported arch_vmap_pud_supported static inline bool arch_vmap_pud_supported(pgprot_t prot) { - return true; + return pgtable_l4_enabled || pgtable_l5_enabled; } #define arch_vmap_pmd_supported arch_vmap_pmd_supported diff --git a/arch/riscv/kernel/acpi.c b/arch/riscv/kernel/acpi.c index 5ee03ebab80e..56cb2c986c48 100644 --- a/arch/riscv/kernel/acpi.c +++ b/arch/riscv/kernel/acpi.c @@ -215,9 +215,9 @@ void __init __acpi_unmap_table(void __iomem *map, unsigned long size) early_iounmap(map, size); } -void *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) +void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) { - return memremap(phys, size, MEMREMAP_WB); + return (void __iomem *)memremap(phys, size, MEMREMAP_WB); } #ifdef CONFIG_PCI diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index a2fc952318e9..35b854cf078e 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -17,6 +17,11 @@ #include <asm/smp.h> #include <asm/pgtable.h> +bool arch_match_cpu_phys_id(int cpu, u64 phys_id) +{ + return phys_id == cpuid_to_hartid_map(cpu); +} + /* * Returns the hart ID of the given device tree node, or -ENODEV if the node * isn't an enabled and valid RISC-V hart node. diff --git a/arch/riscv/kernel/crash_core.c b/arch/riscv/kernel/crash_core.c index b351a3c01355..55f1d7856b54 100644 --- a/arch/riscv/kernel/crash_core.c +++ b/arch/riscv/kernel/crash_core.c @@ -18,4 +18,6 @@ void arch_crash_save_vmcoreinfo(void) vmcoreinfo_append_str("NUMBER(MODULES_END)=0x%lx\n", MODULES_END); #endif vmcoreinfo_append_str("NUMBER(KERNEL_LINK_ADDR)=0x%lx\n", KERNEL_LINK_ADDR); + vmcoreinfo_append_str("NUMBER(va_kernel_pa_offset)=0x%lx\n", + kernel_map.va_kernel_pa_offset); } diff --git a/arch/riscv/kernel/elf_kexec.c b/arch/riscv/kernel/elf_kexec.c index 5372b708fae2..c08bb5c3b385 100644 --- a/arch/riscv/kernel/elf_kexec.c +++ b/arch/riscv/kernel/elf_kexec.c @@ -281,7 +281,7 @@ static void *elf_kexec_load(struct kimage *image, char *kernel_buf, kbuf.buffer = initrd; kbuf.bufsz = kbuf.memsz = initrd_len; kbuf.buf_align = PAGE_SIZE; - kbuf.top_down = false; + kbuf.top_down = true; kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; ret = kexec_add_buffer(&kbuf); if (ret) @@ -425,6 +425,7 @@ int arch_kexec_apply_relocations_add(struct purgatory_info *pi, * sym, instead of searching the whole relsec. */ case R_RISCV_PCREL_HI20: + case R_RISCV_CALL_PLT: case R_RISCV_CALL: *(u64 *)loc = CLEAN_IMM(UITYPE, *(u64 *)loc) | ENCODE_UJTYPE_IMM(val - addr); diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 85bbce0f758c..40420afbb1a0 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -61,11 +61,6 @@ int riscv_hartid_to_cpuid(unsigned long hartid) return -ENOENT; } -bool arch_match_cpu_phys_id(int cpu, u64 phys_id) -{ - return phys_id == cpuid_to_hartid_map(cpu); -} - static void ipi_stop(void) { set_cpu_online(smp_processor_id(), false); diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 9ce504737d18..e4c35ac2357f 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -26,12 +26,13 @@ #include <linux/kfence.h> #include <asm/fixmap.h> -#include <asm/tlbflush.h> -#include <asm/sections.h> -#include <asm/soc.h> #include <asm/io.h> -#include <asm/ptdump.h> #include <asm/numa.h> +#include <asm/pgtable.h> +#include <asm/ptdump.h> +#include <asm/sections.h> +#include <asm/soc.h> +#include <asm/tlbflush.h> #include "../kernel/head.h" @@ -214,8 +215,13 @@ static void __init setup_bootmem(void) memblock_reserve(vmlinux_start, vmlinux_end - vmlinux_start); phys_ram_end = memblock_end_of_DRAM(); + + /* + * Make sure we align the start of the memory on a PMD boundary so that + * at worst, we map the linear mapping with PMD mappings. + */ if (!IS_ENABLED(CONFIG_XIP_KERNEL)) - phys_ram_base = memblock_start_of_DRAM(); + phys_ram_base = memblock_start_of_DRAM() & PMD_MASK; /* * In 64-bit, any use of __va/__pa before this point is wrong as we diff --git a/arch/riscv/mm/kasan_init.c b/arch/riscv/mm/kasan_init.c index 8fc0efcf905c..a01bc15dce24 100644 --- a/arch/riscv/mm/kasan_init.c +++ b/arch/riscv/mm/kasan_init.c @@ -22,7 +22,6 @@ * region is not and then we have to go down to the PUD level. */ -extern pgd_t early_pg_dir[PTRS_PER_PGD]; pgd_t tmp_pg_dir[PTRS_PER_PGD] __page_aligned_bss; p4d_t tmp_p4d[PTRS_PER_P4D] __page_aligned_bss; pud_t tmp_pud[PTRS_PER_PUD] __page_aligned_bss; diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index d03d4cb9332c..3a6a9a88bd38 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -116,7 +116,6 @@ CONFIG_UNIX=y CONFIG_UNIX_DIAG=m CONFIG_XFRM_USER=m CONFIG_NET_KEY=m -CONFIG_NET_TC_SKB_EXT=y CONFIG_SMC=m CONFIG_SMC_DIAG=m CONFIG_INET=y @@ -193,6 +192,7 @@ CONFIG_NFT_REJECT=m CONFIG_NFT_COMPAT=m CONFIG_NFT_HASH=m CONFIG_NFT_FIB_INET=m +CONFIG_NETFILTER_XTABLES_COMPAT=y CONFIG_NETFILTER_XT_SET=m CONFIG_NETFILTER_XT_TARGET_AUDIT=m CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m @@ -379,6 +379,7 @@ CONFIG_NET_ACT_SIMP=m CONFIG_NET_ACT_SKBEDIT=m CONFIG_NET_ACT_CSUM=m CONFIG_NET_ACT_GATE=m +CONFIG_NET_TC_SKB_EXT=y CONFIG_DNS_RESOLVER=y CONFIG_OPENVSWITCH=m CONFIG_VSOCKETS=m @@ -395,6 +396,7 @@ CONFIG_HOTPLUG_PCI=y CONFIG_HOTPLUG_PCI_S390=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_SAFE=y +# CONFIG_FW_LOADER is not set CONFIG_CONNECTOR=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=m @@ -502,7 +504,6 @@ CONFIG_NLMON=m # CONFIG_NET_VENDOR_GOOGLE is not set # CONFIG_NET_VENDOR_HUAWEI is not set # CONFIG_NET_VENDOR_INTEL is not set -# CONFIG_NET_VENDOR_WANGXUN is not set # CONFIG_NET_VENDOR_LITEX is not set # CONFIG_NET_VENDOR_MARVELL is not set CONFIG_MLX4_EN=m @@ -542,6 +543,7 @@ CONFIG_MLX5_CORE_EN=y # CONFIG_NET_VENDOR_TI is not set # CONFIG_NET_VENDOR_VERTEXCOM is not set # CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WANGXUN is not set # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set CONFIG_PPP=m @@ -646,7 +648,6 @@ CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_INODE64=y CONFIG_HUGETLBFS=y -CONFIG_CONFIGFS_FS=m CONFIG_ECRYPT_FS=m CONFIG_CRAMFS=m CONFIG_SQUASHFS=m @@ -690,7 +691,6 @@ CONFIG_HARDENED_USERCOPY=y CONFIG_FORTIFY_SOURCE=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_LOCKDOWN_LSM=y CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y CONFIG_SECURITY_LANDLOCK=y @@ -744,7 +744,6 @@ CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m -CONFIG_CRYPTO_SHA3=m CONFIG_CRYPTO_SM3_GENERIC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_WP512=m @@ -844,6 +843,7 @@ CONFIG_PREEMPT_TRACER=y CONFIG_SCHED_TRACER=y CONFIG_FTRACE_SYSCALLS=y CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_USER_EVENTS=y CONFIG_HIST_TRIGGERS=y CONFIG_FTRACE_STARTUP_TEST=y # CONFIG_EVENT_TRACE_STARTUP_TEST is not set @@ -866,6 +866,7 @@ CONFIG_FAIL_MAKE_REQUEST=y CONFIG_FAIL_IO_TIMEOUT=y CONFIG_FAIL_FUTEX=y CONFIG_FAULT_INJECTION_DEBUG_FS=y +CONFIG_FAULT_INJECTION_CONFIGFS=y CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y CONFIG_LKDTM=m CONFIG_TEST_MIN_HEAP=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 1855759cdc6a..b13a5a0d99d2 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -107,7 +107,6 @@ CONFIG_UNIX=y CONFIG_UNIX_DIAG=m CONFIG_XFRM_USER=m CONFIG_NET_KEY=m -CONFIG_NET_TC_SKB_EXT=y CONFIG_SMC=m CONFIG_SMC_DIAG=m CONFIG_INET=y @@ -184,6 +183,7 @@ CONFIG_NFT_REJECT=m CONFIG_NFT_COMPAT=m CONFIG_NFT_HASH=m CONFIG_NFT_FIB_INET=m +CONFIG_NETFILTER_XTABLES_COMPAT=y CONFIG_NETFILTER_XT_SET=m CONFIG_NETFILTER_XT_TARGET_AUDIT=m CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m @@ -369,6 +369,7 @@ CONFIG_NET_ACT_SIMP=m CONFIG_NET_ACT_SKBEDIT=m CONFIG_NET_ACT_CSUM=m CONFIG_NET_ACT_GATE=m +CONFIG_NET_TC_SKB_EXT=y CONFIG_DNS_RESOLVER=y CONFIG_OPENVSWITCH=m CONFIG_VSOCKETS=m @@ -385,6 +386,7 @@ CONFIG_HOTPLUG_PCI_S390=y CONFIG_UEVENT_HELPER=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_SAFE=y +# CONFIG_FW_LOADER is not set CONFIG_CONNECTOR=y CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=m @@ -492,7 +494,6 @@ CONFIG_NLMON=m # CONFIG_NET_VENDOR_GOOGLE is not set # CONFIG_NET_VENDOR_HUAWEI is not set # CONFIG_NET_VENDOR_INTEL is not set -# CONFIG_NET_VENDOR_WANGXUN is not set # CONFIG_NET_VENDOR_LITEX is not set # CONFIG_NET_VENDOR_MARVELL is not set CONFIG_MLX4_EN=m @@ -532,6 +533,7 @@ CONFIG_MLX5_CORE_EN=y # CONFIG_NET_VENDOR_TI is not set # CONFIG_NET_VENDOR_VERTEXCOM is not set # CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WANGXUN is not set # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set CONFIG_PPP=m @@ -673,7 +675,6 @@ CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_LOCKDOWN_LSM=y CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y CONFIG_SECURITY_LANDLOCK=y @@ -729,7 +730,6 @@ CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD160=m -CONFIG_CRYPTO_SHA3=m CONFIG_CRYPTO_SM3_GENERIC=m CONFIG_CRYPTO_VMAC=m CONFIG_CRYPTO_WP512=m @@ -793,6 +793,7 @@ CONFIG_STACK_TRACER=y CONFIG_SCHED_TRACER=y CONFIG_FTRACE_SYSCALLS=y CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_USER_EVENTS=y CONFIG_HIST_TRIGGERS=y CONFIG_SAMPLES=y CONFIG_SAMPLE_TRACE_PRINTK=m diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig index 6f68b39817ef..e62fb2015102 100644 --- a/arch/s390/configs/zfcpdump_defconfig +++ b/arch/s390/configs/zfcpdump_defconfig @@ -53,7 +53,6 @@ CONFIG_ZFCP=y # CONFIG_HVC_IUCV is not set # CONFIG_HW_RANDOM_S390 is not set # CONFIG_HMC_DRV is not set -# CONFIG_S390_UV_UAPI is not set # CONFIG_S390_TAPE is not set # CONFIG_VMCP is not set # CONFIG_MONWRITER is not set diff --git a/arch/s390/include/uapi/asm/ptrace.h b/arch/s390/include/uapi/asm/ptrace.h index f0fe3bcc78a8..bb0826024bb9 100644 --- a/arch/s390/include/uapi/asm/ptrace.h +++ b/arch/s390/include/uapi/asm/ptrace.h @@ -8,6 +8,8 @@ #ifndef _UAPI_S390_PTRACE_H #define _UAPI_S390_PTRACE_H +#include <linux/const.h> + /* * Offsets in the user_regs_struct. They are used for the ptrace * system call and in entry.S diff --git a/arch/s390/kernel/sthyi.c b/arch/s390/kernel/sthyi.c index 4d141e2c132e..2ea7f208f0e7 100644 --- a/arch/s390/kernel/sthyi.c +++ b/arch/s390/kernel/sthyi.c @@ -459,9 +459,9 @@ static int sthyi_update_cache(u64 *rc) * * Fills the destination with system information returned by the STHYI * instruction. The data is generated by emulation or execution of STHYI, - * if available. The return value is the condition code that would be - * returned, the rc parameter is the return code which is passed in - * register R2 + 1. + * if available. The return value is either a negative error value or + * the condition code that would be returned, the rc parameter is the + * return code which is passed in register R2 + 1. */ int sthyi_fill(void *dst, u64 *rc) { diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 954d39adf85c..341abafb96e4 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -389,8 +389,8 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu) */ int handle_sthyi(struct kvm_vcpu *vcpu) { - int reg1, reg2, r = 0; - u64 code, addr, cc = 0, rc = 0; + int reg1, reg2, cc = 0, r = 0; + u64 code, addr, rc = 0; struct sthyi_sctns *sctns = NULL; if (!test_kvm_facility(vcpu->kvm, 74)) @@ -421,7 +421,10 @@ int handle_sthyi(struct kvm_vcpu *vcpu) return -ENOMEM; cc = sthyi_fill(sctns, &rc); - + if (cc < 0) { + free_page((unsigned long)sctns); + return cc; + } out: if (!cc) { if (kvm_s390_pv_cpu_is_protected(vcpu)) { diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index b26649233d12..24a66670f5c3 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -763,6 +763,8 @@ void __init vmem_map_init(void) if (static_key_enabled(&cpu_has_bear)) set_memory_nx(0, 1); set_memory_nx(PAGE_SIZE, 1); + if (debug_pagealloc_enabled()) + set_memory_4k(0, ident_map_size >> PAGE_SHIFT); pr_info("Write protected kernel read-only data: %luk\n", (unsigned long)(__end_rodata - _stext) >> 10); diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h index 2667f35d5ea5..0a0d5c3d184c 100644 --- a/arch/sparc/include/asm/processor_64.h +++ b/arch/sparc/include/asm/processor_64.h @@ -213,7 +213,6 @@ unsigned long __get_wchan(struct task_struct *task); */ #define ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCHW -#define ARCH_HAS_SPINLOCK_PREFETCH static inline void prefetch(const void *x) { @@ -239,8 +238,6 @@ static inline void prefetchw(const void *x) : "r" (x)); } -#define spin_lock_prefetch(x) prefetchw(x) - #define HAVE_ARCH_PICK_MMAP_LAYOUT int do_mathemu(struct pt_regs *regs, struct fpustate *f, bool illegal_insn_trap); diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 7422db409770..e36261b4ea14 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2593,6 +2593,13 @@ config CPU_IBRS_ENTRY This mitigates both spectre_v2 and retbleed at great cost to performance. +config CPU_SRSO + bool "Mitigate speculative RAS overflow on AMD" + depends on CPU_SUP_AMD && X86_64 && RETHUNK + default y + help + Enable the SRSO mitigation needed on AMD Zen1-4 machines. + config SLS bool "Mitigate Straight-Line-Speculation" depends on CC_HAS_SLS && X86_64 @@ -2603,6 +2610,25 @@ config SLS against straight line speculation. The kernel image might be slightly larger. +config GDS_FORCE_MITIGATION + bool "Force GDS Mitigation" + depends on CPU_SUP_INTEL + default n + help + Gather Data Sampling (GDS) is a hardware vulnerability which allows + unprivileged speculative access to data which was previously stored in + vector registers. + + This option is equivalent to setting gather_data_sampling=force on the + command line. The microcode mitigation is used if present, otherwise + AVX is disabled as a mitigation. On affected systems that are missing + the microcode any userspace code that unconditionally uses AVX will + break with this option set. + + Setting this option on systems not vulnerable to GDS has no effect. + + If in doubt, say N. + endif config ARCH_HAS_ADD_PAGES diff --git a/arch/x86/boot/compressed/idt_64.c b/arch/x86/boot/compressed/idt_64.c index 6debb816e83d..3cdf94b41456 100644 --- a/arch/x86/boot/compressed/idt_64.c +++ b/arch/x86/boot/compressed/idt_64.c @@ -63,7 +63,14 @@ void load_stage2_idt(void) set_idt_entry(X86_TRAP_PF, boot_page_fault); #ifdef CONFIG_AMD_MEM_ENCRYPT - set_idt_entry(X86_TRAP_VC, boot_stage2_vc); + /* + * Clear the second stage #VC handler in case guest types + * needing #VC have not been detected. + */ + if (sev_status & BIT(1)) + set_idt_entry(X86_TRAP_VC, boot_stage2_vc); + else + set_idt_entry(X86_TRAP_VC, NULL); #endif load_boot_idt(&boot_idt_desc); diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c index 09dc8c187b3c..c3e343bd4760 100644 --- a/arch/x86/boot/compressed/sev.c +++ b/arch/x86/boot/compressed/sev.c @@ -405,12 +405,45 @@ void sev_enable(struct boot_params *bp) bp->cc_blob_address = 0; /* + * Do an initial SEV capability check before snp_init() which + * loads the CPUID page and the same checks afterwards are done + * without the hypervisor and are trustworthy. + * + * If the HV fakes SEV support, the guest will crash'n'burn + * which is good enough. + */ + + /* Check for the SME/SEV support leaf */ + eax = 0x80000000; + ecx = 0; + native_cpuid(&eax, &ebx, &ecx, &edx); + if (eax < 0x8000001f) + return; + + /* + * Check for the SME/SEV feature: + * CPUID Fn8000_001F[EAX] + * - Bit 0 - Secure Memory Encryption support + * - Bit 1 - Secure Encrypted Virtualization support + * CPUID Fn8000_001F[EBX] + * - Bits 5:0 - Pagetable bit position used to indicate encryption + */ + eax = 0x8000001f; + ecx = 0; + native_cpuid(&eax, &ebx, &ecx, &edx); + /* Check whether SEV is supported */ + if (!(eax & BIT(1))) + return; + + /* * Setup/preliminary detection of SNP. This will be sanity-checked * against CPUID/MSR values later. */ snp = snp_init(bp); - /* Check for the SME/SEV support leaf */ + /* Now repeat the checks with the SNP CPUID table. */ + + /* Recheck the SME/SEV support leaf */ eax = 0x80000000; ecx = 0; native_cpuid(&eax, &ebx, &ecx, &edx); @@ -418,7 +451,7 @@ void sev_enable(struct boot_params *bp) return; /* - * Check for the SME/SEV feature: + * Recheck for the SME/SEV feature: * CPUID Fn8000_001F[EAX] * - Bit 0 - Secure Memory Encryption support * - Bit 1 - Secure Encrypted Virtualization support diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c index 11a5c68d1218..7645730dc228 100644 --- a/arch/x86/entry/vdso/vma.c +++ b/arch/x86/entry/vdso/vma.c @@ -299,8 +299,8 @@ static unsigned long vdso_addr(unsigned long start, unsigned len) /* Round the lowest possible end address up to a PMD boundary. */ end = (start + len + PMD_SIZE - 1) & PMD_MASK; - if (end >= TASK_SIZE_MAX) - end = TASK_SIZE_MAX; + if (end >= DEFAULT_MAP_WINDOW) + end = DEFAULT_MAP_WINDOW; end -= len; if (end > start) { diff --git a/arch/x86/hyperv/hv_apic.c b/arch/x86/hyperv/hv_apic.c index 1fbda2f94184..b21335e6a210 100644 --- a/arch/x86/hyperv/hv_apic.c +++ b/arch/x86/hyperv/hv_apic.c @@ -107,7 +107,6 @@ static bool cpu_is_self(int cpu) static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector, bool exclude_self) { - struct hv_send_ipi_ex **arg; struct hv_send_ipi_ex *ipi_arg; unsigned long flags; int nr_bank = 0; @@ -117,9 +116,8 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector, return false; local_irq_save(flags); - arg = (struct hv_send_ipi_ex **)this_cpu_ptr(hyperv_pcpu_input_arg); + ipi_arg = *this_cpu_ptr(hyperv_pcpu_input_arg); - ipi_arg = *arg; if (unlikely(!ipi_arg)) goto ipi_mask_ex_done; diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 6c04b52f139b..953e280c07c3 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -14,6 +14,7 @@ #include <asm/apic.h> #include <asm/desc.h> #include <asm/sev.h> +#include <asm/ibt.h> #include <asm/hypervisor.h> #include <asm/hyperv-tlfs.h> #include <asm/mshyperv.h> @@ -472,6 +473,26 @@ void __init hyperv_init(void) } /* + * Some versions of Hyper-V that provide IBT in guest VMs have a bug + * in that there's no ENDBR64 instruction at the entry to the + * hypercall page. Because hypercalls are invoked via an indirect call + * to the hypercall page, all hypercall attempts fail when IBT is + * enabled, and Linux panics. For such buggy versions, disable IBT. + * + * Fixed versions of Hyper-V always provide ENDBR64 on the hypercall + * page, so if future Linux kernel versions enable IBT for 32-bit + * builds, additional hypercall page hackery will be required here + * to provide an ENDBR32. + */ +#ifdef CONFIG_X86_KERNEL_IBT + if (cpu_feature_enabled(X86_FEATURE_IBT) && + *(u32 *)hv_hypercall_pg != gen_endbr()) { + setup_clear_cpu_cap(X86_FEATURE_IBT); + pr_warn("Hyper-V: Disabling IBT because of Hyper-V bug\n"); + } +#endif + + /* * hyperv_init() is called before LAPIC is initialized: see * apic_intr_mode_init() -> x86_platform.apic_post_init() and * apic_bsp_setup() -> setup_local_APIC(). The direct-mode STIMER diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 85d38b9f3586..db5d2ea39fc0 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -25,6 +25,10 @@ void __init hv_vtl_init_platform(void) x86_init.irqs.pre_vector_init = x86_init_noop; x86_init.timers.timer_init = x86_init_noop; + /* Avoid searching for BIOS MP tables */ + x86_init.mpparse.find_smp_config = x86_init_noop; + x86_init.mpparse.get_smp_config = x86_init_uint_noop; + x86_platform.get_wallclock = get_rtc_noop; x86_platform.set_wallclock = set_rtc_noop; x86_platform.get_nmi_reason = hv_get_nmi_reason; diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c index 14f46ad2ca64..28be6df88063 100644 --- a/arch/x86/hyperv/ivm.c +++ b/arch/x86/hyperv/ivm.c @@ -247,7 +247,7 @@ EXPORT_SYMBOL_GPL(hv_ghcb_msr_read); static int hv_mark_gpa_visibility(u16 count, const u64 pfn[], enum hv_mem_host_visibility visibility) { - struct hv_gpa_range_for_visibility **input_pcpu, *input; + struct hv_gpa_range_for_visibility *input; u16 pages_processed; u64 hv_status; unsigned long flags; @@ -263,9 +263,8 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[], } local_irq_save(flags); - input_pcpu = (struct hv_gpa_range_for_visibility **) - this_cpu_ptr(hyperv_pcpu_input_arg); - input = *input_pcpu; + input = *this_cpu_ptr(hyperv_pcpu_input_arg); + if (unlikely(!input)) { local_irq_restore(flags); return -EINVAL; diff --git a/arch/x86/hyperv/mmu.c b/arch/x86/hyperv/mmu.c index 8460bd35e10c..1cc113200ff5 100644 --- a/arch/x86/hyperv/mmu.c +++ b/arch/x86/hyperv/mmu.c @@ -61,7 +61,6 @@ static void hyperv_flush_tlb_multi(const struct cpumask *cpus, const struct flush_tlb_info *info) { int cpu, vcpu, gva_n, max_gvas; - struct hv_tlb_flush **flush_pcpu; struct hv_tlb_flush *flush; u64 status; unsigned long flags; @@ -74,10 +73,7 @@ static void hyperv_flush_tlb_multi(const struct cpumask *cpus, local_irq_save(flags); - flush_pcpu = (struct hv_tlb_flush **) - this_cpu_ptr(hyperv_pcpu_input_arg); - - flush = *flush_pcpu; + flush = *this_cpu_ptr(hyperv_pcpu_input_arg); if (unlikely(!flush)) { local_irq_restore(flags); @@ -178,17 +174,13 @@ static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus, const struct flush_tlb_info *info) { int nr_bank = 0, max_gvas, gva_n; - struct hv_tlb_flush_ex **flush_pcpu; struct hv_tlb_flush_ex *flush; u64 status; if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) return HV_STATUS_INVALID_PARAMETER; - flush_pcpu = (struct hv_tlb_flush_ex **) - this_cpu_ptr(hyperv_pcpu_input_arg); - - flush = *flush_pcpu; + flush = *this_cpu_ptr(hyperv_pcpu_input_arg); if (info->mm) { /* diff --git a/arch/x86/hyperv/nested.c b/arch/x86/hyperv/nested.c index 5d70968c8538..9dc259fa322e 100644 --- a/arch/x86/hyperv/nested.c +++ b/arch/x86/hyperv/nested.c @@ -19,7 +19,6 @@ int hyperv_flush_guest_mapping(u64 as) { - struct hv_guest_mapping_flush **flush_pcpu; struct hv_guest_mapping_flush *flush; u64 status; unsigned long flags; @@ -30,10 +29,7 @@ int hyperv_flush_guest_mapping(u64 as) local_irq_save(flags); - flush_pcpu = (struct hv_guest_mapping_flush **) - this_cpu_ptr(hyperv_pcpu_input_arg); - - flush = *flush_pcpu; + flush = *this_cpu_ptr(hyperv_pcpu_input_arg); if (unlikely(!flush)) { local_irq_restore(flags); @@ -90,7 +86,6 @@ EXPORT_SYMBOL_GPL(hyperv_fill_flush_guest_mapping_list); int hyperv_flush_guest_mapping_range(u64 as, hyperv_fill_flush_list_func fill_flush_list_func, void *data) { - struct hv_guest_mapping_flush_list **flush_pcpu; struct hv_guest_mapping_flush_list *flush; u64 status; unsigned long flags; @@ -102,10 +97,8 @@ int hyperv_flush_guest_mapping_range(u64 as, local_irq_save(flags); - flush_pcpu = (struct hv_guest_mapping_flush_list **) - this_cpu_ptr(hyperv_pcpu_input_arg); + flush = *this_cpu_ptr(hyperv_pcpu_input_arg); - flush = *flush_pcpu; if (unlikely(!flush)) { local_irq_restore(flags); goto fault; diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 8eb74cf386db..2888c0ee4df0 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -15,6 +15,7 @@ #include <asm/mpspec.h> #include <asm/x86_init.h> #include <asm/cpufeature.h> +#include <asm/irq_vectors.h> #ifdef CONFIG_ACPI_APEI # include <asm/pgtable_types.h> @@ -31,6 +32,7 @@ extern int acpi_skip_timer_override; extern int acpi_use_timer_override; extern int acpi_fix_pin2_polarity; extern int acpi_disable_cmcff; +extern bool acpi_int_src_ovr[NR_IRQS_LEGACY]; extern u8 acpi_sci_flags; extern u32 acpi_sci_override_gsi; diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index cb8ca46213be..b69b0d7756aa 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -14,7 +14,7 @@ * Defines x86 CPU feature bits */ #define NCAPINTS 21 /* N 32-bit words worth of info */ -#define NBUGINTS 1 /* N 32-bit bug flags */ +#define NBUGINTS 2 /* N 32-bit bug flags */ /* * Note: If the comment begins with a quoted string, that string is used @@ -309,6 +309,10 @@ #define X86_FEATURE_SMBA (11*32+21) /* "" Slow Memory Bandwidth Allocation */ #define X86_FEATURE_BMEC (11*32+22) /* "" Bandwidth Monitoring Event Configuration */ +#define X86_FEATURE_SRSO (11*32+24) /* "" AMD BTB untrain RETs */ +#define X86_FEATURE_SRSO_ALIAS (11*32+25) /* "" AMD BTB untrain RETs through aliasing */ +#define X86_FEATURE_IBPB_ON_VMEXIT (11*32+26) /* "" Issue an IBPB only on VMEXIT */ + /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ #define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */ @@ -442,6 +446,10 @@ #define X86_FEATURE_AUTOIBRS (20*32+ 8) /* "" Automatic IBRS */ #define X86_FEATURE_NO_SMM_CTL_MSR (20*32+ 9) /* "" SMM_CTL MSR is not present */ +#define X86_FEATURE_SBPB (20*32+27) /* "" Selective Branch Prediction Barrier */ +#define X86_FEATURE_IBPB_BRTYPE (20*32+28) /* "" MSR_PRED_CMD[IBPB] flushes all branch type predictions */ +#define X86_FEATURE_SRSO_NO (20*32+29) /* "" CPU is not affected by SRSO */ + /* * BUG word(s) */ @@ -483,5 +491,9 @@ #define X86_BUG_RETBLEED X86_BUG(27) /* CPU is affected by RETBleed */ #define X86_BUG_EIBRS_PBRSB X86_BUG(28) /* EIBRS is vulnerable to Post Barrier RSB Predictions */ #define X86_BUG_SMT_RSB X86_BUG(29) /* CPU is vulnerable to Cross-Thread Return Address Predictions */ +#define X86_BUG_GDS X86_BUG(30) /* CPU is affected by Gather Data Sampling */ +/* BUG word 2 */ +#define X86_BUG_SRSO X86_BUG(1*32 + 0) /* AMD SRSO bug */ +#define X86_BUG_DIV0 X86_BUG(1*32 + 1) /* AMD DIV0 speculation bug */ #endif /* _ASM_X86_CPUFEATURES_H */ diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h index 0953aa32a324..97a3de7892d3 100644 --- a/arch/x86/include/asm/linkage.h +++ b/arch/x86/include/asm/linkage.h @@ -21,7 +21,7 @@ #define FUNCTION_PADDING #endif -#if (CONFIG_FUNCTION_ALIGNMENT > 8) && !defined(__DISABLE_EXPORTS) && !defined(BULID_VDSO) +#if (CONFIG_FUNCTION_ALIGNMENT > 8) && !defined(__DISABLE_EXPORTS) && !defined(BUILD_VDSO) # define __FUNC_ALIGN __ALIGN; FUNCTION_PADDING #else # define __FUNC_ALIGN __ALIGN diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index 88d9ef98e087..fa83d88e4c99 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -5,7 +5,7 @@ #include <linux/types.h> #include <linux/nmi.h> #include <linux/msi.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/hyperv-tlfs.h> #include <asm/nospec-branch.h> #include <asm/paravirt.h> diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index a00a53e15ab7..1d111350197f 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -57,6 +57,7 @@ #define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */ #define PRED_CMD_IBPB BIT(0) /* Indirect Branch Prediction Barrier */ +#define PRED_CMD_SBPB BIT(7) /* Selective Branch Prediction Barrier */ #define MSR_PPIN_CTL 0x0000004e #define MSR_PPIN 0x0000004f @@ -155,6 +156,15 @@ * Not susceptible to Post-Barrier * Return Stack Buffer Predictions. */ +#define ARCH_CAP_GDS_CTRL BIT(25) /* + * CPU is vulnerable to Gather + * Data Sampling (GDS) and + * has controls for mitigation. + */ +#define ARCH_CAP_GDS_NO BIT(26) /* + * CPU is not vulnerable to Gather + * Data Sampling (GDS). + */ #define ARCH_CAP_XAPIC_DISABLE BIT(21) /* * IA32_XAPIC_DISABLE_STATUS MSR @@ -178,6 +188,8 @@ #define RNGDS_MITG_DIS BIT(0) /* SRBDS support */ #define RTM_ALLOW BIT(1) /* TSX development mode */ #define FB_CLEAR_DIS BIT(3) /* CPU Fill buffer clear disable */ +#define GDS_MITG_DIS BIT(4) /* Disable GDS mitigation */ +#define GDS_MITG_LOCKED BIT(5) /* GDS mitigation locked */ #define MSR_IA32_SYSENTER_CS 0x00000174 #define MSR_IA32_SYSENTER_ESP 0x00000175 diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index 1a65cf4acb2b..3faf044569a5 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -211,7 +211,8 @@ * eventually turn into it's own annotation. */ .macro VALIDATE_UNRET_END -#if defined(CONFIG_NOINSTR_VALIDATION) && defined(CONFIG_CPU_UNRET_ENTRY) +#if defined(CONFIG_NOINSTR_VALIDATION) && \ + (defined(CONFIG_CPU_UNRET_ENTRY) || defined(CONFIG_CPU_SRSO)) ANNOTATE_RETPOLINE_SAFE nop #endif @@ -289,13 +290,18 @@ */ .macro UNTRAIN_RET #if defined(CONFIG_CPU_UNRET_ENTRY) || defined(CONFIG_CPU_IBPB_ENTRY) || \ - defined(CONFIG_CALL_DEPTH_TRACKING) + defined(CONFIG_CALL_DEPTH_TRACKING) || defined(CONFIG_CPU_SRSO) VALIDATE_UNRET_END ALTERNATIVE_3 "", \ CALL_ZEN_UNTRAIN_RET, X86_FEATURE_UNRET, \ "call entry_ibpb", X86_FEATURE_ENTRY_IBPB, \ __stringify(RESET_CALL_DEPTH), X86_FEATURE_CALL_DEPTH #endif + +#ifdef CONFIG_CPU_SRSO + ALTERNATIVE_2 "", "call srso_untrain_ret", X86_FEATURE_SRSO, \ + "call srso_untrain_ret_alias", X86_FEATURE_SRSO_ALIAS +#endif .endm .macro UNTRAIN_RET_FROM_CALL @@ -307,6 +313,11 @@ "call entry_ibpb", X86_FEATURE_ENTRY_IBPB, \ __stringify(RESET_CALL_DEPTH_FROM_CALL), X86_FEATURE_CALL_DEPTH #endif + +#ifdef CONFIG_CPU_SRSO + ALTERNATIVE_2 "", "call srso_untrain_ret", X86_FEATURE_SRSO, \ + "call srso_untrain_ret_alias", X86_FEATURE_SRSO_ALIAS +#endif .endm @@ -332,6 +343,8 @@ extern retpoline_thunk_t __x86_indirect_jump_thunk_array[]; extern void __x86_return_thunk(void); extern void zen_untrain_ret(void); +extern void srso_untrain_ret(void); +extern void srso_untrain_ret_alias(void); extern void entry_ibpb(void); #ifdef CONFIG_CALL_THUNKS @@ -479,11 +492,11 @@ void alternative_msr_write(unsigned int msr, u64 val, unsigned int feature) : "memory"); } +extern u64 x86_pred_cmd; + static inline void indirect_branch_prediction_barrier(void) { - u64 val = PRED_CMD_IBPB; - - alternative_msr_write(MSR_IA32_PRED_CMD, val, X86_FEATURE_USE_IBPB); + alternative_msr_write(MSR_IA32_PRED_CMD, x86_pred_cmd, X86_FEATURE_USE_IBPB); } /* The Intel SPEC CTRL MSR base value cache */ diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index d46300e94f85..fd750247ca89 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -586,7 +586,6 @@ extern char ignore_fpu_irq; #define HAVE_ARCH_PICK_MMAP_LAYOUT 1 #define ARCH_HAS_PREFETCHW -#define ARCH_HAS_SPINLOCK_PREFETCH #ifdef CONFIG_X86_32 # define BASE_PREFETCH "" @@ -620,11 +619,6 @@ static __always_inline void prefetchw(const void *x) "m" (*(const char *)x)); } -static inline void spin_lock_prefetch(const void *x) -{ - prefetchw(x); -} - #define TOP_OF_INIT_STACK ((unsigned long)&init_stack + sizeof(init_stack) - \ TOP_OF_KERNEL_STACK_PADDING) @@ -682,9 +676,13 @@ extern u16 get_llc_id(unsigned int cpu); #ifdef CONFIG_CPU_SUP_AMD extern u32 amd_get_nodes_per_socket(void); extern u32 amd_get_highest_perf(void); +extern bool cpu_has_ibpb_brtype_microcode(void); +extern void amd_clear_divider(void); #else static inline u32 amd_get_nodes_per_socket(void) { return 0; } static inline u32 amd_get_highest_perf(void) { return 0; } +static inline bool cpu_has_ibpb_brtype_microcode(void) { return false; } +static inline void amd_clear_divider(void) { } #endif extern unsigned long arch_align_stack(unsigned long sp); @@ -727,4 +725,6 @@ bool arch_is_platform_page(u64 paddr); #define arch_is_platform_page arch_is_platform_page #endif +extern bool gds_ucode_mitigated(void); + #endif /* _ASM_X86_PROCESSOR_H */ diff --git a/arch/x86/include/asm/segment.h b/arch/x86/include/asm/segment.h index 794f69625780..9d6411c65920 100644 --- a/arch/x86/include/asm/segment.h +++ b/arch/x86/include/asm/segment.h @@ -56,7 +56,7 @@ #define GDT_ENTRY_INVALID_SEG 0 -#ifdef CONFIG_X86_32 +#if defined(CONFIG_X86_32) && !defined(BUILD_VDSO32_64) /* * The layout of the per-CPU GDT under Linux: * diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 21b542a6866c..53369c57751e 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -52,6 +52,7 @@ int acpi_lapic; int acpi_ioapic; int acpi_strict; int acpi_disable_cmcff; +bool acpi_int_src_ovr[NR_IRQS_LEGACY]; /* ACPI SCI override configuration */ u8 acpi_sci_flags __initdata; @@ -588,6 +589,9 @@ acpi_parse_int_src_ovr(union acpi_subtable_headers * header, acpi_table_print_madt_entry(&header->common); + if (intsrc->source_irq < NR_IRQS_LEGACY) + acpi_int_src_ovr[intsrc->source_irq] = true; + if (intsrc->source_irq == acpi_gbl_FADT.sci_interrupt) { acpi_sci_ioapic_setup(intsrc->source_irq, intsrc->inti_flags & ACPI_MADT_POLARITY_MASK, diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 26ad7ca423e7..70f9d56f9305 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -73,8 +73,13 @@ static const int amd_erratum_1054[] = static const int amd_zenbleed[] = AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x17, 0x30, 0x0, 0x4f, 0xf), AMD_MODEL_RANGE(0x17, 0x60, 0x0, 0x7f, 0xf), + AMD_MODEL_RANGE(0x17, 0x90, 0x0, 0x91, 0xf), AMD_MODEL_RANGE(0x17, 0xa0, 0x0, 0xaf, 0xf)); +static const int amd_div0[] = + AMD_LEGACY_ERRATUM(AMD_MODEL_RANGE(0x17, 0x00, 0x0, 0x2f, 0xf), + AMD_MODEL_RANGE(0x17, 0x50, 0x0, 0x5f, 0xf)); + static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) { int osvw_id = *erratum++; @@ -1130,6 +1135,11 @@ static void init_amd(struct cpuinfo_x86 *c) WARN_ON_ONCE(msr_set_bit(MSR_EFER, _EFER_AUTOIBRS)); zenbleed_check(c); + + if (cpu_has_amd_erratum(c, amd_div0)) { + pr_notice_once("AMD Zen1 DIV0 bug detected. Disable SMT for full protection.\n"); + setup_force_cpu_bug(X86_BUG_DIV0); + } } #ifdef CONFIG_X86_32 @@ -1290,3 +1300,32 @@ void amd_check_microcode(void) { on_each_cpu(zenbleed_check_cpu, NULL, 1); } + +bool cpu_has_ibpb_brtype_microcode(void) +{ + switch (boot_cpu_data.x86) { + /* Zen1/2 IBPB flushes branch type predictions too. */ + case 0x17: + return boot_cpu_has(X86_FEATURE_AMD_IBPB); + case 0x19: + /* Poke the MSR bit on Zen3/4 to check its presence. */ + if (!wrmsrl_safe(MSR_IA32_PRED_CMD, PRED_CMD_SBPB)) { + setup_force_cpu_cap(X86_FEATURE_SBPB); + return true; + } else { + return false; + } + default: + return false; + } +} + +/* + * Issue a DIV 0/1 insn to clear any division data from previous DIV + * operations. + */ +void noinstr amd_clear_divider(void) +{ + asm volatile(ALTERNATIVE("", "div %2\n\t", X86_BUG_DIV0) + :: "a" (0), "d" (0), "r" (1)); +} diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index 95507448e781..d02f73c5339d 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -47,6 +47,8 @@ static void __init taa_select_mitigation(void); static void __init mmio_select_mitigation(void); static void __init srbds_select_mitigation(void); static void __init l1d_flush_select_mitigation(void); +static void __init srso_select_mitigation(void); +static void __init gds_select_mitigation(void); /* The base value of the SPEC_CTRL MSR without task-specific bits set */ u64 x86_spec_ctrl_base; @@ -56,6 +58,9 @@ EXPORT_SYMBOL_GPL(x86_spec_ctrl_base); DEFINE_PER_CPU(u64, x86_spec_ctrl_current); EXPORT_SYMBOL_GPL(x86_spec_ctrl_current); +u64 x86_pred_cmd __ro_after_init = PRED_CMD_IBPB; +EXPORT_SYMBOL_GPL(x86_pred_cmd); + static DEFINE_MUTEX(spec_ctrl_mutex); /* Update SPEC_CTRL MSR and its cached copy unconditionally */ @@ -160,6 +165,8 @@ void __init cpu_select_mitigations(void) md_clear_select_mitigation(); srbds_select_mitigation(); l1d_flush_select_mitigation(); + srso_select_mitigation(); + gds_select_mitigation(); } /* @@ -646,6 +653,149 @@ static int __init l1d_flush_parse_cmdline(char *str) early_param("l1d_flush", l1d_flush_parse_cmdline); #undef pr_fmt +#define pr_fmt(fmt) "GDS: " fmt + +enum gds_mitigations { + GDS_MITIGATION_OFF, + GDS_MITIGATION_UCODE_NEEDED, + GDS_MITIGATION_FORCE, + GDS_MITIGATION_FULL, + GDS_MITIGATION_FULL_LOCKED, + GDS_MITIGATION_HYPERVISOR, +}; + +#if IS_ENABLED(CONFIG_GDS_FORCE_MITIGATION) +static enum gds_mitigations gds_mitigation __ro_after_init = GDS_MITIGATION_FORCE; +#else +static enum gds_mitigations gds_mitigation __ro_after_init = GDS_MITIGATION_FULL; +#endif + +static const char * const gds_strings[] = { + [GDS_MITIGATION_OFF] = "Vulnerable", + [GDS_MITIGATION_UCODE_NEEDED] = "Vulnerable: No microcode", + [GDS_MITIGATION_FORCE] = "Mitigation: AVX disabled, no microcode", + [GDS_MITIGATION_FULL] = "Mitigation: Microcode", + [GDS_MITIGATION_FULL_LOCKED] = "Mitigation: Microcode (locked)", + [GDS_MITIGATION_HYPERVISOR] = "Unknown: Dependent on hypervisor status", +}; + +bool gds_ucode_mitigated(void) +{ + return (gds_mitigation == GDS_MITIGATION_FULL || + gds_mitigation == GDS_MITIGATION_FULL_LOCKED); +} +EXPORT_SYMBOL_GPL(gds_ucode_mitigated); + +void update_gds_msr(void) +{ + u64 mcu_ctrl_after; + u64 mcu_ctrl; + + switch (gds_mitigation) { + case GDS_MITIGATION_OFF: + rdmsrl(MSR_IA32_MCU_OPT_CTRL, mcu_ctrl); + mcu_ctrl |= GDS_MITG_DIS; + break; + case GDS_MITIGATION_FULL_LOCKED: + /* + * The LOCKED state comes from the boot CPU. APs might not have + * the same state. Make sure the mitigation is enabled on all + * CPUs. + */ + case GDS_MITIGATION_FULL: + rdmsrl(MSR_IA32_MCU_OPT_CTRL, mcu_ctrl); + mcu_ctrl &= ~GDS_MITG_DIS; + break; + case GDS_MITIGATION_FORCE: + case GDS_MITIGATION_UCODE_NEEDED: + case GDS_MITIGATION_HYPERVISOR: + return; + }; + + wrmsrl(MSR_IA32_MCU_OPT_CTRL, mcu_ctrl); + + /* + * Check to make sure that the WRMSR value was not ignored. Writes to + * GDS_MITG_DIS will be ignored if this processor is locked but the boot + * processor was not. + */ + rdmsrl(MSR_IA32_MCU_OPT_CTRL, mcu_ctrl_after); + WARN_ON_ONCE(mcu_ctrl != mcu_ctrl_after); +} + +static void __init gds_select_mitigation(void) +{ + u64 mcu_ctrl; + + if (!boot_cpu_has_bug(X86_BUG_GDS)) + return; + + if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) { + gds_mitigation = GDS_MITIGATION_HYPERVISOR; + goto out; + } + + if (cpu_mitigations_off()) + gds_mitigation = GDS_MITIGATION_OFF; + /* Will verify below that mitigation _can_ be disabled */ + + /* No microcode */ + if (!(x86_read_arch_cap_msr() & ARCH_CAP_GDS_CTRL)) { + if (gds_mitigation == GDS_MITIGATION_FORCE) { + /* + * This only needs to be done on the boot CPU so do it + * here rather than in update_gds_msr() + */ + setup_clear_cpu_cap(X86_FEATURE_AVX); + pr_warn("Microcode update needed! Disabling AVX as mitigation.\n"); + } else { + gds_mitigation = GDS_MITIGATION_UCODE_NEEDED; + } + goto out; + } + + /* Microcode has mitigation, use it */ + if (gds_mitigation == GDS_MITIGATION_FORCE) + gds_mitigation = GDS_MITIGATION_FULL; + + rdmsrl(MSR_IA32_MCU_OPT_CTRL, mcu_ctrl); + if (mcu_ctrl & GDS_MITG_LOCKED) { + if (gds_mitigation == GDS_MITIGATION_OFF) + pr_warn("Mitigation locked. Disable failed.\n"); + + /* + * The mitigation is selected from the boot CPU. All other CPUs + * _should_ have the same state. If the boot CPU isn't locked + * but others are then update_gds_msr() will WARN() of the state + * mismatch. If the boot CPU is locked update_gds_msr() will + * ensure the other CPUs have the mitigation enabled. + */ + gds_mitigation = GDS_MITIGATION_FULL_LOCKED; + } + + update_gds_msr(); +out: + pr_info("%s\n", gds_strings[gds_mitigation]); +} + +static int __init gds_parse_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (!boot_cpu_has_bug(X86_BUG_GDS)) + return 0; + + if (!strcmp(str, "off")) + gds_mitigation = GDS_MITIGATION_OFF; + else if (!strcmp(str, "force")) + gds_mitigation = GDS_MITIGATION_FORCE; + + return 0; +} +early_param("gather_data_sampling", gds_parse_cmdline); + +#undef pr_fmt #define pr_fmt(fmt) "Spectre V1 : " fmt enum spectre_v1_mitigation { @@ -2188,6 +2338,165 @@ static int __init l1tf_cmdline(char *str) early_param("l1tf", l1tf_cmdline); #undef pr_fmt +#define pr_fmt(fmt) "Speculative Return Stack Overflow: " fmt + +enum srso_mitigation { + SRSO_MITIGATION_NONE, + SRSO_MITIGATION_MICROCODE, + SRSO_MITIGATION_SAFE_RET, + SRSO_MITIGATION_IBPB, + SRSO_MITIGATION_IBPB_ON_VMEXIT, +}; + +enum srso_mitigation_cmd { + SRSO_CMD_OFF, + SRSO_CMD_MICROCODE, + SRSO_CMD_SAFE_RET, + SRSO_CMD_IBPB, + SRSO_CMD_IBPB_ON_VMEXIT, +}; + +static const char * const srso_strings[] = { + [SRSO_MITIGATION_NONE] = "Vulnerable", + [SRSO_MITIGATION_MICROCODE] = "Mitigation: microcode", + [SRSO_MITIGATION_SAFE_RET] = "Mitigation: safe RET", + [SRSO_MITIGATION_IBPB] = "Mitigation: IBPB", + [SRSO_MITIGATION_IBPB_ON_VMEXIT] = "Mitigation: IBPB on VMEXIT only" +}; + +static enum srso_mitigation srso_mitigation __ro_after_init = SRSO_MITIGATION_NONE; +static enum srso_mitigation_cmd srso_cmd __ro_after_init = SRSO_CMD_SAFE_RET; + +static int __init srso_parse_cmdline(char *str) +{ + if (!str) + return -EINVAL; + + if (!strcmp(str, "off")) + srso_cmd = SRSO_CMD_OFF; + else if (!strcmp(str, "microcode")) + srso_cmd = SRSO_CMD_MICROCODE; + else if (!strcmp(str, "safe-ret")) + srso_cmd = SRSO_CMD_SAFE_RET; + else if (!strcmp(str, "ibpb")) + srso_cmd = SRSO_CMD_IBPB; + else if (!strcmp(str, "ibpb-vmexit")) + srso_cmd = SRSO_CMD_IBPB_ON_VMEXIT; + else + pr_err("Ignoring unknown SRSO option (%s).", str); + + return 0; +} +early_param("spec_rstack_overflow", srso_parse_cmdline); + +#define SRSO_NOTICE "WARNING: See https://kernel.org/doc/html/latest/admin-guide/hw-vuln/srso.html for mitigation options." + +static void __init srso_select_mitigation(void) +{ + bool has_microcode; + + if (!boot_cpu_has_bug(X86_BUG_SRSO) || cpu_mitigations_off()) + goto pred_cmd; + + /* + * The first check is for the kernel running as a guest in order + * for guests to verify whether IBPB is a viable mitigation. + */ + has_microcode = boot_cpu_has(X86_FEATURE_IBPB_BRTYPE) || cpu_has_ibpb_brtype_microcode(); + if (!has_microcode) { + pr_warn("IBPB-extending microcode not applied!\n"); + pr_warn(SRSO_NOTICE); + } else { + /* + * Enable the synthetic (even if in a real CPUID leaf) + * flags for guests. + */ + setup_force_cpu_cap(X86_FEATURE_IBPB_BRTYPE); + + /* + * Zen1/2 with SMT off aren't vulnerable after the right + * IBPB microcode has been applied. + */ + if ((boot_cpu_data.x86 < 0x19) && + (!cpu_smt_possible() || (cpu_smt_control == CPU_SMT_DISABLED))) + setup_force_cpu_cap(X86_FEATURE_SRSO_NO); + } + + if (retbleed_mitigation == RETBLEED_MITIGATION_IBPB) { + if (has_microcode) { + pr_err("Retbleed IBPB mitigation enabled, using same for SRSO\n"); + srso_mitigation = SRSO_MITIGATION_IBPB; + goto pred_cmd; + } + } + + switch (srso_cmd) { + case SRSO_CMD_OFF: + return; + + case SRSO_CMD_MICROCODE: + if (has_microcode) { + srso_mitigation = SRSO_MITIGATION_MICROCODE; + pr_warn(SRSO_NOTICE); + } + break; + + case SRSO_CMD_SAFE_RET: + if (IS_ENABLED(CONFIG_CPU_SRSO)) { + /* + * Enable the return thunk for generated code + * like ftrace, static_call, etc. + */ + setup_force_cpu_cap(X86_FEATURE_RETHUNK); + + if (boot_cpu_data.x86 == 0x19) + setup_force_cpu_cap(X86_FEATURE_SRSO_ALIAS); + else + setup_force_cpu_cap(X86_FEATURE_SRSO); + srso_mitigation = SRSO_MITIGATION_SAFE_RET; + } else { + pr_err("WARNING: kernel not compiled with CPU_SRSO.\n"); + goto pred_cmd; + } + break; + + case SRSO_CMD_IBPB: + if (IS_ENABLED(CONFIG_CPU_IBPB_ENTRY)) { + if (has_microcode) { + setup_force_cpu_cap(X86_FEATURE_ENTRY_IBPB); + srso_mitigation = SRSO_MITIGATION_IBPB; + } + } else { + pr_err("WARNING: kernel not compiled with CPU_IBPB_ENTRY.\n"); + goto pred_cmd; + } + break; + + case SRSO_CMD_IBPB_ON_VMEXIT: + if (IS_ENABLED(CONFIG_CPU_SRSO)) { + if (!boot_cpu_has(X86_FEATURE_ENTRY_IBPB) && has_microcode) { + setup_force_cpu_cap(X86_FEATURE_IBPB_ON_VMEXIT); + srso_mitigation = SRSO_MITIGATION_IBPB_ON_VMEXIT; + } + } else { + pr_err("WARNING: kernel not compiled with CPU_SRSO.\n"); + goto pred_cmd; + } + break; + + default: + break; + } + + pr_info("%s%s\n", srso_strings[srso_mitigation], (has_microcode ? "" : ", no microcode")); + +pred_cmd: + if ((boot_cpu_has(X86_FEATURE_SRSO_NO) || srso_cmd == SRSO_CMD_OFF) && + boot_cpu_has(X86_FEATURE_SBPB)) + x86_pred_cmd = PRED_CMD_SBPB; +} + +#undef pr_fmt #define pr_fmt(fmt) fmt #ifdef CONFIG_SYSFS @@ -2385,6 +2694,18 @@ static ssize_t retbleed_show_state(char *buf) return sysfs_emit(buf, "%s\n", retbleed_strings[retbleed_mitigation]); } +static ssize_t srso_show_state(char *buf) +{ + return sysfs_emit(buf, "%s%s\n", + srso_strings[srso_mitigation], + (cpu_has_ibpb_brtype_microcode() ? "" : ", no microcode")); +} + +static ssize_t gds_show_state(char *buf) +{ + return sysfs_emit(buf, "%s\n", gds_strings[gds_mitigation]); +} + static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr, char *buf, unsigned int bug) { @@ -2434,6 +2755,12 @@ static ssize_t cpu_show_common(struct device *dev, struct device_attribute *attr case X86_BUG_RETBLEED: return retbleed_show_state(buf); + case X86_BUG_SRSO: + return srso_show_state(buf); + + case X86_BUG_GDS: + return gds_show_state(buf); + default: break; } @@ -2498,4 +2825,14 @@ ssize_t cpu_show_retbleed(struct device *dev, struct device_attribute *attr, cha { return cpu_show_common(dev, attr, buf, X86_BUG_RETBLEED); } + +ssize_t cpu_show_spec_rstack_overflow(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_SRSO); +} + +ssize_t cpu_show_gds(struct device *dev, struct device_attribute *attr, char *buf) +{ + return cpu_show_common(dev, attr, buf, X86_BUG_GDS); +} #endif diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 0ba1067f4e5f..e3a65e9fc750 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1250,6 +1250,10 @@ static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { #define RETBLEED BIT(3) /* CPU is affected by SMT (cross-thread) return predictions */ #define SMT_RSB BIT(4) +/* CPU is affected by SRSO */ +#define SRSO BIT(5) +/* CPU is affected by GDS */ +#define GDS BIT(6) static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { VULNBL_INTEL_STEPPINGS(IVYBRIDGE, X86_STEPPING_ANY, SRBDS), @@ -1262,27 +1266,30 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { VULNBL_INTEL_STEPPINGS(BROADWELL_X, X86_STEPPING_ANY, MMIO), VULNBL_INTEL_STEPPINGS(BROADWELL, X86_STEPPING_ANY, SRBDS), VULNBL_INTEL_STEPPINGS(SKYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED), + VULNBL_INTEL_STEPPINGS(SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(SKYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), + VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), + VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(CANNONLAKE_L, X86_STEPPING_ANY, RETBLEED), - VULNBL_INTEL_STEPPINGS(ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED), - VULNBL_INTEL_STEPPINGS(ICELAKE_D, X86_STEPPING_ANY, MMIO), - VULNBL_INTEL_STEPPINGS(ICELAKE_X, X86_STEPPING_ANY, MMIO), - VULNBL_INTEL_STEPPINGS(COMETLAKE, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED), + VULNBL_INTEL_STEPPINGS(ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS), + VULNBL_INTEL_STEPPINGS(ICELAKE_D, X86_STEPPING_ANY, MMIO | GDS), + VULNBL_INTEL_STEPPINGS(ICELAKE_X, X86_STEPPING_ANY, MMIO | GDS), + VULNBL_INTEL_STEPPINGS(COMETLAKE, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(COMETLAKE_L, X86_STEPPINGS(0x0, 0x0), MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(COMETLAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED), + VULNBL_INTEL_STEPPINGS(COMETLAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS), + VULNBL_INTEL_STEPPINGS(TIGERLAKE_L, X86_STEPPING_ANY, GDS), + VULNBL_INTEL_STEPPINGS(TIGERLAKE, X86_STEPPING_ANY, GDS), VULNBL_INTEL_STEPPINGS(LAKEFIELD, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED), - VULNBL_INTEL_STEPPINGS(ROCKETLAKE, X86_STEPPING_ANY, MMIO | RETBLEED), + VULNBL_INTEL_STEPPINGS(ROCKETLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(ATOM_TREMONT, X86_STEPPING_ANY, MMIO | MMIO_SBDS), VULNBL_INTEL_STEPPINGS(ATOM_TREMONT_D, X86_STEPPING_ANY, MMIO), VULNBL_INTEL_STEPPINGS(ATOM_TREMONT_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS), VULNBL_AMD(0x15, RETBLEED), VULNBL_AMD(0x16, RETBLEED), - VULNBL_AMD(0x17, RETBLEED | SMT_RSB), + VULNBL_AMD(0x17, RETBLEED | SMT_RSB | SRSO), VULNBL_HYGON(0x18, RETBLEED | SMT_RSB), + VULNBL_AMD(0x19, SRSO), {} }; @@ -1406,6 +1413,21 @@ static void __init cpu_set_bug_bits(struct cpuinfo_x86 *c) if (cpu_matches(cpu_vuln_blacklist, SMT_RSB)) setup_force_cpu_bug(X86_BUG_SMT_RSB); + if (!cpu_has(c, X86_FEATURE_SRSO_NO)) { + if (cpu_matches(cpu_vuln_blacklist, SRSO)) + setup_force_cpu_bug(X86_BUG_SRSO); + } + + /* + * Check if CPU is vulnerable to GDS. If running in a virtual machine on + * an affected processor, the VMM may have disabled the use of GATHER by + * disabling AVX2. The only way to do this in HW is to clear XCR0[2], + * which means that AVX will be disabled. + */ + if (cpu_matches(cpu_vuln_blacklist, GDS) && !(ia32_cap & ARCH_CAP_GDS_NO) && + boot_cpu_has(X86_FEATURE_AVX)) + setup_force_cpu_bug(X86_BUG_GDS); + if (cpu_matches(cpu_vuln_whitelist, NO_MELTDOWN)) return; @@ -1962,6 +1984,8 @@ void identify_secondary_cpu(struct cpuinfo_x86 *c) validate_apic_and_package_id(c); x86_spec_ctrl_setup_ap(); update_srbds_msr(); + if (boot_cpu_has_bug(X86_BUG_GDS)) + update_gds_msr(); tsx_ap_init(); } diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index 1c44630d4789..1dcd7d4e38ef 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -83,6 +83,7 @@ void cpu_select_mitigations(void); extern void x86_spec_ctrl_setup_ap(void); extern void update_srbds_msr(void); +extern void update_gds_msr(void); extern enum spectre_v2_mitigation spectre_v2_enabled; diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 4a817d20ce3b..1885326a8f65 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -206,6 +206,8 @@ DEFINE_IDTENTRY(exc_divide_error) { do_error_trap(regs, 0, "divide error", X86_TRAP_DE, SIGFPE, FPE_INTDIV, error_get_trap_addr(regs)); + + amd_clear_divider(); } DEFINE_IDTENTRY(exc_overflow) diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 03c885d3640f..ef06211bae4c 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -134,13 +134,27 @@ SECTIONS SOFTIRQENTRY_TEXT #ifdef CONFIG_RETPOLINE __indirect_thunk_start = .; - *(.text.__x86.*) + *(.text.__x86.indirect_thunk) + *(.text.__x86.return_thunk) __indirect_thunk_end = .; #endif STATIC_CALL_TEXT ALIGN_ENTRY_TEXT_BEGIN +#ifdef CONFIG_CPU_SRSO + *(.text.__x86.rethunk_untrain) +#endif + ENTRY_TEXT + +#ifdef CONFIG_CPU_SRSO + /* + * See the comment above srso_untrain_ret_alias()'s + * definition. + */ + . = srso_untrain_ret_alias | (1 << 2) | (1 << 8) | (1 << 14) | (1 << 20); + *(.text.__x86.rethunk_safe) +#endif ALIGN_ENTRY_TEXT_END *(.gnu.warning) @@ -509,7 +523,24 @@ INIT_PER_CPU(irq_stack_backing_store); #endif #ifdef CONFIG_RETHUNK -. = ASSERT((__x86_return_thunk & 0x3f) == 0, "__x86_return_thunk not cacheline-aligned"); +. = ASSERT((__ret & 0x3f) == 0, "__ret not cacheline-aligned"); +. = ASSERT((srso_safe_ret & 0x3f) == 0, "srso_safe_ret not cacheline-aligned"); +#endif + +#ifdef CONFIG_CPU_SRSO +/* + * GNU ld cannot do XOR until 2.41. + * https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=f6f78318fca803c4907fb8d7f6ded8295f1947b1 + * + * LLVM lld cannot do XOR until lld-17. + * https://github.com/llvm/llvm-project/commit/fae96104d4378166cbe5c875ef8ed808a356f3fb + * + * Instead do: (A | B) - (A & B) in order to compute the XOR + * of the two function addresses: + */ +. = ASSERT(((ABSOLUTE(srso_untrain_ret_alias) | srso_safe_ret_alias) - + (ABSOLUTE(srso_untrain_ret_alias) & srso_safe_ret_alias)) == ((1 << 2) | (1 << 8) | (1 << 14) | (1 << 20)), + "SRSO function pair won't alias"); #endif #endif /* CONFIG_X86_64 */ diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 7f4d13383cf2..d3432687c9e6 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -729,6 +729,9 @@ void kvm_set_cpu_caps(void) F(NULL_SEL_CLR_BASE) | F(AUTOIBRS) | 0 /* PrefetchCtlMsr */ ); + if (cpu_feature_enabled(X86_FEATURE_SRSO_NO)) + kvm_cpu_cap_set(X86_FEATURE_SRSO_NO); + kvm_cpu_cap_init_kvm_defined(CPUID_8000_0022_EAX, F(PERFMON_V2) ); diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 07756b7348ae..d3aec1f2cad2 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -2417,15 +2417,18 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm) */ memset(vcpu->arch.regs, 0, sizeof(vcpu->arch.regs)); - vcpu->arch.regs[VCPU_REGS_RAX] = ghcb_get_rax_if_valid(ghcb); - vcpu->arch.regs[VCPU_REGS_RBX] = ghcb_get_rbx_if_valid(ghcb); - vcpu->arch.regs[VCPU_REGS_RCX] = ghcb_get_rcx_if_valid(ghcb); - vcpu->arch.regs[VCPU_REGS_RDX] = ghcb_get_rdx_if_valid(ghcb); - vcpu->arch.regs[VCPU_REGS_RSI] = ghcb_get_rsi_if_valid(ghcb); + BUILD_BUG_ON(sizeof(svm->sev_es.valid_bitmap) != sizeof(ghcb->save.valid_bitmap)); + memcpy(&svm->sev_es.valid_bitmap, &ghcb->save.valid_bitmap, sizeof(ghcb->save.valid_bitmap)); - svm->vmcb->save.cpl = ghcb_get_cpl_if_valid(ghcb); + vcpu->arch.regs[VCPU_REGS_RAX] = kvm_ghcb_get_rax_if_valid(svm, ghcb); + vcpu->arch.regs[VCPU_REGS_RBX] = kvm_ghcb_get_rbx_if_valid(svm, ghcb); + vcpu->arch.regs[VCPU_REGS_RCX] = kvm_ghcb_get_rcx_if_valid(svm, ghcb); + vcpu->arch.regs[VCPU_REGS_RDX] = kvm_ghcb_get_rdx_if_valid(svm, ghcb); + vcpu->arch.regs[VCPU_REGS_RSI] = kvm_ghcb_get_rsi_if_valid(svm, ghcb); - if (ghcb_xcr0_is_valid(ghcb)) { + svm->vmcb->save.cpl = kvm_ghcb_get_cpl_if_valid(svm, ghcb); + + if (kvm_ghcb_xcr0_is_valid(svm)) { vcpu->arch.xcr0 = ghcb_get_xcr0(ghcb); kvm_update_cpuid_runtime(vcpu); } @@ -2436,84 +2439,88 @@ static void sev_es_sync_from_ghcb(struct vcpu_svm *svm) control->exit_code_hi = upper_32_bits(exit_code); control->exit_info_1 = ghcb_get_sw_exit_info_1(ghcb); control->exit_info_2 = ghcb_get_sw_exit_info_2(ghcb); + svm->sev_es.sw_scratch = kvm_ghcb_get_sw_scratch_if_valid(svm, ghcb); /* Clear the valid entries fields */ memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); } +static u64 kvm_ghcb_get_sw_exit_code(struct vmcb_control_area *control) +{ + return (((u64)control->exit_code_hi) << 32) | control->exit_code; +} + static int sev_es_validate_vmgexit(struct vcpu_svm *svm) { - struct kvm_vcpu *vcpu; - struct ghcb *ghcb; + struct vmcb_control_area *control = &svm->vmcb->control; + struct kvm_vcpu *vcpu = &svm->vcpu; u64 exit_code; u64 reason; - ghcb = svm->sev_es.ghcb; - /* * Retrieve the exit code now even though it may not be marked valid * as it could help with debugging. */ - exit_code = ghcb_get_sw_exit_code(ghcb); + exit_code = kvm_ghcb_get_sw_exit_code(control); /* Only GHCB Usage code 0 is supported */ - if (ghcb->ghcb_usage) { + if (svm->sev_es.ghcb->ghcb_usage) { reason = GHCB_ERR_INVALID_USAGE; goto vmgexit_err; } reason = GHCB_ERR_MISSING_INPUT; - if (!ghcb_sw_exit_code_is_valid(ghcb) || - !ghcb_sw_exit_info_1_is_valid(ghcb) || - !ghcb_sw_exit_info_2_is_valid(ghcb)) + if (!kvm_ghcb_sw_exit_code_is_valid(svm) || + !kvm_ghcb_sw_exit_info_1_is_valid(svm) || + !kvm_ghcb_sw_exit_info_2_is_valid(svm)) goto vmgexit_err; - switch (ghcb_get_sw_exit_code(ghcb)) { + switch (exit_code) { case SVM_EXIT_READ_DR7: break; case SVM_EXIT_WRITE_DR7: - if (!ghcb_rax_is_valid(ghcb)) + if (!kvm_ghcb_rax_is_valid(svm)) goto vmgexit_err; break; case SVM_EXIT_RDTSC: break; case SVM_EXIT_RDPMC: - if (!ghcb_rcx_is_valid(ghcb)) + if (!kvm_ghcb_rcx_is_valid(svm)) goto vmgexit_err; break; case SVM_EXIT_CPUID: - if (!ghcb_rax_is_valid(ghcb) || - !ghcb_rcx_is_valid(ghcb)) + if (!kvm_ghcb_rax_is_valid(svm) || + !kvm_ghcb_rcx_is_valid(svm)) goto vmgexit_err; - if (ghcb_get_rax(ghcb) == 0xd) - if (!ghcb_xcr0_is_valid(ghcb)) + if (vcpu->arch.regs[VCPU_REGS_RAX] == 0xd) + if (!kvm_ghcb_xcr0_is_valid(svm)) goto vmgexit_err; break; case SVM_EXIT_INVD: break; case SVM_EXIT_IOIO: - if (ghcb_get_sw_exit_info_1(ghcb) & SVM_IOIO_STR_MASK) { - if (!ghcb_sw_scratch_is_valid(ghcb)) + if (control->exit_info_1 & SVM_IOIO_STR_MASK) { + if (!kvm_ghcb_sw_scratch_is_valid(svm)) goto vmgexit_err; } else { - if (!(ghcb_get_sw_exit_info_1(ghcb) & SVM_IOIO_TYPE_MASK)) - if (!ghcb_rax_is_valid(ghcb)) + if (!(control->exit_info_1 & SVM_IOIO_TYPE_MASK)) + if (!kvm_ghcb_rax_is_valid(svm)) goto vmgexit_err; } break; case SVM_EXIT_MSR: - if (!ghcb_rcx_is_valid(ghcb)) + if (!kvm_ghcb_rcx_is_valid(svm)) goto vmgexit_err; - if (ghcb_get_sw_exit_info_1(ghcb)) { - if (!ghcb_rax_is_valid(ghcb) || - !ghcb_rdx_is_valid(ghcb)) + if (control->exit_info_1) { + if (!kvm_ghcb_rax_is_valid(svm) || + !kvm_ghcb_rdx_is_valid(svm)) goto vmgexit_err; } break; case SVM_EXIT_VMMCALL: - if (!ghcb_rax_is_valid(ghcb) || - !ghcb_cpl_is_valid(ghcb)) + if (!kvm_ghcb_rax_is_valid(svm) || + !kvm_ghcb_cpl_is_valid(svm)) goto vmgexit_err; break; case SVM_EXIT_RDTSCP: @@ -2521,19 +2528,19 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) case SVM_EXIT_WBINVD: break; case SVM_EXIT_MONITOR: - if (!ghcb_rax_is_valid(ghcb) || - !ghcb_rcx_is_valid(ghcb) || - !ghcb_rdx_is_valid(ghcb)) + if (!kvm_ghcb_rax_is_valid(svm) || + !kvm_ghcb_rcx_is_valid(svm) || + !kvm_ghcb_rdx_is_valid(svm)) goto vmgexit_err; break; case SVM_EXIT_MWAIT: - if (!ghcb_rax_is_valid(ghcb) || - !ghcb_rcx_is_valid(ghcb)) + if (!kvm_ghcb_rax_is_valid(svm) || + !kvm_ghcb_rcx_is_valid(svm)) goto vmgexit_err; break; case SVM_VMGEXIT_MMIO_READ: case SVM_VMGEXIT_MMIO_WRITE: - if (!ghcb_sw_scratch_is_valid(ghcb)) + if (!kvm_ghcb_sw_scratch_is_valid(svm)) goto vmgexit_err; break; case SVM_VMGEXIT_NMI_COMPLETE: @@ -2549,11 +2556,9 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm) return 0; vmgexit_err: - vcpu = &svm->vcpu; - if (reason == GHCB_ERR_INVALID_USAGE) { vcpu_unimpl(vcpu, "vmgexit: ghcb usage %#x is not valid\n", - ghcb->ghcb_usage); + svm->sev_es.ghcb->ghcb_usage); } else if (reason == GHCB_ERR_INVALID_EVENT) { vcpu_unimpl(vcpu, "vmgexit: exit code %#llx is not valid\n", exit_code); @@ -2563,11 +2568,8 @@ vmgexit_err: dump_ghcb(svm); } - /* Clear the valid entries fields */ - memset(ghcb->save.valid_bitmap, 0, sizeof(ghcb->save.valid_bitmap)); - - ghcb_set_sw_exit_info_1(ghcb, 2); - ghcb_set_sw_exit_info_2(ghcb, reason); + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, reason); /* Resume the guest to "return" the error code. */ return 1; @@ -2586,7 +2588,7 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm) */ if (svm->sev_es.ghcb_sa_sync) { kvm_write_guest(svm->vcpu.kvm, - ghcb_get_sw_scratch(svm->sev_es.ghcb), + svm->sev_es.sw_scratch, svm->sev_es.ghcb_sa, svm->sev_es.ghcb_sa_len); svm->sev_es.ghcb_sa_sync = false; @@ -2632,12 +2634,11 @@ void pre_sev_run(struct vcpu_svm *svm, int cpu) static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) { struct vmcb_control_area *control = &svm->vmcb->control; - struct ghcb *ghcb = svm->sev_es.ghcb; u64 ghcb_scratch_beg, ghcb_scratch_end; u64 scratch_gpa_beg, scratch_gpa_end; void *scratch_va; - scratch_gpa_beg = ghcb_get_sw_scratch(ghcb); + scratch_gpa_beg = svm->sev_es.sw_scratch; if (!scratch_gpa_beg) { pr_err("vmgexit: scratch gpa not provided\n"); goto e_scratch; @@ -2708,8 +2709,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) return 0; e_scratch: - ghcb_set_sw_exit_info_1(ghcb, 2); - ghcb_set_sw_exit_info_2(ghcb, GHCB_ERR_INVALID_SCRATCH_AREA); + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_SCRATCH_AREA); return 1; } @@ -2822,7 +2823,6 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); struct vmcb_control_area *control = &svm->vmcb->control; u64 ghcb_gpa, exit_code; - struct ghcb *ghcb; int ret; /* Validate the GHCB */ @@ -2847,20 +2847,18 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) } svm->sev_es.ghcb = svm->sev_es.ghcb_map.hva; - ghcb = svm->sev_es.ghcb_map.hva; - trace_kvm_vmgexit_enter(vcpu->vcpu_id, ghcb); - - exit_code = ghcb_get_sw_exit_code(ghcb); + trace_kvm_vmgexit_enter(vcpu->vcpu_id, svm->sev_es.ghcb); + sev_es_sync_from_ghcb(svm); ret = sev_es_validate_vmgexit(svm); if (ret) return ret; - sev_es_sync_from_ghcb(svm); - ghcb_set_sw_exit_info_1(ghcb, 0); - ghcb_set_sw_exit_info_2(ghcb, 0); + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 0); + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, 0); + exit_code = kvm_ghcb_get_sw_exit_code(control); switch (exit_code) { case SVM_VMGEXIT_MMIO_READ: ret = setup_vmgexit_scratch(svm, true, control->exit_info_2); @@ -2898,13 +2896,13 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) break; case 1: /* Get AP jump table address */ - ghcb_set_sw_exit_info_2(ghcb, sev->ap_jump_table); + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, sev->ap_jump_table); break; default: pr_err("svm: vmgexit: unsupported AP jump table request - exit_info_1=%#llx\n", control->exit_info_1); - ghcb_set_sw_exit_info_1(ghcb, 2); - ghcb_set_sw_exit_info_2(ghcb, GHCB_ERR_INVALID_INPUT); + ghcb_set_sw_exit_info_1(svm->sev_es.ghcb, 2); + ghcb_set_sw_exit_info_2(svm->sev_es.ghcb, GHCB_ERR_INVALID_INPUT); } ret = 1; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 956726d867aa..03e852dedcc1 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1498,7 +1498,9 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (sd->current_vmcb != svm->vmcb) { sd->current_vmcb = svm->vmcb; - indirect_branch_prediction_barrier(); + + if (!cpu_feature_enabled(X86_FEATURE_IBPB_ON_VMEXIT)) + indirect_branch_prediction_barrier(); } if (kvm_vcpu_apicv_active(vcpu)) avic_vcpu_load(vcpu, cpu); diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 18af7e712a5a..8239c8de45ac 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -190,10 +190,12 @@ struct vcpu_sev_es_state { /* SEV-ES support */ struct sev_es_save_area *vmsa; struct ghcb *ghcb; + u8 valid_bitmap[16]; struct kvm_host_map ghcb_map; bool received_first_sipi; /* SEV-ES scratch area support */ + u64 sw_scratch; void *ghcb_sa; u32 ghcb_sa_len; bool ghcb_sa_sync; @@ -744,4 +746,28 @@ void sev_es_unmap_ghcb(struct vcpu_svm *svm); void __svm_sev_es_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted); void __svm_vcpu_run(struct vcpu_svm *svm, bool spec_ctrl_intercepted); +#define DEFINE_KVM_GHCB_ACCESSORS(field) \ + static __always_inline bool kvm_ghcb_##field##_is_valid(const struct vcpu_svm *svm) \ + { \ + return test_bit(GHCB_BITMAP_IDX(field), \ + (unsigned long *)&svm->sev_es.valid_bitmap); \ + } \ + \ + static __always_inline u64 kvm_ghcb_get_##field##_if_valid(struct vcpu_svm *svm, struct ghcb *ghcb) \ + { \ + return kvm_ghcb_##field##_is_valid(svm) ? ghcb->save.field : 0; \ + } \ + +DEFINE_KVM_GHCB_ACCESSORS(cpl) +DEFINE_KVM_GHCB_ACCESSORS(rax) +DEFINE_KVM_GHCB_ACCESSORS(rcx) +DEFINE_KVM_GHCB_ACCESSORS(rdx) +DEFINE_KVM_GHCB_ACCESSORS(rbx) +DEFINE_KVM_GHCB_ACCESSORS(rsi) +DEFINE_KVM_GHCB_ACCESSORS(sw_exit_code) +DEFINE_KVM_GHCB_ACCESSORS(sw_exit_info_1) +DEFINE_KVM_GHCB_ACCESSORS(sw_exit_info_2) +DEFINE_KVM_GHCB_ACCESSORS(sw_scratch) +DEFINE_KVM_GHCB_ACCESSORS(xcr0) + #endif diff --git a/arch/x86/kvm/svm/vmenter.S b/arch/x86/kvm/svm/vmenter.S index 8e8295e774f0..265452fc9ebe 100644 --- a/arch/x86/kvm/svm/vmenter.S +++ b/arch/x86/kvm/svm/vmenter.S @@ -224,6 +224,9 @@ SYM_FUNC_START(__svm_vcpu_run) */ UNTRAIN_RET + /* SRSO */ + ALTERNATIVE "", "call entry_ibpb", X86_FEATURE_IBPB_ON_VMEXIT + /* * Clear all general purpose registers except RSP and RAX to prevent * speculative use of the guest's values, even those that are reloaded diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 278dbd37dab2..c381770bcbf1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1616,7 +1616,7 @@ static bool kvm_is_immutable_feature_msr(u32 msr) ARCH_CAP_SKIP_VMENTRY_L1DFLUSH | ARCH_CAP_SSB_NO | ARCH_CAP_MDS_NO | \ ARCH_CAP_PSCHANGE_MC_NO | ARCH_CAP_TSX_CTRL_MSR | ARCH_CAP_TAA_NO | \ ARCH_CAP_SBDR_SSDP_NO | ARCH_CAP_FBSDP_NO | ARCH_CAP_PSDP_NO | \ - ARCH_CAP_FB_CLEAR | ARCH_CAP_RRSBA | ARCH_CAP_PBRSB_NO) + ARCH_CAP_FB_CLEAR | ARCH_CAP_RRSBA | ARCH_CAP_PBRSB_NO | ARCH_CAP_GDS_NO) static u64 kvm_get_arch_capabilities(void) { @@ -1673,6 +1673,9 @@ static u64 kvm_get_arch_capabilities(void) */ } + if (!boot_cpu_has_bug(X86_BUG_GDS) || gds_ucode_mitigated()) + data |= ARCH_CAP_GDS_NO; + return data; } diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S index 3fd066d42ec0..2cff585f22f2 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -11,6 +11,7 @@ #include <asm/unwind_hints.h> #include <asm/percpu.h> #include <asm/frame.h> +#include <asm/nops.h> .section .text.__x86.indirect_thunk @@ -131,6 +132,46 @@ SYM_CODE_END(__x86_indirect_jump_thunk_array) */ #ifdef CONFIG_RETHUNK +/* + * srso_untrain_ret_alias() and srso_safe_ret_alias() are placed at + * special addresses: + * + * - srso_untrain_ret_alias() is 2M aligned + * - srso_safe_ret_alias() is also in the same 2M page but bits 2, 8, 14 + * and 20 in its virtual address are set (while those bits in the + * srso_untrain_ret_alias() function are cleared). + * + * This guarantees that those two addresses will alias in the branch + * target buffer of Zen3/4 generations, leading to any potential + * poisoned entries at that BTB slot to get evicted. + * + * As a result, srso_safe_ret_alias() becomes a safe return. + */ +#ifdef CONFIG_CPU_SRSO + .section .text.__x86.rethunk_untrain + +SYM_START(srso_untrain_ret_alias, SYM_L_GLOBAL, SYM_A_NONE) + ANNOTATE_NOENDBR + ASM_NOP2 + lfence + jmp __x86_return_thunk +SYM_FUNC_END(srso_untrain_ret_alias) +__EXPORT_THUNK(srso_untrain_ret_alias) + + .section .text.__x86.rethunk_safe +#endif + +/* Needs a definition for the __x86_return_thunk alternative below. */ +SYM_START(srso_safe_ret_alias, SYM_L_GLOBAL, SYM_A_NONE) +#ifdef CONFIG_CPU_SRSO + add $8, %_ASM_SP + UNWIND_HINT_FUNC +#endif + ANNOTATE_UNRET_SAFE + ret + int3 +SYM_FUNC_END(srso_safe_ret_alias) + .section .text.__x86.return_thunk /* @@ -143,7 +184,7 @@ SYM_CODE_END(__x86_indirect_jump_thunk_array) * from re-poisioning the BTB prediction. */ .align 64 - .skip 64 - (__x86_return_thunk - zen_untrain_ret), 0xcc + .skip 64 - (__ret - zen_untrain_ret), 0xcc SYM_START(zen_untrain_ret, SYM_L_GLOBAL, SYM_A_NONE) ANNOTATE_NOENDBR /* @@ -175,10 +216,10 @@ SYM_START(zen_untrain_ret, SYM_L_GLOBAL, SYM_A_NONE) * evicted, __x86_return_thunk will suffer Straight Line Speculation * which will be contained safely by the INT3. */ -SYM_INNER_LABEL(__x86_return_thunk, SYM_L_GLOBAL) +SYM_INNER_LABEL(__ret, SYM_L_GLOBAL) ret int3 -SYM_CODE_END(__x86_return_thunk) +SYM_CODE_END(__ret) /* * Ensure the TEST decoding / BTB invalidation is complete. @@ -189,11 +230,45 @@ SYM_CODE_END(__x86_return_thunk) * Jump back and execute the RET in the middle of the TEST instruction. * INT3 is for SLS protection. */ - jmp __x86_return_thunk + jmp __ret int3 SYM_FUNC_END(zen_untrain_ret) __EXPORT_THUNK(zen_untrain_ret) +/* + * SRSO untraining sequence for Zen1/2, similar to zen_untrain_ret() + * above. On kernel entry, srso_untrain_ret() is executed which is a + * + * movabs $0xccccccc308c48348,%rax + * + * and when the return thunk executes the inner label srso_safe_ret() + * later, it is a stack manipulation and a RET which is mispredicted and + * thus a "safe" one to use. + */ + .align 64 + .skip 64 - (srso_safe_ret - srso_untrain_ret), 0xcc +SYM_START(srso_untrain_ret, SYM_L_GLOBAL, SYM_A_NONE) + ANNOTATE_NOENDBR + .byte 0x48, 0xb8 + +SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL) + add $8, %_ASM_SP + ret + int3 + int3 + int3 + lfence + call srso_safe_ret + int3 +SYM_CODE_END(srso_safe_ret) +SYM_FUNC_END(srso_untrain_ret) +__EXPORT_THUNK(srso_untrain_ret) + +SYM_FUNC_START(__x86_return_thunk) + ALTERNATIVE_2 "jmp __ret", "call srso_safe_ret", X86_FEATURE_SRSO, \ + "call srso_safe_ret_alias", X86_FEATURE_SRSO_ALIAS + int3 +SYM_CODE_END(__x86_return_thunk) EXPORT_SYMBOL(__x86_return_thunk) #endif /* CONFIG_RETHUNK */ diff --git a/block/blk-core.c b/block/blk-core.c index 90de50082146..9866468c72a2 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -722,14 +722,9 @@ void submit_bio_noacct(struct bio *bio) struct block_device *bdev = bio->bi_bdev; struct request_queue *q = bdev_get_queue(bdev); blk_status_t status = BLK_STS_IOERR; - struct blk_plug *plug; might_sleep(); - plug = blk_mq_plug(bio); - if (plug && plug->nowait) - bio->bi_opf |= REQ_NOWAIT; - /* * For a REQ_NOWAIT based request, return -EOPNOTSUPP * if queue does not support NOWAIT. @@ -1059,7 +1054,6 @@ void blk_start_plug_nr_ios(struct blk_plug *plug, unsigned short nr_ios) plug->rq_count = 0; plug->multiple_queues = false; plug->has_elevator = false; - plug->nowait = false; INIT_LIST_HEAD(&plug->cb_list); /* diff --git a/block/blk-iocost.c b/block/blk-iocost.c index dd64e2066f01..089fcb9cfce3 100644 --- a/block/blk-iocost.c +++ b/block/blk-iocost.c @@ -3301,11 +3301,12 @@ static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input, if (qos[QOS_MIN] > qos[QOS_MAX]) goto einval; - if (enable) { + if (enable && !ioc->enabled) { blk_stat_enable_accounting(disk->queue); blk_queue_flag_set(QUEUE_FLAG_RQ_ALLOC_TIME, disk->queue); ioc->enabled = true; - } else { + } else if (!enable && ioc->enabled) { + blk_stat_disable_accounting(disk->queue); blk_queue_flag_clear(QUEUE_FLAG_RQ_ALLOC_TIME, disk->queue); ioc->enabled = false; } diff --git a/block/fops.c b/block/fops.c index a286bf3325c5..838ffada5341 100644 --- a/block/fops.c +++ b/block/fops.c @@ -358,13 +358,14 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb, task_io_account_write(bio->bi_iter.bi_size); } + if (iocb->ki_flags & IOCB_NOWAIT) + bio->bi_opf |= REQ_NOWAIT; + if (iocb->ki_flags & IOCB_HIPRI) { - bio->bi_opf |= REQ_POLLED | REQ_NOWAIT; + bio->bi_opf |= REQ_POLLED; submit_bio(bio); WRITE_ONCE(iocb->private, bio); } else { - if (iocb->ki_flags & IOCB_NOWAIT) - bio->bi_opf |= REQ_NOWAIT; submit_bio(bio); } return -EIOCBQUEUED; diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c index 52b339aefadc..9967fcfa27ec 100644 --- a/drivers/accel/ivpu/ivpu_gem.c +++ b/drivers/accel/ivpu/ivpu_gem.c @@ -173,6 +173,9 @@ static void internal_free_pages_locked(struct ivpu_bo *bo) { unsigned int i, npages = bo->base.size >> PAGE_SHIFT; + if (ivpu_bo_cache_mode(bo) != DRM_IVPU_BO_CACHED) + set_pages_array_wb(bo->pages, bo->base.size >> PAGE_SHIFT); + for (i = 0; i < npages; i++) put_page(bo->pages[i]); @@ -587,6 +590,11 @@ ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 fla if (ivpu_bo_cache_mode(bo) != DRM_IVPU_BO_CACHED) drm_clflush_pages(bo->pages, bo->base.size >> PAGE_SHIFT); + if (bo->flags & DRM_IVPU_BO_WC) + set_pages_array_wc(bo->pages, bo->base.size >> PAGE_SHIFT); + else if (bo->flags & DRM_IVPU_BO_UNCACHED) + set_pages_array_uc(bo->pages, bo->base.size >> PAGE_SHIFT); + prot = ivpu_bo_pgprot(bo, PAGE_KERNEL); bo->kvaddr = vmap(bo->pages, bo->base.size >> PAGE_SHIFT, VM_MAP, prot); if (!bo->kvaddr) { diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 1dd8d5aebf67..a4d9f149b48d 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -470,6 +470,45 @@ static const struct dmi_system_id asus_laptop[] = { { } }; +static const struct dmi_system_id tongfang_gm_rg[] = { + { + .ident = "TongFang GMxRGxx/XMG CORE 15 (M22)/TUXEDO Stellaris 15 Gen4 AMD", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"), + }, + }, + { } +}; + +static const struct dmi_system_id maingear_laptop[] = { + { + .ident = "MAINGEAR Vector Pro 2 15", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-15A3070T"), + } + }, + { + .ident = "MAINGEAR Vector Pro 2 17", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro Electronics Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "MG-VCP2-17A3070T"), + }, + }, + { } +}; + +static const struct dmi_system_id pcspecialist_laptop[] = { + { + .ident = "PCSpecialist Elimina Pro 16 M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PCSpecialist"), + DMI_MATCH(DMI_PRODUCT_NAME, "Elimina Pro 16 M"), + }, + }, + { } +}; + static const struct dmi_system_id lg_laptop[] = { { .ident = "LG Electronics 17U70P", @@ -493,6 +532,9 @@ struct irq_override_cmp { static const struct irq_override_cmp override_table[] = { { medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, { asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, + { tongfang_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, + { maingear_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, + { pcspecialist_laptop, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, { lg_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, }; @@ -512,6 +554,28 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity, return entry->override; } +#ifdef CONFIG_X86 + /* + * Always use the MADT override info, except for the i8042 PS/2 ctrl + * IRQs (1 and 12). For these the DSDT IRQ settings should sometimes + * be used otherwise PS/2 keyboards / mice will not work. + */ + if (gsi != 1 && gsi != 12) + return true; + + /* If the override comes from an INT_SRC_OVR MADT entry, honor it. */ + if (acpi_int_src_ovr[gsi]) + return true; + + /* + * IRQ override isn't needed on modern AMD Zen systems and + * this override breaks active low IRQs on AMD Ryzen 6000 and + * newer systems. Skip it. + */ + if (boot_cpu_has(X86_FEATURE_ZEN)) + return false; +#endif + return true; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5b145f1aaa1b..87e385542576 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1714,6 +1714,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) {"BSG1160", }, {"BSG2150", }, {"CSC3551", }, + {"CSC3556", }, {"INT33FE", }, {"INT3515", }, /* Non-conforming _HID for Cirrus Logic already released */ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 486c8271cab7..d720f93d8b19 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -6617,6 +6617,7 @@ err_init_binder_device_failed: err_alloc_device_names_failed: debugfs_remove_recursive(binder_debugfs_dir_entry_root); + binder_alloc_shrinker_exit(); return ret; } diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 662a2a2e2e84..e3db8297095a 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -1087,6 +1087,12 @@ int binder_alloc_shrinker_init(void) return ret; } +void binder_alloc_shrinker_exit(void) +{ + unregister_shrinker(&binder_shrinker); + list_lru_destroy(&binder_alloc_lru); +} + /** * check_buffer() - verify that buffer/offset is safe to access * @alloc: binder_alloc for this proc diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 138d1d5af9ce..dc1e2b01dd64 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -129,6 +129,7 @@ extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, int pid); extern void binder_alloc_init(struct binder_alloc *alloc); extern int binder_alloc_shrinker_init(void); +extern void binder_alloc_shrinker_exit(void); extern void binder_alloc_vma_close(struct binder_alloc *alloc); extern struct binder_buffer * binder_alloc_prepare_to_free(struct binder_alloc *alloc, diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 370d18aca71e..c6ece32de8e3 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1100,7 +1100,14 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev) } } else { sdev->sector_size = ata_id_logical_sector_size(dev->id); + /* + * Stop the drive on suspend but do not issue START STOP UNIT + * on resume as this is not necessary and may fail: the device + * will be woken up by ata_port_pm_resume() with a port reset + * and device revalidation. + */ sdev->manage_start_stop = 1; + sdev->no_start_on_resume = 1; } /* diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index c1815b9dae68..fe6690ecf563 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -509,73 +509,30 @@ static void __init cpu_dev_register_generic(void) } #ifdef CONFIG_GENERIC_CPU_VULNERABILITIES - -ssize_t __weak cpu_show_meltdown(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_spectre_v1(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_spectre_v2(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_spec_store_bypass(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_l1tf(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_mds(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_tsx_async_abort(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_itlb_multihit(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_srbds(struct device *dev, +static ssize_t cpu_show_not_affected(struct device *dev, struct device_attribute *attr, char *buf) { return sysfs_emit(buf, "Not affected\n"); } -ssize_t __weak cpu_show_mmio_stale_data(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} - -ssize_t __weak cpu_show_retbleed(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "Not affected\n"); -} +#define CPU_SHOW_VULN_FALLBACK(func) \ + ssize_t cpu_show_##func(struct device *, \ + struct device_attribute *, char *) \ + __attribute__((weak, alias("cpu_show_not_affected"))) + +CPU_SHOW_VULN_FALLBACK(meltdown); +CPU_SHOW_VULN_FALLBACK(spectre_v1); +CPU_SHOW_VULN_FALLBACK(spectre_v2); +CPU_SHOW_VULN_FALLBACK(spec_store_bypass); +CPU_SHOW_VULN_FALLBACK(l1tf); +CPU_SHOW_VULN_FALLBACK(mds); +CPU_SHOW_VULN_FALLBACK(tsx_async_abort); +CPU_SHOW_VULN_FALLBACK(itlb_multihit); +CPU_SHOW_VULN_FALLBACK(srbds); +CPU_SHOW_VULN_FALLBACK(mmio_stale_data); +CPU_SHOW_VULN_FALLBACK(retbleed); +CPU_SHOW_VULN_FALLBACK(spec_rstack_overflow); +CPU_SHOW_VULN_FALLBACK(gds); static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL); static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL); @@ -588,6 +545,8 @@ static DEVICE_ATTR(itlb_multihit, 0444, cpu_show_itlb_multihit, NULL); static DEVICE_ATTR(srbds, 0444, cpu_show_srbds, NULL); static DEVICE_ATTR(mmio_stale_data, 0444, cpu_show_mmio_stale_data, NULL); static DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL); +static DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL); +static DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL); static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_meltdown.attr, @@ -601,6 +560,8 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = { &dev_attr_srbds.attr, &dev_attr_mmio_stale_data.attr, &dev_attr_retbleed.attr, + &dev_attr_spec_rstack_overflow.attr, + &dev_attr_gather_data_sampling.attr, NULL }; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 24afcc93ac01..2328cc05be36 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3675,7 +3675,7 @@ static int rbd_lock(struct rbd_device *rbd_dev) ret = ceph_cls_lock(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc, RBD_LOCK_NAME, CEPH_CLS_LOCK_EXCLUSIVE, cookie, RBD_LOCK_TAG, "", 0); - if (ret) + if (ret && ret != -EEXIST) return ret; __rbd_lock(rbd_dev, cookie); @@ -3878,7 +3878,7 @@ static struct ceph_locker *get_lock_owner_info(struct rbd_device *rbd_dev) &rbd_dev->header_oloc, RBD_LOCK_NAME, &lock_type, &lock_tag, &lockers, &num_lockers); if (ret) { - rbd_warn(rbd_dev, "failed to retrieve lockers: %d", ret); + rbd_warn(rbd_dev, "failed to get header lockers: %d", ret); return ERR_PTR(ret); } @@ -3940,8 +3940,10 @@ static int find_watcher(struct rbd_device *rbd_dev, ret = ceph_osdc_list_watchers(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc, &watchers, &num_watchers); - if (ret) + if (ret) { + rbd_warn(rbd_dev, "failed to get watchers: %d", ret); return ret; + } sscanf(locker->id.cookie, RBD_LOCK_COOKIE_PREFIX " %llu", &cookie); for (i = 0; i < num_watchers; i++) { @@ -3985,8 +3987,12 @@ static int rbd_try_lock(struct rbd_device *rbd_dev) locker = refreshed_locker = NULL; ret = rbd_lock(rbd_dev); - if (ret != -EBUSY) + if (!ret) + goto out; + if (ret != -EBUSY) { + rbd_warn(rbd_dev, "failed to lock header: %d", ret); goto out; + } /* determine if the current lock holder is still alive */ locker = get_lock_owner_info(rbd_dev); @@ -4089,11 +4095,8 @@ static int rbd_try_acquire_lock(struct rbd_device *rbd_dev) ret = rbd_try_lock(rbd_dev); if (ret < 0) { - rbd_warn(rbd_dev, "failed to lock header: %d", ret); - if (ret == -EBLOCKLISTED) - goto out; - - ret = 1; /* request lock anyway */ + rbd_warn(rbd_dev, "failed to acquire lock: %d", ret); + goto out; } if (ret > 0) { up_write(&rbd_dev->lock_rwsem); @@ -6627,12 +6630,11 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev) cancel_delayed_work_sync(&rbd_dev->lock_dwork); if (!ret) ret = -ETIMEDOUT; - } - if (ret) { - rbd_warn(rbd_dev, "failed to acquire exclusive lock: %ld", ret); - return ret; + rbd_warn(rbd_dev, "failed to acquire lock: %ld", ret); } + if (ret) + return ret; /* * The lock may have been released by now, unless automatic lock diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 5676e6dd5b16..06673c6ca255 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -1870,15 +1870,16 @@ static void zram_bio_discard(struct zram *zram, struct bio *bio) static void zram_bio_read(struct zram *zram, struct bio *bio) { - struct bvec_iter iter; - struct bio_vec bv; - unsigned long start_time; + unsigned long start_time = bio_start_io_acct(bio); + struct bvec_iter iter = bio->bi_iter; - start_time = bio_start_io_acct(bio); - bio_for_each_segment(bv, bio, iter) { + do { u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + struct bio_vec bv = bio_iter_iovec(bio, iter); + + bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset); if (zram_bvec_read(zram, &bv, index, offset, bio) < 0) { atomic64_inc(&zram->stats.failed_reads); @@ -1890,22 +1891,26 @@ static void zram_bio_read(struct zram *zram, struct bio *bio) zram_slot_lock(zram, index); zram_accessed(zram, index); zram_slot_unlock(zram, index); - } + + bio_advance_iter_single(bio, &iter, bv.bv_len); + } while (iter.bi_size); + bio_end_io_acct(bio, start_time); bio_endio(bio); } static void zram_bio_write(struct zram *zram, struct bio *bio) { - struct bvec_iter iter; - struct bio_vec bv; - unsigned long start_time; + unsigned long start_time = bio_start_io_acct(bio); + struct bvec_iter iter = bio->bi_iter; - start_time = bio_start_io_acct(bio); - bio_for_each_segment(bv, bio, iter) { + do { u32 index = iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; u32 offset = (iter.bi_sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + struct bio_vec bv = bio_iter_iovec(bio, iter); + + bv.bv_len = min_t(u32, bv.bv_len, PAGE_SIZE - offset); if (zram_bvec_write(zram, &bv, index, offset, bio) < 0) { atomic64_inc(&zram->stats.failed_writes); @@ -1916,7 +1921,10 @@ static void zram_bio_write(struct zram *zram, struct bio *bio) zram_slot_lock(zram, index); zram_accessed(zram, index); zram_slot_unlock(zram, index); - } + + bio_advance_iter_single(bio, &iter, bv.bv_len); + } while (iter.bi_size); + bio_end_io_acct(bio, start_time); bio_endio(bio); } diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index cf5499e51999..ea6b4013bc38 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -510,70 +510,6 @@ static int tpm_add_legacy_sysfs(struct tpm_chip *chip) return 0; } -/* - * Some AMD fTPM versions may cause stutter - * https://www.amd.com/en/support/kb/faq/pa-410 - * - * Fixes are available in two series of fTPM firmware: - * 6.x.y.z series: 6.0.18.6 + - * 3.x.y.z series: 3.57.y.5 + - */ -#ifdef CONFIG_X86 -static bool tpm_amd_is_rng_defective(struct tpm_chip *chip) -{ - u32 val1, val2; - u64 version; - int ret; - - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) - return false; - - ret = tpm_request_locality(chip); - if (ret) - return false; - - ret = tpm2_get_tpm_pt(chip, TPM2_PT_MANUFACTURER, &val1, NULL); - if (ret) - goto release; - if (val1 != 0x414D4400U /* AMD */) { - ret = -ENODEV; - goto release; - } - ret = tpm2_get_tpm_pt(chip, TPM2_PT_FIRMWARE_VERSION_1, &val1, NULL); - if (ret) - goto release; - ret = tpm2_get_tpm_pt(chip, TPM2_PT_FIRMWARE_VERSION_2, &val2, NULL); - -release: - tpm_relinquish_locality(chip); - - if (ret) - return false; - - version = ((u64)val1 << 32) | val2; - if ((version >> 48) == 6) { - if (version >= 0x0006000000180006ULL) - return false; - } else if ((version >> 48) == 3) { - if (version >= 0x0003005700000005ULL) - return false; - } else { - return false; - } - - dev_warn(&chip->dev, - "AMD fTPM version 0x%llx causes system stutter; hwrng disabled\n", - version); - - return true; -} -#else -static inline bool tpm_amd_is_rng_defective(struct tpm_chip *chip) -{ - return false; -} -#endif /* CONFIG_X86 */ - static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) { struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng); @@ -585,10 +521,20 @@ static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) return tpm_get_random(chip, data, max); } +static bool tpm_is_hwrng_enabled(struct tpm_chip *chip) +{ + if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM)) + return false; + if (tpm_is_firmware_upgrade(chip)) + return false; + if (chip->flags & TPM_CHIP_FLAG_HWRNG_DISABLED) + return false; + return true; +} + static int tpm_add_hwrng(struct tpm_chip *chip) { - if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM) || tpm_is_firmware_upgrade(chip) || - tpm_amd_is_rng_defective(chip)) + if (!tpm_is_hwrng_enabled(chip)) return 0; snprintf(chip->hwrng_name, sizeof(chip->hwrng_name), @@ -693,7 +639,7 @@ int tpm_chip_register(struct tpm_chip *chip) return 0; out_hwrng: - if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip)) + if (tpm_is_hwrng_enabled(chip)) hwrng_unregister(&chip->hwrng); out_ppi: tpm_bios_log_teardown(chip); @@ -718,8 +664,7 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); void tpm_chip_unregister(struct tpm_chip *chip) { tpm_del_legacy_sysfs(chip); - if (IS_ENABLED(CONFIG_HW_RANDOM_TPM) && !tpm_is_firmware_upgrade(chip) && - !tpm_amd_is_rng_defective(chip)) + if (tpm_is_hwrng_enabled(chip)) hwrng_unregister(&chip->hwrng); tpm_bios_log_teardown(chip); if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_firmware_upgrade(chip)) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 1a5d09b18513..9eb1a1859012 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -463,6 +463,28 @@ static bool crb_req_canceled(struct tpm_chip *chip, u8 status) return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE; } +static int crb_check_flags(struct tpm_chip *chip) +{ + u32 val; + int ret; + + ret = crb_request_locality(chip, 0); + if (ret) + return ret; + + ret = tpm2_get_tpm_pt(chip, TPM2_PT_MANUFACTURER, &val, NULL); + if (ret) + goto release; + + if (val == 0x414D4400U /* AMD */) + chip->flags |= TPM_CHIP_FLAG_HWRNG_DISABLED; + +release: + crb_relinquish_locality(chip, 0); + + return ret; +} + static const struct tpm_class_ops tpm_crb = { .flags = TPM_OPS_AUTO_STARTUP, .status = crb_status, @@ -800,6 +822,14 @@ static int crb_acpi_add(struct acpi_device *device) chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; + rc = tpm_chip_bootstrap(chip); + if (rc) + goto out; + + rc = crb_check_flags(chip); + if (rc) + goto out; + rc = tpm_chip_register(chip); out: diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index cc42cf3de960..7fa3d91042b2 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -89,7 +89,7 @@ static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr) tpm_tis_flush(iobase); } -static int interrupts = -1; +static int interrupts; module_param(interrupts, int, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -164,10 +164,26 @@ static const struct dmi_system_id tpm_tis_dmi_table[] = { }, { .callback = tpm_tis_disable_irq, + .ident = "ThinkStation P620", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkStation P620"), + }, + }, + { + .callback = tpm_tis_disable_irq, + .ident = "TUXEDO InfinityBook S 15/17 Gen7", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_PRODUCT_NAME, "TUXEDO InfinityBook S 15/17 Gen7"), + }, + }, + { + .callback = tpm_tis_disable_irq, .ident = "UPX-TGL", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), - DMI_MATCH(DMI_PRODUCT_VERSION, "UPX-TGL"), + DMI_MATCH(DMI_PRODUCT_NAME, "UPX-TGL01"), }, }, {} diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 93f38a8178ba..6b3b424addab 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -444,6 +444,7 @@ config COMMON_CLK_BD718XX config COMMON_CLK_FIXED_MMIO bool "Clock driver for Memory Mapped Fixed values" depends on COMMON_CLK && OF + depends on HAS_IOMEM help Support for Memory Mapped IO Fixed clocks diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index b6c7c2725906..44f435103c65 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -291,7 +291,7 @@ static int imx93_clocks_probe(struct platform_device *pdev) anatop_base = devm_of_iomap(dev, np, 0, NULL); of_node_put(np); if (WARN_ON(IS_ERR(anatop_base))) { - ret = PTR_ERR(base); + ret = PTR_ERR(anatop_base); goto unregister_hws; } diff --git a/drivers/clk/mediatek/clk-mt8183.c b/drivers/clk/mediatek/clk-mt8183.c index 1ba421b38ec5..e31f94387d87 100644 --- a/drivers/clk/mediatek/clk-mt8183.c +++ b/drivers/clk/mediatek/clk-mt8183.c @@ -328,6 +328,14 @@ static const char * const atb_parents[] = { "syspll_d5" }; +static const char * const sspm_parents[] = { + "clk26m", + "univpll_d2_d4", + "syspll_d2_d2", + "univpll_d2_d2", + "syspll_d3" +}; + static const char * const dpi0_parents[] = { "clk26m", "tvdpll_d2", @@ -507,6 +515,9 @@ static const struct mtk_mux top_muxes[] = { /* CLK_CFG_6 */ MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_ATB, "atb_sel", atb_parents, 0xa0, 0xa4, 0xa8, 0, 2, 7, 0x004, 24), + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_MUX_SSPM, "sspm_sel", + sspm_parents, 0xa0, 0xa4, 0xa8, 8, 3, 15, 0x004, 25, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_DPI0, "dpi0_sel", dpi0_parents, 0xa0, 0xa4, 0xa8, 16, 4, 23, 0x004, 26), MUX_GATE_CLR_SET_UPD(CLK_TOP_MUX_SCAM, "scam_sel", @@ -673,10 +684,18 @@ static const struct mtk_gate_regs infra3_cg_regs = { GATE_MTK(_id, _name, _parent, &infra2_cg_regs, _shift, \ &mtk_clk_gate_ops_setclr) +#define GATE_INFRA2_FLAGS(_id, _name, _parent, _shift, _flag) \ + GATE_MTK_FLAGS(_id, _name, _parent, &infra2_cg_regs, \ + _shift, &mtk_clk_gate_ops_setclr, _flag) + #define GATE_INFRA3(_id, _name, _parent, _shift) \ GATE_MTK(_id, _name, _parent, &infra3_cg_regs, _shift, \ &mtk_clk_gate_ops_setclr) +#define GATE_INFRA3_FLAGS(_id, _name, _parent, _shift, _flag) \ + GATE_MTK_FLAGS(_id, _name, _parent, &infra3_cg_regs, \ + _shift, &mtk_clk_gate_ops_setclr, _flag) + static const struct mtk_gate infra_clks[] = { /* INFRA0 */ GATE_INFRA0(CLK_INFRA_PMIC_TMR, "infra_pmic_tmr", "axi_sel", 0), @@ -748,7 +767,11 @@ static const struct mtk_gate infra_clks[] = { GATE_INFRA2(CLK_INFRA_UNIPRO_TICK, "infra_unipro_tick", "fufs_sel", 12), GATE_INFRA2(CLK_INFRA_UFS_MP_SAP_BCLK, "infra_ufs_mp_sap_bck", "fufs_sel", 13), GATE_INFRA2(CLK_INFRA_MD32_BCLK, "infra_md32_bclk", "axi_sel", 14), + /* infra_sspm is main clock in co-processor, should not be closed in Linux. */ + GATE_INFRA2_FLAGS(CLK_INFRA_SSPM, "infra_sspm", "sspm_sel", 15, CLK_IS_CRITICAL), GATE_INFRA2(CLK_INFRA_UNIPRO_MBIST, "infra_unipro_mbist", "axi_sel", 16), + /* infra_sspm_bus_hclk is main clock in co-processor, should not be closed in Linux. */ + GATE_INFRA2_FLAGS(CLK_INFRA_SSPM_BUS_HCLK, "infra_sspm_bus_hclk", "axi_sel", 17, CLK_IS_CRITICAL), GATE_INFRA2(CLK_INFRA_I2C5, "infra_i2c5", "i2c_sel", 18), GATE_INFRA2(CLK_INFRA_I2C5_ARBITER, "infra_i2c5_arbiter", "i2c_sel", 19), GATE_INFRA2(CLK_INFRA_I2C5_IMM, "infra_i2c5_imm", "i2c_sel", 20), @@ -766,6 +789,10 @@ static const struct mtk_gate infra_clks[] = { GATE_INFRA3(CLK_INFRA_MSDC0_SELF, "infra_msdc0_self", "msdc50_0_sel", 0), GATE_INFRA3(CLK_INFRA_MSDC1_SELF, "infra_msdc1_self", "msdc50_0_sel", 1), GATE_INFRA3(CLK_INFRA_MSDC2_SELF, "infra_msdc2_self", "msdc50_0_sel", 2), + /* infra_sspm_26m_self is main clock in co-processor, should not be closed in Linux. */ + GATE_INFRA3_FLAGS(CLK_INFRA_SSPM_26M_SELF, "infra_sspm_26m_self", "f_f26m_ck", 3, CLK_IS_CRITICAL), + /* infra_sspm_32k_self is main clock in co-processor, should not be closed in Linux. */ + GATE_INFRA3_FLAGS(CLK_INFRA_SSPM_32K_SELF, "infra_sspm_32k_self", "f_f26m_ck", 4, CLK_IS_CRITICAL), GATE_INFRA3(CLK_INFRA_UFS_AXI, "infra_ufs_axi", "axi_sel", 5), GATE_INFRA3(CLK_INFRA_I2C6, "infra_i2c6", "i2c_sel", 6), GATE_INFRA3(CLK_INFRA_AP_MSDC0, "infra_ap_msdc0", "msdc50_hclk_sel", 7), diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 8fef90bf962f..6fa7639a3050 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -367,9 +367,9 @@ static int meson_clk_pll_enable(struct clk_hw *hw) * 3. enable the lock detect module */ if (MESON_PARM_APPLICABLE(&pll->current_en)) { - usleep_range(10, 20); + udelay(10); meson_parm_write(clk->map, &pll->current_en, 1); - usleep_range(40, 50); + udelay(40); } if (MESON_PARM_APPLICABLE(&pll->l_detect)) { diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index bca21df51168..62962ae84b77 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -3,13 +3,6 @@ # Counter devices # -menuconfig COUNTER - tristate "Counter support" - help - This enables counter device support through the Generic Counter - interface. You only need to enable this, if you also want to enable - one or more of the counter device drivers below. - config I8254 tristate select COUNTER @@ -25,6 +18,13 @@ config I8254 If built as a module its name will be i8254. +menuconfig COUNTER + tristate "Counter support" + help + This enables counter device support through the Generic Counter + interface. You only need to enable this, if you also want to enable + one or more of the counter device drivers below. + if COUNTER config 104_QUAD_8 diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 81fba0dcbee9..9a1e194d5cf8 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1012,8 +1012,8 @@ static int amd_pstate_update_status(const char *buf, size_t size) return 0; } -static ssize_t show_status(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) { ssize_t ret; @@ -1024,7 +1024,7 @@ static ssize_t show_status(struct kobject *kobj, return ret; } -static ssize_t store_status(struct kobject *a, struct kobj_attribute *b, +static ssize_t status_store(struct device *a, struct device_attribute *b, const char *buf, size_t count) { char *p = memchr(buf, '\n', count); @@ -1043,7 +1043,7 @@ cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq); cpufreq_freq_attr_ro(amd_pstate_highest_perf); cpufreq_freq_attr_rw(energy_performance_preference); cpufreq_freq_attr_ro(energy_performance_available_preferences); -define_one_global_rw(status); +static DEVICE_ATTR_RW(status); static struct freq_attr *amd_pstate_attr[] = { &amd_pstate_max_freq, @@ -1062,7 +1062,7 @@ static struct freq_attr *amd_pstate_epp_attr[] = { }; static struct attribute *pstate_global_attributes[] = { - &status.attr, + &dev_attr_status.attr, NULL }; diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index c2d6d9c3c930..b88af1262f1a 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -120,20 +120,6 @@ static void psci_pd_remove(void) } } -static bool psci_pd_try_set_osi_mode(void) -{ - int ret; - - if (!psci_has_osi_support()) - return false; - - ret = psci_set_osi_mode(true); - if (ret) - return false; - - return true; -} - static void psci_cpuidle_domain_sync_state(struct device *dev) { /* @@ -152,15 +138,12 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *node; - bool use_osi; + bool use_osi = psci_has_osi_support(); int ret = 0, pd_count = 0; if (!np) return -ENODEV; - /* If OSI mode is supported, let's try to enable it. */ - use_osi = psci_pd_try_set_osi_mode(); - /* * Parse child nodes for the "#power-domain-cells" property and * initialize a genpd/genpd-of-provider pair when it's found. @@ -170,33 +153,37 @@ static int psci_cpuidle_domain_probe(struct platform_device *pdev) continue; ret = psci_pd_init(node, use_osi); - if (ret) - goto put_node; + if (ret) { + of_node_put(node); + goto exit; + } pd_count++; } /* Bail out if not using the hierarchical CPU topology. */ if (!pd_count) - goto no_pd; + return 0; /* Link genpd masters/subdomains to model the CPU topology. */ ret = dt_idle_pd_init_topology(np); if (ret) goto remove_pd; + /* let's try to enable OSI. */ + ret = psci_set_osi_mode(use_osi); + if (ret) + goto remove_pd; + pr_info("Initialized CPU PM domain topology using %s mode\n", use_osi ? "OSI" : "PC"); return 0; -put_node: - of_node_put(node); remove_pd: + dt_idle_pd_remove_topology(np); psci_pd_remove(); +exit: pr_err("failed to create CPU PM domains ret=%d\n", ret); -no_pd: - if (use_osi) - psci_set_osi_mode(false); return ret; } diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c index b37165514d4e..1af63c189039 100644 --- a/drivers/cpuidle/dt_idle_genpd.c +++ b/drivers/cpuidle/dt_idle_genpd.c @@ -152,6 +152,30 @@ int dt_idle_pd_init_topology(struct device_node *np) return 0; } +int dt_idle_pd_remove_topology(struct device_node *np) +{ + struct device_node *node; + struct of_phandle_args child, parent; + int ret; + + for_each_child_of_node(np, node) { + if (of_parse_phandle_with_args(node, "power-domains", + "#power-domain-cells", 0, &parent)) + continue; + + child.np = node; + child.args_count = 0; + ret = of_genpd_remove_subdomain(&parent, &child); + of_node_put(parent.np); + if (ret) { + of_node_put(node); + return ret; + } + } + + return 0; +} + struct device *dt_idle_attach_cpu(int cpu, const char *name) { struct device *dev; diff --git a/drivers/cpuidle/dt_idle_genpd.h b/drivers/cpuidle/dt_idle_genpd.h index a95483d08a02..3be1f70f55b5 100644 --- a/drivers/cpuidle/dt_idle_genpd.h +++ b/drivers/cpuidle/dt_idle_genpd.h @@ -14,6 +14,8 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np, int dt_idle_pd_init_topology(struct device_node *np); +int dt_idle_pd_remove_topology(struct device_node *np); + struct device *dt_idle_attach_cpu(int cpu, const char *name); void dt_idle_detach_cpu(struct device *dev); @@ -36,6 +38,11 @@ static inline int dt_idle_pd_init_topology(struct device_node *np) return 0; } +static inline int dt_idle_pd_remove_topology(struct device_node *np) +{ + return 0; +} + static inline struct device *dt_idle_attach_cpu(int cpu, const char *name) { return NULL; diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index d6d067fbee97..ca60bb8114f2 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -121,6 +121,45 @@ static bool cxl_is_security_command(u16 opcode) return false; } +static void cxl_set_security_cmd_enabled(struct cxl_security_state *security, + u16 opcode) +{ + switch (opcode) { + case CXL_MBOX_OP_SANITIZE: + set_bit(CXL_SEC_ENABLED_SANITIZE, security->enabled_cmds); + break; + case CXL_MBOX_OP_SECURE_ERASE: + set_bit(CXL_SEC_ENABLED_SECURE_ERASE, + security->enabled_cmds); + break; + case CXL_MBOX_OP_GET_SECURITY_STATE: + set_bit(CXL_SEC_ENABLED_GET_SECURITY_STATE, + security->enabled_cmds); + break; + case CXL_MBOX_OP_SET_PASSPHRASE: + set_bit(CXL_SEC_ENABLED_SET_PASSPHRASE, + security->enabled_cmds); + break; + case CXL_MBOX_OP_DISABLE_PASSPHRASE: + set_bit(CXL_SEC_ENABLED_DISABLE_PASSPHRASE, + security->enabled_cmds); + break; + case CXL_MBOX_OP_UNLOCK: + set_bit(CXL_SEC_ENABLED_UNLOCK, security->enabled_cmds); + break; + case CXL_MBOX_OP_FREEZE_SECURITY: + set_bit(CXL_SEC_ENABLED_FREEZE_SECURITY, + security->enabled_cmds); + break; + case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE: + set_bit(CXL_SEC_ENABLED_PASSPHRASE_SECURE_ERASE, + security->enabled_cmds); + break; + default: + break; + } +} + static bool cxl_is_poison_command(u16 opcode) { #define CXL_MBOX_OP_POISON_CMDS 0x43 @@ -677,7 +716,8 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel) u16 opcode = le16_to_cpu(cel_entry[i].opcode); struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); - if (!cmd && !cxl_is_poison_command(opcode)) { + if (!cmd && (!cxl_is_poison_command(opcode) || + !cxl_is_security_command(opcode))) { dev_dbg(dev, "Opcode 0x%04x unsupported by driver\n", opcode); continue; @@ -689,6 +729,9 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel) if (cxl_is_poison_command(opcode)) cxl_set_poison_cmd_enabled(&mds->poison, opcode); + if (cxl_is_security_command(opcode)) + cxl_set_security_cmd_enabled(&mds->security, opcode); + dev_dbg(dev, "Opcode 0x%04x enabled\n", opcode); } } diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index f99e7ec3cc40..14b547c07f54 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -477,9 +477,28 @@ static struct attribute_group cxl_memdev_pmem_attribute_group = { .attrs = cxl_memdev_pmem_attributes, }; +static umode_t cxl_memdev_security_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); + + if (a == &dev_attr_security_sanitize.attr && + !test_bit(CXL_SEC_ENABLED_SANITIZE, mds->security.enabled_cmds)) + return 0; + + if (a == &dev_attr_security_erase.attr && + !test_bit(CXL_SEC_ENABLED_SECURE_ERASE, mds->security.enabled_cmds)) + return 0; + + return a->mode; +} + static struct attribute_group cxl_memdev_security_attribute_group = { .name = "security", .attrs = cxl_memdev_security_attributes, + .is_visible = cxl_memdev_security_visible, }; static const struct attribute_group *cxl_memdev_attribute_groups[] = { diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 499113328586..706f8a6d1ef4 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -244,6 +244,19 @@ enum poison_cmd_enabled_bits { CXL_POISON_ENABLED_MAX }; +/* Device enabled security commands */ +enum security_cmd_enabled_bits { + CXL_SEC_ENABLED_SANITIZE, + CXL_SEC_ENABLED_SECURE_ERASE, + CXL_SEC_ENABLED_GET_SECURITY_STATE, + CXL_SEC_ENABLED_SET_PASSPHRASE, + CXL_SEC_ENABLED_DISABLE_PASSPHRASE, + CXL_SEC_ENABLED_UNLOCK, + CXL_SEC_ENABLED_FREEZE_SECURITY, + CXL_SEC_ENABLED_PASSPHRASE_SECURE_ERASE, + CXL_SEC_ENABLED_MAX +}; + /** * struct cxl_poison_state - Driver poison state info * @@ -346,6 +359,7 @@ struct cxl_fw_state { * struct cxl_security_state - Device security state * * @state: state of last security operation + * @enabled_cmds: All security commands enabled in the CEL * @poll: polling for sanitization is enabled, device has no mbox irq support * @poll_tmo_secs: polling timeout * @poll_dwork: polling work item @@ -353,6 +367,7 @@ struct cxl_fw_state { */ struct cxl_security_state { unsigned long state; + DECLARE_BITMAP(enabled_cmds, CXL_SEC_ENABLED_MAX); bool poll; int poll_tmo_secs; struct delayed_work poll_dwork; @@ -434,6 +449,7 @@ struct cxl_dev_state { * @next_persistent_bytes: persistent capacity change pending device reset * @event: event log driver state * @poison: poison driver state info + * @security: security driver state info * @fw: firmware upload / activation state * @mbox_send: @dev specific transport for transmitting mailbox commands * diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 644c188d6a11..08fdd0e2ed1b 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -211,6 +211,7 @@ config FSL_DMA config FSL_EDMA tristate "Freescale eDMA engine support" depends on OF + depends on HAS_IOMEM select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help @@ -280,6 +281,7 @@ config IMX_SDMA config INTEL_IDMA64 tristate "Intel integrated DMA 64-bit support" + depends on HAS_IOMEM select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index 5abbcc61c528..9a15f0d12c79 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -384,9 +384,7 @@ static void idxd_wq_disable_cleanup(struct idxd_wq *wq) wq->threshold = 0; wq->priority = 0; wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES; - clear_bit(WQ_FLAG_DEDICATED, &wq->flags); - clear_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags); - clear_bit(WQ_FLAG_ATS_DISABLE, &wq->flags); + wq->flags = 0; memset(wq->name, 0, WQ_NAME_SIZE); wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER; idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH); diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma.c index ebd8733f72ad..9413fad08a60 100644 --- a/drivers/dma/mcf-edma.c +++ b/drivers/dma/mcf-edma.c @@ -190,7 +190,13 @@ static int mcf_edma_probe(struct platform_device *pdev) return -EINVAL; } - chans = pdata->dma_channels; + if (!pdata->dma_channels) { + dev_info(&pdev->dev, "setting default channel number to 64"); + chans = 64; + } else { + chans = pdata->dma_channels; + } + len = sizeof(*mcf_edma) + sizeof(*mcf_chan) * chans; mcf_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); if (!mcf_edma) @@ -202,11 +208,6 @@ static int mcf_edma_probe(struct platform_device *pdev) mcf_edma->drvdata = &mcf_data; mcf_edma->big_endian = 1; - if (!mcf_edma->n_chans) { - dev_info(&pdev->dev, "setting default channel number to 64"); - mcf_edma->n_chans = 64; - } - mutex_init(&mcf_edma->fsl_edma_mutex); mcf_edma->membase = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 95a462a1f511..b6e0ac8314e5 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -192,7 +192,7 @@ struct owl_dma_pchan { }; /** - * struct owl_dma_pchan - Wrapper for DMA ENGINE channel + * struct owl_dma_vchan - Wrapper for DMA ENGINE channel * @vc: wrapped virtual channel * @pchan: the physical channel utilized by this channel * @txd: active transaction on this channel diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index b4731fe6bbc1..3cf0b38387ae 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -404,6 +404,12 @@ enum desc_status { */ BUSY, /* + * Pause was called while descriptor was BUSY. Due to hardware + * limitations, only termination is possible for descriptors + * that have been paused. + */ + PAUSED, + /* * Sitting on the channel work_list but xfer done * by PL330 core */ @@ -2041,7 +2047,7 @@ static inline void fill_queue(struct dma_pl330_chan *pch) list_for_each_entry(desc, &pch->work_list, node) { /* If already submitted */ - if (desc->status == BUSY) + if (desc->status == BUSY || desc->status == PAUSED) continue; ret = pl330_submit_req(pch->thread, desc); @@ -2326,6 +2332,7 @@ static int pl330_pause(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); struct pl330_dmac *pl330 = pch->dmac; + struct dma_pl330_desc *desc; unsigned long flags; pm_runtime_get_sync(pl330->ddma.dev); @@ -2335,6 +2342,10 @@ static int pl330_pause(struct dma_chan *chan) _stop(pch->thread); spin_unlock(&pl330->lock); + list_for_each_entry(desc, &pch->work_list, node) { + if (desc->status == BUSY) + desc->status = PAUSED; + } spin_unlock_irqrestore(&pch->lock, flags); pm_runtime_mark_last_busy(pl330->ddma.dev); pm_runtime_put_autosuspend(pl330->ddma.dev); @@ -2425,7 +2436,7 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, else if (running && desc == running) transferred = pl330_get_current_xferred_count(pch, desc); - else if (desc->status == BUSY) + else if (desc->status == BUSY || desc->status == PAUSED) /* * Busy but not running means either just enqueued, * or finished and not yet marked done @@ -2442,6 +2453,9 @@ pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, case DONE: ret = DMA_COMPLETE; break; + case PAUSED: + ret = DMA_PAUSED; + break; case PREP: case BUSY: ret = DMA_IN_PROGRESS; diff --git a/drivers/dma/xilinx/xdma.c b/drivers/dma/xilinx/xdma.c index 93ee298d52b8..e0bfd129d563 100644 --- a/drivers/dma/xilinx/xdma.c +++ b/drivers/dma/xilinx/xdma.c @@ -668,6 +668,8 @@ static int xdma_set_vector_reg(struct xdma_device *xdev, u32 vec_tbl_start, val |= irq_start << shift; irq_start++; irq_num--; + if (!irq_num) + break; } /* write IRQ register */ @@ -715,7 +717,7 @@ static int xdma_irq_init(struct xdma_device *xdev) ret = request_irq(irq, xdma_channel_isr, 0, "xdma-c2h-channel", &xdev->c2h_chans[j]); if (ret) { - xdma_err(xdev, "H2C channel%d request irq%d failed: %d", + xdma_err(xdev, "C2H channel%d request irq%d failed: %d", j, irq, ret); goto failed_init_c2h; } @@ -892,7 +894,7 @@ static int xdma_probe(struct platform_device *pdev) } reg_base = devm_ioremap_resource(&pdev->dev, res); - if (!reg_base) { + if (IS_ERR(reg_base)) { xdma_err(xdev, "ioremap failed"); goto failed; } diff --git a/drivers/firmware/arm_scmi/mailbox.c b/drivers/firmware/arm_scmi/mailbox.c index 1efa5e9392c4..19246ed1f01f 100644 --- a/drivers/firmware/arm_scmi/mailbox.c +++ b/drivers/firmware/arm_scmi/mailbox.c @@ -166,8 +166,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, return -ENOMEM; shmem = of_parse_phandle(cdev->of_node, "shmem", idx); - if (!of_device_is_compatible(shmem, "arm,scmi-shmem")) + if (!of_device_is_compatible(shmem, "arm,scmi-shmem")) { + of_node_put(shmem); return -ENXIO; + } ret = of_address_to_resource(shmem, 0, &res); of_node_put(shmem); diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c index 6971dcf72fb9..0493aa3c12bf 100644 --- a/drivers/firmware/arm_scmi/raw_mode.c +++ b/drivers/firmware/arm_scmi/raw_mode.c @@ -818,10 +818,13 @@ static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp, * before sending it with a single RAW xfer. */ if (rd->tx_size < rd->tx_req_size) { - size_t cnt; + ssize_t cnt; cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos, buf, count); + if (cnt < 0) + return cnt; + rd->tx_size += cnt; if (cnt < count) return cnt; diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 621c37efe3ec..c193516a254d 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -40,6 +40,7 @@ /** * struct scmi_smc - Structure representing a SCMI smc transport * + * @irq: An optional IRQ for completion * @cinfo: SCMI channel info * @shmem: Transmit/Receive shared memory area * @shmem_lock: Lock to protect access to Tx/Rx shared memory area. @@ -52,6 +53,7 @@ */ struct scmi_smc { + int irq; struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; /* Protect access to shmem area */ @@ -127,7 +129,7 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, struct resource res; struct device_node *np; u32 func_id; - int ret, irq; + int ret; if (!tx) return -ENODEV; @@ -137,8 +139,10 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, return -ENOMEM; np = of_parse_phandle(cdev->of_node, "shmem", 0); - if (!of_device_is_compatible(np, "arm,scmi-shmem")) + if (!of_device_is_compatible(np, "arm,scmi-shmem")) { + of_node_put(np); return -ENXIO; + } ret = of_address_to_resource(np, 0, &res); of_node_put(np); @@ -167,11 +171,10 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, * completion of a message is signaled by an interrupt rather than by * the return of the SMC call. */ - irq = of_irq_get_byname(cdev->of_node, "a2p"); - if (irq > 0) { - ret = devm_request_irq(dev, irq, smc_msg_done_isr, - IRQF_NO_SUSPEND, - dev_name(dev), scmi_info); + scmi_info->irq = of_irq_get_byname(cdev->of_node, "a2p"); + if (scmi_info->irq > 0) { + ret = request_irq(scmi_info->irq, smc_msg_done_isr, + IRQF_NO_SUSPEND, dev_name(dev), scmi_info); if (ret) { dev_err(dev, "failed to setup SCMI smc irq\n"); return ret; @@ -193,6 +196,10 @@ static int smc_chan_free(int id, void *p, void *data) struct scmi_chan_info *cinfo = p; struct scmi_smc *scmi_info = cinfo->transport_info; + /* Ignore any possible further reception on the IRQ path */ + if (scmi_info->irq > 0) + free_irq(scmi_info->irq, scmi_info); + cinfo->transport_info = NULL; scmi_info->cinfo = NULL; diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 6a9aa97373d3..49b70c70dc69 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -978,7 +978,8 @@ static int cs_dsp_create_control(struct cs_dsp *dsp, ctl->alg_region.alg == alg_region->alg && ctl->alg_region.type == alg_region->type) { if ((!subname && !ctl->subname) || - (subname && !strncmp(ctl->subname, subname, ctl->subname_len))) { + (subname && (ctl->subname_len == subname_len) && + !strncmp(ctl->subname, subname, ctl->subname_len))) { if (!ctl->enabled) ctl->enabled = 1; return 0; diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c index 890eb454599a..1990263fbba0 100644 --- a/drivers/firmware/smccc/soc_id.c +++ b/drivers/firmware/smccc/soc_id.c @@ -34,7 +34,6 @@ static struct soc_device_attribute *soc_dev_attr; static int __init smccc_soc_init(void) { - struct arm_smccc_res res; int soc_id_rev, soc_id_version; static char soc_id_str[20], soc_id_rev_str[12]; static char soc_id_jep106_id_str[12]; @@ -49,13 +48,13 @@ static int __init smccc_soc_init(void) } if (soc_id_version < 0) { - pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0); + pr_err("Invalid SoC Version: %x\n", soc_id_version); return -EINVAL; } soc_id_rev = arm_smccc_get_soc_id_revision(); if (soc_id_rev < 0) { - pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0); + pr_err("Invalid SoC Revision: %x\n", soc_id_rev); return -EINVAL; } diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 8b49b0abacd5..f1f6f1c32987 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -429,6 +429,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) gc->set_config = gpio_sim_set_config; gc->to_irq = gpio_sim_to_irq; gc->free = gpio_sim_free; + gc->can_sleep = true; ret = devm_gpiochip_add_data(dev, gc, chip); if (ret) diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index e73885a4dc32..afb42a8e916f 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -18,7 +18,7 @@ #include <linux/spinlock.h> #include <linux/types.h> -#define WS16C48_EXTENT 10 +#define WS16C48_EXTENT 11 #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) static unsigned int base[MAX_NUM_WS16C48]; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index a3b86b86dc47..6dc950c1b689 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1296,6 +1296,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, void amdgpu_device_pci_config_reset(struct amdgpu_device *adev); int amdgpu_device_pci_reset(struct amdgpu_device *adev); bool amdgpu_device_need_post(struct amdgpu_device *adev); +bool amdgpu_sg_display_supported(struct amdgpu_device *adev); bool amdgpu_device_pcie_dynamic_switching_supported(void); bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev); bool amdgpu_device_aspm_support_quirk(void); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 040f4cb6ab2d..fb78a8f47587 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -295,7 +295,7 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, if (!p->gang_size) { ret = -EINVAL; - goto free_partial_kdata; + goto free_all_kdata; } for (i = 0; i < p->gang_size; ++i) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index a2cdde0ca0a7..45e9d737e5b8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1459,6 +1459,32 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev) } /* + * On APUs with >= 64GB white flickering has been observed w/ SG enabled. + * Disable S/G on such systems until we have a proper fix. + * https://gitlab.freedesktop.org/drm/amd/-/issues/2354 + * https://gitlab.freedesktop.org/drm/amd/-/issues/2735 + */ +bool amdgpu_sg_display_supported(struct amdgpu_device *adev) +{ + switch (amdgpu_sg_display) { + case -1: + break; + case 0: + return false; + case 1: + return true; + default: + return false; + } + if ((totalram_pages() << (PAGE_SHIFT - 10)) + + (adev->gmc.real_vram_size / 1024) >= 64000000) { + DRM_WARN("Disabling S/G due to >=64GB RAM\n"); + return false; + } + return true; +} + +/* * Intel hosts such as Raptor Lake and Sapphire Rapids don't support dynamic * speed switching. Until we have confirmation from Intel that a specific host * supports it, it's safer that we keep it disabled for all. diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 3a7af59e83ca..0451533ddde4 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -471,8 +471,12 @@ static void gfx_v11_0_check_fw_cp_gfx_shadow(struct amdgpu_device *adev) case IP_VERSION(11, 0, 3): if ((adev->gfx.me_fw_version >= 1505) && (adev->gfx.pfp_fw_version >= 1600) && - (adev->gfx.mec_fw_version >= 512)) - adev->gfx.cp_gfx_shadow = true; + (adev->gfx.mec_fw_version >= 512)) { + if (amdgpu_sriov_vf(adev)) + adev->gfx.cp_gfx_shadow = true; + else + adev->gfx.cp_gfx_shadow = false; + } break; default: adev->gfx.cp_gfx_shadow = false; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c index e1a392bcea70..af5685f4cb34 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c @@ -137,14 +137,15 @@ static int psp_v13_0_wait_for_bootloader(struct psp_context *psp) int ret; int retry_loop; + /* Wait for bootloader to signify that it is ready having bit 31 of + * C2PMSG_35 set to 1. All other bits are expected to be cleared. + * If there is an error in processing command, bits[7:0] will be set. + * This is applicable for PSP v13.0.6 and newer. + */ for (retry_loop = 0; retry_loop < 10; retry_loop++) { - /* Wait for bootloader to signify that is - ready having bit 31 of C2PMSG_35 set to 1 */ - ret = psp_wait_for(psp, - SOC15_REG_OFFSET(MP0, 0, regMP0_SMN_C2PMSG_35), - 0x80000000, - 0x80000000, - false); + ret = psp_wait_for( + psp, SOC15_REG_OFFSET(MP0, 0, regMP0_SMN_C2PMSG_35), + 0x80000000, 0xffffffff, false); if (ret == 0) return 0; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 49f40d9f16e8..f5a6f562e2a8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1543,11 +1543,7 @@ static bool kfd_ignore_crat(void) if (ignore_crat) return true; -#ifndef KFD_SUPPORT_IOMMU_V2 ret = true; -#else - ret = false; -#endif return ret; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 0b3dc754e06b..a53e0757fe64 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -194,11 +194,6 @@ static void kfd_device_info_init(struct kfd_dev *kfd, kfd_device_info_set_event_interrupt_class(kfd); - /* Raven */ - if (gc_version == IP_VERSION(9, 1, 0) || - gc_version == IP_VERSION(9, 2, 2)) - kfd->device_info.needs_iommu_device = true; - if (gc_version < IP_VERSION(11, 0, 0)) { /* Navi2x+, Navi1x+ */ if (gc_version == IP_VERSION(10, 3, 6)) @@ -233,10 +228,6 @@ static void kfd_device_info_init(struct kfd_dev *kfd, asic_type != CHIP_TONGA) kfd->device_info.supports_cwsr = true; - if (asic_type == CHIP_KAVERI || - asic_type == CHIP_CARRIZO) - kfd->device_info.needs_iommu_device = true; - if (asic_type != CHIP_HAWAII && !vf) kfd->device_info.needs_pci_atomics = true; } @@ -249,7 +240,6 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) uint32_t gfx_target_version = 0; switch (adev->asic_type) { -#ifdef KFD_SUPPORT_IOMMU_V2 #ifdef CONFIG_DRM_AMDGPU_CIK case CHIP_KAVERI: gfx_target_version = 70000; @@ -262,7 +252,6 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) if (!vf) f2g = &gfx_v8_kfd2kgd; break; -#endif #ifdef CONFIG_DRM_AMDGPU_CIK case CHIP_HAWAII: gfx_target_version = 70001; @@ -298,7 +287,6 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) gfx_target_version = 90000; f2g = &gfx_v9_kfd2kgd; break; -#ifdef KFD_SUPPORT_IOMMU_V2 /* Raven */ case IP_VERSION(9, 1, 0): case IP_VERSION(9, 2, 2): @@ -306,7 +294,6 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) if (!vf) f2g = &gfx_v9_kfd2kgd; break; -#endif /* Vega12 */ case IP_VERSION(9, 2, 1): gfx_target_version = 90004; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 2df153828ff4..01192f5abe46 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -2538,18 +2538,12 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev) } switch (dev->adev->asic_type) { - case CHIP_CARRIZO: - device_queue_manager_init_vi(&dqm->asic_ops); - break; - case CHIP_KAVERI: - device_queue_manager_init_cik(&dqm->asic_ops); - break; - case CHIP_HAWAII: device_queue_manager_init_cik_hawaii(&dqm->asic_ops); break; + case CHIP_CARRIZO: case CHIP_TONGA: case CHIP_FIJI: case CHIP_POLARIS10: diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 0fa739fd6a9c..e5554a36e8c8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1638,9 +1638,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } break; } - if (init_data.flags.gpu_vm_support && - (amdgpu_sg_display == 0)) - init_data.flags.gpu_vm_support = false; + if (init_data.flags.gpu_vm_support) + init_data.flags.gpu_vm_support = amdgpu_sg_display_supported(adev); if (init_data.flags.gpu_vm_support) adev->mode_info.gpu_vm_support = true; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 9bc86deac9e8..b885c39bd16b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -1320,7 +1320,7 @@ int compute_mst_dsc_configs_for_state(struct drm_atomic_state *state, if (computed_streams[i]) continue; - if (!res_pool->funcs->remove_stream_from_ctx || + if (res_pool->funcs->remove_stream_from_ctx && res_pool->funcs->remove_stream_from_ctx(stream->ctx->dc, dc_state, stream) != DC_OK) return -EINVAL; diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index 20d4d08a6a2f..6966420dfbac 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -777,7 +777,8 @@ void dce110_edp_wait_for_hpd_ready( dal_gpio_destroy_irq(&hpd); /* ensure that the panel is detected */ - ASSERT(edp_hpd_high); + if (!edp_hpd_high) + DC_LOG_DC("%s: wait timed out!\n", __func__); } void dce110_edp_power_control( diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.c index e5b7ef7422b8..50dc83404644 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dpp.c @@ -357,8 +357,11 @@ void dpp3_set_cursor_attributes( int cur_rom_en = 0; if (color_format == CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA || - color_format == CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA) - cur_rom_en = 1; + color_format == CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA) { + if (cursor_attributes->attribute_flags.bits.ENABLE_CURSOR_DEGAMMA) { + cur_rom_en = 1; + } + } REG_UPDATE_3(CURSOR0_CONTROL, CUR0_MODE, color_format, diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index ce41a8309582..222af2fae745 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -1581,9 +1581,9 @@ static int smu_disable_dpms(struct smu_context *smu) /* * For SMU 13.0.4/11, PMFW will handle the features disablement properly - * for gpu reset case. Driver involvement is unnecessary. + * for gpu reset and S0i3 cases. Driver involvement is unnecessary. */ - if (amdgpu_in_reset(adev)) { + if (amdgpu_in_reset(adev) || adev->in_s0ix) { switch (adev->ip_versions[MP1_HWIP][0]) { case IP_VERSION(13, 0, 4): case IP_VERSION(13, 0, 11): diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 3d188616ba24..fddcd834bcec 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -331,11 +331,13 @@ static int smu_v13_0_0_check_powerplay_table(struct smu_context *smu) struct smu_13_0_0_powerplay_table *powerplay_table = table_context->power_play_table; struct smu_baco_context *smu_baco = &smu->smu_baco; +#if 0 PPTable_t *pptable = smu->smu_table.driver_pptable; const OverDriveLimits_t * const overdrive_upperlimits = &pptable->SkuTable.OverDriveLimitsBasicMax; const OverDriveLimits_t * const overdrive_lowerlimits = &pptable->SkuTable.OverDriveLimitsMin; +#endif if (powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_HARDWAREDC) smu->dc_controlled_by_gpio = true; @@ -347,18 +349,27 @@ static int smu_v13_0_0_check_powerplay_table(struct smu_context *smu) if (powerplay_table->platform_caps & SMU_13_0_0_PP_PLATFORM_CAP_MACO) smu_baco->maco_support = true; + /* + * We are in the transition to a new OD mechanism. + * Disable the OD feature support for SMU13 temporarily. + * TODO: get this reverted when new OD mechanism online + */ +#if 0 if (!overdrive_lowerlimits->FeatureCtrlMask || !overdrive_upperlimits->FeatureCtrlMask) smu->od_enabled = false; - table_context->thermal_controller_type = - powerplay_table->thermal_controller_type; - /* * Instead of having its own buffer space and get overdrive_table copied, * smu->od_settings just points to the actual overdrive_table */ smu->od_settings = &powerplay_table->overdrive_table; +#else + smu->od_enabled = false; +#endif + + table_context->thermal_controller_type = + powerplay_table->thermal_controller_type; return 0; } @@ -1140,7 +1151,6 @@ static int smu_v13_0_0_print_clk_levels(struct smu_context *smu, (OverDriveTableExternal_t *)smu->smu_table.overdrive_table; struct smu_13_0_dpm_table *single_dpm_table; struct smu_13_0_pcie_table *pcie_table; - const int link_width[] = {0, 1, 2, 4, 8, 12, 16}; uint32_t gen_speed, lane_width; int i, curr_freq, size = 0; int32_t min_value, max_value; @@ -1256,7 +1266,7 @@ static int smu_v13_0_0_print_clk_levels(struct smu_context *smu, (pcie_table->pcie_lane[i] == 6) ? "x16" : "", pcie_table->clk_freq[i], (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) && - (lane_width == DECODE_LANE_WIDTH(link_width[pcie_table->pcie_lane[i]])) ? + (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ? "*" : ""); break; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 1ac552142763..fe4ee2daa5d8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -1993,9 +1993,8 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table gpu_metrics->average_socket_power = SMUQ10_TO_UINT(metrics->SocketPower); - /* Energy is reported in 15.625mJ units */ - gpu_metrics->energy_accumulator = - SMUQ10_TO_UINT(metrics->SocketEnergyAcc); + /* Energy counter reported in 15.259uJ (2^-16) units */ + gpu_metrics->energy_accumulator = metrics->SocketEnergyAcc; gpu_metrics->current_gfxclk = SMUQ10_TO_UINT(metrics->GfxclkFrequency[xcc0]); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index b1f0937ccade..62f2886ab4df 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -323,10 +323,12 @@ static int smu_v13_0_7_check_powerplay_table(struct smu_context *smu) struct smu_baco_context *smu_baco = &smu->smu_baco; PPTable_t *smc_pptable = table_context->driver_pptable; BoardTable_t *BoardTable = &smc_pptable->BoardTable; +#if 0 const OverDriveLimits_t * const overdrive_upperlimits = &smc_pptable->SkuTable.OverDriveLimitsBasicMax; const OverDriveLimits_t * const overdrive_lowerlimits = &smc_pptable->SkuTable.OverDriveLimitsMin; +#endif if (powerplay_table->platform_caps & SMU_13_0_7_PP_PLATFORM_CAP_HARDWAREDC) smu->dc_controlled_by_gpio = true; @@ -338,18 +340,22 @@ static int smu_v13_0_7_check_powerplay_table(struct smu_context *smu) if (smu_baco->platform_support && (BoardTable->HsrEnabled || BoardTable->VddqOffEnabled)) smu_baco->maco_support = true; +#if 0 if (!overdrive_lowerlimits->FeatureCtrlMask || !overdrive_upperlimits->FeatureCtrlMask) smu->od_enabled = false; - table_context->thermal_controller_type = - powerplay_table->thermal_controller_type; - /* * Instead of having its own buffer space and get overdrive_table copied, * smu->od_settings just points to the actual overdrive_table */ smu->od_settings = &powerplay_table->overdrive_table; +#else + smu->od_enabled = false; +#endif + + table_context->thermal_controller_type = + powerplay_table->thermal_controller_type; return 0; } diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 504d51c42f79..aadb396508c5 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -2517,9 +2517,11 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) }; int int_status[3], i; - if (it6505->enable_drv_hold || pm_runtime_get_if_in_use(dev) <= 0) + if (it6505->enable_drv_hold || !it6505->powered) return IRQ_HANDLED; + pm_runtime_get_sync(dev); + int_status[0] = it6505_read(it6505, INT_STATUS_01); int_status[1] = it6505_read(it6505, INT_STATUS_02); int_status[2] = it6505_read(it6505, INT_STATUS_03); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 5163e5224aad..9663601ce098 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -774,9 +774,7 @@ static struct mipi_dsi_device *lt9611_attach_dsi(struct lt9611 *lt9611, dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | - MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO_NO_HSA | - MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP | - MIPI_DSI_MODE_NO_EOT_PACKET; + MIPI_DSI_MODE_VIDEO_HSE; ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 4ea6507a77e5..baaf0e0feb06 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -623,7 +623,13 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct int ret; if (obj->import_attach) { + /* Reset both vm_ops and vm_private_data, so we don't end up with + * vm_ops pointing to our implementation if the dma-buf backend + * doesn't set those fields. + */ vma->vm_private_data = NULL; + vma->vm_ops = NULL; + ret = dma_buf_mmap(obj->dma_buf, vma, 0); /* Drop the reference drm_gem_mmap_obj() acquired.*/ diff --git a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c index 23857cc08eca..2702ad4c26c8 100644 --- a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c @@ -165,14 +165,60 @@ static u32 preparser_disable(bool state) return MI_ARB_CHECK | 1 << 8 | state; } -u32 *gen12_emit_aux_table_inv(struct intel_gt *gt, u32 *cs, const i915_reg_t inv_reg) +static i915_reg_t gen12_get_aux_inv_reg(struct intel_engine_cs *engine) { - u32 gsi_offset = gt->uncore->gsi_offset; + switch (engine->id) { + case RCS0: + return GEN12_CCS_AUX_INV; + case BCS0: + return GEN12_BCS0_AUX_INV; + case VCS0: + return GEN12_VD0_AUX_INV; + case VCS2: + return GEN12_VD2_AUX_INV; + case VECS0: + return GEN12_VE0_AUX_INV; + case CCS0: + return GEN12_CCS0_AUX_INV; + default: + return INVALID_MMIO_REG; + } +} + +static bool gen12_needs_ccs_aux_inv(struct intel_engine_cs *engine) +{ + i915_reg_t reg = gen12_get_aux_inv_reg(engine); + + if (IS_PONTEVECCHIO(engine->i915)) + return false; + + /* + * So far platforms supported by i915 having flat ccs do not require + * AUX invalidation. Check also whether the engine requires it. + */ + return i915_mmio_reg_valid(reg) && !HAS_FLAT_CCS(engine->i915); +} + +u32 *gen12_emit_aux_table_inv(struct intel_engine_cs *engine, u32 *cs) +{ + i915_reg_t inv_reg = gen12_get_aux_inv_reg(engine); + u32 gsi_offset = engine->gt->uncore->gsi_offset; + + if (!gen12_needs_ccs_aux_inv(engine)) + return cs; *cs++ = MI_LOAD_REGISTER_IMM(1) | MI_LRI_MMIO_REMAP_EN; *cs++ = i915_mmio_reg_offset(inv_reg) + gsi_offset; *cs++ = AUX_INV; - *cs++ = MI_NOOP; + + *cs++ = MI_SEMAPHORE_WAIT_TOKEN | + MI_SEMAPHORE_REGISTER_POLL | + MI_SEMAPHORE_POLL | + MI_SEMAPHORE_SAD_EQ_SDD; + *cs++ = 0; + *cs++ = i915_mmio_reg_offset(inv_reg) + gsi_offset; + *cs++ = 0; + *cs++ = 0; return cs; } @@ -202,8 +248,13 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) { struct intel_engine_cs *engine = rq->engine; - if (mode & EMIT_FLUSH) { - u32 flags = 0; + /* + * On Aux CCS platforms the invalidation of the Aux + * table requires quiescing memory traffic beforehand + */ + if (mode & EMIT_FLUSH || gen12_needs_ccs_aux_inv(engine)) { + u32 bit_group_0 = 0; + u32 bit_group_1 = 0; int err; u32 *cs; @@ -211,32 +262,40 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) if (err) return err; - flags |= PIPE_CONTROL_TILE_CACHE_FLUSH; - flags |= PIPE_CONTROL_FLUSH_L3; - flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; - flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + bit_group_0 |= PIPE_CONTROL0_HDC_PIPELINE_FLUSH; + + /* + * When required, in MTL and beyond platforms we + * need to set the CCS_FLUSH bit in the pipe control + */ + if (GRAPHICS_VER_FULL(rq->i915) >= IP_VER(12, 70)) + bit_group_0 |= PIPE_CONTROL_CCS_FLUSH; + + bit_group_1 |= PIPE_CONTROL_TILE_CACHE_FLUSH; + bit_group_1 |= PIPE_CONTROL_FLUSH_L3; + bit_group_1 |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + bit_group_1 |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; /* Wa_1409600907:tgl,adl-p */ - flags |= PIPE_CONTROL_DEPTH_STALL; - flags |= PIPE_CONTROL_DC_FLUSH_ENABLE; - flags |= PIPE_CONTROL_FLUSH_ENABLE; + bit_group_1 |= PIPE_CONTROL_DEPTH_STALL; + bit_group_1 |= PIPE_CONTROL_DC_FLUSH_ENABLE; + bit_group_1 |= PIPE_CONTROL_FLUSH_ENABLE; - flags |= PIPE_CONTROL_STORE_DATA_INDEX; - flags |= PIPE_CONTROL_QW_WRITE; + bit_group_1 |= PIPE_CONTROL_STORE_DATA_INDEX; + bit_group_1 |= PIPE_CONTROL_QW_WRITE; - flags |= PIPE_CONTROL_CS_STALL; + bit_group_1 |= PIPE_CONTROL_CS_STALL; if (!HAS_3D_PIPELINE(engine->i915)) - flags &= ~PIPE_CONTROL_3D_ARCH_FLAGS; + bit_group_1 &= ~PIPE_CONTROL_3D_ARCH_FLAGS; else if (engine->class == COMPUTE_CLASS) - flags &= ~PIPE_CONTROL_3D_ENGINE_FLAGS; + bit_group_1 &= ~PIPE_CONTROL_3D_ENGINE_FLAGS; cs = intel_ring_begin(rq, 6); if (IS_ERR(cs)) return PTR_ERR(cs); - cs = gen12_emit_pipe_control(cs, - PIPE_CONTROL0_HDC_PIPELINE_FLUSH, - flags, LRC_PPHWSP_SCRATCH_ADDR); + cs = gen12_emit_pipe_control(cs, bit_group_0, bit_group_1, + LRC_PPHWSP_SCRATCH_ADDR); intel_ring_advance(rq, cs); } @@ -267,10 +326,9 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) else if (engine->class == COMPUTE_CLASS) flags &= ~PIPE_CONTROL_3D_ENGINE_FLAGS; - if (!HAS_FLAT_CCS(rq->engine->i915)) - count = 8 + 4; - else - count = 8; + count = 8; + if (gen12_needs_ccs_aux_inv(rq->engine)) + count += 8; cs = intel_ring_begin(rq, count); if (IS_ERR(cs)) @@ -285,11 +343,7 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) cs = gen8_emit_pipe_control(cs, flags, LRC_PPHWSP_SCRATCH_ADDR); - if (!HAS_FLAT_CCS(rq->engine->i915)) { - /* hsdes: 1809175790 */ - cs = gen12_emit_aux_table_inv(rq->engine->gt, - cs, GEN12_GFX_CCS_AUX_NV); - } + cs = gen12_emit_aux_table_inv(engine, cs); *cs++ = preparser_disable(false); intel_ring_advance(rq, cs); @@ -300,21 +354,14 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) int gen12_emit_flush_xcs(struct i915_request *rq, u32 mode) { - intel_engine_mask_t aux_inv = 0; - u32 cmd, *cs; + u32 cmd = 4; + u32 *cs; - cmd = 4; if (mode & EMIT_INVALIDATE) { cmd += 2; - if (!HAS_FLAT_CCS(rq->engine->i915) && - (rq->engine->class == VIDEO_DECODE_CLASS || - rq->engine->class == VIDEO_ENHANCEMENT_CLASS)) { - aux_inv = rq->engine->mask & - ~GENMASK(_BCS(I915_MAX_BCS - 1), BCS0); - if (aux_inv) - cmd += 4; - } + if (gen12_needs_ccs_aux_inv(rq->engine)) + cmd += 8; } cs = intel_ring_begin(rq, cmd); @@ -338,6 +385,10 @@ int gen12_emit_flush_xcs(struct i915_request *rq, u32 mode) cmd |= MI_INVALIDATE_TLB; if (rq->engine->class == VIDEO_DECODE_CLASS) cmd |= MI_INVALIDATE_BSD; + + if (gen12_needs_ccs_aux_inv(rq->engine) && + rq->engine->class == COPY_ENGINE_CLASS) + cmd |= MI_FLUSH_DW_CCS; } *cs++ = cmd; @@ -345,14 +396,7 @@ int gen12_emit_flush_xcs(struct i915_request *rq, u32 mode) *cs++ = 0; /* upper addr */ *cs++ = 0; /* value */ - if (aux_inv) { /* hsdes: 1809175790 */ - if (rq->engine->class == VIDEO_DECODE_CLASS) - cs = gen12_emit_aux_table_inv(rq->engine->gt, - cs, GEN12_VD0_AUX_NV); - else - cs = gen12_emit_aux_table_inv(rq->engine->gt, - cs, GEN12_VE0_AUX_NV); - } + cs = gen12_emit_aux_table_inv(rq->engine, cs); if (mode & EMIT_INVALIDATE) *cs++ = preparser_disable(false); diff --git a/drivers/gpu/drm/i915/gt/gen8_engine_cs.h b/drivers/gpu/drm/i915/gt/gen8_engine_cs.h index 655e5c00ddc2..867ba697aceb 100644 --- a/drivers/gpu/drm/i915/gt/gen8_engine_cs.h +++ b/drivers/gpu/drm/i915/gt/gen8_engine_cs.h @@ -13,6 +13,7 @@ #include "intel_gt_regs.h" #include "intel_gpu_commands.h" +struct intel_engine_cs; struct intel_gt; struct i915_request; @@ -46,28 +47,32 @@ u32 *gen8_emit_fini_breadcrumb_rcs(struct i915_request *rq, u32 *cs); u32 *gen11_emit_fini_breadcrumb_rcs(struct i915_request *rq, u32 *cs); u32 *gen12_emit_fini_breadcrumb_rcs(struct i915_request *rq, u32 *cs); -u32 *gen12_emit_aux_table_inv(struct intel_gt *gt, u32 *cs, const i915_reg_t inv_reg); +u32 *gen12_emit_aux_table_inv(struct intel_engine_cs *engine, u32 *cs); static inline u32 * -__gen8_emit_pipe_control(u32 *batch, u32 flags0, u32 flags1, u32 offset) +__gen8_emit_pipe_control(u32 *batch, u32 bit_group_0, + u32 bit_group_1, u32 offset) { memset(batch, 0, 6 * sizeof(u32)); - batch[0] = GFX_OP_PIPE_CONTROL(6) | flags0; - batch[1] = flags1; + batch[0] = GFX_OP_PIPE_CONTROL(6) | bit_group_0; + batch[1] = bit_group_1; batch[2] = offset; return batch + 6; } -static inline u32 *gen8_emit_pipe_control(u32 *batch, u32 flags, u32 offset) +static inline u32 *gen8_emit_pipe_control(u32 *batch, + u32 bit_group_1, u32 offset) { - return __gen8_emit_pipe_control(batch, 0, flags, offset); + return __gen8_emit_pipe_control(batch, 0, bit_group_1, offset); } -static inline u32 *gen12_emit_pipe_control(u32 *batch, u32 flags0, u32 flags1, u32 offset) +static inline u32 *gen12_emit_pipe_control(u32 *batch, u32 bit_group_0, + u32 bit_group_1, u32 offset) { - return __gen8_emit_pipe_control(batch, flags0, flags1, offset); + return __gen8_emit_pipe_control(batch, bit_group_0, + bit_group_1, offset); } static inline u32 * diff --git a/drivers/gpu/drm/i915/gt/intel_gpu_commands.h b/drivers/gpu/drm/i915/gt/intel_gpu_commands.h index 5d143e2a8db0..2bd8d98d2110 100644 --- a/drivers/gpu/drm/i915/gt/intel_gpu_commands.h +++ b/drivers/gpu/drm/i915/gt/intel_gpu_commands.h @@ -121,6 +121,7 @@ #define MI_SEMAPHORE_TARGET(engine) ((engine)<<15) #define MI_SEMAPHORE_WAIT MI_INSTR(0x1c, 2) /* GEN8+ */ #define MI_SEMAPHORE_WAIT_TOKEN MI_INSTR(0x1c, 3) /* GEN12+ */ +#define MI_SEMAPHORE_REGISTER_POLL (1 << 16) #define MI_SEMAPHORE_POLL (1 << 15) #define MI_SEMAPHORE_SAD_GT_SDD (0 << 12) #define MI_SEMAPHORE_SAD_GTE_SDD (1 << 12) @@ -299,6 +300,7 @@ #define PIPE_CONTROL_QW_WRITE (1<<14) #define PIPE_CONTROL_POST_SYNC_OP_MASK (3<<14) #define PIPE_CONTROL_DEPTH_STALL (1<<13) +#define PIPE_CONTROL_CCS_FLUSH (1<<13) /* MTL+ */ #define PIPE_CONTROL_WRITE_FLUSH (1<<12) #define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */ #define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on ILK */ diff --git a/drivers/gpu/drm/i915/gt/intel_gt_regs.h b/drivers/gpu/drm/i915/gt/intel_gt_regs.h index 718cb2c80f79..2cdfb2f713d0 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_regs.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_regs.h @@ -332,9 +332,11 @@ #define GEN8_PRIVATE_PAT_HI _MMIO(0x40e0 + 4) #define GEN10_PAT_INDEX(index) _MMIO(0x40e0 + (index) * 4) #define BSD_HWS_PGA_GEN7 _MMIO(0x4180) -#define GEN12_GFX_CCS_AUX_NV _MMIO(0x4208) -#define GEN12_VD0_AUX_NV _MMIO(0x4218) -#define GEN12_VD1_AUX_NV _MMIO(0x4228) + +#define GEN12_CCS_AUX_INV _MMIO(0x4208) +#define GEN12_VD0_AUX_INV _MMIO(0x4218) +#define GEN12_VE0_AUX_INV _MMIO(0x4238) +#define GEN12_BCS0_AUX_INV _MMIO(0x4248) #define GEN8_RTCR _MMIO(0x4260) #define GEN8_M1TCR _MMIO(0x4264) @@ -342,14 +344,12 @@ #define GEN8_BTCR _MMIO(0x426c) #define GEN8_VTCR _MMIO(0x4270) -#define GEN12_VD2_AUX_NV _MMIO(0x4298) -#define GEN12_VD3_AUX_NV _MMIO(0x42a8) -#define GEN12_VE0_AUX_NV _MMIO(0x4238) - #define BLT_HWS_PGA_GEN7 _MMIO(0x4280) -#define GEN12_VE1_AUX_NV _MMIO(0x42b8) +#define GEN12_VD2_AUX_INV _MMIO(0x4298) +#define GEN12_CCS0_AUX_INV _MMIO(0x42c8) #define AUX_INV REG_BIT(0) + #define VEBOX_HWS_PGA_GEN7 _MMIO(0x4380) #define GEN12_AUX_ERR_DBG _MMIO(0x43f4) diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index a4ec20aaafe2..9477c2422321 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -1364,10 +1364,7 @@ gen12_emit_indirect_ctx_rcs(const struct intel_context *ce, u32 *cs) IS_DG2_G11(ce->engine->i915)) cs = gen8_emit_pipe_control(cs, PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE, 0); - /* hsdes: 1809175790 */ - if (!HAS_FLAT_CCS(ce->engine->i915)) - cs = gen12_emit_aux_table_inv(ce->engine->gt, - cs, GEN12_GFX_CCS_AUX_NV); + cs = gen12_emit_aux_table_inv(ce->engine, cs); /* Wa_16014892111 */ if (IS_MTL_GRAPHICS_STEP(ce->engine->i915, M, STEP_A0, STEP_B0) || @@ -1392,17 +1389,7 @@ gen12_emit_indirect_ctx_xcs(const struct intel_context *ce, u32 *cs) PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE, 0); - /* hsdes: 1809175790 */ - if (!HAS_FLAT_CCS(ce->engine->i915)) { - if (ce->engine->class == VIDEO_DECODE_CLASS) - cs = gen12_emit_aux_table_inv(ce->engine->gt, - cs, GEN12_VD0_AUX_NV); - else if (ce->engine->class == VIDEO_ENHANCEMENT_CLASS) - cs = gen12_emit_aux_table_inv(ce->engine->gt, - cs, GEN12_VE0_AUX_NV); - } - - return cs; + return gen12_emit_aux_table_inv(ce->engine, cs); } static void diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 2a0438f12a14..af9afdb53c7f 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -491,7 +491,7 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, return; } - msg_length = REG_FIELD_GET(DP_AUX_CH_CTL_MESSAGE_SIZE_MASK, reg); + msg_length = REG_FIELD_GET(DP_AUX_CH_CTL_MESSAGE_SIZE_MASK, value); // check the msg in DATA register. msg = vgpu_vreg(vgpu, offset + 4); diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index 8ef93889061a..5ec293011d99 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -449,8 +449,11 @@ int i915_active_add_request(struct i915_active *ref, struct i915_request *rq) } } while (unlikely(is_barrier(active))); - if (!__i915_active_fence_set(active, fence)) + fence = __i915_active_fence_set(active, fence); + if (!fence) __i915_active_acquire(ref); + else + dma_fence_put(fence); out: i915_active_release(ref); @@ -469,13 +472,9 @@ __i915_active_set_fence(struct i915_active *ref, return NULL; } - rcu_read_lock(); prev = __i915_active_fence_set(active, fence); - if (prev) - prev = dma_fence_get_rcu(prev); - else + if (!prev) __i915_active_acquire(ref); - rcu_read_unlock(); return prev; } @@ -1019,10 +1018,11 @@ void i915_request_add_active_barriers(struct i915_request *rq) * * Records the new @fence as the last active fence along its timeline in * this active tracker, moving the tracking callbacks from the previous - * fence onto this one. Returns the previous fence (if not already completed), - * which the caller must ensure is executed before the new fence. To ensure - * that the order of fences within the timeline of the i915_active_fence is - * understood, it should be locked by the caller. + * fence onto this one. Gets and returns a reference to the previous fence + * (if not already completed), which the caller must put after making sure + * that it is executed before the new fence. To ensure that the order of + * fences within the timeline of the i915_active_fence is understood, it + * should be locked by the caller. */ struct dma_fence * __i915_active_fence_set(struct i915_active_fence *active, @@ -1031,7 +1031,23 @@ __i915_active_fence_set(struct i915_active_fence *active, struct dma_fence *prev; unsigned long flags; - if (fence == rcu_access_pointer(active->fence)) + /* + * In case of fences embedded in i915_requests, their memory is + * SLAB_FAILSAFE_BY_RCU, then it can be reused right after release + * by new requests. Then, there is a risk of passing back a pointer + * to a new, completely unrelated fence that reuses the same memory + * while tracked under a different active tracker. Combined with i915 + * perf open/close operations that build await dependencies between + * engine kernel context requests and user requests from different + * timelines, this can lead to dependency loops and infinite waits. + * + * As a countermeasure, we try to get a reference to the active->fence + * first, so if we succeed and pass it back to our user then it is not + * released and potentially reused by an unrelated request before the + * user has a chance to set up an await dependency on it. + */ + prev = i915_active_fence_get(active); + if (fence == prev) return fence; GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)); @@ -1040,27 +1056,56 @@ __i915_active_fence_set(struct i915_active_fence *active, * Consider that we have two threads arriving (A and B), with * C already resident as the active->fence. * - * A does the xchg first, and so it sees C or NULL depending - * on the timing of the interrupt handler. If it is NULL, the - * previous fence must have been signaled and we know that - * we are first on the timeline. If it is still present, - * we acquire the lock on that fence and serialise with the interrupt - * handler, in the process removing it from any future interrupt - * callback. A will then wait on C before executing (if present). - * - * As B is second, it sees A as the previous fence and so waits for - * it to complete its transition and takes over the occupancy for - * itself -- remembering that it needs to wait on A before executing. + * Both A and B have got a reference to C or NULL, depending on the + * timing of the interrupt handler. Let's assume that if A has got C + * then it has locked C first (before B). * * Note the strong ordering of the timeline also provides consistent * nesting rules for the fence->lock; the inner lock is always the * older lock. */ spin_lock_irqsave(fence->lock, flags); - prev = xchg(__active_fence_slot(active), fence); - if (prev) { - GEM_BUG_ON(prev == fence); + if (prev) spin_lock_nested(prev->lock, SINGLE_DEPTH_NESTING); + + /* + * A does the cmpxchg first, and so it sees C or NULL, as before, or + * something else, depending on the timing of other threads and/or + * interrupt handler. If not the same as before then A unlocks C if + * applicable and retries, starting from an attempt to get a new + * active->fence. Meanwhile, B follows the same path as A. + * Once A succeeds with cmpxch, B fails again, retires, gets A from + * active->fence, locks it as soon as A completes, and possibly + * succeeds with cmpxchg. + */ + while (cmpxchg(__active_fence_slot(active), prev, fence) != prev) { + if (prev) { + spin_unlock(prev->lock); + dma_fence_put(prev); + } + spin_unlock_irqrestore(fence->lock, flags); + + prev = i915_active_fence_get(active); + GEM_BUG_ON(prev == fence); + + spin_lock_irqsave(fence->lock, flags); + if (prev) + spin_lock_nested(prev->lock, SINGLE_DEPTH_NESTING); + } + + /* + * If prev is NULL then the previous fence must have been signaled + * and we know that we are first on the timeline. If it is still + * present then, having the lock on that fence already acquired, we + * serialise with the interrupt handler, in the process of removing it + * from any future interrupt callback. A will then wait on C before + * executing (if present). + * + * As B is second, it sees A as the previous fence and so waits for + * it to complete its transition and takes over the occupancy for + * itself -- remembering that it needs to wait on A before executing. + */ + if (prev) { __list_del_entry(&active->cb.node); spin_unlock(prev->lock); /* serialise with prev->cb_list */ } @@ -1077,11 +1122,7 @@ int i915_active_fence_set(struct i915_active_fence *active, int err = 0; /* Must maintain timeline ordering wrt previous active requests */ - rcu_read_lock(); fence = __i915_active_fence_set(active, &rq->fence); - if (fence) /* but the previous fence may not belong to that timeline! */ - fence = dma_fence_get_rcu(fence); - rcu_read_unlock(); if (fence) { err = i915_request_await_dma_fence(rq, fence); dma_fence_put(fence); diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 894068bb37b6..833b73edefdb 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1661,6 +1661,11 @@ __i915_request_ensure_parallel_ordering(struct i915_request *rq, request_to_parent(rq)->parallel.last_rq = i915_request_get(rq); + /* + * Users have to put a reference potentially got by + * __i915_active_fence_set() to the returned request + * when no longer needed + */ return to_request(__i915_active_fence_set(&timeline->last_request, &rq->fence)); } @@ -1707,6 +1712,10 @@ __i915_request_ensure_ordering(struct i915_request *rq, 0); } + /* + * Users have to put the reference to prev potentially got + * by __i915_active_fence_set() when no longer needed + */ return prev; } @@ -1760,6 +1769,8 @@ __i915_request_add_to_timeline(struct i915_request *rq) prev = __i915_request_ensure_ordering(rq, timeline); else prev = __i915_request_ensure_parallel_ordering(rq, timeline); + if (prev) + i915_request_put(prev); /* * Make sure that no request gazumped us - if it was allocated after diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c index 5f26090b0c98..89585b31b985 100644 --- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c @@ -310,7 +310,7 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) dev_warn(ipu_crtc->dev, "8-pixel align hactive %d -> %d\n", sig_cfg.mode.hactive, new_hactive); - sig_cfg.mode.hfront_porch = new_hactive - sig_cfg.mode.hactive; + sig_cfg.mode.hfront_porch -= new_hactive - sig_cfg.mode.hactive; sig_cfg.mode.hactive = new_hactive; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f75c6f09dd2a..a2e0033e8a26 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -967,7 +967,7 @@ nouveau_connector_get_modes(struct drm_connector *connector) /* Determine display colour depth for everything except LVDS now, * DP requires this before mode_valid() is called. */ - if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && nv_connector->native_mode) + if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) nouveau_connector_detect_depth(connector); /* Find the native mode if this is a digital panel, if we didn't diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 40c8ea43c42f..b8ac66b4a2c4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -26,6 +26,8 @@ #include "head.h" #include "ior.h" +#include <drm/display/drm_dp.h> + #include <subdev/bios.h> #include <subdev/bios/init.h> #include <subdev/gpio.h> @@ -634,6 +636,50 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) return outp->dp.rates != 0; } +/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() + * converted to work inside nvkm. This is a temporary holdover until we start + * passing the drm_dp_aux device through NVKM + */ +static int +nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) +{ + struct nvkm_i2c_aux *aux = outp->dp.aux; + u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; + int ret; + + ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); + if (ret < 0) + return ret; + + /* + * Prior to DP1.3 the bit represented by + * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. + * If it is set DP_DPCD_REV at 0000h could be at a value less than + * the true capability of the panel. The only way to check is to + * then compare 0000h and 2200h. + */ + if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) + return 0; + + ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); + if (ret < 0) + return ret; + + if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { + OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", + outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); + return 0; + } + + if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) + return 0; + + memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); + + return 0; +} + void nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) { @@ -689,7 +735,7 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); } - if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, sizeof(outp->dp.dpcd))) { + if (!nvkm_dp_read_dpcd_caps(outp)) { const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; const u8 *rate; int rate_max; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h index 00dbeda7e346..de161e7a04aa 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h @@ -117,6 +117,7 @@ void gk104_grctx_generate_r418800(struct gf100_gr *); extern const struct gf100_grctx_func gk110_grctx; void gk110_grctx_generate_r419eb0(struct gf100_gr *); +void gk110_grctx_generate_r419f78(struct gf100_gr *); extern const struct gf100_grctx_func gk110b_grctx; extern const struct gf100_grctx_func gk208_grctx; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c index 94233d0119df..52a234b1ef01 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk104.c @@ -906,7 +906,9 @@ static void gk104_grctx_generate_r419f78(struct gf100_gr *gr) { struct nvkm_device *device = gr->base.engine.subdev.device; - nvkm_mask(device, 0x419f78, 0x00000001, 0x00000000); + + /* bit 3 set disables loads in fp helper invocations, we need it enabled */ + nvkm_mask(device, 0x419f78, 0x00000009, 0x00000000); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c index 4391458e1fb2..3acdd9eeb74a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110.c @@ -820,6 +820,15 @@ gk110_grctx_generate_r419eb0(struct gf100_gr *gr) nvkm_mask(device, 0x419eb0, 0x00001000, 0x00001000); } +void +gk110_grctx_generate_r419f78(struct gf100_gr *gr) +{ + struct nvkm_device *device = gr->base.engine.subdev.device; + + /* bit 3 set disables loads in fp helper invocations, we need it enabled */ + nvkm_mask(device, 0x419f78, 0x00000008, 0x00000000); +} + const struct gf100_grctx_func gk110_grctx = { .main = gf100_grctx_generate_main, @@ -854,4 +863,5 @@ gk110_grctx = { .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, .r418800 = gk104_grctx_generate_r418800, .r419eb0 = gk110_grctx_generate_r419eb0, + .r419f78 = gk110_grctx_generate_r419f78, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c index 7b9a34f9ec3c..5597e87624ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk110b.c @@ -103,4 +103,5 @@ gk110b_grctx = { .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, .r418800 = gk104_grctx_generate_r418800, .r419eb0 = gk110_grctx_generate_r419eb0, + .r419f78 = gk110_grctx_generate_r419f78, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c index c78d07a8bb7d..612656496541 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgk208.c @@ -568,4 +568,5 @@ gk208_grctx = { .dist_skip_table = gf117_grctx_generate_dist_skip_table, .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, .r418800 = gk104_grctx_generate_r418800, + .r419f78 = gk110_grctx_generate_r419f78, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c index beac66eb2a80..9906974ac3f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgm107.c @@ -988,4 +988,5 @@ gm107_grctx = { .r406500 = gm107_grctx_generate_r406500, .gpc_tpc_nr = gk104_grctx_generate_gpc_tpc_nr, .r419e00 = gm107_grctx_generate_r419e00, + .r419f78 = gk110_grctx_generate_r419f78, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c index 3b6c8100a242..a7775aa18541 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/tu102.c @@ -206,19 +206,6 @@ tu102_gr_av_to_init_veid(struct nvkm_blob *blob, struct gf100_gr_pack **ppack) return gk20a_gr_av_to_init_(blob, 64, 0x00100000, ppack); } -int -tu102_gr_load(struct gf100_gr *gr, int ver, const struct gf100_gr_fwif *fwif) -{ - int ret; - - ret = gm200_gr_load(gr, ver, fwif); - if (ret) - return ret; - - return gk20a_gr_load_net(gr, "gr/", "sw_veid_bundle_init", ver, tu102_gr_av_to_init_veid, - &gr->bundle_veid); -} - static const struct gf100_gr_fwif tu102_gr_fwif[] = { { 0, gm200_gr_load, &tu102_gr, &gp108_gr_fecs_acr, &gp108_gr_gpccs_acr }, diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c index 102e1fc7ee38..be4ec5bb5223 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c @@ -569,6 +569,7 @@ static const struct of_device_id s6d7aa0_of_match[] = { }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, s6d7aa0_of_match); static struct mipi_dsi_driver s6d7aa0_driver = { .probe = s6d7aa0_probe, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index a530ecc4d207..bf34498c1b6d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -833,12 +833,12 @@ static int vop_plane_atomic_check(struct drm_plane *plane, * need align with 2 pixel. */ if (fb->format->is_yuv && ((new_plane_state->src.x1 >> 16) % 2)) { - DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n"); + DRM_DEBUG_KMS("Invalid Source: Yuv format not support odd xpos\n"); return -EINVAL; } if (fb->format->is_yuv && new_plane_state->rotation & DRM_MODE_REFLECT_Y) { - DRM_ERROR("Invalid Source: Yuv format does not support this rotation\n"); + DRM_DEBUG_KMS("Invalid Source: Yuv format does not support this rotation\n"); return -EINVAL; } @@ -846,7 +846,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, struct vop *vop = to_vop(crtc); if (!vop->data->afbc) { - DRM_ERROR("vop does not support AFBC\n"); + DRM_DEBUG_KMS("vop does not support AFBC\n"); return -EINVAL; } @@ -855,15 +855,16 @@ static int vop_plane_atomic_check(struct drm_plane *plane, return ret; if (new_plane_state->src.x1 || new_plane_state->src.y1) { - DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", - new_plane_state->src.x1, - new_plane_state->src.y1, fb->offsets[0]); + DRM_DEBUG_KMS("AFBC does not support offset display, " \ + "xpos=%d, ypos=%d, offset=%d\n", + new_plane_state->src.x1, new_plane_state->src.y1, + fb->offsets[0]); return -EINVAL; } if (new_plane_state->rotation && new_plane_state->rotation != DRM_MODE_ROTATE_0) { - DRM_ERROR("No rotation support in AFBC, rotation=%d\n", - new_plane_state->rotation); + DRM_DEBUG_KMS("No rotation support in AFBC, rotation=%d\n", + new_plane_state->rotation); return -EINVAL; } } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 7139a522b2f3..54e3083076b7 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -519,7 +519,8 @@ static bool ttm_bo_evict_swapout_allowable(struct ttm_buffer_object *bo, if (bo->pin_count) { *locked = false; - *busy = false; + if (busy) + *busy = false; return false; } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 5261526d286f..a8dec24b146b 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2615,9 +2615,13 @@ static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops vc4_snd_dai_ops = { + .probe = vc4_hdmi_audio_cpu_dai_probe, +}; + static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = { .name = "vc4-hdmi-cpu-dai", - .probe = vc4_hdmi_audio_cpu_dai_probe, + .ops = &vc4_snd_dai_ops, .playback = { .stream_name = "Playback", .channels_min = 1, diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 5978e9dbc286..ebf15f31d97e 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -209,8 +209,7 @@ int vmbus_connect(void) * Setup the vmbus event connection for channel interrupt * abstraction stuff */ - vmbus_connection.int_page = - (void *)hv_alloc_hyperv_zeroed_page(); + vmbus_connection.int_page = hv_alloc_hyperv_zeroed_page(); if (vmbus_connection.int_page == NULL) { ret = -ENOMEM; goto cleanup; @@ -225,8 +224,8 @@ int vmbus_connect(void) * Setup the monitor notification facility. The 1st page for * parent->child and the 2nd page for child->parent */ - vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_page(); - vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_page(); + vmbus_connection.monitor_pages[0] = hv_alloc_hyperv_page(); + vmbus_connection.monitor_pages[1] = hv_alloc_hyperv_page(); if ((vmbus_connection.monitor_pages[0] == NULL) || (vmbus_connection.monitor_pages[1] == NULL)) { ret = -ENOMEM; @@ -333,15 +332,15 @@ void vmbus_disconnect(void) destroy_workqueue(vmbus_connection.work_queue); if (vmbus_connection.int_page) { - hv_free_hyperv_page((unsigned long)vmbus_connection.int_page); + hv_free_hyperv_page(vmbus_connection.int_page); vmbus_connection.int_page = NULL; } set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[0], 1); set_memory_encrypted((unsigned long)vmbus_connection.monitor_pages[1], 1); - hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[0]); - hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[1]); + hv_free_hyperv_page(vmbus_connection.monitor_pages[0]); + hv_free_hyperv_page(vmbus_connection.monitor_pages[1]); vmbus_connection.monitor_pages[0] = NULL; vmbus_connection.monitor_pages[1] = NULL; } diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index dffcc894f117..0d7a3ba66396 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1628,7 +1628,7 @@ static int hv_free_page_report(struct page_reporting_dev_info *pr_dev_info, WARN_ON_ONCE(nents > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES); WARN_ON_ONCE(sgl->length < (HV_HYP_PAGE_SIZE << page_reporting_order)); local_irq_save(flags); - hint = *(struct hv_memory_hint **)this_cpu_ptr(hyperv_pcpu_input_arg); + hint = *this_cpu_ptr(hyperv_pcpu_input_arg); if (!hint) { local_irq_restore(flags); return -ENOSPC; diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 542a1d53b303..6a2258fef1fe 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -115,12 +115,12 @@ void *hv_alloc_hyperv_zeroed_page(void) } EXPORT_SYMBOL_GPL(hv_alloc_hyperv_zeroed_page); -void hv_free_hyperv_page(unsigned long addr) +void hv_free_hyperv_page(void *addr) { if (PAGE_SIZE == HV_HYP_PAGE_SIZE) - free_page(addr); + free_page((unsigned long)addr); else - kfree((void *)addr); + kfree(addr); } EXPORT_SYMBOL_GPL(hv_free_hyperv_page); @@ -253,7 +253,7 @@ static void hv_kmsg_dump_unregister(void) atomic_notifier_chain_unregister(&panic_notifier_list, &hyperv_panic_report_block); - hv_free_hyperv_page((unsigned long)hv_panic_page); + hv_free_hyperv_page(hv_panic_page); hv_panic_page = NULL; } @@ -270,7 +270,7 @@ static void hv_kmsg_dump_register(void) ret = kmsg_dump_register(&hv_kmsg_dumper); if (ret) { pr_err("Hyper-V: kmsg dump register error 0x%x\n", ret); - hv_free_hyperv_page((unsigned long)hv_panic_page); + hv_free_hyperv_page(hv_panic_page); hv_panic_page = NULL; } } diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index a997dbcb563f..023807859be7 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -13,9 +13,11 @@ #include <linux/crc16.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/hid.h> #include <linux/hwmon.h> #include <linux/jiffies.h> +#include <linux/ktime.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/seq_file.h> @@ -63,6 +65,8 @@ static const char *const aqc_device_names[] = { #define CTRL_REPORT_ID 0x03 #define AQUAERO_CTRL_REPORT_ID 0x0b +#define CTRL_REPORT_DELAY 200 /* ms */ + /* The HID report that the official software always sends * after writing values, currently same for all devices */ @@ -527,6 +531,9 @@ struct aqc_data { int secondary_ctrl_report_size; u8 *secondary_ctrl_report; + ktime_t last_ctrl_report_op; + int ctrl_report_delay; /* Delay between two ctrl report operations, in ms */ + int buffer_size; u8 *buffer; int checksum_start; @@ -611,17 +618,35 @@ static int aqc_aquastreamxt_convert_fan_rpm(u16 val) return 0; } +static void aqc_delay_ctrl_report(struct aqc_data *priv) +{ + /* + * If previous read or write is too close to this one, delay the current operation + * to give the device enough time to process the previous one. + */ + if (priv->ctrl_report_delay) { + s64 delta = ktime_ms_delta(ktime_get(), priv->last_ctrl_report_op); + + if (delta < priv->ctrl_report_delay) + msleep(priv->ctrl_report_delay - delta); + } +} + /* Expects the mutex to be locked */ static int aqc_get_ctrl_data(struct aqc_data *priv) { int ret; + aqc_delay_ctrl_report(priv); + memset(priv->buffer, 0x00, priv->buffer_size); ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); if (ret < 0) ret = -ENODATA; + priv->last_ctrl_report_op = ktime_get(); + return ret; } @@ -631,6 +656,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv) int ret; u16 checksum; + aqc_delay_ctrl_report(priv); + /* Checksum is not needed for Aquaero */ if (priv->kind != aquaero) { /* Init and xorout value for CRC-16/USB is 0xffff */ @@ -646,12 +673,16 @@ static int aqc_send_ctrl_data(struct aqc_data *priv) ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret < 0) - return ret; + goto record_access_and_ret; /* The official software sends this report after every change, so do it here as well */ ret = hid_hw_raw_request(priv->hdev, priv->secondary_ctrl_report_id, priv->secondary_ctrl_report, priv->secondary_ctrl_report_size, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + +record_access_and_ret: + priv->last_ctrl_report_op = ktime_get(); + return ret; } @@ -1524,6 +1555,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->buffer_size = AQUAERO_CTRL_REPORT_SIZE; priv->temp_ctrl_offset = AQUAERO_TEMP_CTRL_OFFSET; + priv->ctrl_report_delay = CTRL_REPORT_DELAY; priv->temp_label = label_temp_sensors; priv->virtual_temp_label = label_virtual_temp_sensors; @@ -1547,6 +1579,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET; priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; + priv->ctrl_report_delay = CTRL_REPORT_DELAY; priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; @@ -1597,6 +1630,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET; priv->buffer_size = OCTO_CTRL_REPORT_SIZE; + priv->ctrl_report_delay = CTRL_REPORT_DELAY; priv->power_cycle_count_offset = OCTO_POWER_CYCLES; @@ -1624,6 +1658,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; + priv->ctrl_report_delay = CTRL_REPORT_DELAY; priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET; priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index fa5070ae26bc..7c5f4b10a7c1 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -17,12 +17,13 @@ enum chips {pfe1100, pfe3000}; /* - * Disable status check for pfe3000 devices, because some devices report - * communication error (invalid command) for VOUT_MODE command (0x20) - * although correct VOUT_MODE (0x16) is returned: it leads to incorrect - * exponent in linear mode. + * Disable status check because some devices report communication error + * (invalid command) for VOUT_MODE command (0x20) although the correct + * VOUT_MODE (0x16) is returned: it leads to incorrect exponent in linear + * mode. + * This affects both pfe3000 and pfe1100. */ -static struct pmbus_platform_data pfe3000_plat_data = { +static struct pmbus_platform_data pfe_plat_data = { .flags = PMBUS_SKIP_STATUS_CHECK, }; @@ -94,16 +95,15 @@ static int pfe_pmbus_probe(struct i2c_client *client) int model; model = (int)i2c_match_id(pfe_device_id, client)->driver_data; + client->dev.platform_data = &pfe_plat_data; /* * PFE3000-12-069RA devices may not stay in page 0 during device * probe which leads to probe failure (read status word failed). * So let's set the device to page 0 at the beginning. */ - if (model == pfe3000) { - client->dev.platform_data = &pfe3000_plat_data; + if (model == pfe3000) i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); - } return pmbus_do_probe(client, &pfe_driver_info[model]); } diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 8685e0b58a83..7bc3ebfe8081 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -62,7 +62,6 @@ #define AD7192_MODE_STA_MASK BIT(20) /* Status Register transmission Mask */ #define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */ #define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */ -#define AD7192_MODE_ACX BIT(14) /* AC excitation enable(AD7195 only)*/ #define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */ #define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/ #define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */ @@ -91,6 +90,7 @@ /* Configuration Register Bit Designations (AD7192_REG_CONF) */ #define AD7192_CONF_CHOP BIT(23) /* CHOP enable */ +#define AD7192_CONF_ACX BIT(22) /* AC excitation enable(AD7195 only) */ #define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */ #define AD7192_CONF_CHAN(x) ((x) << 8) /* Channel select */ #define AD7192_CONF_CHAN_MASK (0x7FF << 8) /* Channel select mask */ @@ -472,7 +472,7 @@ static ssize_t ad7192_show_ac_excitation(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7192_state *st = iio_priv(indio_dev); - return sysfs_emit(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); + return sysfs_emit(buf, "%d\n", !!(st->conf & AD7192_CONF_ACX)); } static ssize_t ad7192_show_bridge_switch(struct device *dev, @@ -513,13 +513,13 @@ static ssize_t ad7192_set(struct device *dev, ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon); break; - case AD7192_REG_MODE: + case AD7192_REG_CONF: if (val) - st->mode |= AD7192_MODE_ACX; + st->conf |= AD7192_CONF_ACX; else - st->mode &= ~AD7192_MODE_ACX; + st->conf &= ~AD7192_CONF_ACX; - ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); break; default: ret = -EINVAL; @@ -579,12 +579,11 @@ static IIO_DEVICE_ATTR(bridge_switch_en, 0644, static IIO_DEVICE_ATTR(ac_excitation_en, 0644, ad7192_show_ac_excitation, ad7192_set, - AD7192_REG_MODE); + AD7192_REG_CONF); static struct attribute *ad7192_attributes[] = { &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, - &iio_dev_attr_ac_excitation_en.dev_attr.attr, NULL }; @@ -595,6 +594,7 @@ static const struct attribute_group ad7192_attribute_group = { static struct attribute *ad7195_attributes[] = { &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr, &iio_dev_attr_bridge_switch_en.dev_attr.attr, + &iio_dev_attr_ac_excitation_en.dev_attr.attr, NULL }; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 213526c1592f..aea83f369437 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -124,6 +124,7 @@ static const struct regmap_config ina2xx_regmap_config = { enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { + const char *name; u16 config_default; int calibration_value; int shunt_voltage_lsb; /* nV */ @@ -155,6 +156,7 @@ struct ina2xx_chip_info { static const struct ina2xx_config ina2xx_config[] = { [ina219] = { + .name = "ina219", .config_default = INA219_CONFIG_DEFAULT, .calibration_value = 4096, .shunt_voltage_lsb = 10000, @@ -164,6 +166,7 @@ static const struct ina2xx_config ina2xx_config[] = { .chip_id = ina219, }, [ina226] = { + .name = "ina226", .config_default = INA226_CONFIG_DEFAULT, .calibration_value = 2048, .shunt_voltage_lsb = 2500, @@ -996,7 +999,7 @@ static int ina2xx_probe(struct i2c_client *client) /* Patch the current config register with default. */ val = chip->config->config_default; - if (id->driver_data == ina226) { + if (type == ina226) { ina226_set_average(chip, INA226_DEFAULT_AVG, &val); ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); @@ -1015,7 +1018,7 @@ static int ina2xx_probe(struct i2c_client *client) } indio_dev->modes = INDIO_DIRECT_MODE; - if (id->driver_data == ina226) { + if (type == ina226) { indio_dev->channels = ina226_channels; indio_dev->num_channels = ARRAY_SIZE(ina226_channels); indio_dev->info = &ina226_info; @@ -1024,7 +1027,7 @@ static int ina2xx_probe(struct i2c_client *client) indio_dev->num_channels = ARRAY_SIZE(ina219_channels); indio_dev->info = &ina219_info; } - indio_dev->name = id->name; + indio_dev->name = id ? id->name : chip->config->name; ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, &ina2xx_setup_ops); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index af6bfcc19075..eb78a6f17fd0 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -916,12 +916,6 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) goto err_vref; } - ret = clk_prepare_enable(priv->core_clk); - if (ret) { - dev_err(dev, "failed to enable core clk\n"); - goto err_core_clk; - } - regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); @@ -948,8 +942,6 @@ err_adc_clk: regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, MESON_SAR_ADC_REG3_ADC_EN, 0); meson_sar_adc_set_bandgap(indio_dev, false); - clk_disable_unprepare(priv->core_clk); -err_core_clk: regulator_disable(priv->vref); err_vref: meson_sar_adc_unlock(indio_dev); @@ -977,8 +969,6 @@ static void meson_sar_adc_hw_disable(struct iio_dev *indio_dev) meson_sar_adc_set_bandgap(indio_dev, false); - clk_disable_unprepare(priv->core_clk); - regulator_disable(priv->vref); if (!ret) @@ -1211,7 +1201,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (IS_ERR(priv->clkin)) return dev_err_probe(dev, PTR_ERR(priv->clkin), "failed to get clkin\n"); - priv->core_clk = devm_clk_get(dev, "core"); + priv->core_clk = devm_clk_get_enabled(dev, "core"); if (IS_ERR(priv->core_clk)) return dev_err_probe(dev, PTR_ERR(priv->core_clk), "failed to get core clk\n"); @@ -1294,15 +1284,26 @@ static int meson_sar_adc_remove(struct platform_device *pdev) static int meson_sar_adc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); meson_sar_adc_hw_disable(indio_dev); + clk_disable_unprepare(priv->core_clk); + return 0; } static int meson_sar_adc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(priv->core_clk); + if (ret) { + dev_err(dev, "failed to enable core clk\n"); + return ret; + } return meson_sar_adc_hw_enable(indio_dev); } diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index 943e9e14d1e9..b72d39fc2434 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -253,7 +253,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, platform_set_drvdata(pdev, indio_dev); state->ec = ec->ec_dev; - state->msg = devm_kzalloc(&pdev->dev, + state->msg = devm_kzalloc(&pdev->dev, sizeof(*state->msg) + max((u16)sizeof(struct ec_params_motion_sense), state->ec->max_response), GFP_KERNEL); if (!state->msg) diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 9bf8337806fc..8c8e0bbfc99f 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -344,9 +344,12 @@ static int admv1013_update_quad_filters(struct admv1013_state *st) static int admv1013_update_mixer_vgate(struct admv1013_state *st) { - unsigned int vcm, mixer_vgate; + unsigned int mixer_vgate; + int vcm; vcm = regulator_get_voltage(st->reg); + if (vcm < 0) + return vcm; if (vcm < 1800000) mixer_vgate = (2389 * vcm / 1000000 + 8100) / 100; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 6a18b363cf73..b6e6b1df8a61 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2687,7 +2687,7 @@ unknown_format: static int lsm6dsx_get_acpi_mount_matrix(struct device *dev, struct iio_mount_matrix *orientation) { - return false; + return -EOPNOTSUPP; } #endif diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index c117f50d0cf3..adcba832e6fa 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1888,7 +1888,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops; int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - struct fwnode_handle *fwnode; + struct fwnode_handle *fwnode = NULL; int ret; if (!indio_dev->info) @@ -1899,7 +1899,8 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) /* If the calling driver did not initialize firmware node, do it here */ if (dev_fwnode(&indio_dev->dev)) fwnode = dev_fwnode(&indio_dev->dev); - else + /* The default dummy IIO device has no parent */ + else if (indio_dev->dev.parent) fwnode = dev_fwnode(indio_dev->dev.parent); device_set_node(&indio_dev->dev, fwnode); diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c index 489902bed7f0..b50bf8973d9a 100644 --- a/drivers/iio/light/rohm-bu27008.c +++ b/drivers/iio/light/rohm-bu27008.c @@ -190,7 +190,7 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = { .address = BU27008_REG_##data##_LO, \ .scan_index = BU27008_##color, \ .scan_type = { \ - .sign = 's', \ + .sign = 'u', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_LE, \ @@ -633,7 +633,7 @@ static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val, for (i = 0; i < data->gts.num_itime; i++) { new_time_sel = data->gts.itime_table[i].sel; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, - new_time_sel, val, val2 * 1000, gain_sel); + new_time_sel, val, val2, gain_sel); if (!ret) break; } @@ -662,7 +662,7 @@ static int bu27008_set_scale(struct bu27008_data *data, goto unlock_out; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, - val, val2 * 1000, &gain_sel); + val, val2, &gain_sel); if (ret) { ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel); if (ret) @@ -677,6 +677,21 @@ unlock_out: return ret; } +static int bu27008_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + static int bu27008_write_raw(struct iio_dev *idev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -756,6 +771,7 @@ static int bu27008_update_scan_mode(struct iio_dev *idev, static const struct iio_info bu27008_info = { .read_raw = &bu27008_read_raw, .write_raw = &bu27008_write_raw, + .write_raw_get_fmt = &bu27008_write_raw_get_fmt, .read_avail = &bu27008_read_avail, .update_scan_mode = bu27008_update_scan_mode, .validate_trigger = iio_validate_own_trigger, diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c index e63ef5789cde..bf3de853a811 100644 --- a/drivers/iio/light/rohm-bu27034.c +++ b/drivers/iio/light/rohm-bu27034.c @@ -575,7 +575,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, return -EINVAL; if (chan == BU27034_CHAN_ALS) { - if (val == 0 && val2 == 1000) + if (val == 0 && val2 == 1000000) return 0; return -EINVAL; @@ -587,7 +587,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, goto unlock_out; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, - val, val2 * 1000, &gain_sel); + val, val2, &gain_sel); if (ret) { /* * Could not support scale with given time. Need to change time. @@ -624,7 +624,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, /* Can we provide requested scale with this time? */ ret = iio_gts_find_gain_sel_for_scale_using_time( - &data->gts, new_time_sel, val, val2 * 1000, + &data->gts, new_time_sel, val, val2, &gain_sel); if (ret) continue; @@ -1217,6 +1217,21 @@ static int bu27034_read_raw(struct iio_dev *idev, } } +static int bu27034_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + static int bu27034_write_raw(struct iio_dev *idev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -1267,6 +1282,7 @@ static int bu27034_read_avail(struct iio_dev *idev, static const struct iio_info bu27034_info = { .read_raw = &bu27034_read_raw, .write_raw = &bu27034_write_raw, + .write_raw_get_fmt = &bu27034_write_raw_get_fmt, .read_avail = &bu27034_read_avail, }; diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 755a9c57db6f..f9ab671c8eda 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -85,6 +85,8 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, dma_addr_t mask; int i; + umem->iova = va = virt; + if (umem->is_odp) { unsigned int page_size = BIT(to_ib_umem_odp(umem)->page_shift); @@ -100,7 +102,6 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem, */ pgsz_bitmap &= GENMASK(BITS_PER_LONG - 1, PAGE_SHIFT); - umem->iova = va = virt; /* The best result is the smallest page size that results in the minimum * number of required pages. Compute the largest page size that could * work based on VA address bits that don't change. diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index b42166fe7454..63e98e2d3596 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -1253,6 +1253,8 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) rc = bnxt_re_setup_chip_ctx(rdev, wqe_mode); if (rc) { + bnxt_unregister_dev(rdev->en_dev); + clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags); ibdev_err(&rdev->ibdev, "Failed to get chip context\n"); return -EINVAL; } @@ -1526,8 +1528,8 @@ static void bnxt_re_remove(struct auxiliary_device *adev) } bnxt_re_setup_cc(rdev, false); ib_unregister_device(&rdev->ibdev); - ib_dealloc_device(&rdev->ibdev); bnxt_re_dev_uninit(rdev); + ib_dealloc_device(&rdev->ibdev); skip_remove: mutex_unlock(&bnxt_re_mutex); } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 5fd8f7c90bb0..739d942761d1 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -819,6 +819,7 @@ static int bnxt_qplib_alloc_dpi_tbl(struct bnxt_qplib_res *res, } memset((u8 *)dpit->tbl, 0xFF, bytes); + mutex_init(&res->dpi_tbl_lock); dpit->priv_db = dpit->ucreg.bar_reg + dpit->ucreg.offset; return 0; diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 9dbb89e9f4af..baaa4406d5e6 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -12307,6 +12307,7 @@ static void free_cntrs(struct hfi1_devdata *dd) if (dd->synth_stats_timer.function) del_timer_sync(&dd->synth_stats_timer); + cancel_work_sync(&dd->update_cntr_work); ppd = (struct hfi1_pportdata *)(dd + 1); for (i = 0; i < dd->num_pports; i++, ppd++) { kfree(ppd->cntrs); diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index 8f385f9c2dd3..d5f2a6b5376b 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -83,6 +83,11 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm) temp = agg_peak[bucket] * bcm->vote_scale; bcm->vote_y[bucket] = bcm_div(temp, bcm->aux_data.unit); + + if (bcm->enable_mask && (bcm->vote_x[bucket] || bcm->vote_y[bucket])) { + bcm->vote_x[bucket] = 0; + bcm->vote_y[bucket] = bcm->enable_mask; + } } if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 && diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index 04391c1ba465..7843d8864d6b 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -81,6 +81,7 @@ struct qcom_icc_node { * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm * @vote_scale: scaling factor for vote_x and vote_y + * @enable_mask: optional mask to send as vote instead of vote_x/vote_y * @dirty: flag used to indicate whether the bcm needs to be committed * @keepalive: flag used to indicate whether a keepalive is required * @aux_data: auxiliary data used when calculating threshold values and @@ -97,6 +98,7 @@ struct qcom_icc_bcm { u64 vote_x[QCOM_ICC_NUM_BUCKETS]; u64 vote_y[QCOM_ICC_NUM_BUCKETS]; u64 vote_scale; + u32 enable_mask; bool dirty; bool keepalive; struct bcm_db aux_data; diff --git a/drivers/interconnect/qcom/sa8775p.c b/drivers/interconnect/qcom/sa8775p.c index da21cc31a580..f56538669de0 100644 --- a/drivers/interconnect/qcom/sa8775p.c +++ b/drivers/interconnect/qcom/sa8775p.c @@ -1873,6 +1873,7 @@ static struct qcom_icc_node srvc_snoc = { static struct qcom_icc_bcm bcm_acv = { .name = "ACV", + .enable_mask = 0x8, .num_nodes = 1, .nodes = { &ebi }, }; diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c index 2d7a8e7b85ec..e64c214b4020 100644 --- a/drivers/interconnect/qcom/sm8450.c +++ b/drivers/interconnect/qcom/sm8450.c @@ -1337,6 +1337,7 @@ static struct qcom_icc_node qns_mem_noc_sf_disp = { static struct qcom_icc_bcm bcm_acv = { .name = "ACV", + .enable_mask = 0x8, .num_nodes = 1, .nodes = { &ebi }, }; @@ -1349,6 +1350,7 @@ static struct qcom_icc_bcm bcm_ce0 = { static struct qcom_icc_bcm bcm_cn0 = { .name = "CN0", + .enable_mask = 0x1, .keepalive = true, .num_nodes = 55, .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, @@ -1383,6 +1385,7 @@ static struct qcom_icc_bcm bcm_cn0 = { static struct qcom_icc_bcm bcm_co0 = { .name = "CO0", + .enable_mask = 0x1, .num_nodes = 2, .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, }; @@ -1403,6 +1406,7 @@ static struct qcom_icc_bcm bcm_mm0 = { static struct qcom_icc_bcm bcm_mm1 = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 12, .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, &qnm_camnoc_sf, &qnm_mdp, @@ -1445,6 +1449,7 @@ static struct qcom_icc_bcm bcm_sh0 = { static struct qcom_icc_bcm bcm_sh1 = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 7, .nodes = { &alm_gpu_tcu, &alm_sys_tcu, &qnm_nsp_gemnoc, &qnm_pcie, @@ -1461,6 +1466,7 @@ static struct qcom_icc_bcm bcm_sn0 = { static struct qcom_icc_bcm bcm_sn1 = { .name = "SN1", + .enable_mask = 0x1, .num_nodes = 4, .nodes = { &qhm_gic, &qxm_pimem, &xm_gic, &qns_gemnoc_gc }, @@ -1492,6 +1498,7 @@ static struct qcom_icc_bcm bcm_sn7 = { static struct qcom_icc_bcm bcm_acv_disp = { .name = "ACV", + .enable_mask = 0x1, .num_nodes = 1, .nodes = { &ebi_disp }, }; @@ -1510,6 +1517,7 @@ static struct qcom_icc_bcm bcm_mm0_disp = { static struct qcom_icc_bcm bcm_mm1_disp = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 3, .nodes = { &qnm_mdp_disp, &qnm_rot_disp, &qns_mem_noc_sf_disp }, @@ -1523,6 +1531,7 @@ static struct qcom_icc_bcm bcm_sh0_disp = { static struct qcom_icc_bcm bcm_sh1_disp = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 1, .nodes = { &qnm_pcie_disp }, }; diff --git a/drivers/interconnect/qcom/sm8550.c b/drivers/interconnect/qcom/sm8550.c index d823ba988ef6..0864ed285375 100644 --- a/drivers/interconnect/qcom/sm8550.c +++ b/drivers/interconnect/qcom/sm8550.c @@ -1473,6 +1473,7 @@ static struct qcom_icc_node qns_mem_noc_sf_cam_ife_2 = { static struct qcom_icc_bcm bcm_acv = { .name = "ACV", + .enable_mask = 0x8, .num_nodes = 1, .nodes = { &ebi }, }; @@ -1485,6 +1486,7 @@ static struct qcom_icc_bcm bcm_ce0 = { static struct qcom_icc_bcm bcm_cn0 = { .name = "CN0", + .enable_mask = 0x1, .keepalive = true, .num_nodes = 54, .nodes = { &qsm_cfg, &qhs_ahb2phy0, @@ -1524,6 +1526,7 @@ static struct qcom_icc_bcm bcm_cn1 = { static struct qcom_icc_bcm bcm_co0 = { .name = "CO0", + .enable_mask = 0x1, .num_nodes = 2, .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, }; @@ -1549,6 +1552,7 @@ static struct qcom_icc_bcm bcm_mm0 = { static struct qcom_icc_bcm bcm_mm1 = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 8, .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, &qnm_camnoc_sf, &qnm_vapss_hcp, @@ -1589,6 +1593,7 @@ static struct qcom_icc_bcm bcm_sh0 = { static struct qcom_icc_bcm bcm_sh1 = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 13, .nodes = { &alm_gpu_tcu, &alm_sys_tcu, &chm_apps, &qnm_gpu, @@ -1608,6 +1613,7 @@ static struct qcom_icc_bcm bcm_sn0 = { static struct qcom_icc_bcm bcm_sn1 = { .name = "SN1", + .enable_mask = 0x1, .num_nodes = 3, .nodes = { &qhm_gic, &xm_gic, &qns_gemnoc_gc }, @@ -1633,6 +1639,7 @@ static struct qcom_icc_bcm bcm_sn7 = { static struct qcom_icc_bcm bcm_acv_disp = { .name = "ACV", + .enable_mask = 0x1, .num_nodes = 1, .nodes = { &ebi_disp }, }; @@ -1657,12 +1664,14 @@ static struct qcom_icc_bcm bcm_sh0_disp = { static struct qcom_icc_bcm bcm_sh1_disp = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 2, .nodes = { &qnm_mnoc_hf_disp, &qnm_pcie_disp }, }; static struct qcom_icc_bcm bcm_acv_cam_ife_0 = { .name = "ACV", + .enable_mask = 0x0, .num_nodes = 1, .nodes = { &ebi_cam_ife_0 }, }; @@ -1681,6 +1690,7 @@ static struct qcom_icc_bcm bcm_mm0_cam_ife_0 = { static struct qcom_icc_bcm bcm_mm1_cam_ife_0 = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 4, .nodes = { &qnm_camnoc_hf_cam_ife_0, &qnm_camnoc_icp_cam_ife_0, &qnm_camnoc_sf_cam_ife_0, &qns_mem_noc_sf_cam_ife_0 }, @@ -1694,6 +1704,7 @@ static struct qcom_icc_bcm bcm_sh0_cam_ife_0 = { static struct qcom_icc_bcm bcm_sh1_cam_ife_0 = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 3, .nodes = { &qnm_mnoc_hf_cam_ife_0, &qnm_mnoc_sf_cam_ife_0, &qnm_pcie_cam_ife_0 }, @@ -1701,6 +1712,7 @@ static struct qcom_icc_bcm bcm_sh1_cam_ife_0 = { static struct qcom_icc_bcm bcm_acv_cam_ife_1 = { .name = "ACV", + .enable_mask = 0x0, .num_nodes = 1, .nodes = { &ebi_cam_ife_1 }, }; @@ -1719,6 +1731,7 @@ static struct qcom_icc_bcm bcm_mm0_cam_ife_1 = { static struct qcom_icc_bcm bcm_mm1_cam_ife_1 = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 4, .nodes = { &qnm_camnoc_hf_cam_ife_1, &qnm_camnoc_icp_cam_ife_1, &qnm_camnoc_sf_cam_ife_1, &qns_mem_noc_sf_cam_ife_1 }, @@ -1732,6 +1745,7 @@ static struct qcom_icc_bcm bcm_sh0_cam_ife_1 = { static struct qcom_icc_bcm bcm_sh1_cam_ife_1 = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 3, .nodes = { &qnm_mnoc_hf_cam_ife_1, &qnm_mnoc_sf_cam_ife_1, &qnm_pcie_cam_ife_1 }, @@ -1739,6 +1753,7 @@ static struct qcom_icc_bcm bcm_sh1_cam_ife_1 = { static struct qcom_icc_bcm bcm_acv_cam_ife_2 = { .name = "ACV", + .enable_mask = 0x0, .num_nodes = 1, .nodes = { &ebi_cam_ife_2 }, }; @@ -1757,6 +1772,7 @@ static struct qcom_icc_bcm bcm_mm0_cam_ife_2 = { static struct qcom_icc_bcm bcm_mm1_cam_ife_2 = { .name = "MM1", + .enable_mask = 0x1, .num_nodes = 4, .nodes = { &qnm_camnoc_hf_cam_ife_2, &qnm_camnoc_icp_cam_ife_2, &qnm_camnoc_sf_cam_ife_2, &qns_mem_noc_sf_cam_ife_2 }, @@ -1770,6 +1786,7 @@ static struct qcom_icc_bcm bcm_sh0_cam_ife_2 = { static struct qcom_icc_bcm bcm_sh1_cam_ife_2 = { .name = "SH1", + .enable_mask = 0x1, .num_nodes = 3, .nodes = { &qnm_mnoc_hf_cam_ife_2, &qnm_mnoc_sf_cam_ife_2, &qnm_pcie_cam_ife_2 }, diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index c0331b268010..fe391de1aba3 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -839,7 +839,7 @@ hfcpci_fill_fifo(struct bchannel *bch) *z1t = cpu_to_le16(new_z1); /* now send data */ if (bch->tx_idx < bch->tx_skb->len) return; - dev_kfree_skb(bch->tx_skb); + dev_kfree_skb_any(bch->tx_skb); if (get_next_bframe(bch)) goto next_t_frame; return; @@ -895,7 +895,7 @@ hfcpci_fill_fifo(struct bchannel *bch) } bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ bz->f1 = new_f1; /* next frame */ - dev_kfree_skb(bch->tx_skb); + dev_kfree_skb_any(bch->tx_skb); get_next_bframe(bch); } @@ -1119,7 +1119,7 @@ tx_birq(struct bchannel *bch) if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) hfcpci_fill_fifo(bch); else { - dev_kfree_skb(bch->tx_skb); + dev_kfree_skb_any(bch->tx_skb); if (get_next_bframe(bch)) hfcpci_fill_fifo(bch); } @@ -2277,7 +2277,7 @@ _hfcpci_softirq(struct device *dev, void *unused) return 0; if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { - spin_lock(&hc->lock); + spin_lock_irq(&hc->lock); bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ main_rec_hfcpci(bch); @@ -2288,7 +2288,7 @@ _hfcpci_softirq(struct device *dev, void *unused) main_rec_hfcpci(bch); tx_birq(bch); } - spin_unlock(&hc->lock); + spin_unlock_irq(&hc->lock); } return 0; } diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h index fa09d511a8ed..baf31258f5c9 100644 --- a/drivers/isdn/mISDN/dsp.h +++ b/drivers/isdn/mISDN/dsp.h @@ -247,7 +247,7 @@ extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp); extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id); extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb); extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb); -extern void dsp_cmx_send(void *arg); +extern void dsp_cmx_send(struct timer_list *arg); extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb); extern int dsp_cmx_del_conf_member(struct dsp *dsp); extern int dsp_cmx_del_conf(struct dsp_conf *conf); diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 357b87592eb4..61cb45c5d0d8 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1614,7 +1614,7 @@ static u16 dsp_count; /* last sample count */ static int dsp_count_valid; /* if we have last sample count */ void -dsp_cmx_send(void *arg) +dsp_cmx_send(struct timer_list *arg) { struct dsp_conf *conf; struct dsp_conf_member *member; diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 386084530c2f..fae95f166688 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -1195,7 +1195,7 @@ static int __init dsp_init(void) } /* set sample timer */ - timer_setup(&dsp_spl_tl, (void *)dsp_cmx_send, 0); + timer_setup(&dsp_spl_tl, dsp_cmx_send, 0); dsp_spl_tl.expires = jiffies + dsp_tics; dsp_spl_jiffies = dsp_spl_tl.expires; add_timer(&dsp_spl_tl); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 7f0802a5518c..3418d2dd9371 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -251,8 +251,8 @@ int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt, pkt->extradata_size = 0; pkt->shdr.hdr.size = - struct_size((struct hfi_session_set_buffers_pkt *)0, - buffer_info, bd->num_buffers); + struct_size_t(struct hfi_session_set_buffers_pkt, + buffer_info, bd->num_buffers); } pkt->response_req = bd->response_required; diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 4a750da1c12a..deb6e65b59af 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -755,6 +755,43 @@ const char *const tegra_mc_error_names[8] = { [6] = "SMMU translation error", }; +struct icc_node *tegra_mc_icc_xlate(struct of_phandle_args *spec, void *data) +{ + struct tegra_mc *mc = icc_provider_to_tegra_mc(data); + struct icc_node *node; + + list_for_each_entry(node, &mc->provider.nodes, node_list) { + if (node->id == spec->args[0]) + return node; + } + + /* + * If a client driver calls devm_of_icc_get() before the MC driver + * is probed, then return EPROBE_DEFER to the client driver. + */ + return ERR_PTR(-EPROBE_DEFER); +} + +static int tegra_mc_icc_get(struct icc_node *node, u32 *average, u32 *peak) +{ + *average = 0; + *peak = 0; + + return 0; +} + +static int tegra_mc_icc_set(struct icc_node *src, struct icc_node *dst) +{ + return 0; +} + +const struct tegra_mc_icc_ops tegra_mc_icc_ops = { + .xlate = tegra_mc_icc_xlate, + .aggregate = icc_std_aggregate, + .get_bw = tegra_mc_icc_get, + .set = tegra_mc_icc_set, +}; + /* * Memory Controller (MC) has few Memory Clients that are issuing memory * bandwidth allocation requests to the MC interconnect provider. The MC diff --git a/drivers/memory/tegra/tegra194.c b/drivers/memory/tegra/tegra194.c index b2416ee3ac26..26035ac3a1eb 100644 --- a/drivers/memory/tegra/tegra194.c +++ b/drivers/memory/tegra/tegra194.c @@ -1355,6 +1355,7 @@ const struct tegra_mc_soc tegra194_mc_soc = { MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, .has_addr_hi_reg = true, .ops = &tegra186_mc_ops, + .icc_ops = &tegra_mc_icc_ops, .ch_intmask = 0x00000f00, .global_intstatus_channel_shift = 8, }; diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index 8e873a7bc34f..8fb83b39f5f5 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -827,7 +827,7 @@ static int tegra234_mc_icc_set(struct icc_node *src, struct icc_node *dst) return 0; if (!mc->bwmgr_mrq_supported) - return -EINVAL; + return 0; if (!mc->bpmp) { dev_err(mc->dev, "BPMP reference NULL\n"); @@ -874,7 +874,7 @@ static int tegra234_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, struct tegra_mc *mc = icc_provider_to_tegra_mc(p); if (!mc->bwmgr_mrq_supported) - return -EINVAL; + return 0; if (node->id == TEGRA_ICC_MC_CPU_CLUSTER0 || node->id == TEGRA_ICC_MC_CPU_CLUSTER1 || @@ -889,27 +889,6 @@ static int tegra234_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, return 0; } -static struct icc_node* -tegra234_mc_of_icc_xlate(struct of_phandle_args *spec, void *data) -{ - struct tegra_mc *mc = icc_provider_to_tegra_mc(data); - unsigned int cl_id = spec->args[0]; - struct icc_node *node; - - list_for_each_entry(node, &mc->provider.nodes, node_list) { - if (node->id != cl_id) - continue; - - return node; - } - - /* - * If a client driver calls devm_of_icc_get() before the MC driver - * is probed, then return EPROBE_DEFER to the client driver. - */ - return ERR_PTR(-EPROBE_DEFER); -} - static int tegra234_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak) { *avg = 0; @@ -919,7 +898,7 @@ static int tegra234_mc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pea } static const struct tegra_mc_icc_ops tegra234_mc_icc_ops = { - .xlate = tegra234_mc_of_icc_xlate, + .xlate = tegra_mc_icc_xlate, .aggregate = tegra234_mc_icc_aggregate, .get_bw = tegra234_mc_icc_get_init_bw, .set = tegra234_mc_icc_set, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6f5b259a6d6a..85be64579fc9 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -237,6 +237,29 @@ config MFD_CROS_EC_DEV To compile this driver as a module, choose M here: the module will be called cros-ec-dev. +config MFD_CS42L43 + tristate + select MFD_CORE + select REGMAP + +config MFD_CS42L43_I2C + tristate "Cirrus Logic CS42L43 (I2C)" + depends on I2C + select REGMAP_I2C + select MFD_CS42L43 + help + Select this to support the Cirrus Logic CS42L43 PC CODEC with + headphone and class D speaker drivers over I2C. + +config MFD_CS42L43_SDW + tristate "Cirrus Logic CS42L43 (SoundWire)" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select MFD_CS42L43 + help + Select this to support the Cirrus Logic CS42L43 PC CODEC with + headphone and class D speaker drivers over SoundWire. + config MFD_MADERA tristate "Cirrus Logic Madera codecs" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3d1f1dc73b5..c66f07edcd0e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,9 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o +obj-$(CONFIG_MFD_CS42L43) += cs42l43.o +obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o +obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o diff --git a/drivers/mfd/cs42l43-i2c.c b/drivers/mfd/cs42l43-i2c.c new file mode 100644 index 000000000000..4922211680c9 --- /dev/null +++ b/drivers/mfd/cs42l43-i2c.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CS42L43 I2C driver + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> + +#include "cs42l43.h" + +static const struct regmap_config cs42l43_i2c_regmap = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + + .max_register = CS42L43_MCU_RAM_MAX, + .readable_reg = cs42l43_readable_register, + .volatile_reg = cs42l43_volatile_register, + .precious_reg = cs42l43_precious_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs42l43_reg_default, + .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default), +}; + +static int cs42l43_i2c_probe(struct i2c_client *i2c) +{ + struct cs42l43 *cs42l43; + int ret; + + cs42l43 = devm_kzalloc(&i2c->dev, sizeof(*cs42l43), GFP_KERNEL); + if (!cs42l43) + return -ENOMEM; + + cs42l43->dev = &i2c->dev; + cs42l43->irq = i2c->irq; + /* A device on an I2C is always attached by definition. */ + cs42l43->attached = true; + + cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap); + if (IS_ERR(cs42l43->regmap)) { + ret = PTR_ERR(cs42l43->regmap); + dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return cs42l43_dev_probe(cs42l43); +} + +static void cs42l43_i2c_remove(struct i2c_client *i2c) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&i2c->dev); + + cs42l43_dev_remove(cs42l43); +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id cs42l43_of_match[] = { + { .compatible = "cirrus,cs42l43", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l43_of_match); +#endif + +#if IS_ENABLED(CONFIG_ACPI) +static const struct acpi_device_id cs42l43_acpi_match[] = { + { "CSC4243", 0 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match); +#endif + +static struct i2c_driver cs42l43_i2c_driver = { + .driver = { + .name = "cs42l43", + .pm = pm_ptr(&cs42l43_pm_ops), + .of_match_table = of_match_ptr(cs42l43_of_match), + .acpi_match_table = ACPI_PTR(cs42l43_acpi_match), + }, + + .probe = cs42l43_i2c_probe, + .remove = cs42l43_i2c_remove, +}; +module_i2c_driver(cs42l43_i2c_driver); + +MODULE_IMPORT_NS(MFD_CS42L43); + +MODULE_DESCRIPTION("CS42L43 I2C Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cs42l43-sdw.c b/drivers/mfd/cs42l43-sdw.c new file mode 100644 index 000000000000..7392b3d2e6b9 --- /dev/null +++ b/drivers/mfd/cs42l43-sdw.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CS42L43 SoundWire driver + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> + +#include "cs42l43.h" + +enum cs42l43_sdw_ports { + CS42L43_DMIC_DEC_ASP_PORT = 1, + CS42L43_SPK_TX_PORT, + CS42L43_SPDIF_HP_PORT, + CS42L43_SPK_RX_PORT, + CS42L43_ASP_PORT, +}; + +static const struct regmap_config cs42l43_sdw_regmap = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + + .max_register = CS42L43_MCU_RAM_MAX, + .readable_reg = cs42l43_readable_register, + .volatile_reg = cs42l43_volatile_register, + .precious_reg = cs42l43_precious_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = cs42l43_reg_default, + .num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default), +}; + +static int cs42l43_read_prop(struct sdw_slave *sdw) +{ + struct sdw_slave_prop *prop = &sdw->prop; + struct device *dev = &sdw->dev; + struct sdw_dpn_prop *dpn; + unsigned long addr; + int nval; + int i; + u32 bit; + + prop->use_domain_irq = true; + prop->paging_support = true; + prop->wake_capable = true; + prop->source_ports = BIT(CS42L43_DMIC_DEC_ASP_PORT) | BIT(CS42L43_SPK_TX_PORT); + prop->sink_ports = BIT(CS42L43_SPDIF_HP_PORT) | + BIT(CS42L43_SPK_RX_PORT) | BIT(CS42L43_ASP_PORT); + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | + SDW_SCP_INT1_IMPL_DEF; + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].max_ch = 2; + dpn[i].type = SDW_DPN_FULL; + dpn[i].max_word = 24; + i++; + } + /* + * All ports are 2 channels max, except the first one, + * CS42L43_DMIC_DEC_ASP_PORT. + */ + dpn[CS42L43_DMIC_DEC_ASP_PORT].max_ch = 4; + + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].max_ch = 2; + dpn[i].type = SDW_DPN_FULL; + dpn[i].max_word = 24; + i++; + } + + return 0; +} + +static int cs42l43_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); + + switch (status) { + case SDW_SLAVE_ATTACHED: + dev_dbg(cs42l43->dev, "Device attach\n"); + + sdw_write_no_pm(sdw, CS42L43_GEN_INT_MASK_1, + CS42L43_INT_STAT_GEN1_MASK); + + cs42l43->attached = true; + + complete(&cs42l43->device_attach); + break; + case SDW_SLAVE_UNATTACHED: + dev_dbg(cs42l43->dev, "Device detach\n"); + + cs42l43->attached = false; + + reinit_completion(&cs42l43->device_attach); + complete(&cs42l43->device_detach); + break; + default: + break; + } + + return 0; +} + +static int cs42l43_sdw_interrupt(struct sdw_slave *sdw, + struct sdw_slave_intr_status *status) +{ + /* + * The IRQ itself was handled through the regmap_irq handler, this is + * just clearing up the additional Cirrus SoundWire registers that are + * not covered by the SoundWire framework or the IRQ handler itself. + * There is only a single bit in GEN_INT_STAT_1 and it doesn't clear if + * IRQs are still pending so doing a read/write here after handling the + * IRQ is fine. + */ + sdw_read_no_pm(sdw, CS42L43_GEN_INT_STAT_1); + sdw_write_no_pm(sdw, CS42L43_GEN_INT_STAT_1, CS42L43_INT_STAT_GEN1_MASK); + + return 0; +} + +static int cs42l43_sdw_bus_config(struct sdw_slave *sdw, + struct sdw_bus_params *params) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); + int ret = 0; + + mutex_lock(&cs42l43->pll_lock); + + if (cs42l43->sdw_freq != params->curr_dr_freq / 2) { + if (cs42l43->sdw_pll_active) { + dev_err(cs42l43->dev, + "PLL active can't change SoundWire bus clock\n"); + ret = -EBUSY; + } else { + cs42l43->sdw_freq = params->curr_dr_freq / 2; + } + } + + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static const struct sdw_slave_ops cs42l43_sdw_ops = { + .read_prop = cs42l43_read_prop, + .update_status = cs42l43_sdw_update_status, + .interrupt_callback = cs42l43_sdw_interrupt, + .bus_config = cs42l43_sdw_bus_config, +}; + +static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id) +{ + struct cs42l43 *cs42l43; + struct device *dev = &sdw->dev; + int ret; + + cs42l43 = devm_kzalloc(dev, sizeof(*cs42l43), GFP_KERNEL); + if (!cs42l43) + return -ENOMEM; + + cs42l43->dev = dev; + cs42l43->sdw = sdw; + + cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap); + if (IS_ERR(cs42l43->regmap)) { + ret = PTR_ERR(cs42l43->regmap); + dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return cs42l43_dev_probe(cs42l43); +} + +static int cs42l43_sdw_remove(struct sdw_slave *sdw) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev); + + cs42l43_dev_remove(cs42l43); + + return 0; +} + +static const struct sdw_device_id cs42l43_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0), + {} +}; +MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id); + +static struct sdw_driver cs42l43_sdw_driver = { + .driver = { + .name = "cs42l43", + .pm = pm_ptr(&cs42l43_pm_ops), + }, + + .probe = cs42l43_sdw_probe, + .remove = cs42l43_sdw_remove, + .id_table = cs42l43_sdw_id, + .ops = &cs42l43_sdw_ops, +}; +module_sdw_driver(cs42l43_sdw_driver); + +MODULE_IMPORT_NS(MFD_CS42L43); + +MODULE_DESCRIPTION("CS42L43 SoundWire Driver"); +MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c new file mode 100644 index 000000000000..37b23e9bae82 --- /dev/null +++ b/drivers/mfd/cs42l43.c @@ -0,0 +1,1188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CS42L43 core driver + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/bitops.h> +#include <linux/build_bug.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/jiffies.h> +#include <linux/mfd/core.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw.h> + +#include "cs42l43.h" + +#define CS42L43_RESET_DELAY 20 + +#define CS42L43_SDW_ATTACH_TIMEOUT 500 +#define CS42L43_SDW_DETACH_TIMEOUT 100 + +#define CS42L43_MCU_BOOT_STAGE1 1 +#define CS42L43_MCU_BOOT_STAGE2 2 +#define CS42L43_MCU_BOOT_STAGE3 3 +#define CS42L43_MCU_BOOT_STAGE4 4 +#define CS42L43_MCU_POLL 5000 +#define CS42L43_MCU_CMD_TIMEOUT 20000 +#define CS42L43_MCU_UPDATE_FORMAT 3 +#define CS42L43_MCU_UPDATE_OFFSET 0x100000 +#define CS42L43_MCU_UPDATE_TIMEOUT 500000 +#define CS42L43_MCU_UPDATE_RETRIES 5 + +#define CS42L43_MCU_SUPPORTED_REV 0x2105 +#define CS42L43_MCU_SHADOW_REGS_REQUIRED_REV 0x2200 +#define CS42L43_MCU_SUPPORTED_BIOS_REV 0x0001 + +#define CS42L43_VDDP_DELAY 50 +#define CS42L43_VDDD_DELAY 1000 + +#define CS42L43_AUTOSUSPEND_TIME 250 + +struct cs42l43_patch_header { + __le16 version; + __le16 size; + u8 reserved; + u8 secure; + __le16 bss_size; + __le32 apply_addr; + __le32 checksum; + __le32 sha; + __le16 swrev; + __le16 patchid; + __le16 ipxid; + __le16 romver; + __le32 load_addr; +} __packed; + +static const struct reg_sequence cs42l43_reva_patch[] = { + { 0x4000, 0x00000055 }, + { 0x4000, 0x000000AA }, + { 0x10084, 0x00000000 }, + { 0x1741C, 0x00CD2000 }, + { 0x1718C, 0x00000003 }, + { 0x4000, 0x00000000 }, + { CS42L43_CCM_BLK_CLK_CONTROL, 0x00000002 }, + { CS42L43_HPPATHVOL, 0x011B011B }, + { CS42L43_OSC_DIV_SEL, 0x00000001 }, + { CS42L43_DACCNFG2, 0x00000005 }, + { CS42L43_MIC_DETECT_CONTROL_ANDROID, 0x80790079 }, + { CS42L43_RELID, 0x0000000F }, +}; + +const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS] = { + { CS42L43_DRV_CTRL1, 0x000186C0 }, + { CS42L43_DRV_CTRL3, 0x286DB018 }, + { CS42L43_DRV_CTRL4, 0x000006D8 }, + { CS42L43_DRV_CTRL_5, 0x136C00C0 }, + { CS42L43_GPIO_CTRL1, 0x00000707 }, + { CS42L43_GPIO_CTRL2, 0x00000000 }, + { CS42L43_GPIO_FN_SEL, 0x00000000 }, + { CS42L43_MCLK_SRC_SEL, 0x00000000 }, + { CS42L43_SAMPLE_RATE1, 0x00000003 }, + { CS42L43_SAMPLE_RATE2, 0x00000003 }, + { CS42L43_SAMPLE_RATE3, 0x00000003 }, + { CS42L43_SAMPLE_RATE4, 0x00000003 }, + { CS42L43_PLL_CONTROL, 0x00000000 }, + { CS42L43_FS_SELECT1, 0x00000000 }, + { CS42L43_FS_SELECT2, 0x00000000 }, + { CS42L43_FS_SELECT3, 0x00000000 }, + { CS42L43_FS_SELECT4, 0x00000000 }, + { CS42L43_PDM_CONTROL, 0x00000000 }, + { CS42L43_ASP_CLK_CONFIG1, 0x00010001 }, + { CS42L43_ASP_CLK_CONFIG2, 0x00000000 }, + { CS42L43_OSC_DIV_SEL, 0x00000001 }, + { CS42L43_ADC_B_CTRL1, 0x00000000 }, + { CS42L43_ADC_B_CTRL2, 0x00000000 }, + { CS42L43_DECIM_HPF_WNF_CTRL1, 0x00000001 }, + { CS42L43_DECIM_HPF_WNF_CTRL2, 0x00000001 }, + { CS42L43_DECIM_HPF_WNF_CTRL3, 0x00000001 }, + { CS42L43_DECIM_HPF_WNF_CTRL4, 0x00000001 }, + { CS42L43_DMIC_PDM_CTRL, 0x00000000 }, + { CS42L43_DECIM_VOL_CTRL_CH1_CH2, 0x20122012 }, + { CS42L43_DECIM_VOL_CTRL_CH3_CH4, 0x20122012 }, + { CS42L43_INTP_VOLUME_CTRL1, 0x00000180 }, + { CS42L43_INTP_VOLUME_CTRL2, 0x00000180 }, + { CS42L43_AMP1_2_VOL_RAMP, 0x00000022 }, + { CS42L43_ASP_CTRL, 0x00000004 }, + { CS42L43_ASP_FSYNC_CTRL1, 0x000000FA }, + { CS42L43_ASP_FSYNC_CTRL2, 0x00000001 }, + { CS42L43_ASP_FSYNC_CTRL3, 0x00000000 }, + { CS42L43_ASP_FSYNC_CTRL4, 0x000001F4 }, + { CS42L43_ASP_DATA_CTRL, 0x0000003A }, + { CS42L43_ASP_RX_EN, 0x00000000 }, + { CS42L43_ASP_TX_EN, 0x00000000 }, + { CS42L43_ASP_RX_CH1_CTRL, 0x00170001 }, + { CS42L43_ASP_RX_CH2_CTRL, 0x00170031 }, + { CS42L43_ASP_RX_CH3_CTRL, 0x00170061 }, + { CS42L43_ASP_RX_CH4_CTRL, 0x00170091 }, + { CS42L43_ASP_RX_CH5_CTRL, 0x001700C1 }, + { CS42L43_ASP_RX_CH6_CTRL, 0x001700F1 }, + { CS42L43_ASP_TX_CH1_CTRL, 0x00170001 }, + { CS42L43_ASP_TX_CH2_CTRL, 0x00170031 }, + { CS42L43_ASP_TX_CH3_CTRL, 0x00170061 }, + { CS42L43_ASP_TX_CH4_CTRL, 0x00170091 }, + { CS42L43_ASP_TX_CH5_CTRL, 0x001700C1 }, + { CS42L43_ASP_TX_CH6_CTRL, 0x001700F1 }, + { CS42L43_ASPTX1_INPUT, 0x00800000 }, + { CS42L43_ASPTX2_INPUT, 0x00800000 }, + { CS42L43_ASPTX3_INPUT, 0x00800000 }, + { CS42L43_ASPTX4_INPUT, 0x00800000 }, + { CS42L43_ASPTX5_INPUT, 0x00800000 }, + { CS42L43_ASPTX6_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP1_CH1_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP1_CH2_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP1_CH3_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP1_CH4_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP2_CH1_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP2_CH2_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP3_CH1_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP3_CH2_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP4_CH1_INPUT, 0x00800000 }, + { CS42L43_SWIRE_DP4_CH2_INPUT, 0x00800000 }, + { CS42L43_ASRC_INT1_INPUT1, 0x00800000 }, + { CS42L43_ASRC_INT2_INPUT1, 0x00800000 }, + { CS42L43_ASRC_INT3_INPUT1, 0x00800000 }, + { CS42L43_ASRC_INT4_INPUT1, 0x00800000 }, + { CS42L43_ASRC_DEC1_INPUT1, 0x00800000 }, + { CS42L43_ASRC_DEC2_INPUT1, 0x00800000 }, + { CS42L43_ASRC_DEC3_INPUT1, 0x00800000 }, + { CS42L43_ASRC_DEC4_INPUT1, 0x00800000 }, + { CS42L43_ISRC1INT1_INPUT1, 0x00800000 }, + { CS42L43_ISRC1INT2_INPUT1, 0x00800000 }, + { CS42L43_ISRC1DEC1_INPUT1, 0x00800000 }, + { CS42L43_ISRC1DEC2_INPUT1, 0x00800000 }, + { CS42L43_ISRC2INT1_INPUT1, 0x00800000 }, + { CS42L43_ISRC2INT2_INPUT1, 0x00800000 }, + { CS42L43_ISRC2DEC1_INPUT1, 0x00800000 }, + { CS42L43_ISRC2DEC2_INPUT1, 0x00800000 }, + { CS42L43_EQ1MIX_INPUT1, 0x00800000 }, + { CS42L43_EQ1MIX_INPUT2, 0x00800000 }, + { CS42L43_EQ1MIX_INPUT3, 0x00800000 }, + { CS42L43_EQ1MIX_INPUT4, 0x00800000 }, + { CS42L43_EQ2MIX_INPUT1, 0x00800000 }, + { CS42L43_EQ2MIX_INPUT2, 0x00800000 }, + { CS42L43_EQ2MIX_INPUT3, 0x00800000 }, + { CS42L43_EQ2MIX_INPUT4, 0x00800000 }, + { CS42L43_SPDIF1_INPUT1, 0x00800000 }, + { CS42L43_SPDIF2_INPUT1, 0x00800000 }, + { CS42L43_AMP1MIX_INPUT1, 0x00800000 }, + { CS42L43_AMP1MIX_INPUT2, 0x00800000 }, + { CS42L43_AMP1MIX_INPUT3, 0x00800000 }, + { CS42L43_AMP1MIX_INPUT4, 0x00800000 }, + { CS42L43_AMP2MIX_INPUT1, 0x00800000 }, + { CS42L43_AMP2MIX_INPUT2, 0x00800000 }, + { CS42L43_AMP2MIX_INPUT3, 0x00800000 }, + { CS42L43_AMP2MIX_INPUT4, 0x00800000 }, + { CS42L43_AMP3MIX_INPUT1, 0x00800000 }, + { CS42L43_AMP3MIX_INPUT2, 0x00800000 }, + { CS42L43_AMP3MIX_INPUT3, 0x00800000 }, + { CS42L43_AMP3MIX_INPUT4, 0x00800000 }, + { CS42L43_AMP4MIX_INPUT1, 0x00800000 }, + { CS42L43_AMP4MIX_INPUT2, 0x00800000 }, + { CS42L43_AMP4MIX_INPUT3, 0x00800000 }, + { CS42L43_AMP4MIX_INPUT4, 0x00800000 }, + { CS42L43_ASRC_INT_ENABLES, 0x00000100 }, + { CS42L43_ASRC_DEC_ENABLES, 0x00000100 }, + { CS42L43_PDNCNTL, 0x00000000 }, + { CS42L43_RINGSENSE_DEB_CTRL, 0x0000001B }, + { CS42L43_TIPSENSE_DEB_CTRL, 0x0000001B }, + { CS42L43_HS2, 0x050106F3 }, + { CS42L43_STEREO_MIC_CTRL, 0x00000000 }, + { CS42L43_STEREO_MIC_CLAMP_CTRL, 0x00000001 }, + { CS42L43_BLOCK_EN2, 0x00000000 }, + { CS42L43_BLOCK_EN3, 0x00000000 }, + { CS42L43_BLOCK_EN4, 0x00000000 }, + { CS42L43_BLOCK_EN5, 0x00000000 }, + { CS42L43_BLOCK_EN6, 0x00000000 }, + { CS42L43_BLOCK_EN7, 0x00000000 }, + { CS42L43_BLOCK_EN8, 0x00000000 }, + { CS42L43_BLOCK_EN9, 0x00000000 }, + { CS42L43_BLOCK_EN10, 0x00000000 }, + { CS42L43_BLOCK_EN11, 0x00000000 }, + { CS42L43_TONE_CH1_CTRL, 0x00000000 }, + { CS42L43_TONE_CH2_CTRL, 0x00000000 }, + { CS42L43_MIC_DETECT_CONTROL_1, 0x00000003 }, + { CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, 0x02000003 }, + { CS42L43_MIC_DETECT_CONTROL_ANDROID, 0x80790079 }, + { CS42L43_ISRC1_CTRL, 0x00000000 }, + { CS42L43_ISRC2_CTRL, 0x00000000 }, + { CS42L43_CTRL_REG, 0x00000006 }, + { CS42L43_FDIV_FRAC, 0x40000000 }, + { CS42L43_CAL_RATIO, 0x00000080 }, + { CS42L43_SPI_CLK_CONFIG1, 0x00000000 }, + { CS42L43_SPI_CONFIG1, 0x00000000 }, + { CS42L43_SPI_CONFIG2, 0x00000000 }, + { CS42L43_SPI_CONFIG3, 0x00000001 }, + { CS42L43_SPI_CONFIG4, 0x00000000 }, + { CS42L43_TRAN_CONFIG3, 0x00000000 }, + { CS42L43_TRAN_CONFIG4, 0x00000000 }, + { CS42L43_TRAN_CONFIG5, 0x00000000 }, + { CS42L43_TRAN_CONFIG6, 0x00000000 }, + { CS42L43_TRAN_CONFIG7, 0x00000000 }, + { CS42L43_TRAN_CONFIG8, 0x00000000 }, + { CS42L43_DACCNFG1, 0x00000008 }, + { CS42L43_DACCNFG2, 0x00000005 }, + { CS42L43_HPPATHVOL, 0x011B011B }, + { CS42L43_PGAVOL, 0x00003470 }, + { CS42L43_LOADDETENA, 0x00000000 }, + { CS42L43_CTRL, 0x00000037 }, + { CS42L43_COEFF_DATA_IN0, 0x00000000 }, + { CS42L43_COEFF_RD_WR0, 0x00000000 }, + { CS42L43_START_EQZ0, 0x00000000 }, + { CS42L43_MUTE_EQ_IN0, 0x00000000 }, + { CS42L43_DECIM_MASK, 0x0000000F }, + { CS42L43_EQ_MIX_MASK, 0x0000000F }, + { CS42L43_ASP_MASK, 0x000000FF }, + { CS42L43_PLL_MASK, 0x00000003 }, + { CS42L43_SOFT_MASK, 0x0000FFFF }, + { CS42L43_SWIRE_MASK, 0x00007FFF }, + { CS42L43_MSM_MASK, 0x00000FFF }, + { CS42L43_ACC_DET_MASK, 0x00000FFF }, + { CS42L43_I2C_TGT_MASK, 0x00000003 }, + { CS42L43_SPI_MSTR_MASK, 0x00000007 }, + { CS42L43_SW_TO_SPI_BRIDGE_MASK, 0x00000001 }, + { CS42L43_OTP_MASK, 0x00000007 }, + { CS42L43_CLASS_D_AMP_MASK, 0x00003FFF }, + { CS42L43_GPIO_INT_MASK, 0x0000003F }, + { CS42L43_ASRC_MASK, 0x0000000F }, + { CS42L43_HPOUT_MASK, 0x00000003 }, +}; +EXPORT_SYMBOL_NS_GPL(cs42l43_reg_default, MFD_CS42L43); + +bool cs42l43_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L43_DEVID: + case CS42L43_REVID: + case CS42L43_RELID: + case CS42L43_SFT_RESET: + case CS42L43_DRV_CTRL1: + case CS42L43_DRV_CTRL3: + case CS42L43_DRV_CTRL4: + case CS42L43_DRV_CTRL_5: + case CS42L43_GPIO_CTRL1: + case CS42L43_GPIO_CTRL2: + case CS42L43_GPIO_STS: + case CS42L43_GPIO_FN_SEL: + case CS42L43_MCLK_SRC_SEL: + case CS42L43_SAMPLE_RATE1 ... CS42L43_SAMPLE_RATE4: + case CS42L43_PLL_CONTROL: + case CS42L43_FS_SELECT1 ... CS42L43_FS_SELECT4: + case CS42L43_PDM_CONTROL: + case CS42L43_ASP_CLK_CONFIG1 ... CS42L43_ASP_CLK_CONFIG2: + case CS42L43_OSC_DIV_SEL: + case CS42L43_ADC_B_CTRL1 ... CS42L43_ADC_B_CTRL2: + case CS42L43_DECIM_HPF_WNF_CTRL1 ... CS42L43_DECIM_HPF_WNF_CTRL4: + case CS42L43_DMIC_PDM_CTRL: + case CS42L43_DECIM_VOL_CTRL_CH1_CH2 ... CS42L43_DECIM_VOL_CTRL_CH3_CH4: + case CS42L43_INTP_VOLUME_CTRL1 ... CS42L43_INTP_VOLUME_CTRL2: + case CS42L43_AMP1_2_VOL_RAMP: + case CS42L43_ASP_CTRL: + case CS42L43_ASP_FSYNC_CTRL1 ... CS42L43_ASP_FSYNC_CTRL4: + case CS42L43_ASP_DATA_CTRL: + case CS42L43_ASP_RX_EN ... CS42L43_ASP_TX_EN: + case CS42L43_ASP_RX_CH1_CTRL ... CS42L43_ASP_RX_CH6_CTRL: + case CS42L43_ASP_TX_CH1_CTRL ... CS42L43_ASP_TX_CH6_CTRL: + case CS42L43_OTP_REVISION_ID: + case CS42L43_ASPTX1_INPUT: + case CS42L43_ASPTX2_INPUT: + case CS42L43_ASPTX3_INPUT: + case CS42L43_ASPTX4_INPUT: + case CS42L43_ASPTX5_INPUT: + case CS42L43_ASPTX6_INPUT: + case CS42L43_SWIRE_DP1_CH1_INPUT: + case CS42L43_SWIRE_DP1_CH2_INPUT: + case CS42L43_SWIRE_DP1_CH3_INPUT: + case CS42L43_SWIRE_DP1_CH4_INPUT: + case CS42L43_SWIRE_DP2_CH1_INPUT: + case CS42L43_SWIRE_DP2_CH2_INPUT: + case CS42L43_SWIRE_DP3_CH1_INPUT: + case CS42L43_SWIRE_DP3_CH2_INPUT: + case CS42L43_SWIRE_DP4_CH1_INPUT: + case CS42L43_SWIRE_DP4_CH2_INPUT: + case CS42L43_ASRC_INT1_INPUT1: + case CS42L43_ASRC_INT2_INPUT1: + case CS42L43_ASRC_INT3_INPUT1: + case CS42L43_ASRC_INT4_INPUT1: + case CS42L43_ASRC_DEC1_INPUT1: + case CS42L43_ASRC_DEC2_INPUT1: + case CS42L43_ASRC_DEC3_INPUT1: + case CS42L43_ASRC_DEC4_INPUT1: + case CS42L43_ISRC1INT1_INPUT1: + case CS42L43_ISRC1INT2_INPUT1: + case CS42L43_ISRC1DEC1_INPUT1: + case CS42L43_ISRC1DEC2_INPUT1: + case CS42L43_ISRC2INT1_INPUT1: + case CS42L43_ISRC2INT2_INPUT1: + case CS42L43_ISRC2DEC1_INPUT1: + case CS42L43_ISRC2DEC2_INPUT1: + case CS42L43_EQ1MIX_INPUT1 ... CS42L43_EQ1MIX_INPUT4: + case CS42L43_EQ2MIX_INPUT1 ... CS42L43_EQ2MIX_INPUT4: + case CS42L43_SPDIF1_INPUT1: + case CS42L43_SPDIF2_INPUT1: + case CS42L43_AMP1MIX_INPUT1 ... CS42L43_AMP1MIX_INPUT4: + case CS42L43_AMP2MIX_INPUT1 ... CS42L43_AMP2MIX_INPUT4: + case CS42L43_AMP3MIX_INPUT1 ... CS42L43_AMP3MIX_INPUT4: + case CS42L43_AMP4MIX_INPUT1 ... CS42L43_AMP4MIX_INPUT4: + case CS42L43_ASRC_INT_ENABLES ... CS42L43_ASRC_DEC_ENABLES: + case CS42L43_PDNCNTL: + case CS42L43_RINGSENSE_DEB_CTRL: + case CS42L43_TIPSENSE_DEB_CTRL: + case CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS: + case CS42L43_HS2: + case CS42L43_HS_STAT: + case CS42L43_MCU_SW_INTERRUPT: + case CS42L43_STEREO_MIC_CTRL: + case CS42L43_STEREO_MIC_CLAMP_CTRL: + case CS42L43_BLOCK_EN2 ... CS42L43_BLOCK_EN11: + case CS42L43_TONE_CH1_CTRL ... CS42L43_TONE_CH2_CTRL: + case CS42L43_MIC_DETECT_CONTROL_1: + case CS42L43_DETECT_STATUS_1: + case CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL: + case CS42L43_MIC_DETECT_CONTROL_ANDROID: + case CS42L43_ISRC1_CTRL: + case CS42L43_ISRC2_CTRL: + case CS42L43_CTRL_REG: + case CS42L43_FDIV_FRAC: + case CS42L43_CAL_RATIO: + case CS42L43_SPI_CLK_CONFIG1: + case CS42L43_SPI_CONFIG1 ... CS42L43_SPI_CONFIG4: + case CS42L43_SPI_STATUS1 ... CS42L43_SPI_STATUS2: + case CS42L43_TRAN_CONFIG1 ... CS42L43_TRAN_CONFIG8: + case CS42L43_TRAN_STATUS1 ... CS42L43_TRAN_STATUS3: + case CS42L43_TX_DATA: + case CS42L43_RX_DATA: + case CS42L43_DACCNFG1 ... CS42L43_DACCNFG2: + case CS42L43_HPPATHVOL: + case CS42L43_PGAVOL: + case CS42L43_LOADDETRESULTS: + case CS42L43_LOADDETENA: + case CS42L43_CTRL: + case CS42L43_COEFF_DATA_IN0: + case CS42L43_COEFF_RD_WR0: + case CS42L43_INIT_DONE0: + case CS42L43_START_EQZ0: + case CS42L43_MUTE_EQ_IN0: + case CS42L43_DECIM_INT ... CS42L43_HPOUT_INT: + case CS42L43_DECIM_MASK ... CS42L43_HPOUT_MASK: + case CS42L43_DECIM_INT_SHADOW ... CS42L43_HP_OUT_SHADOW: + case CS42L43_BOOT_CONTROL: + case CS42L43_BLOCK_EN: + case CS42L43_SHUTTER_CONTROL: + case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs42l43_readable_register, MFD_CS42L43); + +bool cs42l43_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L43_SFT_RESET: + case CS42L43_TX_DATA: + case CS42L43_RX_DATA: + case CS42L43_DECIM_INT ... CS42L43_HPOUT_INT: + case CS42L43_MCU_SW_REV ... CS42L43_MCU_RAM_MAX: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs42l43_precious_register, MFD_CS42L43); + +bool cs42l43_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L43_DEVID: + case CS42L43_REVID: + case CS42L43_RELID: + case CS42L43_GPIO_STS: + case CS42L43_OTP_REVISION_ID: + case CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS: + case CS42L43_HS_STAT: + case CS42L43_MCU_SW_INTERRUPT: + case CS42L43_DETECT_STATUS_1: + case CS42L43_SPI_STATUS1 ... CS42L43_SPI_STATUS2: + case CS42L43_TRAN_CONFIG1 ... CS42L43_TRAN_CONFIG2: + case CS42L43_TRAN_CONFIG8: + case CS42L43_TRAN_STATUS1 ... CS42L43_TRAN_STATUS3: + case CS42L43_LOADDETRESULTS: + case CS42L43_INIT_DONE0: + case CS42L43_DECIM_INT_SHADOW ... CS42L43_HP_OUT_SHADOW: + case CS42L43_BOOT_CONTROL: + case CS42L43_BLOCK_EN: + return true; + default: + return cs42l43_precious_register(dev, reg); + } +} +EXPORT_SYMBOL_NS_GPL(cs42l43_volatile_register, MFD_CS42L43); + +#define CS42L43_IRQ_OFFSET(reg) ((CS42L43_##reg##_INT) - CS42L43_DECIM_INT) + +#define CS42L43_IRQ_REG(name, reg) REGMAP_IRQ_REG(CS42L43_##name, \ + CS42L43_IRQ_OFFSET(reg), \ + CS42L43_##name##_INT_MASK) + +static const struct regmap_irq cs42l43_regmap_irqs[] = { + CS42L43_IRQ_REG(PLL_LOST_LOCK, PLL), + CS42L43_IRQ_REG(PLL_READY, PLL), + + CS42L43_IRQ_REG(HP_STARTUP_DONE, MSM), + CS42L43_IRQ_REG(HP_SHUTDOWN_DONE, MSM), + CS42L43_IRQ_REG(HSDET_DONE, MSM), + CS42L43_IRQ_REG(TIPSENSE_UNPLUG_DB, MSM), + CS42L43_IRQ_REG(TIPSENSE_PLUG_DB, MSM), + CS42L43_IRQ_REG(RINGSENSE_UNPLUG_DB, MSM), + CS42L43_IRQ_REG(RINGSENSE_PLUG_DB, MSM), + CS42L43_IRQ_REG(TIPSENSE_UNPLUG_PDET, MSM), + CS42L43_IRQ_REG(TIPSENSE_PLUG_PDET, MSM), + CS42L43_IRQ_REG(RINGSENSE_UNPLUG_PDET, MSM), + CS42L43_IRQ_REG(RINGSENSE_PLUG_PDET, MSM), + + CS42L43_IRQ_REG(HS2_BIAS_SENSE, ACC_DET), + CS42L43_IRQ_REG(HS1_BIAS_SENSE, ACC_DET), + CS42L43_IRQ_REG(DC_DETECT1_FALSE, ACC_DET), + CS42L43_IRQ_REG(DC_DETECT1_TRUE, ACC_DET), + CS42L43_IRQ_REG(HSBIAS_CLAMPED, ACC_DET), + CS42L43_IRQ_REG(HS3_4_BIAS_SENSE, ACC_DET), + + CS42L43_IRQ_REG(AMP2_CLK_STOP_FAULT, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_CLK_STOP_FAULT, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_VDDSPK_FAULT, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_VDDSPK_FAULT, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_SHUTDOWN_DONE, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_SHUTDOWN_DONE, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_STARTUP_DONE, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_STARTUP_DONE, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_THERM_SHDN, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_THERM_SHDN, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_THERM_WARN, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_THERM_WARN, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP2_SCDET, CLASS_D_AMP), + CS42L43_IRQ_REG(AMP1_SCDET, CLASS_D_AMP), + + CS42L43_IRQ_REG(GPIO3_FALL, GPIO), + CS42L43_IRQ_REG(GPIO3_RISE, GPIO), + CS42L43_IRQ_REG(GPIO2_FALL, GPIO), + CS42L43_IRQ_REG(GPIO2_RISE, GPIO), + CS42L43_IRQ_REG(GPIO1_FALL, GPIO), + CS42L43_IRQ_REG(GPIO1_RISE, GPIO), + + CS42L43_IRQ_REG(HP_ILIMIT, HPOUT), + CS42L43_IRQ_REG(HP_LOADDET_DONE, HPOUT), +}; + +static const struct regmap_irq_chip cs42l43_irq_chip = { + .name = "cs42l43", + + .status_base = CS42L43_DECIM_INT, + .mask_base = CS42L43_DECIM_MASK, + .num_regs = 16, + + .irqs = cs42l43_regmap_irqs, + .num_irqs = ARRAY_SIZE(cs42l43_regmap_irqs), + + .runtime_pm = true, +}; + +static const char * const cs42l43_core_supplies[] = { + "vdd-a", "vdd-io", "vdd-cp", +}; + +static const char * const cs42l43_parent_supplies[] = { "vdd-amp" }; + +static const struct mfd_cell cs42l43_devs[] = { + { .name = "cs42l43-pinctrl", }, + { .name = "cs42l43-spi", }, + { + .name = "cs42l43-codec", + .parent_supplies = cs42l43_parent_supplies, + .num_parent_supplies = ARRAY_SIZE(cs42l43_parent_supplies), + }, +}; + +/* + * If the device is connected over Soundwire, as well as soft resetting the + * device, this function will also way for the device to detach from the bus + * before returning. + */ +static int cs42l43_soft_reset(struct cs42l43 *cs42l43) +{ + static const struct reg_sequence reset[] = { + { CS42L43_SFT_RESET, CS42L43_SFT_RESET_VAL }, + }; + + reinit_completion(&cs42l43->device_detach); + + /* + * Apply cache only because the soft reset will cause the device to + * detach from the soundwire bus. + */ + regcache_cache_only(cs42l43->regmap, true); + regmap_multi_reg_write_bypassed(cs42l43->regmap, reset, ARRAY_SIZE(reset)); + + msleep(CS42L43_RESET_DELAY); + + if (cs42l43->sdw) { + unsigned long timeout = msecs_to_jiffies(CS42L43_SDW_DETACH_TIMEOUT); + unsigned long time; + + time = wait_for_completion_timeout(&cs42l43->device_detach, timeout); + if (!time) { + dev_err(cs42l43->dev, "Timed out waiting for device detach\n"); + return -ETIMEDOUT; + } + } + + return -EAGAIN; +} + +/* + * This function is essentially a no-op on I2C, but will wait for the device to + * attach when the device is used on a SoundWire bus. + */ +static int cs42l43_wait_for_attach(struct cs42l43 *cs42l43) +{ + if (!cs42l43->attached) { + unsigned long timeout = msecs_to_jiffies(CS42L43_SDW_ATTACH_TIMEOUT); + unsigned long time; + + time = wait_for_completion_timeout(&cs42l43->device_attach, timeout); + if (!time) { + dev_err(cs42l43->dev, "Timed out waiting for device re-attach\n"); + return -ETIMEDOUT; + } + } + + regcache_cache_only(cs42l43->regmap, false); + + /* The hardware requires enabling OSC_DIV before doing any SoundWire reads. */ + if (cs42l43->sdw) + regmap_write(cs42l43->regmap, CS42L43_OSC_DIV_SEL, + CS42L43_OSC_DIV2_EN_MASK); + + return 0; +} + +/* + * This function will advance the firmware into boot stage 3 from boot stage 2. + * Boot stage 3 is required to send commands to the firmware. This is achieved + * by setting the firmware NEED configuration register to zero, this indicates + * no configuration is required forcing the firmware to advance to boot stage 3. + * + * Later revisions of the firmware require the use of an alternative register + * for this purpose, which is indicated through the shadow flag. + */ +static int cs42l43_mcu_stage_2_3(struct cs42l43 *cs42l43, bool shadow) +{ + unsigned int need_reg = CS42L43_NEED_CONFIGS; + unsigned int val; + int ret; + + if (shadow) + need_reg = CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS; + + regmap_write(cs42l43->regmap, need_reg, 0); + + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_BOOT_STATUS, + val, (val == CS42L43_MCU_BOOT_STAGE3), + CS42L43_MCU_POLL, CS42L43_MCU_CMD_TIMEOUT); + if (ret) { + dev_err(cs42l43->dev, "Failed to move to stage 3: %d, 0x%x\n", ret, val); + return ret; + } + + return -EAGAIN; +} + +/* + * This function will return the firmware to boot stage 2 from boot stage 3. + * Boot stage 2 is required to apply updates to the firmware. This is achieved + * by setting the firmware NEED configuration register to FW_PATCH_NEED_CFG, + * setting the HAVE configuration register to 0, and soft resetting. The + * firmware will see it is missing a patch configuration and will pause in boot + * stage 2. + * + * Note: Unlike cs42l43_mcu_stage_2_3 there is no need to consider the shadow + * register here as the driver will only return to boot stage 2 if the firmware + * requires update which means the revision does not include shadow register + * support. + */ +static int cs42l43_mcu_stage_3_2(struct cs42l43 *cs42l43) +{ + regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_NEED_CONFIGS, + CS42L43_FW_PATCH_NEED_CFG_MASK); + regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_HAVE_CONFIGS, 0); + + return cs42l43_soft_reset(cs42l43); +} + +/* + * Disable the firmware running on the device such that the driver can access + * the registers without fear of the MCU changing them under it. + */ +static int cs42l43_mcu_disable(struct cs42l43 *cs42l43) +{ + unsigned int val; + int ret; + + regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG, + CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL); + regmap_write(cs42l43->regmap, CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION, + CS42L43_FW_MM_CTRL_MCU_SEL_MASK); + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_CONTROL_IND_MASK); + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0); + + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_SOFT_INT_SHADOW, val, + (val & CS42L43_CONTROL_APPLIED_INT_MASK), + CS42L43_MCU_POLL, CS42L43_MCU_CMD_TIMEOUT); + if (ret) { + dev_err(cs42l43->dev, "Failed to disable firmware: %d, 0x%x\n", ret, val); + return ret; + } + + /* Soft reset to clear any register state the firmware left behind. */ + return cs42l43_soft_reset(cs42l43); +} + +/* + * Callback to load firmware updates. + */ +static void cs42l43_mcu_load_firmware(const struct firmware *firmware, void *context) +{ + struct cs42l43 *cs42l43 = context; + const struct cs42l43_patch_header *hdr; + unsigned int loadaddr, val; + int ret; + + if (!firmware) { + dev_err(cs42l43->dev, "Failed to load firmware\n"); + cs42l43->firmware_error = -ENODEV; + goto err; + } + + hdr = (const struct cs42l43_patch_header *)&firmware->data[0]; + loadaddr = le32_to_cpu(hdr->load_addr); + + if (le16_to_cpu(hdr->version) != CS42L43_MCU_UPDATE_FORMAT) { + dev_err(cs42l43->dev, "Bad firmware file format: %d\n", hdr->version); + cs42l43->firmware_error = -EINVAL; + goto err_release; + } + + regmap_write(cs42l43->regmap, CS42L43_PATCH_START_ADDR, loadaddr); + regmap_bulk_write(cs42l43->regmap, loadaddr + CS42L43_MCU_UPDATE_OFFSET, + &firmware->data[0], firmware->size / sizeof(u32)); + + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, CS42L43_PATCH_IND_MASK); + regmap_write(cs42l43->regmap, CS42L43_MCU_SW_INTERRUPT, 0); + + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_SOFT_INT_SHADOW, val, + (val & CS42L43_PATCH_APPLIED_INT_MASK), + CS42L43_MCU_POLL, CS42L43_MCU_UPDATE_TIMEOUT); + if (ret) { + dev_err(cs42l43->dev, "Failed to update firmware: %d, 0x%x\n", ret, val); + cs42l43->firmware_error = ret; + goto err_release; + } + +err_release: + release_firmware(firmware); +err: + complete(&cs42l43->firmware_download); +} + +/* + * The process of updating the firmware is split into a series of steps, at the + * end of each step a soft reset of the device might be required which will + * require the driver to wait for the device to re-attach on the SoundWire bus, + * if that control bus is being used. + */ +static int cs42l43_mcu_update_step(struct cs42l43 *cs42l43) +{ + unsigned int mcu_rev, bios_rev, boot_status, secure_cfg; + bool patched, shadow; + int ret; + + /* Clear any stale software interrupt bits. */ + regmap_read(cs42l43->regmap, CS42L43_SOFT_INT, &mcu_rev); + + ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_STATUS, &boot_status); + if (ret) { + dev_err(cs42l43->dev, "Failed to read boot status: %d\n", ret); + return ret; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_MCU_SW_REV, &mcu_rev); + if (ret) { + dev_err(cs42l43->dev, "Failed to read firmware revision: %d\n", ret); + return ret; + } + + bios_rev = (((mcu_rev & CS42L43_BIOS_MAJOR_REV_MASK) << 12) | + ((mcu_rev & CS42L43_BIOS_MINOR_REV_MASK) << 4) | + ((mcu_rev & CS42L43_BIOS_SUBMINOR_REV_MASK) >> 8)) >> + CS42L43_BIOS_MAJOR_REV_SHIFT; + mcu_rev = ((mcu_rev & CS42L43_FW_MAJOR_REV_MASK) << 12) | + ((mcu_rev & CS42L43_FW_MINOR_REV_MASK) << 4) | + ((mcu_rev & CS42L43_FW_SUBMINOR_REV_MASK) >> 8); + + /* + * The firmware has two revision numbers bringing either of them up to a + * supported version will provide the features the driver requires. + */ + patched = mcu_rev >= CS42L43_MCU_SUPPORTED_REV || + bios_rev >= CS42L43_MCU_SUPPORTED_BIOS_REV; + /* + * Later versions of the firmwware require the driver to access some + * features through a set of shadow registers. + */ + shadow = mcu_rev >= CS42L43_MCU_SHADOW_REGS_REQUIRED_REV; + + ret = regmap_read(cs42l43->regmap, CS42L43_BOOT_CONTROL, &secure_cfg); + if (ret) { + dev_err(cs42l43->dev, "Failed to read security settings: %d\n", ret); + return ret; + } + + cs42l43->hw_lock = secure_cfg & CS42L43_LOCK_HW_STS_MASK; + + if (!patched && cs42l43->hw_lock) { + dev_err(cs42l43->dev, "Unpatched secure device\n"); + return -EPERM; + } + + dev_dbg(cs42l43->dev, "Firmware(0x%x, 0x%x) in boot stage %d\n", + mcu_rev, bios_rev, boot_status); + + switch (boot_status) { + case CS42L43_MCU_BOOT_STAGE2: + if (!patched) { + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + "cs42l43.bin", cs42l43->dev, + GFP_KERNEL, cs42l43, + cs42l43_mcu_load_firmware); + if (ret) { + dev_err(cs42l43->dev, "Failed to request firmware: %d\n", ret); + return ret; + } + + wait_for_completion(&cs42l43->firmware_download); + + if (cs42l43->firmware_error) + return cs42l43->firmware_error; + + return -EAGAIN; + } else { + return cs42l43_mcu_stage_2_3(cs42l43, shadow); + } + case CS42L43_MCU_BOOT_STAGE3: + if (patched) + return cs42l43_mcu_disable(cs42l43); + else + return cs42l43_mcu_stage_3_2(cs42l43); + case CS42L43_MCU_BOOT_STAGE4: + return 0; + default: + dev_err(cs42l43->dev, "Invalid boot status: %d\n", boot_status); + return -EINVAL; + } +} + +/* + * Update the firmware running on the device. + */ +static int cs42l43_mcu_update(struct cs42l43 *cs42l43) +{ + int i, ret; + + for (i = 0; i < CS42L43_MCU_UPDATE_RETRIES; i++) { + ret = cs42l43_mcu_update_step(cs42l43); + if (ret != -EAGAIN) + return ret; + + ret = cs42l43_wait_for_attach(cs42l43); + if (ret) + return ret; + } + + dev_err(cs42l43->dev, "Failed retrying update\n"); + return -ETIMEDOUT; +} + +static int cs42l43_irq_config(struct cs42l43 *cs42l43) +{ + struct irq_data *irq_data; + unsigned long irq_flags; + int ret; + + if (cs42l43->sdw) + cs42l43->irq = cs42l43->sdw->irq; + + cs42l43->irq_chip = cs42l43_irq_chip; + cs42l43->irq_chip.irq_drv_data = cs42l43; + + irq_data = irq_get_irq_data(cs42l43->irq); + if (!irq_data) { + dev_err(cs42l43->dev, "Invalid IRQ: %d\n", cs42l43->irq); + return -EINVAL; + } + + irq_flags = irqd_get_trigger_type(irq_data); + switch (irq_flags) { + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_FALLING: + break; + case IRQ_TYPE_NONE: + default: + irq_flags = IRQF_TRIGGER_LOW; + break; + } + + irq_flags |= IRQF_ONESHOT; + + ret = devm_regmap_add_irq_chip(cs42l43->dev, cs42l43->regmap, + cs42l43->irq, irq_flags, 0, + &cs42l43->irq_chip, &cs42l43->irq_data); + if (ret) { + dev_err(cs42l43->dev, "Failed to add IRQ chip: %d\n", ret); + return ret; + } + + dev_dbg(cs42l43->dev, "Configured IRQ %d with flags 0x%lx\n", + cs42l43->irq, irq_flags); + + return 0; +} + +static void cs42l43_boot_work(struct work_struct *work) +{ + struct cs42l43 *cs42l43 = container_of(work, struct cs42l43, boot_work); + unsigned int devid, revid, otp; + int ret; + + ret = cs42l43_wait_for_attach(cs42l43); + if (ret) + goto err; + + ret = regmap_read(cs42l43->regmap, CS42L43_DEVID, &devid); + if (ret) { + dev_err(cs42l43->dev, "Failed to read devid: %d\n", ret); + goto err; + } + + switch (devid) { + case CS42L43_DEVID_VAL: + break; + default: + dev_err(cs42l43->dev, "Unrecognised devid: 0x%06x\n", devid); + goto err; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_REVID, &revid); + if (ret) { + dev_err(cs42l43->dev, "Failed to read rev: %d\n", ret); + goto err; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_OTP_REVISION_ID, &otp); + if (ret) { + dev_err(cs42l43->dev, "Failed to read otp rev: %d\n", ret); + goto err; + } + + dev_info(cs42l43->dev, + "devid: 0x%06x, rev: 0x%02x, otp: 0x%02x\n", devid, revid, otp); + + ret = cs42l43_mcu_update(cs42l43); + if (ret) + goto err; + + ret = regmap_register_patch(cs42l43->regmap, cs42l43_reva_patch, + ARRAY_SIZE(cs42l43_reva_patch)); + if (ret) { + dev_err(cs42l43->dev, "Failed to apply register patch: %d\n", ret); + goto err; + } + + ret = cs42l43_irq_config(cs42l43); + if (ret) + goto err; + + ret = devm_mfd_add_devices(cs42l43->dev, PLATFORM_DEVID_NONE, + cs42l43_devs, ARRAY_SIZE(cs42l43_devs), + NULL, 0, NULL); + if (ret) { + dev_err(cs42l43->dev, "Failed to add subdevices: %d\n", ret); + goto err; + } + + pm_runtime_mark_last_busy(cs42l43->dev); + pm_runtime_put_autosuspend(cs42l43->dev); + + return; + +err: + pm_runtime_put_sync(cs42l43->dev); + cs42l43_dev_remove(cs42l43); +} + +static int cs42l43_power_up(struct cs42l43 *cs42l43) +{ + int ret; + + ret = regulator_enable(cs42l43->vdd_p); + if (ret) { + dev_err(cs42l43->dev, "Failed to enable vdd-p: %d\n", ret); + return ret; + } + + /* vdd-p must be on for 50uS before any other supply */ + usleep_range(CS42L43_VDDP_DELAY, 2 * CS42L43_VDDP_DELAY); + + gpiod_set_value_cansleep(cs42l43->reset, 1); + + ret = regulator_bulk_enable(CS42L43_N_SUPPLIES, cs42l43->core_supplies); + if (ret) { + dev_err(cs42l43->dev, "Failed to enable core supplies: %d\n", ret); + goto err_reset; + } + + ret = regulator_enable(cs42l43->vdd_d); + if (ret) { + dev_err(cs42l43->dev, "Failed to enable vdd-d: %d\n", ret); + goto err_core_supplies; + } + + usleep_range(CS42L43_VDDD_DELAY, 2 * CS42L43_VDDD_DELAY); + + return 0; + +err_core_supplies: + regulator_bulk_disable(CS42L43_N_SUPPLIES, cs42l43->core_supplies); +err_reset: + gpiod_set_value_cansleep(cs42l43->reset, 0); + regulator_disable(cs42l43->vdd_p); + + return ret; +} + +static int cs42l43_power_down(struct cs42l43 *cs42l43) +{ + int ret; + + ret = regulator_disable(cs42l43->vdd_d); + if (ret) { + dev_err(cs42l43->dev, "Failed to disable vdd-d: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(CS42L43_N_SUPPLIES, cs42l43->core_supplies); + if (ret) { + dev_err(cs42l43->dev, "Failed to disable core supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(cs42l43->reset, 0); + + ret = regulator_disable(cs42l43->vdd_p); + if (ret) { + dev_err(cs42l43->dev, "Failed to disable vdd-p: %d\n", ret); + return ret; + } + + return 0; +} + +int cs42l43_dev_probe(struct cs42l43 *cs42l43) +{ + int i, ret; + + dev_set_drvdata(cs42l43->dev, cs42l43); + + mutex_init(&cs42l43->pll_lock); + init_completion(&cs42l43->device_attach); + init_completion(&cs42l43->device_detach); + init_completion(&cs42l43->firmware_download); + INIT_WORK(&cs42l43->boot_work, cs42l43_boot_work); + + regcache_cache_only(cs42l43->regmap, true); + + cs42l43->reset = devm_gpiod_get_optional(cs42l43->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs42l43->reset)) + return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->reset), + "Failed to get reset\n"); + + cs42l43->vdd_p = devm_regulator_get(cs42l43->dev, "vdd-p"); + if (IS_ERR(cs42l43->vdd_p)) + return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->vdd_p), + "Failed to get vdd-p\n"); + + cs42l43->vdd_d = devm_regulator_get(cs42l43->dev, "vdd-d"); + if (IS_ERR(cs42l43->vdd_d)) + return dev_err_probe(cs42l43->dev, PTR_ERR(cs42l43->vdd_d), + "Failed to get vdd-d\n"); + + BUILD_BUG_ON(ARRAY_SIZE(cs42l43_core_supplies) != CS42L43_N_SUPPLIES); + + for (i = 0; i < CS42L43_N_SUPPLIES; i++) + cs42l43->core_supplies[i].supply = cs42l43_core_supplies[i]; + + ret = devm_regulator_bulk_get(cs42l43->dev, CS42L43_N_SUPPLIES, + cs42l43->core_supplies); + if (ret) + return dev_err_probe(cs42l43->dev, ret, + "Failed to get core supplies\n"); + + ret = cs42l43_power_up(cs42l43); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(cs42l43->dev, CS42L43_AUTOSUSPEND_TIME); + pm_runtime_use_autosuspend(cs42l43->dev); + pm_runtime_set_active(cs42l43->dev); + /* + * The device is already powered up, but keep it from suspending until + * the boot work runs. + */ + pm_runtime_get_noresume(cs42l43->dev); + devm_pm_runtime_enable(cs42l43->dev); + + queue_work(system_long_wq, &cs42l43->boot_work); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_dev_probe, MFD_CS42L43); + +void cs42l43_dev_remove(struct cs42l43 *cs42l43) +{ + cs42l43_power_down(cs42l43); +} +EXPORT_SYMBOL_NS_GPL(cs42l43_dev_remove, MFD_CS42L43); + +static int cs42l43_suspend(struct device *dev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + int ret; + + /* + * Don't care about being resumed here, but the driver does want + * force_resume to always trigger an actual resume, so that register + * state for the MCU/GPIOs is returned as soon as possible after system + * resume. force_resume will resume if the reference count is resumed on + * suspend hence the get_noresume. + */ + pm_runtime_get_noresume(dev); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + dev_err(cs42l43->dev, "Failed to force suspend: %d\n", ret); + pm_runtime_put_noidle(dev); + return ret; + } + + pm_runtime_put_noidle(dev); + + ret = cs42l43_power_down(cs42l43); + if (ret) + return ret; + + return 0; +} + +static int cs42l43_resume(struct device *dev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + int ret; + + ret = cs42l43_power_up(cs42l43); + if (ret) + return ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(cs42l43->dev, "Failed to force resume: %d\n", ret); + return ret; + } + + return 0; +} + +static int cs42l43_runtime_suspend(struct device *dev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + + /* + * Whilst the driver doesn't power the chip down here, going into runtime + * suspend lets the SoundWire bus power down, which means the driver + * can't communicate with the device any more. + */ + regcache_cache_only(cs42l43->regmap, true); + + return 0; +} + +static int cs42l43_runtime_resume(struct device *dev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(dev); + unsigned int reset_canary; + int ret; + + ret = cs42l43_wait_for_attach(cs42l43); + if (ret) + return ret; + + ret = regmap_read(cs42l43->regmap, CS42L43_RELID, &reset_canary); + if (ret) { + dev_err(cs42l43->dev, "Failed to check reset canary: %d\n", ret); + goto err; + } + + if (!reset_canary) { + /* + * If the canary has cleared the chip has reset, re-handle the + * MCU and mark the cache as dirty to indicate the chip reset. + */ + ret = cs42l43_mcu_update(cs42l43); + if (ret) + goto err; + + regcache_mark_dirty(cs42l43->regmap); + } + + ret = regcache_sync(cs42l43->regmap); + if (ret) { + dev_err(cs42l43->dev, "Failed to restore register cache: %d\n", ret); + goto err; + } + + return 0; + +err: + regcache_cache_only(cs42l43->regmap, true); + + return ret; +} + +EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume) + SET_RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL) +}; + +MODULE_DESCRIPTION("CS42L43 Core Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("cs42l43.bin"); diff --git a/drivers/mfd/cs42l43.h b/drivers/mfd/cs42l43.h new file mode 100644 index 000000000000..eb4caf393833 --- /dev/null +++ b/drivers/mfd/cs42l43.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 core driver internal data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/mfd/cs42l43.h> +#include <linux/pm.h> +#include <linux/regmap.h> + +#ifndef CS42L43_CORE_INT_H +#define CS42L43_CORE_INT_H + +#define CS42L43_N_DEFAULTS 176 + +extern const struct dev_pm_ops cs42l43_pm_ops; +extern const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS]; + +bool cs42l43_readable_register(struct device *dev, unsigned int reg); +bool cs42l43_precious_register(struct device *dev, unsigned int reg); +bool cs42l43_volatile_register(struct device *dev, unsigned int reg); + +int cs42l43_dev_probe(struct cs42l43 *cs42l43); +void cs42l43_dev_remove(struct cs42l43 *cs42l43); + +#endif /* CS42L43_CORE_INT_H */ diff --git a/drivers/misc/cardreader/rts5227.c b/drivers/misc/cardreader/rts5227.c index d676cf63a966..3dae5e3a1697 100644 --- a/drivers/misc/cardreader/rts5227.c +++ b/drivers/misc/cardreader/rts5227.c @@ -195,7 +195,7 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) } } - if (option->force_clkreq_0) + if (option->force_clkreq_0 && pcr->aspm_mode == ASPM_MODE_CFG) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); else diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index cfebad51d1d8..f4ab09439da7 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -435,17 +435,10 @@ static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) option->ltr_enabled = false; } } - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; } static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) { - struct rtsx_cr_option *option = &pcr->option; rtsx_pci_write_register(pcr, RTS5228_AUTOLOAD_CFG1, CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); @@ -476,17 +469,6 @@ static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); - /* - * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced - * to drive low, and we forcibly request clock. - */ - if (option->force_clkreq_0) - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); - else - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); - rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); if (pcr->rtd3_en) { diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 91d240dd68fa..47ab72a43256 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -327,12 +327,11 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) } } - /* * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced * to drive low, and we forcibly request clock. */ - if (option->force_clkreq_0) + if (option->force_clkreq_0 && pcr->aspm_mode == ASPM_MODE_CFG) rtsx_pci_write_register(pcr, PETXCFG, FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); else diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 9b42b20a3e5a..79b18f6f73a8 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -517,17 +517,10 @@ static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) option->ltr_enabled = false; } } - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; } static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) { - struct rtsx_cr_option *option = &pcr->option; /* Set mcu_cnt to 7 to ensure data can be sampled properly */ rtsx_pci_write_register(pcr, 0xFC03, 0x7F, 0x07); @@ -546,17 +539,6 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) rts5260_init_hw(pcr); - /* - * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced - * to drive low, and we forcibly request clock. - */ - if (option->force_clkreq_0) - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); - else - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); - rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); return 0; diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index b1e76030cafd..94af6bf8a25a 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -498,17 +498,10 @@ static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) option->ltr_enabled = false; } } - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; } static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) { - struct rtsx_cr_option *option = &pcr->option; u32 val; rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1, @@ -554,17 +547,6 @@ static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); - /* - * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced - * to drive low, and we forcibly request clock. - */ - if (option->force_clkreq_0) - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); - else - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); - rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); if (pcr->rtd3_en) { diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 32b7783e9d4f..a3f4b52bb159 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1326,8 +1326,11 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) return err; } - if (pcr->aspm_mode == ASPM_MODE_REG) + if (pcr->aspm_mode == ASPM_MODE_REG) { rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0x30, 0x30); + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + } /* No CD interrupt if probing driver with card inserted. * So we need to initialize pcr->card_exist here. diff --git a/drivers/misc/tps6594-esm.c b/drivers/misc/tps6594-esm.c index b488f704f104..05e2c151e632 100644 --- a/drivers/misc/tps6594-esm.c +++ b/drivers/misc/tps6594-esm.c @@ -13,6 +13,8 @@ #include <linux/mfd/tps6594.h> +#define TPS6594_DEV_REV_1 0x08 + static irqreturn_t tps6594_esm_isr(int irq, void *dev_id) { struct platform_device *pdev = dev_id; @@ -32,11 +34,26 @@ static int tps6594_esm_probe(struct platform_device *pdev) { struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; + unsigned int rev; int irq; int ret; int i; - for (i = 0 ; i < pdev->num_resources ; i++) { + /* + * Due to a bug in revision 1 of the PMIC, the GPIO3 used for the + * SoC ESM function is used to power the load switch instead. + * As a consequence, ESM can not be used on those PMIC. + * Check the version and return an error in case of revision 1. + */ + ret = regmap_read(tps->regmap, TPS6594_REG_DEV_REV, &rev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read PMIC revision\n"); + if (rev == TPS6594_DEV_REV_1) + return dev_err_probe(dev, -ENODEV, + "ESM not supported for revision 1 PMIC\n"); + + for (i = 0; i < pdev->num_resources; i++) { irq = platform_get_irq_byname(pdev, pdev->resource[i].name); if (irq < 0) return dev_err_probe(dev, irq, "Failed to get %s irq\n", diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 2d002c81dcf3..d0d6ffcf78d4 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -338,13 +338,7 @@ static void moxart_transfer_pio(struct moxart_host *host) return; } for (len = 0; len < remain && len < host->fifo_width;) { - /* SCR data must be read in big endian. */ - if (data->mrq->cmd->opcode == SD_APP_SEND_SCR) - *sgp = ioread32be(host->base + - REG_DATA_WINDOW); - else - *sgp = ioread32(host->base + - REG_DATA_WINDOW); + *sgp = ioread32(host->base + REG_DATA_WINDOW); sgp++; len += 4; } diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index a202a69a4b08..b01ffb4d0973 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -29,9 +29,16 @@ struct f_sdhost_priv { bool enable_cmd_dat_delay; }; +static void *sdhci_f_sdhost_priv(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return sdhci_pltfm_priv(pltfm_host); +} + static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) { - struct f_sdhost_priv *priv = sdhci_priv(host); + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); u32 ctrl = 0; usleep_range(2500, 3000); @@ -64,7 +71,7 @@ static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host) static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) { - struct f_sdhost_priv *priv = sdhci_priv(host); + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); u32 ctl; if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) @@ -95,30 +102,32 @@ static const struct sdhci_ops sdhci_f_sdh30_ops = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; +static const struct sdhci_pltfm_data sdhci_f_sdh30_pltfm_data = { + .ops = &sdhci_f_sdh30_ops, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC + | SDHCI_QUIRK_INVERTED_WRITE_PROTECT, + .quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE + | SDHCI_QUIRK2_TUNING_WORK_AROUND, +}; + static int sdhci_f_sdh30_probe(struct platform_device *pdev) { struct sdhci_host *host; struct device *dev = &pdev->dev; - int irq, ctrl = 0, ret = 0; + int ctrl = 0, ret = 0; struct f_sdhost_priv *priv; + struct sdhci_pltfm_host *pltfm_host; u32 reg = 0; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv)); + host = sdhci_pltfm_init(pdev, &sdhci_f_sdh30_pltfm_data, + sizeof(struct f_sdhost_priv)); if (IS_ERR(host)) return PTR_ERR(host); - priv = sdhci_priv(host); + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); priv->dev = dev; - host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT; - host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE | - SDHCI_QUIRK2_TUNING_WORK_AROUND; - priv->enable_cmd_dat_delay = device_property_read_bool(dev, "fujitsu,cmd-dat-delay-select"); @@ -126,18 +135,6 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) if (ret) goto err; - platform_set_drvdata(pdev, host); - - host->hw_name = "f_sdh30"; - host->ops = &sdhci_f_sdh30_ops; - host->irq = irq; - - host->ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->ioaddr)) { - ret = PTR_ERR(host->ioaddr); - goto err; - } - if (dev_of_node(dev)) { sdhci_get_of_property(pdev); @@ -204,24 +201,21 @@ err_rst: err_clk: clk_disable_unprepare(priv->clk_iface); err: - sdhci_free_host(host); + sdhci_pltfm_free(pdev); + return ret; } static int sdhci_f_sdh30_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); - struct f_sdhost_priv *priv = sdhci_priv(host); - - sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == - 0xffffffff); + struct f_sdhost_priv *priv = sdhci_f_sdhost_priv(host); reset_control_assert(priv->rst); clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk_iface); - sdhci_free_host(host); - platform_set_drvdata(pdev, NULL); + sdhci_pltfm_unregister(pdev); return 0; } diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index 086426139173..7366e85c09fd 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -135,7 +135,7 @@ static int fun_exec_op(struct nand_chip *chip, const struct nand_operation *op, unsigned int i; int ret; - if (op->cs > NAND_MAX_CHIPS) + if (op->cs >= NAND_MAX_CHIPS) return -EINVAL; if (check_only) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index d3faf8086631..b10011dec1e6 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1278,7 +1278,6 @@ static int meson_nand_attach_chip(struct nand_chip *nand) struct meson_nfc *nfc = nand_get_controller_data(nand); struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); - int nsectors = mtd->writesize / 1024; int raw_writesize; int ret; @@ -1304,7 +1303,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand) nand->options |= NAND_NO_SUBPAGE_WRITE; ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps, - mtd->oobsize - 2 * nsectors); + mtd->oobsize - 2); if (ret) { dev_err(nfc->dev, "failed to ECC init\n"); return -EINVAL; diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index 6e1eac6644a6..4a97d4a76454 100644 --- a/drivers/mtd/nand/raw/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -177,17 +177,17 @@ static void elm_load_syndrome(struct elm_info *info, switch (info->bch_type) { case BCH8_ECC: /* syndrome fragment 0 = ecc[9-12B] */ - val = cpu_to_be32(*(u32 *) &ecc[9]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[9]); elm_write_reg(info, offset, val); /* syndrome fragment 1 = ecc[5-8B] */ offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[5]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[5]); elm_write_reg(info, offset, val); /* syndrome fragment 2 = ecc[1-4B] */ offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[1]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[1]); elm_write_reg(info, offset, val); /* syndrome fragment 3 = ecc[0B] */ @@ -197,35 +197,35 @@ static void elm_load_syndrome(struct elm_info *info, break; case BCH4_ECC: /* syndrome fragment 0 = ecc[20-52b] bits */ - val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | + val = ((__force u32)cpu_to_be32(*(u32 *)&ecc[3]) >> 4) | ((ecc[2] & 0xf) << 28); elm_write_reg(info, offset, val); /* syndrome fragment 1 = ecc[0-20b] bits */ offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 12; elm_write_reg(info, offset, val); break; case BCH16_ECC: - val = cpu_to_be32(*(u32 *) &ecc[22]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[22]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[18]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[18]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[14]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[14]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[10]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[10]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[6]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[6]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[2]); + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[2]); elm_write_reg(info, offset, val); offset += 4; - val = cpu_to_be32(*(u32 *) &ecc[0]) >> 16; + val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 16; elm_write_reg(info, offset, val); break; default: diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index 2312e27362cb..5a04680342c3 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -562,9 +562,10 @@ static int rk_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 * * The rk_nfc_ooblayout_free() function already has reserved - * these 4 bytes with: + * these 4 bytes together with 2 bytes for BBM + * by reducing it's length: * - * oob_region->offset = NFC_SYS_DATA_SIZE + 2; + * oob_region->length = rknand->metadata_size - NFC_SYS_DATA_SIZE - 2; */ if (!i) memcpy(rk_nfc_oob_ptr(chip, i), @@ -597,7 +598,7 @@ static int rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, int pages_per_blk = mtd->erasesize / mtd->writesize; int ret = 0, i, boot_rom_mode = 0; dma_addr_t dma_data, dma_oob; - u32 reg; + u32 tmp; u8 *oob; nand_prog_page_begin_op(chip, page, 0, NULL, 0); @@ -624,6 +625,13 @@ static int rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, * * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... * + * The code here just swaps the first 4 bytes with the last + * 4 bytes without losing any data. + * + * The chip->oob_poi data layout: + * + * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 + * * Configure the ECC algorithm supported by the boot ROM. */ if ((page < (pages_per_blk * rknand->boot_blks)) && @@ -634,21 +642,17 @@ static int rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, } for (i = 0; i < ecc->steps; i++) { - if (!i) { - reg = 0xFFFFFFFF; - } else { + if (!i) + oob = chip->oob_poi + (ecc->steps - 1) * NFC_SYS_DATA_SIZE; + else oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; - reg = oob[0] | oob[1] << 8 | oob[2] << 16 | - oob[3] << 24; - } - if (!i && boot_rom_mode) - reg = (page & (pages_per_blk - 1)) * 4; + tmp = oob[0] | oob[1] << 8 | oob[2] << 16 | oob[3] << 24; if (nfc->cfg->type == NFC_V9) - nfc->oob_buf[i] = reg; + nfc->oob_buf[i] = tmp; else - nfc->oob_buf[i * (oob_step / 4)] = reg; + nfc->oob_buf[i * (oob_step / 4)] = tmp; } dma_data = dma_map_single(nfc->dev, (void *)nfc->page_buf, @@ -811,12 +815,17 @@ static int rk_nfc_read_page_hwecc(struct nand_chip *chip, u8 *buf, int oob_on, goto timeout_err; } - for (i = 1; i < ecc->steps; i++) { - oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; + for (i = 0; i < ecc->steps; i++) { + if (!i) + oob = chip->oob_poi + (ecc->steps - 1) * NFC_SYS_DATA_SIZE; + else + oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; + if (nfc->cfg->type == NFC_V9) tmp = nfc->oob_buf[i]; else tmp = nfc->oob_buf[i * (oob_step / 4)]; + *oob++ = (u8)tmp; *oob++ = (u8)(tmp >> 8); *oob++ = (u8)(tmp >> 16); @@ -933,12 +942,8 @@ static int rk_nfc_ooblayout_free(struct mtd_info *mtd, int section, if (section) return -ERANGE; - /* - * The beginning of the OOB area stores the reserved data for the NFC, - * the size of the reserved data is NFC_SYS_DATA_SIZE bytes. - */ oob_region->length = rknand->metadata_size - NFC_SYS_DATA_SIZE - 2; - oob_region->offset = NFC_SYS_DATA_SIZE + 2; + oob_region->offset = 2; return 0; } diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 7380b1ebaccd..a80427c13121 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -73,7 +73,7 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: @@ -92,7 +92,7 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, if (spi_mem_exec_op(spinand->spimem, &op)) return nanddev_get_ecc_conf(nand)->strength; - mbf >>= 4; + mbf = *(spinand->scratchbuf) >> 4; if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf)) return nanddev_get_ecc_conf(nand)->strength; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 3ad58cd284d8..f507e3759301 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -108,7 +108,7 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: @@ -126,7 +126,7 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, if (spi_mem_exec_op(spinand->spimem, &op)) return nanddev_get_ecc_conf(nand)->strength; - mbf >>= 4; + mbf = *(spinand->scratchbuf) >> 4; if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf)) return nanddev_get_ecc_conf(nand)->strength; diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 36876aa849ed..15f9a80c10b9 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -361,7 +361,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, */ static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) { - struct spi_mem_op op = {}; + struct spi_mem_op op; u8 addr_mode; int ret; @@ -492,7 +492,7 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - struct spi_mem_op op = {}; + struct spi_mem_op op; int ret; ret = cypress_nor_set_addr_mode_nbytes(nor); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 484c9e3e5e82..447b06ea4fc9 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5901,7 +5901,9 @@ void bond_setup(struct net_device *bond_dev) bond_dev->hw_features = BOND_VLAN_FEATURES | NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_VLAN_CTAG_FILTER; + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_STAG_RX | + NETIF_F_HW_VLAN_STAG_FILTER; bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; bond_dev->features |= bond_dev->hw_features; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index cde253d27bd0..72374b066f64 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1436,7 +1436,9 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - clk_prepare_enable(priv->clk); + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; priv->clk_mdiv = devm_clk_get_optional(&pdev->dev, "sw_switch_mdiv"); if (IS_ERR(priv->clk_mdiv)) { @@ -1444,7 +1446,9 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev) goto out_clk; } - clk_prepare_enable(priv->clk_mdiv); + ret = clk_prepare_enable(priv->clk_mdiv); + if (ret) + goto out_clk; ret = bcm_sf2_sw_rst(priv); if (ret) { diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b18cd170ec06..6c0623f88654 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -635,10 +635,9 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x1030, 0x1030), regmap_reg_range(0x1100, 0x1115), regmap_reg_range(0x111a, 0x111f), - regmap_reg_range(0x1122, 0x1127), - regmap_reg_range(0x112a, 0x112b), - regmap_reg_range(0x1136, 0x1139), - regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1120, 0x112b), + regmap_reg_range(0x1134, 0x113b), + regmap_reg_range(0x113c, 0x113f), regmap_reg_range(0x1400, 0x1401), regmap_reg_range(0x1403, 0x1403), regmap_reg_range(0x1410, 0x1417), @@ -669,10 +668,9 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x2030, 0x2030), regmap_reg_range(0x2100, 0x2115), regmap_reg_range(0x211a, 0x211f), - regmap_reg_range(0x2122, 0x2127), - regmap_reg_range(0x212a, 0x212b), - regmap_reg_range(0x2136, 0x2139), - regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2120, 0x212b), + regmap_reg_range(0x2134, 0x213b), + regmap_reg_range(0x213c, 0x213f), regmap_reg_range(0x2400, 0x2401), regmap_reg_range(0x2403, 0x2403), regmap_reg_range(0x2410, 0x2417), @@ -703,10 +701,9 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x3030, 0x3030), regmap_reg_range(0x3100, 0x3115), regmap_reg_range(0x311a, 0x311f), - regmap_reg_range(0x3122, 0x3127), - regmap_reg_range(0x312a, 0x312b), - regmap_reg_range(0x3136, 0x3139), - regmap_reg_range(0x313e, 0x313f), + regmap_reg_range(0x3120, 0x312b), + regmap_reg_range(0x3134, 0x313b), + regmap_reg_range(0x313c, 0x313f), regmap_reg_range(0x3400, 0x3401), regmap_reg_range(0x3403, 0x3403), regmap_reg_range(0x3410, 0x3417), @@ -737,10 +734,9 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x4030, 0x4030), regmap_reg_range(0x4100, 0x4115), regmap_reg_range(0x411a, 0x411f), - regmap_reg_range(0x4122, 0x4127), - regmap_reg_range(0x412a, 0x412b), - regmap_reg_range(0x4136, 0x4139), - regmap_reg_range(0x413e, 0x413f), + regmap_reg_range(0x4120, 0x412b), + regmap_reg_range(0x4134, 0x413b), + regmap_reg_range(0x413c, 0x413f), regmap_reg_range(0x4400, 0x4401), regmap_reg_range(0x4403, 0x4403), regmap_reg_range(0x4410, 0x4417), @@ -771,10 +767,9 @@ static const struct regmap_range ksz9477_valid_regs[] = { regmap_reg_range(0x5030, 0x5030), regmap_reg_range(0x5100, 0x5115), regmap_reg_range(0x511a, 0x511f), - regmap_reg_range(0x5122, 0x5127), - regmap_reg_range(0x512a, 0x512b), - regmap_reg_range(0x5136, 0x5139), - regmap_reg_range(0x513e, 0x513f), + regmap_reg_range(0x5120, 0x512b), + regmap_reg_range(0x5134, 0x513b), + regmap_reg_range(0x513c, 0x513f), regmap_reg_range(0x5400, 0x5401), regmap_reg_range(0x5403, 0x5403), regmap_reg_range(0x5410, 0x5417), diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 8da46d284e35..bef879c6d500 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1625,8 +1625,10 @@ static void felix_teardown(struct dsa_switch *ds) struct felix *felix = ocelot_to_felix(ocelot); struct dsa_port *dp; + rtnl_lock(); if (felix->tag_proto_ops) felix->tag_proto_ops->teardown(ds); + rtnl_unlock(); dsa_switch_for_each_available_port(dp, ds) ocelot_deinit_port(ocelot, dp->index); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e5b54e6025be..1eb490c48c52 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -633,12 +633,13 @@ tx_kick_pending: return NETDEV_TX_OK; } -static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) +static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, txr->txq_index); u16 cons = txr->tx_cons; struct pci_dev *pdev = bp->pdev; + int nr_pkts = bnapi->tx_pkts; int i; unsigned int tx_bytes = 0; @@ -688,6 +689,7 @@ next_tx_int: dev_kfree_skb_any(skb); } + bnapi->tx_pkts = 0; WRITE_ONCE(txr->tx_cons, cons); __netif_txq_completed_wake(txq, nr_pkts, tx_bytes, @@ -697,17 +699,24 @@ next_tx_int: static struct page *__bnxt_alloc_rx_page(struct bnxt *bp, dma_addr_t *mapping, struct bnxt_rx_ring_info *rxr, + unsigned int *offset, gfp_t gfp) { struct device *dev = &bp->pdev->dev; struct page *page; - page = page_pool_dev_alloc_pages(rxr->page_pool); + if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) { + page = page_pool_dev_alloc_frag(rxr->page_pool, offset, + BNXT_RX_PAGE_SIZE); + } else { + page = page_pool_dev_alloc_pages(rxr->page_pool); + *offset = 0; + } if (!page) return NULL; - *mapping = dma_map_page_attrs(dev, page, 0, PAGE_SIZE, bp->rx_dir, - DMA_ATTR_WEAK_ORDERING); + *mapping = dma_map_page_attrs(dev, page, *offset, BNXT_RX_PAGE_SIZE, + bp->rx_dir, DMA_ATTR_WEAK_ORDERING); if (dma_mapping_error(dev, *mapping)) { page_pool_recycle_direct(rxr->page_pool, page); return NULL; @@ -747,15 +756,16 @@ int bnxt_alloc_rx_data(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, dma_addr_t mapping; if (BNXT_RX_PAGE_MODE(bp)) { + unsigned int offset; struct page *page = - __bnxt_alloc_rx_page(bp, &mapping, rxr, gfp); + __bnxt_alloc_rx_page(bp, &mapping, rxr, &offset, gfp); if (!page) return -ENOMEM; mapping += bp->rx_dma_offset; rx_buf->data = page; - rx_buf->data_ptr = page_address(page) + bp->rx_offset; + rx_buf->data_ptr = page_address(page) + offset + bp->rx_offset; } else { u8 *data = __bnxt_alloc_rx_frag(bp, &mapping, gfp); @@ -815,7 +825,7 @@ static inline int bnxt_alloc_rx_page(struct bnxt *bp, unsigned int offset = 0; if (BNXT_RX_PAGE_MODE(bp)) { - page = __bnxt_alloc_rx_page(bp, &mapping, rxr, gfp); + page = __bnxt_alloc_rx_page(bp, &mapping, rxr, &offset, gfp); if (!page) return -ENOMEM; @@ -962,15 +972,15 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, return NULL; } dma_addr -= bp->rx_dma_offset; - dma_unmap_page_attrs(&bp->pdev->dev, dma_addr, PAGE_SIZE, bp->rx_dir, - DMA_ATTR_WEAK_ORDERING); - skb = build_skb(page_address(page), PAGE_SIZE); + dma_unmap_page_attrs(&bp->pdev->dev, dma_addr, BNXT_RX_PAGE_SIZE, + bp->rx_dir, DMA_ATTR_WEAK_ORDERING); + skb = build_skb(data_ptr - bp->rx_offset, BNXT_RX_PAGE_SIZE); if (!skb) { page_pool_recycle_direct(rxr->page_pool, page); return NULL; } skb_mark_for_recycle(skb); - skb_reserve(skb, bp->rx_dma_offset); + skb_reserve(skb, bp->rx_offset); __skb_put(skb, len); return skb; @@ -996,8 +1006,8 @@ static struct sk_buff *bnxt_rx_page_skb(struct bnxt *bp, return NULL; } dma_addr -= bp->rx_dma_offset; - dma_unmap_page_attrs(&bp->pdev->dev, dma_addr, PAGE_SIZE, bp->rx_dir, - DMA_ATTR_WEAK_ORDERING); + dma_unmap_page_attrs(&bp->pdev->dev, dma_addr, BNXT_RX_PAGE_SIZE, + bp->rx_dir, DMA_ATTR_WEAK_ORDERING); if (unlikely(!payload)) payload = eth_get_headlen(bp->dev, data_ptr, len); @@ -1010,7 +1020,7 @@ static struct sk_buff *bnxt_rx_page_skb(struct bnxt *bp, skb_mark_for_recycle(skb); off = (void *)data_ptr - page_address(page); - skb_add_rx_frag(skb, 0, page, off, len, PAGE_SIZE); + skb_add_rx_frag(skb, 0, page, off, len, BNXT_RX_PAGE_SIZE); memcpy(skb->data - NET_IP_ALIGN, data_ptr - NET_IP_ALIGN, payload + NET_IP_ALIGN); @@ -1141,7 +1151,7 @@ static struct sk_buff *bnxt_rx_agg_pages_skb(struct bnxt *bp, skb->data_len += total_frag_len; skb->len += total_frag_len; - skb->truesize += PAGE_SIZE * agg_bufs; + skb->truesize += BNXT_RX_PAGE_SIZE * agg_bufs; return skb; } @@ -2569,12 +2579,11 @@ static int __bnxt_poll_work(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, return rx_pkts; } -static void __bnxt_poll_work_done(struct bnxt *bp, struct bnxt_napi *bnapi) +static void __bnxt_poll_work_done(struct bnxt *bp, struct bnxt_napi *bnapi, + int budget) { - if (bnapi->tx_pkts) { - bnapi->tx_int(bp, bnapi, bnapi->tx_pkts); - bnapi->tx_pkts = 0; - } + if (bnapi->tx_pkts) + bnapi->tx_int(bp, bnapi, budget); if ((bnapi->events & BNXT_RX_EVENT) && !(bnapi->in_reset)) { struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; @@ -2603,7 +2612,7 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, */ bnxt_db_cq(bp, &cpr->cp_db, cpr->cp_raw_cons); - __bnxt_poll_work_done(bp, bnapi); + __bnxt_poll_work_done(bp, bnapi, budget); return rx_pkts; } @@ -2734,7 +2743,7 @@ static int __bnxt_poll_cqs(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) } static void __bnxt_poll_cqs_done(struct bnxt *bp, struct bnxt_napi *bnapi, - u64 dbr_type) + u64 dbr_type, int budget) { struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; int i; @@ -2750,7 +2759,7 @@ static void __bnxt_poll_cqs_done(struct bnxt *bp, struct bnxt_napi *bnapi, cpr2->had_work_done = 0; } } - __bnxt_poll_work_done(bp, bnapi); + __bnxt_poll_work_done(bp, bnapi, budget); } static int bnxt_poll_p5(struct napi_struct *napi, int budget) @@ -2780,7 +2789,8 @@ static int bnxt_poll_p5(struct napi_struct *napi, int budget) if (cpr->has_more_work) break; - __bnxt_poll_cqs_done(bp, bnapi, DBR_TYPE_CQ_ARMALL); + __bnxt_poll_cqs_done(bp, bnapi, DBR_TYPE_CQ_ARMALL, + budget); cpr->cp_raw_cons = raw_cons; if (napi_complete_done(napi, work_done)) BNXT_DB_NQ_ARM_P5(&cpr->cp_db, @@ -2810,7 +2820,7 @@ static int bnxt_poll_p5(struct napi_struct *napi, int budget) } raw_cons = NEXT_RAW_CMP(raw_cons); } - __bnxt_poll_cqs_done(bp, bnapi, DBR_TYPE_CQ); + __bnxt_poll_cqs_done(bp, bnapi, DBR_TYPE_CQ, budget); if (raw_cons != cpr->cp_raw_cons) { cpr->cp_raw_cons = raw_cons; BNXT_DB_NQ_P5(&cpr->cp_db, raw_cons); @@ -2943,8 +2953,8 @@ skip_rx_tpa_free: rx_buf->data = NULL; if (BNXT_RX_PAGE_MODE(bp)) { mapping -= bp->rx_dma_offset; - dma_unmap_page_attrs(&pdev->dev, mapping, PAGE_SIZE, - bp->rx_dir, + dma_unmap_page_attrs(&pdev->dev, mapping, + BNXT_RX_PAGE_SIZE, bp->rx_dir, DMA_ATTR_WEAK_ORDERING); page_pool_recycle_direct(rxr->page_pool, data); } else { @@ -3213,6 +3223,8 @@ static int bnxt_alloc_rx_page_pool(struct bnxt *bp, pp.napi = &rxr->bnapi->napi; pp.dev = &bp->pdev->dev; pp.dma_dir = DMA_BIDIRECTIONAL; + if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) + pp.flags |= PP_FLAG_PAGE_FRAG; rxr->page_pool = page_pool_create(&pp); if (IS_ERR(rxr->page_pool)) { @@ -3989,26 +4001,29 @@ void bnxt_set_ring_params(struct bnxt *bp) */ int bnxt_set_rx_skb_mode(struct bnxt *bp, bool page_mode) { + struct net_device *dev = bp->dev; + if (page_mode) { bp->flags &= ~BNXT_FLAG_AGG_RINGS; bp->flags |= BNXT_FLAG_RX_PAGE_MODE; - if (bp->dev->mtu > BNXT_MAX_PAGE_MODE_MTU) { + if (bp->xdp_prog->aux->xdp_has_frags) + dev->max_mtu = min_t(u16, bp->max_mtu, BNXT_MAX_MTU); + else + dev->max_mtu = + min_t(u16, bp->max_mtu, BNXT_MAX_PAGE_MODE_MTU); + if (dev->mtu > BNXT_MAX_PAGE_MODE_MTU) { bp->flags |= BNXT_FLAG_JUMBO; bp->rx_skb_func = bnxt_rx_multi_page_skb; - bp->dev->max_mtu = - min_t(u16, bp->max_mtu, BNXT_MAX_MTU); } else { bp->flags |= BNXT_FLAG_NO_AGG_RINGS; bp->rx_skb_func = bnxt_rx_page_skb; - bp->dev->max_mtu = - min_t(u16, bp->max_mtu, BNXT_MAX_PAGE_MODE_MTU); } bp->rx_dir = DMA_BIDIRECTIONAL; /* Disable LRO or GRO_HW */ - netdev_update_features(bp->dev); + netdev_update_features(dev); } else { - bp->dev->max_mtu = bp->max_mtu; + dev->max_mtu = bp->max_mtu; bp->flags &= ~BNXT_FLAG_RX_PAGE_MODE; bp->rx_dir = DMA_FROM_DEVICE; bp->rx_skb_func = bnxt_rx_skb; @@ -9429,6 +9444,8 @@ static void bnxt_enable_napi(struct bnxt *bp) cpr->sw_stats.rx.rx_resets++; bnapi->in_reset = false; + bnapi->tx_pkts = 0; + if (bnapi->rx_ring) { INIT_WORK(&cpr->dim.work, bnxt_dim_work); cpr->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 080e73496066..bb95c3dc5270 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1005,7 +1005,7 @@ struct bnxt_napi { struct bnxt_tx_ring_info *tx_ring; void (*tx_int)(struct bnxt *, struct bnxt_napi *, - int); + int budget); int tx_pkts; u8 events; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 4efa5fe6972b..fb43232310b2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -125,16 +125,20 @@ static void __bnxt_xmit_xdp_redirect(struct bnxt *bp, dma_unmap_len_set(tx_buf, len, 0); } -void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) +void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; bool rx_doorbell_needed = false; + int nr_pkts = bnapi->tx_pkts; struct bnxt_sw_tx_bd *tx_buf; u16 tx_cons = txr->tx_cons; u16 last_tx_cons = tx_cons; int i, j, frags; + if (!budget) + return; + for (i = 0; i < nr_pkts; i++) { tx_buf = &txr->tx_buf_ring[tx_cons]; @@ -161,6 +165,8 @@ void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) } tx_cons = NEXT_TX(tx_cons); } + + bnapi->tx_pkts = 0; WRITE_ONCE(txr->tx_cons, tx_cons); if (rx_doorbell_needed) { tx_buf = &txr->tx_buf_ring[last_tx_cons]; @@ -180,8 +186,8 @@ void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, u8 *data_ptr, unsigned int len, struct xdp_buff *xdp) { + u32 buflen = BNXT_RX_PAGE_SIZE; struct bnxt_sw_rx_bd *rx_buf; - u32 buflen = PAGE_SIZE; struct pci_dev *pdev; dma_addr_t mapping; u32 offset; @@ -297,7 +303,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, rx_buf = &rxr->rx_buf_ring[cons]; mapping = rx_buf->mapping - bp->rx_dma_offset; dma_unmap_page_attrs(&pdev->dev, mapping, - PAGE_SIZE, bp->rx_dir, + BNXT_RX_PAGE_SIZE, bp->rx_dir, DMA_ATTR_WEAK_ORDERING); /* if we are unable to allocate a new buffer, abort and reuse */ @@ -480,7 +486,7 @@ bnxt_xdp_build_skb(struct bnxt *bp, struct sk_buff *skb, u8 num_frags, } xdp_update_skb_shared_info(skb, num_frags, sinfo->xdp_frags_size, - PAGE_SIZE * sinfo->nr_frags, + BNXT_RX_PAGE_SIZE * sinfo->nr_frags, xdp_buff_is_frag_pfmemalloc(xdp)); return skb; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h index ea430d6961df..5e412c5655ba 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h @@ -16,7 +16,7 @@ struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp, struct bnxt_tx_ring_info *txr, dma_addr_t mapping, u32 len, struct xdp_buff *xdp); -void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts); +void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget); bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, struct xdp_buff xdp, struct page *page, u8 **data_ptr, unsigned int *len, u8 *event); diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 1416262d4296..e0a4cb7e3f50 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -1186,14 +1186,9 @@ static int enetc_init_port_rss_memory(struct enetc_si *si) static int enetc_pf_register_with_ierb(struct pci_dev *pdev) { - struct device_node *node = pdev->dev.of_node; struct platform_device *ierb_pdev; struct device_node *ierb_node; - /* Don't register with the IERB if the PF itself is disabled */ - if (!node || !of_device_is_available(node)) - return 0; - ierb_node = of_find_compatible_node(NULL, NULL, "fsl,ls1028a-enetc-ierb"); if (!ierb_node || !of_device_is_available(ierb_node)) @@ -1208,56 +1203,81 @@ static int enetc_pf_register_with_ierb(struct pci_dev *pdev) return enetc_ierb_register_pf(ierb_pdev, pdev); } -static int enetc_pf_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static struct enetc_si *enetc_psi_create(struct pci_dev *pdev) { - struct device_node *node = pdev->dev.of_node; - struct enetc_ndev_priv *priv; - struct net_device *ndev; struct enetc_si *si; - struct enetc_pf *pf; int err; - err = enetc_pf_register_with_ierb(pdev); - if (err == -EPROBE_DEFER) - return err; - if (err) - dev_warn(&pdev->dev, - "Could not register with IERB driver: %pe, please update the device tree\n", - ERR_PTR(err)); - - err = enetc_pci_probe(pdev, KBUILD_MODNAME, sizeof(*pf)); - if (err) - return dev_err_probe(&pdev->dev, err, "PCI probing failed\n"); + err = enetc_pci_probe(pdev, KBUILD_MODNAME, sizeof(struct enetc_pf)); + if (err) { + dev_err_probe(&pdev->dev, err, "PCI probing failed\n"); + goto out; + } si = pci_get_drvdata(pdev); if (!si->hw.port || !si->hw.global) { err = -ENODEV; dev_err(&pdev->dev, "could not map PF space, probing a VF?\n"); - goto err_map_pf_space; + goto out_pci_remove; } err = enetc_setup_cbdr(&pdev->dev, &si->hw, ENETC_CBDR_DEFAULT_SIZE, &si->cbd_ring); if (err) - goto err_setup_cbdr; + goto out_pci_remove; err = enetc_init_port_rfs_memory(si); if (err) { dev_err(&pdev->dev, "Failed to initialize RFS memory\n"); - goto err_init_port_rfs; + goto out_teardown_cbdr; } err = enetc_init_port_rss_memory(si); if (err) { dev_err(&pdev->dev, "Failed to initialize RSS memory\n"); - goto err_init_port_rss; + goto out_teardown_cbdr; } - if (node && !of_device_is_available(node)) { - dev_info(&pdev->dev, "device is disabled, skipping\n"); - err = -ENODEV; - goto err_device_disabled; + return si; + +out_teardown_cbdr: + enetc_teardown_cbdr(&si->cbd_ring); +out_pci_remove: + enetc_pci_remove(pdev); +out: + return ERR_PTR(err); +} + +static void enetc_psi_destroy(struct pci_dev *pdev) +{ + struct enetc_si *si = pci_get_drvdata(pdev); + + enetc_teardown_cbdr(&si->cbd_ring); + enetc_pci_remove(pdev); +} + +static int enetc_pf_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device_node *node = pdev->dev.of_node; + struct enetc_ndev_priv *priv; + struct net_device *ndev; + struct enetc_si *si; + struct enetc_pf *pf; + int err; + + err = enetc_pf_register_with_ierb(pdev); + if (err == -EPROBE_DEFER) + return err; + if (err) + dev_warn(&pdev->dev, + "Could not register with IERB driver: %pe, please update the device tree\n", + ERR_PTR(err)); + + si = enetc_psi_create(pdev); + if (IS_ERR(si)) { + err = PTR_ERR(si); + goto err_psi_create; } pf = enetc_si_priv(si); @@ -1339,15 +1359,9 @@ err_alloc_si_res: si->ndev = NULL; free_netdev(ndev); err_alloc_netdev: -err_init_port_rss: -err_init_port_rfs: -err_device_disabled: err_setup_mac_addresses: - enetc_teardown_cbdr(&si->cbd_ring); -err_setup_cbdr: -err_map_pf_space: - enetc_pci_remove(pdev); - + enetc_psi_destroy(pdev); +err_psi_create: return err; } @@ -1370,12 +1384,29 @@ static void enetc_pf_remove(struct pci_dev *pdev) enetc_free_msix(priv); enetc_free_si_resources(priv); - enetc_teardown_cbdr(&si->cbd_ring); free_netdev(si->ndev); - enetc_pci_remove(pdev); + enetc_psi_destroy(pdev); +} + +static void enetc_fixup_clear_rss_rfs(struct pci_dev *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct enetc_si *si; + + /* Only apply quirk for disabled functions. For the ones + * that are enabled, enetc_pf_probe() will apply it. + */ + if (node && of_device_is_available(node)) + return; + + si = enetc_psi_create(pdev); + if (si) + enetc_psi_destroy(pdev); } +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PF, + enetc_fixup_clear_rss_rfs); static const struct pci_device_id enetc_pf_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PF) }, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 52546f625c8b..f276b5ecb431 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -464,9 +464,9 @@ static void hns3_dbg_fill_content(char *content, u16 len, if (result) { if (item_len < strlen(result[i])) break; - strscpy(pos, result[i], strlen(result[i])); + memcpy(pos, result[i], strlen(result[i])); } else { - strscpy(pos, items[i].name, strlen(items[i].name)); + memcpy(pos, items[i].name, strlen(items[i].name)); } pos += item_len; len -= item_len; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 9f6890059666..b7b51e56b030 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -5854,6 +5854,9 @@ void hns3_external_lb_prepare(struct net_device *ndev, bool if_running) if (!if_running) return; + if (test_and_set_bit(HNS3_NIC_STATE_DOWN, &priv->state)) + return; + netif_carrier_off(ndev); netif_tx_disable(ndev); @@ -5882,7 +5885,16 @@ void hns3_external_lb_restore(struct net_device *ndev, bool if_running) if (!if_running) return; - hns3_nic_reset_all_ring(priv->ae_handle); + if (hns3_nic_resetting(ndev)) + return; + + if (!test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) + return; + + if (hns3_nic_reset_all_ring(priv->ae_handle)) + return; + + clear_bit(HNS3_NIC_STATE_DOWN, &priv->state); for (i = 0; i < priv->vector_num; i++) hns3_vector_enable(&priv->tqp_vector[i]); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 409db2e70965..0fb2eaee3e8a 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -111,9 +111,9 @@ static void hclge_dbg_fill_content(char *content, u16 len, if (result) { if (item_len < strlen(result[i])) break; - strscpy(pos, result[i], strlen(result[i])); + memcpy(pos, result[i], strlen(result[i])); } else { - strscpy(pos, items[i].name, strlen(items[i].name)); + memcpy(pos, items[i].name, strlen(items[i].name)); } pos += item_len; len -= item_len; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index bf675c15fbb9..a940e35aef29 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -72,6 +72,8 @@ static void hclge_restore_hw_table(struct hclge_dev *hdev); static void hclge_sync_promisc_mode(struct hclge_dev *hdev); static void hclge_sync_fd_table(struct hclge_dev *hdev); static void hclge_update_fec_stats(struct hclge_dev *hdev); +static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret, + int wait_cnt); static struct hnae3_ae_algo ae_algo; @@ -7558,6 +7560,8 @@ static void hclge_enable_fd(struct hnae3_handle *handle, bool enable) static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) { +#define HCLGE_LINK_STATUS_WAIT_CNT 3 + struct hclge_desc desc; struct hclge_config_mac_mode_cmd *req = (struct hclge_config_mac_mode_cmd *)desc.data; @@ -7582,9 +7586,15 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en); ret = hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) + if (ret) { dev_err(&hdev->pdev->dev, "mac enable fail, ret =%d.\n", ret); + return; + } + + if (!enable) + hclge_mac_link_status_wait(hdev, HCLGE_LINK_STATUS_DOWN, + HCLGE_LINK_STATUS_WAIT_CNT); } static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid, @@ -7647,10 +7657,9 @@ static void hclge_phy_link_status_wait(struct hclge_dev *hdev, } while (++i < HCLGE_PHY_LINK_STATUS_NUM); } -static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret) +static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret, + int wait_cnt) { -#define HCLGE_MAC_LINK_STATUS_NUM 100 - int link_status; int i = 0; int ret; @@ -7663,13 +7672,15 @@ static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret) return 0; msleep(HCLGE_LINK_STATUS_MS); - } while (++i < HCLGE_MAC_LINK_STATUS_NUM); + } while (++i < wait_cnt); return -EBUSY; } static int hclge_mac_phy_link_status_wait(struct hclge_dev *hdev, bool en, bool is_phy) { +#define HCLGE_MAC_LINK_STATUS_NUM 100 + int link_ret; link_ret = en ? HCLGE_LINK_STATUS_UP : HCLGE_LINK_STATUS_DOWN; @@ -7677,7 +7688,8 @@ static int hclge_mac_phy_link_status_wait(struct hclge_dev *hdev, bool en, if (is_phy) hclge_phy_link_status_wait(hdev, link_ret); - return hclge_mac_link_status_wait(hdev, link_ret); + return hclge_mac_link_status_wait(hdev, link_ret, + HCLGE_MAC_LINK_STATUS_NUM); } static int hclge_set_app_loopback(struct hclge_dev *hdev, bool en) @@ -10915,9 +10927,12 @@ int hclge_cfg_flowctrl(struct hclge_dev *hdev) u32 rx_pause, tx_pause; u8 flowctl; - if (!phydev->link || !phydev->autoneg) + if (!phydev->link) return 0; + if (!phydev->autoneg) + return hclge_mac_pause_setup_hw(hdev); + local_advertising = linkmode_adv_to_lcl_adv_t(phydev->advertising); if (phydev->pause) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index de509e5751a7..c58c31221762 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -1553,7 +1553,7 @@ static int hclge_bp_setup_hw(struct hclge_dev *hdev, u8 tc) return 0; } -static int hclge_mac_pause_setup_hw(struct hclge_dev *hdev) +int hclge_mac_pause_setup_hw(struct hclge_dev *hdev) { bool tx_en, rx_en; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h index 45dcfef3f90c..53eec6df5194 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h @@ -245,6 +245,7 @@ int hclge_pfc_pause_en_cfg(struct hclge_dev *hdev, u8 tx_rx_bitmap, u8 pfc_bitmap); int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx); int hclge_pause_addr_cfg(struct hclge_dev *hdev, const u8 *mac_addr); +int hclge_mac_pause_setup_hw(struct hclge_dev *hdev); void hclge_pfc_rx_stats_get(struct hclge_dev *hdev, u64 *stats); void hclge_pfc_tx_stats_get(struct hclge_dev *hdev, u64 *stats); int hclge_tm_qs_shaper_cfg(struct hclge_vport *vport, int max_tx_rate); diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 763d613adbcc..df76cdaddcfb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -97,6 +97,8 @@ static int pending_scrq(struct ibmvnic_adapter *, static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *, struct ibmvnic_sub_crq_queue *); static int ibmvnic_poll(struct napi_struct *napi, int data); +static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter); +static inline void reinit_init_done(struct ibmvnic_adapter *adapter); static void send_query_map(struct ibmvnic_adapter *adapter); static int send_request_map(struct ibmvnic_adapter *, dma_addr_t, u32, u8); static int send_request_unmap(struct ibmvnic_adapter *, u8); @@ -114,6 +116,7 @@ static void ibmvnic_tx_scrq_clean_buffer(struct ibmvnic_adapter *adapter, static void free_long_term_buff(struct ibmvnic_adapter *adapter, struct ibmvnic_long_term_buff *ltb); static void ibmvnic_disable_irqs(struct ibmvnic_adapter *adapter); +static void flush_reset_queue(struct ibmvnic_adapter *adapter); struct ibmvnic_stat { char name[ETH_GSTRING_LEN]; @@ -1505,8 +1508,8 @@ static const char *adapter_state_to_string(enum vnic_state state) static int ibmvnic_login(struct net_device *netdev) { + unsigned long flags, timeout = msecs_to_jiffies(20000); struct ibmvnic_adapter *adapter = netdev_priv(netdev); - unsigned long timeout = msecs_to_jiffies(20000); int retry_count = 0; int retries = 10; bool retry; @@ -1527,11 +1530,9 @@ static int ibmvnic_login(struct net_device *netdev) if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { - netdev_warn(netdev, "Login timed out, retrying...\n"); - retry = true; - adapter->init_done_rc = 0; - retry_count++; - continue; + netdev_warn(netdev, "Login timed out\n"); + adapter->login_pending = false; + goto partial_reset; } if (adapter->init_done_rc == ABORTED) { @@ -1573,10 +1574,69 @@ static int ibmvnic_login(struct net_device *netdev) "SCRQ irq initialization failed\n"); return rc; } + /* Default/timeout error handling, reset and start fresh */ } else if (adapter->init_done_rc) { netdev_warn(netdev, "Adapter login failed, init_done_rc = %d\n", adapter->init_done_rc); - return -EIO; + +partial_reset: + /* adapter login failed, so free any CRQs or sub-CRQs + * and register again before attempting to login again. + * If we don't do this then the VIOS may think that + * we are already logged in and reject any subsequent + * attempts + */ + netdev_warn(netdev, + "Freeing and re-registering CRQs before attempting to login again\n"); + retry = true; + adapter->init_done_rc = 0; + release_sub_crqs(adapter, true); + /* Much of this is similar logic as ibmvnic_probe(), + * we are essentially re-initializing communication + * with the server. We really should not run any + * resets/failovers here because this is already a form + * of reset and we do not want parallel resets occurring + */ + do { + reinit_init_done(adapter); + /* Clear any failovers we got in the previous + * pass since we are re-initializing the CRQ + */ + adapter->failover_pending = false; + release_crq_queue(adapter); + /* If we don't sleep here then we risk an + * unnecessary failover event from the VIOS. + * This is a known VIOS issue caused by a vnic + * device freeing and registering a CRQ too + * quickly. + */ + msleep(1500); + /* Avoid any resets, since we are currently + * resetting. + */ + spin_lock_irqsave(&adapter->rwi_lock, flags); + flush_reset_queue(adapter); + spin_unlock_irqrestore(&adapter->rwi_lock, + flags); + + rc = init_crq_queue(adapter); + if (rc) { + netdev_err(netdev, "login recovery: init CRQ failed %d\n", + rc); + return -EIO; + } + + rc = ibmvnic_reset_init(adapter, false); + if (rc) + netdev_err(netdev, "login recovery: Reset init failed %d\n", + rc); + /* IBMVNIC_CRQ_INIT will return EAGAIN if it + * fails, since ibmvnic_reset_init will free + * irq's in failure, we won't be able to receive + * new CRQs so we need to keep trying. probe() + * handles this similarly. + */ + } while (rc == -EAGAIN && retry_count++ < retries); } } while (retry); @@ -1588,12 +1648,22 @@ static int ibmvnic_login(struct net_device *netdev) static void release_login_buffer(struct ibmvnic_adapter *adapter) { + if (!adapter->login_buf) + return; + + dma_unmap_single(&adapter->vdev->dev, adapter->login_buf_token, + adapter->login_buf_sz, DMA_TO_DEVICE); kfree(adapter->login_buf); adapter->login_buf = NULL; } static void release_login_rsp_buffer(struct ibmvnic_adapter *adapter) { + if (!adapter->login_rsp_buf) + return; + + dma_unmap_single(&adapter->vdev->dev, adapter->login_rsp_buf_token, + adapter->login_rsp_buf_sz, DMA_FROM_DEVICE); kfree(adapter->login_rsp_buf); adapter->login_rsp_buf = NULL; } @@ -4830,11 +4900,14 @@ static int send_login(struct ibmvnic_adapter *adapter) if (rc) { adapter->login_pending = false; netdev_err(adapter->netdev, "Failed to send login, rc=%d\n", rc); - goto buf_rsp_map_failed; + goto buf_send_failed; } return 0; +buf_send_failed: + dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size, + DMA_FROM_DEVICE); buf_rsp_map_failed: kfree(login_rsp_buffer); adapter->login_rsp_buf = NULL; @@ -5396,6 +5469,7 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, int num_tx_pools; int num_rx_pools; u64 *size_array; + u32 rsp_len; int i; /* CHECK: Test/set of login_pending does not need to be atomic @@ -5407,11 +5481,6 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, } adapter->login_pending = false; - dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz, - DMA_TO_DEVICE); - dma_unmap_single(dev, adapter->login_rsp_buf_token, - adapter->login_rsp_buf_sz, DMA_FROM_DEVICE); - /* If the number of queues requested can't be allocated by the * server, the login response will return with code 1. We will need * to resend the login buffer with fewer queues requested. @@ -5447,6 +5516,23 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, ibmvnic_reset(adapter, VNIC_RESET_FATAL); return -EIO; } + + rsp_len = be32_to_cpu(login_rsp->len); + if (be32_to_cpu(login->login_rsp_len) < rsp_len || + rsp_len <= be32_to_cpu(login_rsp->off_txsubm_subcrqs) || + rsp_len <= be32_to_cpu(login_rsp->off_rxadd_subcrqs) || + rsp_len <= be32_to_cpu(login_rsp->off_rxadd_buff_size) || + rsp_len <= be32_to_cpu(login_rsp->off_supp_tx_desc)) { + /* This can happen if a login request times out and there are + * 2 outstanding login requests sent, the LOGIN_RSP crq + * could have been for the older login request. So we are + * parsing the newer response buffer which may be incomplete + */ + dev_err(dev, "FATAL: Login rsp offsets/lengths invalid\n"); + ibmvnic_reset(adapter, VNIC_RESET_FATAL); + return -EIO; + } + size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + be32_to_cpu(adapter->login_rsp_buf->off_rxadd_buff_size)); /* variable buffer sizes are not supported, so just read the diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index 2f47cfa7f06e..460ca561819a 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -1401,14 +1401,15 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; + spin_lock_bh(&adapter->fdir_fltr_lock); if (adapter->fdir_active_fltr >= IAVF_MAX_FDIR_FILTERS) { + spin_unlock_bh(&adapter->fdir_fltr_lock); dev_err(&adapter->pdev->dev, "Unable to add Flow Director filter because VF reached the limit of max allowed filters (%u)\n", IAVF_MAX_FDIR_FILTERS); return -ENOSPC; } - spin_lock_bh(&adapter->fdir_fltr_lock); if (iavf_find_fdir_fltr_by_loc(adapter, fsp->location)) { dev_err(&adapter->pdev->dev, "Failed to add Flow Director filter, it already exists\n"); spin_unlock_bh(&adapter->fdir_fltr_lock); @@ -1781,7 +1782,9 @@ static int iavf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, case ETHTOOL_GRXCLSRLCNT: if (!FDIR_FLTR_SUPPORT(adapter)) break; + spin_lock_bh(&adapter->fdir_fltr_lock); cmd->rule_cnt = adapter->fdir_active_fltr; + spin_unlock_bh(&adapter->fdir_fltr_lock); cmd->data = IAVF_MAX_FDIR_FILTERS; ret = 0; break; diff --git a/drivers/net/ethernet/intel/iavf/iavf_fdir.c b/drivers/net/ethernet/intel/iavf/iavf_fdir.c index 6146203efd84..505e82ebafe4 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_fdir.c +++ b/drivers/net/ethernet/intel/iavf/iavf_fdir.c @@ -722,7 +722,9 @@ void iavf_print_fdir_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *f bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr *fltr) { struct iavf_fdir_fltr *tmp; + bool ret = false; + spin_lock_bh(&adapter->fdir_fltr_lock); list_for_each_entry(tmp, &adapter->fdir_list_head, list) { if (tmp->flow_type != fltr->flow_type) continue; @@ -732,11 +734,14 @@ bool iavf_fdir_is_dup_fltr(struct iavf_adapter *adapter, struct iavf_fdir_fltr * !memcmp(&tmp->ip_data, &fltr->ip_data, sizeof(fltr->ip_data)) && !memcmp(&tmp->ext_data, &fltr->ext_data, - sizeof(fltr->ext_data))) - return true; + sizeof(fltr->ext_data))) { + ret = true; + break; + } } + spin_unlock_bh(&adapter->fdir_fltr_lock); - return false; + return ret; } /** diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index f02d44455772..cf92c39467c8 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -8813,6 +8813,7 @@ ice_setup_tc(struct net_device *netdev, enum tc_setup_type type, { struct ice_netdev_priv *np = netdev_priv(netdev); struct ice_pf *pf = np->vsi->back; + bool locked = false; int err; switch (type) { @@ -8822,10 +8823,27 @@ ice_setup_tc(struct net_device *netdev, enum tc_setup_type type, ice_setup_tc_block_cb, np, np, true); case TC_SETUP_QDISC_MQPRIO: + if (pf->adev) { + mutex_lock(&pf->adev_mutex); + device_lock(&pf->adev->dev); + locked = true; + if (pf->adev->dev.driver) { + netdev_err(netdev, "Cannot change qdisc when RDMA is active\n"); + err = -EBUSY; + goto adev_unlock; + } + } + /* setup traffic classifier for receive side */ mutex_lock(&pf->tc_mutex); err = ice_setup_tc_mqprio_qdisc(netdev, type_data); mutex_unlock(&pf->tc_mutex); + +adev_unlock: + if (locked) { + device_unlock(&pf->adev->dev); + mutex_unlock(&pf->adev_mutex); + } return err; default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 9db384f66a8e..38901d2a4680 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -195,6 +195,10 @@ struct igc_adapter { u32 qbv_config_change_errors; bool qbv_transition; unsigned int qbv_count; + /* Access to oper_gate_closed, admin_gate_closed and qbv_transition + * are protected by the qbv_tx_lock. + */ + spinlock_t qbv_tx_lock; /* OS defined structs */ struct pci_dev *pdev; diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index bdeb36790d77..6f557e843e49 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -4801,6 +4801,7 @@ static int igc_sw_init(struct igc_adapter *adapter) adapter->nfc_rule_count = 0; spin_lock_init(&adapter->stats64_lock); + spin_lock_init(&adapter->qbv_tx_lock); /* Assume MSI-X interrupts, will be checked during IRQ allocation */ adapter->flags |= IGC_FLAG_HAS_MSIX; @@ -6119,15 +6120,15 @@ static int igc_tsn_enable_launchtime(struct igc_adapter *adapter, return igc_tsn_offload_apply(adapter); } -static int igc_tsn_clear_schedule(struct igc_adapter *adapter) +static int igc_qbv_clear_schedule(struct igc_adapter *adapter) { + unsigned long flags; int i; adapter->base_time = 0; adapter->cycle_time = NSEC_PER_SEC; adapter->taprio_offload_enable = false; adapter->qbv_config_change_errors = 0; - adapter->qbv_transition = false; adapter->qbv_count = 0; for (i = 0; i < adapter->num_tx_queues; i++) { @@ -6136,10 +6137,28 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter) ring->start_time = 0; ring->end_time = NSEC_PER_SEC; ring->max_sdu = 0; + } + + spin_lock_irqsave(&adapter->qbv_tx_lock, flags); + + adapter->qbv_transition = false; + + for (i = 0; i < adapter->num_tx_queues; i++) { + struct igc_ring *ring = adapter->tx_ring[i]; + ring->oper_gate_closed = false; ring->admin_gate_closed = false; } + spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); + + return 0; +} + +static int igc_tsn_clear_schedule(struct igc_adapter *adapter) +{ + igc_qbv_clear_schedule(adapter); + return 0; } @@ -6150,6 +6169,7 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, struct igc_hw *hw = &adapter->hw; u32 start_time = 0, end_time = 0; struct timespec64 now; + unsigned long flags; size_t n; int i; @@ -6217,6 +6237,8 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, start_time += e->interval; } + spin_lock_irqsave(&adapter->qbv_tx_lock, flags); + /* Check whether a queue gets configured. * If not, set the start and end time to be end time. */ @@ -6241,6 +6263,8 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, } } + spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); + for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *ring = adapter->tx_ring[i]; struct net_device *dev = adapter->netdev; @@ -6619,8 +6643,11 @@ static enum hrtimer_restart igc_qbv_scheduling_timer(struct hrtimer *timer) { struct igc_adapter *adapter = container_of(timer, struct igc_adapter, hrtimer); + unsigned long flags; unsigned int i; + spin_lock_irqsave(&adapter->qbv_tx_lock, flags); + adapter->qbv_transition = true; for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *tx_ring = adapter->tx_ring[i]; @@ -6633,6 +6660,9 @@ static enum hrtimer_restart igc_qbv_scheduling_timer(struct hrtimer *timer) } } adapter->qbv_transition = false; + + spin_unlock_irqrestore(&adapter->qbv_tx_lock, flags); + return HRTIMER_NORESTART; } diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index 2b9335cb4bb3..8537578e1cf1 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -1302,11 +1302,10 @@ static int korina_probe(struct platform_device *pdev) else if (of_get_ethdev_address(pdev->dev.of_node, dev) < 0) eth_hw_addr_random(dev); - clk = devm_clk_get_optional(&pdev->dev, "mdioclk"); + clk = devm_clk_get_optional_enabled(&pdev->dev, "mdioclk"); if (IS_ERR(clk)) return PTR_ERR(clk); if (clk) { - clk_prepare_enable(clk); lp->mii_clock_freq = clk_get_rate(clk); } else { lp->mii_clock_freq = 200000000; /* max possible input clk */ diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c index 035ead7935c7..dab61cc1acb5 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_ctrl_mbox.c @@ -98,6 +98,9 @@ int octep_ctrl_mbox_init(struct octep_ctrl_mbox *mbox) writeq(OCTEP_CTRL_MBOX_STATUS_INIT, OCTEP_CTRL_MBOX_INFO_HOST_STATUS(mbox->barmem)); + mutex_init(&mbox->h2fq_lock); + mutex_init(&mbox->f2hq_lock); + mbox->h2fq.sz = readl(OCTEP_CTRL_MBOX_H2FQ_SZ(mbox->barmem)); mbox->h2fq.hw_prod = OCTEP_CTRL_MBOX_H2FQ_PROD(mbox->barmem); mbox->h2fq.hw_cons = OCTEP_CTRL_MBOX_H2FQ_CONS(mbox->barmem); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index f328d957b2db..35857dc19542 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -727,7 +727,8 @@ pick_fw_ver: err = request_firmware_direct(&fw->bin, fw_path, fw->dev.dev); if (err) { - if (ver_maj == PRESTERA_SUPP_FW_MAJ_VER) { + if (ver_maj != PRESTERA_PREV_FW_MAJ_VER || + ver_min != PRESTERA_PREV_FW_MIN_VER) { ver_maj = PRESTERA_PREV_FW_MAJ_VER; ver_min = PRESTERA_PREV_FW_MIN_VER; diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index a9a1028cb17b..de317179a7dc 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -166,11 +166,11 @@ prestera_util_neigh2nc_key(struct prestera_switch *sw, struct neighbour *n, static bool __prestera_fi_is_direct(struct fib_info *fi) { - struct fib_nh *fib_nh; + struct fib_nh_common *fib_nhc; if (fib_info_num_path(fi) == 1) { - fib_nh = fib_info_nh(fi, 0); - if (fib_nh->fib_nh_gw_family == AF_UNSPEC) + fib_nhc = fib_info_nhc(fi, 0); + if (fib_nhc->nhc_gw_family == AF_UNSPEC) return true; } @@ -261,7 +261,7 @@ static bool __prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr, struct net_device *dev) { - struct fib_nh *fib_nh; + struct fib_nh_common *fib_nhc; struct fib_result res; bool reachable; @@ -269,8 +269,8 @@ __prestera_util_kern_n_is_reachable_v4(u32 tb_id, __be32 *addr, if (!prestera_util_kern_get_route(&res, tb_id, addr)) if (prestera_fi_is_direct(res.fi)) { - fib_nh = fib_info_nh(res.fi, 0); - if (dev == fib_nh->fib_nh_dev) + fib_nhc = fib_info_nhc(res.fi, 0); + if (dev == fib_nhc->nhc_dev) reachable = true; } @@ -324,7 +324,7 @@ prestera_kern_fib_info_nhc(struct fib_notifier_info *info, int n) if (info->family == AF_INET) { fen4_info = container_of(info, struct fib_entry_notifier_info, info); - return &fib_info_nh(fen4_info->fi, n)->nh_common; + return fib_info_nhc(fen4_info->fi, n); } else if (info->family == AF_INET6) { fen6_info = container_of(info, struct fib6_entry_notifier_info, info); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c index b0128336ff01..e869c65d8e90 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/reporter_vnic.c @@ -2,6 +2,7 @@ /* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. */ #include "reporter_vnic.h" +#include "en_stats.h" #include "devlink.h" #define VNIC_ENV_GET64(vnic_env_stats, c) \ @@ -36,55 +37,72 @@ int mlx5_reporter_vnic_diagnose_counters(struct mlx5_core_dev *dev, if (err) return err; - err = devlink_fmsg_u64_pair_put(fmsg, "total_error_queues", - VNIC_ENV_GET64(&vnic, total_error_queues)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "send_queue_priority_update_flow", - VNIC_ENV_GET64(&vnic, send_queue_priority_update_flow)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "comp_eq_overrun", - VNIC_ENV_GET64(&vnic, comp_eq_overrun)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "async_eq_overrun", - VNIC_ENV_GET64(&vnic, async_eq_overrun)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "cq_overrun", - VNIC_ENV_GET64(&vnic, cq_overrun)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "invalid_command", - VNIC_ENV_GET64(&vnic, invalid_command)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "quota_exceeded_command", - VNIC_ENV_GET64(&vnic, quota_exceeded_command)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "nic_receive_steering_discard", - VNIC_ENV_GET64(&vnic, nic_receive_steering_discard)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "generated_pkt_steering_fail", - VNIC_ENV_GET64(&vnic, generated_pkt_steering_fail)); - if (err) - return err; - - err = devlink_fmsg_u64_pair_put(fmsg, "handled_pkt_steering_fail", - VNIC_ENV_GET64(&vnic, handled_pkt_steering_fail)); - if (err) - return err; + if (MLX5_CAP_GEN(dev, vnic_env_queue_counters)) { + err = devlink_fmsg_u32_pair_put(fmsg, "total_error_queues", + VNIC_ENV_GET(&vnic, total_error_queues)); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "send_queue_priority_update_flow", + VNIC_ENV_GET(&vnic, + send_queue_priority_update_flow)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, eq_overrun_count)) { + err = devlink_fmsg_u32_pair_put(fmsg, "comp_eq_overrun", + VNIC_ENV_GET(&vnic, comp_eq_overrun)); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "async_eq_overrun", + VNIC_ENV_GET(&vnic, async_eq_overrun)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, vnic_env_cq_overrun)) { + err = devlink_fmsg_u32_pair_put(fmsg, "cq_overrun", + VNIC_ENV_GET(&vnic, cq_overrun)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, invalid_command_count)) { + err = devlink_fmsg_u32_pair_put(fmsg, "invalid_command", + VNIC_ENV_GET(&vnic, invalid_command)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, quota_exceeded_count)) { + err = devlink_fmsg_u32_pair_put(fmsg, "quota_exceeded_command", + VNIC_ENV_GET(&vnic, quota_exceeded_command)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, nic_receive_steering_discard)) { + err = devlink_fmsg_u64_pair_put(fmsg, "nic_receive_steering_discard", + VNIC_ENV_GET64(&vnic, + nic_receive_steering_discard)); + if (err) + return err; + } + + if (MLX5_CAP_GEN(dev, vnic_env_cnt_steering_fail)) { + err = devlink_fmsg_u64_pair_put(fmsg, "generated_pkt_steering_fail", + VNIC_ENV_GET64(&vnic, + generated_pkt_steering_fail)); + if (err) + return err; + + err = devlink_fmsg_u64_pair_put(fmsg, "handled_pkt_steering_fail", + VNIC_ENV_GET64(&vnic, handled_pkt_steering_fail)); + if (err) + return err; + } err = devlink_fmsg_obj_nest_end(fmsg); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c index f0c3464f037f..1730f6a716ee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c @@ -1030,9 +1030,6 @@ int mlx5e_tc_tun_encap_dests_set(struct mlx5e_priv *priv, int out_index; int err = 0; - if (!mlx5e_is_eswitch_flow(flow)) - return 0; - parse_attr = attr->parse_attr; esw_attr = attr->esw_attr; *vf_tun = false; @@ -1464,10 +1461,12 @@ static void mlx5e_invalidate_encap(struct mlx5e_priv *priv, attr = mlx5e_tc_get_encap_attr(flow); esw_attr = attr->esw_attr; - if (flow_flag_test(flow, SLOW)) + if (flow_flag_test(flow, SLOW)) { mlx5e_tc_unoffload_from_slow_path(esw, flow); - else + } else { mlx5e_tc_unoffload_fdb_rules(esw, flow, flow->attr); + mlx5e_tc_unoffload_flow_post_acts(flow); + } mlx5e_tc_detach_mod_hdr(priv, flow, attr); attr->modify_hdr = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c index d97e6df66f45..b8dd74453655 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c @@ -323,8 +323,11 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq, net_prefetch(mxbuf->xdp.data); prog = rcu_dereference(rq->xdp_prog); - if (likely(prog && mlx5e_xdp_handle(rq, prog, mxbuf))) + if (likely(prog && mlx5e_xdp_handle(rq, prog, mxbuf))) { + if (likely(__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags))) + wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); return NULL; /* page/packet was consumed by XDP */ + } /* XDP_PASS: copy the data from the UMEM to a new SKB. The frame reuse * will be handled by mlx5e_free_rx_wqe. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c index dbe87bf89c0d..832d36be4a17 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c @@ -808,9 +808,9 @@ static void setup_fte_upper_proto_match(struct mlx5_flow_spec *spec, struct upsp } if (upspec->sport) { - MLX5_SET(fte_match_set_lyr_2_4, spec->match_criteria, udp_dport, + MLX5_SET(fte_match_set_lyr_2_4, spec->match_criteria, udp_sport, upspec->sport_mask); - MLX5_SET(fte_match_set_lyr_2_4, spec->match_value, udp_dport, upspec->sport); + MLX5_SET(fte_match_set_lyr_2_4, spec->match_value, udp_sport, upspec->sport); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c index eab5bc718771..8d995e304869 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c @@ -58,7 +58,9 @@ static int mlx5e_ipsec_remove_trailer(struct sk_buff *skb, struct xfrm_state *x) trailer_len = alen + plen + 2; - pskb_trim(skb, skb->len - trailer_len); + ret = pskb_trim(skb, skb->len - trailer_len); + if (unlikely(ret)) + return ret; if (skb->protocol == htons(ETH_P_IP)) { ipv4hdr->tot_len = htons(ntohs(ipv4hdr->tot_len) - trailer_len); ip_send_check(ipv4hdr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c index cf704f106b7c..984fa04bd331 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.c @@ -188,7 +188,6 @@ static void mlx5e_tls_debugfs_init(struct mlx5e_tls *tls, int mlx5e_ktls_init(struct mlx5e_priv *priv) { - struct mlx5_crypto_dek_pool *dek_pool; struct mlx5e_tls *tls; if (!mlx5e_is_ktls_device(priv->mdev)) @@ -199,12 +198,6 @@ int mlx5e_ktls_init(struct mlx5e_priv *priv) return -ENOMEM; tls->mdev = priv->mdev; - dek_pool = mlx5_crypto_dek_pool_create(priv->mdev, MLX5_ACCEL_OBJ_TLS_KEY); - if (IS_ERR(dek_pool)) { - kfree(tls); - return PTR_ERR(dek_pool); - } - tls->dek_pool = dek_pool; priv->tls = tls; mlx5e_tls_debugfs_init(tls, priv->dfs_root); @@ -222,7 +215,6 @@ void mlx5e_ktls_cleanup(struct mlx5e_priv *priv) debugfs_remove_recursive(tls->debugfs.dfs); tls->debugfs.dfs = NULL; - mlx5_crypto_dek_pool_destroy(tls->dek_pool); kfree(priv->tls); priv->tls = NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index efb2cf74ad6a..d61be26a4df1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -908,28 +908,51 @@ static void mlx5e_tls_tx_debugfs_init(struct mlx5e_tls *tls, int mlx5e_ktls_init_tx(struct mlx5e_priv *priv) { + struct mlx5_crypto_dek_pool *dek_pool; struct mlx5e_tls *tls = priv->tls; + int err; + + if (!mlx5e_is_ktls_device(priv->mdev)) + return 0; + + /* DEK pool could be used by either or both of TX and RX. But we have to + * put the creation here to avoid syndrome when doing devlink reload. + */ + dek_pool = mlx5_crypto_dek_pool_create(priv->mdev, MLX5_ACCEL_OBJ_TLS_KEY); + if (IS_ERR(dek_pool)) + return PTR_ERR(dek_pool); + tls->dek_pool = dek_pool; if (!mlx5e_is_ktls_tx(priv->mdev)) return 0; priv->tls->tx_pool = mlx5e_tls_tx_pool_init(priv->mdev, &priv->tls->sw_stats); - if (!priv->tls->tx_pool) - return -ENOMEM; + if (!priv->tls->tx_pool) { + err = -ENOMEM; + goto err_tx_pool_init; + } mlx5e_tls_tx_debugfs_init(tls, tls->debugfs.dfs); return 0; + +err_tx_pool_init: + mlx5_crypto_dek_pool_destroy(dek_pool); + return err; } void mlx5e_ktls_cleanup_tx(struct mlx5e_priv *priv) { if (!mlx5e_is_ktls_tx(priv->mdev)) - return; + goto dek_pool_destroy; debugfs_remove_recursive(priv->tls->debugfs.dfs_tx); priv->tls->debugfs.dfs_tx = NULL; mlx5e_tls_tx_pool_cleanup(priv->tls->tx_pool); priv->tls->tx_pool = NULL; + +dek_pool_destroy: + if (mlx5e_is_ktls_device(priv->mdev)) + mlx5_crypto_dek_pool_destroy(priv->tls->dek_pool); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c index 7fc901a6ec5f..414e28584881 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec_fs.c @@ -161,6 +161,7 @@ static int macsec_fs_tx_create_crypto_table_groups(struct mlx5e_flow_table *ft) if (!in) { kfree(ft->g); + ft->g = NULL; return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 933a7772a7a3..5aa51d74f8b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -135,6 +135,16 @@ static void arfs_del_rules(struct mlx5e_flow_steering *fs); int mlx5e_arfs_disable(struct mlx5e_flow_steering *fs) { + /* Moving to switchdev mode, fs->arfs is freed by mlx5e_nic_profile + * cleanup_rx callback and it is not recreated when + * mlx5e_uplink_rep_profile is loaded as mlx5e_create_flow_steering() + * is not called by the uplink_rep profile init_rx callback. Thus, if + * ntuple is set, moving to switchdev flow will enter this function + * with fs->arfs nullified. + */ + if (!mlx5e_fs_get_arfs(fs)) + return 0; + arfs_del_rules(fs); return arfs_disable(fs); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index defb1efccb78..c27df14df145 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1036,7 +1036,23 @@ static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_s return err; } -static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state) +static void mlx5e_flush_rq_cq(struct mlx5e_rq *rq) +{ + struct mlx5_cqwq *cqwq = &rq->cq.wq; + struct mlx5_cqe64 *cqe; + + if (test_bit(MLX5E_RQ_STATE_MINI_CQE_ENHANCED, &rq->state)) { + while ((cqe = mlx5_cqwq_get_cqe_enahnced_comp(cqwq))) + mlx5_cqwq_pop(cqwq); + } else { + while ((cqe = mlx5_cqwq_get_cqe(cqwq))) + mlx5_cqwq_pop(cqwq); + } + + mlx5_cqwq_update_db_record(cqwq); +} + +int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state) { struct net_device *dev = rq->netdev; int err; @@ -1046,6 +1062,10 @@ static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state) netdev_err(dev, "Failed to move rq 0x%x to reset\n", rq->rqn); return err; } + + mlx5e_free_rx_descs(rq); + mlx5e_flush_rq_cq(rq); + err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); if (err) { netdev_err(dev, "Failed to move rq 0x%x to ready\n", rq->rqn); @@ -1055,13 +1075,6 @@ static int mlx5e_rq_to_ready(struct mlx5e_rq *rq, int curr_state) return 0; } -int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state) -{ - mlx5e_free_rx_descs(rq); - - return mlx5e_rq_to_ready(rq, curr_state); -} - static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) { struct mlx5_core_dev *mdev = rq->mdev; @@ -5253,6 +5266,7 @@ void mlx5e_destroy_q_counters(struct mlx5e_priv *priv) static int mlx5e_nic_init(struct mlx5_core_dev *mdev, struct net_device *netdev) { + const bool take_rtnl = netdev->reg_state == NETREG_REGISTERED; struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_flow_steering *fs; int err; @@ -5281,9 +5295,19 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, mlx5_core_err(mdev, "TLS initialization failed, %d\n", err); mlx5e_health_create_reporters(priv); + + /* If netdev is already registered (e.g. move from uplink to nic profile), + * RTNL lock must be held before triggering netdev notifiers. + */ + if (take_rtnl) + rtnl_lock(); + /* update XDP supported features */ mlx5e_set_xdp_feature(netdev); + if (take_rtnl) + rtnl_unlock(); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 152b62138450..99b3843396f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1012,7 +1012,7 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) err = mlx5e_open_drop_rq(priv, &priv->drop_rq); if (err) { mlx5_core_err(mdev, "open drop rq failed, %d\n", err); - return err; + goto err_rx_res_free; } err = mlx5e_rx_res_init(priv->rx_res, priv->mdev, 0, @@ -1046,6 +1046,7 @@ err_destroy_rx_res: mlx5e_rx_res_destroy(priv->rx_res); err_close_drop_rq: mlx5e_close_drop_rq(&priv->drop_rq); +err_rx_res_free: mlx5e_rx_res_free(priv->rx_res); priv->rx_res = NULL; err_free_fs: @@ -1159,6 +1160,10 @@ static int mlx5e_init_rep_tx(struct mlx5e_priv *priv) return err; } + err = mlx5e_rep_neigh_init(rpriv); + if (err) + goto err_neigh_init; + if (rpriv->rep->vport == MLX5_VPORT_UPLINK) { err = mlx5e_init_uplink_rep_tx(rpriv); if (err) @@ -1175,6 +1180,8 @@ err_ht_init: if (rpriv->rep->vport == MLX5_VPORT_UPLINK) mlx5e_cleanup_uplink_rep_tx(rpriv); err_init_tx: + mlx5e_rep_neigh_cleanup(rpriv); +err_neigh_init: mlx5e_destroy_tises(priv); return err; } @@ -1188,22 +1195,17 @@ static void mlx5e_cleanup_rep_tx(struct mlx5e_priv *priv) if (rpriv->rep->vport == MLX5_VPORT_UPLINK) mlx5e_cleanup_uplink_rep_tx(rpriv); + mlx5e_rep_neigh_cleanup(rpriv); mlx5e_destroy_tises(priv); } static void mlx5e_rep_enable(struct mlx5e_priv *priv) { - struct mlx5e_rep_priv *rpriv = priv->ppriv; - mlx5e_set_netdev_mtu_boundaries(priv); - mlx5e_rep_neigh_init(rpriv); } static void mlx5e_rep_disable(struct mlx5e_priv *priv) { - struct mlx5e_rep_priv *rpriv = priv->ppriv; - - mlx5e_rep_neigh_cleanup(rpriv); } static int mlx5e_update_rep_rx(struct mlx5e_priv *priv) @@ -1253,7 +1255,6 @@ static int uplink_rep_async_event(struct notifier_block *nb, unsigned long event static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv) { - struct mlx5e_rep_priv *rpriv = priv->ppriv; struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; u16 max_mtu; @@ -1275,7 +1276,6 @@ static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv) mlx5_notifier_register(mdev, &priv->events_nb); mlx5e_dcbnl_initialize(priv); mlx5e_dcbnl_init_app(priv); - mlx5e_rep_neigh_init(rpriv); mlx5e_rep_bridge_init(priv); netdev->wanted_features |= NETIF_F_HW_TC; @@ -1290,7 +1290,6 @@ static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv) static void mlx5e_uplink_rep_disable(struct mlx5e_priv *priv) { - struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_core_dev *mdev = priv->mdev; rtnl_lock(); @@ -1300,7 +1299,6 @@ static void mlx5e_uplink_rep_disable(struct mlx5e_priv *priv) rtnl_unlock(); mlx5e_rep_bridge_cleanup(priv); - mlx5e_rep_neigh_cleanup(rpriv); mlx5e_dcbnl_delete_app(priv); mlx5_notifier_unregister(mdev, &priv->events_nb); mlx5e_rep_tc_disable(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 8d0a3f69693e..31708d5aa608 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1725,6 +1725,19 @@ verify_attr_actions(u32 actions, struct netlink_ext_ack *extack) return 0; } +static bool +has_encap_dests(struct mlx5_flow_attr *attr) +{ + struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr; + int out_index; + + for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) + if (esw_attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP) + return true; + + return false; +} + static int post_process_attr(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr, @@ -1737,9 +1750,11 @@ post_process_attr(struct mlx5e_tc_flow *flow, if (err) goto err_out; - err = mlx5e_tc_tun_encap_dests_set(flow->priv, flow, attr, extack, &vf_tun); - if (err) - goto err_out; + if (mlx5e_is_eswitch_flow(flow) && has_encap_dests(attr)) { + err = mlx5e_tc_tun_encap_dests_set(flow->priv, flow, attr, extack, &vf_tun); + if (err) + goto err_out; + } if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { err = mlx5e_tc_attach_mod_hdr(flow->priv, flow, attr); @@ -1928,9 +1943,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_flow_attr *attr = flow->attr; - struct mlx5_esw_flow_attr *esw_attr; - esw_attr = attr->esw_attr; mlx5e_put_flow_tunnel_id(flow); remove_unready_flow(flow); @@ -1951,12 +1964,6 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, mlx5_tc_ct_match_del(get_ct_priv(priv), &flow->attr->ct_attr); - if (esw_attr->int_port) - mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(priv), esw_attr->int_port); - - if (esw_attr->dest_int_port) - mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(priv), esw_attr->dest_int_port); - if (flow_flag_test(flow, L3_TO_L2_DECAP)) mlx5e_detach_decap(priv, flow); @@ -4253,6 +4260,7 @@ static void mlx5_free_flow_attr_actions(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *attr) { struct mlx5_core_dev *counter_dev = get_flow_counter_dev(flow); + struct mlx5_esw_flow_attr *esw_attr; if (!attr) return; @@ -4270,6 +4278,18 @@ mlx5_free_flow_attr_actions(struct mlx5e_tc_flow *flow, struct mlx5_flow_attr *a mlx5e_tc_detach_mod_hdr(flow->priv, flow, attr); } + if (mlx5e_is_eswitch_flow(flow)) { + esw_attr = attr->esw_attr; + + if (esw_attr->int_port) + mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(flow->priv), + esw_attr->int_port); + + if (esw_attr->dest_int_port) + mlx5e_tc_int_port_put(mlx5e_get_int_port_priv(flow->priv), + esw_attr->dest_int_port); + } + mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), attr); free_branch_attr(flow, attr->branch_true); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_debugfs.c index b6a45eff28f5..dbd7cbe6cbf3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge_debugfs.c @@ -64,7 +64,7 @@ void mlx5_esw_bridge_debugfs_init(struct net_device *br_netdev, struct mlx5_esw_ bridge->debugfs_dir = debugfs_create_dir(br_netdev->name, bridge->br_offloads->debugfs_root); - debugfs_create_file("fdb", 0444, bridge->debugfs_dir, bridge, + debugfs_create_file("fdb", 0400, bridge->debugfs_dir, bridge, &mlx5_esw_bridge_debugfs_fops); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c index af779c700278..fdf2be548e85 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c @@ -60,7 +60,7 @@ static struct devlink_port *mlx5_esw_dl_port_alloc(struct mlx5_eswitch *esw, u16 } else if (mlx5_core_is_ec_vf_vport(esw->dev, vport_num)) { memcpy(dl_port->attrs.switch_id.id, ppid.id, ppid.id_len); dl_port->attrs.switch_id.id_len = ppid.id_len; - devlink_port_attrs_pci_vf_set(dl_port, controller_num, pfnum, + devlink_port_attrs_pci_vf_set(dl_port, 0, pfnum, vport_num - 1, false); } return dl_port; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index bdfe609cc9ec..e59380ee1ead 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -1436,7 +1436,6 @@ esw_chains_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *miss_fdb) esw_init_chains_offload_flags(esw, &attr.flags); attr.ns = MLX5_FLOW_NAMESPACE_FDB; - attr.fs_base_prio = FDB_TC_OFFLOAD; attr.max_grp_num = esw->params.large_group_num; attr.default_ft = miss_fdb; attr.mapping = esw->offloads.reg_c0_obj_pool; @@ -2779,9 +2778,9 @@ static int mlx5_esw_offloads_set_ns_peer(struct mlx5_eswitch *esw, struct mlx5_eswitch *peer_esw, bool pair) { - u8 peer_idx = mlx5_get_dev_index(peer_esw->dev); + u16 peer_vhca_id = MLX5_CAP_GEN(peer_esw->dev, vhca_id); + u16 vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); struct mlx5_flow_root_namespace *peer_ns; - u8 idx = mlx5_get_dev_index(esw->dev); struct mlx5_flow_root_namespace *ns; int err; @@ -2789,18 +2788,18 @@ static int mlx5_esw_offloads_set_ns_peer(struct mlx5_eswitch *esw, ns = esw->dev->priv.steering->fdb_root_ns; if (pair) { - err = mlx5_flow_namespace_set_peer(ns, peer_ns, peer_idx); + err = mlx5_flow_namespace_set_peer(ns, peer_ns, peer_vhca_id); if (err) return err; - err = mlx5_flow_namespace_set_peer(peer_ns, ns, idx); + err = mlx5_flow_namespace_set_peer(peer_ns, ns, vhca_id); if (err) { - mlx5_flow_namespace_set_peer(ns, NULL, peer_idx); + mlx5_flow_namespace_set_peer(ns, NULL, peer_vhca_id); return err; } } else { - mlx5_flow_namespace_set_peer(ns, NULL, peer_idx); - mlx5_flow_namespace_set_peer(peer_ns, NULL, idx); + mlx5_flow_namespace_set_peer(ns, NULL, peer_vhca_id); + mlx5_flow_namespace_set_peer(peer_ns, NULL, vhca_id); } return 0; @@ -4196,7 +4195,7 @@ int mlx5_devlink_port_fn_migratable_set(struct devlink_port *port, bool enable, } hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); - MLX5_SET(cmd_hca_cap_2, hca_caps, migratable, 1); + MLX5_SET(cmd_hca_cap_2, hca_caps, migratable, enable); err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport->vport, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE2); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 91dcb0dcad10..aab7059bf6e9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -140,7 +140,7 @@ static void mlx5_cmd_stub_modify_header_dealloc(struct mlx5_flow_root_namespace static int mlx5_cmd_stub_set_peer(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_root_namespace *peer_ns, - u8 peer_idx) + u16 peer_vhca_id) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index b6b9a5a20591..7790ae5531e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -94,7 +94,7 @@ struct mlx5_flow_cmds { int (*set_peer)(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_root_namespace *peer_ns, - u8 peer_idx); + u16 peer_vhca_id); int (*create_ns)(struct mlx5_flow_root_namespace *ns); int (*destroy_ns)(struct mlx5_flow_root_namespace *ns); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 4ef04aa28771..6b069fa411c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -889,7 +889,7 @@ static struct mlx5_flow_table *find_closest_ft_recursive(struct fs_node *root, struct fs_node *iter = list_entry(start, struct fs_node, list); struct mlx5_flow_table *ft = NULL; - if (!root || root->type == FS_TYPE_PRIO_CHAINS) + if (!root) return NULL; list_for_each_advance_continue(iter, &root->children, reverse) { @@ -905,20 +905,42 @@ static struct mlx5_flow_table *find_closest_ft_recursive(struct fs_node *root, return ft; } -/* If reverse is false then return the first flow table in next priority of - * prio in the tree, else return the last flow table in the previous priority - * of prio in the tree. +static struct fs_node *find_prio_chains_parent(struct fs_node *parent, + struct fs_node **child) +{ + struct fs_node *node = NULL; + + while (parent && parent->type != FS_TYPE_PRIO_CHAINS) { + node = parent; + parent = parent->parent; + } + + if (child) + *child = node; + + return parent; +} + +/* If reverse is false then return the first flow table next to the passed node + * in the tree, else return the last flow table before the node in the tree. + * If skip is true, skip the flow tables in the same prio_chains prio. */ -static struct mlx5_flow_table *find_closest_ft(struct fs_prio *prio, bool reverse) +static struct mlx5_flow_table *find_closest_ft(struct fs_node *node, bool reverse, + bool skip) { + struct fs_node *prio_chains_parent = NULL; struct mlx5_flow_table *ft = NULL; struct fs_node *curr_node; struct fs_node *parent; - parent = prio->node.parent; - curr_node = &prio->node; + if (skip) + prio_chains_parent = find_prio_chains_parent(node, NULL); + parent = node->parent; + curr_node = node; while (!ft && parent) { - ft = find_closest_ft_recursive(parent, &curr_node->list, reverse); + if (parent != prio_chains_parent) + ft = find_closest_ft_recursive(parent, &curr_node->list, + reverse); curr_node = parent; parent = curr_node->parent; } @@ -926,15 +948,15 @@ static struct mlx5_flow_table *find_closest_ft(struct fs_prio *prio, bool revers } /* Assuming all the tree is locked by mutex chain lock */ -static struct mlx5_flow_table *find_next_chained_ft(struct fs_prio *prio) +static struct mlx5_flow_table *find_next_chained_ft(struct fs_node *node) { - return find_closest_ft(prio, false); + return find_closest_ft(node, false, true); } /* Assuming all the tree is locked by mutex chain lock */ -static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio) +static struct mlx5_flow_table *find_prev_chained_ft(struct fs_node *node) { - return find_closest_ft(prio, true); + return find_closest_ft(node, true, true); } static struct mlx5_flow_table *find_next_fwd_ft(struct mlx5_flow_table *ft, @@ -946,7 +968,7 @@ static struct mlx5_flow_table *find_next_fwd_ft(struct mlx5_flow_table *ft, next_ns = flow_act->action & MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_NS; fs_get_obj(prio, next_ns ? ft->ns->node.parent : ft->node.parent); - return find_next_chained_ft(prio); + return find_next_chained_ft(&prio->node); } static int connect_fts_in_prio(struct mlx5_core_dev *dev, @@ -970,21 +992,55 @@ static int connect_fts_in_prio(struct mlx5_core_dev *dev, return 0; } +static struct mlx5_flow_table *find_closet_ft_prio_chains(struct fs_node *node, + struct fs_node *parent, + struct fs_node **child, + bool reverse) +{ + struct mlx5_flow_table *ft; + + ft = find_closest_ft(node, reverse, false); + + if (ft && parent == find_prio_chains_parent(&ft->node, child)) + return ft; + + return NULL; +} + /* Connect flow tables from previous priority of prio to ft */ static int connect_prev_fts(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, struct fs_prio *prio) { + struct fs_node *prio_parent, *parent = NULL, *child, *node; struct mlx5_flow_table *prev_ft; + int err = 0; + + prio_parent = find_prio_chains_parent(&prio->node, &child); + + /* return directly if not under the first sub ns of prio_chains prio */ + if (prio_parent && !list_is_first(&child->list, &prio_parent->children)) + return 0; - prev_ft = find_prev_chained_ft(prio); - if (prev_ft) { + prev_ft = find_prev_chained_ft(&prio->node); + while (prev_ft) { struct fs_prio *prev_prio; fs_get_obj(prev_prio, prev_ft->node.parent); - return connect_fts_in_prio(dev, prev_prio, ft); + err = connect_fts_in_prio(dev, prev_prio, ft); + if (err) + break; + + if (!parent) { + parent = find_prio_chains_parent(&prev_prio->node, &child); + if (!parent) + break; + } + + node = child; + prev_ft = find_closet_ft_prio_chains(node, parent, &child, true); } - return 0; + return err; } static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio @@ -1123,7 +1179,7 @@ static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table if (err) return err; - next_ft = first_ft ? first_ft : find_next_chained_ft(prio); + next_ft = first_ft ? first_ft : find_next_chained_ft(&prio->node); err = connect_fwd_rules(dev, ft, next_ft); if (err) return err; @@ -1198,7 +1254,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa tree_init_node(&ft->node, del_hw_flow_table, del_sw_flow_table); next_ft = unmanaged ? ft_attr->next_ft : - find_next_chained_ft(fs_prio); + find_next_chained_ft(&fs_prio->node); ft->def_miss_action = ns->def_miss_action; ft->ns = ns; err = root->cmds->create_flow_table(root, ft, ft_attr, next_ft); @@ -2195,13 +2251,20 @@ EXPORT_SYMBOL(mlx5_del_flow_rules); /* Assuming prio->node.children(flow tables) is sorted by level */ static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft) { + struct fs_node *prio_parent, *child; struct fs_prio *prio; fs_get_obj(prio, ft->node.parent); if (!list_is_last(&ft->node.list, &prio->node.children)) return list_next_entry(ft, node.list); - return find_next_chained_ft(prio); + + prio_parent = find_prio_chains_parent(&prio->node, &child); + + if (prio_parent && list_is_first(&child->list, &prio_parent->children)) + return find_closest_ft(&prio->node, false, false); + + return find_next_chained_ft(&prio->node); } static int update_root_ft_destroy(struct mlx5_flow_table *ft) @@ -3621,7 +3684,7 @@ void mlx5_destroy_match_definer(struct mlx5_core_dev *dev, int mlx5_flow_namespace_set_peer(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_root_namespace *peer_ns, - u8 peer_idx) + u16 peer_vhca_id) { if (peer_ns && ns->mode != peer_ns->mode) { mlx5_core_err(ns->dev, @@ -3629,7 +3692,7 @@ int mlx5_flow_namespace_set_peer(struct mlx5_flow_root_namespace *ns, return -EINVAL; } - return ns->cmds->set_peer(ns, peer_ns, peer_idx); + return ns->cmds->set_peer(ns, peer_ns, peer_vhca_id); } /* This function should be called only at init stage of the namespace. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 03e64c4c245d..4aed1768b85f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -303,7 +303,7 @@ const struct mlx5_flow_cmds *mlx5_fs_cmd_get_fw_cmds(void); int mlx5_flow_namespace_set_peer(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_root_namespace *peer_ns, - u8 peer_idx); + u16 peer_vhca_id); int mlx5_flow_namespace_set_mode(struct mlx5_flow_namespace *ns, enum mlx5_flow_steering_mode mode); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c index d3a3fe4ce670..7d9bbb494d95 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/port_sel.c @@ -574,7 +574,7 @@ static int __mlx5_lag_modify_definers_destinations(struct mlx5_lag *ldev, for (i = 0; i < ldev->ports; i++) { for (j = 0; j < ldev->buckets; j++) { idx = i * ldev->buckets + j; - if (ldev->v2p_map[i] == ports[i]) + if (ldev->v2p_map[idx] == ports[idx]) continue; dest.vport.vhca_id = MLX5_CAP_GEN(ldev->pf[ports[idx] - 1].dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index 973babfaff25..377372f0578a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -227,10 +227,15 @@ static void mlx5_timestamp_overflow(struct work_struct *work) clock = container_of(timer, struct mlx5_clock, timer); mdev = container_of(clock, struct mlx5_core_dev, clock); + if (mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) + goto out; + write_seqlock_irqsave(&clock->lock, flags); timecounter_read(&timer->tc); mlx5_update_clock_info_page(mdev); write_sequnlock_irqrestore(&clock->lock, flags); + +out: schedule_delayed_work(&timer->overflow_work, timer->overflow_period); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c index db9df9798ffa..a80ecb672f33 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c @@ -178,7 +178,7 @@ mlx5_chains_create_table(struct mlx5_fs_chains *chains, if (!mlx5_chains_ignore_flow_level_supported(chains) || (chain == 0 && prio == 1 && level == 0)) { ft_attr.level = chains->fs_base_level; - ft_attr.prio = chains->fs_base_prio; + ft_attr.prio = chains->fs_base_prio + prio - 1; ns = (chains->ns == MLX5_FLOW_NAMESPACE_FDB) ? mlx5_get_fdb_sub_ns(chains->dev, chain) : mlx5_get_flow_namespace(chains->dev, chains->ns); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 88dbea6631d5..72ae560a1c68 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1506,6 +1506,7 @@ void mlx5_uninit_one(struct mlx5_core_dev *dev) if (!test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) { mlx5_core_warn(dev, "%s: interface is down, NOP\n", __func__); + mlx5_devlink_params_unregister(priv_to_devlink(dev)); mlx5_cleanup_once(dev); goto out; } @@ -1988,7 +1989,7 @@ static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev, mlx5_enter_error_state(dev, false); mlx5_error_sw_reset(dev); - mlx5_unload_one(dev, true); + mlx5_unload_one(dev, false); mlx5_drain_health_wq(dev); mlx5_pci_disable_device(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index c4be257c043d..682d3dc00dd1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -361,7 +361,7 @@ static inline bool mlx5_core_is_ec_vf_vport(const struct mlx5_core_dev *dev, u16 static inline int mlx5_vport_to_func_id(const struct mlx5_core_dev *dev, u16 vport, bool ec_vf_func) { - return ec_vf_func ? vport - mlx5_core_ec_vf_vport_base(dev) + return ec_vf_func ? vport - mlx5_core_ec_vf_vport_base(dev) + 1 : vport; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 4e42a3b9b8ee..a2fc937d5461 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -285,8 +285,7 @@ static u16 mlx5_get_max_vfs(struct mlx5_core_dev *dev) host_total_vfs = MLX5_GET(query_esw_functions_out, out, host_params_context.host_total_vfs); kvfree(out); - if (host_total_vfs) - return host_total_vfs; + return host_total_vfs; } done: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c index e739ec6cdf90..54bb0866ed72 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -2079,7 +2079,7 @@ mlx5dr_action_create_dest_vport(struct mlx5dr_domain *dmn, peer_vport = vhca_id_valid && mlx5_core_is_pf(dmn->mdev) && (vhca_id != dmn->info.caps.gvmi); - vport_dmn = peer_vport ? dmn->peer_dmn[vhca_id] : dmn; + vport_dmn = peer_vport ? xa_load(&dmn->peer_dmn_xa, vhca_id) : dmn; if (!vport_dmn) { mlx5dr_dbg(dmn, "No peer vport domain for given vhca_id\n"); return NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index 7491911ebcb5..8c2a34a0d6be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -564,11 +564,12 @@ int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev, err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out)); if (err) - return err; + goto err_free_in; *reformat_id = MLX5_GET(alloc_packet_reformat_context_out, out, packet_reformat_id); - kvfree(in); +err_free_in: + kvfree(in); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c index 75dc85dc24ef..3d74109f8230 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c @@ -475,6 +475,7 @@ mlx5dr_domain_create(struct mlx5_core_dev *mdev, enum mlx5dr_domain_type type) mutex_init(&dmn->info.rx.mutex); mutex_init(&dmn->info.tx.mutex); xa_init(&dmn->definers_xa); + xa_init(&dmn->peer_dmn_xa); if (dr_domain_caps_init(mdev, dmn)) { mlx5dr_err(dmn, "Failed init domain, no caps\n"); @@ -507,6 +508,7 @@ mlx5dr_domain_create(struct mlx5_core_dev *mdev, enum mlx5dr_domain_type type) uninit_caps: dr_domain_caps_uninit(dmn); def_xa_destroy: + xa_destroy(&dmn->peer_dmn_xa); xa_destroy(&dmn->definers_xa); kfree(dmn); return NULL; @@ -547,6 +549,7 @@ int mlx5dr_domain_destroy(struct mlx5dr_domain *dmn) dr_domain_uninit_csum_recalc_fts(dmn); dr_domain_uninit_resources(dmn); dr_domain_caps_uninit(dmn); + xa_destroy(&dmn->peer_dmn_xa); xa_destroy(&dmn->definers_xa); mutex_destroy(&dmn->info.tx.mutex); mutex_destroy(&dmn->info.rx.mutex); @@ -556,17 +559,21 @@ int mlx5dr_domain_destroy(struct mlx5dr_domain *dmn) void mlx5dr_domain_set_peer(struct mlx5dr_domain *dmn, struct mlx5dr_domain *peer_dmn, - u8 peer_idx) + u16 peer_vhca_id) { + struct mlx5dr_domain *peer; + mlx5dr_domain_lock(dmn); - if (dmn->peer_dmn[peer_idx]) - refcount_dec(&dmn->peer_dmn[peer_idx]->refcount); + peer = xa_load(&dmn->peer_dmn_xa, peer_vhca_id); + if (peer) + refcount_dec(&peer->refcount); - dmn->peer_dmn[peer_idx] = peer_dmn; + WARN_ON(xa_err(xa_store(&dmn->peer_dmn_xa, peer_vhca_id, peer_dmn, GFP_KERNEL))); - if (dmn->peer_dmn[peer_idx]) - refcount_inc(&dmn->peer_dmn[peer_idx]->refcount); + peer = xa_load(&dmn->peer_dmn_xa, peer_vhca_id); + if (peer) + refcount_inc(&peer->refcount); mlx5dr_domain_unlock(dmn); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ptrn.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ptrn.c index d6947fe13d56..8ca534ef5d03 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ptrn.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ptrn.c @@ -82,7 +82,7 @@ dr_ptrn_alloc_pattern(struct mlx5dr_ptrn_mgr *mgr, u32 chunk_size; u32 index; - chunk_size = ilog2(num_of_actions); + chunk_size = ilog2(roundup_pow_of_two(num_of_actions)); /* HW modify action index granularity is at least 64B */ chunk_size = max_t(u32, chunk_size, DR_CHUNK_SIZE_8); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c index 69d7a8f3c402..f708b029425a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c @@ -1652,17 +1652,18 @@ dr_ste_v0_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value, struct mlx5dr_domain *dmn = sb->dmn; struct mlx5dr_domain *vport_dmn; u8 *bit_mask = sb->bit_mask; + struct mlx5dr_domain *peer; bool source_gvmi_set; DR_STE_SET_TAG(src_gvmi_qp, tag, source_qp, misc, source_sqn); if (sb->vhca_id_valid) { + peer = xa_load(&dmn->peer_dmn_xa, id); /* Find port GVMI based on the eswitch_owner_vhca_id */ if (id == dmn->info.caps.gvmi) vport_dmn = dmn; - else if (id < MLX5_MAX_PORTS && dmn->peer_dmn[id] && - (id == dmn->peer_dmn[id]->info.caps.gvmi)) - vport_dmn = dmn->peer_dmn[id]; + else if (peer && (id == peer->info.caps.gvmi)) + vport_dmn = peer; else return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c index f4ef0b22b991..dd856cde188d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v1.c @@ -1984,16 +1984,17 @@ static int dr_ste_v1_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value, struct mlx5dr_domain *dmn = sb->dmn; struct mlx5dr_domain *vport_dmn; u8 *bit_mask = sb->bit_mask; + struct mlx5dr_domain *peer; DR_STE_SET_TAG(src_gvmi_qp_v1, tag, source_qp, misc, source_sqn); if (sb->vhca_id_valid) { + peer = xa_load(&dmn->peer_dmn_xa, id); /* Find port GVMI based on the eswitch_owner_vhca_id */ if (id == dmn->info.caps.gvmi) vport_dmn = dmn; - else if (id < MLX5_MAX_PORTS && dmn->peer_dmn[id] && - (id == dmn->peer_dmn[id]->info.caps.gvmi)) - vport_dmn = dmn->peer_dmn[id]; + else if (peer && (id == peer->info.caps.gvmi)) + vport_dmn = peer; else return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index 1622dbbe6b97..6c59de3e28f6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -935,7 +935,6 @@ struct mlx5dr_domain_info { }; struct mlx5dr_domain { - struct mlx5dr_domain *peer_dmn[MLX5_MAX_PORTS]; struct mlx5_core_dev *mdev; u32 pdn; struct mlx5_uars_page *uar; @@ -956,6 +955,7 @@ struct mlx5dr_domain { struct list_head dbg_tbl_list; struct mlx5dr_dbg_dump_info dump_info; struct xarray definers_xa; + struct xarray peer_dmn_xa; /* memory management statistics */ u32 num_buddies[DR_ICM_TYPE_MAX]; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c index 6aac5f006bf8..feb307fb3440 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c @@ -781,14 +781,14 @@ restore_fte: static int mlx5_cmd_dr_set_peer(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_root_namespace *peer_ns, - u8 peer_idx) + u16 peer_vhca_id) { struct mlx5dr_domain *peer_domain = NULL; if (peer_ns) peer_domain = peer_ns->fs_dr_domain.dr_domain; mlx5dr_domain_set_peer(ns->fs_dr_domain.dr_domain, - peer_domain, peer_idx); + peer_domain, peer_vhca_id); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index 24cbb33ecd6c..89fced86936f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -49,7 +49,7 @@ int mlx5dr_domain_sync(struct mlx5dr_domain *domain, u32 flags); void mlx5dr_domain_set_peer(struct mlx5dr_domain *dmn, struct mlx5dr_domain *peer_dmn, - u8 peer_idx); + u16 peer_vhca_id); struct mlx5dr_table * mlx5dr_table_create(struct mlx5dr_domain *domain, u32 level, u32 flags, diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index a499e460594b..c2ad0921e893 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -8,6 +8,7 @@ #include <linux/ethtool.h> #include <linux/filter.h> #include <linux/mm.h> +#include <linux/pci.h> #include <net/checksum.h> #include <net/ip6_checksum.h> @@ -2345,9 +2346,12 @@ int mana_attach(struct net_device *ndev) static int mana_dealloc_queues(struct net_device *ndev) { struct mana_port_context *apc = netdev_priv(ndev); + unsigned long timeout = jiffies + 120 * HZ; struct gdma_dev *gd = apc->ac->gdma_dev; struct mana_txq *txq; + struct sk_buff *skb; int i, err; + u32 tsleep; if (apc->port_is_up) return -EINVAL; @@ -2363,15 +2367,40 @@ static int mana_dealloc_queues(struct net_device *ndev) * to false, but it doesn't matter since mana_start_xmit() drops any * new packets due to apc->port_is_up being false. * - * Drain all the in-flight TX packets + * Drain all the in-flight TX packets. + * A timeout of 120 seconds for all the queues is used. + * This will break the while loop when h/w is not responding. + * This value of 120 has been decided here considering max + * number of queues. */ + for (i = 0; i < apc->num_queues; i++) { txq = &apc->tx_qp[i].txq; - - while (atomic_read(&txq->pending_sends) > 0) - usleep_range(1000, 2000); + tsleep = 1000; + while (atomic_read(&txq->pending_sends) > 0 && + time_before(jiffies, timeout)) { + usleep_range(tsleep, tsleep + 1000); + tsleep <<= 1; + } + if (atomic_read(&txq->pending_sends)) { + err = pcie_flr(to_pci_dev(gd->gdma_context->dev)); + if (err) { + netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n", + err, atomic_read(&txq->pending_sends), + txq->gdma_txq_id); + } + break; + } } + for (i = 0; i < apc->num_queues; i++) { + txq = &apc->tx_qp[i].txq; + while ((skb = skb_dequeue(&txq->pending_skbs))) { + mana_unmap_skb(skb, apc); + dev_kfree_skb_any(skb); + } + atomic_set(&txq->pending_sends, 0); + } /* We're 100% sure the queues can no longer be woken up, because * we're sure now mana_poll_tx_cq() can't be running. */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 612b0015dc43..432fb93aa801 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1817,6 +1817,7 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu) static void ionic_tx_timeout_work(struct work_struct *ws) { struct ionic_lif *lif = container_of(ws, struct ionic_lif, tx_timeout_work); + int err; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return; @@ -1829,8 +1830,11 @@ static void ionic_tx_timeout_work(struct work_struct *ws) mutex_lock(&lif->queue_lock); ionic_stop_queues_reconfig(lif); - ionic_start_queues_reconfig(lif); + err = ionic_start_queues_reconfig(lif); mutex_unlock(&lif->queue_lock); + + if (err) + dev_err(lif->ionic->dev, "%s: Restarting queues failed\n", __func__); } static void ionic_tx_timeout(struct net_device *netdev, unsigned int txqueue) @@ -2800,17 +2804,22 @@ static int ionic_cmb_reconfig(struct ionic_lif *lif, if (err) { dev_err(lif->ionic->dev, "CMB restore failed: %d\n", err); - goto errout; + goto err_out; } } - ionic_start_queues_reconfig(lif); - } else { - /* This was detached in ionic_stop_queues_reconfig() */ - netif_device_attach(lif->netdev); + err = ionic_start_queues_reconfig(lif); + if (err) { + dev_err(lif->ionic->dev, + "CMB reconfig failed: %d\n", err); + goto err_out; + } } -errout: +err_out: + /* This was detached in ionic_stop_queues_reconfig() */ + netif_device_attach(lif->netdev); + return err; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index f8682356d0cf..94d4f9413ab7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -194,6 +194,22 @@ void qed_hw_remove(struct qed_dev *cdev); struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn); /** + * qed_ptt_acquire_context(): Allocate a PTT window honoring the context + * atomicy. + * + * @p_hwfn: HW device data. + * @is_atomic: Hint from the caller - if the func can sleep or not. + * + * Context: The function should not sleep in case is_atomic == true. + * Return: struct qed_ptt. + * + * Should be called at the entry point to the driver + * (at the beginning of an exported function). + */ +struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn, + bool is_atomic); + +/** * qed_ptt_release(): Release PTT Window. * * @p_hwfn: HW device data. diff --git a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c index 3764190b948e..04602ac94708 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c +++ b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c @@ -693,13 +693,14 @@ static void _qed_fcoe_get_pstats(struct qed_hwfn *p_hwfn, } static int qed_fcoe_get_stats(struct qed_hwfn *p_hwfn, - struct qed_fcoe_stats *p_stats) + struct qed_fcoe_stats *p_stats, + bool is_atomic) { struct qed_ptt *p_ptt; memset(p_stats, 0, sizeof(*p_stats)); - p_ptt = qed_ptt_acquire(p_hwfn); + p_ptt = qed_ptt_acquire_context(p_hwfn, is_atomic); if (!p_ptt) { DP_ERR(p_hwfn, "Failed to acquire ptt\n"); @@ -973,19 +974,27 @@ static int qed_fcoe_destroy_conn(struct qed_dev *cdev, QED_SPQ_MODE_EBLOCK, NULL); } +static int qed_fcoe_stats_context(struct qed_dev *cdev, + struct qed_fcoe_stats *stats, + bool is_atomic) +{ + return qed_fcoe_get_stats(QED_AFFIN_HWFN(cdev), stats, is_atomic); +} + static int qed_fcoe_stats(struct qed_dev *cdev, struct qed_fcoe_stats *stats) { - return qed_fcoe_get_stats(QED_AFFIN_HWFN(cdev), stats); + return qed_fcoe_stats_context(cdev, stats, false); } void qed_get_protocol_stats_fcoe(struct qed_dev *cdev, - struct qed_mcp_fcoe_stats *stats) + struct qed_mcp_fcoe_stats *stats, + bool is_atomic) { struct qed_fcoe_stats proto_stats; /* Retrieve FW statistics */ memset(&proto_stats, 0, sizeof(proto_stats)); - if (qed_fcoe_stats(cdev, &proto_stats)) { + if (qed_fcoe_stats_context(cdev, &proto_stats, is_atomic)) { DP_VERBOSE(cdev, QED_MSG_STORAGE, "Failed to collect FCoE statistics\n"); return; diff --git a/drivers/net/ethernet/qlogic/qed/qed_fcoe.h b/drivers/net/ethernet/qlogic/qed/qed_fcoe.h index 19c85adf4ceb..214e8299ecb4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_fcoe.h +++ b/drivers/net/ethernet/qlogic/qed/qed_fcoe.h @@ -28,8 +28,20 @@ int qed_fcoe_alloc(struct qed_hwfn *p_hwfn); void qed_fcoe_setup(struct qed_hwfn *p_hwfn); void qed_fcoe_free(struct qed_hwfn *p_hwfn); +/** + * qed_get_protocol_stats_fcoe(): Fills provided statistics + * struct with statistics. + * + * @cdev: Qed dev pointer. + * @stats: Points to struct that will be filled with statistics. + * @is_atomic: Hint from the caller - if the func can sleep or not. + * + * Context: The function should not sleep in case is_atomic == true. + * Return: Void. + */ void qed_get_protocol_stats_fcoe(struct qed_dev *cdev, - struct qed_mcp_fcoe_stats *stats); + struct qed_mcp_fcoe_stats *stats, + bool is_atomic); #else /* CONFIG_QED_FCOE */ static inline int qed_fcoe_alloc(struct qed_hwfn *p_hwfn) { @@ -40,7 +52,8 @@ static inline void qed_fcoe_setup(struct qed_hwfn *p_hwfn) {} static inline void qed_fcoe_free(struct qed_hwfn *p_hwfn) {} static inline void qed_get_protocol_stats_fcoe(struct qed_dev *cdev, - struct qed_mcp_fcoe_stats *stats) + struct qed_mcp_fcoe_stats *stats, + bool is_atomic) { } #endif /* CONFIG_QED_FCOE */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.c b/drivers/net/ethernet/qlogic/qed/qed_hw.c index 554f30b0cfd5..6263f847b6b9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.c +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.c @@ -23,7 +23,10 @@ #include "qed_reg_addr.h" #include "qed_sriov.h" -#define QED_BAR_ACQUIRE_TIMEOUT 1000 +#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT 1000 +#define QED_BAR_ACQUIRE_TIMEOUT_USLEEP 1000 +#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT 100000 +#define QED_BAR_ACQUIRE_TIMEOUT_UDELAY 10 /* Invalid values */ #define QED_BAR_INVALID_OFFSET (cpu_to_le32(-1)) @@ -85,11 +88,21 @@ void qed_ptt_pool_free(struct qed_hwfn *p_hwfn) struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn) { + return qed_ptt_acquire_context(p_hwfn, false); +} + +struct qed_ptt *qed_ptt_acquire_context(struct qed_hwfn *p_hwfn, bool is_atomic) +{ struct qed_ptt *p_ptt; - unsigned int i; + unsigned int i, count; + + if (is_atomic) + count = QED_BAR_ACQUIRE_TIMEOUT_UDELAY_CNT; + else + count = QED_BAR_ACQUIRE_TIMEOUT_USLEEP_CNT; /* Take the free PTT from the list */ - for (i = 0; i < QED_BAR_ACQUIRE_TIMEOUT; i++) { + for (i = 0; i < count; i++) { spin_lock_bh(&p_hwfn->p_ptt_pool->lock); if (!list_empty(&p_hwfn->p_ptt_pool->free_list)) { @@ -105,7 +118,12 @@ struct qed_ptt *qed_ptt_acquire(struct qed_hwfn *p_hwfn) } spin_unlock_bh(&p_hwfn->p_ptt_pool->lock); - usleep_range(1000, 2000); + + if (is_atomic) + udelay(QED_BAR_ACQUIRE_TIMEOUT_UDELAY); + else + usleep_range(QED_BAR_ACQUIRE_TIMEOUT_USLEEP, + QED_BAR_ACQUIRE_TIMEOUT_USLEEP * 2); } DP_NOTICE(p_hwfn, "PTT acquire timeout - failed to allocate PTT\n"); diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 511ab214eb9c..980e7289b481 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -999,13 +999,14 @@ static void _qed_iscsi_get_pstats(struct qed_hwfn *p_hwfn, } static int qed_iscsi_get_stats(struct qed_hwfn *p_hwfn, - struct qed_iscsi_stats *stats) + struct qed_iscsi_stats *stats, + bool is_atomic) { struct qed_ptt *p_ptt; memset(stats, 0, sizeof(*stats)); - p_ptt = qed_ptt_acquire(p_hwfn); + p_ptt = qed_ptt_acquire_context(p_hwfn, is_atomic); if (!p_ptt) { DP_ERR(p_hwfn, "Failed to acquire ptt\n"); return -EAGAIN; @@ -1336,9 +1337,16 @@ static int qed_iscsi_destroy_conn(struct qed_dev *cdev, QED_SPQ_MODE_EBLOCK, NULL); } +static int qed_iscsi_stats_context(struct qed_dev *cdev, + struct qed_iscsi_stats *stats, + bool is_atomic) +{ + return qed_iscsi_get_stats(QED_AFFIN_HWFN(cdev), stats, is_atomic); +} + static int qed_iscsi_stats(struct qed_dev *cdev, struct qed_iscsi_stats *stats) { - return qed_iscsi_get_stats(QED_AFFIN_HWFN(cdev), stats); + return qed_iscsi_stats_context(cdev, stats, false); } static int qed_iscsi_change_mac(struct qed_dev *cdev, @@ -1358,13 +1366,14 @@ static int qed_iscsi_change_mac(struct qed_dev *cdev, } void qed_get_protocol_stats_iscsi(struct qed_dev *cdev, - struct qed_mcp_iscsi_stats *stats) + struct qed_mcp_iscsi_stats *stats, + bool is_atomic) { struct qed_iscsi_stats proto_stats; /* Retrieve FW statistics */ memset(&proto_stats, 0, sizeof(proto_stats)); - if (qed_iscsi_stats(cdev, &proto_stats)) { + if (qed_iscsi_stats_context(cdev, &proto_stats, is_atomic)) { DP_VERBOSE(cdev, QED_MSG_STORAGE, "Failed to collect ISCSI statistics\n"); return; diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.h b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h index dec2b00259d4..974cb8d26608 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h @@ -39,11 +39,14 @@ void qed_iscsi_free(struct qed_hwfn *p_hwfn); * * @cdev: Qed dev pointer. * @stats: Points to struct that will be filled with statistics. + * @is_atomic: Hint from the caller - if the func can sleep or not. * + * Context: The function should not sleep in case is_atomic == true. * Return: Void. */ void qed_get_protocol_stats_iscsi(struct qed_dev *cdev, - struct qed_mcp_iscsi_stats *stats); + struct qed_mcp_iscsi_stats *stats, + bool is_atomic); #else /* IS_ENABLED(CONFIG_QED_ISCSI) */ static inline int qed_iscsi_alloc(struct qed_hwfn *p_hwfn) { @@ -56,7 +59,8 @@ static inline void qed_iscsi_free(struct qed_hwfn *p_hwfn) {} static inline void qed_get_protocol_stats_iscsi(struct qed_dev *cdev, - struct qed_mcp_iscsi_stats *stats) {} + struct qed_mcp_iscsi_stats *stats, + bool is_atomic) {} #endif /* IS_ENABLED(CONFIG_QED_ISCSI) */ #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 7776d3bdd459..970b9aabbc3d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1863,7 +1863,8 @@ static void __qed_get_vport_stats(struct qed_hwfn *p_hwfn, } static void _qed_get_vport_stats(struct qed_dev *cdev, - struct qed_eth_stats *stats) + struct qed_eth_stats *stats, + bool is_atomic) { u8 fw_vport = 0; int i; @@ -1872,10 +1873,11 @@ static void _qed_get_vport_stats(struct qed_dev *cdev, for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - struct qed_ptt *p_ptt = IS_PF(cdev) ? qed_ptt_acquire(p_hwfn) - : NULL; + struct qed_ptt *p_ptt; bool b_get_port_stats; + p_ptt = IS_PF(cdev) ? qed_ptt_acquire_context(p_hwfn, is_atomic) + : NULL; if (IS_PF(cdev)) { /* The main vport index is relative first */ if (qed_fw_vport(p_hwfn, 0, &fw_vport)) { @@ -1901,6 +1903,13 @@ out: void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats) { + qed_get_vport_stats_context(cdev, stats, false); +} + +void qed_get_vport_stats_context(struct qed_dev *cdev, + struct qed_eth_stats *stats, + bool is_atomic) +{ u32 i; if (!cdev || cdev->recov_in_prog) { @@ -1908,7 +1917,7 @@ void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats) return; } - _qed_get_vport_stats(cdev, stats); + _qed_get_vport_stats(cdev, stats, is_atomic); if (!cdev->reset_stats) return; @@ -1960,7 +1969,7 @@ void qed_reset_vport_stats(struct qed_dev *cdev) if (!cdev->reset_stats) { DP_INFO(cdev, "Reset stats not allocated\n"); } else { - _qed_get_vport_stats(cdev, cdev->reset_stats); + _qed_get_vport_stats(cdev, cdev->reset_stats, false); cdev->reset_stats->common.link_change_count = 0; } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index a538cf478c14..2d2f82c785ad 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -249,8 +249,32 @@ qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, enum spq_mode comp_mode, struct qed_spq_comp_cb *p_comp_data); +/** + * qed_get_vport_stats(): Fills provided statistics + * struct with statistics. + * + * @cdev: Qed dev pointer. + * @stats: Points to struct that will be filled with statistics. + * + * Return: Void. + */ void qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats); +/** + * qed_get_vport_stats_context(): Fills provided statistics + * struct with statistics. + * + * @cdev: Qed dev pointer. + * @stats: Points to struct that will be filled with statistics. + * @is_atomic: Hint from the caller - if the func can sleep or not. + * + * Context: The function should not sleep in case is_atomic == true. + * Return: Void. + */ +void qed_get_vport_stats_context(struct qed_dev *cdev, + struct qed_eth_stats *stats, + bool is_atomic); + void qed_reset_vport_stats(struct qed_dev *cdev); /** diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index f5af83342856..c278f8893042 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -3092,7 +3092,7 @@ void qed_get_protocol_stats(struct qed_dev *cdev, switch (type) { case QED_MCP_LAN_STATS: - qed_get_vport_stats(cdev, ð_stats); + qed_get_vport_stats_context(cdev, ð_stats, true); stats->lan_stats.ucast_rx_pkts = eth_stats.common.rx_ucast_pkts; stats->lan_stats.ucast_tx_pkts = @@ -3100,10 +3100,10 @@ void qed_get_protocol_stats(struct qed_dev *cdev, stats->lan_stats.fcs_err = -1; break; case QED_MCP_FCOE_STATS: - qed_get_protocol_stats_fcoe(cdev, &stats->fcoe_stats); + qed_get_protocol_stats_fcoe(cdev, &stats->fcoe_stats, true); break; case QED_MCP_ISCSI_STATS: - qed_get_protocol_stats_iscsi(cdev, &stats->iscsi_stats); + qed_get_protocol_stats_iscsi(cdev, &stats->iscsi_stats, true); break; default: DP_VERBOSE(cdev, QED_MSG_SP, diff --git a/drivers/net/ethernet/sfc/falcon/selftest.c b/drivers/net/ethernet/sfc/falcon/selftest.c index 9e5ce2a13787..cf1d67b6d86d 100644 --- a/drivers/net/ethernet/sfc/falcon/selftest.c +++ b/drivers/net/ethernet/sfc/falcon/selftest.c @@ -40,15 +40,16 @@ */ struct ef4_loopback_payload { char pad[2]; /* Ensures ip is 4-byte aligned */ - struct ethhdr header; - struct iphdr ip; - struct udphdr udp; - __be16 iteration; - char msg[64]; + struct_group_attr(packet, __packed, + struct ethhdr header; + struct iphdr ip; + struct udphdr udp; + __be16 iteration; + char msg[64]; + ); } __packed __aligned(4); -#define EF4_LOOPBACK_PAYLOAD_LEN (sizeof(struct ef4_loopback_payload) - \ - offsetof(struct ef4_loopback_payload, \ - header)) +#define EF4_LOOPBACK_PAYLOAD_LEN \ + sizeof_field(struct ef4_loopback_payload, packet) /* Loopback test source MAC address */ static const u8 payload_source[ETH_ALEN] __aligned(2) = { @@ -299,7 +300,7 @@ void ef4_loopback_rx_packet(struct ef4_nic *efx, payload = &state->payload; - memcpy(&received.header, buf_ptr, + memcpy(&received.packet, buf_ptr, min_t(int, pkt_len, EF4_LOOPBACK_PAYLOAD_LEN)); received.ip.saddr = payload->ip.saddr; if (state->offload_csum) @@ -370,7 +371,7 @@ void ef4_loopback_rx_packet(struct ef4_nic *efx, buf_ptr, pkt_len, 0); netif_err(efx, drv, efx->net_dev, "expected packet:\n"); print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, - &state->payload.header, EF4_LOOPBACK_PAYLOAD_LEN, + &state->payload.packet, EF4_LOOPBACK_PAYLOAD_LEN, 0); } #endif @@ -440,6 +441,8 @@ static int ef4_begin_loopback(struct ef4_tx_queue *tx_queue) payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); /* Strip off the leading padding */ skb_pull(skb, offsetof(struct ef4_loopback_payload, header)); + /* Strip off the trailing padding */ + skb_trim(skb, EF4_LOOPBACK_PAYLOAD_LEN); /* Ensure everything we've written is visible to the * interrupt handler. */ diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index 96d856b9043c..19a0b8584afb 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -43,15 +43,16 @@ */ struct efx_loopback_payload { char pad[2]; /* Ensures ip is 4-byte aligned */ - struct ethhdr header; - struct iphdr ip; - struct udphdr udp; - __be16 iteration; - char msg[64]; + struct_group_attr(packet, __packed, + struct ethhdr header; + struct iphdr ip; + struct udphdr udp; + __be16 iteration; + char msg[64]; + ); } __packed __aligned(4); -#define EFX_LOOPBACK_PAYLOAD_LEN (sizeof(struct efx_loopback_payload) - \ - offsetof(struct efx_loopback_payload, \ - header)) +#define EFX_LOOPBACK_PAYLOAD_LEN \ + sizeof_field(struct efx_loopback_payload, packet) /* Loopback test source MAC address */ static const u8 payload_source[ETH_ALEN] __aligned(2) = { @@ -297,7 +298,7 @@ void efx_loopback_rx_packet(struct efx_nic *efx, payload = &state->payload; - memcpy(&received.header, buf_ptr, + memcpy(&received.packet, buf_ptr, min_t(int, pkt_len, EFX_LOOPBACK_PAYLOAD_LEN)); received.ip.saddr = payload->ip.saddr; if (state->offload_csum) @@ -368,7 +369,7 @@ void efx_loopback_rx_packet(struct efx_nic *efx, buf_ptr, pkt_len, 0); netif_err(efx, drv, efx->net_dev, "expected packet:\n"); print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, - &state->payload.header, EFX_LOOPBACK_PAYLOAD_LEN, + &state->payload.packet, EFX_LOOPBACK_PAYLOAD_LEN, 0); } #endif @@ -438,6 +439,8 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); /* Strip off the leading padding */ skb_pull(skb, offsetof(struct efx_loopback_payload, header)); + /* Strip off the trailing padding */ + skb_trim(skb, EFX_LOOPBACK_PAYLOAD_LEN); /* Ensure everything we've written is visible to the * interrupt handler. */ diff --git a/drivers/net/ethernet/sfc/siena/selftest.c b/drivers/net/ethernet/sfc/siena/selftest.c index 111ac17194a5..b55fd3346972 100644 --- a/drivers/net/ethernet/sfc/siena/selftest.c +++ b/drivers/net/ethernet/sfc/siena/selftest.c @@ -43,15 +43,16 @@ */ struct efx_loopback_payload { char pad[2]; /* Ensures ip is 4-byte aligned */ - struct ethhdr header; - struct iphdr ip; - struct udphdr udp; - __be16 iteration; - char msg[64]; + struct_group_attr(packet, __packed, + struct ethhdr header; + struct iphdr ip; + struct udphdr udp; + __be16 iteration; + char msg[64]; + ); } __packed __aligned(4); -#define EFX_LOOPBACK_PAYLOAD_LEN (sizeof(struct efx_loopback_payload) - \ - offsetof(struct efx_loopback_payload, \ - header)) +#define EFX_LOOPBACK_PAYLOAD_LEN \ + sizeof_field(struct efx_loopback_payload, packet) /* Loopback test source MAC address */ static const u8 payload_source[ETH_ALEN] __aligned(2) = { @@ -297,7 +298,7 @@ void efx_siena_loopback_rx_packet(struct efx_nic *efx, payload = &state->payload; - memcpy(&received.header, buf_ptr, + memcpy(&received.packet, buf_ptr, min_t(int, pkt_len, EFX_LOOPBACK_PAYLOAD_LEN)); received.ip.saddr = payload->ip.saddr; if (state->offload_csum) @@ -368,7 +369,7 @@ void efx_siena_loopback_rx_packet(struct efx_nic *efx, buf_ptr, pkt_len, 0); netif_err(efx, drv, efx->net_dev, "expected packet:\n"); print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, - &state->payload.header, EFX_LOOPBACK_PAYLOAD_LEN, + &state->payload.packet, EFX_LOOPBACK_PAYLOAD_LEN, 0); } #endif @@ -438,6 +439,8 @@ static int efx_begin_loopback(struct efx_tx_queue *tx_queue) payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); /* Strip off the leading padding */ skb_pull(skb, offsetof(struct efx_loopback_payload, header)); + /* Strip off the trailing padding */ + skb_trim(skb, EFX_LOOPBACK_PAYLOAD_LEN); /* Ensure everything we've written is visible to the * interrupt handler. */ diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 2d7347b71c41..0dcd6a568b06 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -1851,6 +1851,17 @@ static int netsec_of_probe(struct platform_device *pdev, return err; } + /* + * SynQuacer is physically configured with TX and RX delays + * but the standard firmware claimed otherwise for a long + * time, ignore it. + */ + if (of_machine_is_compatible("socionext,developer-box") && + priv->phy_interface != PHY_INTERFACE_MODE_RGMII_ID) { + dev_warn(&pdev->dev, "Outdated firmware reports incorrect PHY mode, overriding\n"); + priv->phy_interface = PHY_INTERFACE_MODE_RGMII_ID; + } + priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); if (!priv->phy_np) { dev_err(&pdev->dev, "missing required property 'phy-handle'\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c index f8367c5b490b..fbb0ccf84afc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c @@ -234,7 +234,8 @@ static int tegra_mgbe_probe(struct platform_device *pdev) res.addr = mgbe->regs; res.irq = irq; - mgbe->clks = devm_kzalloc(&pdev->dev, sizeof(*mgbe->clks), GFP_KERNEL); + mgbe->clks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(mgbe_clks), + sizeof(*mgbe->clks), GFP_KERNEL); if (!mgbe->clks) return -ENOMEM; diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index e0ac1bcd9925..49f303353ecb 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1567,12 +1567,16 @@ static int temac_probe(struct platform_device *pdev) } /* Error handle returned DMA RX and TX interrupts */ - if (lp->rx_irq < 0) - return dev_err_probe(&pdev->dev, lp->rx_irq, + if (lp->rx_irq <= 0) { + rc = lp->rx_irq ?: -EINVAL; + return dev_err_probe(&pdev->dev, rc, "could not get DMA RX irq\n"); - if (lp->tx_irq < 0) - return dev_err_probe(&pdev->dev, lp->tx_irq, + } + if (lp->tx_irq <= 0) { + rc = lp->tx_irq ?: -EINVAL; + return dev_err_probe(&pdev->dev, rc, "could not get DMA TX irq\n"); + } if (temac_np) { /* Retrieve the MAC address */ diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 984dfa5d6c11..144ec756c796 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -743,7 +743,7 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); - secy->netdev->stats.rx_dropped++; + DEV_STATS_INC(secy->netdev, rx_dropped); return false; } @@ -767,7 +767,7 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u rxsc_stats->stats.InPktsNotValid++; u64_stats_update_end(&rxsc_stats->syncp); this_cpu_inc(rx_sa->stats->InPktsNotValid); - secy->netdev->stats.rx_errors++; + DEV_STATS_INC(secy->netdev, rx_errors); return false; } @@ -1069,7 +1069,7 @@ static enum rx_handler_result handle_not_macsec(struct sk_buff *skb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoTag++; u64_stats_update_end(&secy_stats->syncp); - macsec->secy.netdev->stats.rx_dropped++; + DEV_STATS_INC(macsec->secy.netdev, rx_dropped); continue; } @@ -1179,7 +1179,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsBadTag++; u64_stats_update_end(&secy_stats->syncp); - secy->netdev->stats.rx_errors++; + DEV_STATS_INC(secy->netdev, rx_errors); goto drop_nosa; } @@ -1196,7 +1196,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsNotUsingSA++; u64_stats_update_end(&rxsc_stats->syncp); - secy->netdev->stats.rx_errors++; + DEV_STATS_INC(secy->netdev, rx_errors); if (active_rx_sa) this_cpu_inc(active_rx_sa->stats->InPktsNotUsingSA); goto drop_nosa; @@ -1230,7 +1230,7 @@ static rx_handler_result_t macsec_handle_frame(struct sk_buff **pskb) u64_stats_update_begin(&rxsc_stats->syncp); rxsc_stats->stats.InPktsLate++; u64_stats_update_end(&rxsc_stats->syncp); - macsec->secy.netdev->stats.rx_dropped++; + DEV_STATS_INC(macsec->secy.netdev, rx_dropped); goto drop; } } @@ -1271,7 +1271,7 @@ deliver: if (ret == NET_RX_SUCCESS) count_rx(dev, len); else - macsec->secy.netdev->stats.rx_dropped++; + DEV_STATS_INC(macsec->secy.netdev, rx_dropped); rcu_read_unlock(); @@ -1308,7 +1308,7 @@ nosci: u64_stats_update_begin(&secy_stats->syncp); secy_stats->stats.InPktsNoSCI++; u64_stats_update_end(&secy_stats->syncp); - macsec->secy.netdev->stats.rx_errors++; + DEV_STATS_INC(macsec->secy.netdev, rx_errors); continue; } @@ -1327,7 +1327,7 @@ nosci: secy_stats->stats.InPktsUnknownSCI++; u64_stats_update_end(&secy_stats->syncp); } else { - macsec->secy.netdev->stats.rx_dropped++; + DEV_STATS_INC(macsec->secy.netdev, rx_dropped); } } @@ -3422,7 +3422,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, if (!secy->operational) { kfree_skb(skb); - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -3430,7 +3430,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, skb = macsec_encrypt(skb, dev); if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EINPROGRESS) - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return NETDEV_TX_OK; } @@ -3667,9 +3667,9 @@ static void macsec_get_stats64(struct net_device *dev, dev_fetch_sw_netstats(s, dev->tstats); - s->rx_dropped = dev->stats.rx_dropped; - s->tx_dropped = dev->stats.tx_dropped; - s->rx_errors = dev->stats.rx_errors; + s->rx_dropped = atomic_long_read(&dev->stats.__rx_dropped); + s->tx_dropped = atomic_long_read(&dev->stats.__tx_dropped); + s->rx_errors = atomic_long_read(&dev->stats.__rx_errors); } static int macsec_get_iflink(const struct net_device *dev) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index c1f307d90518..8a77ec33b417 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -459,21 +459,27 @@ static int at803x_set_wol(struct phy_device *phydev, phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i], mac[(i * 2) + 1] | (mac[(i * 2)] << 8)); - /* Enable WOL function */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL, - 0, AT803X_WOL_EN); - if (ret) - return ret; + /* Enable WOL function for 1588 */ + if (phydev->drv->phy_id == ATH8031_PHY_ID) { + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + 0, AT803X_WOL_EN); + if (ret) + return ret; + } /* Enable WOL interrupt */ ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL); if (ret) return ret; } else { - /* Disable WoL function */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); - if (ret) - return ret; + /* Disable WoL function for 1588 */ + if (phydev->drv->phy_id == ATH8031_PHY_ID) { + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); + if (ret) + return ret; + } /* Disable WOL interrupt */ ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0); if (ret) @@ -508,11 +514,11 @@ static void at803x_get_wol(struct phy_device *phydev, wol->supported = WAKE_MAGIC; wol->wolopts = 0; - value = phy_read_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL); + value = phy_read(phydev, AT803X_INTR_ENABLE); if (value < 0) return; - if (value & AT803X_WOL_EN) + if (value & AT803X_INTR_ENABLE_WOL) wol->wolopts |= WAKE_MAGIC; } @@ -858,9 +864,6 @@ static int at803x_probe(struct phy_device *phydev) if (phydev->drv->phy_id == ATH8031_PHY_ID) { int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG); int mode_cfg; - struct ethtool_wolinfo wol = { - .wolopts = 0, - }; if (ccr < 0) return ccr; @@ -877,12 +880,14 @@ static int at803x_probe(struct phy_device *phydev) break; } - /* Disable WOL by default */ - ret = at803x_set_wol(phydev, &wol); - if (ret < 0) { - phydev_err(phydev, "failed to disable WOL on probe: %d\n", ret); + /* Disable WoL in 1588 register which is enabled + * by default + */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); + if (ret) return ret; - } } return 0; @@ -2059,8 +2064,6 @@ static struct phy_driver at803x_driver[] = { .flags = PHY_POLL_CABLE_TEST, .config_init = at803x_config_init, .link_change_notify = at803x_link_change_notify, - .set_wol = at803x_set_wol, - .get_wol = at803x_get_wol, .suspend = at803x_suspend, .resume = at803x_resume, /* PHY_BASIC_FEATURES */ diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 9137fb8c1c42..49d1d6acf95e 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -534,7 +534,7 @@ static int tap_open(struct inode *inode, struct file *file) q->sock.state = SS_CONNECTED; q->sock.file = file; q->sock.ops = &tap_socket_ops; - sock_init_data_uid(&q->sock, &q->sk, inode->i_uid); + sock_init_data_uid(&q->sock, &q->sk, current_fsuid()); q->sk.sk_write_space = tap_sock_write_space; q->sk.sk_destruct = tap_sock_destruct; q->flags = IFF_VNET_HDR | IFF_NO_PI | IFF_TAP; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index d75456adc62a..100339bc8b04 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1594,7 +1594,7 @@ static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, if (zerocopy) return false; - if (SKB_DATA_ALIGN(len + TUN_RX_PAD) + + if (SKB_DATA_ALIGN(len + TUN_RX_PAD + XDP_PACKET_HEADROOM) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) > PAGE_SIZE) return false; @@ -3469,7 +3469,7 @@ static int tun_chr_open(struct inode *inode, struct file * file) tfile->socket.file = file; tfile->socket.ops = &tun_socket_ops; - sock_init_data_uid(&tfile->socket, &tfile->sk, inode->i_uid); + sock_init_data_uid(&tfile->socket, &tfile->sk, current_fsuid()); tfile->sk.sk_write_space = tun_sock_write_space; tfile->sk.sk_sndbuf = INT_MAX; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index c00a89b24df9..6d61052353f0 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -618,6 +618,13 @@ static const struct usb_device_id products[] = { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, + .idProduct = 0x8005, /* A-300 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = 0, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, .idProduct = 0x8006, /* B-500/SL-5600 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, @@ -625,11 +632,25 @@ static const struct usb_device_id products[] = { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, + .idProduct = 0x8006, /* B-500/SL-5600 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = 0, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, .idProduct = 0x8007, /* C-700 */ ZAURUS_MASTER_INTERFACE, .driver_info = 0, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, + .idProduct = 0x8007, /* C-700 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = 0, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9031, /* C-750 C-760 */ diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index c458c030fadf..59cde06aa7f6 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -4224,8 +4224,6 @@ static void lan78xx_disconnect(struct usb_interface *intf) if (!dev) return; - set_bit(EVENT_DEV_DISCONNECT, &dev->flags); - netif_napi_del(&dev->napi); udev = interface_to_usbdev(intf); @@ -4233,6 +4231,8 @@ static void lan78xx_disconnect(struct usb_interface *intf) unregister_netdev(net); + timer_shutdown_sync(&dev->stat_monitor); + set_bit(EVENT_DEV_DISCONNECT, &dev->flags); cancel_delayed_work_sync(&dev->wq); phydev = net->phydev; @@ -4247,9 +4247,6 @@ static void lan78xx_disconnect(struct usb_interface *intf) usb_scuttle_anchored_urbs(&dev->deferred); - if (timer_pending(&dev->stat_monitor)) - del_timer_sync(&dev->stat_monitor); - lan78xx_unbind(dev, intf); lan78xx_free_tx_resources(dev); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 417f7ea1fffa..344af3c5c836 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1423,6 +1423,7 @@ static const struct usb_device_id products[] = { {QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0195, 4)}, /* Quectel EG95 */ {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */ + {QMI_QUIRK_SET_DTR(0x2c7c, 0x030e, 4)}, /* Quectel EM05GV2 */ {QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */ {QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */ {QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/ diff --git a/drivers/net/usb/zaurus.c b/drivers/net/usb/zaurus.c index 7984f2157d22..df3617c4c44e 100644 --- a/drivers/net/usb/zaurus.c +++ b/drivers/net/usb/zaurus.c @@ -289,11 +289,25 @@ static const struct usb_device_id products [] = { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, + .idProduct = 0x8005, /* A-300 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = (unsigned long)&bogus_mdlm_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, .idProduct = 0x8006, /* B-500/SL-5600 */ ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, + .idProduct = 0x8006, /* B-500/SL-5600 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = (unsigned long)&bogus_mdlm_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8007, /* C-700 */ @@ -301,6 +315,13 @@ static const struct usb_device_id products [] = { .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO + | USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x04DD, + .idProduct = 0x8007, /* C-700 */ + ZAURUS_FAKE_INTERFACE, + .driver_info = (unsigned long)&bogus_mdlm_info, +}, { + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9031, /* C-750 C-760 */ diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index a3de081cda5e..c3ff30ab782e 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -713,6 +713,12 @@ static struct vxlan_vni_node *vxlan_vni_alloc(struct vxlan_dev *vxlan, return vninode; } +static void vxlan_vni_free(struct vxlan_vni_node *vninode) +{ + free_percpu(vninode->stats); + kfree(vninode); +} + static int vxlan_vni_add(struct vxlan_dev *vxlan, struct vxlan_vni_group *vg, u32 vni, union vxlan_addr *group, @@ -740,7 +746,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan, &vninode->vnode, vxlan_vni_rht_params); if (err) { - kfree(vninode); + vxlan_vni_free(vninode); return err; } @@ -763,8 +769,7 @@ static void vxlan_vni_node_rcu_free(struct rcu_head *rcu) struct vxlan_vni_node *v; v = container_of(rcu, struct vxlan_vni_node, rcu); - free_percpu(v->stats); - kfree(v); + vxlan_vni_free(v); } static int vxlan_vni_del(struct vxlan_dev *vxlan, diff --git a/drivers/net/wireguard/allowedips.c b/drivers/net/wireguard/allowedips.c index 5bf7822c53f1..0ba714ca5185 100644 --- a/drivers/net/wireguard/allowedips.c +++ b/drivers/net/wireguard/allowedips.c @@ -6,7 +6,7 @@ #include "allowedips.h" #include "peer.h" -enum { MAX_ALLOWEDIPS_BITS = 128 }; +enum { MAX_ALLOWEDIPS_DEPTH = 129 }; static struct kmem_cache *node_cache; @@ -42,7 +42,7 @@ static void push_rcu(struct allowedips_node **stack, struct allowedips_node __rcu *p, unsigned int *len) { if (rcu_access_pointer(p)) { - if (WARN_ON(IS_ENABLED(DEBUG) && *len >= MAX_ALLOWEDIPS_BITS)) + if (WARN_ON(IS_ENABLED(DEBUG) && *len >= MAX_ALLOWEDIPS_DEPTH)) return; stack[(*len)++] = rcu_dereference_raw(p); } @@ -55,7 +55,7 @@ static void node_free_rcu(struct rcu_head *rcu) static void root_free_rcu(struct rcu_head *rcu) { - struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_BITS] = { + struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_DEPTH] = { container_of(rcu, struct allowedips_node, rcu) }; unsigned int len = 1; @@ -68,7 +68,7 @@ static void root_free_rcu(struct rcu_head *rcu) static void root_remove_peer_lists(struct allowedips_node *root) { - struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_BITS] = { root }; + struct allowedips_node *node, *stack[MAX_ALLOWEDIPS_DEPTH] = { root }; unsigned int len = 1; while (len > 0 && (node = stack[--len])) { diff --git a/drivers/net/wireguard/selftest/allowedips.c b/drivers/net/wireguard/selftest/allowedips.c index 78ebe2892a78..3d1f64ff2e12 100644 --- a/drivers/net/wireguard/selftest/allowedips.c +++ b/drivers/net/wireguard/selftest/allowedips.c @@ -593,16 +593,20 @@ bool __init wg_allowedips_selftest(void) wg_allowedips_remove_by_peer(&t, a, &mutex); test_negative(4, a, 192, 168, 0, 1); - /* These will hit the WARN_ON(len >= MAX_ALLOWEDIPS_BITS) in free_node + /* These will hit the WARN_ON(len >= MAX_ALLOWEDIPS_DEPTH) in free_node * if something goes wrong. */ - for (i = 0; i < MAX_ALLOWEDIPS_BITS; ++i) { - part = cpu_to_be64(~(1LLU << (i % 64))); - memset(&ip, 0xff, 16); - memcpy((u8 *)&ip + (i < 64) * 8, &part, 8); + for (i = 0; i < 64; ++i) { + part = cpu_to_be64(~0LLU << i); + memset(&ip, 0xff, 8); + memcpy((u8 *)&ip + 8, &part, 8); + wg_allowedips_insert_v6(&t, &ip, 128, a, &mutex); + memcpy(&ip, &part, 8); + memset((u8 *)&ip + 8, 0, 8); wg_allowedips_insert_v6(&t, &ip, 128, a, &mutex); } - + memset(&ip, 0, 16); + wg_allowedips_insert_v6(&t, &ip, 128, a, &mutex); wg_allowedips_free(&t, &mutex); wg_allowedips_init(&t); diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 1cebba7889d7..139da578831a 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -376,7 +376,6 @@ static void ath11k_ahb_ext_irq_enable(struct ath11k_base *ab) struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; if (!irq_grp->napi_enabled) { - dev_set_threaded(&irq_grp->napi_ndev, true); napi_enable(&irq_grp->napi); irq_grp->napi_enabled = true; } diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index c899616fbee4..c63083633b37 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -466,7 +466,6 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab) struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; if (!irq_grp->napi_enabled) { - dev_set_threaded(&irq_grp->napi_ndev, true); napi_enable(&irq_grp->napi); irq_grp->napi_enabled = true; } diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 6512267ae4ca..4928e4e91660 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -2144,8 +2144,7 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, struct wmi_tlv *tlv; void *ptr; int i, ret, len; - u32 *tmp_ptr; - u8 extraie_len_with_pad = 0; + u32 *tmp_ptr, extraie_len_with_pad = 0; struct ath12k_wmi_hint_short_ssid_arg *s_ssid = NULL; struct ath12k_wmi_hint_bssid_arg *hint_bssid = NULL; diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index a75bfa9fd1cf..dc2b3b46781e 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -36,11 +36,6 @@ ath6kl_core-y += wmi.o ath6kl_core-y += core.o ath6kl_core-y += recovery.o -# FIXME: temporarily silence -Wdangling-pointer on non W=1+ builds -ifndef KBUILD_EXTRA_WARN -CFLAGS_htc_mbox.o += $(call cc-disable-warning, dangling-pointer) -endif - ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o ath6kl_core-$(CONFIG_ATH6KL_TRACING) += trace.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index de8a2e27f49c..2a90bb24ba77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1456,6 +1456,10 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE; params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE; params_v1 = kzalloc(params_size, GFP_KERNEL); + if (!params_v1) { + err = -ENOMEM; + goto exit_params; + } params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le); kfree(params); @@ -1473,6 +1477,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bphy_err(drvr, "error (%d)\n", err); } +exit_params: kfree(params); exit: return err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 792adaf880b4..bece26741d3a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -398,7 +398,12 @@ struct brcmf_scan_params_le { * fixed parameter portion is assumed, otherwise * ssid in the fixed portion is ignored */ - __le16 channel_list[1]; /* list of chanspecs */ + union { + __le16 padding; /* Reserve space for at least 1 entry for abort + * which uses an on stack brcmf_scan_params_le + */ + DECLARE_FLEX_ARRAY(__le16, channel_list); /* chanspecs */ + }; }; struct brcmf_scan_params_v2_le { diff --git a/drivers/net/wireless/legacy/rayctl.h b/drivers/net/wireless/legacy/rayctl.h index 2b0f332043d7..1f3bde8ac73d 100644 --- a/drivers/net/wireless/legacy/rayctl.h +++ b/drivers/net/wireless/legacy/rayctl.h @@ -577,7 +577,7 @@ struct tx_msg { struct tib_structure tib; struct phy_header phy; struct mac_header mac; - UCHAR var[1]; + UCHAR var[]; }; /****** ECF Receive Control Structure (RCS) Area at Shared RAM offset 0x0800 */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c index 68e88224b8b1..ccedea7e8a50 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c @@ -128,12 +128,12 @@ mt7615_eeprom_parse_hw_band_cap(struct mt7615_dev *dev) case MT_EE_5GHZ: dev->mphy.cap.has_5ghz = true; break; - case MT_EE_2GHZ: - dev->mphy.cap.has_2ghz = true; - break; case MT_EE_DBDC: dev->dbdc_support = true; fallthrough; + case MT_EE_2GHZ: + dev->mphy.cap.has_2ghz = true; + break; default: dev->mphy.cap.has_2ghz = true; dev->mphy.cap.has_5ghz = true; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index b114babec698..c93e6250cb8b 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -2524,7 +2524,7 @@ static int cmac_dma_init(struct rtw89_dev *rtwdev, u8 mac_idx) u32 reg; int ret; - if (chip_id != RTL8852A && chip_id != RTL8852B) + if (chip_id != RTL8852B) return 0; ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index c8d20cddf658..88f760a7cbc3 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -396,7 +396,7 @@ static void xenvif_get_requests(struct xenvif_queue *queue, struct gnttab_map_grant_ref *gop = queue->tx_map_ops + *map_ops; struct xen_netif_tx_request *txp = first; - nr_slots = shinfo->nr_frags + 1; + nr_slots = shinfo->nr_frags + frag_overflow + 1; copy_count(skb) = 0; XENVIF_TX_CB(skb)->split_mask = 0; @@ -462,8 +462,8 @@ static void xenvif_get_requests(struct xenvif_queue *queue, } } - for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; - shinfo->nr_frags++, gop++) { + for (shinfo->nr_frags = 0; nr_slots > 0 && shinfo->nr_frags < MAX_SKB_FRAGS; + shinfo->nr_frags++, gop++, nr_slots--) { index = pending_index(queue->pending_cons++); pending_idx = queue->pending_ring[index]; xenvif_tx_create_map_op(queue, pending_idx, txp, @@ -476,12 +476,12 @@ static void xenvif_get_requests(struct xenvif_queue *queue, txp++; } - if (frag_overflow) { + if (nr_slots > 0) { shinfo = skb_shinfo(nskb); frags = shinfo->frags; - for (shinfo->nr_frags = 0; shinfo->nr_frags < frag_overflow; + for (shinfo->nr_frags = 0; shinfo->nr_frags < nr_slots; shinfo->nr_frags++, txp++, gop++) { index = pending_index(queue->pending_cons++); pending_idx = queue->pending_ring[index]; @@ -492,6 +492,11 @@ static void xenvif_get_requests(struct xenvif_queue *queue, } skb_shinfo(skb)->frag_list = nskb; + } else if (nskb) { + /* A frag_list skb was allocated but it is no longer needed + * because enough slots were converted to copy ops above. + */ + kfree_skb(nskb); } (*copy_ops) = cop - queue->tx_copy_ops; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 37b6fa746662..f3a01b79148c 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -3933,6 +3933,12 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl) */ nvme_mpath_clear_ctrl_paths(ctrl); + /* + * Unquiesce io queues so any pending IO won't hang, especially + * those submitted from scan work + */ + nvme_unquiesce_io_queues(ctrl); + /* prevent racing with ns scanning */ flush_work(&ctrl->scan_work); @@ -3942,10 +3948,8 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl) * removing the namespaces' disks; fail all the queues now to avoid * potentially having to clean up the failed sync later. */ - if (ctrl->state == NVME_CTRL_DEAD) { + if (ctrl->state == NVME_CTRL_DEAD) nvme_mark_namespaces_dead(ctrl); - nvme_unquiesce_io_queues(ctrl); - } /* this is a no-op when called from the controller reset handler */ nvme_change_ctrl_state(ctrl, NVME_CTRL_DELETING_NOIO); diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index 5c3250f36ce7..d39f3219358b 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -786,11 +786,9 @@ int nvme_ns_chr_uring_cmd_iopoll(struct io_uring_cmd *ioucmd, if (!(ioucmd->flags & IORING_URING_CMD_POLLED)) return 0; - rcu_read_lock(); req = READ_ONCE(ioucmd->cookie); if (req && blk_rq_is_poll(req)) ret = blk_rq_poll(req, iob, poll_flags); - rcu_read_unlock(); return ret; } #ifdef CONFIG_NVME_MULTIPATH diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index baf69af7ea78..2f57da12d983 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3402,7 +3402,8 @@ static const struct pci_device_id nvme_id_table[] = { { PCI_DEVICE(0x1d97, 0x2263), /* SPCC */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x144d, 0xa80b), /* Samsung PM9B1 256G and 512G */ - .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES | + NVME_QUIRK_BOGUS_NID, }, { PCI_DEVICE(0x144d, 0xa809), /* Samsung MZALQ256HBJD 256G */ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, { PCI_DEVICE(0x144d, 0xa802), /* Samsung SM953 */ diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index d433b2ec07a6..337a624a537c 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -883,6 +883,7 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new) goto out_cleanup_tagset; if (!new) { + nvme_start_freeze(&ctrl->ctrl); nvme_unquiesce_io_queues(&ctrl->ctrl); if (!nvme_wait_freeze_timeout(&ctrl->ctrl, NVME_IO_TIMEOUT)) { /* @@ -891,6 +892,7 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new) * to be safe. */ ret = -ENODEV; + nvme_unfreeze(&ctrl->ctrl); goto out_wait_freeze_timed_out; } blk_mq_update_nr_hw_queues(ctrl->ctrl.tagset, @@ -940,7 +942,6 @@ static void nvme_rdma_teardown_io_queues(struct nvme_rdma_ctrl *ctrl, bool remove) { if (ctrl->ctrl.queue_count > 1) { - nvme_start_freeze(&ctrl->ctrl); nvme_quiesce_io_queues(&ctrl->ctrl); nvme_sync_io_queues(&ctrl->ctrl); nvme_rdma_stop_io_queues(ctrl); diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 9ce417cd32a7..5b332d9f87fc 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1868,6 +1868,7 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new) goto out_cleanup_connect_q; if (!new) { + nvme_start_freeze(ctrl); nvme_unquiesce_io_queues(ctrl); if (!nvme_wait_freeze_timeout(ctrl, NVME_IO_TIMEOUT)) { /* @@ -1876,6 +1877,7 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new) * to be safe. */ ret = -ENODEV; + nvme_unfreeze(ctrl); goto out_wait_freeze_timed_out; } blk_mq_update_nr_hw_queues(ctrl->tagset, @@ -1980,7 +1982,6 @@ static void nvme_tcp_teardown_io_queues(struct nvme_ctrl *ctrl, if (ctrl->queue_count <= 1) return; nvme_quiesce_admin_queue(ctrl); - nvme_start_freeze(ctrl); nvme_quiesce_io_queues(ctrl); nvme_sync_io_queues(ctrl); nvme_tcp_stop_io_queues(ctrl); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index bf3405f4289e..8b1dcd537020 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -121,6 +121,8 @@ module_param(sba_reserve_agpgart, int, 0444); MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); #endif +struct proc_dir_entry *proc_runway_root __ro_after_init; +struct proc_dir_entry *proc_mckinley_root __ro_after_init; /************************************ ** SBA register read and write support @@ -1968,11 +1970,15 @@ static int __init sba_driver_callback(struct parisc_device *dev) #ifdef CONFIG_PROC_FS switch (dev->id.hversion) { case PLUTO_MCKINLEY_PORT: + if (!proc_mckinley_root) + proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); root = proc_mckinley_root; break; case ASTRO_RUNWAY_PORT: case IKE_MERCED_PORT: default: + if (!proc_runway_root) + proc_runway_root = proc_mkdir("bus/runway", NULL); root = proc_runway_root; break; } diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 0dcc497b0449..5e4475254bd0 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -28,7 +28,6 @@ #include <linux/sysctl.h> #include <asm/io.h> -#include <asm/dma.h> #include <linux/uaccess.h> #include <asm/superio.h> @@ -226,9 +225,9 @@ static int parport_PS2_supported(struct parport *pb) /* --- Initialisation code -------------------------------- */ -struct parport *parport_gsc_probe_port(unsigned long base, +static struct parport *parport_gsc_probe_port(unsigned long base, unsigned long base_hi, int irq, - int dma, struct parisc_device *padev) + struct parisc_device *padev) { struct parport_gsc_private *priv; struct parport_operations *ops; @@ -250,12 +249,9 @@ struct parport *parport_gsc_probe_port(unsigned long base, } priv->ctr = 0xc; priv->ctr_writable = 0xff; - priv->dma_buf = NULL; - priv->dma_handle = 0; p->base = base; p->base_hi = base_hi; p->irq = irq; - p->dma = dma; p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT; p->ops = ops; p->private_data = priv; @@ -286,17 +282,9 @@ struct parport *parport_gsc_probe_port(unsigned long base, if (p->irq == PARPORT_IRQ_AUTO) { p->irq = PARPORT_IRQ_NONE; } - if (p->irq != PARPORT_IRQ_NONE) { + if (p->irq != PARPORT_IRQ_NONE) pr_cont(", irq %d", p->irq); - if (p->dma == PARPORT_DMA_AUTO) { - p->dma = PARPORT_DMA_NONE; - } - } - if (p->dma == PARPORT_DMA_AUTO) /* To use DMA, giving the irq - is mandatory (see above) */ - p->dma = PARPORT_DMA_NONE; - pr_cont(" ["); #define printmode(x) \ do { \ @@ -321,7 +309,6 @@ do { \ pr_warn("%s: irq %d in use, resorting to polled operation\n", p->name, p->irq); p->irq = PARPORT_IRQ_NONE; - p->dma = PARPORT_DMA_NONE; } } @@ -369,8 +356,7 @@ static int __init parport_init_chip(struct parisc_device *dev) pr_info("%s: enhanced parport-modes not supported\n", __func__); } - p = parport_gsc_probe_port(port, 0, dev->irq, - /* PARPORT_IRQ_NONE */ PARPORT_DMA_NONE, dev); + p = parport_gsc_probe_port(port, 0, dev->irq, dev); if (p) parport_count++; dev_set_drvdata(&dev->dev, p); @@ -382,16 +368,10 @@ static void __exit parport_remove_chip(struct parisc_device *dev) { struct parport *p = dev_get_drvdata(&dev->dev); if (p) { - struct parport_gsc_private *priv = p->private_data; struct parport_operations *ops = p->ops; parport_remove_port(p); - if (p->dma != PARPORT_DMA_NONE) - free_dma(p->dma); if (p->irq != PARPORT_IRQ_NONE) free_irq(p->irq, p); - if (priv->dma_buf) - dma_free_coherent(&priv->dev->dev, PAGE_SIZE, - priv->dma_buf, priv->dma_handle); kfree (p->private_data); parport_put_port(p); kfree (ops); /* hope no-one cached it */ diff --git a/drivers/parport/parport_gsc.h b/drivers/parport/parport_gsc.h index 9301217edf12..d447a568c257 100644 --- a/drivers/parport/parport_gsc.h +++ b/drivers/parport/parport_gsc.h @@ -63,8 +63,6 @@ struct parport_gsc_private { int writeIntrThreshold; /* buffer suitable for DMA, if DMA enabled */ - char *dma_buf; - dma_addr_t dma_handle; struct pci_dev *dev; }; @@ -199,9 +197,4 @@ extern void parport_gsc_inc_use_count(void); extern void parport_gsc_dec_use_count(void); -extern struct parport *parport_gsc_probe_port(unsigned long base, - unsigned long base_hi, - int irq, int dma, - struct parisc_device *padev); - #endif /* __DRIVERS_PARPORT_PARPORT_GSC_H */ diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 5bc81cc0a2de..46b252bbe500 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -11,6 +11,7 @@ #include <linux/pci.h> #include <linux/errno.h> #include <linux/ioport.h> +#include <linux/of.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -332,6 +333,7 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } */ void pci_bus_add_device(struct pci_dev *dev) { + struct device_node *dn = dev->dev.of_node; int retval; /* @@ -344,7 +346,7 @@ void pci_bus_add_device(struct pci_dev *dev) pci_proc_attach_device(dev); pci_bridge_d3_update(dev); - dev->match_driver = true; + dev->match_driver = !dn || of_device_is_available(dn); retval = device_attach(&dev->dev); if (retval < 0 && retval != -EPROBE_DEFER) pci_warn(dev, "device attach failed (%d)\n", retval); diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 8d49bad7f847..0859be86e718 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -179,7 +179,6 @@ config PCI_MVEBU depends on MVEBU_MBUS depends on ARM depends on OF - depends on BROKEN select PCI_BRIDGE_EMUL help Add support for Marvell EBU PCIe controller. This PCIe controller diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index cf61733bf78d..9952057c8819 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -485,20 +485,15 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_remove_edma; - if (dw_pcie_link_up(pci)) { - dw_pcie_print_link_status(pci); - } else { + if (!dw_pcie_link_up(pci)) { ret = dw_pcie_start_link(pci); if (ret) goto err_remove_edma; - - if (pci->ops && pci->ops->start_link) { - ret = dw_pcie_wait_for_link(pci); - if (ret) - goto err_stop_link; - } } + /* Ignore errors, the link may come up later */ + dw_pcie_wait_for_link(pci); + bridge->sysdata = pp; ret = pci_host_probe(bridge); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c87848cd8686..1f2ee71da4da 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -644,20 +644,9 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } -void dw_pcie_print_link_status(struct dw_pcie *pci) -{ - u32 offset, val; - - offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); - val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); - - dev_info(pci->dev, "PCIe Gen.%u x%u link up\n", - FIELD_GET(PCI_EXP_LNKSTA_CLS, val), - FIELD_GET(PCI_EXP_LNKSTA_NLW, val)); -} - int dw_pcie_wait_for_link(struct dw_pcie *pci) { + u32 offset, val; int retries; /* Check if the link is up or not */ @@ -673,7 +662,12 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) return -ETIMEDOUT; } - dw_pcie_print_link_status(pci); + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); + + dev_info(pci->dev, "PCIe Gen.%u x%u link up\n", + FIELD_GET(PCI_EXP_LNKSTA_CLS, val), + FIELD_GET(PCI_EXP_LNKSTA_NLW, val)); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 615660640801..79713ce075cc 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -429,7 +429,6 @@ void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); void dw_pcie_edma_remove(struct dw_pcie *pci); -void dw_pcie_print_link_status(struct dw_pcie *pci); static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 328d1e416014..601129772b2d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -498,6 +498,7 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge) acpiphp_native_scan_bridge(dev); } } else { + LIST_HEAD(add_list); int max, pass; acpiphp_rescan_slot(slot); @@ -511,10 +512,15 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge) if (pass && dev->subordinate) { check_hotplug_bridge(slot, dev); pcibios_resource_survey_bus(dev->subordinate); + if (pci_is_root_bus(bus)) + __pci_bus_size_bridges(dev->subordinate, &add_list); } } } - pci_assign_unassigned_bridge_resources(bus->self); + if (pci_is_root_bus(bus)) + __pci_bus_assign_resources(bus, &add_list, NULL); + else + pci_assign_unassigned_bridge_resources(bus->self); } acpiphp_sanitize_bus(bus); diff --git a/drivers/pci/of.c b/drivers/pci/of.c index e51219f9f523..3c158b17dcb5 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -34,11 +34,6 @@ int pci_set_of_node(struct pci_dev *dev) if (!node) return 0; - if (!of_device_is_available(node)) { - of_node_put(node); - return -ENODEV; - } - device_set_node(&dev->dev, of_fwnode_handle(node)); return 0; } diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig index 530426a74f75..d6318cb57aff 100644 --- a/drivers/pinctrl/cirrus/Kconfig +++ b/drivers/pinctrl/cirrus/Kconfig @@ -1,4 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only +config PINCTRL_CS42L43 + tristate "Cirrus Logic CS42L43 Pinctrl Driver" + depends on MFD_CS42L43 + select GPIOLIB + select PINMUX + select PINCONF + select GENERIC_PINCONF + help + Select this to support the GPIO/Pinctrl functions of the Cirrus + Logic CS42L43 PC CODEC. + config PINCTRL_LOCHNAGAR tristate "Cirrus Logic Lochnagar pinctrl driver" depends on MFD_LOCHNAGAR diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile index a484518c840e..9b618d766907 100644 --- a/drivers/pinctrl/cirrus/Makefile +++ b/drivers/pinctrl/cirrus/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only # Cirrus Logic pinctrl drivers +obj-$(CONFIG_PINCTRL_CS42L43) += pinctrl-cs42l43.o + obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o pinctrl-madera-objs := pinctrl-madera-core.o diff --git a/drivers/pinctrl/cirrus/pinctrl-cs42l43.c b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c new file mode 100644 index 000000000000..c09646318419 --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-cs42l43.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 Pinctrl and GPIO driver +// +// Copyright (c) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/bits.h> +#include <linux/build_bug.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/string_helpers.h> + +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinmux.h> + +#include "../pinctrl-utils.h" + +#define CS42L43_NUM_GPIOS 3 + +struct cs42l43_pin { + struct gpio_chip gpio_chip; + + struct device *dev; + struct regmap *regmap; + bool shutters_locked; +}; + +struct cs42l43_pin_data { + unsigned int reg; + unsigned int shift; + unsigned int mask; +}; + +#define CS42L43_PIN(_number, _name, _reg, _field) { \ + .number = _number, .name = _name, \ + .drv_data = &((struct cs42l43_pin_data){ \ + .reg = CS42L43_##_reg, \ + .shift = CS42L43_##_field##_DRV_SHIFT, \ + .mask = CS42L43_##_field##_DRV_MASK, \ + }), \ +} + +static const struct pinctrl_pin_desc cs42l43_pin_pins[] = { + CS42L43_PIN(0, "gpio1", DRV_CTRL4, GPIO1), + CS42L43_PIN(1, "gpio2", DRV_CTRL4, GPIO2), + CS42L43_PIN(2, "gpio3", DRV_CTRL4, GPIO3), + CS42L43_PIN(3, "asp_dout", DRV_CTRL1, ASP_DOUT), + CS42L43_PIN(4, "asp_fsync", DRV_CTRL1, ASP_FSYNC), + CS42L43_PIN(5, "asp_bclk", DRV_CTRL1, ASP_BCLK), + CS42L43_PIN(6, "pdmout2_clk", DRV_CTRL3, PDMOUT2_CLK), + CS42L43_PIN(7, "pdmout2_data", DRV_CTRL3, PDMOUT2_DATA), + CS42L43_PIN(8, "pdmout1_clk", DRV_CTRL3, PDMOUT1_CLK), + CS42L43_PIN(9, "pdmout1_data", DRV_CTRL3, PDMOUT1_DATA), + CS42L43_PIN(10, "i2c_sda", DRV_CTRL3, I2C_SDA), + CS42L43_PIN(11, "i2c_scl", DRV_CTRL_5, I2C_SCL), + CS42L43_PIN(12, "spi_miso", DRV_CTRL3, SPI_MISO), + CS42L43_PIN(13, "spi_sck", DRV_CTRL_5, SPI_SCK), + CS42L43_PIN(14, "spi_ssb", DRV_CTRL_5, SPI_SSB), +}; + +static const unsigned int cs42l43_pin_gpio1_pins[] = { 0 }; +static const unsigned int cs42l43_pin_gpio2_pins[] = { 1 }; +static const unsigned int cs42l43_pin_gpio3_pins[] = { 2 }; +static const unsigned int cs42l43_pin_asp_pins[] = { 3, 4, 5 }; +static const unsigned int cs42l43_pin_pdmout2_pins[] = { 6, 7 }; +static const unsigned int cs42l43_pin_pdmout1_pins[] = { 8, 9 }; +static const unsigned int cs42l43_pin_i2c_pins[] = { 10, 11 }; +static const unsigned int cs42l43_pin_spi_pins[] = { 12, 13, 14 }; + +#define CS42L43_PINGROUP(_name) \ + PINCTRL_PINGROUP(#_name, cs42l43_pin_##_name##_pins, \ + ARRAY_SIZE(cs42l43_pin_##_name##_pins)) + +static const struct pingroup cs42l43_pin_groups[] = { + CS42L43_PINGROUP(gpio1), + CS42L43_PINGROUP(gpio2), + CS42L43_PINGROUP(gpio3), + CS42L43_PINGROUP(asp), + CS42L43_PINGROUP(pdmout2), + CS42L43_PINGROUP(pdmout1), + CS42L43_PINGROUP(i2c), + CS42L43_PINGROUP(spi), +}; + +static int cs42l43_pin_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(cs42l43_pin_groups); +} + +static const char *cs42l43_pin_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group_idx) +{ + return cs42l43_pin_groups[group_idx].name; +} + +static int cs42l43_pin_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group_idx, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = cs42l43_pin_groups[group_idx].pins; + *num_pins = cs42l43_pin_groups[group_idx].npins; + + return 0; +} + +static const struct pinctrl_ops cs42l43_pin_group_ops = { + .get_groups_count = cs42l43_pin_get_groups_count, + .get_group_name = cs42l43_pin_get_group_name, + .get_group_pins = cs42l43_pin_get_group_pins, +#if IS_ENABLED(CONFIG_OF) + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, +#endif +}; + +enum cs42l43_pin_funcs { + CS42L43_FUNC_GPIO, + CS42L43_FUNC_SPDIF, + CS42L43_FUNC_IRQ, + CS42L43_FUNC_MIC_SHT, + CS42L43_FUNC_SPK_SHT, + CS42L43_FUNC_MAX +}; + +static const char * const cs42l43_pin_funcs[] = { + "gpio", "spdif", "irq", "mic-shutter", "spk-shutter", +}; + +static const char * const cs42l43_pin_gpio_groups[] = { "gpio1", "gpio3" }; +static const char * const cs42l43_pin_spdif_groups[] = { "gpio3" }; +static const char * const cs42l43_pin_irq_groups[] = { "gpio1" }; +static const char * const cs42l43_pin_shutter_groups[] = { "gpio1", "gpio2", "gpio3" }; + +static const struct pinfunction cs42l43_pin_func_groups[] = { + PINCTRL_PINFUNCTION("gpio", cs42l43_pin_gpio_groups, + ARRAY_SIZE(cs42l43_pin_gpio_groups)), + PINCTRL_PINFUNCTION("spdif", cs42l43_pin_spdif_groups, + ARRAY_SIZE(cs42l43_pin_spdif_groups)), + PINCTRL_PINFUNCTION("irq", cs42l43_pin_irq_groups, + ARRAY_SIZE(cs42l43_pin_irq_groups)), + PINCTRL_PINFUNCTION("mic-shutter", cs42l43_pin_shutter_groups, + ARRAY_SIZE(cs42l43_pin_shutter_groups)), + PINCTRL_PINFUNCTION("spk-shutter", cs42l43_pin_shutter_groups, + ARRAY_SIZE(cs42l43_pin_shutter_groups)), +}; + +static_assert(ARRAY_SIZE(cs42l43_pin_funcs) == CS42L43_FUNC_MAX); +static_assert(ARRAY_SIZE(cs42l43_pin_func_groups) == CS42L43_FUNC_MAX); + +static int cs42l43_pin_get_func_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(cs42l43_pin_funcs); +} + +static const char *cs42l43_pin_get_func_name(struct pinctrl_dev *pctldev, + unsigned int func_idx) +{ + return cs42l43_pin_funcs[func_idx]; +} + +static int cs42l43_pin_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int func_idx, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = cs42l43_pin_func_groups[func_idx].groups; + *num_groups = cs42l43_pin_func_groups[func_idx].ngroups; + + return 0; +} + +static int cs42l43_pin_set_mux(struct pinctrl_dev *pctldev, + unsigned int func_idx, unsigned int group_idx) +{ + struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int reg, mask, val; + + dev_dbg(priv->dev, "Setting %s to %s\n", + cs42l43_pin_groups[group_idx].name, cs42l43_pin_funcs[func_idx]); + + switch (func_idx) { + case CS42L43_FUNC_MIC_SHT: + reg = CS42L43_SHUTTER_CONTROL; + mask = CS42L43_MIC_SHUTTER_CFG_MASK; + val = 0x2 << (group_idx + CS42L43_MIC_SHUTTER_CFG_SHIFT); + break; + case CS42L43_FUNC_SPK_SHT: + reg = CS42L43_SHUTTER_CONTROL; + mask = CS42L43_SPK_SHUTTER_CFG_MASK; + val = 0x2 << (group_idx + CS42L43_SPK_SHUTTER_CFG_SHIFT); + break; + default: + reg = CS42L43_GPIO_FN_SEL; + mask = BIT(group_idx + CS42L43_GPIO1_FN_SEL_SHIFT); + val = (func_idx == CS42L43_FUNC_GPIO) ? + (0x1 << (group_idx + CS42L43_GPIO1_FN_SEL_SHIFT)) : 0; + break; + } + + if (priv->shutters_locked && reg == CS42L43_SHUTTER_CONTROL) { + dev_err(priv->dev, "Shutter configuration not available\n"); + return -EPERM; + } + + return regmap_update_bits(priv->regmap, reg, mask, val); +} + +static int cs42l43_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int shift = offset + CS42L43_GPIO1_DIR_SHIFT; + int ret; + + dev_dbg(priv->dev, "Setting gpio%d to %s\n", + offset + 1, input ? "input" : "output"); + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for direction: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1, + BIT(shift), !!input << shift); + if (ret) + dev_err(priv->dev, "Failed to set gpio%d direction: %d\n", + offset + 1, ret); + + pm_runtime_put(priv->dev); + + return ret; +} + +static int cs42l43_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + return cs42l43_pin_set_mux(pctldev, 0, offset); +} + +static void cs42l43_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + cs42l43_gpio_set_direction(pctldev, range, offset, true); +} + +static const struct pinmux_ops cs42l43_pin_mux_ops = { + .get_functions_count = cs42l43_pin_get_func_count, + .get_function_name = cs42l43_pin_get_func_name, + .get_function_groups = cs42l43_pin_get_func_groups, + + .set_mux = cs42l43_pin_set_mux, + + .gpio_request_enable = cs42l43_gpio_request_enable, + .gpio_disable_free = cs42l43_gpio_disable_free, + .gpio_set_direction = cs42l43_gpio_set_direction, + + .strict = true, +}; + +static const unsigned int cs42l43_pin_drv_str_ma[] = { 1, 2, 4, 8, 9, 10, 12, 16 }; + +static inline int cs42l43_pin_get_drv_str(struct cs42l43_pin *priv, unsigned int pin) +{ + const struct cs42l43_pin_data *pdat = cs42l43_pin_pins[pin].drv_data; + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, pdat->reg, &val); + if (ret) + return ret; + + return cs42l43_pin_drv_str_ma[(val & pdat->mask) >> pdat->shift]; +} + +static inline int cs42l43_pin_set_drv_str(struct cs42l43_pin *priv, unsigned int pin, + unsigned int ma) +{ + const struct cs42l43_pin_data *pdat = cs42l43_pin_pins[pin].drv_data; + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l43_pin_drv_str_ma); i++) { + if (ma == cs42l43_pin_drv_str_ma[i]) { + if ((i << pdat->shift) > pdat->mask) + goto err; + + dev_dbg(priv->dev, "Set drive strength for %s to %d mA\n", + cs42l43_pin_pins[pin].name, ma); + + return regmap_update_bits(priv->regmap, pdat->reg, + pdat->mask, i << pdat->shift); + } + } + +err: + dev_err(priv->dev, "Invalid drive strength for %s: %d mA\n", + cs42l43_pin_pins[pin].name, ma); + return -EINVAL; +} + +static inline int cs42l43_pin_get_db(struct cs42l43_pin *priv, unsigned int pin) +{ + unsigned int val; + int ret; + + if (pin >= CS42L43_NUM_GPIOS) + return -ENOTSUPP; + + ret = regmap_read(priv->regmap, CS42L43_GPIO_CTRL2, &val); + if (ret) + return ret; + + if (val & (CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin)) + return 0; + + return 85; // Debounce is roughly 85uS +} + +static inline int cs42l43_pin_set_db(struct cs42l43_pin *priv, unsigned int pin, + unsigned int us) +{ + if (pin >= CS42L43_NUM_GPIOS) + return -ENOTSUPP; + + dev_dbg(priv->dev, "Set debounce %s for %s\n", + str_on_off(us), cs42l43_pin_pins[pin].name); + + return regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL2, + CS42L43_GPIO1_DEGLITCH_BYP_MASK << pin, + !!us << pin); +} + +static int cs42l43_pin_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int param = pinconf_to_config_param(*config); + int ret; + + switch (param) { + case PIN_CONFIG_DRIVE_STRENGTH: + ret = cs42l43_pin_get_drv_str(priv, pin); + if (ret < 0) + return ret; + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + ret = cs42l43_pin_get_db(priv, pin); + if (ret < 0) + return ret; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, ret); + + return 0; +} + +static int cs42l43_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + struct cs42l43_pin *priv = pinctrl_dev_get_drvdata(pctldev); + unsigned int val; + int ret; + + while (num_configs) { + val = pinconf_to_config_argument(*configs); + + switch (pinconf_to_config_param(*configs)) { + case PIN_CONFIG_DRIVE_STRENGTH: + ret = cs42l43_pin_set_drv_str(priv, pin, val); + if (ret) + return ret; + break; + case PIN_CONFIG_INPUT_DEBOUNCE: + ret = cs42l43_pin_set_db(priv, pin, val); + if (ret) + return ret; + break; + default: + return -ENOTSUPP; + } + + configs++; + num_configs--; + } + + return 0; +} + +static int cs42l43_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned int selector, unsigned long *config) +{ + int i, ret; + + for (i = 0; i < cs42l43_pin_groups[selector].npins; ++i) { + ret = cs42l43_pin_config_get(pctldev, + cs42l43_pin_groups[selector].pins[i], + config); + if (ret) + return ret; + } + + return 0; +} + +static int cs42l43_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + int i, ret; + + for (i = 0; i < cs42l43_pin_groups[selector].npins; ++i) { + ret = cs42l43_pin_config_set(pctldev, + cs42l43_pin_groups[selector].pins[i], + configs, num_configs); + if (ret) + return ret; + } + + return 0; +} + +static const struct pinconf_ops cs42l43_pin_conf_ops = { + .is_generic = true, + + .pin_config_get = cs42l43_pin_config_get, + .pin_config_set = cs42l43_pin_config_set, + .pin_config_group_get = cs42l43_pin_config_group_get, + .pin_config_group_set = cs42l43_pin_config_group_set, +}; + +static struct pinctrl_desc cs42l43_pin_desc = { + .name = "cs42l43-pinctrl", + .owner = THIS_MODULE, + + .pins = cs42l43_pin_pins, + .npins = ARRAY_SIZE(cs42l43_pin_pins), + + .pctlops = &cs42l43_pin_group_ops, + .pmxops = &cs42l43_pin_mux_ops, + .confops = &cs42l43_pin_conf_ops, +}; + +static int cs42l43_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct cs42l43_pin *priv = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for get: %d\n", ret); + return ret; + } + + ret = regmap_read(priv->regmap, CS42L43_GPIO_STS, &val); + if (ret) + dev_err(priv->dev, "Failed to get gpio%d: %d\n", offset + 1, ret); + else + ret = !!(val & BIT(offset + CS42L43_GPIO1_STS_SHIFT)); + + pm_runtime_put(priv->dev); + + return ret; +} + +static void cs42l43_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct cs42l43_pin *priv = gpiochip_get_data(chip); + unsigned int shift = offset + CS42L43_GPIO1_LVL_SHIFT; + int ret; + + dev_dbg(priv->dev, "Setting gpio%d to %s\n", + offset + 1, value ? "high" : "low"); + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for set: %d\n", ret); + return; + } + + ret = regmap_update_bits(priv->regmap, CS42L43_GPIO_CTRL1, + BIT(shift), value << shift); + if (ret) + dev_err(priv->dev, "Failed to set gpio%d: %d\n", offset + 1, ret); + + pm_runtime_put(priv->dev); +} + +static int cs42l43_gpio_direction_in(struct gpio_chip *chip, unsigned int offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int cs42l43_gpio_direction_out(struct gpio_chip *chip, + unsigned int offset, int value) +{ + cs42l43_gpio_set(chip, offset, value); + + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int cs42l43_gpio_add_pin_ranges(struct gpio_chip *chip) +{ + struct cs42l43_pin *priv = gpiochip_get_data(chip); + int ret; + + ret = gpiochip_add_pin_range(&priv->gpio_chip, priv->gpio_chip.label, + 0, 0, CS42L43_NUM_GPIOS); + if (ret) + dev_err(priv->dev, "Failed to add GPIO pin range: %d\n", ret); + + return ret; +} + +static int cs42l43_pin_probe(struct platform_device *pdev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); + struct cs42l43_pin *priv; + struct pinctrl_dev *pctldev; + struct fwnode_handle *fwnode = dev_fwnode(cs42l43->dev); + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->regmap = cs42l43->regmap; + + priv->shutters_locked = cs42l43->hw_lock; + + priv->gpio_chip.request = gpiochip_generic_request; + priv->gpio_chip.free = gpiochip_generic_free; + priv->gpio_chip.direction_input = cs42l43_gpio_direction_in; + priv->gpio_chip.direction_output = cs42l43_gpio_direction_out; + priv->gpio_chip.add_pin_ranges = cs42l43_gpio_add_pin_ranges; + priv->gpio_chip.get = cs42l43_gpio_get; + priv->gpio_chip.set = cs42l43_gpio_set; + priv->gpio_chip.label = dev_name(priv->dev); + priv->gpio_chip.parent = priv->dev; + priv->gpio_chip.can_sleep = true; + priv->gpio_chip.base = -1; + priv->gpio_chip.ngpio = CS42L43_NUM_GPIOS; + + if (is_of_node(fwnode)) { + fwnode = fwnode_get_named_child_node(fwnode, "pinctrl"); + + if (fwnode && !fwnode->dev) + fwnode->dev = priv->dev; + } + + priv->gpio_chip.fwnode = fwnode; + + device_set_node(priv->dev, fwnode); + + devm_pm_runtime_enable(priv->dev); + pm_runtime_idle(priv->dev); + + pctldev = devm_pinctrl_register(priv->dev, &cs42l43_pin_desc, priv); + if (IS_ERR(pctldev)) + return dev_err_probe(priv->dev, PTR_ERR(pctldev), + "Failed to register pinctrl\n"); + + ret = devm_gpiochip_add_data(priv->dev, &priv->gpio_chip, priv); + if (ret) + return dev_err_probe(priv->dev, ret, + "Failed to register gpiochip\n"); + + return 0; +} + +static const struct platform_device_id cs42l43_pin_id_table[] = { + { "cs42l43-pinctrl", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs42l43_pin_id_table); + +static struct platform_driver cs42l43_pin_driver = { + .driver = { + .name = "cs42l43-pinctrl", + }, + .probe = cs42l43_pin_probe, + .id_table = cs42l43_pin_id_table, +}; +module_platform_driver(cs42l43_pin_driver); + +MODULE_DESCRIPTION("CS42L43 Pinctrl Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index ab69d517a36a..a70e67749be3 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -176,7 +176,8 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf) int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev) { - u8 mode, flag = 0; + u8 flag = 0; + int mode; int src; mode = amd_pmf_get_pprof_modes(dev); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 1f59ac55c5f7..a95004e3d80b 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -335,8 +335,8 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn node = dev_to_node(&_pci_dev->dev); if (node == NUMA_NO_NODE) { - pr_info("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n", - cpu, bus_no, dev, fn); + pr_info_once("Fail to get numa node for CPU:%d bus:%d dev:%d fn:%d\n", + cpu, bus_no, dev, fn); continue; } diff --git a/drivers/platform/x86/lenovo-ymc.c b/drivers/platform/x86/lenovo-ymc.c index 41676188b373..f360370d5002 100644 --- a/drivers/platform/x86/lenovo-ymc.c +++ b/drivers/platform/x86/lenovo-ymc.c @@ -24,6 +24,10 @@ static bool ec_trigger __read_mostly; module_param(ec_trigger, bool, 0444); MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events"); +static bool force; +module_param(force, bool, 0444); +MODULE_PARM_DESC(force, "Force loading on boards without a convertible DMI chassis-type"); + static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = { { /* Lenovo Yoga 7 14ARB7 */ @@ -35,6 +39,20 @@ static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = { { } }; +static const struct dmi_system_id allowed_chasis_types_dmi_table[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" /* Convertible */), + }, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "32" /* Detachable */), + }, + }, + { } +}; + struct lenovo_ymc_private { struct input_dev *input_dev; struct acpi_device *ec_acpi_dev; @@ -111,6 +129,13 @@ static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx) struct input_dev *input_dev; int err; + if (!dmi_check_system(allowed_chasis_types_dmi_table)) { + if (force) + dev_info(&wdev->dev, "Force loading Lenovo YMC support\n"); + else + return -ENODEV; + } + ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table); priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 67367f010139..7d33977d9c60 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -62,10 +62,6 @@ #define MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET 0x37 #define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a #define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b -#define MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET 0x3c -#define MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET 0x3d -#define MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET 0x3e -#define MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET 0x3f #define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41 #define MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET 0x42 @@ -126,6 +122,10 @@ #define MLXPLAT_CPLD_LPC_REG_LC_SD_EVENT_OFFSET 0xaa #define MLXPLAT_CPLD_LPC_REG_LC_SD_MASK_OFFSET 0xab #define MLXPLAT_CPLD_LPC_REG_LC_PWR_ON 0xb2 +#define MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET 0xb6 +#define MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET 0xb7 +#define MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET 0xb8 +#define MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET 0xb9 #define MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET 0xc2 #define MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT 0xc3 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET 0xc7 @@ -222,7 +222,7 @@ MLXPLAT_CPLD_AGGR_MASK_LC_SDWN) #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1 #define MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2 BIT(2) -#define MLXPLAT_CPLD_LOW_AGGR_MASK_PWR_BUT BIT(4) +#define MLXPLAT_CPLD_LOW_AGGR_MASK_PWR_BUT GENMASK(5, 4) #define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6) #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) @@ -237,7 +237,7 @@ #define MLXPLAT_CPLD_GWP_MASK GENMASK(0, 0) #define MLXPLAT_CPLD_EROT_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PWR_BUTTON_MASK BIT(0) -#define MLXPLAT_CPLD_LATCH_RST_MASK BIT(5) +#define MLXPLAT_CPLD_LATCH_RST_MASK BIT(6) #define MLXPLAT_CPLD_THERMAL1_PDB_MASK BIT(3) #define MLXPLAT_CPLD_THERMAL2_PDB_MASK BIT(4) #define MLXPLAT_CPLD_INTRUSION_MASK BIT(6) @@ -2356,7 +2356,7 @@ mlxplat_mlxcpld_l1_switch_pwr_events_handler(void *handle, enum mlxreg_hotplug_k u8 action) { dev_info(&mlxplat_dev->dev, "System shutdown due to short press of power button"); - kernel_halt(); + kernel_power_off(); return 0; } @@ -2475,7 +2475,7 @@ static struct mlxreg_core_item mlxplat_mlxcpld_l1_switch_events_items[] = { .reg = MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET, .mask = MLXPLAT_CPLD_PWR_BUTTON_MASK, .count = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_pwr_events_items_data), - .inversed = 0, + .inversed = 1, .health = false, }, { @@ -2484,7 +2484,7 @@ static struct mlxreg_core_item mlxplat_mlxcpld_l1_switch_events_items[] = { .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, .mask = MLXPLAT_CPLD_L1_CHA_HEALTH_MASK, .count = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_health_events_items_data), - .inversed = 0, + .inversed = 1, .health = false, .ind = 8, }, @@ -3677,7 +3677,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { { .label = "latch_reset", .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, - .mask = GENMASK(7, 0) & ~BIT(5), + .mask = GENMASK(7, 0) & ~BIT(6), .mode = 0200, }, { @@ -6238,8 +6238,6 @@ static void mlxplat_i2c_mux_topolgy_exit(struct mlxplat_priv *priv) if (priv->pdev_mux[i]) platform_device_unregister(priv->pdev_mux[i]); } - - mlxplat_post_exit(); } static int mlxplat_i2c_main_complition_notify(void *handle, int id) @@ -6369,6 +6367,7 @@ static void __exit mlxplat_exit(void) pm_power_off = NULL; mlxplat_pre_exit(priv); mlxplat_i2c_main_exit(priv); + mlxplat_post_exit(); } module_exit(mlxplat_exit); diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c index ff93986e3d35..f26a3121092f 100644 --- a/drivers/platform/x86/msi-ec.c +++ b/drivers/platform/x86/msi-ec.c @@ -27,15 +27,15 @@ #include <linux/seq_file.h> #include <linux/string.h> -static const char *const SM_ECO_NAME = "eco"; -static const char *const SM_COMFORT_NAME = "comfort"; -static const char *const SM_SPORT_NAME = "sport"; -static const char *const SM_TURBO_NAME = "turbo"; - -static const char *const FM_AUTO_NAME = "auto"; -static const char *const FM_SILENT_NAME = "silent"; -static const char *const FM_BASIC_NAME = "basic"; -static const char *const FM_ADVANCED_NAME = "advanced"; +#define SM_ECO_NAME "eco" +#define SM_COMFORT_NAME "comfort" +#define SM_SPORT_NAME "sport" +#define SM_TURBO_NAME "turbo" + +#define FM_AUTO_NAME "auto" +#define FM_SILENT_NAME "silent" +#define FM_BASIC_NAME "basic" +#define FM_ADVANCED_NAME "advanced" static const char * const ALLOWED_FW_0[] __initconst = { "14C1EMS1.012", diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 2c2abf69f049..8158e3cf5d6d 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -329,6 +329,19 @@ static const struct smi_node cs35l41_hda = { .bus_type = SMI_AUTO_DETECT, }; +static const struct smi_node cs35l56_hda = { + .instances = { + { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 }, + { "cs35l56-hda", IRQ_RESOURCE_AUTO, 0 }, + /* a 5th entry is an alias address, not a real device */ + { "cs35l56-hda_dummy_dev" }, + {} + }, + .bus_type = SMI_AUTO_DETECT, +}; + /* * Note new device-ids must also be added to ignore_serial_bus_ids in * drivers/acpi/scan.c: acpi_device_enumeration_by_parent(). @@ -337,6 +350,7 @@ static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, + { "CSC3556", (unsigned long)&cs35l56_hda }, { "INT3515", (unsigned long)&int3515_data }, /* Non-conforming _HID for Cirrus Logic already released */ { "CLSA0100", (unsigned long)&cs35l41_hda }, diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 4e646e5e48f6..8fac57b28f8a 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -818,7 +818,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, return -EINVAL; ra.reg = rd->regs[rpi->id]; - if (!ra.reg) + if (!ra.reg.val) return -EINVAL; /* non-hardware data are collected by the polling thread */ @@ -830,7 +830,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, ra.mask = rpi->mask; if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { - pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg, rd->rp->name, rd->name); + pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name); return -EIO; } @@ -920,7 +920,7 @@ static int rapl_check_unit_core(struct rapl_domain *rd) ra.mask = ~0; if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", - ra.reg, rd->rp->name, rd->name); + ra.reg.val, rd->rp->name, rd->name); return -ENODEV; } @@ -948,7 +948,7 @@ static int rapl_check_unit_atom(struct rapl_domain *rd) ra.mask = ~0; if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", - ra.reg, rd->rp->name, rd->name); + ra.reg.val, rd->rp->name, rd->name); return -ENODEV; } @@ -1135,7 +1135,7 @@ static int rapl_check_unit_tpmi(struct rapl_domain *rd) ra.mask = ~0; if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", - ra.reg, rd->rp->name, rd->name); + ra.reg.val, rd->rp->name, rd->name); return -ENODEV; } @@ -1411,8 +1411,8 @@ static int rapl_get_domain_unit(struct rapl_domain *rd) struct rapl_defaults *defaults = get_defaults(rd->rp); int ret; - if (!rd->regs[RAPL_DOMAIN_REG_UNIT]) { - if (!rd->rp->priv->reg_unit) { + if (!rd->regs[RAPL_DOMAIN_REG_UNIT].val) { + if (!rd->rp->priv->reg_unit.val) { pr_err("No valid Unit register found\n"); return -ENODEV; } diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c index 569e25eab1e1..dd471021f237 100644 --- a/drivers/powercap/intel_rapl_msr.c +++ b/drivers/powercap/intel_rapl_msr.c @@ -34,28 +34,32 @@ static struct rapl_if_priv *rapl_msr_priv; static struct rapl_if_priv rapl_msr_priv_intel = { .type = RAPL_IF_MSR, - .reg_unit = MSR_RAPL_POWER_UNIT, - .regs[RAPL_DOMAIN_PACKAGE] = { - MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO }, - .regs[RAPL_DOMAIN_PP0] = { - MSR_PP0_POWER_LIMIT, MSR_PP0_ENERGY_STATUS, 0, MSR_PP0_POLICY, 0 }, - .regs[RAPL_DOMAIN_PP1] = { - MSR_PP1_POWER_LIMIT, MSR_PP1_ENERGY_STATUS, 0, MSR_PP1_POLICY, 0 }, - .regs[RAPL_DOMAIN_DRAM] = { - MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO }, - .regs[RAPL_DOMAIN_PLATFORM] = { - MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0}, + .reg_unit.msr = MSR_RAPL_POWER_UNIT, + .regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_LIMIT].msr = MSR_PKG_POWER_LIMIT, + .regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_STATUS].msr = MSR_PKG_ENERGY_STATUS, + .regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_PERF].msr = MSR_PKG_PERF_STATUS, + .regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_INFO].msr = MSR_PKG_POWER_INFO, + .regs[RAPL_DOMAIN_PP0][RAPL_DOMAIN_REG_LIMIT].msr = MSR_PP0_POWER_LIMIT, + .regs[RAPL_DOMAIN_PP0][RAPL_DOMAIN_REG_STATUS].msr = MSR_PP0_ENERGY_STATUS, + .regs[RAPL_DOMAIN_PP0][RAPL_DOMAIN_REG_POLICY].msr = MSR_PP0_POLICY, + .regs[RAPL_DOMAIN_PP1][RAPL_DOMAIN_REG_LIMIT].msr = MSR_PP1_POWER_LIMIT, + .regs[RAPL_DOMAIN_PP1][RAPL_DOMAIN_REG_STATUS].msr = MSR_PP1_ENERGY_STATUS, + .regs[RAPL_DOMAIN_PP1][RAPL_DOMAIN_REG_POLICY].msr = MSR_PP1_POLICY, + .regs[RAPL_DOMAIN_DRAM][RAPL_DOMAIN_REG_LIMIT].msr = MSR_DRAM_POWER_LIMIT, + .regs[RAPL_DOMAIN_DRAM][RAPL_DOMAIN_REG_STATUS].msr = MSR_DRAM_ENERGY_STATUS, + .regs[RAPL_DOMAIN_DRAM][RAPL_DOMAIN_REG_PERF].msr = MSR_DRAM_PERF_STATUS, + .regs[RAPL_DOMAIN_DRAM][RAPL_DOMAIN_REG_INFO].msr = MSR_DRAM_POWER_INFO, + .regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_LIMIT].msr = MSR_PLATFORM_POWER_LIMIT, + .regs[RAPL_DOMAIN_PLATFORM][RAPL_DOMAIN_REG_STATUS].msr = MSR_PLATFORM_ENERGY_STATUS, .limits[RAPL_DOMAIN_PACKAGE] = BIT(POWER_LIMIT2), .limits[RAPL_DOMAIN_PLATFORM] = BIT(POWER_LIMIT2), }; static struct rapl_if_priv rapl_msr_priv_amd = { .type = RAPL_IF_MSR, - .reg_unit = MSR_AMD_RAPL_POWER_UNIT, - .regs[RAPL_DOMAIN_PACKAGE] = { - 0, MSR_AMD_PKG_ENERGY_STATUS, 0, 0, 0 }, - .regs[RAPL_DOMAIN_PP0] = { - 0, MSR_AMD_CORE_ENERGY_STATUS, 0, 0, 0 }, + .reg_unit.msr = MSR_AMD_RAPL_POWER_UNIT, + .regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_STATUS].msr = MSR_AMD_PKG_ENERGY_STATUS, + .regs[RAPL_DOMAIN_PP0][RAPL_DOMAIN_REG_STATUS].msr = MSR_AMD_CORE_ENERGY_STATUS, }; /* Handles CPU hotplug on multi-socket systems. @@ -99,10 +103,8 @@ static int rapl_cpu_down_prep(unsigned int cpu) static int rapl_msr_read_raw(int cpu, struct reg_action *ra) { - u32 msr = (u32)ra->reg; - - if (rdmsrl_safe_on_cpu(cpu, msr, &ra->value)) { - pr_debug("failed to read msr 0x%x on cpu %d\n", msr, cpu); + if (rdmsrl_safe_on_cpu(cpu, ra->reg.msr, &ra->value)) { + pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg.msr, cpu); return -EIO; } ra->value &= ra->mask; @@ -112,17 +114,16 @@ static int rapl_msr_read_raw(int cpu, struct reg_action *ra) static void rapl_msr_update_func(void *info) { struct reg_action *ra = info; - u32 msr = (u32)ra->reg; u64 val; - ra->err = rdmsrl_safe(msr, &val); + ra->err = rdmsrl_safe(ra->reg.msr, &val); if (ra->err) return; val &= ~ra->mask; val |= ra->value; - ra->err = wrmsrl_safe(msr, val); + ra->err = wrmsrl_safe(ra->reg.msr, val); } static int rapl_msr_write_raw(int cpu, struct reg_action *ra) @@ -171,7 +172,7 @@ static int rapl_msr_probe(struct platform_device *pdev) if (id) { rapl_msr_priv->limits[RAPL_DOMAIN_PACKAGE] |= BIT(POWER_LIMIT4); - rapl_msr_priv->regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_PL4] = + rapl_msr_priv->regs[RAPL_DOMAIN_PACKAGE][RAPL_DOMAIN_REG_PL4].msr = MSR_VR_CURRENT_CONFIG; pr_info("PL4 support detected.\n"); } diff --git a/drivers/powercap/intel_rapl_tpmi.c b/drivers/powercap/intel_rapl_tpmi.c index 4f4f13ded225..891c90fefd8b 100644 --- a/drivers/powercap/intel_rapl_tpmi.c +++ b/drivers/powercap/intel_rapl_tpmi.c @@ -59,10 +59,10 @@ static struct powercap_control_type *tpmi_control_type; static int tpmi_rapl_read_raw(int id, struct reg_action *ra) { - if (!ra->reg) + if (!ra->reg.mmio) return -EINVAL; - ra->value = readq((void __iomem *)ra->reg); + ra->value = readq(ra->reg.mmio); ra->value &= ra->mask; return 0; @@ -72,15 +72,15 @@ static int tpmi_rapl_write_raw(int id, struct reg_action *ra) { u64 val; - if (!ra->reg) + if (!ra->reg.mmio) return -EINVAL; - val = readq((void __iomem *)ra->reg); + val = readq(ra->reg.mmio); val &= ~ra->mask; val |= ra->value; - writeq(val, (void __iomem *)ra->reg); + writeq(val, ra->reg.mmio); return 0; } @@ -138,8 +138,7 @@ static int parse_one_domain(struct tpmi_rapl_package *trp, u32 offset) enum tpmi_rapl_register reg_index; enum rapl_domain_reg_id reg_id; int tpmi_domain_size, tpmi_domain_flags; - u64 *tpmi_rapl_regs = trp->base + offset; - u64 tpmi_domain_header = readq((void __iomem *)tpmi_rapl_regs); + u64 tpmi_domain_header = readq(trp->base + offset); /* Domain Parent bits are ignored for now */ tpmi_domain_version = tpmi_domain_header & 0xff; @@ -180,7 +179,7 @@ static int parse_one_domain(struct tpmi_rapl_package *trp, u32 offset) return -EINVAL; } - if (trp->priv.regs[domain_type][RAPL_DOMAIN_REG_UNIT]) { + if (trp->priv.regs[domain_type][RAPL_DOMAIN_REG_UNIT].mmio) { pr_warn(FW_BUG "Duplicate Domain type %d\n", tpmi_domain_type); return -EINVAL; } @@ -218,7 +217,7 @@ static int parse_one_domain(struct tpmi_rapl_package *trp, u32 offset) default: continue; } - trp->priv.regs[domain_type][reg_id] = (u64)&tpmi_rapl_regs[reg_index]; + trp->priv.regs[domain_type][reg_id].mmio = trp->base + offset + reg_index * 8; } return 0; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 1d195429753d..613eab729704 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -716,7 +716,6 @@ struct qeth_card_info { u16 chid; u8 ids_valid:1; /* cssid,iid,chid */ u8 dev_addr_is_registered:1; - u8 open_when_online:1; u8 promisc_mode:1; u8 use_v1_blkt:1; u8 is_vm_nic:1; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 1d5b207c2b9e..cd783290bde5 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5373,8 +5373,6 @@ int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc, qeth_clear_ipacmd_list(card); rtnl_lock(); - card->info.open_when_online = card->dev->flags & IFF_UP; - dev_close(card->dev); netif_device_detach(card->dev); netif_carrier_off(card->dev); rtnl_unlock(); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 9f13ed170a43..75910c0bcc2b 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -2388,9 +2388,12 @@ static int qeth_l2_set_online(struct qeth_card *card, bool carrier_ok) qeth_enable_hw_features(dev); qeth_l2_enable_brport_features(card); - if (card->info.open_when_online) { - card->info.open_when_online = 0; - dev_open(dev, NULL); + if (netif_running(dev)) { + local_bh_disable(); + napi_schedule(&card->napi); + /* kick-start the NAPI softirq: */ + local_bh_enable(); + qeth_l2_set_rx_mode(dev); } rtnl_unlock(); } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index af4e60d2917e..b92a32b4b114 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2018,9 +2018,11 @@ static int qeth_l3_set_online(struct qeth_card *card, bool carrier_ok) netif_device_attach(dev); qeth_enable_hw_features(dev); - if (card->info.open_when_online) { - card->info.open_when_online = 0; - dev_open(dev, NULL); + if (netif_running(dev)) { + local_bh_disable(); + napi_schedule(&card->napi); + /* kick-start the NAPI softirq: */ + local_bh_enable(); } rtnl_unlock(); } diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index f21307537829..4f0d0e55f0d4 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -534,8 +534,7 @@ static void zfcp_fc_adisc_handler(void *data) /* re-init to undo drop from zfcp_fc_adisc() */ port->d_id = ntoh24(adisc_resp->adisc_port_id); - /* port is good, unblock rport without going through erp */ - zfcp_scsi_schedule_rport_register(port); + /* port is still good, nothing to do */ out: atomic_andnot(ZFCP_STATUS_PORT_LINK_TEST, &port->status); put_device(&port->dev); @@ -595,9 +594,6 @@ void zfcp_fc_link_test_work(struct work_struct *work) int retval; set_worker_desc("zadisc%16llx", port->wwpn); /* < WORKER_DESC_LEN=24 */ - get_device(&port->dev); - port->rport_task = RPORT_DEL; - zfcp_scsi_rport_work(&port->rport_work); /* only issue one test command at one time per port */ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_LINK_TEST) diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index e1e4f9d10887..857be0f3ae5b 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -1598,7 +1598,7 @@ NCR_700_intr(int irq, void *dev_id) printk("scsi%d (%d:%d) PHASE MISMATCH IN SEND MESSAGE %d remain, return %p[%04x], phase %s\n", host->host_no, pun, lun, count, (void *)temp, temp - hostdata->pScript, sbcl_to_string(NCR_700_readb(host, SBCL_REG))); #endif resume_offset = hostdata->pScript + Ent_SendMessagePhaseMismatch; - } else if(dsp >= to32bit(&slot->pSG[0].ins) && + } else if (slot && dsp >= to32bit(&slot->pSG[0].ins) && dsp <= to32bit(&slot->pSG[NCR_700_SG_SEGMENTS].ins)) { int data_transfer = NCR_700_readl(host, DBC_REG) & 0xffffff; int SGcount = (dsp - to32bit(&slot->pSG[0].ins))/sizeof(struct NCR_700_SG_List); diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index d82de34f6fd7..e51e92f932fa 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -27,7 +27,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.54" +#define DRV_VERSION "1.6.0.55" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 26dbd347156e..be89ce96df46 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -2139,7 +2139,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic, bool new_sc) { - int ret = SUCCESS; + int ret = 0; struct fnic_pending_aborts_iter_data iter_data = { .fnic = fnic, .lun_dev = lr_sc->device, @@ -2159,9 +2159,11 @@ static int fnic_clean_pending_aborts(struct fnic *fnic, /* walk again to check, if IOs are still pending in fw */ if (fnic_is_abts_pending(fnic, lr_sc)) - ret = FAILED; + ret = 1; clean_pending_aborts_end: + FNIC_SCSI_DBG(KERN_INFO, fnic->lport->host, + "%s: exit status: %d\n", __func__, ret); return ret; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index a62e091894f6..d26941b131fd 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -109,8 +109,6 @@ lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba, } } -#define LPFC_INVALID_REFTAG ((u32)-1) - /** * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread * @phba: The Hba for which this call is being executed. @@ -978,8 +976,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, sgpe = scsi_prot_sglist(sc); lba = scsi_prot_ref_tag(sc); - if (lba == LPFC_INVALID_REFTAG) - return 0; /* First check if we need to match the LBA */ if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) { @@ -1560,8 +1556,6 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* extract some info from the scsi command for pde*/ reftag = scsi_prot_ref_tag(sc); - if (reftag == LPFC_INVALID_REFTAG) - goto out; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); @@ -1723,8 +1717,6 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* extract some info from the scsi command */ blksize = scsi_prot_interval(sc); reftag = scsi_prot_ref_tag(sc); - if (reftag == LPFC_INVALID_REFTAG) - goto out; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); @@ -1953,8 +1945,6 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* extract some info from the scsi command for pde*/ reftag = scsi_prot_ref_tag(sc); - if (reftag == LPFC_INVALID_REFTAG) - goto out; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); @@ -2154,8 +2144,6 @@ lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* extract some info from the scsi command */ blksize = scsi_prot_interval(sc); reftag = scsi_prot_ref_tag(sc); - if (reftag == LPFC_INVALID_REFTAG) - goto out; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); @@ -2746,8 +2734,6 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_io_buf *lpfc_cmd) src = (struct scsi_dif_tuple *)sg_virt(sgpe); start_ref_tag = scsi_prot_ref_tag(cmd); - if (start_ref_tag == LPFC_INVALID_REFTAG) - goto out; start_app_tag = src->app_tag; len = sgpe->length; while (src && protsegcnt) { @@ -3493,11 +3479,11 @@ err: scsi_cmnd->sc_data_direction); lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, - "9084 Cannot setup S/G List for HBA" - "IO segs %d/%d SGL %d SCSI %d: %d %d\n", + "9084 Cannot setup S/G List for HBA " + "IO segs %d/%d SGL %d SCSI %d: %d %d %d\n", lpfc_cmd->seg_cnt, lpfc_cmd->prot_seg_cnt, phba->cfg_total_seg_cnt, phba->cfg_sg_seg_cnt, - prot_group_type, num_sge); + prot_group_type, num_sge, ret); lpfc_cmd->seg_cnt = 0; lpfc_cmd->prot_seg_cnt = 0; diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 2e886c1d867d..4995e1ef4e0e 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -1181,7 +1181,8 @@ static int pm8001_pci_probe(struct pci_dev *pdev, pm80xx_set_thermal_config(pm8001_ha); } - if (pm8001_init_sas_add(pm8001_ha)) + rc = pm8001_init_sas_add(pm8001_ha); + if (rc) goto err_out_shost; /* phy setting support for motherboard controller */ rc = pm8001_configure_phy_settings(pm8001_ha); diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 2a31ddc99dde..7825765c936c 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -31,6 +31,7 @@ static void qedf_remove(struct pci_dev *pdev); static void qedf_shutdown(struct pci_dev *pdev); static void qedf_schedule_recovery_handler(void *dev); static void qedf_recovery_handler(struct work_struct *work); +static int qedf_suspend(struct pci_dev *pdev, pm_message_t state); /* * Driver module parameters. @@ -3271,6 +3272,7 @@ static struct pci_driver qedf_pci_driver = { .probe = qedf_probe, .remove = qedf_remove, .shutdown = qedf_shutdown, + .suspend = qedf_suspend, }; static int __qedf_probe(struct pci_dev *pdev, int mode) @@ -4000,6 +4002,22 @@ static void qedf_shutdown(struct pci_dev *pdev) __qedf_remove(pdev, QEDF_MODE_NORMAL); } +static int qedf_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct qedf_ctx *qedf; + + if (!pdev) { + QEDF_ERR(NULL, "pdev is NULL.\n"); + return -ENODEV; + } + + qedf = pci_get_drvdata(pdev); + + QEDF_ERR(&qedf->dbg_ctx, "%s: Device does not support suspend operation\n", __func__); + + return -EPERM; +} + /* * Recovery handler code */ diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 450522b204d6..cd0180b1f5b9 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -69,6 +69,7 @@ static struct nvm_iscsi_block *qedi_get_nvram_block(struct qedi_ctx *qedi); static void qedi_recovery_handler(struct work_struct *work); static void qedi_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type); +static int qedi_suspend(struct pci_dev *pdev, pm_message_t state); static int qedi_iscsi_event_cb(void *context, u8 fw_event_code, void *fw_handle) { @@ -1976,8 +1977,9 @@ static int qedi_cpu_offline(unsigned int cpu) struct qedi_percpu_s *p = this_cpu_ptr(&qedi_percpu); struct qedi_work *work, *tmp; struct task_struct *thread; + unsigned long flags; - spin_lock_bh(&p->p_work_lock); + spin_lock_irqsave(&p->p_work_lock, flags); thread = p->iothread; p->iothread = NULL; @@ -1988,7 +1990,7 @@ static int qedi_cpu_offline(unsigned int cpu) kfree(work); } - spin_unlock_bh(&p->p_work_lock); + spin_unlock_irqrestore(&p->p_work_lock, flags); if (thread) kthread_stop(thread); return 0; @@ -2510,6 +2512,22 @@ static void qedi_shutdown(struct pci_dev *pdev) __qedi_remove(pdev, QEDI_MODE_SHUTDOWN); } +static int qedi_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct qedi_ctx *qedi; + + if (!pdev) { + QEDI_ERR(NULL, "pdev is NULL.\n"); + return -ENODEV; + } + + qedi = pci_get_drvdata(pdev); + + QEDI_ERR(&qedi->dbg_ctx, "%s: Device does not support suspend operation\n", __func__); + + return -EPERM; +} + static int __qedi_probe(struct pci_dev *pdev, int mode) { struct qedi_ctx *qedi; @@ -2868,6 +2886,7 @@ static struct pci_driver qedi_pci_driver = { .remove = qedi_remove, .shutdown = qedi_shutdown, .err_handler = &qedi_err_handler, + .suspend = qedi_suspend, }; static int __init qedi_init(void) diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c index 898a0bdf8df6..711252e52d8e 100644 --- a/drivers/scsi/raid_class.c +++ b/drivers/scsi/raid_class.c @@ -248,6 +248,7 @@ int raid_component_add(struct raid_template *r,struct device *raid_dev, return 0; err_out: + put_device(&rc->dev); list_del(&rc->node); rd->component_count--; put_device(component_dev); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 4a6eb1741be0..41f23cd0bfb4 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -406,7 +406,7 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf, size_t length, loff_t *ppos) { int host, channel, id, lun; - char *buffer, *p; + char *buffer, *end, *p; int err; if (!buf || length > PAGE_SIZE) @@ -421,10 +421,14 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf, goto out; err = -EINVAL; - if (length < PAGE_SIZE) - buffer[length] = '\0'; - else if (buffer[PAGE_SIZE-1]) - goto out; + if (length < PAGE_SIZE) { + end = buffer + length; + *end = '\0'; + } else { + end = buffer + PAGE_SIZE - 1; + if (*end) + goto out; + } /* * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi @@ -433,10 +437,10 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf, if (!strncmp("scsi add-single-device", buffer, 22)) { p = buffer + 23; - host = simple_strtoul(p, &p, 0); - channel = simple_strtoul(p + 1, &p, 0); - id = simple_strtoul(p + 1, &p, 0); - lun = simple_strtoul(p + 1, &p, 0); + host = (p < end) ? simple_strtoul(p, &p, 0) : 0; + channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; + id = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; + lun = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; err = scsi_add_single_device(host, channel, id, lun); @@ -447,10 +451,10 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf, } else if (!strncmp("scsi remove-single-device", buffer, 25)) { p = buffer + 26; - host = simple_strtoul(p, &p, 0); - channel = simple_strtoul(p + 1, &p, 0); - id = simple_strtoul(p + 1, &p, 0); - lun = simple_strtoul(p + 1, &p, 0); + host = (p < end) ? simple_strtoul(p, &p, 0) : 0; + channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; + id = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; + lun = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; err = scsi_remove_single_device(host, channel, id, lun); } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 68b12afa0721..3c668cfb146d 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3876,7 +3876,7 @@ static int sd_suspend_runtime(struct device *dev) static int sd_resume(struct device *dev) { struct scsi_disk *sdkp = dev_get_drvdata(dev); - int ret; + int ret = 0; if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */ return 0; @@ -3884,8 +3884,11 @@ static int sd_resume(struct device *dev) if (!sdkp->device->manage_start_stop) return 0; - sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); - ret = sd_start_stop_device(sdkp, 1); + if (!sdkp->device->no_start_on_resume) { + sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); + ret = sd_start_stop_device(sdkp, 1); + } + if (!ret) opal_unlock_from_suspend(sdkp->opal_dev); return ret; diff --git a/drivers/scsi/snic/snic_disc.c b/drivers/scsi/snic/snic_disc.c index 3e2e5783924d..e429ad23c396 100644 --- a/drivers/scsi/snic/snic_disc.c +++ b/drivers/scsi/snic/snic_disc.c @@ -303,6 +303,7 @@ snic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid) "Snic Tgt: device_add, with err = %d\n", ret); + put_device(&tgt->dev); put_device(&snic->shost->shost_gendev); spin_lock_irqsave(snic->shost->host_lock, flags); list_del(&tgt->list); diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 7f12d931fe7c..047ffaf7d42a 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -366,6 +366,7 @@ static void storvsc_on_channel_callback(void *context); #define STORVSC_FC_MAX_LUNS_PER_TARGET 255 #define STORVSC_FC_MAX_TARGETS 128 #define STORVSC_FC_MAX_CHANNELS 8 +#define STORVSC_FC_MAX_XFER_SIZE ((u32)(512 * 1024)) #define STORVSC_IDE_MAX_LUNS_PER_TARGET 64 #define STORVSC_IDE_MAX_TARGETS 1 @@ -1673,10 +1674,6 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) */ static enum scsi_timeout_action storvsc_eh_timed_out(struct scsi_cmnd *scmnd) { -#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS) - if (scmnd->device->host->transportt == fc_transport_template) - return fc_eh_timed_out(scmnd); -#endif return SCSI_EH_RESET_TIMER; } @@ -2006,6 +2003,9 @@ static int storvsc_probe(struct hv_device *device, * protecting it from any weird value. */ max_xfer_bytes = round_down(stor_device->max_transfer_bytes, HV_HYP_PAGE_SIZE); + if (is_fc) + max_xfer_bytes = min(max_xfer_bytes, STORVSC_FC_MAX_XFER_SIZE); + /* max_hw_sectors_kb */ host->max_sectors = max_xfer_bytes >> 9; /* diff --git a/drivers/soc/imx/imx8mp-blk-ctrl.c b/drivers/soc/imx/imx8mp-blk-ctrl.c index 870aecc0202a..1c1fcab4979a 100644 --- a/drivers/soc/imx/imx8mp-blk-ctrl.c +++ b/drivers/soc/imx/imx8mp-blk-ctrl.c @@ -164,7 +164,7 @@ static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) clk_hsio_pll->hw.init = &init; hw = &clk_hsio_pll->hw; - ret = devm_clk_hw_register(bc->dev, hw); + ret = devm_clk_hw_register(bc->bus_power_dev, hw); if (ret) return ret; diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index cf78839b3f74..1cc2281cb370 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -3,6 +3,7 @@ #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/irq.h> #include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_registers.h> @@ -25,6 +26,23 @@ static int sdw_get_id(struct sdw_bus *bus) return 0; } +static int sdw_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct sdw_bus *bus = h->host_data; + + irq_set_chip_data(virq, bus); + irq_set_chip(virq, &bus->irq_chip); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops sdw_domain_ops = { + .map = sdw_irq_map, +}; + /** * sdw_bus_master_add() - add a bus Master instance * @bus: bus instance @@ -151,6 +169,14 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, bus->params.curr_bank = SDW_BANK0; bus->params.next_bank = SDW_BANK1; + bus->irq_chip.name = dev_name(bus->dev); + bus->domain = irq_domain_create_linear(fwnode, SDW_MAX_DEVICES, + &sdw_domain_ops, bus); + if (!bus->domain) { + dev_err(bus->dev, "Failed to add IRQ domain\n"); + return -EINVAL; + } + return 0; } EXPORT_SYMBOL(sdw_bus_master_add); @@ -187,6 +213,9 @@ static int sdw_delete_slave(struct device *dev, void *data) void sdw_bus_master_delete(struct sdw_bus *bus) { device_for_each_child(bus->dev, NULL, sdw_delete_slave); + + irq_domain_remove(bus->domain); + sdw_master_device_del(bus); sdw_bus_debugfs_exit(bus); @@ -1725,6 +1754,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) struct device *dev = &slave->dev; struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); + if (slave->prop.use_domain_irq && slave->irq) + handle_nested_irq(slave->irq); + if (drv->ops && drv->ops->interrupt_callback) { slave_intr.sdca_cascade = sdca_cascade; slave_intr.control_port = clear; diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 1f43ee848eac..fafbc284e82d 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -122,6 +122,12 @@ static int sdw_drv_probe(struct device *dev) if (drv->ops && drv->ops->read_prop) drv->ops->read_prop(slave); + if (slave->prop.use_domain_irq) { + slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num); + if (!slave->irq) + dev_warn(dev, "Failed to map IRQ\n"); + } + /* init the sysfs as we have properties now */ ret = sdw_slave_sysfs_init(slave); if (ret < 0) @@ -166,7 +172,13 @@ static int sdw_drv_remove(struct device *dev) int ret = 0; mutex_lock(&slave->sdw_dev_lock); + slave->probed = false; + + if (slave->prop.use_domain_irq) + irq_dispose_mapping(irq_find_mapping(slave->bus->domain, + slave->dev_num)); + mutex_unlock(&slave->sdw_dev_lock); if (drv->remove) diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c index 013f1633f082..2f00fc3bf274 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c @@ -57,10 +57,10 @@ static int rapl_mmio_cpu_down_prep(unsigned int cpu) static int rapl_mmio_read_raw(int cpu, struct reg_action *ra) { - if (!ra->reg) + if (!ra->reg.mmio) return -EINVAL; - ra->value = readq((void __iomem *)ra->reg); + ra->value = readq(ra->reg.mmio); ra->value &= ra->mask; return 0; } @@ -69,13 +69,13 @@ static int rapl_mmio_write_raw(int cpu, struct reg_action *ra) { u64 val; - if (!ra->reg) + if (!ra->reg.mmio) return -EINVAL; - val = readq((void __iomem *)ra->reg); + val = readq(ra->reg.mmio); val &= ~ra->mask; val |= ra->value; - writeq(val, (void __iomem *)ra->reg); + writeq(val, ra->reg.mmio); return 0; } @@ -92,13 +92,13 @@ int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc for (domain = RAPL_DOMAIN_PACKAGE; domain < RAPL_DOMAIN_MAX; domain++) { for (reg = RAPL_DOMAIN_REG_LIMIT; reg < RAPL_DOMAIN_REG_MAX; reg++) if (rapl_regs->regs[domain][reg]) - rapl_mmio_priv.regs[domain][reg] = - (u64)proc_priv->mmio_base + + rapl_mmio_priv.regs[domain][reg].mmio = + proc_priv->mmio_base + rapl_regs->regs[domain][reg]; rapl_mmio_priv.limits[domain] = rapl_regs->limits[domain]; } rapl_mmio_priv.type = RAPL_IF_MMIO; - rapl_mmio_priv.reg_unit = (u64)proc_priv->mmio_base + rapl_regs->reg_unit; + rapl_mmio_priv.reg_unit.mmio = proc_priv->mmio_base + rapl_regs->reg_unit; rapl_mmio_priv.read_raw = rapl_mmio_read_raw; rapl_mmio_priv.write_raw = rapl_mmio_write_raw; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 62b26b7998fd..3fb4553a6442 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1964,6 +1964,8 @@ unlock: pm_runtime_mark_last_busy(&tb->dev); pm_runtime_put_autosuspend(&tb->dev); + + kfree(ev); } static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 1269f417515b..0dfd1e083994 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -579,7 +579,9 @@ int tb_switch_tmu_disable(struct tb_switch *sw) * uni-directional mode and we don't want to change it's TMU * mode. */ - tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); + ret = tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); + if (ret) + return ret; tb_port_tmu_time_sync_disable(up); ret = tb_port_tmu_time_sync_disable(down); diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c index f8a5e79ed3b4..ab0652d8705a 100644 --- a/drivers/ufs/host/ufs-renesas.c +++ b/drivers/ufs/host/ufs-renesas.c @@ -359,7 +359,7 @@ static int ufs_renesas_init(struct ufs_hba *hba) { struct ufs_renesas_priv *priv; - priv = devm_kmalloc(hba->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(hba->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; ufshcd_set_variant(hba, priv); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 766005d20bae..501e8bc9738e 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -42,6 +42,7 @@ struct usb_conn_info { struct power_supply_desc desc; struct power_supply *charger; + bool initial_detection; }; /* @@ -86,11 +87,13 @@ static void usb_conn_detect_cable(struct work_struct *work) dev_dbg(info->dev, "role %s -> %s, gpios: id %d, vbus %d\n", usb_role_string(info->last_role), usb_role_string(role), id, vbus); - if (info->last_role == role) { + if (!info->initial_detection && info->last_role == role) { dev_warn(info->dev, "repeated role: %s\n", usb_role_string(role)); return; } + info->initial_detection = false; + if (info->last_role == USB_ROLE_HOST && info->vbus) regulator_disable(info->vbus); @@ -258,6 +261,7 @@ static int usb_conn_probe(struct platform_device *pdev) device_set_wakeup_capable(&pdev->dev, true); /* Perform initial detection */ + info->initial_detection = true; usb_conn_queue_dwork(info, 0); return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5fd067151fbf..858fe4c299b7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -4455,9 +4455,14 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) u32 count; if (pm_runtime_suspended(dwc->dev)) { + dwc->pending_events = true; + /* + * Trigger runtime resume. The get() function will be balanced + * after processing the pending events in dwc3_process_pending + * events(). + */ pm_runtime_get(dwc->dev); disable_irq_nosync(dwc->irq_gadget); - dwc->pending_events = true; return IRQ_HANDLED; } @@ -4718,6 +4723,8 @@ void dwc3_gadget_process_pending_events(struct dwc3 *dwc) { if (dwc->pending_events) { dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf); + dwc3_thread_interrupt(dwc->irq_gadget, dwc->ev_buf); + pm_runtime_put(dwc->dev); dwc->pending_events = false; enable_irq(dwc->irq_gadget); } diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index cd58f2a4e7f3..7d49d8a0b00c 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -822,6 +822,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_disconnect); * usb_gadget_activate() is called. For example, user mode components may * need to be activated before the system can talk to hosts. * + * This routine may sleep; it must not be called in interrupt context + * (such as from within a gadget driver's disconnect() callback). + * * Returns zero on success, else negative errno. */ int usb_gadget_deactivate(struct usb_gadget *gadget) @@ -860,6 +863,8 @@ EXPORT_SYMBOL_GPL(usb_gadget_deactivate); * This routine activates gadget which was previously deactivated with * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. * + * This routine may sleep; it must not be called in interrupt context. + * * Returns zero on success, else negative errno. */ int usb_gadget_activate(struct usb_gadget *gadget) @@ -1638,7 +1643,11 @@ static void gadget_unbind_driver(struct device *dev) usb_gadget_disable_async_callbacks(udc); if (gadget->irq) synchronize_irq(gadget->irq); + mutex_unlock(&udc->connect_lock); + udc->driver->unbind(gadget); + + mutex_lock(&udc->connect_lock); usb_gadget_udc_stop_locked(udc); mutex_unlock(&udc->connect_lock); diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 5e912dd29b4c..115f05a6201a 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -318,7 +318,8 @@ static int alauda_get_media_status(struct us_data *us, unsigned char *data) rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, command, 0xc0, 0, 1, data, 2); - usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); + if (rc == USB_STOR_XFER_GOOD) + usb_stor_dbg(us, "Media status %02X %02X\n", data[0], data[1]); return rc; } @@ -454,9 +455,14 @@ static int alauda_init_media(struct us_data *us) static int alauda_check_media(struct us_data *us) { struct alauda_info *info = (struct alauda_info *) us->extra; - unsigned char status[2]; + unsigned char *status = us->iobuf; + int rc; - alauda_get_media_status(us, status); + rc = alauda_get_media_status(us, status); + if (rc != USB_STOR_XFER_GOOD) { + status[0] = 0xF0; /* Pretend there's no media */ + status[1] = 0; + } /* Check for no media or door open */ if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 66de880b28d0..cdf8261e22db 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -60,6 +60,7 @@ struct dp_altmode { enum dp_state state; bool hpd; + bool pending_hpd; struct mutex lock; /* device lock */ struct work_struct work; @@ -144,8 +145,13 @@ static int dp_altmode_status_update(struct dp_altmode *dp) dp->state = DP_STATE_EXIT; } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) { ret = dp_altmode_configure(dp, con); - if (!ret) + if (!ret) { dp->state = DP_STATE_CONFIGURE; + if (dp->hpd != hpd) { + dp->hpd = hpd; + dp->pending_hpd = true; + } + } } else { if (dp->hpd != hpd) { drm_connector_oob_hotplug_event(dp->connector_fwnode); @@ -161,6 +167,16 @@ static int dp_altmode_configured(struct dp_altmode *dp) { sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); + /* + * If the DFP_D/UFP_D sends a change in HPD when first notifying the + * DisplayPort driver that it is connected, then we wait until + * configuration is complete to signal HPD. + */ + if (dp->pending_hpd) { + drm_connector_oob_hotplug_event(dp->connector_fwnode); + sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); + dp->pending_hpd = false; + } return dp_altmode_notify(dp); } diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 784b9d8107e9..65da61150ba7 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -29,6 +29,7 @@ config TYPEC_MUX_INTEL_PMC tristate "Intel PMC mux control" depends on ACPI depends on INTEL_SCU_IPC + select USB_COMMON select USB_ROLE_SWITCH help Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c index 80e580d50129..4d1122d95013 100644 --- a/drivers/usb/typec/mux/nb7vpq904m.c +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -463,16 +463,18 @@ static int nb7vpq904m_probe(struct i2c_client *client) ret = nb7vpq904m_register_bridge(nb7); if (ret) - return ret; + goto err_disable_gpio; sw_desc.drvdata = nb7; sw_desc.fwnode = dev->fwnode; sw_desc.set = nb7vpq904m_sw_set; nb7->sw = typec_switch_register(dev, &sw_desc); - if (IS_ERR(nb7->sw)) - return dev_err_probe(dev, PTR_ERR(nb7->sw), - "Error registering typec switch\n"); + if (IS_ERR(nb7->sw)) { + ret = dev_err_probe(dev, PTR_ERR(nb7->sw), + "Error registering typec switch\n"); + goto err_disable_gpio; + } retimer_desc.drvdata = nb7; retimer_desc.fwnode = dev->fwnode; @@ -480,12 +482,21 @@ static int nb7vpq904m_probe(struct i2c_client *client) nb7->retimer = typec_retimer_register(dev, &retimer_desc); if (IS_ERR(nb7->retimer)) { - typec_switch_unregister(nb7->sw); - return dev_err_probe(dev, PTR_ERR(nb7->retimer), - "Error registering typec retimer\n"); + ret = dev_err_probe(dev, PTR_ERR(nb7->retimer), + "Error registering typec retimer\n"); + goto err_switch_unregister; } return 0; + +err_switch_unregister: + typec_switch_unregister(nb7->sw); + +err_disable_gpio: + gpiod_set_value(nb7->enable_gpio, 0); + regulator_disable(nb7->vcc_supply); + + return ret; } static void nb7vpq904m_remove(struct i2c_client *client) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 829d75ebab42..cc1d83926497 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -5349,6 +5349,10 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) /* Do nothing, vbus drop expected */ break; + case SNK_HARD_RESET_WAIT_VBUS: + /* Do nothing, its OK to receive vbus off events */ + break; + default: if (port->pwr_role == TYPEC_SINK && port->attached) tcpm_set_state(port, SNK_UNATTACHED, tcpm_wait_for_discharge(port)); @@ -5395,6 +5399,9 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) case SNK_DEBOUNCED: /*Do nothing, still waiting for VSAFE5V for connect */ break; + case SNK_HARD_RESET_WAIT_VBUS: + /* Do nothing, its OK to receive vbus off events */ + break; default: if (port->pwr_role == TYPEC_SINK && port->auto_vbus_discharge_enabled) tcpm_set_state(port, SNK_UNATTACHED, 0); diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 030ab44fce18..82324c327a50 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -441,13 +441,23 @@ void btrfs_wait_block_group_cache_progress(struct btrfs_block_group *cache, u64 num_bytes) { struct btrfs_caching_control *caching_ctl; + int progress; caching_ctl = btrfs_get_caching_control(cache); if (!caching_ctl) return; + /* + * We've already failed to allocate from this block group, so even if + * there's enough space in the block group it isn't contiguous enough to + * allow for an allocation, so wait for at least the next wakeup tick, + * or for the thing to be done. + */ + progress = atomic_read(&caching_ctl->progress); + wait_event(caching_ctl->wait, btrfs_block_group_done(cache) || - (cache->free_space_ctl->free_space >= num_bytes)); + (progress != atomic_read(&caching_ctl->progress) && + (cache->free_space_ctl->free_space >= num_bytes))); btrfs_put_caching_control(caching_ctl); } @@ -802,8 +812,10 @@ next: if (total_found > CACHING_CTL_WAKE_UP) { total_found = 0; - if (wakeup) + if (wakeup) { + atomic_inc(&caching_ctl->progress); wake_up(&caching_ctl->wait); + } } } path->slots[0]++; @@ -910,6 +922,7 @@ int btrfs_cache_block_group(struct btrfs_block_group *cache, bool wait) init_waitqueue_head(&caching_ctl->wait); caching_ctl->block_group = cache; refcount_set(&caching_ctl->count, 2); + atomic_set(&caching_ctl->progress, 0); btrfs_init_work(&caching_ctl->work, caching_thread, NULL, NULL); spin_lock(&cache->lock); diff --git a/fs/btrfs/block-group.h b/fs/btrfs/block-group.h index aba5dff66c19..74b61e663028 100644 --- a/fs/btrfs/block-group.h +++ b/fs/btrfs/block-group.h @@ -90,6 +90,8 @@ struct btrfs_caching_control { wait_queue_head_t wait; struct btrfs_work work; struct btrfs_block_group *block_group; + /* Track progress of caching during allocation. */ + atomic_t progress; refcount_t count; }; diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 9b9914e5f03d..a9a2c5446c18 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1103,7 +1103,8 @@ static int btrfs_init_fs_root(struct btrfs_root *root, dev_t anon_dev) btrfs_drew_lock_init(&root->snapshot_lock); if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID && - !btrfs_is_data_reloc_root(root)) { + !btrfs_is_data_reloc_root(root) && + is_fstree(root->root_key.objectid)) { set_bit(BTRFS_ROOT_SHAREABLE, &root->state); btrfs_check_and_init_root_item(&root->root_item); } @@ -1300,6 +1301,16 @@ static struct btrfs_root *btrfs_get_root_ref(struct btrfs_fs_info *fs_info, root = btrfs_get_global_root(fs_info, objectid); if (root) return root; + + /* + * If we're called for non-subvolume trees, and above function didn't + * find one, do not try to read it from disk. + * + * This is namely for free-space-tree and quota tree, which can change + * at runtime and should only be grabbed from fs_info. + */ + if (!is_fstree(objectid) && objectid != BTRFS_DATA_RELOC_TREE_OBJECTID) + return ERR_PTR(-ENOENT); again: root = btrfs_lookup_fs_root(fs_info, objectid); if (root) { diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 911908ea5f6f..f396a9afa403 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4310,8 +4310,11 @@ have_block_group: ret = 0; } - if (unlikely(block_group->cached == BTRFS_CACHE_ERROR)) + if (unlikely(block_group->cached == BTRFS_CACHE_ERROR)) { + if (!cache_block_group_error) + cache_block_group_error = -EIO; goto loop; + } if (!find_free_extent_check_size_class(ffe_ctl, block_group)) goto loop; diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a91d5ad27984..ca765d62324f 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2145,6 +2145,12 @@ retry: continue; } + if (!folio_test_dirty(folio)) { + /* Someone wrote it for us. */ + folio_unlock(folio); + continue; + } + if (wbc->sync_mode != WB_SYNC_NONE) { if (folio_test_writeback(folio)) submit_write_bio(bio_ctrl, 0); @@ -2164,11 +2170,12 @@ retry: } /* - * the filesystem may choose to bump up nr_to_write. + * The filesystem may choose to bump up nr_to_write. * We have to make sure to honor the new nr_to_write - * at any time + * at any time. */ - nr_to_write_done = wbc->nr_to_write <= 0; + nr_to_write_done = (wbc->sync_mode == WB_SYNC_NONE && + wbc->nr_to_write <= 0); } folio_batch_release(&fbatch); cond_resched(); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 49cef61f6a39..9055e19b01ef 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1654,8 +1654,6 @@ out_unlock: clear_bits, page_ops); start += cur_alloc_size; - if (start >= end) - return ret; } /* @@ -1664,9 +1662,11 @@ out_unlock: * space_info's bytes_may_use counter, reserved in * btrfs_check_data_free_space(). */ - extent_clear_unlock_delalloc(inode, start, end, locked_page, - clear_bits | EXTENT_CLEAR_DATA_RESV, - page_ops); + if (start < end) { + clear_bits |= EXTENT_CLEAR_DATA_RESV; + extent_clear_unlock_delalloc(inode, start, end, locked_page, + clear_bits, page_ops); + } return ret; } diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 25a3361caedc..46c3c1d57266 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1916,7 +1916,39 @@ again: err = PTR_ERR(root); break; } - ASSERT(root->reloc_root == reloc_root); + + if (unlikely(root->reloc_root != reloc_root)) { + if (root->reloc_root) { + btrfs_err(fs_info, +"reloc tree mismatch, root %lld has reloc root key (%lld %u %llu) gen %llu, expect reloc root key (%lld %u %llu) gen %llu", + root->root_key.objectid, + root->reloc_root->root_key.objectid, + root->reloc_root->root_key.type, + root->reloc_root->root_key.offset, + btrfs_root_generation( + &root->reloc_root->root_item), + reloc_root->root_key.objectid, + reloc_root->root_key.type, + reloc_root->root_key.offset, + btrfs_root_generation( + &reloc_root->root_item)); + } else { + btrfs_err(fs_info, +"reloc tree mismatch, root %lld has no reloc root, expect reloc root key (%lld %u %llu) gen %llu", + root->root_key.objectid, + reloc_root->root_key.objectid, + reloc_root->root_key.type, + reloc_root->root_key.offset, + btrfs_root_generation( + &reloc_root->root_item)); + } + list_add(&reloc_root->root_list, &reloc_roots); + btrfs_put_root(root); + btrfs_abort_transaction(trans, -EUCLEAN); + if (!err) + err = -EUCLEAN; + break; + } /* * set reference count to 1, so btrfs_recover_relocation @@ -1989,7 +2021,7 @@ again: root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset, false); if (btrfs_root_refs(&reloc_root->root_item) > 0) { - if (IS_ERR(root)) { + if (WARN_ON(IS_ERR(root))) { /* * For recovery we read the fs roots on mount, * and if we didn't find the root then we marked @@ -1998,17 +2030,14 @@ again: * memory. However there's no reason we can't * handle the error properly here just in case. */ - ASSERT(0); ret = PTR_ERR(root); goto out; } - if (root->reloc_root != reloc_root) { + if (WARN_ON(root->reloc_root != reloc_root)) { /* - * This is actually impossible without something - * going really wrong (like weird race condition - * or cosmic rays). + * This can happen if on-disk metadata has some + * corruption, e.g. bad reloc tree key offset. */ - ASSERT(0); ret = -EINVAL; goto out; } diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 038dfa8f1788..ab08a0b01311 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -446,6 +446,20 @@ static int check_root_key(struct extent_buffer *leaf, struct btrfs_key *key, btrfs_item_key_to_cpu(leaf, &item_key, slot); is_root_item = (item_key.type == BTRFS_ROOT_ITEM_KEY); + /* + * Bad rootid for reloc trees. + * + * Reloc trees are only for subvolume trees, other trees only need + * to be COWed to be relocated. + */ + if (unlikely(is_root_item && key->objectid == BTRFS_TREE_RELOC_OBJECTID && + !is_fstree(key->offset))) { + generic_err(leaf, slot, + "invalid reloc tree for root %lld, root id is not a subvolume tree", + key->offset); + return -EUCLEAN; + } + /* No such tree id */ if (unlikely(key->objectid == 0)) { if (is_root_item) diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 4a2b39d9a61a..bdcffb04513f 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -2019,9 +2019,10 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) } } +WRAP_DIR_ITER(ceph_readdir) // FIXME! const struct file_operations ceph_dir_fops = { .read = ceph_read_dir, - .iterate = ceph_readdir, + .iterate_shared = shared_ceph_readdir, .llseek = ceph_dir_llseek, .open = ceph_open, .release = ceph_release, @@ -2033,7 +2034,7 @@ const struct file_operations ceph_dir_fops = { }; const struct file_operations ceph_snapdir_fops = { - .iterate = ceph_readdir, + .iterate_shared = shared_ceph_readdir, .llseek = ceph_dir_llseek, .open = ceph_open, .release = ceph_release, diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 66048a86c480..5fb367b1d4b0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -4764,7 +4764,7 @@ static void delayed_work(struct work_struct *work) dout("mdsc delayed_work\n"); - if (mdsc->stopping) + if (mdsc->stopping >= CEPH_MDSC_STOPPING_FLUSHED) return; mutex_lock(&mdsc->mutex); @@ -4943,7 +4943,7 @@ void send_flush_mdlog(struct ceph_mds_session *s) void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc) { dout("pre_umount\n"); - mdsc->stopping = 1; + mdsc->stopping = CEPH_MDSC_STOPPING_BEGIN; ceph_mdsc_iterate_sessions(mdsc, send_flush_mdlog, true); ceph_mdsc_iterate_sessions(mdsc, lock_unlock_session, false); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 724307ff89cd..86d2965e68a1 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -380,6 +380,11 @@ struct cap_wait { int want; }; +enum { + CEPH_MDSC_STOPPING_BEGIN = 1, + CEPH_MDSC_STOPPING_FLUSHED = 2, +}; + /* * mds client state */ diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 3fc48b43cab0..a5f52013314d 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -1374,6 +1374,16 @@ static void ceph_kill_sb(struct super_block *s) ceph_mdsc_pre_umount(fsc->mdsc); flush_fs_workqueues(fsc); + /* + * Though the kill_anon_super() will finally trigger the + * sync_filesystem() anyway, we still need to do it here + * and then bump the stage of shutdown to stop the work + * queue as earlier as possible. + */ + sync_filesystem(s); + + fsc->mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHED; + kill_anon_super(s); fsc->client->extra_mon_dispatch = NULL; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 8450b1bd354b..1b960de2bf39 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -429,21 +429,14 @@ static int coda_readdir(struct file *coda_file, struct dir_context *ctx) cfi = coda_ftoc(coda_file); host_file = cfi->cfi_container; - if (host_file->f_op->iterate || host_file->f_op->iterate_shared) { + if (host_file->f_op->iterate_shared) { struct inode *host_inode = file_inode(host_file); ret = -ENOENT; if (!IS_DEADDIR(host_inode)) { - if (host_file->f_op->iterate_shared) { - inode_lock_shared(host_inode); - ret = host_file->f_op->iterate_shared(host_file, ctx); - file_accessed(host_file); - inode_unlock_shared(host_inode); - } else { - inode_lock(host_inode); - ret = host_file->f_op->iterate(host_file, ctx); - file_accessed(host_file); - inode_unlock(host_inode); - } + inode_lock_shared(host_inode); + ret = host_file->f_op->iterate_shared(host_file, ctx); + file_accessed(host_file); + inode_unlock_shared(host_inode); } return ret; } @@ -585,10 +578,11 @@ const struct inode_operations coda_dir_inode_operations = { .setattr = coda_setattr, }; +WRAP_DIR_ITER(coda_readdir) // FIXME! const struct file_operations coda_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = coda_readdir, + .iterate_shared = shared_coda_readdir, .open = coda_open, .release = coda_release, .fsync = coda_fsync, diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 9d6a3c6158bd..566f68ddfa36 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -889,8 +889,6 @@ static void erofs_kill_sb(struct super_block *sb) { struct erofs_sb_info *sbi; - WARN_ON(sb->s_magic != EROFS_SUPER_MAGIC); - /* pseudo mount for anon inodes */ if (sb->s_flags & SB_KERNMOUNT) { kill_anon_super(sb); diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index b69d89a11dd0..de4f12152b62 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1144,10 +1144,11 @@ static void z_erofs_do_decompressed_bvec(struct z_erofs_decompress_backend *be, struct z_erofs_bvec *bvec) { struct z_erofs_bvec_item *item; + unsigned int pgnr; - if (!((bvec->offset + be->pcl->pageofs_out) & ~PAGE_MASK)) { - unsigned int pgnr; - + if (!((bvec->offset + be->pcl->pageofs_out) & ~PAGE_MASK) && + (bvec->end == PAGE_SIZE || + bvec->offset + bvec->end == be->pcl->length)) { pgnr = (bvec->offset + be->pcl->pageofs_out) >> PAGE_SHIFT; DBG_BUGON(pgnr >= be->nr_pages); if (!be->decompressed_pages[pgnr]) { diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c index 9f42f25fab92..e918decb3735 100644 --- a/fs/exfat/balloc.c +++ b/fs/exfat/balloc.c @@ -69,7 +69,7 @@ static int exfat_allocate_bitmap(struct super_block *sb, } sbi->map_sectors = ((need_map_size - 1) >> (sb->s_blocksize_bits)) + 1; - sbi->vol_amap = kmalloc_array(sbi->map_sectors, + sbi->vol_amap = kvmalloc_array(sbi->map_sectors, sizeof(struct buffer_head *), GFP_KERNEL); if (!sbi->vol_amap) return -ENOMEM; @@ -84,7 +84,7 @@ static int exfat_allocate_bitmap(struct super_block *sb, while (j < i) brelse(sbi->vol_amap[j++]); - kfree(sbi->vol_amap); + kvfree(sbi->vol_amap); sbi->vol_amap = NULL; return -EIO; } @@ -138,7 +138,7 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi) for (i = 0; i < sbi->map_sectors; i++) __brelse(sbi->vol_amap[i]); - kfree(sbi->vol_amap); + kvfree(sbi->vol_amap); } int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c index 957574180a5e..e1586bba6d86 100644 --- a/fs/exfat/dir.c +++ b/fs/exfat/dir.c @@ -34,6 +34,7 @@ static int exfat_get_uniname_from_ext_entry(struct super_block *sb, { int i, err; struct exfat_entry_set_cache es; + unsigned int uni_len = 0, len; err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES); if (err) @@ -52,7 +53,10 @@ static int exfat_get_uniname_from_ext_entry(struct super_block *sb, if (exfat_get_entry_type(ep) != TYPE_EXTEND) break; - exfat_extract_uni_name(ep, uniname); + len = exfat_extract_uni_name(ep, uniname); + uni_len += len; + if (len != EXFAT_FILE_NAME_LEN || uni_len >= MAX_NAME_LENGTH) + break; uniname += EXFAT_FILE_NAME_LEN; } @@ -214,7 +218,10 @@ static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb) exfat_init_namebuf(nb); } -/* skip iterating emit_dots when dir is empty */ +/* + * Before calling dir_emit*(), sbi->s_lock should be released + * because page fault can occur in dir_emit*(). + */ #define ITER_POS_FILLED_DOTS (2) static int exfat_iterate(struct file *file, struct dir_context *ctx) { @@ -229,11 +236,10 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx) int err = 0, fake_offset = 0; exfat_init_namebuf(nb); - mutex_lock(&EXFAT_SB(sb)->s_lock); cpos = ctx->pos; if (!dir_emit_dots(file, ctx)) - goto unlock; + goto out; if (ctx->pos == ITER_POS_FILLED_DOTS) { cpos = 0; @@ -245,16 +251,18 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx) /* name buffer should be allocated before use */ err = exfat_alloc_namebuf(nb); if (err) - goto unlock; + goto out; get_new: + mutex_lock(&EXFAT_SB(sb)->s_lock); + if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode)) goto end_of_dir; err = exfat_readdir(inode, &cpos, &de); if (err) { /* - * At least we tried to read a sector. Move cpos to next sector - * position (should be aligned). + * At least we tried to read a sector. + * Move cpos to next sector position (should be aligned). */ if (err == -EIO) { cpos += 1 << (sb->s_blocksize_bits); @@ -277,16 +285,10 @@ get_new: inum = iunique(sb, EXFAT_ROOT_INO); } - /* - * Before calling dir_emit(), sb_lock should be released. - * Because page fault can occur in dir_emit() when the size - * of buffer given from user is larger than one page size. - */ mutex_unlock(&EXFAT_SB(sb)->s_lock); if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, (de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) - goto out_unlocked; - mutex_lock(&EXFAT_SB(sb)->s_lock); + goto out; ctx->pos = cpos; goto get_new; @@ -294,9 +296,8 @@ end_of_dir: if (!cpos && fake_offset) cpos = ITER_POS_FILLED_DOTS; ctx->pos = cpos; -unlock: mutex_unlock(&EXFAT_SB(sb)->s_lock); -out_unlocked: +out: /* * To improve performance, free namebuf after unlock sb_lock. * If namebuf is not allocated, this function do nothing @@ -305,10 +306,11 @@ out_unlocked: return err; } +WRAP_DIR_ITER(exfat_iterate) // FIXME! const struct file_operations exfat_dir_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = exfat_iterate, + .iterate_shared = shared_exfat_iterate, .unlocked_ioctl = exfat_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = exfat_compat_ioctl, @@ -1079,7 +1081,8 @@ rewind: if (entry_type == TYPE_EXTEND) { unsigned short entry_uniname[16], unichar; - if (step != DIRENT_STEP_NAME) { + if (step != DIRENT_STEP_NAME || + name_len >= MAX_NAME_LENGTH) { step = DIRENT_STEP_FILE; continue; } diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 40e624cf7e92..d1dbe47c7975 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -315,7 +315,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child) goto out; error = -EINVAL; - if (!file->f_op->iterate && !file->f_op->iterate_shared) + if (!file->f_op->iterate_shared) goto out_close; buffer.sequence = 0; diff --git a/fs/file.c b/fs/file.c index 35c62b54c9d6..3fd003a8604f 100644 --- a/fs/file.c +++ b/fs/file.c @@ -1036,12 +1036,28 @@ unsigned long __fdget_raw(unsigned int fd) return __fget_light(fd, 0); } +/* + * Try to avoid f_pos locking. We only need it if the + * file is marked for FMODE_ATOMIC_POS, and it can be + * accessed multiple ways. + * + * Always do it for directories, because pidfd_getfd() + * can make a file accessible even if it otherwise would + * not be, and for directories this is a correctness + * issue, not a "POSIX requirement". + */ +static inline bool file_needs_f_pos_lock(struct file *file) +{ + return (file->f_mode & FMODE_ATOMIC_POS) && + (file_count(file) > 1 || file->f_op->iterate_shared); +} + unsigned long __fdget_pos(unsigned int fd) { unsigned long v = __fdget(fd); struct file *file = (struct file *)(v & ~3); - if (file && (file->f_mode & FMODE_ATOMIC_POS)) { + if (file && file_needs_f_pos_lock(file)) { v |= FDPUT_POS_UNLOCK; mutex_lock(&file->f_pos_lock); } diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 1bf3c4453516..b43fa8b8fc05 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1578,7 +1578,7 @@ const struct file_operations gfs2_file_fops = { .fsync = gfs2_fsync, .lock = gfs2_lock, .flock = gfs2_flock, - .splice_read = filemap_splice_read, + .splice_read = copy_splice_read, .splice_write = gfs2_file_splice_write, .setlease = simple_nosetlease, .fallocate = gfs2_fallocate, @@ -1609,7 +1609,7 @@ const struct file_operations gfs2_file_fops_nolock = { .open = gfs2_open, .release = gfs2_release, .fsync = gfs2_fsync, - .splice_read = filemap_splice_read, + .splice_read = copy_splice_read, .splice_write = gfs2_file_splice_write, .setlease = generic_setlease, .fallocate = gfs2_fallocate, diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index ec1631257978..7e835be7032d 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -230,9 +230,11 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) { struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + struct super_block *sb = sdp->sd_vfs; struct gfs2_bufdata *bd; struct gfs2_meta_header *mh; struct gfs2_trans *tr = current->journal_info; + bool withdraw = false; lock_buffer(bh); if (buffer_pinned(bh)) { @@ -266,13 +268,15 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) (unsigned long long)bd->bd_bh->b_blocknr); BUG(); } - if (unlikely(test_bit(SDF_FROZEN, &sdp->sd_flags))) { - fs_info(sdp, "GFS2:adding buf while frozen\n"); - gfs2_assert_withdraw(sdp, 0); - } if (unlikely(gfs2_withdrawn(sdp))) { fs_info(sdp, "GFS2:adding buf while withdrawn! 0x%llx\n", (unsigned long long)bd->bd_bh->b_blocknr); + goto out_unlock; + } + if (unlikely(sb->s_writers.frozen == SB_FREEZE_COMPLETE)) { + fs_info(sdp, "GFS2:adding buf while frozen\n"); + withdraw = true; + goto out_unlock; } gfs2_pin(sdp, bd->bd_bh); mh->__pad0 = cpu_to_be64(0); @@ -281,6 +285,8 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) tr->tr_num_buf_new++; out_unlock: gfs2_log_unlock(sdp); + if (withdraw) + gfs2_assert_withdraw(sdp, 0); out: unlock_buffer(bh); } diff --git a/fs/inode.c b/fs/inode.c index 8fefb69e1f84..67611a360031 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -16,7 +16,6 @@ #include <linux/fsnotify.h> #include <linux/mount.h> #include <linux/posix_acl.h> -#include <linux/prefetch.h> #include <linux/buffer_head.h> /* for inode_has_buffers */ #include <linux/ratelimit.h> #include <linux/list_lru.h> @@ -1041,8 +1040,6 @@ struct inode *new_inode(struct super_block *sb) { struct inode *inode; - spin_lock_prefetch(&sb->s_inode_list_lock); - inode = new_inode_pseudo(sb); if (inode) inode_sb_list_add(inode); diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 9b030297aa64..e98ddb2b1cf2 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1535,9 +1535,10 @@ const struct inode_operations jfs_dir_inode_operations = { #endif }; +WRAP_DIR_ITER(jfs_readdir) // FIXME! const struct file_operations jfs_dir_operations = { .read = generic_read_dir, - .iterate = jfs_readdir, + .iterate_shared = shared_jfs_readdir, .fsync = jfs_fsync, .unlocked_ioctl = jfs_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 8a2321d19194..2c9074ab2315 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -956,10 +956,13 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, last_page = page + (offset + sd->len - 1) / PAGE_SIZE; for (page += offset / PAGE_SIZE; page <= last_page; page++) { /* - * Skip page replacement when extending the contents - * of the current page. + * Skip page replacement when extending the contents of the + * current page. But note that we may get two zero_pages in a + * row from shmem. */ - if (page == *(rqstp->rq_next_page - 1)) + if (page == *(rqstp->rq_next_page - 1) && + offset_in_page(rqstp->rq_res.page_base + + rqstp->rq_res.page_len)) continue; if (unlikely(!svc_rqst_replace_page(rqstp, page))) return -EIO; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index a8ce522ac747..35bc79305318 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -1101,9 +1101,17 @@ int nilfs_set_file_dirty(struct inode *inode, unsigned int nr_dirty) int __nilfs_mark_inode_dirty(struct inode *inode, int flags) { + struct the_nilfs *nilfs = inode->i_sb->s_fs_info; struct buffer_head *ibh; int err; + /* + * Do not dirty inodes after the log writer has been detached + * and its nilfs_root struct has been freed. + */ + if (unlikely(nilfs_purging(nilfs))) + return 0; + err = nilfs_load_inode_block(inode, &ibh); if (unlikely(err)) { nilfs_warn(inode->i_sb, diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index c2553024bd25..581691e4be49 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2845,6 +2845,7 @@ void nilfs_detach_log_writer(struct super_block *sb) nilfs_segctor_destroy(nilfs->ns_writer); nilfs->ns_writer = NULL; } + set_nilfs_purging(nilfs); /* Force to free the list of dirty files */ spin_lock(&nilfs->ns_inode_lock); @@ -2857,4 +2858,5 @@ void nilfs_detach_log_writer(struct super_block *sb) up_write(&nilfs->ns_segctor_sem); nilfs_dispose_list(nilfs, &garbage_list, 1); + clear_nilfs_purging(nilfs); } diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 47c7dfbb7ea5..cd4ae1b8ae16 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -29,6 +29,7 @@ enum { THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */ THE_NILFS_GC_RUNNING, /* gc process is running */ THE_NILFS_SB_DIRTY, /* super block is dirty */ + THE_NILFS_PURGING, /* disposing dirty files for cleanup */ }; /** @@ -208,6 +209,7 @@ THE_NILFS_FNS(INIT, init) THE_NILFS_FNS(DISCONTINUED, discontinued) THE_NILFS_FNS(GC_RUNNING, gc_running) THE_NILFS_FNS(SB_DIRTY, sb_dirty) +THE_NILFS_FNS(PURGING, purging) /* * Mount option operations diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index 518c3a21a556..4596c90e7b7c 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -1525,10 +1525,11 @@ static int ntfs_dir_fsync(struct file *filp, loff_t start, loff_t end, #endif /* NTFS_RW */ +WRAP_DIR_ITER(ntfs_readdir) // FIXME! const struct file_operations ntfs_dir_ops = { .llseek = generic_file_llseek, /* Seek inside directory. */ .read = generic_read_dir, /* Return -EISDIR. */ - .iterate = ntfs_readdir, /* Read directory contents. */ + .iterate_shared = shared_ntfs_readdir, /* Read directory contents. */ #ifdef NTFS_RW .fsync = ntfs_dir_fsync, /* Sync a directory to disk. */ #endif /* NTFS_RW */ diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 91a194596552..bf2c17ea96a0 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2793,10 +2793,11 @@ const struct file_operations ocfs2_fops = { .remap_file_range = ocfs2_remap_file_range, }; +WRAP_DIR_ITER(ocfs2_readdir) // FIXME! const struct file_operations ocfs2_dops = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = ocfs2_readdir, + .iterate_shared = shared_ocfs2_readdir, .fsync = ocfs2_sync_file, .release = ocfs2_dir_release, .open = ocfs2_dir_open, @@ -2842,7 +2843,7 @@ const struct file_operations ocfs2_fops_no_plocks = { const struct file_operations ocfs2_dops_no_plocks = { .llseek = generic_file_llseek, .read = generic_read_dir, - .iterate = ocfs2_readdir, + .iterate_shared = shared_ocfs2_readdir, .fsync = ocfs2_sync_file, .release = ocfs2_dir_release, .open = ocfs2_dir_open, diff --git a/fs/open.c b/fs/open.c index 0c55c8e7f837..e6ead0f19964 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1322,7 +1322,7 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) lookup_flags |= LOOKUP_IN_ROOT; if (how->resolve & RESOLVE_CACHED) { /* Don't bother even trying for create/truncate/tmpfile open */ - if (flags & (O_TRUNC | O_CREAT | O_TMPFILE)) + if (flags & (O_TRUNC | O_CREAT | __O_TMPFILE)) return -EAGAIN; lookup_flags |= LOOKUP_CACHED; } diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index ee5c4736480f..de39e067ae65 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -954,10 +954,11 @@ static int ovl_dir_open(struct inode *inode, struct file *file) return 0; } +WRAP_DIR_ITER(ovl_iterate) // FIXME! const struct file_operations ovl_dir_operations = { .read = generic_read_dir, .open = ovl_dir_open, - .iterate = ovl_iterate, + .iterate_shared = shared_ovl_iterate, .llseek = ovl_dir_llseek, .fsync = ovl_dir_fsync, .release = ovl_dir_release, diff --git a/fs/proc/base.c b/fs/proc/base.c index 05452c3b9872..9df3f4839662 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2817,7 +2817,7 @@ static int proc_##LSM##_attr_dir_iterate(struct file *filp, \ \ static const struct file_operations proc_##LSM##_attr_dir_ops = { \ .read = generic_read_dir, \ - .iterate = proc_##LSM##_attr_dir_iterate, \ + .iterate_shared = proc_##LSM##_attr_dir_iterate, \ .llseek = default_llseek, \ }; \ \ diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 9cb32e1a78a0..23fc24d16b31 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -309,6 +309,8 @@ static void append_kcore_note(char *notes, size_t *i, const char *name, static ssize_t read_kcore_iter(struct kiocb *iocb, struct iov_iter *iter) { + struct file *file = iocb->ki_filp; + char *buf = file->private_data; loff_t *fpos = &iocb->ki_pos; size_t phdrs_offset, notes_offset, data_offset; size_t page_offline_frozen = 1; @@ -555,10 +557,21 @@ static ssize_t read_kcore_iter(struct kiocb *iocb, struct iov_iter *iter) case KCORE_VMEMMAP: case KCORE_TEXT: /* - * We use _copy_to_iter() to bypass usermode hardening - * which would otherwise prevent this operation. + * Sadly we must use a bounce buffer here to be able to + * make use of copy_from_kernel_nofault(), as these + * memory regions might not always be mapped on all + * architectures. */ - if (_copy_to_iter((char *)start, tsz, iter) != tsz) { + if (copy_from_kernel_nofault(buf, (void *)start, tsz)) { + if (iov_iter_zero(tsz, iter) != tsz) { + ret = -EFAULT; + goto out; + } + /* + * We know the bounce buffer is safe to copy from, so + * use _copy_to_iter() directly. + */ + } else if (_copy_to_iter(buf, tsz, iter) != tsz) { ret = -EFAULT; goto out; } @@ -595,6 +608,10 @@ static int open_kcore(struct inode *inode, struct file *filp) if (ret) return ret; + filp->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!filp->private_data) + return -ENOMEM; + if (kcore_need_update) kcore_update_ram(); if (i_size_read(inode) != proc_root_kcore->size) { @@ -605,9 +622,16 @@ static int open_kcore(struct inode *inode, struct file *filp) return 0; } +static int release_kcore(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + static const struct proc_ops kcore_proc_ops = { .proc_read_iter = read_kcore_iter, .proc_open = open_kcore, + .proc_release = release_kcore, .proc_lseek = default_llseek, }; diff --git a/fs/readdir.c b/fs/readdir.c index b264ce60114d..c8c46e294431 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -25,6 +25,53 @@ #include <asm/unaligned.h> /* + * Some filesystems were never converted to '->iterate_shared()' + * and their directory iterators want the inode lock held for + * writing. This wrapper allows for converting from the shared + * semantics to the exclusive inode use. + */ +int wrap_directory_iterator(struct file *file, + struct dir_context *ctx, + int (*iter)(struct file *, struct dir_context *)) +{ + struct inode *inode = file_inode(file); + int ret; + + /* + * We'd love to have an 'inode_upgrade_trylock()' operation, + * see the comment in mmap_upgrade_trylock() in mm/memory.c. + * + * But considering this is for "filesystems that never got + * converted", it really doesn't matter. + * + * Also note that since we have to return with the lock held + * for reading, we can't use the "killable()" locking here, + * since we do need to get the lock even if we're dying. + * + * We could do the write part killably and then get the read + * lock unconditionally if it mattered, but see above on why + * this does the very simplistic conversion. + */ + up_read(&inode->i_rwsem); + down_write(&inode->i_rwsem); + + /* + * Since we dropped the inode lock, we should do the + * DEADDIR test again. See 'iterate_dir()' below. + * + * Note that we don't need to re-do the f_pos games, + * since the file must be locked wrt f_pos anyway. + */ + ret = -ENOENT; + if (!IS_DEADDIR(inode)) + ret = iter(file, ctx); + + downgrade_write(&inode->i_rwsem); + return ret; +} +EXPORT_SYMBOL(wrap_directory_iterator); + +/* * Note the "unsafe_put_user() semantics: we goto a * label for errors. */ @@ -40,39 +87,28 @@ int iterate_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); - bool shared = false; int res = -ENOTDIR; - if (file->f_op->iterate_shared) - shared = true; - else if (!file->f_op->iterate) + + if (!file->f_op->iterate_shared) goto out; res = security_file_permission(file, MAY_READ); if (res) goto out; - if (shared) - res = down_read_killable(&inode->i_rwsem); - else - res = down_write_killable(&inode->i_rwsem); + res = down_read_killable(&inode->i_rwsem); if (res) goto out; res = -ENOENT; if (!IS_DEADDIR(inode)) { ctx->pos = file->f_pos; - if (shared) - res = file->f_op->iterate_shared(file, ctx); - else - res = file->f_op->iterate(file, ctx); + res = file->f_op->iterate_shared(file, ctx); file->f_pos = ctx->pos; fsnotify_access(file); file_accessed(file); } - if (shared) - inode_unlock_shared(inode); - else - inode_unlock(inode); + inode_unlock_shared(inode); out: return res; } diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c index df3fd3b720da..ee772c3d9f00 100644 --- a/fs/smb/client/dfs.c +++ b/fs/smb/client/dfs.c @@ -177,8 +177,12 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl); - if (rc) + if (rc) { + rc = cifs_mount_get_tcon(mnt_ctx); + if (!rc) + rc = cifs_is_path_remote(mnt_ctx); break; + } tit = dfs_cache_get_tgt_iterator(&tl); if (!tit) { diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index 33b7e6c4ceff..e881df1d10cb 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -380,13 +380,13 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) } if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { - if (command == SMB2_OPLOCK_BREAK_HE && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && - le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + if (!(command == SMB2_OPLOCK_BREAK_HE && + (le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_20 || + le16_to_cpu(pdu->StructureSize2) == OP_BREAK_STRUCT_SIZE_21))) { /* special case for SMB2.1 lease break message */ ksmbd_debug(SMB, - "Illegal request size %d for oplock break\n", - le16_to_cpu(pdu->StructureSize2)); + "Illegal request size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); return 1; } } diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 9849d7489345..7cc1b0c47d0a 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -2324,9 +2324,16 @@ next: break; buf_len -= next; eabuf = (struct smb2_ea_info *)((char *)eabuf + next); - if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + if (buf_len < sizeof(struct smb2_ea_info)) { + rc = -EINVAL; break; + } + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) { + rc = -EINVAL; + break; + } } while (next != 0); kfree(attr_name); diff --git a/fs/vboxsf/dir.c b/fs/vboxsf/dir.c index 075f15c43c78..5f1a14d5b927 100644 --- a/fs/vboxsf/dir.c +++ b/fs/vboxsf/dir.c @@ -179,9 +179,10 @@ static int vboxsf_dir_iterate(struct file *dir, struct dir_context *ctx) return 0; } +WRAP_DIR_ITER(vboxsf_dir_iterate) // FIXME! const struct file_operations vboxsf_dir_fops = { .open = vboxsf_dir_open, - .iterate = vboxsf_dir_iterate, + .iterate_shared = shared_vboxsf_dir_iterate, .release = vboxsf_dir_release, .read = generic_read_dir, .llseek = generic_file_llseek, diff --git a/fs/vboxsf/shfl_hostintf.h b/fs/vboxsf/shfl_hostintf.h index aca829062c12..069a019c9247 100644 --- a/fs/vboxsf/shfl_hostintf.h +++ b/fs/vboxsf/shfl_hostintf.h @@ -68,9 +68,9 @@ struct shfl_string { /** UTF-8 or UTF-16 string. Nul terminated. */ union { - u8 utf8[2]; - u16 utf16[1]; - u16 ucs2[1]; /* misnomer, use utf16. */ + u8 legacy_padding[2]; + DECLARE_FLEX_ARRAY(u8, utf8); + DECLARE_FLEX_ARRAY(u16, utf16); } string; }; VMMDEV_ASSERT_SIZE(shfl_string, 6); diff --git a/fs/zonefs/file.c b/fs/zonefs/file.c index 92c9aaae3663..789cfb74c146 100644 --- a/fs/zonefs/file.c +++ b/fs/zonefs/file.c @@ -341,77 +341,6 @@ static loff_t zonefs_file_llseek(struct file *file, loff_t offset, int whence) return generic_file_llseek_size(file, offset, whence, isize, isize); } -struct zonefs_zone_append_bio { - /* The target inode of the BIO */ - struct inode *inode; - - /* For sync writes, the target append write offset */ - u64 append_offset; - - /* - * This member must come last, bio_alloc_bioset will allocate enough - * bytes for entire zonefs_bio but relies on bio being last. - */ - struct bio bio; -}; - -static inline struct zonefs_zone_append_bio * -zonefs_zone_append_bio(struct bio *bio) -{ - return container_of(bio, struct zonefs_zone_append_bio, bio); -} - -static void zonefs_file_zone_append_dio_bio_end_io(struct bio *bio) -{ - struct zonefs_zone_append_bio *za_bio = zonefs_zone_append_bio(bio); - struct zonefs_zone *z = zonefs_inode_zone(za_bio->inode); - sector_t za_sector; - - if (bio->bi_status != BLK_STS_OK) - goto bio_end; - - /* - * If the file zone was written underneath the file system, the zone - * append operation can still succedd (if the zone is not full) but - * the write append location will not be where we expect it to be. - * Check that we wrote where we intended to, that is, at z->z_wpoffset. - */ - za_sector = z->z_sector + (za_bio->append_offset >> SECTOR_SHIFT); - if (bio->bi_iter.bi_sector != za_sector) { - zonefs_warn(za_bio->inode->i_sb, - "Invalid write sector %llu for zone at %llu\n", - bio->bi_iter.bi_sector, z->z_sector); - bio->bi_status = BLK_STS_IOERR; - } - -bio_end: - iomap_dio_bio_end_io(bio); -} - -static void zonefs_file_zone_append_dio_submit_io(const struct iomap_iter *iter, - struct bio *bio, - loff_t file_offset) -{ - struct zonefs_zone_append_bio *za_bio = zonefs_zone_append_bio(bio); - struct inode *inode = iter->inode; - struct zonefs_zone *z = zonefs_inode_zone(inode); - - /* - * Issue a zone append BIO to process sync dio writes. The append - * file offset is saved to check the zone append write location - * on completion of the BIO. - */ - za_bio->inode = inode; - za_bio->append_offset = file_offset; - - bio->bi_opf &= ~REQ_OP_WRITE; - bio->bi_opf |= REQ_OP_ZONE_APPEND; - bio->bi_iter.bi_sector = z->z_sector; - bio->bi_end_io = zonefs_file_zone_append_dio_bio_end_io; - - submit_bio(bio); -} - static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, int error, unsigned int flags) { @@ -442,14 +371,6 @@ static int zonefs_file_write_dio_end_io(struct kiocb *iocb, ssize_t size, return 0; } -static struct bio_set zonefs_zone_append_bio_set; - -static const struct iomap_dio_ops zonefs_zone_append_dio_ops = { - .submit_io = zonefs_file_zone_append_dio_submit_io, - .end_io = zonefs_file_write_dio_end_io, - .bio_set = &zonefs_zone_append_bio_set, -}; - static const struct iomap_dio_ops zonefs_write_dio_ops = { .end_io = zonefs_file_write_dio_end_io, }; @@ -533,9 +454,6 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) struct zonefs_inode_info *zi = ZONEFS_I(inode); struct zonefs_zone *z = zonefs_inode_zone(inode); struct super_block *sb = inode->i_sb; - const struct iomap_dio_ops *dio_ops; - bool sync = is_sync_kiocb(iocb); - bool append = false; ssize_t ret, count; /* @@ -543,7 +461,8 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) * as this can cause write reordering (e.g. the first aio gets EAGAIN * on the inode lock but the second goes through but is now unaligned). */ - if (zonefs_zone_is_seq(z) && !sync && (iocb->ki_flags & IOCB_NOWAIT)) + if (zonefs_zone_is_seq(z) && !is_sync_kiocb(iocb) && + (iocb->ki_flags & IOCB_NOWAIT)) return -EOPNOTSUPP; if (iocb->ki_flags & IOCB_NOWAIT) { @@ -573,18 +492,6 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) goto inode_unlock; } mutex_unlock(&zi->i_truncate_mutex); - append = sync; - } - - if (append) { - unsigned int max = bdev_max_zone_append_sectors(sb->s_bdev); - - max = ALIGN_DOWN(max << SECTOR_SHIFT, sb->s_blocksize); - iov_iter_truncate(from, max); - - dio_ops = &zonefs_zone_append_dio_ops; - } else { - dio_ops = &zonefs_write_dio_ops; } /* @@ -593,7 +500,7 @@ static ssize_t zonefs_file_dio_write(struct kiocb *iocb, struct iov_iter *from) * the user can make sense of the error. */ ret = iomap_dio_rw(iocb, from, &zonefs_write_iomap_ops, - dio_ops, 0, NULL, 0); + &zonefs_write_dio_ops, 0, NULL, 0); if (ret == -ENOTBLK) ret = -EBUSY; @@ -938,15 +845,3 @@ const struct file_operations zonefs_file_operations = { .splice_write = iter_file_splice_write, .iopoll = iocb_bio_iopoll, }; - -int zonefs_file_bioset_init(void) -{ - return bioset_init(&zonefs_zone_append_bio_set, BIO_POOL_SIZE, - offsetof(struct zonefs_zone_append_bio, bio), - BIOSET_NEED_BVECS); -} - -void zonefs_file_bioset_exit(void) -{ - bioset_exit(&zonefs_zone_append_bio_set); -} diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index bbe44a26a8e5..9350221abfc5 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -1412,13 +1412,9 @@ static int __init zonefs_init(void) BUILD_BUG_ON(sizeof(struct zonefs_super) != ZONEFS_SUPER_SIZE); - ret = zonefs_file_bioset_init(); - if (ret) - return ret; - ret = zonefs_init_inodecache(); if (ret) - goto destroy_bioset; + return ret; ret = zonefs_sysfs_init(); if (ret) @@ -1434,8 +1430,6 @@ sysfs_exit: zonefs_sysfs_exit(); destroy_inodecache: zonefs_destroy_inodecache(); -destroy_bioset: - zonefs_file_bioset_exit(); return ret; } @@ -1445,7 +1439,6 @@ static void __exit zonefs_exit(void) unregister_filesystem(&zonefs_type); zonefs_sysfs_exit(); zonefs_destroy_inodecache(); - zonefs_file_bioset_exit(); } MODULE_AUTHOR("Damien Le Moal"); diff --git a/fs/zonefs/zonefs.h b/fs/zonefs/zonefs.h index f663b8ebc2cb..8175652241b5 100644 --- a/fs/zonefs/zonefs.h +++ b/fs/zonefs/zonefs.h @@ -279,8 +279,6 @@ extern const struct file_operations zonefs_dir_operations; extern const struct address_space_operations zonefs_file_aops; extern const struct file_operations zonefs_file_operations; int zonefs_file_truncate(struct inode *inode, loff_t isize); -int zonefs_file_bioset_init(void); -void zonefs_file_bioset_exit(void); /* In sysfs.c */ int zonefs_sysfs_register(struct super_block *sb); diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 402a8c1c202d..a8f4b653ef4e 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -190,7 +190,7 @@ int hv_common_cpu_die(unsigned int cpu); void *hv_alloc_hyperv_page(void); void *hv_alloc_hyperv_zeroed_page(void); -void hv_free_hyperv_page(unsigned long addr); +void hv_free_hyperv_page(void *addr); /** * hv_cpu_number_to_vp_number() - Map CPU to VP. diff --git a/include/asm-generic/word-at-a-time.h b/include/asm-generic/word-at-a-time.h index 20c93f08c993..95a1d214108a 100644 --- a/include/asm-generic/word-at-a-time.h +++ b/include/asm-generic/word-at-a-time.h @@ -38,7 +38,7 @@ static inline long find_zero(unsigned long mask) return (mask >> 8) ? byte : byte + 1; } -static inline bool has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c) +static inline unsigned long has_zero(unsigned long val, unsigned long *data, const struct word_at_a_time *c) { unsigned long rhs = val | c->low_bits; *data = rhs; diff --git a/include/linux/bio.h b/include/linux/bio.h index c4f5b5228105..11984ed29cb8 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -791,7 +791,7 @@ static inline int bio_integrity_add_page(struct bio *bio, struct page *page, static inline void bio_set_polled(struct bio *bio, struct kiocb *kiocb) { bio->bi_opf |= REQ_POLLED; - if (!is_sync_kiocb(kiocb)) + if (kiocb->ki_flags & IOCB_NOWAIT) bio->bi_opf |= REQ_NOWAIT; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index ed44a997f629..87d94be7825a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -969,7 +969,6 @@ struct blk_plug { bool multiple_queues; bool has_elevator; - bool nowait; struct list_head cb_list; /* md requires an unplug callback */ }; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 6e6e57ec69e8..e006c719182b 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -70,6 +70,10 @@ extern ssize_t cpu_show_mmio_stale_data(struct device *dev, char *buf); extern ssize_t cpu_show_retbleed(struct device *dev, struct device_attribute *attr, char *buf); +extern ssize_t cpu_show_spec_rstack_overflow(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t cpu_show_gds(struct device *dev, + struct device_attribute *attr, char *buf); extern __printf(4, 5) struct device *cpu_device_create(struct device *parent, void *drvdata, diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 0d2e2a38b92d..f10fb87d49db 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -175,8 +175,8 @@ static inline unsigned int cpumask_first_zero(const struct cpumask *srcp) /** * cpumask_first_and - return the first cpu from *srcp1 & *srcp2 - * @src1p: the first input - * @src2p: the second input + * @srcp1: the first input + * @srcp2: the second input * * Returns >= nr_cpu_ids if no cpus set in both. See also cpumask_next_and(). */ @@ -1197,6 +1197,10 @@ cpumap_print_bitmask_to_buf(char *buf, const struct cpumask *mask, /** * cpumap_print_list_to_buf - copies the cpumask into the buffer as * comma-separated list of cpus + * @buf: the buffer to copy into + * @mask: the cpumask to copy + * @off: in the string from which we are copying, we copy to @buf + * @count: the maximum number of bytes to print * * Everything is same with the above cpumap_print_bitmask_to_buf() * except the print format. diff --git a/include/linux/firmware/imx/dsp.h b/include/linux/firmware/imx/dsp.h index 4f7895a3b73c..1f176a2683fe 100644 --- a/include/linux/firmware/imx/dsp.h +++ b/include/linux/firmware/imx/dsp.h @@ -37,17 +37,11 @@ struct imx_dsp_ipc { static inline void imx_dsp_set_data(struct imx_dsp_ipc *ipc, void *data) { - if (!ipc) - return; - ipc->private_data = data; } static inline void *imx_dsp_get_data(struct imx_dsp_ipc *ipc) { - if (!ipc) - return NULL; - return ipc->private_data; } diff --git a/include/linux/firmware/mediatek/mtk-adsp-ipc.h b/include/linux/firmware/mediatek/mtk-adsp-ipc.h index 28fd313340b8..5b1d16fa3f56 100644 --- a/include/linux/firmware/mediatek/mtk-adsp-ipc.h +++ b/include/linux/firmware/mediatek/mtk-adsp-ipc.h @@ -46,17 +46,11 @@ struct mtk_adsp_ipc { static inline void mtk_adsp_ipc_set_data(struct mtk_adsp_ipc *ipc, void *data) { - if (!ipc) - return; - ipc->private_data = data; } static inline void *mtk_adsp_ipc_get_data(struct mtk_adsp_ipc *ipc) { - if (!ipc) - return NULL; - return ipc->private_data; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 6867512907d6..562f2623c9c9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1780,7 +1780,6 @@ struct file_operations { ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *, unsigned int flags); - int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); @@ -1817,6 +1816,13 @@ struct file_operations { unsigned int poll_flags); } __randomize_layout; +/* Wrap a directory iterator that needs exclusive inode access */ +int wrap_directory_iterator(struct file *, struct dir_context *, + int (*) (struct file *, struct dir_context *)); +#define WRAP_DIR_ITER(x) \ + static int shared_##x(struct file *file , struct dir_context *ctx) \ + { return wrap_directory_iterator(file, ctx, x); } + struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index bfbc37ce223b..3ac3974b3c78 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1239,9 +1239,6 @@ extern int vmbus_recvpacket_raw(struct vmbus_channel *channel, u32 *buffer_actual_len, u64 *requestid); - -extern void vmbus_ontimer(unsigned long data); - /* Base driver object */ struct hv_driver { const char *name; diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h index e6936cb25047..33f21bd85dbf 100644 --- a/include/linux/intel_rapl.h +++ b/include/linux/intel_rapl.h @@ -100,10 +100,16 @@ struct rapl_package; #define RAPL_DOMAIN_NAME_LENGTH 16 +union rapl_reg { + void __iomem *mmio; + u32 msr; + u64 val; +}; + struct rapl_domain { char name[RAPL_DOMAIN_NAME_LENGTH]; enum rapl_domain_type id; - u64 regs[RAPL_DOMAIN_REG_MAX]; + union rapl_reg regs[RAPL_DOMAIN_REG_MAX]; struct powercap_zone power_zone; struct rapl_domain_data rdd; struct rapl_power_limit rpl[NR_POWER_LIMITS]; @@ -116,7 +122,7 @@ struct rapl_domain { }; struct reg_action { - u64 reg; + union rapl_reg reg; u64 mask; u64 value; int err; @@ -143,8 +149,8 @@ struct rapl_if_priv { enum rapl_if_type type; struct powercap_control_type *control_type; enum cpuhp_state pcap_rapl_online; - u64 reg_unit; - u64 regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; + union rapl_reg reg_unit; + union rapl_reg regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; int limits[RAPL_DOMAIN_MAX]; int (*read_raw)(int id, struct reg_action *ra); int (*write_raw)(int id, struct reg_action *ra); diff --git a/include/linux/mfd/cs42l43-regs.h b/include/linux/mfd/cs42l43-regs.h new file mode 100644 index 000000000000..c39a49269cb7 --- /dev/null +++ b/include/linux/mfd/cs42l43-regs.h @@ -0,0 +1,1184 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs42l43 register definitions + * + * Copyright (c) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS42L43_CORE_REGS_H +#define CS42L43_CORE_REGS_H + +/* Registers */ +#define CS42L43_GEN_INT_STAT_1 0x000000C0 +#define CS42L43_GEN_INT_MASK_1 0x000000C1 +#define CS42L43_DEVID 0x00003000 +#define CS42L43_REVID 0x00003004 +#define CS42L43_RELID 0x0000300C +#define CS42L43_SFT_RESET 0x00003020 +#define CS42L43_DRV_CTRL1 0x00006004 +#define CS42L43_DRV_CTRL3 0x0000600C +#define CS42L43_DRV_CTRL4 0x00006010 +#define CS42L43_DRV_CTRL_5 0x00006014 +#define CS42L43_GPIO_CTRL1 0x00006034 +#define CS42L43_GPIO_CTRL2 0x00006038 +#define CS42L43_GPIO_STS 0x0000603C +#define CS42L43_GPIO_FN_SEL 0x00006040 +#define CS42L43_MCLK_SRC_SEL 0x00007004 +#define CS42L43_CCM_BLK_CLK_CONTROL 0x00007010 +#define CS42L43_SAMPLE_RATE1 0x00007014 +#define CS42L43_SAMPLE_RATE2 0x00007018 +#define CS42L43_SAMPLE_RATE3 0x0000701C +#define CS42L43_SAMPLE_RATE4 0x00007020 +#define CS42L43_PLL_CONTROL 0x00007034 +#define CS42L43_FS_SELECT1 0x00007038 +#define CS42L43_FS_SELECT2 0x0000703C +#define CS42L43_FS_SELECT3 0x00007040 +#define CS42L43_FS_SELECT4 0x00007044 +#define CS42L43_PDM_CONTROL 0x0000704C +#define CS42L43_ASP_CLK_CONFIG1 0x00007058 +#define CS42L43_ASP_CLK_CONFIG2 0x0000705C +#define CS42L43_OSC_DIV_SEL 0x00007068 +#define CS42L43_ADC_B_CTRL1 0x00008000 +#define CS42L43_ADC_B_CTRL2 0x00008004 +#define CS42L43_DECIM_HPF_WNF_CTRL1 0x0000803C +#define CS42L43_DECIM_HPF_WNF_CTRL2 0x00008040 +#define CS42L43_DECIM_HPF_WNF_CTRL3 0x00008044 +#define CS42L43_DECIM_HPF_WNF_CTRL4 0x00008048 +#define CS42L43_DMIC_PDM_CTRL 0x0000804C +#define CS42L43_DECIM_VOL_CTRL_CH1_CH2 0x00008050 +#define CS42L43_DECIM_VOL_CTRL_CH3_CH4 0x00008054 +#define CS42L43_DECIM_VOL_CTRL_UPDATE 0x00008058 +#define CS42L43_INTP_VOLUME_CTRL1 0x00009008 +#define CS42L43_INTP_VOLUME_CTRL2 0x0000900C +#define CS42L43_AMP1_2_VOL_RAMP 0x00009010 +#define CS42L43_ASP_CTRL 0x0000A000 +#define CS42L43_ASP_FSYNC_CTRL1 0x0000A004 +#define CS42L43_ASP_FSYNC_CTRL2 0x0000A008 +#define CS42L43_ASP_FSYNC_CTRL3 0x0000A00C +#define CS42L43_ASP_FSYNC_CTRL4 0x0000A010 +#define CS42L43_ASP_DATA_CTRL 0x0000A018 +#define CS42L43_ASP_RX_EN 0x0000A020 +#define CS42L43_ASP_TX_EN 0x0000A024 +#define CS42L43_ASP_RX_CH1_CTRL 0x0000A028 +#define CS42L43_ASP_RX_CH2_CTRL 0x0000A02C +#define CS42L43_ASP_RX_CH3_CTRL 0x0000A030 +#define CS42L43_ASP_RX_CH4_CTRL 0x0000A034 +#define CS42L43_ASP_RX_CH5_CTRL 0x0000A038 +#define CS42L43_ASP_RX_CH6_CTRL 0x0000A03C +#define CS42L43_ASP_TX_CH1_CTRL 0x0000A068 +#define CS42L43_ASP_TX_CH2_CTRL 0x0000A06C +#define CS42L43_ASP_TX_CH3_CTRL 0x0000A070 +#define CS42L43_ASP_TX_CH4_CTRL 0x0000A074 +#define CS42L43_ASP_TX_CH5_CTRL 0x0000A078 +#define CS42L43_ASP_TX_CH6_CTRL 0x0000A07C +#define CS42L43_OTP_REVISION_ID 0x0000B02C +#define CS42L43_ASPTX1_INPUT 0x0000C200 +#define CS42L43_ASPTX2_INPUT 0x0000C210 +#define CS42L43_ASPTX3_INPUT 0x0000C220 +#define CS42L43_ASPTX4_INPUT 0x0000C230 +#define CS42L43_ASPTX5_INPUT 0x0000C240 +#define CS42L43_ASPTX6_INPUT 0x0000C250 +#define CS42L43_SWIRE_DP1_CH1_INPUT 0x0000C280 +#define CS42L43_SWIRE_DP1_CH2_INPUT 0x0000C290 +#define CS42L43_SWIRE_DP1_CH3_INPUT 0x0000C2A0 +#define CS42L43_SWIRE_DP1_CH4_INPUT 0x0000C2B0 +#define CS42L43_SWIRE_DP2_CH1_INPUT 0x0000C2C0 +#define CS42L43_SWIRE_DP2_CH2_INPUT 0x0000C2D0 +#define CS42L43_SWIRE_DP3_CH1_INPUT 0x0000C2E0 +#define CS42L43_SWIRE_DP3_CH2_INPUT 0x0000C2F0 +#define CS42L43_SWIRE_DP4_CH1_INPUT 0x0000C300 +#define CS42L43_SWIRE_DP4_CH2_INPUT 0x0000C310 +#define CS42L43_ASRC_INT1_INPUT1 0x0000C400 +#define CS42L43_ASRC_INT2_INPUT1 0x0000C410 +#define CS42L43_ASRC_INT3_INPUT1 0x0000C420 +#define CS42L43_ASRC_INT4_INPUT1 0x0000C430 +#define CS42L43_ASRC_DEC1_INPUT1 0x0000C440 +#define CS42L43_ASRC_DEC2_INPUT1 0x0000C450 +#define CS42L43_ASRC_DEC3_INPUT1 0x0000C460 +#define CS42L43_ASRC_DEC4_INPUT1 0x0000C470 +#define CS42L43_ISRC1INT1_INPUT1 0x0000C500 +#define CS42L43_ISRC1INT2_INPUT1 0x0000C510 +#define CS42L43_ISRC1DEC1_INPUT1 0x0000C520 +#define CS42L43_ISRC1DEC2_INPUT1 0x0000C530 +#define CS42L43_ISRC2INT1_INPUT1 0x0000C540 +#define CS42L43_ISRC2INT2_INPUT1 0x0000C550 +#define CS42L43_ISRC2DEC1_INPUT1 0x0000C560 +#define CS42L43_ISRC2DEC2_INPUT1 0x0000C570 +#define CS42L43_EQ1MIX_INPUT1 0x0000C580 +#define CS42L43_EQ1MIX_INPUT2 0x0000C584 +#define CS42L43_EQ1MIX_INPUT3 0x0000C588 +#define CS42L43_EQ1MIX_INPUT4 0x0000C58C +#define CS42L43_EQ2MIX_INPUT1 0x0000C590 +#define CS42L43_EQ2MIX_INPUT2 0x0000C594 +#define CS42L43_EQ2MIX_INPUT3 0x0000C598 +#define CS42L43_EQ2MIX_INPUT4 0x0000C59C +#define CS42L43_SPDIF1_INPUT1 0x0000C600 +#define CS42L43_SPDIF2_INPUT1 0x0000C610 +#define CS42L43_AMP1MIX_INPUT1 0x0000C620 +#define CS42L43_AMP1MIX_INPUT2 0x0000C624 +#define CS42L43_AMP1MIX_INPUT3 0x0000C628 +#define CS42L43_AMP1MIX_INPUT4 0x0000C62C +#define CS42L43_AMP2MIX_INPUT1 0x0000C630 +#define CS42L43_AMP2MIX_INPUT2 0x0000C634 +#define CS42L43_AMP2MIX_INPUT3 0x0000C638 +#define CS42L43_AMP2MIX_INPUT4 0x0000C63C +#define CS42L43_AMP3MIX_INPUT1 0x0000C640 +#define CS42L43_AMP3MIX_INPUT2 0x0000C644 +#define CS42L43_AMP3MIX_INPUT3 0x0000C648 +#define CS42L43_AMP3MIX_INPUT4 0x0000C64C +#define CS42L43_AMP4MIX_INPUT1 0x0000C650 +#define CS42L43_AMP4MIX_INPUT2 0x0000C654 +#define CS42L43_AMP4MIX_INPUT3 0x0000C658 +#define CS42L43_AMP4MIX_INPUT4 0x0000C65C +#define CS42L43_ASRC_INT_ENABLES 0x0000E000 +#define CS42L43_ASRC_DEC_ENABLES 0x0000E004 +#define CS42L43_PDNCNTL 0x00010000 +#define CS42L43_RINGSENSE_DEB_CTRL 0x0001001C +#define CS42L43_TIPSENSE_DEB_CTRL 0x00010020 +#define CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS 0x00010028 +#define CS42L43_HS2 0x00010040 +#define CS42L43_HS_STAT 0x00010048 +#define CS42L43_MCU_SW_INTERRUPT 0x00010094 +#define CS42L43_STEREO_MIC_CTRL 0x000100A4 +#define CS42L43_STEREO_MIC_CLAMP_CTRL 0x000100C4 +#define CS42L43_BLOCK_EN2 0x00010104 +#define CS42L43_BLOCK_EN3 0x00010108 +#define CS42L43_BLOCK_EN4 0x0001010C +#define CS42L43_BLOCK_EN5 0x00010110 +#define CS42L43_BLOCK_EN6 0x00010114 +#define CS42L43_BLOCK_EN7 0x00010118 +#define CS42L43_BLOCK_EN8 0x0001011C +#define CS42L43_BLOCK_EN9 0x00010120 +#define CS42L43_BLOCK_EN10 0x00010124 +#define CS42L43_BLOCK_EN11 0x00010128 +#define CS42L43_TONE_CH1_CTRL 0x00010134 +#define CS42L43_TONE_CH2_CTRL 0x00010138 +#define CS42L43_MIC_DETECT_CONTROL_1 0x00011074 +#define CS42L43_DETECT_STATUS_1 0x0001107C +#define CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL 0x00011090 +#define CS42L43_MIC_DETECT_CONTROL_ANDROID 0x000110B0 +#define CS42L43_ISRC1_CTRL 0x00012004 +#define CS42L43_ISRC2_CTRL 0x00013004 +#define CS42L43_CTRL_REG 0x00014000 +#define CS42L43_FDIV_FRAC 0x00014004 +#define CS42L43_CAL_RATIO 0x00014008 +#define CS42L43_SPI_CLK_CONFIG1 0x00016004 +#define CS42L43_SPI_CONFIG1 0x00016010 +#define CS42L43_SPI_CONFIG2 0x00016014 +#define CS42L43_SPI_CONFIG3 0x00016018 +#define CS42L43_SPI_CONFIG4 0x00016024 +#define CS42L43_SPI_STATUS1 0x00016100 +#define CS42L43_SPI_STATUS2 0x00016104 +#define CS42L43_TRAN_CONFIG1 0x00016200 +#define CS42L43_TRAN_CONFIG2 0x00016204 +#define CS42L43_TRAN_CONFIG3 0x00016208 +#define CS42L43_TRAN_CONFIG4 0x0001620C +#define CS42L43_TRAN_CONFIG5 0x00016220 +#define CS42L43_TRAN_CONFIG6 0x00016224 +#define CS42L43_TRAN_CONFIG7 0x00016228 +#define CS42L43_TRAN_CONFIG8 0x0001622C +#define CS42L43_TRAN_STATUS1 0x00016300 +#define CS42L43_TRAN_STATUS2 0x00016304 +#define CS42L43_TRAN_STATUS3 0x00016308 +#define CS42L43_TX_DATA 0x00016400 +#define CS42L43_RX_DATA 0x00016600 +#define CS42L43_DACCNFG1 0x00017000 +#define CS42L43_DACCNFG2 0x00017004 +#define CS42L43_HPPATHVOL 0x0001700C +#define CS42L43_PGAVOL 0x00017014 +#define CS42L43_LOADDETRESULTS 0x00017018 +#define CS42L43_LOADDETENA 0x00017024 +#define CS42L43_CTRL 0x00017028 +#define CS42L43_COEFF_DATA_IN0 0x00018000 +#define CS42L43_COEFF_RD_WR0 0x00018008 +#define CS42L43_INIT_DONE0 0x00018010 +#define CS42L43_START_EQZ0 0x00018014 +#define CS42L43_MUTE_EQ_IN0 0x0001801C +#define CS42L43_DECIM_INT 0x0001B000 +#define CS42L43_EQ_INT 0x0001B004 +#define CS42L43_ASP_INT 0x0001B008 +#define CS42L43_PLL_INT 0x0001B00C +#define CS42L43_SOFT_INT 0x0001B010 +#define CS42L43_SWIRE_INT 0x0001B014 +#define CS42L43_MSM_INT 0x0001B018 +#define CS42L43_ACC_DET_INT 0x0001B01C +#define CS42L43_I2C_TGT_INT 0x0001B020 +#define CS42L43_SPI_MSTR_INT 0x0001B024 +#define CS42L43_SW_TO_SPI_BRIDGE_INT 0x0001B028 +#define CS42L43_OTP_INT 0x0001B02C +#define CS42L43_CLASS_D_AMP_INT 0x0001B030 +#define CS42L43_GPIO_INT 0x0001B034 +#define CS42L43_ASRC_INT 0x0001B038 +#define CS42L43_HPOUT_INT 0x0001B03C +#define CS42L43_DECIM_MASK 0x0001B0A0 +#define CS42L43_EQ_MIX_MASK 0x0001B0A4 +#define CS42L43_ASP_MASK 0x0001B0A8 +#define CS42L43_PLL_MASK 0x0001B0AC +#define CS42L43_SOFT_MASK 0x0001B0B0 +#define CS42L43_SWIRE_MASK 0x0001B0B4 +#define CS42L43_MSM_MASK 0x0001B0B8 +#define CS42L43_ACC_DET_MASK 0x0001B0BC +#define CS42L43_I2C_TGT_MASK 0x0001B0C0 +#define CS42L43_SPI_MSTR_MASK 0x0001B0C4 +#define CS42L43_SW_TO_SPI_BRIDGE_MASK 0x0001B0C8 +#define CS42L43_OTP_MASK 0x0001B0CC +#define CS42L43_CLASS_D_AMP_MASK 0x0001B0D0 +#define CS42L43_GPIO_INT_MASK 0x0001B0D4 +#define CS42L43_ASRC_MASK 0x0001B0D8 +#define CS42L43_HPOUT_MASK 0x0001B0DC +#define CS42L43_DECIM_INT_SHADOW 0x0001B300 +#define CS42L43_EQ_MIX_INT_SHADOW 0x0001B304 +#define CS42L43_ASP_INT_SHADOW 0x0001B308 +#define CS42L43_PLL_INT_SHADOW 0x0001B30C +#define CS42L43_SOFT_INT_SHADOW 0x0001B310 +#define CS42L43_SWIRE_INT_SHADOW 0x0001B314 +#define CS42L43_MSM_INT_SHADOW 0x0001B318 +#define CS42L43_ACC_DET_INT_SHADOW 0x0001B31C +#define CS42L43_I2C_TGT_INT_SHADOW 0x0001B320 +#define CS42L43_SPI_MSTR_INT_SHADOW 0x0001B324 +#define CS42L43_SW_TO_SPI_BRIDGE_SHADOW 0x0001B328 +#define CS42L43_OTP_INT_SHADOW 0x0001B32C +#define CS42L43_CLASS_D_AMP_INT_SHADOW 0x0001B330 +#define CS42L43_GPIO_SHADOW 0x0001B334 +#define CS42L43_ASRC_SHADOW 0x0001B338 +#define CS42L43_HP_OUT_SHADOW 0x0001B33C +#define CS42L43_BOOT_CONTROL 0x00101000 +#define CS42L43_BLOCK_EN 0x00101008 +#define CS42L43_SHUTTER_CONTROL 0x0010100C +#define CS42L43_MCU_SW_REV 0x00114000 +#define CS42L43_PATCH_START_ADDR 0x00114004 +#define CS42L43_NEED_CONFIGS 0x0011400C +#define CS42L43_BOOT_STATUS 0x0011401C +#define CS42L43_FW_SH_BOOT_CFG_NEED_CONFIGS 0x0011F8F8 +#define CS42L43_FW_MISSION_CTRL_NEED_CONFIGS 0x0011FE00 +#define CS42L43_FW_MISSION_CTRL_HAVE_CONFIGS 0x0011FE04 +#define CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION 0x0011FE0C +#define CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG 0x0011FE10 +#define CS42L43_MCU_RAM_MAX 0x0011FFFF + +/* CS42L43_DEVID */ +#define CS42L43_DEVID_VAL 0x00042A43 + +/* CS42L43_GEN_INT_STAT_1 */ +#define CS42L43_INT_STAT_GEN1_MASK 0x00000001 +#define CS42L43_INT_STAT_GEN1_SHIFT 0 + +/* CS42L43_SFT_RESET */ +#define CS42L43_SFT_RESET_MASK 0xFF000000 +#define CS42L43_SFT_RESET_SHIFT 24 + +#define CS42L43_SFT_RESET_VAL 0x5A000000 + +/* CS42L43_DRV_CTRL1 */ +#define CS42L43_ASP_DOUT_DRV_MASK 0x00038000 +#define CS42L43_ASP_DOUT_DRV_SHIFT 15 +#define CS42L43_ASP_FSYNC_DRV_MASK 0x00000E00 +#define CS42L43_ASP_FSYNC_DRV_SHIFT 9 +#define CS42L43_ASP_BCLK_DRV_MASK 0x000001C0 +#define CS42L43_ASP_BCLK_DRV_SHIFT 6 + +/* CS42L43_DRV_CTRL3 */ +#define CS42L43_I2C_ADDR_DRV_MASK 0x30000000 +#define CS42L43_I2C_ADDR_DRV_SHIFT 28 +#define CS42L43_I2C_SDA_DRV_MASK 0x0C000000 +#define CS42L43_I2C_SDA_DRV_SHIFT 26 +#define CS42L43_PDMOUT2_CLK_DRV_MASK 0x00E00000 +#define CS42L43_PDMOUT2_CLK_DRV_SHIFT 21 +#define CS42L43_PDMOUT2_DATA_DRV_MASK 0x001C0000 +#define CS42L43_PDMOUT2_DATA_DRV_SHIFT 18 +#define CS42L43_PDMOUT1_CLK_DRV_MASK 0x00038000 +#define CS42L43_PDMOUT1_CLK_DRV_SHIFT 15 +#define CS42L43_PDMOUT1_DATA_DRV_MASK 0x00007000 +#define CS42L43_PDMOUT1_DATA_DRV_SHIFT 12 +#define CS42L43_SPI_MISO_DRV_MASK 0x00000038 +#define CS42L43_SPI_MISO_DRV_SHIFT 3 + +/* CS42L43_DRV_CTRL4 */ +#define CS42L43_GPIO3_DRV_MASK 0x00000E00 +#define CS42L43_GPIO3_DRV_SHIFT 9 +#define CS42L43_GPIO2_DRV_MASK 0x000001C0 +#define CS42L43_GPIO2_DRV_SHIFT 6 +#define CS42L43_GPIO1_DRV_MASK 0x00000038 +#define CS42L43_GPIO1_DRV_SHIFT 3 + +/* CS42L43_DRV_CTRL_5 */ +#define CS42L43_I2C_SCL_DRV_MASK 0x18000000 +#define CS42L43_I2C_SCL_DRV_SHIFT 27 +#define CS42L43_SPI_SCK_DRV_MASK 0x07000000 +#define CS42L43_SPI_SCK_DRV_SHIFT 24 +#define CS42L43_SPI_MOSI_DRV_MASK 0x00E00000 +#define CS42L43_SPI_MOSI_DRV_SHIFT 21 +#define CS42L43_SPI_SSB_DRV_MASK 0x001C0000 +#define CS42L43_SPI_SSB_DRV_SHIFT 18 +#define CS42L43_ASP_DIN_DRV_MASK 0x000001C0 +#define CS42L43_ASP_DIN_DRV_SHIFT 6 + +/* CS42L43_GPIO_CTRL1 */ +#define CS42L43_GPIO3_POL_MASK 0x00040000 +#define CS42L43_GPIO3_POL_SHIFT 18 +#define CS42L43_GPIO2_POL_MASK 0x00020000 +#define CS42L43_GPIO2_POL_SHIFT 17 +#define CS42L43_GPIO1_POL_MASK 0x00010000 +#define CS42L43_GPIO1_POL_SHIFT 16 +#define CS42L43_GPIO3_LVL_MASK 0x00000400 +#define CS42L43_GPIO3_LVL_SHIFT 10 +#define CS42L43_GPIO2_LVL_MASK 0x00000200 +#define CS42L43_GPIO2_LVL_SHIFT 9 +#define CS42L43_GPIO1_LVL_MASK 0x00000100 +#define CS42L43_GPIO1_LVL_SHIFT 8 +#define CS42L43_GPIO3_DIR_MASK 0x00000004 +#define CS42L43_GPIO3_DIR_SHIFT 2 +#define CS42L43_GPIO2_DIR_MASK 0x00000002 +#define CS42L43_GPIO2_DIR_SHIFT 1 +#define CS42L43_GPIO1_DIR_MASK 0x00000001 +#define CS42L43_GPIO1_DIR_SHIFT 0 + +/* CS42L43_GPIO_CTRL2 */ +#define CS42L43_GPIO3_DEGLITCH_BYP_MASK 0x00000004 +#define CS42L43_GPIO3_DEGLITCH_BYP_SHIFT 2 +#define CS42L43_GPIO2_DEGLITCH_BYP_MASK 0x00000002 +#define CS42L43_GPIO2_DEGLITCH_BYP_SHIFT 1 +#define CS42L43_GPIO1_DEGLITCH_BYP_MASK 0x00000001 +#define CS42L43_GPIO1_DEGLITCH_BYP_SHIFT 0 + +/* CS42L43_GPIO_STS */ +#define CS42L43_GPIO3_STS_MASK 0x00000004 +#define CS42L43_GPIO3_STS_SHIFT 2 +#define CS42L43_GPIO2_STS_MASK 0x00000002 +#define CS42L43_GPIO2_STS_SHIFT 1 +#define CS42L43_GPIO1_STS_MASK 0x00000001 +#define CS42L43_GPIO1_STS_SHIFT 0 + +/* CS42L43_GPIO_FN_SEL */ +#define CS42L43_GPIO3_FN_SEL_MASK 0x00000004 +#define CS42L43_GPIO3_FN_SEL_SHIFT 2 +#define CS42L43_GPIO1_FN_SEL_MASK 0x00000001 +#define CS42L43_GPIO1_FN_SEL_SHIFT 0 + +/* CS42L43_MCLK_SRC_SEL */ +#define CS42L43_OSC_PLL_MCLK_SEL_MASK 0x00000001 +#define CS42L43_OSC_PLL_MCLK_SEL_SHIFT 0 + +/* CS42L43_SAMPLE_RATE1..CS42L43_SAMPLE_RATE4 */ +#define CS42L43_SAMPLE_RATE_MASK 0x0000001F +#define CS42L43_SAMPLE_RATE_SHIFT 0 + +/* CS42L43_PLL_CONTROL */ +#define CS42L43_PLL_REFCLK_EN_MASK 0x00000008 +#define CS42L43_PLL_REFCLK_EN_SHIFT 3 +#define CS42L43_PLL_REFCLK_DIV_MASK 0x00000006 +#define CS42L43_PLL_REFCLK_DIV_SHIFT 1 +#define CS42L43_PLL_REFCLK_SRC_MASK 0x00000001 +#define CS42L43_PLL_REFCLK_SRC_SHIFT 0 + +/* CS42L43_FS_SELECT1 */ +#define CS42L43_ASP_RATE_MASK 0x00000003 +#define CS42L43_ASP_RATE_SHIFT 0 + +/* CS42L43_FS_SELECT2 */ +#define CS42L43_ASRC_DEC_OUT_RATE_MASK 0x000000C0 +#define CS42L43_ASRC_DEC_OUT_RATE_SHIFT 6 +#define CS42L43_ASRC_INT_OUT_RATE_MASK 0x00000030 +#define CS42L43_ASRC_INT_OUT_RATE_SHIFT 4 +#define CS42L43_ASRC_DEC_IN_RATE_MASK 0x0000000C +#define CS42L43_ASRC_DEC_IN_RATE_SHIFT 2 +#define CS42L43_ASRC_INT_IN_RATE_MASK 0x00000003 +#define CS42L43_ASRC_INT_IN_RATE_SHIFT 0 + +/* CS42L43_FS_SELECT3 */ +#define CS42L43_HPOUT_RATE_MASK 0x0000C000 +#define CS42L43_HPOUT_RATE_SHIFT 14 +#define CS42L43_EQZ_RATE_MASK 0x00003000 +#define CS42L43_EQZ_RATE_SHIFT 12 +#define CS42L43_DIAGGEN_RATE_MASK 0x00000C00 +#define CS42L43_DIAGGEN_RATE_SHIFT 10 +#define CS42L43_DECIM_CH4_RATE_MASK 0x00000300 +#define CS42L43_DECIM_CH4_RATE_SHIFT 8 +#define CS42L43_DECIM_CH3_RATE_MASK 0x000000C0 +#define CS42L43_DECIM_CH3_RATE_SHIFT 6 +#define CS42L43_DECIM_CH2_RATE_MASK 0x00000030 +#define CS42L43_DECIM_CH2_RATE_SHIFT 4 +#define CS42L43_DECIM_CH1_RATE_MASK 0x0000000C +#define CS42L43_DECIM_CH1_RATE_SHIFT 2 +#define CS42L43_AMP1_2_RATE_MASK 0x00000003 +#define CS42L43_AMP1_2_RATE_SHIFT 0 + +/* CS42L43_FS_SELECT4 */ +#define CS42L43_SW_DP7_RATE_MASK 0x00C00000 +#define CS42L43_SW_DP7_RATE_SHIFT 22 +#define CS42L43_SW_DP6_RATE_MASK 0x00300000 +#define CS42L43_SW_DP6_RATE_SHIFT 20 +#define CS42L43_SPDIF_RATE_MASK 0x000C0000 +#define CS42L43_SPDIF_RATE_SHIFT 18 +#define CS42L43_SW_DP5_RATE_MASK 0x00030000 +#define CS42L43_SW_DP5_RATE_SHIFT 16 +#define CS42L43_SW_DP4_RATE_MASK 0x0000C000 +#define CS42L43_SW_DP4_RATE_SHIFT 14 +#define CS42L43_SW_DP3_RATE_MASK 0x00003000 +#define CS42L43_SW_DP3_RATE_SHIFT 12 +#define CS42L43_SW_DP2_RATE_MASK 0x00000C00 +#define CS42L43_SW_DP2_RATE_SHIFT 10 +#define CS42L43_SW_DP1_RATE_MASK 0x00000300 +#define CS42L43_SW_DP1_RATE_SHIFT 8 +#define CS42L43_ISRC2_LOW_RATE_MASK 0x000000C0 +#define CS42L43_ISRC2_LOW_RATE_SHIFT 6 +#define CS42L43_ISRC2_HIGH_RATE_MASK 0x00000030 +#define CS42L43_ISRC2_HIGH_RATE_SHIFT 4 +#define CS42L43_ISRC1_LOW_RATE_MASK 0x0000000C +#define CS42L43_ISRC1_LOW_RATE_SHIFT 2 +#define CS42L43_ISRC1_HIGH_RATE_MASK 0x00000003 +#define CS42L43_ISRC1_HIGH_RATE_SHIFT 0 + +/* CS42L43_PDM_CONTROL */ +#define CS42L43_PDM2_CLK_DIV_MASK 0x0000000C +#define CS42L43_PDM2_CLK_DIV_SHIFT 2 +#define CS42L43_PDM1_CLK_DIV_MASK 0x00000003 +#define CS42L43_PDM1_CLK_DIV_SHIFT 0 + +/* CS42L43_ASP_CLK_CONFIG1 */ +#define CS42L43_ASP_BCLK_N_MASK 0x03FF0000 +#define CS42L43_ASP_BCLK_N_SHIFT 16 +#define CS42L43_ASP_BCLK_M_MASK 0x000003FF +#define CS42L43_ASP_BCLK_M_SHIFT 0 + +/* CS42L43_ASP_CLK_CONFIG2 */ +#define CS42L43_ASP_MASTER_MODE_MASK 0x00000002 +#define CS42L43_ASP_MASTER_MODE_SHIFT 1 +#define CS42L43_ASP_BCLK_INV_MASK 0x00000001 +#define CS42L43_ASP_BCLK_INV_SHIFT 0 + +/* CS42L43_OSC_DIV_SEL */ +#define CS42L43_OSC_DIV2_EN_MASK 0x00000001 +#define CS42L43_OSC_DIV2_EN_SHIFT 0 + +/* CS42L43_ADC_B_CTRL1..CS42L43_ADC_B_CTRL1 */ +#define CS42L43_PGA_WIDESWING_MODE_EN_MASK 0x00000080 +#define CS42L43_PGA_WIDESWING_MODE_EN_SHIFT 7 +#define CS42L43_ADC_AIN_SEL_MASK 0x00000010 +#define CS42L43_ADC_AIN_SEL_SHIFT 4 +#define CS42L43_ADC_PGA_GAIN_MASK 0x0000000F +#define CS42L43_ADC_PGA_GAIN_SHIFT 0 + +/* CS42L43_DECIM_HPF_WNF_CTRL1..CS42L43_DECIM_HPF_WNF_CTRL4 */ +#define CS42L43_DECIM_WNF_CF_MASK 0x00000070 +#define CS42L43_DECIM_WNF_CF_SHIFT 4 +#define CS42L43_DECIM_WNF_EN_MASK 0x00000008 +#define CS42L43_DECIM_WNF_EN_SHIFT 3 +#define CS42L43_DECIM_HPF_CF_MASK 0x00000006 +#define CS42L43_DECIM_HPF_CF_SHIFT 1 +#define CS42L43_DECIM_HPF_EN_MASK 0x00000001 +#define CS42L43_DECIM_HPF_EN_SHIFT 0 + +/* CS42L43_DMIC_PDM_CTRL */ +#define CS42L43_PDM2R_INV_MASK 0x00000020 +#define CS42L43_PDM2R_INV_SHIFT 5 +#define CS42L43_PDM2L_INV_MASK 0x00000010 +#define CS42L43_PDM2L_INV_SHIFT 4 +#define CS42L43_PDM1R_INV_MASK 0x00000008 +#define CS42L43_PDM1R_INV_SHIFT 3 +#define CS42L43_PDM1L_INV_MASK 0x00000004 +#define CS42L43_PDM1L_INV_SHIFT 2 + +/* CS42L43_DECIM_VOL_CTRL_CH1_CH2 */ +#define CS42L43_DECIM2_MUTE_MASK 0x80000000 +#define CS42L43_DECIM2_MUTE_SHIFT 31 +#define CS42L43_DECIM2_VOL_MASK 0x3FC00000 +#define CS42L43_DECIM2_VOL_SHIFT 22 +#define CS42L43_DECIM2_VD_RAMP_MASK 0x00380000 +#define CS42L43_DECIM2_VD_RAMP_SHIFT 19 +#define CS42L43_DECIM2_VI_RAMP_MASK 0x00070000 +#define CS42L43_DECIM2_VI_RAMP_SHIFT 16 +#define CS42L43_DECIM1_MUTE_MASK 0x00008000 +#define CS42L43_DECIM1_MUTE_SHIFT 15 +#define CS42L43_DECIM1_VOL_MASK 0x00003FC0 +#define CS42L43_DECIM1_VOL_SHIFT 6 +#define CS42L43_DECIM1_VD_RAMP_MASK 0x00000038 +#define CS42L43_DECIM1_VD_RAMP_SHIFT 3 +#define CS42L43_DECIM1_VI_RAMP_MASK 0x00000007 +#define CS42L43_DECIM1_VI_RAMP_SHIFT 0 + +/* CS42L43_DECIM_VOL_CTRL_CH3_CH4 */ +#define CS42L43_DECIM4_MUTE_MASK 0x80000000 +#define CS42L43_DECIM4_MUTE_SHIFT 31 +#define CS42L43_DECIM4_VOL_MASK 0x3FC00000 +#define CS42L43_DECIM4_VOL_SHIFT 22 +#define CS42L43_DECIM4_VD_RAMP_MASK 0x00380000 +#define CS42L43_DECIM4_VD_RAMP_SHIFT 19 +#define CS42L43_DECIM4_VI_RAMP_MASK 0x00070000 +#define CS42L43_DECIM4_VI_RAMP_SHIFT 16 +#define CS42L43_DECIM3_MUTE_MASK 0x00008000 +#define CS42L43_DECIM3_MUTE_SHIFT 15 +#define CS42L43_DECIM3_VOL_MASK 0x00003FC0 +#define CS42L43_DECIM3_VOL_SHIFT 6 +#define CS42L43_DECIM3_VD_RAMP_MASK 0x00000038 +#define CS42L43_DECIM3_VD_RAMP_SHIFT 3 +#define CS42L43_DECIM3_VI_RAMP_MASK 0x00000007 +#define CS42L43_DECIM3_VI_RAMP_SHIFT 0 + +/* CS42L43_DECIM_VOL_CTRL_UPDATE */ +#define CS42L43_DECIM4_VOL_UPDATE_MASK 0x00000008 +#define CS42L43_DECIM4_VOL_UPDATE_SHIFT 3 +#define CS42L43_DECIM3_VOL_UPDATE_MASK 0x00000004 +#define CS42L43_DECIM3_VOL_UPDATE_SHIFT 2 +#define CS42L43_DECIM2_VOL_UPDATE_MASK 0x00000002 +#define CS42L43_DECIM2_VOL_UPDATE_SHIFT 1 +#define CS42L43_DECIM1_VOL_UPDATE_MASK 0x00000001 +#define CS42L43_DECIM1_VOL_UPDATE_SHIFT 0 + +/* CS42L43_INTP_VOLUME_CTRL1..CS42L43_INTP_VOLUME_CTRL2 */ +#define CS42L43_AMP1_2_VU_MASK 0x00000200 +#define CS42L43_AMP1_2_VU_SHIFT 9 +#define CS42L43_AMP_MUTE_MASK 0x00000100 +#define CS42L43_AMP_MUTE_SHIFT 8 +#define CS42L43_AMP_VOL_MASK 0x000000FF +#define CS42L43_AMP_VOL_SHIFT 0 + +/* CS42L43_AMP1_2_VOL_RAMP */ +#define CS42L43_AMP1_2_VD_RAMP_MASK 0x00000070 +#define CS42L43_AMP1_2_VD_RAMP_SHIFT 4 +#define CS42L43_AMP1_2_VI_RAMP_MASK 0x00000007 +#define CS42L43_AMP1_2_VI_RAMP_SHIFT 0 + +/* CS42L43_ASP_CTRL */ +#define CS42L43_ASP_FSYNC_MODE_MASK 0x00000004 +#define CS42L43_ASP_FSYNC_MODE_SHIFT 2 +#define CS42L43_ASP_BCLK_EN_MASK 0x00000002 +#define CS42L43_ASP_BCLK_EN_SHIFT 1 +#define CS42L43_ASP_FSYNC_EN_MASK 0x00000001 +#define CS42L43_ASP_FSYNC_EN_SHIFT 0 + +/* CS42L43_ASP_FSYNC_CTRL1 */ +#define CS42L43_ASP_FSYNC_M_MASK 0x0007FFFF +#define CS42L43_ASP_FSYNC_M_SHIFT 0 + +/* CS42L43_ASP_FSYNC_CTRL3 */ +#define CS42L43_ASP_FSYNC_IN_INV_MASK 0x00000002 +#define CS42L43_ASP_FSYNC_IN_INV_SHIFT 1 +#define CS42L43_ASP_FSYNC_OUT_INV_MASK 0x00000001 +#define CS42L43_ASP_FSYNC_OUT_INV_SHIFT 0 + +/* CS42L43_ASP_FSYNC_CTRL4 */ +#define CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK 0x00001FFE +#define CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT 1 + +/* CS42L43_ASP_DATA_CTRL */ +#define CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK 0x00000008 +#define CS42L43_ASP_FSYNC_FRAME_START_PHASE_SHIFT 3 +#define CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK 0x00000007 +#define CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT 0 + +/* CS42L43_ASP_RX_EN */ +#define CS42L43_ASP_RX_CH6_EN_MASK 0x00000020 +#define CS42L43_ASP_RX_CH6_EN_SHIFT 5 +#define CS42L43_ASP_RX_CH5_EN_MASK 0x00000010 +#define CS42L43_ASP_RX_CH5_EN_SHIFT 4 +#define CS42L43_ASP_RX_CH4_EN_MASK 0x00000008 +#define CS42L43_ASP_RX_CH4_EN_SHIFT 3 +#define CS42L43_ASP_RX_CH3_EN_MASK 0x00000004 +#define CS42L43_ASP_RX_CH3_EN_SHIFT 2 +#define CS42L43_ASP_RX_CH2_EN_MASK 0x00000002 +#define CS42L43_ASP_RX_CH2_EN_SHIFT 1 +#define CS42L43_ASP_RX_CH1_EN_MASK 0x00000001 +#define CS42L43_ASP_RX_CH1_EN_SHIFT 0 + +/* CS42L43_ASP_TX_EN */ +#define CS42L43_ASP_TX_CH6_EN_MASK 0x00000020 +#define CS42L43_ASP_TX_CH6_EN_SHIFT 5 +#define CS42L43_ASP_TX_CH5_EN_MASK 0x00000010 +#define CS42L43_ASP_TX_CH5_EN_SHIFT 4 +#define CS42L43_ASP_TX_CH4_EN_MASK 0x00000008 +#define CS42L43_ASP_TX_CH4_EN_SHIFT 3 +#define CS42L43_ASP_TX_CH3_EN_MASK 0x00000004 +#define CS42L43_ASP_TX_CH3_EN_SHIFT 2 +#define CS42L43_ASP_TX_CH2_EN_MASK 0x00000002 +#define CS42L43_ASP_TX_CH2_EN_SHIFT 1 +#define CS42L43_ASP_TX_CH1_EN_MASK 0x00000001 +#define CS42L43_ASP_TX_CH1_EN_SHIFT 0 + +/* CS42L43_ASP_RX_CH1_CTRL..CS42L43_ASP_TX_CH6_CTRL */ +#define CS42L43_ASP_CH_WIDTH_MASK 0x001F0000 +#define CS42L43_ASP_CH_WIDTH_SHIFT 16 +#define CS42L43_ASP_CH_SLOT_MASK 0x00001FFE +#define CS42L43_ASP_CH_SLOT_SHIFT 1 +#define CS42L43_ASP_CH_SLOT_PHASE_MASK 0x00000001 +#define CS42L43_ASP_CH_SLOT_PHASE_SHIFT 0 + +/* CS42L43_ASPTX1_INPUT..CS42L43_AMP4MIX_INPUT4 */ +#define CS42L43_MIXER_VOL_MASK 0x00FE0000 +#define CS42L43_MIXER_VOL_SHIFT 17 +#define CS42L43_MIXER_SRC_MASK 0x000001FF +#define CS42L43_MIXER_SRC_SHIFT 0 + +/* CS42L43_ASRC_INT_ENABLES */ +#define CS42L43_ASRC_INT4_EN_MASK 0x00000008 +#define CS42L43_ASRC_INT4_EN_SHIFT 3 +#define CS42L43_ASRC_INT3_EN_MASK 0x00000004 +#define CS42L43_ASRC_INT3_EN_SHIFT 2 +#define CS42L43_ASRC_INT2_EN_MASK 0x00000002 +#define CS42L43_ASRC_INT2_EN_SHIFT 1 +#define CS42L43_ASRC_INT1_EN_MASK 0x00000001 +#define CS42L43_ASRC_INT1_EN_SHIFT 0 + +/* CS42L43_ASRC_DEC_ENABLES */ +#define CS42L43_ASRC_DEC4_EN_MASK 0x00000008 +#define CS42L43_ASRC_DEC4_EN_SHIFT 3 +#define CS42L43_ASRC_DEC3_EN_MASK 0x00000004 +#define CS42L43_ASRC_DEC3_EN_SHIFT 2 +#define CS42L43_ASRC_DEC2_EN_MASK 0x00000002 +#define CS42L43_ASRC_DEC2_EN_SHIFT 1 +#define CS42L43_ASRC_DEC1_EN_MASK 0x00000001 +#define CS42L43_ASRC_DEC1_EN_SHIFT 0 + +/* CS42L43_PDNCNTL */ +#define CS42L43_RING_SENSE_EN_MASK 0x00000002 +#define CS42L43_RING_SENSE_EN_SHIFT 1 + +/* CS42L43_RINGSENSE_DEB_CTRL */ +#define CS42L43_RINGSENSE_INV_MASK 0x00000080 +#define CS42L43_RINGSENSE_INV_SHIFT 7 +#define CS42L43_RINGSENSE_PULLUP_PDNB_MASK 0x00000040 +#define CS42L43_RINGSENSE_PULLUP_PDNB_SHIFT 6 +#define CS42L43_RINGSENSE_FALLING_DB_TIME_MASK 0x00000038 +#define CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT 3 +#define CS42L43_RINGSENSE_RISING_DB_TIME_MASK 0x00000007 +#define CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT 0 + +/* CS42L43_TIPSENSE_DEB_CTRL */ +#define CS42L43_TIPSENSE_INV_MASK 0x00000080 +#define CS42L43_TIPSENSE_INV_SHIFT 7 +#define CS42L43_TIPSENSE_FALLING_DB_TIME_MASK 0x00000038 +#define CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT 3 +#define CS42L43_TIPSENSE_RISING_DB_TIME_MASK 0x00000007 +#define CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT 0 + +/* CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS */ +#define CS42L43_TIPSENSE_UNPLUG_DB_STS_MASK 0x00000008 +#define CS42L43_TIPSENSE_UNPLUG_DB_STS_SHIFT 3 +#define CS42L43_TIPSENSE_PLUG_DB_STS_MASK 0x00000004 +#define CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT 2 +#define CS42L43_RINGSENSE_UNPLUG_DB_STS_MASK 0x00000002 +#define CS42L43_RINGSENSE_UNPLUG_DB_STS_SHIFT 1 +#define CS42L43_RINGSENSE_PLUG_DB_STS_MASK 0x00000001 +#define CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT 0 + +/* CS42L43_HS2 */ +#define CS42L43_HS_CLAMP_DISABLE_MASK 0x10000000 +#define CS42L43_HS_CLAMP_DISABLE_SHIFT 28 +#define CS42L43_HSBIAS_RAMP_MASK 0x0C000000 +#define CS42L43_HSBIAS_RAMP_SHIFT 26 +#define CS42L43_HSDET_MODE_MASK 0x00018000 +#define CS42L43_HSDET_MODE_SHIFT 15 +#define CS42L43_HSDET_MANUAL_MODE_MASK 0x00006000 +#define CS42L43_HSDET_MANUAL_MODE_SHIFT 13 +#define CS42L43_AUTO_HSDET_TIME_MASK 0x00000700 +#define CS42L43_AUTO_HSDET_TIME_SHIFT 8 +#define CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK 0x00000080 +#define CS42L43_AMP3_4_GNDREF_HS3_SEL_SHIFT 7 +#define CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK 0x00000040 +#define CS42L43_AMP3_4_GNDREF_HS4_SEL_SHIFT 6 +#define CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK 0x00000020 +#define CS42L43_HSBIAS_GNDREF_HS3_SEL_SHIFT 5 +#define CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK 0x00000010 +#define CS42L43_HSBIAS_GNDREF_HS4_SEL_SHIFT 4 +#define CS42L43_HSBIAS_OUT_HS3_SEL_MASK 0x00000008 +#define CS42L43_HSBIAS_OUT_HS3_SEL_SHIFT 3 +#define CS42L43_HSBIAS_OUT_HS4_SEL_MASK 0x00000004 +#define CS42L43_HSBIAS_OUT_HS4_SEL_SHIFT 2 +#define CS42L43_HSGND_HS3_SEL_MASK 0x00000002 +#define CS42L43_HSGND_HS3_SEL_SHIFT 1 +#define CS42L43_HSGND_HS4_SEL_MASK 0x00000001 +#define CS42L43_HSGND_HS4_SEL_SHIFT 0 + +/* CS42L43_HS_STAT */ +#define CS42L43_HSDET_TYPE_STS_MASK 0x00000007 +#define CS42L43_HSDET_TYPE_STS_SHIFT 0 + +/* CS42L43_MCU_SW_INTERRUPT */ +#define CS42L43_CONTROL_IND_MASK 0x00000004 +#define CS42L43_CONTROL_IND_SHIFT 2 +#define CS42L43_CONFIGS_IND_MASK 0x00000002 +#define CS42L43_CONFIGS_IND_SHIFT 1 +#define CS42L43_PATCH_IND_MASK 0x00000001 +#define CS42L43_PATCH_IND_SHIFT 0 + +/* CS42L43_STEREO_MIC_CTRL */ +#define CS42L43_HS2_BIAS_SENSE_EN_MASK 0x00000020 +#define CS42L43_HS2_BIAS_SENSE_EN_SHIFT 5 +#define CS42L43_HS1_BIAS_SENSE_EN_MASK 0x00000010 +#define CS42L43_HS1_BIAS_SENSE_EN_SHIFT 4 +#define CS42L43_HS2_BIAS_EN_MASK 0x00000008 +#define CS42L43_HS2_BIAS_EN_SHIFT 3 +#define CS42L43_HS1_BIAS_EN_MASK 0x00000004 +#define CS42L43_HS1_BIAS_EN_SHIFT 2 +#define CS42L43_JACK_STEREO_CONFIG_MASK 0x00000003 +#define CS42L43_JACK_STEREO_CONFIG_SHIFT 0 + +/* CS42L43_STEREO_MIC_CLAMP_CTRL */ +#define CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK 0x00000002 +#define CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_SHIFT 1 +#define CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK 0x00000001 +#define CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_SHIFT 0 + +/* CS42L43_BLOCK_EN2 */ +#define CS42L43_SPI_MSTR_EN_MASK 0x00000001 +#define CS42L43_SPI_MSTR_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN3 */ +#define CS42L43_PDM2_DIN_R_EN_MASK 0x00000020 +#define CS42L43_PDM2_DIN_R_EN_SHIFT 5 +#define CS42L43_PDM2_DIN_L_EN_MASK 0x00000010 +#define CS42L43_PDM2_DIN_L_EN_SHIFT 4 +#define CS42L43_PDM1_DIN_R_EN_MASK 0x00000008 +#define CS42L43_PDM1_DIN_R_EN_SHIFT 3 +#define CS42L43_PDM1_DIN_L_EN_MASK 0x00000004 +#define CS42L43_PDM1_DIN_L_EN_SHIFT 2 +#define CS42L43_ADC2_EN_MASK 0x00000002 +#define CS42L43_ADC2_EN_SHIFT 1 +#define CS42L43_ADC1_EN_MASK 0x00000001 +#define CS42L43_ADC1_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN4 */ +#define CS42L43_ASRC_DEC_BANK_EN_MASK 0x00000002 +#define CS42L43_ASRC_DEC_BANK_EN_SHIFT 1 +#define CS42L43_ASRC_INT_BANK_EN_MASK 0x00000001 +#define CS42L43_ASRC_INT_BANK_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN5 */ +#define CS42L43_ISRC2_BANK_EN_MASK 0x00000002 +#define CS42L43_ISRC2_BANK_EN_SHIFT 1 +#define CS42L43_ISRC1_BANK_EN_MASK 0x00000001 +#define CS42L43_ISRC1_BANK_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN6 */ +#define CS42L43_MIXER_EN_MASK 0x00000001 +#define CS42L43_MIXER_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN7 */ +#define CS42L43_EQ_EN_MASK 0x00000001 +#define CS42L43_EQ_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN8 */ +#define CS42L43_HP_EN_MASK 0x00000001 +#define CS42L43_HP_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN9 */ +#define CS42L43_TONE_EN_MASK 0x00000001 +#define CS42L43_TONE_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN10 */ +#define CS42L43_AMP2_EN_MASK 0x00000002 +#define CS42L43_AMP2_EN_SHIFT 1 +#define CS42L43_AMP1_EN_MASK 0x00000001 +#define CS42L43_AMP1_EN_SHIFT 0 + +/* CS42L43_BLOCK_EN11 */ +#define CS42L43_SPDIF_EN_MASK 0x00000001 +#define CS42L43_SPDIF_EN_SHIFT 0 + +/* CS42L43_TONE_CH1_CTRL..CS42L43_TONE_CH2_CTRL */ +#define CS42L43_TONE_FREQ_MASK 0x00000070 +#define CS42L43_TONE_FREQ_SHIFT 4 +#define CS42L43_TONE_SEL_MASK 0x0000000F +#define CS42L43_TONE_SEL_SHIFT 0 + +/* CS42L43_MIC_DETECT_CONTROL_1 */ +#define CS42L43_BUTTON_DETECT_MODE_MASK 0x00000018 +#define CS42L43_BUTTON_DETECT_MODE_SHIFT 3 +#define CS42L43_HSBIAS_MODE_MASK 0x00000006 +#define CS42L43_HSBIAS_MODE_SHIFT 1 +#define CS42L43_MIC_LVL_DET_DISABLE_MASK 0x00000001 +#define CS42L43_MIC_LVL_DET_DISABLE_SHIFT 0 + +/* CS42L43_DETECT_STATUS_1 */ +#define CS42L43_HSDET_DC_STS_MASK 0x01FF0000 +#define CS42L43_HSDET_DC_STS_SHIFT 16 +#define CS42L43_JACKDET_STS_MASK 0x00000080 +#define CS42L43_JACKDET_STS_SHIFT 7 +#define CS42L43_HSBIAS_CLAMP_STS_MASK 0x00000040 +#define CS42L43_HSBIAS_CLAMP_STS_SHIFT 6 + +/* CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL */ +#define CS42L43_JACKDET_MODE_MASK 0xC0000000 +#define CS42L43_JACKDET_MODE_SHIFT 30 +#define CS42L43_JACKDET_INV_MASK 0x20000000 +#define CS42L43_JACKDET_INV_SHIFT 29 +#define CS42L43_JACKDET_DB_TIME_MASK 0x03000000 +#define CS42L43_JACKDET_DB_TIME_SHIFT 24 +#define CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK 0x00800000 +#define CS42L43_S0_AUTO_ADCMUTE_DISABLE_SHIFT 23 +#define CS42L43_HSBIAS_SENSE_EN_MASK 0x00000080 +#define CS42L43_HSBIAS_SENSE_EN_SHIFT 7 +#define CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK 0x00000040 +#define CS42L43_AUTO_HSBIAS_CLAMP_EN_SHIFT 6 +#define CS42L43_JACKDET_SENSE_EN_MASK 0x00000020 +#define CS42L43_JACKDET_SENSE_EN_SHIFT 5 +#define CS42L43_HSBIAS_SENSE_TRIP_MASK 0x00000007 +#define CS42L43_HSBIAS_SENSE_TRIP_SHIFT 0 + +/* CS42L43_MIC_DETECT_CONTROL_ANDROID */ +#define CS42L43_HSDET_LVL_COMBWIDTH_MASK 0xC0000000 +#define CS42L43_HSDET_LVL_COMBWIDTH_SHIFT 30 +#define CS42L43_HSDET_LVL2_THRESH_MASK 0x01FF0000 +#define CS42L43_HSDET_LVL2_THRESH_SHIFT 16 +#define CS42L43_HSDET_LVL1_THRESH_MASK 0x000001FF +#define CS42L43_HSDET_LVL1_THRESH_SHIFT 0 + +/* CS42L43_ISRC1_CTRL..CS42L43_ISRC2_CTRL */ +#define CS42L43_ISRC_INT2_EN_MASK 0x00000200 +#define CS42L43_ISRC_INT2_EN_SHIFT 9 +#define CS42L43_ISRC_INT1_EN_MASK 0x00000100 +#define CS42L43_ISRC_INT1_EN_SHIFT 8 +#define CS42L43_ISRC_DEC2_EN_MASK 0x00000002 +#define CS42L43_ISRC_DEC2_EN_SHIFT 1 +#define CS42L43_ISRC_DEC1_EN_MASK 0x00000001 +#define CS42L43_ISRC_DEC1_EN_SHIFT 0 + +/* CS42L43_CTRL_REG */ +#define CS42L43_PLL_MODE_BYPASS_500_MASK 0x00000004 +#define CS42L43_PLL_MODE_BYPASS_500_SHIFT 2 +#define CS42L43_PLL_MODE_BYPASS_1029_MASK 0x00000002 +#define CS42L43_PLL_MODE_BYPASS_1029_SHIFT 1 +#define CS42L43_PLL_EN_MASK 0x00000001 +#define CS42L43_PLL_EN_SHIFT 0 + +/* CS42L43_FDIV_FRAC */ +#define CS42L43_PLL_DIV_INT_MASK 0xFF000000 +#define CS42L43_PLL_DIV_INT_SHIFT 24 +#define CS42L43_PLL_DIV_FRAC_BYTE2_MASK 0x00FF0000 +#define CS42L43_PLL_DIV_FRAC_BYTE2_SHIFT 16 +#define CS42L43_PLL_DIV_FRAC_BYTE1_MASK 0x0000FF00 +#define CS42L43_PLL_DIV_FRAC_BYTE1_SHIFT 8 +#define CS42L43_PLL_DIV_FRAC_BYTE0_MASK 0x000000FF +#define CS42L43_PLL_DIV_FRAC_BYTE0_SHIFT 0 + +/* CS42L43_CAL_RATIO */ +#define CS42L43_PLL_CAL_RATIO_MASK 0x000000FF +#define CS42L43_PLL_CAL_RATIO_SHIFT 0 + +/* CS42L43_SPI_CLK_CONFIG1 */ +#define CS42L43_SCLK_DIV_MASK 0x0000000F +#define CS42L43_SCLK_DIV_SHIFT 0 + +/* CS42L43_SPI_CONFIG1 */ +#define CS42L43_SPI_SS_IDLE_DUR_MASK 0x0F000000 +#define CS42L43_SPI_SS_IDLE_DUR_SHIFT 24 +#define CS42L43_SPI_SS_DELAY_DUR_MASK 0x000F0000 +#define CS42L43_SPI_SS_DELAY_DUR_SHIFT 16 +#define CS42L43_SPI_THREE_WIRE_MASK 0x00000100 +#define CS42L43_SPI_THREE_WIRE_SHIFT 8 +#define CS42L43_SPI_DPHA_MASK 0x00000040 +#define CS42L43_SPI_DPHA_SHIFT 6 +#define CS42L43_SPI_CPHA_MASK 0x00000020 +#define CS42L43_SPI_CPHA_SHIFT 5 +#define CS42L43_SPI_CPOL_MASK 0x00000010 +#define CS42L43_SPI_CPOL_SHIFT 4 +#define CS42L43_SPI_SS_SEL_MASK 0x00000007 +#define CS42L43_SPI_SS_SEL_SHIFT 0 + +/* CS42L43_SPI_CONFIG2 */ +#define CS42L43_SPI_SS_FRC_MASK 0x00000001 +#define CS42L43_SPI_SS_FRC_SHIFT 0 + +/* CS42L43_SPI_CONFIG3 */ +#define CS42L43_SPI_WDT_ENA_MASK 0x00000001 +#define CS42L43_SPI_WDT_ENA_SHIFT 0 + +/* CS42L43_SPI_CONFIG4 */ +#define CS42L43_SPI_STALL_ENA_MASK 0x00010000 +#define CS42L43_SPI_STALL_ENA_SHIFT 16 + +/* CS42L43_SPI_STATUS1 */ +#define CS42L43_SPI_ABORT_STS_MASK 0x00000002 +#define CS42L43_SPI_ABORT_STS_SHIFT 1 +#define CS42L43_SPI_DONE_STS_MASK 0x00000001 +#define CS42L43_SPI_DONE_STS_SHIFT 0 + +/* CS42L43_SPI_STATUS2 */ +#define CS42L43_SPI_RX_DONE_STS_MASK 0x00000010 +#define CS42L43_SPI_RX_DONE_STS_SHIFT 4 +#define CS42L43_SPI_TX_DONE_STS_MASK 0x00000001 +#define CS42L43_SPI_TX_DONE_STS_SHIFT 0 + +/* CS42L43_TRAN_CONFIG1 */ +#define CS42L43_SPI_START_MASK 0x00000001 +#define CS42L43_SPI_START_SHIFT 0 + +/* CS42L43_TRAN_CONFIG2 */ +#define CS42L43_SPI_ABORT_MASK 0x00000001 +#define CS42L43_SPI_ABORT_SHIFT 0 + +/* CS42L43_TRAN_CONFIG3 */ +#define CS42L43_SPI_WORD_SIZE_MASK 0x00070000 +#define CS42L43_SPI_WORD_SIZE_SHIFT 16 +#define CS42L43_SPI_CMD_MASK 0x00000003 +#define CS42L43_SPI_CMD_SHIFT 0 + +/* CS42L43_TRAN_CONFIG4 */ +#define CS42L43_SPI_TX_LENGTH_MASK 0x0000FFFF +#define CS42L43_SPI_TX_LENGTH_SHIFT 0 + +/* CS42L43_TRAN_CONFIG5 */ +#define CS42L43_SPI_RX_LENGTH_MASK 0x0000FFFF +#define CS42L43_SPI_RX_LENGTH_SHIFT 0 + +/* CS42L43_TRAN_CONFIG6 */ +#define CS42L43_SPI_TX_BLOCK_LENGTH_MASK 0x0000000F +#define CS42L43_SPI_TX_BLOCK_LENGTH_SHIFT 0 + +/* CS42L43_TRAN_CONFIG7 */ +#define CS42L43_SPI_RX_BLOCK_LENGTH_MASK 0x0000000F +#define CS42L43_SPI_RX_BLOCK_LENGTH_SHIFT 0 + +/* CS42L43_TRAN_CONFIG8 */ +#define CS42L43_SPI_RX_DONE_MASK 0x00000010 +#define CS42L43_SPI_RX_DONE_SHIFT 4 +#define CS42L43_SPI_TX_DONE_MASK 0x00000001 +#define CS42L43_SPI_TX_DONE_SHIFT 0 + +/* CS42L43_TRAN_STATUS1 */ +#define CS42L43_SPI_BUSY_STS_MASK 0x00000100 +#define CS42L43_SPI_BUSY_STS_SHIFT 8 +#define CS42L43_SPI_RX_REQUEST_MASK 0x00000010 +#define CS42L43_SPI_RX_REQUEST_SHIFT 4 +#define CS42L43_SPI_TX_REQUEST_MASK 0x00000001 +#define CS42L43_SPI_TX_REQUEST_SHIFT 0 + +/* CS42L43_TRAN_STATUS2 */ +#define CS42L43_SPI_TX_BYTE_COUNT_MASK 0x0000FFFF +#define CS42L43_SPI_TX_BYTE_COUNT_SHIFT 0 + +/* CS42L43_TRAN_STATUS3 */ +#define CS42L43_SPI_RX_BYTE_COUNT_MASK 0x0000FFFF +#define CS42L43_SPI_RX_BYTE_COUNT_SHIFT 0 + +/* CS42L43_TX_DATA */ +#define CS42L43_SPI_TX_DATA_MASK 0xFFFFFFFF +#define CS42L43_SPI_TX_DATA_SHIFT 0 + +/* CS42L43_RX_DATA */ +#define CS42L43_SPI_RX_DATA_MASK 0xFFFFFFFF +#define CS42L43_SPI_RX_DATA_SHIFT 0 + +/* CS42L43_DACCNFG1 */ +#define CS42L43_HP_MSTR_VOL_CTRL_EN_MASK 0x00000008 +#define CS42L43_HP_MSTR_VOL_CTRL_EN_SHIFT 3 +#define CS42L43_AMP4_INV_MASK 0x00000002 +#define CS42L43_AMP4_INV_SHIFT 1 +#define CS42L43_AMP3_INV_MASK 0x00000001 +#define CS42L43_AMP3_INV_SHIFT 0 + +/* CS42L43_DACCNFG2 */ +#define CS42L43_HP_AUTO_CLAMP_DISABLE_MASK 0x00000002 +#define CS42L43_HP_AUTO_CLAMP_DISABLE_SHIFT 1 +#define CS42L43_HP_HPF_EN_MASK 0x00000001 +#define CS42L43_HP_HPF_EN_SHIFT 0 + +/* CS42L43_HPPATHVOL */ +#define CS42L43_AMP4_PATH_VOL_MASK 0x01FF0000 +#define CS42L43_AMP4_PATH_VOL_SHIFT 16 +#define CS42L43_AMP3_PATH_VOL_MASK 0x000001FF +#define CS42L43_AMP3_PATH_VOL_SHIFT 0 + +/* CS42L43_PGAVOL */ +#define CS42L43_HP_PATH_VOL_RAMP_MASK 0x0003C000 +#define CS42L43_HP_PATH_VOL_RAMP_SHIFT 14 +#define CS42L43_HP_PATH_VOL_ZC_MASK 0x00002000 +#define CS42L43_HP_PATH_VOL_ZC_SHIFT 13 +#define CS42L43_HP_PATH_VOL_SFT_MASK 0x00001000 +#define CS42L43_HP_PATH_VOL_SFT_SHIFT 12 +#define CS42L43_HP_DIG_VOL_RAMP_MASK 0x00000F00 +#define CS42L43_HP_DIG_VOL_RAMP_SHIFT 8 +#define CS42L43_HP_ANA_VOL_RAMP_MASK 0x0000000F +#define CS42L43_HP_ANA_VOL_RAMP_SHIFT 0 + +/* CS42L43_LOADDETRESULTS */ +#define CS42L43_AMP3_RES_DET_MASK 0x00000003 +#define CS42L43_AMP3_RES_DET_SHIFT 0 + +/* CS42L43_LOADDETENA */ +#define CS42L43_HPLOAD_DET_EN_MASK 0x00000001 +#define CS42L43_HPLOAD_DET_EN_SHIFT 0 + +/* CS42L43_CTRL */ +#define CS42L43_ADPTPWR_MODE_MASK 0x00000007 +#define CS42L43_ADPTPWR_MODE_SHIFT 0 + +/* CS42L43_COEFF_RD_WR0 */ +#define CS42L43_WRITE_MODE_MASK 0x00000002 +#define CS42L43_WRITE_MODE_SHIFT 1 + +/* CS42L43_INIT_DONE0 */ +#define CS42L43_INITIALIZE_DONE_MASK 0x00000001 +#define CS42L43_INITIALIZE_DONE_SHIFT 0 + +/* CS42L43_START_EQZ0 */ +#define CS42L43_START_FILTER_MASK 0x00000001 +#define CS42L43_START_FILTER_SHIFT 0 + +/* CS42L43_MUTE_EQ_IN0 */ +#define CS42L43_MUTE_EQ_CH2_MASK 0x00000002 +#define CS42L43_MUTE_EQ_CH2_SHIFT 1 +#define CS42L43_MUTE_EQ_CH1_MASK 0x00000001 +#define CS42L43_MUTE_EQ_CH1_SHIFT 0 + +/* CS42L43_PLL_INT */ +#define CS42L43_PLL_LOST_LOCK_INT_MASK 0x00000002 +#define CS42L43_PLL_LOST_LOCK_INT_SHIFT 1 +#define CS42L43_PLL_READY_INT_MASK 0x00000001 +#define CS42L43_PLL_READY_INT_SHIFT 0 + +/* CS42L43_SOFT_INT */ +#define CS42L43_CONTROL_APPLIED_INT_MASK 0x00000010 +#define CS42L43_CONTROL_APPLIED_INT_SHIFT 4 +#define CS42L43_CONTROL_WARN_INT_MASK 0x00000008 +#define CS42L43_CONTROL_WARN_INT_SHIFT 3 +#define CS42L43_PATCH_WARN_INT_MASK 0x00000002 +#define CS42L43_PATCH_WARN_INT_SHIFT 1 +#define CS42L43_PATCH_APPLIED_INT_MASK 0x00000001 +#define CS42L43_PATCH_APPLIED_INT_SHIFT 0 + +/* CS42L43_MSM_INT */ +#define CS42L43_HP_STARTUP_DONE_INT_MASK 0x00000800 +#define CS42L43_HP_STARTUP_DONE_INT_SHIFT 11 +#define CS42L43_HP_SHUTDOWN_DONE_INT_MASK 0x00000400 +#define CS42L43_HP_SHUTDOWN_DONE_INT_SHIFT 10 +#define CS42L43_HSDET_DONE_INT_MASK 0x00000200 +#define CS42L43_HSDET_DONE_INT_SHIFT 9 +#define CS42L43_TIPSENSE_UNPLUG_DB_INT_MASK 0x00000080 +#define CS42L43_TIPSENSE_UNPLUG_DB_INT_SHIFT 7 +#define CS42L43_TIPSENSE_PLUG_DB_INT_MASK 0x00000040 +#define CS42L43_TIPSENSE_PLUG_DB_INT_SHIFT 6 +#define CS42L43_RINGSENSE_UNPLUG_DB_INT_MASK 0x00000020 +#define CS42L43_RINGSENSE_UNPLUG_DB_INT_SHIFT 5 +#define CS42L43_RINGSENSE_PLUG_DB_INT_MASK 0x00000010 +#define CS42L43_RINGSENSE_PLUG_DB_INT_SHIFT 4 +#define CS42L43_TIPSENSE_UNPLUG_PDET_INT_MASK 0x00000008 +#define CS42L43_TIPSENSE_UNPLUG_PDET_INT_SHIFT 3 +#define CS42L43_TIPSENSE_PLUG_PDET_INT_MASK 0x00000004 +#define CS42L43_TIPSENSE_PLUG_PDET_INT_SHIFT 2 +#define CS42L43_RINGSENSE_UNPLUG_PDET_INT_MASK 0x00000002 +#define CS42L43_RINGSENSE_UNPLUG_PDET_INT_SHIFT 1 +#define CS42L43_RINGSENSE_PLUG_PDET_INT_MASK 0x00000001 +#define CS42L43_RINGSENSE_PLUG_PDET_INT_SHIFT 0 + +/* CS42L43_ACC_DET_INT */ +#define CS42L43_HS2_BIAS_SENSE_INT_MASK 0x00000800 +#define CS42L43_HS2_BIAS_SENSE_INT_SHIFT 11 +#define CS42L43_HS1_BIAS_SENSE_INT_MASK 0x00000400 +#define CS42L43_HS1_BIAS_SENSE_INT_SHIFT 10 +#define CS42L43_DC_DETECT1_FALSE_INT_MASK 0x00000080 +#define CS42L43_DC_DETECT1_FALSE_INT_SHIFT 7 +#define CS42L43_DC_DETECT1_TRUE_INT_MASK 0x00000040 +#define CS42L43_DC_DETECT1_TRUE_INT_SHIFT 6 +#define CS42L43_HSBIAS_CLAMPED_INT_MASK 0x00000008 +#define CS42L43_HSBIAS_CLAMPED_INT_SHIFT 3 +#define CS42L43_HS3_4_BIAS_SENSE_INT_MASK 0x00000001 +#define CS42L43_HS3_4_BIAS_SENSE_INT_SHIFT 0 + +/* CS42L43_SPI_MSTR_INT */ +#define CS42L43_IRQ_SPI_STALLING_INT_MASK 0x00000004 +#define CS42L43_IRQ_SPI_STALLING_INT_SHIFT 2 +#define CS42L43_IRQ_SPI_STS_INT_MASK 0x00000002 +#define CS42L43_IRQ_SPI_STS_INT_SHIFT 1 +#define CS42L43_IRQ_SPI_BLOCK_INT_MASK 0x00000001 +#define CS42L43_IRQ_SPI_BLOCK_INT_SHIFT 0 + +/* CS42L43_SW_TO_SPI_BRIDGE_INT */ +#define CS42L43_SW2SPI_BUF_OVF_UDF_INT_MASK 0x00000001 +#define CS42L43_SW2SPI_BUF_OVF_UDF_INT_SHIFT 0 + +/* CS42L43_CLASS_D_AMP_INT */ +#define CS42L43_AMP2_CLK_STOP_FAULT_INT_MASK 0x00002000 +#define CS42L43_AMP2_CLK_STOP_FAULT_INT_SHIFT 13 +#define CS42L43_AMP1_CLK_STOP_FAULT_INT_MASK 0x00001000 +#define CS42L43_AMP1_CLK_STOP_FAULT_INT_SHIFT 12 +#define CS42L43_AMP2_VDDSPK_FAULT_INT_MASK 0x00000800 +#define CS42L43_AMP2_VDDSPK_FAULT_INT_SHIFT 11 +#define CS42L43_AMP1_VDDSPK_FAULT_INT_MASK 0x00000400 +#define CS42L43_AMP1_VDDSPK_FAULT_INT_SHIFT 10 +#define CS42L43_AMP2_SHUTDOWN_DONE_INT_MASK 0x00000200 +#define CS42L43_AMP2_SHUTDOWN_DONE_INT_SHIFT 9 +#define CS42L43_AMP1_SHUTDOWN_DONE_INT_MASK 0x00000100 +#define CS42L43_AMP1_SHUTDOWN_DONE_INT_SHIFT 8 +#define CS42L43_AMP2_STARTUP_DONE_INT_MASK 0x00000080 +#define CS42L43_AMP2_STARTUP_DONE_INT_SHIFT 7 +#define CS42L43_AMP1_STARTUP_DONE_INT_MASK 0x00000040 +#define CS42L43_AMP1_STARTUP_DONE_INT_SHIFT 6 +#define CS42L43_AMP2_THERM_SHDN_INT_MASK 0x00000020 +#define CS42L43_AMP2_THERM_SHDN_INT_SHIFT 5 +#define CS42L43_AMP1_THERM_SHDN_INT_MASK 0x00000010 +#define CS42L43_AMP1_THERM_SHDN_INT_SHIFT 4 +#define CS42L43_AMP2_THERM_WARN_INT_MASK 0x00000008 +#define CS42L43_AMP2_THERM_WARN_INT_SHIFT 3 +#define CS42L43_AMP1_THERM_WARN_INT_MASK 0x00000004 +#define CS42L43_AMP1_THERM_WARN_INT_SHIFT 2 +#define CS42L43_AMP2_SCDET_INT_MASK 0x00000002 +#define CS42L43_AMP2_SCDET_INT_SHIFT 1 +#define CS42L43_AMP1_SCDET_INT_MASK 0x00000001 +#define CS42L43_AMP1_SCDET_INT_SHIFT 0 + +/* CS42L43_GPIO_INT */ +#define CS42L43_GPIO3_FALL_INT_MASK 0x00000020 +#define CS42L43_GPIO3_FALL_INT_SHIFT 5 +#define CS42L43_GPIO3_RISE_INT_MASK 0x00000010 +#define CS42L43_GPIO3_RISE_INT_SHIFT 4 +#define CS42L43_GPIO2_FALL_INT_MASK 0x00000008 +#define CS42L43_GPIO2_FALL_INT_SHIFT 3 +#define CS42L43_GPIO2_RISE_INT_MASK 0x00000004 +#define CS42L43_GPIO2_RISE_INT_SHIFT 2 +#define CS42L43_GPIO1_FALL_INT_MASK 0x00000002 +#define CS42L43_GPIO1_FALL_INT_SHIFT 1 +#define CS42L43_GPIO1_RISE_INT_MASK 0x00000001 +#define CS42L43_GPIO1_RISE_INT_SHIFT 0 + +/* CS42L43_HPOUT_INT */ +#define CS42L43_HP_ILIMIT_INT_MASK 0x00000002 +#define CS42L43_HP_ILIMIT_INT_SHIFT 1 +#define CS42L43_HP_LOADDET_DONE_INT_MASK 0x00000001 +#define CS42L43_HP_LOADDET_DONE_INT_SHIFT 0 + +/* CS42L43_BOOT_CONTROL */ +#define CS42L43_LOCK_HW_STS_MASK 0x00000002 +#define CS42L43_LOCK_HW_STS_SHIFT 1 + +/* CS42L43_BLOCK_EN */ +#define CS42L43_MCU_EN_MASK 0x00000001 +#define CS42L43_MCU_EN_SHIFT 0 + +/* CS42L43_SHUTTER_CONTROL */ +#define CS42L43_STATUS_SPK_SHUTTER_MUTE_MASK 0x00008000 +#define CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT 15 +#define CS42L43_SPK_SHUTTER_CFG_MASK 0x00000F00 +#define CS42L43_SPK_SHUTTER_CFG_SHIFT 8 +#define CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK 0x00000080 +#define CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT 7 +#define CS42L43_MIC_SHUTTER_CFG_MASK 0x0000000F +#define CS42L43_MIC_SHUTTER_CFG_SHIFT 0 + +/* CS42L43_MCU_SW_REV */ +#define CS42L43_BIOS_SUBMINOR_REV_MASK 0xFF000000 +#define CS42L43_BIOS_SUBMINOR_REV_SHIFT 24 +#define CS42L43_BIOS_MINOR_REV_MASK 0x00F00000 +#define CS42L43_BIOS_MINOR_REV_SHIFT 20 +#define CS42L43_BIOS_MAJOR_REV_MASK 0x000F0000 +#define CS42L43_BIOS_MAJOR_REV_SHIFT 16 +#define CS42L43_FW_SUBMINOR_REV_MASK 0x0000FF00 +#define CS42L43_FW_SUBMINOR_REV_SHIFT 8 +#define CS42L43_FW_MINOR_REV_MASK 0x000000F0 +#define CS42L43_FW_MINOR_REV_SHIFT 4 +#define CS42L43_FW_MAJOR_REV_MASK 0x0000000F +#define CS42L43_FW_MAJOR_REV_SHIFT 0 + +/* CS42L43_NEED_CONFIGS */ +#define CS42L43_FW_PATCH_NEED_CFG_MASK 0x80000000 +#define CS42L43_FW_PATCH_NEED_CFG_SHIFT 31 + +/* CS42L43_FW_MISSION_CTRL_MM_CTRL_SELECTION */ +#define CS42L43_FW_MM_CTRL_MCU_SEL_MASK 0x00000001 +#define CS42L43_FW_MM_CTRL_MCU_SEL_SHIFT 0 + +/* CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_REG */ +#define CS42L43_FW_MISSION_CTRL_MM_MCU_CFG_DISABLE_VAL 0xF05AA50F + +#endif /* CS42L43_CORE_REGS_H */ diff --git a/include/linux/mfd/cs42l43.h b/include/linux/mfd/cs42l43.h new file mode 100644 index 000000000000..cf8263aab41b --- /dev/null +++ b/include/linux/mfd/cs42l43.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 core driver external data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/soundwire/sdw.h> +#include <linux/workqueue.h> + +#ifndef CS42L43_CORE_EXT_H +#define CS42L43_CORE_EXT_H + +#define CS42L43_N_SUPPLIES 3 + +enum cs42l43_irq_numbers { + CS42L43_PLL_LOST_LOCK, + CS42L43_PLL_READY, + + CS42L43_HP_STARTUP_DONE, + CS42L43_HP_SHUTDOWN_DONE, + CS42L43_HSDET_DONE, + CS42L43_TIPSENSE_UNPLUG_DB, + CS42L43_TIPSENSE_PLUG_DB, + CS42L43_RINGSENSE_UNPLUG_DB, + CS42L43_RINGSENSE_PLUG_DB, + CS42L43_TIPSENSE_UNPLUG_PDET, + CS42L43_TIPSENSE_PLUG_PDET, + CS42L43_RINGSENSE_UNPLUG_PDET, + CS42L43_RINGSENSE_PLUG_PDET, + + CS42L43_HS2_BIAS_SENSE, + CS42L43_HS1_BIAS_SENSE, + CS42L43_DC_DETECT1_FALSE, + CS42L43_DC_DETECT1_TRUE, + CS42L43_HSBIAS_CLAMPED, + CS42L43_HS3_4_BIAS_SENSE, + + CS42L43_AMP2_CLK_STOP_FAULT, + CS42L43_AMP1_CLK_STOP_FAULT, + CS42L43_AMP2_VDDSPK_FAULT, + CS42L43_AMP1_VDDSPK_FAULT, + CS42L43_AMP2_SHUTDOWN_DONE, + CS42L43_AMP1_SHUTDOWN_DONE, + CS42L43_AMP2_STARTUP_DONE, + CS42L43_AMP1_STARTUP_DONE, + CS42L43_AMP2_THERM_SHDN, + CS42L43_AMP1_THERM_SHDN, + CS42L43_AMP2_THERM_WARN, + CS42L43_AMP1_THERM_WARN, + CS42L43_AMP2_SCDET, + CS42L43_AMP1_SCDET, + + CS42L43_GPIO3_FALL, + CS42L43_GPIO3_RISE, + CS42L43_GPIO2_FALL, + CS42L43_GPIO2_RISE, + CS42L43_GPIO1_FALL, + CS42L43_GPIO1_RISE, + + CS42L43_HP_ILIMIT, + CS42L43_HP_LOADDET_DONE, +}; + +struct cs42l43 { + struct device *dev; + struct regmap *regmap; + struct sdw_slave *sdw; + + struct regulator *vdd_p; + struct regulator *vdd_d; + struct regulator_bulk_data core_supplies[CS42L43_N_SUPPLIES]; + + struct gpio_desc *reset; + + int irq; + struct regmap_irq_chip irq_chip; + struct regmap_irq_chip_data *irq_data; + + struct work_struct boot_work; + struct completion device_attach; + struct completion device_detach; + struct completion firmware_download; + int firmware_error; + + unsigned int sdw_freq; + /* Lock to gate control of the PLL and its sources. */ + struct mutex pll_lock; + + bool sdw_pll_active; + bool attached; + bool hw_lock; +}; + +#endif /* CS42L43_CORE_EXT_H */ diff --git a/include/linux/prefetch.h b/include/linux/prefetch.h index b83a3f944f28..b068e2e60939 100644 --- a/include/linux/prefetch.h +++ b/include/linux/prefetch.h @@ -25,11 +25,10 @@ struct page; prefetch() should be defined by the architecture, if not, the #define below provides a no-op define. - There are 3 prefetch() macros: + There are 2 prefetch() macros: prefetch(x) - prefetches the cacheline at "x" for read prefetchw(x) - prefetches the cacheline at "x" for write - spin_lock_prefetch(x) - prefetches the spinlock *x for taking there is also PREFETCH_STRIDE which is the architecure-preferred "lookahead" size for prefetching streamed operations. @@ -44,10 +43,6 @@ struct page; #define prefetchw(x) __builtin_prefetch(x,1) #endif -#ifndef ARCH_HAS_SPINLOCK_PREFETCH -#define spin_lock_prefetch(x) prefetchw(x) -#endif - #ifndef PREFETCH_STRIDE #define PREFETCH_STRIDE (4*L1_CACHE_BYTES) #endif diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 054d7911bfc9..c1637515a8a4 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -62,6 +62,7 @@ struct sk_psock_progs { enum sk_psock_state_bits { SK_PSOCK_TX_ENABLED, + SK_PSOCK_RX_STRP_ENABLED, }; struct sk_psock_link { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index f523ceabd059..a388160f4be0 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -6,6 +6,8 @@ #include <linux/bug.h> #include <linux/lockdep_types.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/mod_devicetable.h> #include <linux/bitfield.h> @@ -370,6 +372,7 @@ struct sdw_dpn_prop { * @clock_reg_supported: the Peripheral implements the clock base and scale * registers introduced with the SoundWire 1.2 specification. SDCA devices * do not need to set this boolean property as the registers are required. + * @use_domain_irq: call actual IRQ handler on slave, as well as callback */ struct sdw_slave_prop { u32 mipi_revision; @@ -394,6 +397,7 @@ struct sdw_slave_prop { u8 scp_int1_mask; u32 quirks; bool clock_reg_supported; + bool use_domain_irq; }; #define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0) @@ -482,6 +486,11 @@ struct sdw_slave_id { __u8 sdw_version:4; }; +struct sdw_extended_slave_id { + int link_id; + struct sdw_slave_id id; +}; + /* * Helper macros to extract the MIPI-defined IDs * @@ -641,6 +650,7 @@ struct sdw_slave_ops { * struct sdw_slave - SoundWire Slave * @id: MIPI device ID * @dev: Linux device + * @irq: IRQ number * @status: Status reported by the Slave * @bus: Bus handle * @prop: Slave properties @@ -670,6 +680,7 @@ struct sdw_slave_ops { struct sdw_slave { struct sdw_slave_id id; struct device dev; + int irq; enum sdw_slave_status status; struct sdw_bus *bus; struct sdw_slave_prop prop; @@ -885,6 +896,7 @@ struct sdw_master_ops { * is used to compute and program bus bandwidth, clock, frame shape, * transport and port parameters * @debugfs: Bus debugfs + * @domain: IRQ domain * @defer_msg: Defer message * @clk_stop_timeout: Clock stop timeout computed * @bank_switch_timeout: Bank switch timeout computed @@ -920,6 +932,8 @@ struct sdw_bus { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif + struct irq_chip irq_chip; + struct irq_domain *domain; struct sdw_defer defer_msg; unsigned int clk_stop_timeout; u32 bank_switch_timeout; diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 11fc88fb0d78..fa67fad4ef51 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -264,11 +264,6 @@ struct sdw_intel_link_dev; */ #define SDW_INTEL_CLK_STOP_BUS_RESET BIT(3) -struct sdw_intel_slave_id { - int link_id; - struct sdw_slave_id id; -}; - struct hdac_bus; /** @@ -298,7 +293,7 @@ struct sdw_intel_ctx { int num_slaves; acpi_handle handle; struct sdw_intel_link_dev **ldev; - struct sdw_intel_slave_id *ids; + struct sdw_extended_slave_id *ids; struct list_head link_list; struct mutex shim_lock; /* lock for access to shared SHIM registers */ u32 shim_mask; diff --git a/include/linux/spi/corgi_lcd.h b/include/linux/spi/corgi_lcd.h index 0b857616919c..fc6c1515dc54 100644 --- a/include/linux/spi/corgi_lcd.h +++ b/include/linux/spi/corgi_lcd.h @@ -15,4 +15,6 @@ struct corgi_lcd_platform_data { void (*kick_battery)(void); }; +void corgi_lcd_limit_intensity(int limit); + #endif /* __LINUX_SPI_CORGI_LCD_H */ diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 8e984d75f5b6..6b0a7dc48a4b 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -101,6 +101,7 @@ struct spi_mem_op { u8 nbytes; u8 buswidth; u8 dtr : 1; + u8 __pad : 7; u16 opcode; } cmd; @@ -108,6 +109,7 @@ struct spi_mem_op { u8 nbytes; u8 buswidth; u8 dtr : 1; + u8 __pad : 7; u64 val; } addr; @@ -115,12 +117,14 @@ struct spi_mem_op { u8 nbytes; u8 buswidth; u8 dtr : 1; + u8 __pad : 7; } dummy; struct { u8 buswidth; u8 dtr : 1; u8 ecc : 1; + u8 __pad : 6; enum spi_mem_data_dir dir; unsigned int nbytes; union { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 6a1e8f157255..4ee9d13749ad 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -283,6 +283,7 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED = BIT(6), TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7), TPM_CHIP_FLAG_SUSPENDED = BIT(8), + TPM_CHIP_FLAG_HWRNG_DISABLED = BIT(9), }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7c7d03aa9d06..d6fa7c8767ad 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -562,6 +562,9 @@ ieee80211_get_sband_iftype_data(const struct ieee80211_supported_band *sband, if (WARN_ON(iftype >= NL80211_IFTYPE_MAX)) return NULL; + if (iftype == NL80211_IFTYPE_AP_VLAN) + iftype = NL80211_IFTYPE_AP; + for (i = 0; i < sband->n_iftype_data; i++) { const struct ieee80211_sband_iftype_data *data = &sband->iftype_data[i]; diff --git a/include/net/gro.h b/include/net/gro.h index 75efa6fb8441..88644b3ca660 100644 --- a/include/net/gro.h +++ b/include/net/gro.h @@ -452,6 +452,49 @@ static inline void gro_normal_one(struct napi_struct *napi, struct sk_buff *skb, gro_normal_list(napi); } +/* This function is the alternative of 'inet_iif' and 'inet_sdif' + * functions in case we can not rely on fields of IPCB. + * + * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized. + * The caller must hold the RCU read lock. + */ +static inline void inet_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif) +{ + *iif = inet_iif(skb) ?: skb->dev->ifindex; + *sdif = 0; + +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (netif_is_l3_slave(skb->dev)) { + struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev); + + *sdif = *iif; + *iif = master ? master->ifindex : 0; + } +#endif +} + +/* This function is the alternative of 'inet6_iif' and 'inet6_sdif' + * functions in case we can not rely on fields of IP6CB. + * + * The caller must verify skb_valid_dst(skb) is false and skb->dev is initialized. + * The caller must hold the RCU read lock. + */ +static inline void inet6_get_iif_sdif(const struct sk_buff *skb, int *iif, int *sdif) +{ + /* using skb->dev->ifindex because skb_dst(skb) is not initialized */ + *iif = skb->dev->ifindex; + *sdif = 0; + +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (netif_is_l3_slave(skb->dev)) { + struct net_device *master = netdev_master_upper_dev_get_rcu(skb->dev); + + *sdif = *iif; + *iif = master ? master->ifindex : 0; + } +#endif +} + extern struct list_head offload_base; #endif /* _NET_IPV6_GRO_H */ diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index caa20a905531..0bb32bfc6183 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -107,11 +107,12 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) static inline u32 inet_request_mark(const struct sock *sk, struct sk_buff *skb) { - if (!sk->sk_mark && - READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept)) + u32 mark = READ_ONCE(sk->sk_mark); + + if (!mark && READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept)) return skb->mark; - return sk->sk_mark; + return mark; } static inline int inet_request_bound_dev_if(const struct sock *sk, diff --git a/include/net/ip.h b/include/net/ip.h index 50d435855ae2..332521170d9b 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -93,7 +93,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm, { ipcm_init(ipcm); - ipcm->sockc.mark = inet->sk.sk_mark; + ipcm->sockc.mark = READ_ONCE(inet->sk.sk_mark); ipcm->sockc.tsflags = inet->sk.sk_tsflags; ipcm->oif = READ_ONCE(inet->sk.sk_bound_dev_if); ipcm->addr = inet->inet_saddr; diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 640441a2f926..35870858ddf2 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -512,6 +512,7 @@ struct nft_set_elem_expr { * * @list: table set list node * @bindings: list of set bindings + * @refs: internal refcounting for async set destruction * @table: table this set belongs to * @net: netnamespace this set belongs to * @name: name of the set @@ -541,6 +542,7 @@ struct nft_set_elem_expr { struct nft_set { struct list_head list; struct list_head bindings; + refcount_t refs; struct nft_table *table; possible_net_t net; char *name; @@ -562,7 +564,8 @@ struct nft_set { struct list_head pending_update; /* runtime data below here */ const struct nft_set_ops *ops ____cacheline_aligned; - u16 flags:14, + u16 flags:13, + dead:1, genmask:2; u8 klen; u8 dlen; @@ -596,7 +599,6 @@ struct nft_set *nft_set_lookup_global(const struct net *net, struct nft_set_ext *nft_set_catchall_lookup(const struct net *net, const struct nft_set *set); -void *nft_set_catchall_gc(const struct nft_set *set); static inline unsigned long nft_set_gc_interval(const struct nft_set *set) { @@ -813,62 +815,6 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, const struct nft_set *set, void *elem); -/** - * struct nft_set_gc_batch_head - nf_tables set garbage collection batch - * - * @rcu: rcu head - * @set: set the elements belong to - * @cnt: count of elements - */ -struct nft_set_gc_batch_head { - struct rcu_head rcu; - const struct nft_set *set; - unsigned int cnt; -}; - -#define NFT_SET_GC_BATCH_SIZE ((PAGE_SIZE - \ - sizeof(struct nft_set_gc_batch_head)) / \ - sizeof(void *)) - -/** - * struct nft_set_gc_batch - nf_tables set garbage collection batch - * - * @head: GC batch head - * @elems: garbage collection elements - */ -struct nft_set_gc_batch { - struct nft_set_gc_batch_head head; - void *elems[NFT_SET_GC_BATCH_SIZE]; -}; - -struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set, - gfp_t gfp); -void nft_set_gc_batch_release(struct rcu_head *rcu); - -static inline void nft_set_gc_batch_complete(struct nft_set_gc_batch *gcb) -{ - if (gcb != NULL) - call_rcu(&gcb->head.rcu, nft_set_gc_batch_release); -} - -static inline struct nft_set_gc_batch * -nft_set_gc_batch_check(const struct nft_set *set, struct nft_set_gc_batch *gcb, - gfp_t gfp) -{ - if (gcb != NULL) { - if (gcb->head.cnt + 1 < ARRAY_SIZE(gcb->elems)) - return gcb; - nft_set_gc_batch_complete(gcb); - } - return nft_set_gc_batch_alloc(set, gfp); -} - -static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb, - void *elem) -{ - gcb->elems[gcb->head.cnt++] = elem; -} - struct nft_expr_ops; /** * struct nft_expr_type - nf_tables expression type @@ -1557,39 +1503,30 @@ static inline void nft_set_elem_change_active(const struct net *net, #endif /* IS_ENABLED(CONFIG_NF_TABLES) */ -/* - * We use a free bit in the genmask field to indicate the element - * is busy, meaning it is currently being processed either by - * the netlink API or GC. - * - * Even though the genmask is only a single byte wide, this works - * because the extension structure if fully constant once initialized, - * so there are no non-atomic write accesses unless it is already - * marked busy. - */ -#define NFT_SET_ELEM_BUSY_MASK (1 << 2) +#define NFT_SET_ELEM_DEAD_MASK (1 << 2) #if defined(__LITTLE_ENDIAN_BITFIELD) -#define NFT_SET_ELEM_BUSY_BIT 2 +#define NFT_SET_ELEM_DEAD_BIT 2 #elif defined(__BIG_ENDIAN_BITFIELD) -#define NFT_SET_ELEM_BUSY_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2) +#define NFT_SET_ELEM_DEAD_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2) #else #error #endif -static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext) +static inline void nft_set_elem_dead(struct nft_set_ext *ext) { unsigned long *word = (unsigned long *)ext; BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0); - return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word); + set_bit(NFT_SET_ELEM_DEAD_BIT, word); } -static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext) +static inline int nft_set_elem_is_dead(const struct nft_set_ext *ext) { unsigned long *word = (unsigned long *)ext; - clear_bit(NFT_SET_ELEM_BUSY_BIT, word); + BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0); + return test_bit(NFT_SET_ELEM_DEAD_BIT, word); } /** @@ -1732,6 +1669,38 @@ struct nft_trans_flowtable { #define nft_trans_flowtable_flags(trans) \ (((struct nft_trans_flowtable *)trans->data)->flags) +#define NFT_TRANS_GC_BATCHCOUNT 256 + +struct nft_trans_gc { + struct list_head list; + struct net *net; + struct nft_set *set; + u32 seq; + u8 count; + void *priv[NFT_TRANS_GC_BATCHCOUNT]; + struct rcu_head rcu; +}; + +struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set, + unsigned int gc_seq, gfp_t gfp); +void nft_trans_gc_destroy(struct nft_trans_gc *trans); + +struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc, + unsigned int gc_seq, gfp_t gfp); +void nft_trans_gc_queue_async_done(struct nft_trans_gc *gc); + +struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp); +void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans); + +void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv); + +struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, + unsigned int gc_seq); + +void nft_setelem_data_deactivate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem); + int __init nft_chain_filter_init(void); void nft_chain_filter_fini(void); @@ -1758,6 +1727,7 @@ struct nftables_pernet { struct mutex commit_mutex; u64 table_handle; unsigned int base_seq; + unsigned int gc_seq; }; extern unsigned int nf_tables_net_id; diff --git a/include/net/route.h b/include/net/route.h index 5a5c726472bd..8c2a8e7d8f8e 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -168,7 +168,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi __be16 dport, __be16 sport, __u8 proto, __u8 tos, int oif) { - flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, + flowi4_init_output(fl4, oif, sk ? READ_ONCE(sk->sk_mark) : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, daddr, saddr, dport, sport, sock_net_uid(net, sk)); @@ -301,7 +301,7 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, if (inet_sk(sk)->transparent) flow_flags |= FLOWI_FLAG_ANYSRC; - flowi4_init_output(fl4, oif, sk->sk_mark, ip_sock_rt_tos(sk), + flowi4_init_output(fl4, oif, READ_ONCE(sk->sk_mark), ip_sock_rt_tos(sk), ip_sock_rt_scope(sk), protocol, flow_flags, dst, src, dport, sport, sk->sk_uid); } diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 1648240c9668..6a9f8a5f387c 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -556,12 +556,12 @@ static inline void vxlan_flag_attr_error(int attrtype, } static inline bool vxlan_fdb_nh_path_select(struct nexthop *nh, - int hash, + u32 hash, struct vxlan_rdst *rdst) { struct fib_nh_common *nhc; - nhc = nexthop_path_fdb_result(nh, hash); + nhc = nexthop_path_fdb_result(nh, hash >> 1); if (unlikely(!nhc)) return false; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 75b2235b99e2..b9230b6add04 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -194,6 +194,7 @@ struct scsi_device { unsigned no_start_on_add:1; /* do not issue start on add */ unsigned allow_restart:1; /* issue START_UNIT in error handler */ unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */ + unsigned no_start_on_resume:1; /* Do not issue START_STOP_UNIT on resume */ unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */ unsigned no_uld_attach:1; /* disable connecting to upper level drivers */ unsigned select_no_atn:1; diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index fc3001483e62..a5ef84944a06 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -175,6 +175,9 @@ struct tegra_mc_icc_ops { int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak); }; +struct icc_node *tegra_mc_icc_xlate(struct of_phandle_args *spec, void *data); +extern const struct tegra_mc_icc_ops tegra_mc_icc_ops; + struct tegra_mc_ops { /* * @probe: Callback to set up SoC-specific bits of the memory controller. This is called diff --git a/include/sound/cs42l43.h b/include/sound/cs42l43.h new file mode 100644 index 000000000000..deb337fc4e8c --- /dev/null +++ b/include/sound/cs42l43.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 CODEC driver external data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS42L43_ASOC_EXT_H +#define CS42L43_ASOC_EXT_H + +#define CS42L43_SYSCLK 0 + +#define CS42L43_SYSCLK_MCLK 0 +#define CS42L43_SYSCLK_SDW 1 + +#endif /* CS42L43_ASOC_EXT_H */ diff --git a/include/sound/designware_i2s.h b/include/sound/designware_i2s.h index 80d275b9ae0d..f6803205a9fb 100644 --- a/include/sound/designware_i2s.h +++ b/include/sound/designware_i2s.h @@ -21,6 +21,8 @@ struct i2s_clk_config_data { u32 sample_rate; }; +struct dw_i2s_dev; + struct i2s_platform_data { #define DWC_I2S_PLAY (1 << 0) #define DWC_I2S_RECORD (1 << 1) @@ -42,6 +44,7 @@ struct i2s_platform_data { void *capture_dma_data; bool (*filter)(struct dma_chan *chan, void *slave); int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); + int (*i2s_pd_init)(struct dw_i2s_dev *dev); }; struct i2s_dma_data { diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index 4f44f0bd5388..228114aca415 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -42,6 +42,7 @@ int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, i int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink); int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink); +int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid); int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, @@ -146,6 +147,9 @@ static inline int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) { return 0; } static inline int +hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid) { return 0; } + +static inline int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { return 0; } static inline int diff --git a/include/sound/l3.h b/include/sound/l3.h deleted file mode 100644 index b6f58072237a..000000000000 --- a/include/sound/l3.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _L3_H_ -#define _L3_H_ 1 - -struct l3_pins { - void (*setdat)(struct l3_pins *, int); - void (*setclk)(struct l3_pins *, int); - void (*setmode)(struct l3_pins *, int); - - int gpio_data; - int gpio_clk; - int gpio_mode; - int use_gpios; - - int data_hold; - int data_setup; - int clock_high; - int mode_hold; - int mode; - int mode_setup; -}; - -struct device; - -int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len); -int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap); - -#endif diff --git a/include/sound/rt5665.h b/include/sound/rt5665.h index 3b3d6a19ca49..e865f041929b 100644 --- a/include/sound/rt5665.h +++ b/include/sound/rt5665.h @@ -31,8 +31,6 @@ struct rt5665_platform_data { bool in3_diff; bool in4_diff; - int ldo1_en; /* GPIO for LDO1_EN */ - enum rt5665_dmic1_data_pin dmic1_data_pin; enum rt5665_dmic2_data_pin dmic2_data_pin; enum rt5665_jd_src jd_src; diff --git a/include/sound/rt5668.h b/include/sound/rt5668.h index 182edfbc9e7a..b682418c6cd6 100644 --- a/include/sound/rt5668.h +++ b/include/sound/rt5668.h @@ -25,9 +25,6 @@ enum rt5668_jd_src { }; struct rt5668_platform_data { - - int ldo1_en; /* GPIO for LDO1_EN */ - enum rt5668_dmic1_data_pin dmic1_data_pin; enum rt5668_dmic1_clk_pin dmic1_clk_pin; enum rt5668_jd_src jd_src; diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h index 3900a07e3935..4256df721e3a 100644 --- a/include/sound/rt5682.h +++ b/include/sound/rt5682.h @@ -31,9 +31,6 @@ enum rt5682_dai_clks { }; struct rt5682_platform_data { - - int ldo1_en; /* GPIO for LDO1_EN */ - enum rt5682_dmic1_data_pin dmic1_data_pin; enum rt5682_dmic1_clk_pin dmic1_clk_pin; enum rt5682_jd_src jd_src; diff --git a/include/sound/rt5682s.h b/include/sound/rt5682s.h index f18d91308b9a..66ca0c75b914 100644 --- a/include/sound/rt5682s.h +++ b/include/sound/rt5682s.h @@ -32,9 +32,6 @@ enum rt5682s_dai_clks { }; struct rt5682s_platform_data { - - int ldo1_en; /* GPIO for LDO1_EN */ - enum rt5682s_dmic1_data_pin dmic1_data_pin; enum rt5682s_dmic1_clk_pin dmic1_clk_pin; enum rt5682s_jd_src jd_src; diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 528279056b3a..6d31d535e8f6 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -9,6 +9,7 @@ #include <linux/stddef.h> #include <linux/acpi.h> #include <linux/mod_devicetable.h> +#include <linux/soundwire/sdw.h> struct snd_soc_acpi_package_context { char *name; /* package name */ @@ -150,6 +151,7 @@ struct snd_soc_acpi_link_adr { * all firmware/topology related fields. * * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @uid: ACPI Unique ID, can be used to disambiguate matches. * @comp_ids: list of compatible audio codecs using the same machine driver, * firmware and topology * @link_mask: describes required board layout, e.g. for SoundWire. @@ -208,4 +210,9 @@ static inline bool snd_soc_acpi_sof_parent(struct device *dev) !strncmp(dev->parent->driver->name, "sof-audio-acpi", strlen("sof-audio-acpi")); } +bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, + const struct snd_soc_acpi_link_adr *link, + struct sdw_extended_slave_id *ids, + int num_slaves); + #endif diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index a33d803fe548..5fcfba47d98c 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -274,6 +274,15 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, const char *snd_soc_dai_name_get(struct snd_soc_dai *dai); struct snd_soc_dai_ops { + /* DAI driver callbacks */ + int (*probe)(struct snd_soc_dai *dai); + int (*remove)(struct snd_soc_dai *dai); + /* compress dai */ + int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); + /* Optional Callback used at pcm creation*/ + int (*pcm_new)(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai); + /* * DAI clocking configuration, all optional. * Called by soc_card drivers, normally in their hw_params. @@ -355,6 +364,10 @@ struct snd_soc_dai_ops { u64 *auto_selectable_formats; int num_auto_selectable_formats; + /* probe ordering - for components with runtime dependencies */ + int probe_order; + int remove_order; + /* bit field */ unsigned int no_capture_mute:1; }; @@ -401,15 +414,6 @@ struct snd_soc_dai_driver { struct snd_soc_dobj dobj; struct of_phandle_args *dai_args; - /* DAI driver callbacks */ - int (*probe)(struct snd_soc_dai *dai); - int (*remove)(struct snd_soc_dai *dai); - /* compress dai */ - int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num); - /* Optional Callback used at pcm creation*/ - int (*pcm_new)(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai); - /* ops */ const struct snd_soc_dai_ops *ops; const struct snd_soc_cdai_ops *cops; @@ -420,10 +424,6 @@ struct snd_soc_dai_driver { unsigned int symmetric_rate:1; unsigned int symmetric_channels:1; unsigned int symmetric_sample_bits:1; - - /* probe ordering - for components with runtime dependencies */ - int probe_order; - int remove_order; }; /* for Playback/Capture */ diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2e38dff16779..d2faec9a323e 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -650,6 +650,7 @@ struct snd_soc_dapm_widget { unsigned char power_checked:1; /* power checked this run */ unsigned char is_supply:1; /* Widget is a supply type widget */ unsigned char is_ep:2; /* Widget is a endpoint type widget */ + unsigned char no_wname_in_kcontrol_name:1; /* No widget name prefix in kcontrol name */ int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index 88560281d420..906e2f327ad2 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -26,9 +26,9 @@ enum sof_comp_type { SOF_COMP_MIXER, SOF_COMP_MUX, SOF_COMP_SRC, - SOF_COMP_SPLITTER, + SOF_COMP_DEPRECATED0, /* Formerly SOF_COMP_SPLITTER */ SOF_COMP_TONE, - SOF_COMP_SWITCH, + SOF_COMP_DEPRECATED1, /* Formerly SOF_COMP_SWITCH */ SOF_COMP_BUFFER, SOF_COMP_EQ_IIR, SOF_COMP_EQ_FIR, diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h deleted file mode 100644 index db82516da162..000000000000 --- a/include/sound/uda134x.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * uda134x.h -- UDA134x ALSA SoC Codec driver - * - * Copyright 2007 Dension Audio Systems Ltd. - * Author: Zoltan Devai - */ - -#ifndef _UDA134X_H -#define _UDA134X_H - -#include <sound/l3.h> - -struct uda134x_platform_data { - struct l3_pins l3; - void (*power) (int); - int model; -#define UDA134X_UDA1340 1 -#define UDA134X_UDA1341 2 -#define UDA134X_UDA1344 3 -#define UDA134X_UDA1345 4 -}; - -#endif /* _UDA134X_H */ diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index bf06db8d2046..7b1ddffa3dfc 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -381,6 +381,7 @@ TRACE_EVENT(tcp_cong_state_set, __field(const void *, skaddr) __field(__u16, sport) __field(__u16, dport) + __field(__u16, family) __array(__u8, saddr, 4) __array(__u8, daddr, 4) __array(__u8, saddr_v6, 16) @@ -396,6 +397,7 @@ TRACE_EVENT(tcp_cong_state_set, __entry->sport = ntohs(inet->inet_sport); __entry->dport = ntohs(inet->inet_dport); + __entry->family = sk->sk_family; p32 = (__be32 *) __entry->saddr; *p32 = inet->inet_saddr; @@ -409,7 +411,8 @@ TRACE_EVENT(tcp_cong_state_set, __entry->cong_state = ca_state; ), - TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c cong_state=%u", + TP_printk("family=%s sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c cong_state=%u", + show_family_name(__entry->family), __entry->sport, __entry->dport, __entry->saddr, __entry->daddr, __entry->saddr_v6, __entry->daddr_v6, diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 7865f5a9885b..4f3932bb712d 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -710,9 +710,11 @@ enum { TCA_FLOWER_KEY_CFM_OPT_UNSPEC, TCA_FLOWER_KEY_CFM_MD_LEVEL, TCA_FLOWER_KEY_CFM_OPCODE, - TCA_FLOWER_KEY_CFM_OPT_MAX, + __TCA_FLOWER_KEY_CFM_OPT_MAX, }; +#define TCA_FLOWER_KEY_CFM_OPT_MAX (__TCA_FLOWER_KEY_CFM_OPT_MAX - 1) + #define TCA_FLOWER_MASK_FLAGS_RANGE (1 << 0) /* Range-based match */ /* Match-all classifier */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index e9ec7e4eb982..453cab2a1209 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -99,7 +99,11 @@ #define SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME 414 #define SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS 415 #define SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS 416 - +/* + * The token value is copied to the dapm_widget's + * no_wname_in_kcontrol_name. + */ +#define SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME 417 /* SSP */ #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index f4591b912ea8..93db3e4e7b68 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -3470,6 +3470,8 @@ static unsigned long io_uring_mmu_get_unmapped_area(struct file *filp, * - use the kernel virtual address of the shared io_uring context * (instead of the userspace-provided address, which has to be 0UL * anyway). + * - use the same pgoff which the get_unmapped_area() uses to + * calculate the page colouring. * For architectures without such aliasing requirements, the * architecture will return any suitable mapping because addr is 0. */ @@ -3478,6 +3480,7 @@ static unsigned long io_uring_mmu_get_unmapped_area(struct file *filp, pgoff = 0; /* has been translated to ptr above */ #ifdef SHM_COLOUR addr = (uintptr_t) ptr; + pgoff = addr >> PAGE_SHIFT; #else addr = 0UL; #endif diff --git a/io_uring/openclose.c b/io_uring/openclose.c index 10ca57f5bd24..e3fae26e025d 100644 --- a/io_uring/openclose.c +++ b/io_uring/openclose.c @@ -35,9 +35,11 @@ static bool io_openat_force_async(struct io_open *open) { /* * Don't bother trying for O_TRUNC, O_CREAT, or O_TMPFILE open, - * it'll always -EAGAIN + * it'll always -EAGAIN. Note that we test for __O_TMPFILE because + * O_TMPFILE includes O_DIRECTORY, which isn't a flag we need to force + * async for. */ - return open->how.flags & (O_TRUNC | O_CREAT | O_TMPFILE); + return open->how.flags & (O_TRUNC | O_CREAT | __O_TMPFILE); } static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 6ae02be7a48e..286ab3db0fde 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -28,6 +28,7 @@ #include <linux/sched.h> #include <linux/workqueue.h> #include <linux/kthread.h> +#include <linux/completion.h> #include <trace/events/xdp.h> #include <linux/btf_ids.h> @@ -73,6 +74,7 @@ struct bpf_cpu_map_entry { struct rcu_head rcu; struct work_struct kthread_stop_wq; + struct completion kthread_running; }; struct bpf_cpu_map { @@ -129,11 +131,17 @@ static void __cpu_map_ring_cleanup(struct ptr_ring *ring) * invoked cpu_map_kthread_stop(). Catch any broken behaviour * gracefully and warn once. */ - struct xdp_frame *xdpf; + void *ptr; - while ((xdpf = ptr_ring_consume(ring))) - if (WARN_ON_ONCE(xdpf)) - xdp_return_frame(xdpf); + while ((ptr = ptr_ring_consume(ring))) { + WARN_ON_ONCE(1); + if (unlikely(__ptr_test_bit(0, &ptr))) { + __ptr_clear_bit(0, &ptr); + kfree_skb(ptr); + continue; + } + xdp_return_frame(ptr); + } } static void put_cpu_map_entry(struct bpf_cpu_map_entry *rcpu) @@ -153,7 +161,6 @@ static void put_cpu_map_entry(struct bpf_cpu_map_entry *rcpu) static void cpu_map_kthread_stop(struct work_struct *work) { struct bpf_cpu_map_entry *rcpu; - int err; rcpu = container_of(work, struct bpf_cpu_map_entry, kthread_stop_wq); @@ -163,14 +170,7 @@ static void cpu_map_kthread_stop(struct work_struct *work) rcu_barrier(); /* kthread_stop will wake_up_process and wait for it to complete */ - err = kthread_stop(rcpu->kthread); - if (err) { - /* kthread_stop may be called before cpu_map_kthread_run - * is executed, so we need to release the memory related - * to rcpu. - */ - put_cpu_map_entry(rcpu); - } + kthread_stop(rcpu->kthread); } static void cpu_map_bpf_prog_run_skb(struct bpf_cpu_map_entry *rcpu, @@ -298,11 +298,11 @@ static int cpu_map_bpf_prog_run(struct bpf_cpu_map_entry *rcpu, void **frames, return nframes; } - static int cpu_map_kthread_run(void *data) { struct bpf_cpu_map_entry *rcpu = data; + complete(&rcpu->kthread_running); set_current_state(TASK_INTERRUPTIBLE); /* When kthread gives stop order, then rcpu have been disconnected @@ -467,6 +467,7 @@ __cpu_map_entry_alloc(struct bpf_map *map, struct bpf_cpumap_val *value, goto free_ptr_ring; /* Setup kthread */ + init_completion(&rcpu->kthread_running); rcpu->kthread = kthread_create_on_node(cpu_map_kthread_run, rcpu, numa, "cpumap/%d/map:%d", cpu, map->id); @@ -480,6 +481,12 @@ __cpu_map_entry_alloc(struct bpf_map *map, struct bpf_cpumap_val *value, kthread_bind(rcpu->kthread, cpu); wake_up_process(rcpu->kthread); + /* Make sure kthread has been running, so kthread_stop() will not + * stop the kthread prematurely and all pending frames or skbs + * will be handled by the kthread before kthread_stop() returns. + */ + wait_for_completion(&rcpu->kthread_running); + return rcpu; free_prog: diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index e1b4bfa938dd..2b4a946a6ff5 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1166,7 +1166,7 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, int error; if (!hibernation_available()) - return 0; + return n; if (len && buf[len-1] == '\n') len--; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 5f2dcabad202..bd1a42b23f3f 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -661,8 +661,7 @@ static DEFINE_PER_CPU(int, bpf_trace_nest_level); BPF_CALL_5(bpf_perf_event_output, struct pt_regs *, regs, struct bpf_map *, map, u64, flags, void *, data, u64, size) { - struct bpf_trace_sample_data *sds = this_cpu_ptr(&bpf_trace_sds); - int nest_level = this_cpu_inc_return(bpf_trace_nest_level); + struct bpf_trace_sample_data *sds; struct perf_raw_record raw = { .frag = { .size = size, @@ -670,7 +669,11 @@ BPF_CALL_5(bpf_perf_event_output, struct pt_regs *, regs, struct bpf_map *, map, }, }; struct perf_sample_data *sd; - int err; + int nest_level, err; + + preempt_disable(); + sds = this_cpu_ptr(&bpf_trace_sds); + nest_level = this_cpu_inc_return(bpf_trace_nest_level); if (WARN_ON_ONCE(nest_level > ARRAY_SIZE(sds->sds))) { err = -EBUSY; @@ -688,9 +691,9 @@ BPF_CALL_5(bpf_perf_event_output, struct pt_regs *, regs, struct bpf_map *, map, perf_sample_save_raw_data(sd, &raw); err = __bpf_perf_event_output(regs, map, flags, sd); - out: this_cpu_dec(bpf_trace_nest_level); + preempt_enable(); return err; } @@ -715,7 +718,6 @@ static DEFINE_PER_CPU(struct bpf_trace_sample_data, bpf_misc_sds); u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy) { - int nest_level = this_cpu_inc_return(bpf_event_output_nest_level); struct perf_raw_frag frag = { .copy = ctx_copy, .size = ctx_size, @@ -732,8 +734,12 @@ u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, }; struct perf_sample_data *sd; struct pt_regs *regs; + int nest_level; u64 ret; + preempt_disable(); + nest_level = this_cpu_inc_return(bpf_event_output_nest_level); + if (WARN_ON_ONCE(nest_level > ARRAY_SIZE(bpf_misc_sds.sds))) { ret = -EBUSY; goto out; @@ -748,6 +754,7 @@ u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, ret = __bpf_perf_event_output(regs, map, flags, sd); out: this_cpu_dec(bpf_event_output_nest_level); + preempt_enable(); return ret; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 02a8f402eeb5..800b4208dba9 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -52,6 +52,7 @@ #include <linux/sched/debug.h> #include <linux/nmi.h> #include <linux/kvm_para.h> +#include <linux/delay.h> #include "workqueue_internal.h" @@ -338,8 +339,10 @@ static cpumask_var_t *wq_numa_possible_cpumask; * Per-cpu work items which run for longer than the following threshold are * automatically considered CPU intensive and excluded from concurrency * management to prevent them from noticeably delaying other per-cpu work items. + * ULONG_MAX indicates that the user hasn't overridden it with a boot parameter. + * The actual value is initialized in wq_cpu_intensive_thresh_init(). */ -static unsigned long wq_cpu_intensive_thresh_us = 10000; +static unsigned long wq_cpu_intensive_thresh_us = ULONG_MAX; module_param_named(cpu_intensive_thresh_us, wq_cpu_intensive_thresh_us, ulong, 0644); static bool wq_disable_numa; @@ -6513,6 +6516,42 @@ void __init workqueue_init_early(void) !system_freezable_power_efficient_wq); } +static void __init wq_cpu_intensive_thresh_init(void) +{ + unsigned long thresh; + unsigned long bogo; + + /* if the user set it to a specific value, keep it */ + if (wq_cpu_intensive_thresh_us != ULONG_MAX) + return; + + /* + * The default of 10ms is derived from the fact that most modern (as of + * 2023) processors can do a lot in 10ms and that it's just below what + * most consider human-perceivable. However, the kernel also runs on a + * lot slower CPUs including microcontrollers where the threshold is way + * too low. + * + * Let's scale up the threshold upto 1 second if BogoMips is below 4000. + * This is by no means accurate but it doesn't have to be. The mechanism + * is still useful even when the threshold is fully scaled up. Also, as + * the reports would usually be applicable to everyone, some machines + * operating on longer thresholds won't significantly diminish their + * usefulness. + */ + thresh = 10 * USEC_PER_MSEC; + + /* see init/calibrate.c for lpj -> BogoMIPS calculation */ + bogo = max_t(unsigned long, loops_per_jiffy / 500000 * HZ, 1); + if (bogo < 4000) + thresh = min_t(unsigned long, thresh * 4000 / bogo, USEC_PER_SEC); + + pr_debug("wq_cpu_intensive_thresh: lpj=%lu BogoMIPS=%lu thresh_us=%lu\n", + loops_per_jiffy, bogo, thresh); + + wq_cpu_intensive_thresh_us = thresh; +} + /** * workqueue_init - bring workqueue subsystem fully online * @@ -6528,6 +6567,8 @@ void __init workqueue_init(void) struct worker_pool *pool; int cpu, bkt; + wq_cpu_intensive_thresh_init(); + /* * It'd be simpler to initialize NUMA in workqueue_init_early() but * CPU to node mapping may not be available that early on some diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index fbc89baf7de6..d6798513a8c2 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1200,7 +1200,7 @@ config WQ_CPU_INTENSIVE_REPORT help Say Y here to enable reporting of concurrency-managed per-cpu work items that hog CPUs for longer than - workqueue.cpu_intensive_threshold_us. Workqueue automatically + workqueue.cpu_intensive_thresh_us. Workqueue automatically detects and excludes them from concurrency management to prevent them from stalling other per-cpu work items. Occassional triggering may not necessarily indicate a problem. Repeated diff --git a/lib/Makefile b/lib/Makefile index 42d307ade225..1ffae65bb7ee 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -82,7 +82,13 @@ obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_SCANF) += test_scanf.o + obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o +ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy) +# FIXME: Clang breaks test_bitmap_const_eval when KASAN and GCOV are enabled +GCOV_PROFILE_test_bitmap.o := n +endif + obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_XARRAY) += test_xarray.o obj-$(CONFIG_TEST_MAPLE_TREE) += test_maple_tree.o diff --git a/lib/cpumask.c b/lib/cpumask.c index de356f16773a..a7fd02b5ae26 100644 --- a/lib/cpumask.c +++ b/lib/cpumask.c @@ -45,6 +45,7 @@ EXPORT_SYMBOL(cpumask_next_wrap); * alloc_cpumask_var_node - allocate a struct cpumask on a given node * @mask: pointer to cpumask_var_t where the cpumask is returned * @flags: GFP_ flags + * @node: memory node from which to allocate or %NUMA_NO_NODE * * Only defined when CONFIG_CPUMASK_OFFSTACK=y, otherwise is * a nop returning a constant 1 (in <linux/cpumask.h>) @@ -157,7 +158,9 @@ EXPORT_SYMBOL(cpumask_local_spread); static DEFINE_PER_CPU(int, distribute_cpu_mask_prev); /** - * cpumask_any_and_distribute - Return an arbitrary cpu within srcp1 & srcp2. + * cpumask_any_and_distribute - Return an arbitrary cpu within src1p & src2p. + * @src1p: first &cpumask for intersection + * @src2p: second &cpumask for intersection * * Iterated calls using the same srcp1 and srcp2 will be distributed within * their intersection. diff --git a/lib/scatterlist.c b/lib/scatterlist.c index e86231a44c3d..c65566b4dc66 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -1148,7 +1148,7 @@ static ssize_t extract_user_to_sg(struct iov_iter *iter, failed: while (sgtable->nents > sgtable->orig_nents) - put_page(sg_page(&sgtable->sgl[--sgtable->nents])); + unpin_user_page(sg_page(&sgtable->sgl[--sgtable->nents])); return res; } diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 187f5b2db4cf..f2ea9f30c7c5 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -1161,6 +1161,10 @@ static void __init test_bitmap_print_buf(void) } } +/* + * FIXME: Clang breaks compile-time evaluations when KASAN and GCOV are enabled. + * To workaround it, GCOV is force-disabled in Makefile for this configuration. + */ static void __init test_bitmap_const_eval(void) { DECLARE_BITMAP(bitmap, BITS_PER_LONG); @@ -1186,11 +1190,7 @@ static void __init test_bitmap_const_eval(void) * the compiler is fixed. */ bitmap_clear(bitmap, 0, BITS_PER_LONG); -#if defined(__s390__) && defined(__clang__) - if (!const_test_bit(7, bitmap)) -#else if (!test_bit(7, bitmap)) -#endif bitmap_set(bitmap, 5, 2); /* Equals to `unsigned long bitopvar = BIT(20)` */ diff --git a/mm/compaction.c b/mm/compaction.c index dbc9f86b1934..eacca2794e47 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -912,11 +912,12 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn, /* * Check if the pageblock has already been marked skipped. - * Only the aligned PFN is checked as the caller isolates + * Only the first PFN is checked as the caller isolates * COMPACT_CLUSTER_MAX at a time so the second call must * not falsely conclude that the block should be skipped. */ - if (!valid_page && pageblock_aligned(low_pfn)) { + if (!valid_page && (pageblock_aligned(low_pfn) || + low_pfn == cc->zone->zone_start_pfn)) { if (!isolation_suitable(cc, page)) { low_pfn = end_pfn; folio = NULL; @@ -2002,7 +2003,8 @@ static isolate_migrate_t isolate_migratepages(struct compact_control *cc) * before making it "skip" so other compaction instances do * not scan the same block. */ - if (pageblock_aligned(low_pfn) && + if ((pageblock_aligned(low_pfn) || + low_pfn == cc->zone->zone_start_pfn) && !fast_find_block && !isolation_suitable(cc, page)) continue; diff --git a/mm/damon/core.c b/mm/damon/core.c index 91cff7f2997e..eb9580942a5c 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -273,6 +273,7 @@ struct damos_filter *damos_new_filter(enum damos_filter_type type, return NULL; filter->type = type; filter->matching = matching; + INIT_LIST_HEAD(&filter->list); return filter; } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 64a3239b6407..6da626bfb52e 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1579,9 +1579,37 @@ static inline void destroy_compound_gigantic_folio(struct folio *folio, unsigned int order) { } #endif +static inline void __clear_hugetlb_destructor(struct hstate *h, + struct folio *folio) +{ + lockdep_assert_held(&hugetlb_lock); + + /* + * Very subtle + * + * For non-gigantic pages set the destructor to the normal compound + * page dtor. This is needed in case someone takes an additional + * temporary ref to the page, and freeing is delayed until they drop + * their reference. + * + * For gigantic pages set the destructor to the null dtor. This + * destructor will never be called. Before freeing the gigantic + * page destroy_compound_gigantic_folio will turn the folio into a + * simple group of pages. After this the destructor does not + * apply. + * + */ + if (hstate_is_gigantic(h)) + folio_set_compound_dtor(folio, NULL_COMPOUND_DTOR); + else + folio_set_compound_dtor(folio, COMPOUND_PAGE_DTOR); +} + /* - * Remove hugetlb folio from lists, and update dtor so that the folio appears - * as just a compound page. + * Remove hugetlb folio from lists. + * If vmemmap exists for the folio, update dtor so that the folio appears + * as just a compound page. Otherwise, wait until after allocating vmemmap + * to update dtor. * * A reference is held on the folio, except in the case of demote. * @@ -1612,31 +1640,19 @@ static void __remove_hugetlb_folio(struct hstate *h, struct folio *folio, } /* - * Very subtle - * - * For non-gigantic pages set the destructor to the normal compound - * page dtor. This is needed in case someone takes an additional - * temporary ref to the page, and freeing is delayed until they drop - * their reference. - * - * For gigantic pages set the destructor to the null dtor. This - * destructor will never be called. Before freeing the gigantic - * page destroy_compound_gigantic_folio will turn the folio into a - * simple group of pages. After this the destructor does not - * apply. - * - * This handles the case where more than one ref is held when and - * after update_and_free_hugetlb_folio is called. - * - * In the case of demote we do not ref count the page as it will soon - * be turned into a page of smaller size. + * We can only clear the hugetlb destructor after allocating vmemmap + * pages. Otherwise, someone (memory error handling) may try to write + * to tail struct pages. + */ + if (!folio_test_hugetlb_vmemmap_optimized(folio)) + __clear_hugetlb_destructor(h, folio); + + /* + * In the case of demote we do not ref count the page as it will soon + * be turned into a page of smaller size. */ if (!demote) folio_ref_unfreeze(folio, 1); - if (hstate_is_gigantic(h)) - folio_set_compound_dtor(folio, NULL_COMPOUND_DTOR); - else - folio_set_compound_dtor(folio, COMPOUND_PAGE_DTOR); h->nr_huge_pages--; h->nr_huge_pages_node[nid]--; @@ -1705,6 +1721,7 @@ static void __update_and_free_hugetlb_folio(struct hstate *h, { int i; struct page *subpage; + bool clear_dtor = folio_test_hugetlb_vmemmap_optimized(folio); if (hstate_is_gigantic(h) && !gigantic_page_runtime_supported()) return; @@ -1735,6 +1752,16 @@ static void __update_and_free_hugetlb_folio(struct hstate *h, if (unlikely(folio_test_hwpoison(folio))) folio_clear_hugetlb_hwpoison(folio); + /* + * If vmemmap pages were allocated above, then we need to clear the + * hugetlb destructor under the hugetlb lock. + */ + if (clear_dtor) { + spin_lock_irq(&hugetlb_lock); + __clear_hugetlb_destructor(h, folio); + spin_unlock_irq(&hugetlb_lock); + } + for (i = 0; i < pages_per_huge_page(h); i++) { subpage = folio_page(folio, i); subpage->flags &= ~(1 << PG_locked | 1 << PG_error | @@ -2784,6 +2784,8 @@ struct page *ksm_might_need_to_copy(struct page *page, anon_vma->root == vma->anon_vma->root) { return page; /* still no need to copy it */ } + if (PageHWPoison(page)) + return ERR_PTR(-EHWPOISON); if (!PageUptodate(page)) return page; /* let do_swap_page report the error */ diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ece5d481b5ff..9a285038d765 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -2466,7 +2466,7 @@ int unpoison_memory(unsigned long pfn) { struct folio *folio; struct page *p; - int ret = -EBUSY; + int ret = -EBUSY, ghp; unsigned long count = 1; bool huge = false; static DEFINE_RATELIMIT_STATE(unpoison_rs, DEFAULT_RATELIMIT_INTERVAL, @@ -2499,6 +2499,13 @@ int unpoison_memory(unsigned long pfn) goto unlock_mutex; } + if (folio_test_slab(folio) || PageTable(&folio->page) || folio_test_reserved(folio)) + goto unlock_mutex; + + /* + * Note that folio->_mapcount is overloaded in SLAB, so the simple test + * in folio_mapped() has to be done after folio_test_slab() is checked. + */ if (folio_mapped(folio)) { unpoison_pr_info("Unpoison: Someone maps the hwpoison page %#lx\n", pfn, &unpoison_rs); @@ -2511,32 +2518,28 @@ int unpoison_memory(unsigned long pfn) goto unlock_mutex; } - if (folio_test_slab(folio) || PageTable(&folio->page) || folio_test_reserved(folio)) - goto unlock_mutex; - - ret = get_hwpoison_page(p, MF_UNPOISON); - if (!ret) { + ghp = get_hwpoison_page(p, MF_UNPOISON); + if (!ghp) { if (PageHuge(p)) { huge = true; count = folio_free_raw_hwp(folio, false); - if (count == 0) { - ret = -EBUSY; + if (count == 0) goto unlock_mutex; - } } ret = folio_test_clear_hwpoison(folio) ? 0 : -EBUSY; - } else if (ret < 0) { - if (ret == -EHWPOISON) { + } else if (ghp < 0) { + if (ghp == -EHWPOISON) { ret = put_page_back_buddy(p) ? 0 : -EBUSY; - } else + } else { + ret = ghp; unpoison_pr_info("Unpoison: failed to grab page %#lx\n", pfn, &unpoison_rs); + } } else { if (PageHuge(p)) { huge = true; count = folio_free_raw_hwp(folio, false); if (count == 0) { - ret = -EBUSY; folio_put(folio); goto unlock_mutex; } diff --git a/mm/memory.c b/mm/memory.c index 603b2f419948..1ec1ef3418bf 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -5705,6 +5705,9 @@ int __access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, if (mmap_read_lock_killable(mm)) return 0; + /* Untag the address before looking up the VMA */ + addr = untagged_addr_remote(mm, addr); + /* Avoid triggering the temporary warning in __get_user_pages */ if (!vma_lookup(mm, addr) && !expand_stack(mm, addr)) return 0; diff --git a/mm/swapfile.c b/mm/swapfile.c index 8e6dde68b389..b15112b1f1a8 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1746,7 +1746,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, struct page *swapcache; spinlock_t *ptl; pte_t *pte, new_pte, old_pte; - bool hwposioned = false; + bool hwpoisoned = PageHWPoison(page); int ret = 1; swapcache = page; @@ -1754,7 +1754,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, if (unlikely(!page)) return -ENOMEM; else if (unlikely(PTR_ERR(page) == -EHWPOISON)) - hwposioned = true; + hwpoisoned = true; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); if (unlikely(!pte || !pte_same_as_swp(ptep_get(pte), @@ -1765,11 +1765,11 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, old_pte = ptep_get(pte); - if (unlikely(hwposioned || !PageUptodate(page))) { + if (unlikely(hwpoisoned || !PageUptodate(page))) { swp_entry_t swp_entry; dec_mm_counter(vma->vm_mm, MM_SWAPENTS); - if (hwposioned) { + if (hwpoisoned) { swp_entry = make_hwpoison_entry(swapcache); page = swapcache; } else { diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 3f057970504e..32916d28d9d9 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -1798,6 +1798,7 @@ static void replace_sub_page(struct size_class *class, struct zspage *zspage, static bool zs_page_isolate(struct page *page, isolate_mode_t mode) { + struct zs_pool *pool; struct zspage *zspage; /* @@ -1807,9 +1808,10 @@ static bool zs_page_isolate(struct page *page, isolate_mode_t mode) VM_BUG_ON_PAGE(PageIsolated(page), page); zspage = get_zspage(page); - migrate_write_lock(zspage); + pool = zspage->pool; + spin_lock(&pool->lock); inc_zspage_isolation(zspage); - migrate_write_unlock(zspage); + spin_unlock(&pool->lock); return true; } @@ -1875,12 +1877,12 @@ static int zs_page_migrate(struct page *newpage, struct page *page, kunmap_atomic(s_addr); replace_sub_page(class, zspage, newpage, page); + dec_zspage_isolation(zspage); /* * Since we complete the data copy and set up new zspage structure, * it's okay to release the pool's lock. */ spin_unlock(&pool->lock); - dec_zspage_isolation(zspage); migrate_write_unlock(zspage); get_page(newpage); @@ -1897,14 +1899,16 @@ static int zs_page_migrate(struct page *newpage, struct page *page, static void zs_page_putback(struct page *page) { + struct zs_pool *pool; struct zspage *zspage; VM_BUG_ON_PAGE(!PageIsolated(page), page); zspage = get_zspage(page); - migrate_write_lock(zspage); + pool = zspage->pool; + spin_lock(&pool->lock); dec_zspage_isolation(zspage); - migrate_write_unlock(zspage); + spin_unlock(&pool->lock); } static const struct movable_operations zsmalloc_mops = { diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index e40aa3e3641c..b3662119ddbc 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -384,8 +384,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, dev->name); vlan_vid_add(dev, htons(ETH_P_8021Q), 0); } - if (event == NETDEV_DOWN && - (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) + if (event == NETDEV_DOWN) vlan_vid_del(dev, htons(ETH_P_8021Q), 0); vlan_info = rtnl_dereference(dev->vlan_info); diff --git a/net/can/raw.c b/net/can/raw.c index ba6b52b1d776..e10f59375659 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -865,7 +865,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) skb->dev = dev; skb->priority = sk->sk_priority; - skb->mark = sk->sk_mark; + skb->mark = READ_ONCE(sk->sk_mark); skb->tstamp = sockc.transmit_time; skb_setup_tx_timestamp(skb, sockc.tsflags); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 11c04e7d928e..658a6f2320cf 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -3334,17 +3334,24 @@ static int linger_reg_commit_wait(struct ceph_osd_linger_request *lreq) int ret; dout("%s lreq %p linger_id %llu\n", __func__, lreq, lreq->linger_id); - ret = wait_for_completion_interruptible(&lreq->reg_commit_wait); + ret = wait_for_completion_killable(&lreq->reg_commit_wait); return ret ?: lreq->reg_commit_error; } -static int linger_notify_finish_wait(struct ceph_osd_linger_request *lreq) +static int linger_notify_finish_wait(struct ceph_osd_linger_request *lreq, + unsigned long timeout) { - int ret; + long left; dout("%s lreq %p linger_id %llu\n", __func__, lreq, lreq->linger_id); - ret = wait_for_completion_interruptible(&lreq->notify_finish_wait); - return ret ?: lreq->notify_finish_error; + left = wait_for_completion_killable_timeout(&lreq->notify_finish_wait, + ceph_timeout_jiffies(timeout)); + if (left <= 0) + left = left ?: -ETIMEDOUT; + else + left = lreq->notify_finish_error; /* completed */ + + return left; } /* @@ -4896,7 +4903,8 @@ int ceph_osdc_notify(struct ceph_osd_client *osdc, linger_submit(lreq); ret = linger_reg_commit_wait(lreq); if (!ret) - ret = linger_notify_finish_wait(lreq); + ret = linger_notify_finish_wait(lreq, + msecs_to_jiffies(2 * timeout * MSEC_PER_SEC)); else dout("lreq %p failed to initiate notify %d\n", lreq, ret); diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index d4172534dfa8..cca7594be92e 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -496,8 +496,11 @@ bpf_sk_storage_diag_alloc(const struct nlattr *nla_stgs) return ERR_PTR(-EPERM); nla_for_each_nested(nla, nla_stgs, rem) { - if (nla_type(nla) == SK_DIAG_BPF_STORAGE_REQ_MAP_FD) + if (nla_type(nla) == SK_DIAG_BPF_STORAGE_REQ_MAP_FD) { + if (nla_len(nla) != sizeof(u32)) + return ERR_PTR(-EINVAL); nr_maps++; + } } diag = kzalloc(struct_size(diag, maps, nr_maps), GFP_KERNEL); diff --git a/net/core/filter.c b/net/core/filter.c index 06ba0e56e369..28a59596987a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4116,12 +4116,6 @@ BPF_CALL_2(bpf_xdp_adjust_tail, struct xdp_buff *, xdp, int, offset) if (unlikely(data_end > data_hard_end)) return -EINVAL; - /* ALL drivers MUST init xdp->frame_sz, chicken check below */ - if (unlikely(xdp->frame_sz > PAGE_SIZE)) { - WARN_ONCE(1, "Too BIG xdp->frame_sz = %d\n", xdp->frame_sz); - return -EINVAL; - } - if (unlikely(data_end < xdp->data + ETH_HLEN)) return -EINVAL; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3ad4e030846d..aef25aa5cf1d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -5140,13 +5140,17 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); if (br_spec) { nla_for_each_nested(attr, br_spec, rem) { - if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + if (nla_type(attr) == IFLA_BRIDGE_FLAGS && !have_flags) { if (nla_len(attr) < sizeof(flags)) return -EINVAL; have_flags = true; flags = nla_get_u16(attr); - break; + } + + if (nla_type(attr) == IFLA_BRIDGE_MODE) { + if (nla_len(attr) < sizeof(u16)) + return -EINVAL; } } } diff --git a/net/core/skmsg.c b/net/core/skmsg.c index a29508e1ff35..ef1a2eb6520b 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1120,13 +1120,19 @@ static void sk_psock_strp_data_ready(struct sock *sk) int sk_psock_init_strp(struct sock *sk, struct sk_psock *psock) { + int ret; + static const struct strp_callbacks cb = { .rcv_msg = sk_psock_strp_read, .read_sock_done = sk_psock_strp_read_done, .parse_msg = sk_psock_strp_parse, }; - return strp_init(&psock->strp, sk, &cb); + ret = strp_init(&psock->strp, sk, &cb); + if (!ret) + sk_psock_set_state(psock, SK_PSOCK_RX_STRP_ENABLED); + + return ret; } void sk_psock_start_strp(struct sock *sk, struct sk_psock *psock) @@ -1154,7 +1160,7 @@ void sk_psock_stop_strp(struct sock *sk, struct sk_psock *psock) static void sk_psock_done_strp(struct sk_psock *psock) { /* Parser has been stopped */ - if (psock->progs.stream_parser) + if (sk_psock_test_state(psock, SK_PSOCK_RX_STRP_ENABLED)) strp_done(&psock->strp); } #else diff --git a/net/core/sock.c b/net/core/sock.c index 9370fd50aa2c..732fc37a4771 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -429,6 +429,7 @@ static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen, { struct __kernel_sock_timeval tv; int err = sock_copy_user_timeval(&tv, optval, optlen, old_timeval); + long val; if (err) return err; @@ -439,7 +440,7 @@ static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen, if (tv.tv_sec < 0) { static int warned __read_mostly; - *timeo_p = 0; + WRITE_ONCE(*timeo_p, 0); if (warned < 10 && net_ratelimit()) { warned++; pr_info("%s: `%s' (pid %d) tries to set negative timeout\n", @@ -447,11 +448,12 @@ static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen, } return 0; } - *timeo_p = MAX_SCHEDULE_TIMEOUT; - if (tv.tv_sec == 0 && tv.tv_usec == 0) - return 0; - if (tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1)) - *timeo_p = tv.tv_sec * HZ + DIV_ROUND_UP((unsigned long)tv.tv_usec, USEC_PER_SEC / HZ); + val = MAX_SCHEDULE_TIMEOUT; + if ((tv.tv_sec || tv.tv_usec) && + (tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1))) + val = tv.tv_sec * HZ + DIV_ROUND_UP((unsigned long)tv.tv_usec, + USEC_PER_SEC / HZ); + WRITE_ONCE(*timeo_p, val); return 0; } @@ -804,7 +806,7 @@ EXPORT_SYMBOL(sock_no_linger); void sock_set_priority(struct sock *sk, u32 priority) { lock_sock(sk); - sk->sk_priority = priority; + WRITE_ONCE(sk->sk_priority, priority); release_sock(sk); } EXPORT_SYMBOL(sock_set_priority); @@ -813,9 +815,9 @@ void sock_set_sndtimeo(struct sock *sk, s64 secs) { lock_sock(sk); if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) - sk->sk_sndtimeo = secs * HZ; + WRITE_ONCE(sk->sk_sndtimeo, secs * HZ); else - sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; + WRITE_ONCE(sk->sk_sndtimeo, MAX_SCHEDULE_TIMEOUT); release_sock(sk); } EXPORT_SYMBOL(sock_set_sndtimeo); @@ -988,7 +990,7 @@ EXPORT_SYMBOL(sock_set_rcvbuf); static void __sock_set_mark(struct sock *sk, u32 val) { if (val != sk->sk_mark) { - sk->sk_mark = val; + WRITE_ONCE(sk->sk_mark, val); sk_dst_reset(sk); } } @@ -1007,7 +1009,7 @@ static void sock_release_reserved_memory(struct sock *sk, int bytes) bytes = round_down(bytes, PAGE_SIZE); WARN_ON(bytes > sk->sk_reserved_mem); - sk->sk_reserved_mem -= bytes; + WRITE_ONCE(sk->sk_reserved_mem, sk->sk_reserved_mem - bytes); sk_mem_reclaim(sk); } @@ -1044,7 +1046,8 @@ static int sock_reserve_memory(struct sock *sk, int bytes) } sk->sk_forward_alloc += pages << PAGE_SHIFT; - sk->sk_reserved_mem += pages << PAGE_SHIFT; + WRITE_ONCE(sk->sk_reserved_mem, + sk->sk_reserved_mem + (pages << PAGE_SHIFT)); return 0; } @@ -1213,7 +1216,7 @@ set_sndbuf: if ((val >= 0 && val <= 6) || sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) || sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) - sk->sk_priority = val; + WRITE_ONCE(sk->sk_priority, val); else ret = -EPERM; break; @@ -1438,7 +1441,8 @@ set_sndbuf: cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED); - sk->sk_max_pacing_rate = ulval; + /* Pairs with READ_ONCE() from sk_getsockopt() */ + WRITE_ONCE(sk->sk_max_pacing_rate, ulval); sk->sk_pacing_rate = min(sk->sk_pacing_rate, ulval); break; } @@ -1533,7 +1537,9 @@ set_sndbuf: } if ((u8)val == SOCK_TXREHASH_DEFAULT) val = READ_ONCE(sock_net(sk)->core.sysctl_txrehash); - /* Paired with READ_ONCE() in tcp_rtx_synack() */ + /* Paired with READ_ONCE() in tcp_rtx_synack() + * and sk_getsockopt(). + */ WRITE_ONCE(sk->sk_txrehash, (u8)val); break; @@ -1633,11 +1639,11 @@ int sk_getsockopt(struct sock *sk, int level, int optname, break; case SO_SNDBUF: - v.val = sk->sk_sndbuf; + v.val = READ_ONCE(sk->sk_sndbuf); break; case SO_RCVBUF: - v.val = sk->sk_rcvbuf; + v.val = READ_ONCE(sk->sk_rcvbuf); break; case SO_REUSEADDR: @@ -1679,7 +1685,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname, break; case SO_PRIORITY: - v.val = sk->sk_priority; + v.val = READ_ONCE(sk->sk_priority); break; case SO_LINGER: @@ -1717,16 +1723,18 @@ int sk_getsockopt(struct sock *sk, int level, int optname, case SO_RCVTIMEO_OLD: case SO_RCVTIMEO_NEW: - lv = sock_get_timeout(sk->sk_rcvtimeo, &v, SO_RCVTIMEO_OLD == optname); + lv = sock_get_timeout(READ_ONCE(sk->sk_rcvtimeo), &v, + SO_RCVTIMEO_OLD == optname); break; case SO_SNDTIMEO_OLD: case SO_SNDTIMEO_NEW: - lv = sock_get_timeout(sk->sk_sndtimeo, &v, SO_SNDTIMEO_OLD == optname); + lv = sock_get_timeout(READ_ONCE(sk->sk_sndtimeo), &v, + SO_SNDTIMEO_OLD == optname); break; case SO_RCVLOWAT: - v.val = sk->sk_rcvlowat; + v.val = READ_ONCE(sk->sk_rcvlowat); break; case SO_SNDLOWAT: @@ -1770,7 +1778,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname, spin_unlock(&sk->sk_peer_lock); if (!peer_pid) - return -ESRCH; + return -ENODATA; pidfd = pidfd_prepare(peer_pid, 0, &pidfd_file); put_pid(peer_pid); @@ -1843,7 +1851,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname, optval, optlen, len); case SO_MARK: - v.val = sk->sk_mark; + v.val = READ_ONCE(sk->sk_mark); break; case SO_RCVMARK: @@ -1862,7 +1870,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname, if (!sock->ops->set_peek_off) return -EOPNOTSUPP; - v.val = sk->sk_peek_off; + v.val = READ_ONCE(sk->sk_peek_off); break; case SO_NOFCS: v.val = sock_flag(sk, SOCK_NOFCS); @@ -1892,7 +1900,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname, #ifdef CONFIG_NET_RX_BUSY_POLL case SO_BUSY_POLL: - v.val = sk->sk_ll_usec; + v.val = READ_ONCE(sk->sk_ll_usec); break; case SO_PREFER_BUSY_POLL: v.val = READ_ONCE(sk->sk_prefer_busy_poll); @@ -1900,12 +1908,14 @@ int sk_getsockopt(struct sock *sk, int level, int optname, #endif case SO_MAX_PACING_RATE: + /* The READ_ONCE() pair with the WRITE_ONCE() in sk_setsockopt() */ if (sizeof(v.ulval) != sizeof(v.val) && len >= sizeof(v.ulval)) { lv = sizeof(v.ulval); - v.ulval = sk->sk_max_pacing_rate; + v.ulval = READ_ONCE(sk->sk_max_pacing_rate); } else { /* 32bit version */ - v.val = min_t(unsigned long, sk->sk_max_pacing_rate, ~0U); + v.val = min_t(unsigned long, ~0U, + READ_ONCE(sk->sk_max_pacing_rate)); } break; @@ -1973,11 +1983,12 @@ int sk_getsockopt(struct sock *sk, int level, int optname, break; case SO_RESERVE_MEM: - v.val = sk->sk_reserved_mem; + v.val = READ_ONCE(sk->sk_reserved_mem); break; case SO_TXREHASH: - v.val = sk->sk_txrehash; + /* Paired with WRITE_ONCE() in sk_setsockopt() */ + v.val = READ_ONCE(sk->sk_txrehash); break; default: @@ -3168,7 +3179,7 @@ EXPORT_SYMBOL(__sk_mem_reclaim); int sk_set_peek_off(struct sock *sk, int val) { - sk->sk_peek_off = val; + WRITE_ONCE(sk->sk_peek_off, val); return 0; } EXPORT_SYMBOL_GPL(sk_set_peek_off); diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 19538d628714..8f07fea39d9e 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -115,7 +115,6 @@ static void sock_map_sk_acquire(struct sock *sk) __acquires(&sk->sk_lock.slock) { lock_sock(sk); - preempt_disable(); rcu_read_lock(); } @@ -123,7 +122,6 @@ static void sock_map_sk_release(struct sock *sk) __releases(&sk->sk_lock.slock) { rcu_read_unlock(); - preempt_enable(); release_sock(sk); } @@ -148,13 +146,13 @@ static void sock_map_del_link(struct sock *sk, list_for_each_entry_safe(link, tmp, &psock->link, list) { if (link->link_raw == link_raw) { struct bpf_map *map = link->map; - struct bpf_stab *stab = container_of(map, struct bpf_stab, - map); - if (psock->saved_data_ready && stab->progs.stream_parser) + struct sk_psock_progs *progs = sock_map_progs(map); + + if (psock->saved_data_ready && progs->stream_parser) strp_stop = true; - if (psock->saved_data_ready && stab->progs.stream_verdict) + if (psock->saved_data_ready && progs->stream_verdict) verdict_stop = true; - if (psock->saved_data_ready && stab->progs.skb_verdict) + if (psock->saved_data_ready && progs->skb_verdict) verdict_stop = true; list_del(&link->list); sk_psock_free_link(link); diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index c0c438128575..2e6b8c8fd2de 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -980,7 +980,7 @@ static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested_deprecated(data, DCB_BCN_ATTR_MAX, - tb[DCB_ATTR_BCN], dcbnl_pfc_up_nest, + tb[DCB_ATTR_BCN], dcbnl_bcn_nest, NULL); if (ret) return ret; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 7249ef218178..d29d1163203d 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -238,8 +238,8 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req opt = ireq->ipv6_opt; if (!opt) opt = rcu_dereference(np->opt); - err = ip6_xmit(sk, skb, &fl6, sk->sk_mark, opt, np->tclass, - sk->sk_priority); + err = ip6_xmit(sk, skb, &fl6, READ_ONCE(sk->sk_mark), opt, + np->tclass, sk->sk_priority); rcu_read_unlock(); err = net_xmit_eval(err); } diff --git a/net/dccp/output.c b/net/dccp/output.c index b8a24734385e..fd2eb148d24d 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -187,7 +187,7 @@ unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu) /* And store cached results */ icsk->icsk_pmtu_cookie = pmtu; - dp->dccps_mss_cache = cur_mps; + WRITE_ONCE(dp->dccps_mss_cache, cur_mps); return cur_mps; } diff --git a/net/dccp/proto.c b/net/dccp/proto.c index f331e5977a84..4e3266e4d7c3 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -630,7 +630,7 @@ static int do_dccp_getsockopt(struct sock *sk, int level, int optname, return dccp_getsockopt_service(sk, len, (__be32 __user *)optval, optlen); case DCCP_SOCKOPT_GET_CUR_MPS: - val = dp->dccps_mss_cache; + val = READ_ONCE(dp->dccps_mss_cache); break; case DCCP_SOCKOPT_AVAILABLE_CCIDS: return ccid_getsockopt_builtin_ccids(sk, len, optval, optlen); @@ -739,7 +739,7 @@ int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) trace_dccp_probe(sk, len); - if (len > dp->dccps_mss_cache) + if (len > READ_ONCE(dp->dccps_mss_cache)) return -EMSGSIZE; lock_sock(sk); @@ -772,6 +772,12 @@ int dccp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) goto out_discard; } + /* We need to check dccps_mss_cache after socket is locked. */ + if (len > dp->dccps_mss_cache) { + rc = -EMSGSIZE; + goto out_discard; + } + skb_reserve(skb, sk->sk_prot->max_header); rc = memcpy_from_msg(skb_put(skb, len), msg, len); if (rc != 0) diff --git a/net/dsa/port.c b/net/dsa/port.c index 0ce8fd311c78..2f6195d7b741 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1727,8 +1727,15 @@ int dsa_port_phylink_create(struct dsa_port *dp) ds->ops->phylink_mac_an_restart) dp->pl_config.legacy_pre_march2020 = true; - if (ds->ops->phylink_get_caps) + if (ds->ops->phylink_get_caps) { ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config); + } else { + /* For legacy drivers */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + dp->pl_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_GMII, + dp->pl_config.supported_interfaces); + } pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode, &dsa_port_phylink_mac_ops); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index b812eb36f0e3..f7426926a104 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -150,7 +150,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, } #endif - if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark)) + if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, READ_ONCE(sk->sk_mark))) goto errout; if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || @@ -799,7 +799,7 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) entry.ifindex = sk->sk_bound_dev_if; entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0; if (sk_fullsock(sk)) - entry.mark = sk->sk_mark; + entry.mark = READ_ONCE(sk->sk_mark); else if (sk->sk_state == TCP_NEW_SYN_RECV) entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark; else if (sk->sk_state == TCP_TIME_WAIT) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 6e70839257f7..6ba1a0fafbaa 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -184,9 +184,9 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct sock *sk, ip_options_build(skb, &opt->opt, daddr, rt); } - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); if (!skb->mark) - skb->mark = sk->sk_mark; + skb->mark = READ_ONCE(sk->sk_mark); /* Send it out. */ return ip_local_out(net, skb->sk, skb); @@ -528,8 +528,8 @@ packet_routed: skb_shinfo(skb)->gso_segs ?: 1); /* TODO : should we use skb->sk here instead of sk ? */ - skb->priority = sk->sk_priority; - skb->mark = sk->sk_mark; + skb->priority = READ_ONCE(sk->sk_priority); + skb->mark = READ_ONCE(sk->sk_mark); res = ip_local_out(net, sk, skb); rcu_read_unlock(); @@ -1158,10 +1158,15 @@ alloc_new_skb: } copy = datalen - transhdrlen - fraggap - pagedlen; + /* [!] NOTE: copy will be negative if pagedlen>0 + * because then the equation reduces to -fraggap. + */ if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { err = -EFAULT; kfree_skb(skb); goto error; + } else if (flags & MSG_SPLICE_PAGES) { + copy = 0; } offset += copy; @@ -1209,6 +1214,10 @@ alloc_new_skb: } else if (flags & MSG_SPLICE_PAGES) { struct msghdr *msg = from; + err = -EIO; + if (WARN_ON_ONCE(copy > msg->msg_iter.count)) + goto error; + err = skb_splice_from_iter(skb, &msg->msg_iter, copy, sk->sk_allocation); if (err < 0) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 8e97d8d4cc9d..d41bce8927b2 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -592,7 +592,7 @@ void __ip_sock_set_tos(struct sock *sk, int val) } if (inet_sk(sk)->tos != val) { inet_sk(sk)->tos = val; - sk->sk_priority = rt_tos2priority(val); + WRITE_ONCE(sk->sk_priority, rt_tos2priority(val)); sk_dst_reset(sk); } } diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 92c02c886fe7..586b1b3e35b8 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -224,7 +224,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) .un.frag.__unused = 0, .un.frag.mtu = htons(mtu), }; - icmph->checksum = ip_compute_csum(icmph, len); + icmph->checksum = csum_fold(skb_checksum(skb, 0, len, 0)); skb_reset_transport_header(skb); niph = skb_push(skb, sizeof(*niph)); diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index f95142e56da0..be5498f5dd31 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -3221,13 +3221,9 @@ static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb) &rtm_dump_nexthop_cb, &filter); if (err < 0) { if (likely(skb->len)) - goto out; - goto out_err; + err = skb->len; } -out: - err = skb->len; -out_err: cb->seq = net->nexthop.seq; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); return err; @@ -3367,25 +3363,19 @@ static int rtm_dump_nexthop_bucket_nh(struct sk_buff *skb, dd->filter.res_bucket_nh_id != nhge->nh->id) continue; + dd->ctx->bucket_index = bucket_index; err = nh_fill_res_bucket(skb, nh, bucket, bucket_index, RTM_NEWNEXTHOPBUCKET, portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->extack); - if (err < 0) { - if (likely(skb->len)) - goto out; - goto out_err; - } + if (err) + return err; } dd->ctx->done_nh_idx = dd->ctx->nh.idx + 1; - bucket_index = 0; + dd->ctx->bucket_index = 0; -out: - err = skb->len; -out_err: - dd->ctx->bucket_index = bucket_index; - return err; + return 0; } static int rtm_dump_nexthop_bucket_cb(struct sk_buff *skb, @@ -3434,13 +3424,9 @@ static int rtm_dump_nexthop_bucket(struct sk_buff *skb, if (err < 0) { if (likely(skb->len)) - goto out; - goto out_err; + err = skb->len; } -out: - err = skb->len; -out_err: cb->seq = net->nexthop.seq; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); return err; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 7782ff5e6539..cb381f5aa464 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -348,7 +348,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, goto error; skb_reserve(skb, hlen); - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc->mark; skb->tstamp = sockc->transmit_time; skb_dst_set(skb, &rt->dst); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 98d7e6ba7493..92fede388d52 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -518,7 +518,7 @@ static void __build_flow_key(const struct net *net, struct flowi4 *fl4, const struct inet_sock *inet = inet_sk(sk); oif = sk->sk_bound_dev_if; - mark = sk->sk_mark; + mark = READ_ONCE(sk->sk_mark); tos = ip_sock_rt_tos(sk); scope = ip_sock_rt_scope(sk); prot = inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol; @@ -552,7 +552,7 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) inet_opt = rcu_dereference(inet->inet_opt); if (inet_opt && inet_opt->opt.srr) daddr = inet_opt->opt.faddr; - flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, READ_ONCE(sk->sk_mark), ip_sock_rt_tos(sk) & IPTOS_RT_MASK, ip_sock_rt_scope(sk), inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 069642014636..a59cc4b83861 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -931,9 +931,9 @@ static void tcp_v4_send_ack(const struct sock *sk, ctl_sk = this_cpu_read(ipv4_tcp_sk); sock_net_set(ctl_sk, net); ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? - inet_twsk(sk)->tw_mark : sk->sk_mark; + inet_twsk(sk)->tw_mark : READ_ONCE(sk->sk_mark); ctl_sk->sk_priority = (sk->sk_state == TCP_TIME_WAIT) ? - inet_twsk(sk)->tw_priority : sk->sk_priority; + inet_twsk(sk)->tw_priority : READ_ONCE(sk->sk_priority); transmit_time = tcp_transmit_time(sk); ip_send_unicast_reply(ctl_sk, skb, &TCP_SKB_CB(skb)->header.h4.opt, diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 82f4575f9cd9..99ac5efe244d 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -40,7 +40,7 @@ struct tcp_fastopen_metrics { struct tcp_metrics_block { struct tcp_metrics_block __rcu *tcpm_next; - possible_net_t tcpm_net; + struct net *tcpm_net; struct inetpeer_addr tcpm_saddr; struct inetpeer_addr tcpm_daddr; unsigned long tcpm_stamp; @@ -51,34 +51,38 @@ struct tcp_metrics_block { struct rcu_head rcu_head; }; -static inline struct net *tm_net(struct tcp_metrics_block *tm) +static inline struct net *tm_net(const struct tcp_metrics_block *tm) { - return read_pnet(&tm->tcpm_net); + /* Paired with the WRITE_ONCE() in tcpm_new() */ + return READ_ONCE(tm->tcpm_net); } static bool tcp_metric_locked(struct tcp_metrics_block *tm, enum tcp_metric_index idx) { - return tm->tcpm_lock & (1 << idx); + /* Paired with WRITE_ONCE() in tcpm_suck_dst() */ + return READ_ONCE(tm->tcpm_lock) & (1 << idx); } -static u32 tcp_metric_get(struct tcp_metrics_block *tm, +static u32 tcp_metric_get(const struct tcp_metrics_block *tm, enum tcp_metric_index idx) { - return tm->tcpm_vals[idx]; + /* Paired with WRITE_ONCE() in tcp_metric_set() */ + return READ_ONCE(tm->tcpm_vals[idx]); } static void tcp_metric_set(struct tcp_metrics_block *tm, enum tcp_metric_index idx, u32 val) { - tm->tcpm_vals[idx] = val; + /* Paired with READ_ONCE() in tcp_metric_get() */ + WRITE_ONCE(tm->tcpm_vals[idx], val); } static bool addr_same(const struct inetpeer_addr *a, const struct inetpeer_addr *b) { - return inetpeer_addr_cmp(a, b) == 0; + return (a->family == b->family) && !inetpeer_addr_cmp(a, b); } struct tcpm_hash_bucket { @@ -89,6 +93,7 @@ static struct tcpm_hash_bucket *tcp_metrics_hash __read_mostly; static unsigned int tcp_metrics_hash_log __read_mostly; static DEFINE_SPINLOCK(tcp_metrics_lock); +static DEFINE_SEQLOCK(fastopen_seqlock); static void tcpm_suck_dst(struct tcp_metrics_block *tm, const struct dst_entry *dst, @@ -97,7 +102,7 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, u32 msval; u32 val; - tm->tcpm_stamp = jiffies; + WRITE_ONCE(tm->tcpm_stamp, jiffies); val = 0; if (dst_metric_locked(dst, RTAX_RTT)) @@ -110,30 +115,42 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, val |= 1 << TCP_METRIC_CWND; if (dst_metric_locked(dst, RTAX_REORDERING)) val |= 1 << TCP_METRIC_REORDERING; - tm->tcpm_lock = val; + /* Paired with READ_ONCE() in tcp_metric_locked() */ + WRITE_ONCE(tm->tcpm_lock, val); msval = dst_metric_raw(dst, RTAX_RTT); - tm->tcpm_vals[TCP_METRIC_RTT] = msval * USEC_PER_MSEC; + tcp_metric_set(tm, TCP_METRIC_RTT, msval * USEC_PER_MSEC); msval = dst_metric_raw(dst, RTAX_RTTVAR); - tm->tcpm_vals[TCP_METRIC_RTTVAR] = msval * USEC_PER_MSEC; - tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH); - tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND); - tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING); + tcp_metric_set(tm, TCP_METRIC_RTTVAR, msval * USEC_PER_MSEC); + tcp_metric_set(tm, TCP_METRIC_SSTHRESH, + dst_metric_raw(dst, RTAX_SSTHRESH)); + tcp_metric_set(tm, TCP_METRIC_CWND, + dst_metric_raw(dst, RTAX_CWND)); + tcp_metric_set(tm, TCP_METRIC_REORDERING, + dst_metric_raw(dst, RTAX_REORDERING)); if (fastopen_clear) { + write_seqlock(&fastopen_seqlock); tm->tcpm_fastopen.mss = 0; tm->tcpm_fastopen.syn_loss = 0; tm->tcpm_fastopen.try_exp = 0; tm->tcpm_fastopen.cookie.exp = false; tm->tcpm_fastopen.cookie.len = 0; + write_sequnlock(&fastopen_seqlock); } } #define TCP_METRICS_TIMEOUT (60 * 60 * HZ) -static void tcpm_check_stamp(struct tcp_metrics_block *tm, struct dst_entry *dst) +static void tcpm_check_stamp(struct tcp_metrics_block *tm, + const struct dst_entry *dst) { - if (tm && unlikely(time_after(jiffies, tm->tcpm_stamp + TCP_METRICS_TIMEOUT))) + unsigned long limit; + + if (!tm) + return; + limit = READ_ONCE(tm->tcpm_stamp) + TCP_METRICS_TIMEOUT; + if (unlikely(time_after(jiffies, limit))) tcpm_suck_dst(tm, dst, false); } @@ -174,20 +191,23 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, oldest = deref_locked(tcp_metrics_hash[hash].chain); for (tm = deref_locked(oldest->tcpm_next); tm; tm = deref_locked(tm->tcpm_next)) { - if (time_before(tm->tcpm_stamp, oldest->tcpm_stamp)) + if (time_before(READ_ONCE(tm->tcpm_stamp), + READ_ONCE(oldest->tcpm_stamp))) oldest = tm; } tm = oldest; } else { - tm = kmalloc(sizeof(*tm), GFP_ATOMIC); + tm = kzalloc(sizeof(*tm), GFP_ATOMIC); if (!tm) goto out_unlock; } - write_pnet(&tm->tcpm_net, net); + /* Paired with the READ_ONCE() in tm_net() */ + WRITE_ONCE(tm->tcpm_net, net); + tm->tcpm_saddr = *saddr; tm->tcpm_daddr = *daddr; - tcpm_suck_dst(tm, dst, true); + tcpm_suck_dst(tm, dst, reclaim); if (likely(!reclaim)) { tm->tcpm_next = tcp_metrics_hash[hash].chain; @@ -434,7 +454,7 @@ void tcp_update_metrics(struct sock *sk) tp->reordering); } } - tm->tcpm_stamp = jiffies; + WRITE_ONCE(tm->tcpm_stamp, jiffies); out_unlock: rcu_read_unlock(); } @@ -539,8 +559,6 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) return ret; } -static DEFINE_SEQLOCK(fastopen_seqlock); - void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie) { @@ -647,7 +665,7 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, } if (nla_put_msecs(msg, TCP_METRICS_ATTR_AGE, - jiffies - tm->tcpm_stamp, + jiffies - READ_ONCE(tm->tcpm_stamp), TCP_METRICS_ATTR_PAD) < 0) goto nla_put_failure; @@ -658,7 +676,7 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, if (!nest) goto nla_put_failure; for (i = 0; i < TCP_METRIC_MAX_KERNEL + 1; i++) { - u32 val = tm->tcpm_vals[i]; + u32 val = tcp_metric_get(tm, i); if (!val) continue; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 42a96b3547c9..abfa860367aa 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -114,6 +114,7 @@ #include <net/sock_reuseport.h> #include <net/addrconf.h> #include <net/udp_tunnel.h> +#include <net/gro.h> #if IS_ENABLED(CONFIG_IPV6) #include <net/ipv6_stubs.h> #endif @@ -555,10 +556,13 @@ struct sock *udp4_lib_lookup_skb(const struct sk_buff *skb, { const struct iphdr *iph = ip_hdr(skb); struct net *net = dev_net(skb->dev); + int iif, sdif; + + inet_get_iif_sdif(skb, &iif, &sdif); return __udp4_lib_lookup(net, iph->saddr, sport, - iph->daddr, dport, inet_iif(skb), - inet_sdif(skb), net->ipv4.udp_table, NULL); + iph->daddr, dport, iif, + sdif, net->ipv4.udp_table, NULL); } /* Must be called under rcu_read_lock(). diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index f402946da344..0f46b3c2e4ac 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -609,10 +609,13 @@ static struct sock *udp4_gro_lookup_skb(struct sk_buff *skb, __be16 sport, { const struct iphdr *iph = skb_gro_network_header(skb); struct net *net = dev_net(skb->dev); + int iif, sdif; + + inet_get_iif_sdif(skb, &iif, &sdif); return __udp4_lib_lookup(net, iph->saddr, sport, - iph->daddr, dport, inet_iif(skb), - inet_sdif(skb), net->ipv4.udp_table, NULL); + iph->daddr, dport, iif, + sdif, net->ipv4.udp_table, NULL); } INDIRECT_CALLABLE_SCOPE diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index cc3d5ad17257..67a3b8f6e72b 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1073,7 +1073,7 @@ static int ip6mr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt, And all this only to mangle msg->im6_msgtype and to set msg->im6_mbz to "mbz" :-) */ - skb_push(skb, -skb_network_offset(pkt)); + __skb_pull(skb, skb_network_offset(pkt)); skb_push(skb, sizeof(*msg)); skb_reset_transport_header(skb); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 18634ebd20a4..a42be96ae209 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -197,7 +197,8 @@ static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, static inline int ndisc_is_useropt(const struct net_device *dev, struct nd_opt_hdr *opt) { - return opt->nd_opt_type == ND_OPT_RDNSS || + return opt->nd_opt_type == ND_OPT_PREFIX_INFO || + opt->nd_opt_type == ND_OPT_RDNSS || opt->nd_opt_type == ND_OPT_DNSSL || opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL || opt->nd_opt_type == ND_OPT_PREF64 || diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index f804c11e2146..c2c291827a2c 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -120,7 +120,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipcm6_init_sk(&ipc6, np); ipc6.sockc.tsflags = sk->sk_tsflags; - ipc6.sockc.mark = sk->sk_mark; + ipc6.sockc.mark = READ_ONCE(sk->sk_mark); fl6.flowi6_oif = oif; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index ac1cef094c5f..49381f35b623 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -614,7 +614,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, skb_reserve(skb, hlen); skb->protocol = htons(ETH_P_IPV6); - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc->mark; skb->tstamp = sockc->transmit_time; @@ -774,12 +774,12 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) */ memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = READ_ONCE(sk->sk_mark); fl6.flowi6_uid = sk->sk_uid; ipcm6_init(&ipc6); ipc6.sockc.tsflags = sk->sk_tsflags; - ipc6.sockc.mark = sk->sk_mark; + ipc6.sockc.mark = fl6.flowi6_mark; if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 64e873f5895f..56a55585eb79 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2951,7 +2951,8 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) if (!oif && skb->dev) oif = l3mdev_master_ifindex(skb->dev); - ip6_update_pmtu(skb, sock_net(sk), mtu, oif, sk->sk_mark, sk->sk_uid); + ip6_update_pmtu(skb, sock_net(sk), mtu, oif, READ_ONCE(sk->sk_mark), + sk->sk_uid); dst = __sk_dst_get(sk); if (!dst || !dst->obsolete || @@ -3172,8 +3173,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif) void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) { - ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark, - sk->sk_uid); + ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, + READ_ONCE(sk->sk_mark), sk->sk_uid); } EXPORT_SYMBOL_GPL(ip6_sk_redirect); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4714eb695913..6e86721e1cdb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -564,8 +564,8 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, opt = ireq->ipv6_opt; if (!opt) opt = rcu_dereference(np->opt); - err = ip6_xmit(sk, skb, fl6, skb->mark ? : sk->sk_mark, opt, - tclass, sk->sk_priority); + err = ip6_xmit(sk, skb, fl6, skb->mark ? : READ_ONCE(sk->sk_mark), + opt, tclass, sk->sk_priority); rcu_read_unlock(); err = net_xmit_eval(err); } @@ -939,7 +939,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 if (sk->sk_state == TCP_TIME_WAIT) mark = inet_twsk(sk)->tw_mark; else - mark = sk->sk_mark; + mark = READ_ONCE(sk->sk_mark); skb_set_delivery_time(buff, tcp_transmit_time(sk), true); } if (txhash) { @@ -1128,7 +1128,8 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, tcp_time_stamp_raw() + tcp_rsk(req)->ts_off, READ_ONCE(req->ts_recent), sk->sk_bound_dev_if, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index), - ipv6_get_dsfield(ipv6_hdr(skb)), 0, sk->sk_priority, + ipv6_get_dsfield(ipv6_hdr(skb)), 0, + READ_ONCE(sk->sk_priority), READ_ONCE(tcp_rsk(req)->txhash)); } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b7c972aa09a7..f787e6b8424c 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -51,6 +51,7 @@ #include <net/inet6_hashtables.h> #include <net/busy_poll.h> #include <net/sock_reuseport.h> +#include <net/gro.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> @@ -300,10 +301,13 @@ struct sock *udp6_lib_lookup_skb(const struct sk_buff *skb, { const struct ipv6hdr *iph = ipv6_hdr(skb); struct net *net = dev_net(skb->dev); + int iif, sdif; + + inet6_get_iif_sdif(skb, &iif, &sdif); return __udp6_lib_lookup(net, &iph->saddr, sport, - &iph->daddr, dport, inet6_iif(skb), - inet6_sdif(skb), net->ipv4.udp_table, NULL); + &iph->daddr, dport, iif, + sdif, net->ipv4.udp_table, NULL); } /* Must be called under rcu_read_lock(). @@ -624,7 +628,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) { if (tunnel) { ip6_redirect(skb, sock_net(sk), inet6_iif(skb), - sk->sk_mark, sk->sk_uid); + READ_ONCE(sk->sk_mark), sk->sk_uid); } else { ip6_sk_redirect(skb, sk); } @@ -1356,7 +1360,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipcm6_init(&ipc6); ipc6.gso_size = READ_ONCE(up->gso_size); ipc6.sockc.tsflags = sk->sk_tsflags; - ipc6.sockc.mark = sk->sk_mark; + ipc6.sockc.mark = READ_ONCE(sk->sk_mark); /* destination address check */ if (sin6) { diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 09fa7a42cb93..6b95ba241ebe 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -118,10 +118,13 @@ static struct sock *udp6_gro_lookup_skb(struct sk_buff *skb, __be16 sport, { const struct ipv6hdr *iph = skb_gro_network_header(skb); struct net *net = dev_net(skb->dev); + int iif, sdif; + + inet6_get_iif_sdif(skb, &iif, &sdif); return __udp6_lib_lookup(net, &iph->saddr, sport, - &iph->daddr, dport, inet6_iif(skb), - inet6_sdif(skb), net->ipv4.udp_table, NULL); + &iph->daddr, dport, iif, + sdif, net->ipv4.udp_table, NULL); } INDIRECT_CALLABLE_SCOPE diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index b1623f9c4f92..ff78217f0cb1 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -519,7 +519,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) /* Get and verify the address */ memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = READ_ONCE(sk->sk_mark); fl6.flowi6_uid = sk->sk_uid; ipcm6_init(&ipc6); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 3317d1cca156..d80658547836 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2335,7 +2335,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); - if (flags & MPTCP_CF_FASTCLOSE) { + if ((flags & MPTCP_CF_FASTCLOSE) && !__mptcp_check_fallback(msk)) { /* be sure to force the tcp_disconnect() path, * to generate the egress reset */ @@ -3328,7 +3328,7 @@ static void mptcp_release_cb(struct sock *sk) if (__test_and_clear_bit(MPTCP_CLEAN_UNA, &msk->cb_flags)) __mptcp_clean_una_wakeup(sk); - if (unlikely(&msk->cb_flags)) { + if (unlikely(msk->cb_flags)) { /* be sure to set the current sk state before tacking actions * depending on sk_state, that is processing MPTCP_ERROR_REPORT */ diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 37fbe22e2433..ba2a873a4d2e 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -325,7 +325,6 @@ struct mptcp_sock { u32 subflow_id; u32 setsockopt_seq; char ca_name[TCP_CA_NAME_MAX]; - struct mptcp_sock *dl_next; }; #define mptcp_data_lock(sk) spin_lock_bh(&(sk)->sk_lock.slock) diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 63f7a09335c5..a3f1fe810cc9 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -103,7 +103,7 @@ static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, in break; case SO_MARK: if (READ_ONCE(ssk->sk_mark) != sk->sk_mark) { - ssk->sk_mark = sk->sk_mark; + WRITE_ONCE(ssk->sk_mark, sk->sk_mark); sk_dst_reset(ssk); } break; diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 9ee3b7abbaf6..94ae7dd01c65 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1793,16 +1793,31 @@ static void subflow_state_change(struct sock *sk) void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_ssk) { struct request_sock_queue *queue = &inet_csk(listener_ssk)->icsk_accept_queue; - struct mptcp_sock *msk, *next, *head = NULL; - struct request_sock *req; - struct sock *sk; + struct request_sock *req, *head, *tail; + struct mptcp_subflow_context *subflow; + struct sock *sk, *ssk; - /* build a list of all unaccepted mptcp sockets */ + /* Due to lock dependencies no relevant lock can be acquired under rskq_lock. + * Splice the req list, so that accept() can not reach the pending ssk after + * the listener socket is released below. + */ spin_lock_bh(&queue->rskq_lock); - for (req = queue->rskq_accept_head; req; req = req->dl_next) { - struct mptcp_subflow_context *subflow; - struct sock *ssk = req->sk; + head = queue->rskq_accept_head; + tail = queue->rskq_accept_tail; + queue->rskq_accept_head = NULL; + queue->rskq_accept_tail = NULL; + spin_unlock_bh(&queue->rskq_lock); + + if (!head) + return; + /* can't acquire the msk socket lock under the subflow one, + * or will cause ABBA deadlock + */ + release_sock(listener_ssk); + + for (req = head; req; req = req->dl_next) { + ssk = req->sk; if (!sk_is_mptcp(ssk)) continue; @@ -1810,32 +1825,10 @@ void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_s if (!subflow || !subflow->conn) continue; - /* skip if already in list */ sk = subflow->conn; - msk = mptcp_sk(sk); - if (msk->dl_next || msk == head) - continue; - sock_hold(sk); - msk->dl_next = head; - head = msk; - } - spin_unlock_bh(&queue->rskq_lock); - if (!head) - return; - - /* can't acquire the msk socket lock under the subflow one, - * or will cause ABBA deadlock - */ - release_sock(listener_ssk); - - for (msk = head; msk; msk = next) { - sk = (struct sock *)msk; lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - next = msk->dl_next; - msk->dl_next = NULL; - __mptcp_unaccepted_force_close(sk); release_sock(sk); @@ -1859,6 +1852,13 @@ void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_s /* we are still under the listener msk socket lock */ lock_sock_nested(listener_ssk, SINGLE_DEPTH_NESTING); + + /* restore the listener queue, to let the TCP code clean it up */ + spin_lock_bh(&queue->rskq_lock); + WARN_ON_ONCE(queue->rskq_accept_head); + queue->rskq_accept_head = head; + queue->rskq_accept_tail = tail; + spin_unlock_bh(&queue->rskq_lock); } static int subflow_ulp_init(struct sock *sk) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d3c6ecd1f5a6..c62227ae7746 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -31,7 +31,9 @@ static LIST_HEAD(nf_tables_expressions); static LIST_HEAD(nf_tables_objects); static LIST_HEAD(nf_tables_flowtables); static LIST_HEAD(nf_tables_destroy_list); +static LIST_HEAD(nf_tables_gc_list); static DEFINE_SPINLOCK(nf_tables_destroy_list_lock); +static DEFINE_SPINLOCK(nf_tables_gc_list_lock); enum { NFT_VALIDATE_SKIP = 0, @@ -120,6 +122,9 @@ static void nft_validate_state_update(struct nft_table *table, u8 new_validate_s static void nf_tables_trans_destroy_work(struct work_struct *w); static DECLARE_WORK(trans_destroy_work, nf_tables_trans_destroy_work); +static void nft_trans_gc_work(struct work_struct *work); +static DECLARE_WORK(trans_gc_work, nft_trans_gc_work); + static void nft_ctx_init(struct nft_ctx *ctx, struct net *net, const struct sk_buff *skb, @@ -582,10 +587,6 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type, return __nft_trans_set_add(ctx, msg_type, set, NULL); } -static void nft_setelem_data_deactivate(const struct net *net, - const struct nft_set *set, - struct nft_set_elem *elem); - static int nft_mapelem_deactivate(const struct nft_ctx *ctx, struct nft_set *set, const struct nft_set_iter *iter, @@ -5055,6 +5056,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info, INIT_LIST_HEAD(&set->bindings); INIT_LIST_HEAD(&set->catchall_list); + refcount_set(&set->refs, 1); set->table = table; write_pnet(&set->net, net); set->ops = ops; @@ -5122,6 +5124,14 @@ static void nft_set_catchall_destroy(const struct nft_ctx *ctx, } } +static void nft_set_put(struct nft_set *set) +{ + if (refcount_dec_and_test(&set->refs)) { + kfree(set->name); + kvfree(set); + } +} + static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) { int i; @@ -5134,8 +5144,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) set->ops->destroy(ctx, set); nft_set_catchall_destroy(ctx, set); - kfree(set->name); - kvfree(set); + nft_set_put(set); } static int nf_tables_delset(struct sk_buff *skb, const struct nfnl_info *info, @@ -5602,8 +5611,12 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx, const struct nft_set_iter *iter, struct nft_set_elem *elem) { + const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); struct nft_set_dump_args *args; + if (nft_set_elem_expired(ext)) + return 0; + args = container_of(iter, struct nft_set_dump_args, iter); return nf_tables_fill_setelem(args->skb, set, elem, args->reset); } @@ -6274,7 +6287,8 @@ struct nft_set_ext *nft_set_catchall_lookup(const struct net *net, list_for_each_entry_rcu(catchall, &set->catchall_list, list) { ext = nft_set_elem_ext(set, catchall->elem); if (nft_set_elem_active(ext, genmask) && - !nft_set_elem_expired(ext)) + !nft_set_elem_expired(ext) && + !nft_set_elem_is_dead(ext)) return ext; } @@ -6282,29 +6296,6 @@ struct nft_set_ext *nft_set_catchall_lookup(const struct net *net, } EXPORT_SYMBOL_GPL(nft_set_catchall_lookup); -void *nft_set_catchall_gc(const struct nft_set *set) -{ - struct nft_set_elem_catchall *catchall, *next; - struct nft_set_ext *ext; - void *elem = NULL; - - list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { - ext = nft_set_elem_ext(set, catchall->elem); - - if (!nft_set_elem_expired(ext) || - nft_set_elem_mark_busy(ext)) - continue; - - elem = catchall->elem; - list_del_rcu(&catchall->list); - kfree_rcu(catchall, rcu); - break; - } - - return elem; -} -EXPORT_SYMBOL_GPL(nft_set_catchall_gc); - static int nft_setelem_catchall_insert(const struct net *net, struct nft_set *set, const struct nft_set_elem *elem, @@ -6366,7 +6357,6 @@ static void nft_setelem_activate(struct net *net, struct nft_set *set, if (nft_setelem_is_catchall(set, elem)) { nft_set_elem_change_active(net, set, ext); - nft_set_elem_clear_busy(ext); } else { set->ops->activate(net, set, elem); } @@ -6381,8 +6371,7 @@ static int nft_setelem_catchall_deactivate(const struct net *net, list_for_each_entry(catchall, &set->catchall_list, list) { ext = nft_set_elem_ext(set, catchall->elem); - if (!nft_is_active(net, ext) || - nft_set_elem_mark_busy(ext)) + if (!nft_is_active(net, ext)) continue; kfree(elem->priv); @@ -6777,7 +6766,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, goto err_elem_free; } - ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; + ext->genmask = nft_genmask_cur(ctx->net); err = nft_setelem_insert(ctx->net, set, &elem, &ext2, flags); if (err) { @@ -6929,9 +6918,9 @@ static void nft_setelem_data_activate(const struct net *net, nft_use_inc_restore(&(*nft_set_ext_obj(ext))->use); } -static void nft_setelem_data_deactivate(const struct net *net, - const struct nft_set *set, - struct nft_set_elem *elem) +void nft_setelem_data_deactivate(const struct net *net, + const struct nft_set *set, + struct nft_set_elem *elem) { const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv); @@ -7095,8 +7084,7 @@ static int nft_set_catchall_flush(const struct nft_ctx *ctx, list_for_each_entry_rcu(catchall, &set->catchall_list, list) { ext = nft_set_elem_ext(set, catchall->elem); - if (!nft_set_elem_active(ext, genmask) || - nft_set_elem_mark_busy(ext)) + if (!nft_set_elem_active(ext, genmask)) continue; elem.priv = catchall->elem; @@ -7170,29 +7158,6 @@ static int nf_tables_delsetelem(struct sk_buff *skb, return err; } -void nft_set_gc_batch_release(struct rcu_head *rcu) -{ - struct nft_set_gc_batch *gcb; - unsigned int i; - - gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu); - for (i = 0; i < gcb->head.cnt; i++) - nft_set_elem_destroy(gcb->head.set, gcb->elems[i], true); - kfree(gcb); -} - -struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set, - gfp_t gfp) -{ - struct nft_set_gc_batch *gcb; - - gcb = kzalloc(sizeof(*gcb), gfp); - if (gcb == NULL) - return gcb; - gcb->head.set = set; - return gcb; -} - /* * Stateful objects */ @@ -9414,6 +9379,207 @@ void nft_chain_del(struct nft_chain *chain) list_del_rcu(&chain->list); } +static void nft_trans_gc_setelem_remove(struct nft_ctx *ctx, + struct nft_trans_gc *trans) +{ + void **priv = trans->priv; + unsigned int i; + + for (i = 0; i < trans->count; i++) { + struct nft_set_elem elem = { + .priv = priv[i], + }; + + nft_setelem_data_deactivate(ctx->net, trans->set, &elem); + nft_setelem_remove(ctx->net, trans->set, &elem); + } +} + +void nft_trans_gc_destroy(struct nft_trans_gc *trans) +{ + nft_set_put(trans->set); + put_net(trans->net); + kfree(trans); +} + +static void nft_trans_gc_trans_free(struct rcu_head *rcu) +{ + struct nft_set_elem elem = {}; + struct nft_trans_gc *trans; + struct nft_ctx ctx = {}; + unsigned int i; + + trans = container_of(rcu, struct nft_trans_gc, rcu); + ctx.net = read_pnet(&trans->set->net); + + for (i = 0; i < trans->count; i++) { + elem.priv = trans->priv[i]; + if (!nft_setelem_is_catchall(trans->set, &elem)) + atomic_dec(&trans->set->nelems); + + nf_tables_set_elem_destroy(&ctx, trans->set, elem.priv); + } + + nft_trans_gc_destroy(trans); +} + +static bool nft_trans_gc_work_done(struct nft_trans_gc *trans) +{ + struct nftables_pernet *nft_net; + struct nft_ctx ctx = {}; + + nft_net = nft_pernet(trans->net); + + mutex_lock(&nft_net->commit_mutex); + + /* Check for race with transaction, otherwise this batch refers to + * stale objects that might not be there anymore. Skip transaction if + * set has been destroyed from control plane transaction in case gc + * worker loses race. + */ + if (READ_ONCE(nft_net->gc_seq) != trans->seq || trans->set->dead) { + mutex_unlock(&nft_net->commit_mutex); + return false; + } + + ctx.net = trans->net; + ctx.table = trans->set->table; + + nft_trans_gc_setelem_remove(&ctx, trans); + mutex_unlock(&nft_net->commit_mutex); + + return true; +} + +static void nft_trans_gc_work(struct work_struct *work) +{ + struct nft_trans_gc *trans, *next; + LIST_HEAD(trans_gc_list); + + spin_lock(&nf_tables_destroy_list_lock); + list_splice_init(&nf_tables_gc_list, &trans_gc_list); + spin_unlock(&nf_tables_destroy_list_lock); + + list_for_each_entry_safe(trans, next, &trans_gc_list, list) { + list_del(&trans->list); + if (!nft_trans_gc_work_done(trans)) { + nft_trans_gc_destroy(trans); + continue; + } + call_rcu(&trans->rcu, nft_trans_gc_trans_free); + } +} + +struct nft_trans_gc *nft_trans_gc_alloc(struct nft_set *set, + unsigned int gc_seq, gfp_t gfp) +{ + struct net *net = read_pnet(&set->net); + struct nft_trans_gc *trans; + + trans = kzalloc(sizeof(*trans), gfp); + if (!trans) + return NULL; + + refcount_inc(&set->refs); + trans->set = set; + trans->net = get_net(net); + trans->seq = gc_seq; + + return trans; +} + +void nft_trans_gc_elem_add(struct nft_trans_gc *trans, void *priv) +{ + trans->priv[trans->count++] = priv; +} + +static void nft_trans_gc_queue_work(struct nft_trans_gc *trans) +{ + spin_lock(&nf_tables_gc_list_lock); + list_add_tail(&trans->list, &nf_tables_gc_list); + spin_unlock(&nf_tables_gc_list_lock); + + schedule_work(&trans_gc_work); +} + +static int nft_trans_gc_space(struct nft_trans_gc *trans) +{ + return NFT_TRANS_GC_BATCHCOUNT - trans->count; +} + +struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc, + unsigned int gc_seq, gfp_t gfp) +{ + if (nft_trans_gc_space(gc)) + return gc; + + nft_trans_gc_queue_work(gc); + + return nft_trans_gc_alloc(gc->set, gc_seq, gfp); +} + +void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans) +{ + if (trans->count == 0) { + nft_trans_gc_destroy(trans); + return; + } + + nft_trans_gc_queue_work(trans); +} + +struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp) +{ + if (WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net))) + return NULL; + + if (nft_trans_gc_space(gc)) + return gc; + + call_rcu(&gc->rcu, nft_trans_gc_trans_free); + + return nft_trans_gc_alloc(gc->set, 0, gfp); +} + +void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans) +{ + WARN_ON_ONCE(!lockdep_commit_lock_is_held(trans->net)); + + if (trans->count == 0) { + nft_trans_gc_destroy(trans); + return; + } + + call_rcu(&trans->rcu, nft_trans_gc_trans_free); +} + +struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, + unsigned int gc_seq) +{ + struct nft_set_elem_catchall *catchall; + const struct nft_set *set = gc->set; + struct nft_set_ext *ext; + + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + + if (!nft_set_elem_expired(ext)) + continue; + if (nft_set_elem_is_dead(ext)) + goto dead_elem; + + nft_set_elem_dead(ext); +dead_elem: + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (!gc) + return NULL; + + nft_trans_gc_elem_add(gc, catchall->elem); + } + + return gc; +} + static void nf_tables_module_autoload_cleanup(struct net *net) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -9576,11 +9742,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) { struct nftables_pernet *nft_net = nft_pernet(net); struct nft_trans *trans, *next; + unsigned int base_seq, gc_seq; LIST_HEAD(set_update_list); struct nft_trans_elem *te; struct nft_chain *chain; struct nft_table *table; - unsigned int base_seq; LIST_HEAD(adl); int err; @@ -9657,6 +9823,10 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) WRITE_ONCE(nft_net->base_seq, base_seq); + /* Bump gc counter, it becomes odd, this is the busy mark. */ + gc_seq = READ_ONCE(nft_net->gc_seq); + WRITE_ONCE(nft_net->gc_seq, ++gc_seq); + /* step 3. Start new generation, rules_gen_X now in use. */ net->nft.gencursor = nft_gencursor_next(net); @@ -9764,6 +9934,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) break; case NFT_MSG_DELSET: case NFT_MSG_DESTROYSET: + nft_trans_set(trans)->dead = 1; list_del_rcu(&nft_trans_set(trans)->list); nf_tables_set_notify(&trans->ctx, nft_trans_set(trans), trans->msg_type, GFP_KERNEL); @@ -9866,6 +10037,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) nft_commit_notify(net, NETLINK_CB(skb).portid); nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); nf_tables_commit_audit_log(&adl, nft_net->base_seq); + + WRITE_ONCE(nft_net->gc_seq, ++gc_seq); nf_tables_commit_release(net); return 0; @@ -10915,6 +11088,7 @@ static int __net_init nf_tables_init_net(struct net *net) INIT_LIST_HEAD(&nft_net->notify_list); mutex_init(&nft_net->commit_mutex); nft_net->base_seq = 1; + nft_net->gc_seq = 0; return 0; } @@ -10943,10 +11117,16 @@ static void __net_exit nf_tables_exit_net(struct net *net) WARN_ON_ONCE(!list_empty(&nft_net->notify_list)); } +static void nf_tables_exit_batch(struct list_head *net_exit_list) +{ + flush_work(&trans_gc_work); +} + static struct pernet_operations nf_tables_net_ops = { .init = nf_tables_init_net, .pre_exit = nf_tables_pre_exit_net, .exit = nf_tables_exit_net, + .exit_batch = nf_tables_exit_batch, .id = &nf_tables_net_id, .size = sizeof(struct nftables_pernet), }; @@ -11018,6 +11198,7 @@ static void __exit nf_tables_module_exit(void) nft_chain_filter_fini(); nft_chain_route_fini(); unregister_pernet_subsys(&nf_tables_net_ops); + cancel_work_sync(&trans_gc_work); cancel_work_sync(&trans_destroy_work); rcu_barrier(); rhltable_destroy(&nft_objname_ht); diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 0b73cb0e752f..cef5df846000 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -59,6 +59,8 @@ static inline int nft_rhash_cmp(struct rhashtable_compare_arg *arg, if (memcmp(nft_set_ext_key(&he->ext), x->key, x->set->klen)) return 1; + if (nft_set_elem_is_dead(&he->ext)) + return 1; if (nft_set_elem_expired(&he->ext)) return 1; if (!nft_set_elem_active(&he->ext, x->genmask)) @@ -188,7 +190,6 @@ static void nft_rhash_activate(const struct net *net, const struct nft_set *set, struct nft_rhash_elem *he = elem->priv; nft_set_elem_change_active(net, set, &he->ext); - nft_set_elem_clear_busy(&he->ext); } static bool nft_rhash_flush(const struct net *net, @@ -196,12 +197,9 @@ static bool nft_rhash_flush(const struct net *net, { struct nft_rhash_elem *he = priv; - if (!nft_set_elem_mark_busy(&he->ext) || - !nft_is_active(net, &he->ext)) { - nft_set_elem_change_active(net, set, &he->ext); - return true; - } - return false; + nft_set_elem_change_active(net, set, &he->ext); + + return true; } static void *nft_rhash_deactivate(const struct net *net, @@ -218,9 +216,8 @@ static void *nft_rhash_deactivate(const struct net *net, rcu_read_lock(); he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params); - if (he != NULL && - !nft_rhash_flush(net, set, he)) - he = NULL; + if (he) + nft_set_elem_change_active(net, set, &he->ext); rcu_read_unlock(); @@ -252,7 +249,9 @@ static bool nft_rhash_delete(const struct nft_set *set, if (he == NULL) return false; - return rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params) == 0; + nft_set_elem_dead(&he->ext); + + return true; } static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set, @@ -278,8 +277,6 @@ static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set, if (iter->count < iter->skip) goto cont; - if (nft_set_elem_expired(&he->ext)) - goto cont; if (!nft_set_elem_active(&he->ext, iter->genmask)) goto cont; @@ -314,25 +311,48 @@ static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set, static void nft_rhash_gc(struct work_struct *work) { + struct nftables_pernet *nft_net; struct nft_set *set; struct nft_rhash_elem *he; struct nft_rhash *priv; - struct nft_set_gc_batch *gcb = NULL; struct rhashtable_iter hti; + struct nft_trans_gc *gc; + struct net *net; + u32 gc_seq; priv = container_of(work, struct nft_rhash, gc_work.work); set = nft_set_container_of(priv); + net = read_pnet(&set->net); + nft_net = nft_pernet(net); + gc_seq = READ_ONCE(nft_net->gc_seq); + + gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL); + if (!gc) + goto done; rhashtable_walk_enter(&priv->ht, &hti); rhashtable_walk_start(&hti); while ((he = rhashtable_walk_next(&hti))) { if (IS_ERR(he)) { - if (PTR_ERR(he) != -EAGAIN) - break; + if (PTR_ERR(he) != -EAGAIN) { + nft_trans_gc_destroy(gc); + gc = NULL; + goto try_later; + } continue; } + /* Ruleset has been updated, try later. */ + if (READ_ONCE(nft_net->gc_seq) != gc_seq) { + nft_trans_gc_destroy(gc); + gc = NULL; + goto try_later; + } + + if (nft_set_elem_is_dead(&he->ext)) + goto dead_elem; + if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) && nft_rhash_expr_needs_gc_run(set, &he->ext)) goto needs_gc_run; @@ -340,26 +360,26 @@ static void nft_rhash_gc(struct work_struct *work) if (!nft_set_elem_expired(&he->ext)) continue; needs_gc_run: - if (nft_set_elem_mark_busy(&he->ext)) - continue; + nft_set_elem_dead(&he->ext); +dead_elem: + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (!gc) + goto try_later; - gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); - if (gcb == NULL) - break; - rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params); - atomic_dec(&set->nelems); - nft_set_gc_batch_add(gcb, he); + nft_trans_gc_elem_add(gc, he); } + + gc = nft_trans_gc_catchall(gc, gc_seq); + +try_later: + /* catchall list iteration requires rcu read side lock. */ rhashtable_walk_stop(&hti); rhashtable_walk_exit(&hti); - he = nft_set_catchall_gc(set); - if (he) { - gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); - if (gcb) - nft_set_gc_batch_add(gcb, he); - } - nft_set_gc_batch_complete(gcb); + if (gc) + nft_trans_gc_queue_async_done(gc); + +done: queue_delayed_work(system_power_efficient_wq, &priv->gc_work, nft_set_gc_interval(set)); } @@ -394,7 +414,7 @@ static int nft_rhash_init(const struct nft_set *set, return err; INIT_DEFERRABLE_WORK(&priv->gc_work, nft_rhash_gc); - if (set->flags & NFT_SET_TIMEOUT) + if (set->flags & (NFT_SET_TIMEOUT | NFT_SET_EVAL)) nft_rhash_gc_init(set); return 0; @@ -422,7 +442,6 @@ static void nft_rhash_destroy(const struct nft_ctx *ctx, }; cancel_delayed_work_sync(&priv->gc_work); - rcu_barrier(); rhashtable_free_and_destroy(&priv->ht, nft_rhash_elem_destroy, (void *)&rhash_ctx); } diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 49915a2a58eb..a5b8301afe4a 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -566,8 +566,7 @@ next_match: goto out; if (last) { - if (nft_set_elem_expired(&f->mt[b].e->ext) || - (genmask && + if ((genmask && !nft_set_elem_active(&f->mt[b].e->ext, genmask))) goto next_match; @@ -601,8 +600,17 @@ out: static void *nft_pipapo_get(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, unsigned int flags) { - return pipapo_get(net, set, (const u8 *)elem->key.val.data, - nft_genmask_cur(net)); + struct nft_pipapo_elem *ret; + + ret = pipapo_get(net, set, (const u8 *)elem->key.val.data, + nft_genmask_cur(net)); + if (IS_ERR(ret)) + return ret; + + if (nft_set_elem_expired(&ret->ext)) + return ERR_PTR(-ENOENT); + + return ret; } /** @@ -1528,16 +1536,34 @@ static void pipapo_drop(struct nft_pipapo_match *m, } } +static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set, + struct nft_pipapo_elem *e) + +{ + struct nft_set_elem elem = { + .priv = e, + }; + + nft_setelem_data_deactivate(net, set, &elem); +} + /** * pipapo_gc() - Drop expired entries from set, destroy start and end elements * @set: nftables API set representation * @m: Matching data */ -static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m) +static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m) { + struct nft_set *set = (struct nft_set *) _set; struct nft_pipapo *priv = nft_set_priv(set); + struct net *net = read_pnet(&set->net); int rules_f0, first_rule = 0; struct nft_pipapo_elem *e; + struct nft_trans_gc *gc; + + gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL); + if (!gc) + return; while ((rules_f0 = pipapo_rules_same_key(m->f, first_rule))) { union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS]; @@ -1561,13 +1587,20 @@ static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m) f--; i--; e = f->mt[rulemap[i].to].e; - if (nft_set_elem_expired(&e->ext) && - !nft_set_elem_mark_busy(&e->ext)) { + + /* synchronous gc never fails, there is no need to set on + * NFT_SET_ELEM_DEAD_BIT. + */ + if (nft_set_elem_expired(&e->ext)) { priv->dirty = true; - pipapo_drop(m, rulemap); - rcu_barrier(); - nft_set_elem_destroy(set, e, true); + gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); + if (!gc) + break; + + nft_pipapo_gc_deactivate(net, set, e); + pipapo_drop(m, rulemap); + nft_trans_gc_elem_add(gc, e); /* And check again current first rule, which is now the * first we haven't checked. @@ -1577,11 +1610,11 @@ static void pipapo_gc(const struct nft_set *set, struct nft_pipapo_match *m) } } - e = nft_set_catchall_gc(set); - if (e) - nft_set_elem_destroy(set, e, true); - - priv->last_gc = jiffies; + gc = nft_trans_gc_catchall(gc, 0); + if (gc) { + nft_trans_gc_queue_sync_done(gc); + priv->last_gc = jiffies; + } } /** @@ -1706,7 +1739,6 @@ static void nft_pipapo_activate(const struct net *net, return; nft_set_elem_change_active(net, set, &e->ext); - nft_set_elem_clear_busy(&e->ext); } /** @@ -2005,8 +2037,6 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set, goto cont; e = f->mt[r].e; - if (nft_set_elem_expired(&e->ext)) - goto cont; elem.priv = e; diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 8d73fffd2d09..f9d4c8fcbbf8 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -46,6 +46,12 @@ static int nft_rbtree_cmp(const struct nft_set *set, set->klen); } +static bool nft_rbtree_elem_expired(const struct nft_rbtree_elem *rbe) +{ + return nft_set_elem_expired(&rbe->ext) || + nft_set_elem_is_dead(&rbe->ext); +} + static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set, const u32 *key, const struct nft_set_ext **ext, unsigned int seq) @@ -80,7 +86,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set continue; } - if (nft_set_elem_expired(&rbe->ext)) + if (nft_rbtree_elem_expired(rbe)) return false; if (nft_rbtree_interval_end(rbe)) { @@ -98,7 +104,7 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set if (set->flags & NFT_SET_INTERVAL && interval != NULL && nft_set_elem_active(&interval->ext, genmask) && - !nft_set_elem_expired(&interval->ext) && + !nft_rbtree_elem_expired(interval) && nft_rbtree_interval_start(interval)) { *ext = &interval->ext; return true; @@ -215,6 +221,18 @@ static void *nft_rbtree_get(const struct net *net, const struct nft_set *set, return rbe; } +static void nft_rbtree_gc_remove(struct net *net, struct nft_set *set, + struct nft_rbtree *priv, + struct nft_rbtree_elem *rbe) +{ + struct nft_set_elem elem = { + .priv = rbe, + }; + + nft_setelem_data_deactivate(net, set, &elem); + rb_erase(&rbe->node, &priv->root); +} + static int nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv, struct nft_rbtree_elem *rbe, @@ -222,11 +240,12 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, { struct nft_set *set = (struct nft_set *)__set; struct rb_node *prev = rb_prev(&rbe->node); + struct net *net = read_pnet(&set->net); struct nft_rbtree_elem *rbe_prev; - struct nft_set_gc_batch *gcb; + struct nft_trans_gc *gc; - gcb = nft_set_gc_batch_check(set, NULL, GFP_ATOMIC); - if (!gcb) + gc = nft_trans_gc_alloc(set, 0, GFP_ATOMIC); + if (!gc) return -ENOMEM; /* search for end interval coming before this element. @@ -244,17 +263,28 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, if (prev) { rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node); + nft_rbtree_gc_remove(net, set, priv, rbe_prev); - rb_erase(&rbe_prev->node, &priv->root); - atomic_dec(&set->nelems); - nft_set_gc_batch_add(gcb, rbe_prev); + /* There is always room in this trans gc for this element, + * memory allocation never actually happens, hence, the warning + * splat in such case. No need to set NFT_SET_ELEM_DEAD_BIT, + * this is synchronous gc which never fails. + */ + gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); + if (WARN_ON_ONCE(!gc)) + return -ENOMEM; + + nft_trans_gc_elem_add(gc, rbe_prev); } - rb_erase(&rbe->node, &priv->root); - atomic_dec(&set->nelems); + nft_rbtree_gc_remove(net, set, priv, rbe); + gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); + if (WARN_ON_ONCE(!gc)) + return -ENOMEM; + + nft_trans_gc_elem_add(gc, rbe); - nft_set_gc_batch_add(gcb, rbe); - nft_set_gc_batch_complete(gcb); + nft_trans_gc_queue_sync_done(gc); return 0; } @@ -482,7 +512,6 @@ static void nft_rbtree_activate(const struct net *net, struct nft_rbtree_elem *rbe = elem->priv; nft_set_elem_change_active(net, set, &rbe->ext); - nft_set_elem_clear_busy(&rbe->ext); } static bool nft_rbtree_flush(const struct net *net, @@ -490,12 +519,9 @@ static bool nft_rbtree_flush(const struct net *net, { struct nft_rbtree_elem *rbe = priv; - if (!nft_set_elem_mark_busy(&rbe->ext) || - !nft_is_active(net, &rbe->ext)) { - nft_set_elem_change_active(net, set, &rbe->ext); - return true; - } - return false; + nft_set_elem_change_active(net, set, &rbe->ext); + + return true; } static void *nft_rbtree_deactivate(const struct net *net, @@ -552,8 +578,6 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, if (iter->count < iter->skip) goto cont; - if (nft_set_elem_expired(&rbe->ext)) - goto cont; if (!nft_set_elem_active(&rbe->ext, iter->genmask)) goto cont; @@ -572,26 +596,40 @@ cont: static void nft_rbtree_gc(struct work_struct *work) { - struct nft_rbtree_elem *rbe, *rbe_end = NULL, *rbe_prev = NULL; - struct nft_set_gc_batch *gcb = NULL; + struct nft_rbtree_elem *rbe, *rbe_end = NULL; + struct nftables_pernet *nft_net; struct nft_rbtree *priv; + struct nft_trans_gc *gc; struct rb_node *node; struct nft_set *set; + unsigned int gc_seq; struct net *net; - u8 genmask; priv = container_of(work, struct nft_rbtree, gc_work.work); set = nft_set_container_of(priv); net = read_pnet(&set->net); - genmask = nft_genmask_cur(net); + nft_net = nft_pernet(net); + gc_seq = READ_ONCE(nft_net->gc_seq); + + gc = nft_trans_gc_alloc(set, gc_seq, GFP_KERNEL); + if (!gc) + goto done; write_lock_bh(&priv->lock); write_seqcount_begin(&priv->count); for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { + + /* Ruleset has been updated, try later. */ + if (READ_ONCE(nft_net->gc_seq) != gc_seq) { + nft_trans_gc_destroy(gc); + gc = NULL; + goto try_later; + } + rbe = rb_entry(node, struct nft_rbtree_elem, node); - if (!nft_set_elem_active(&rbe->ext, genmask)) - continue; + if (nft_set_elem_is_dead(&rbe->ext)) + goto dead_elem; /* elements are reversed in the rbtree for historical reasons, * from highest to lowest value, that is why end element is @@ -604,46 +642,36 @@ static void nft_rbtree_gc(struct work_struct *work) if (!nft_set_elem_expired(&rbe->ext)) continue; - if (nft_set_elem_mark_busy(&rbe->ext)) { - rbe_end = NULL; + nft_set_elem_dead(&rbe->ext); + + if (!rbe_end) continue; - } - if (rbe_prev) { - rb_erase(&rbe_prev->node, &priv->root); - rbe_prev = NULL; - } - gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); - if (!gcb) - break; + nft_set_elem_dead(&rbe_end->ext); - atomic_dec(&set->nelems); - nft_set_gc_batch_add(gcb, rbe); - rbe_prev = rbe; + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (!gc) + goto try_later; - if (rbe_end) { - atomic_dec(&set->nelems); - nft_set_gc_batch_add(gcb, rbe_end); - rb_erase(&rbe_end->node, &priv->root); - rbe_end = NULL; - } - node = rb_next(node); - if (!node) - break; + nft_trans_gc_elem_add(gc, rbe_end); + rbe_end = NULL; +dead_elem: + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (!gc) + goto try_later; + + nft_trans_gc_elem_add(gc, rbe); } - if (rbe_prev) - rb_erase(&rbe_prev->node, &priv->root); + + gc = nft_trans_gc_catchall(gc, gc_seq); + +try_later: write_seqcount_end(&priv->count); write_unlock_bh(&priv->lock); - rbe = nft_set_catchall_gc(set); - if (rbe) { - gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC); - if (gcb) - nft_set_gc_batch_add(gcb, rbe); - } - nft_set_gc_batch_complete(gcb); - + if (gc) + nft_trans_gc_queue_async_done(gc); +done: queue_delayed_work(system_power_efficient_wq, &priv->gc_work, nft_set_gc_interval(set)); } diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index 84def74698b7..9ed85be79452 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -107,7 +107,7 @@ static void nft_socket_eval(const struct nft_expr *expr, break; case NFT_SOCKET_MARK: if (sk_fullsock(sk)) { - *dest = sk->sk_mark; + *dest = READ_ONCE(sk->sk_mark); } else { regs->verdict.code = NFT_BREAK; return; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 7013f55f05d1..76e01f292aaf 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -77,7 +77,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && transparent && sk_fullsock(sk)) - pskb->mark = sk->sk_mark; + pskb->mark = READ_ONCE(sk->sk_mark); if (sk != skb->sk) sock_gen_put(sk); @@ -138,7 +138,7 @@ socket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && transparent && sk_fullsock(sk)) - pskb->mark = sk->sk_mark; + pskb->mark = READ_ONCE(sk->sk_mark); if (sk != skb->sk) sock_gen_put(sk); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8e3ddec4c3d5..a2935bd18ed9 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -401,18 +401,20 @@ static void __packet_set_status(struct packet_sock *po, void *frame, int status) { union tpacket_uhdr h; + /* WRITE_ONCE() are paired with READ_ONCE() in __packet_get_status */ + h.raw = frame; switch (po->tp_version) { case TPACKET_V1: - h.h1->tp_status = status; + WRITE_ONCE(h.h1->tp_status, status); flush_dcache_page(pgv_to_page(&h.h1->tp_status)); break; case TPACKET_V2: - h.h2->tp_status = status; + WRITE_ONCE(h.h2->tp_status, status); flush_dcache_page(pgv_to_page(&h.h2->tp_status)); break; case TPACKET_V3: - h.h3->tp_status = status; + WRITE_ONCE(h.h3->tp_status, status); flush_dcache_page(pgv_to_page(&h.h3->tp_status)); break; default: @@ -429,17 +431,19 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) smp_rmb(); + /* READ_ONCE() are paired with WRITE_ONCE() in __packet_set_status */ + h.raw = frame; switch (po->tp_version) { case TPACKET_V1: flush_dcache_page(pgv_to_page(&h.h1->tp_status)); - return h.h1->tp_status; + return READ_ONCE(h.h1->tp_status); case TPACKET_V2: flush_dcache_page(pgv_to_page(&h.h2->tp_status)); - return h.h2->tp_status; + return READ_ONCE(h.h2->tp_status); case TPACKET_V3: flush_dcache_page(pgv_to_page(&h.h3->tp_status)); - return h.h3->tp_status; + return READ_ONCE(h.h3->tp_status); default: WARN(1, "TPACKET version not supported.\n"); BUG(); @@ -2050,8 +2054,8 @@ retry: skb->protocol = proto; skb->dev = dev; - skb->priority = sk->sk_priority; - skb->mark = sk->sk_mark; + skb->priority = READ_ONCE(sk->sk_priority); + skb->mark = READ_ONCE(sk->sk_mark); skb->tstamp = sockc.transmit_time; skb_setup_tx_timestamp(skb, sockc.tsflags); @@ -2585,8 +2589,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, skb->protocol = proto; skb->dev = dev; - skb->priority = po->sk.sk_priority; - skb->mark = po->sk.sk_mark; + skb->priority = READ_ONCE(po->sk.sk_priority); + skb->mark = READ_ONCE(po->sk.sk_mark); skb->tstamp = sockc->transmit_time; skb_setup_tx_timestamp(skb, sockc->tsflags); skb_zcopy_set_nouarg(skb, ph.raw); @@ -2988,7 +2992,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_unlock; sockcm_init(&sockc, sk); - sockc.mark = sk->sk_mark; + sockc.mark = READ_ONCE(sk->sk_mark); if (msg->msg_controllen) { err = sock_cmsg_send(sk, msg, &sockc); if (unlikely(err)) @@ -3061,7 +3065,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) skb->protocol = proto; skb->dev = dev; - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); skb->mark = sockc.mark; skb->tstamp = sockc.transmit_time; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 8da9d039d964..9f0711da9c95 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -776,7 +776,8 @@ mpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = { [TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL] = { .type = NLA_U32 }, }; -static const struct nla_policy cfm_opt_policy[TCA_FLOWER_KEY_CFM_OPT_MAX] = { +static const struct nla_policy +cfm_opt_policy[TCA_FLOWER_KEY_CFM_OPT_MAX + 1] = { [TCA_FLOWER_KEY_CFM_MD_LEVEL] = NLA_POLICY_MAX(NLA_U8, FLOW_DIS_CFM_MDL_MAX), [TCA_FLOWER_KEY_CFM_OPCODE] = { .type = NLA_U8 }, @@ -1709,7 +1710,7 @@ static int fl_set_key_cfm(struct nlattr **tb, struct fl_flow_key *mask, struct netlink_ext_ack *extack) { - struct nlattr *nla_cfm_opt[TCA_FLOWER_KEY_CFM_OPT_MAX]; + struct nlattr *nla_cfm_opt[TCA_FLOWER_KEY_CFM_OPT_MAX + 1]; int err; if (!tb[TCA_FLOWER_KEY_CFM]) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 8641f8059317..c49d6af0e048 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -267,7 +267,6 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, return -ENOBUFS; fnew->id = f->id; - fnew->res = f->res; fnew->ifindex = f->ifindex; fnew->tp = f->tp; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index d0c53724d3e8..1e20bbd687f1 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -513,7 +513,6 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, if (fold) { f->id = fold->id; f->iif = fold->iif; - f->res = fold->res; f->handle = fold->handle; f->tp = fold->tp; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 5abf31e432ca..da4c179a4d41 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -826,7 +826,6 @@ static struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp, new->ifindex = n->ifindex; new->fshift = n->fshift; - new->res = n->res; new->flags = n->flags; RCU_INIT_POINTER(new->ht_down, ht); @@ -1024,18 +1023,62 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; } + /* At this point, we need to derive the new handle that will be used to + * uniquely map the identity of this table match entry. The + * identity of the entry that we need to construct is 32 bits made of: + * htid(12b):bucketid(8b):node/entryid(12b) + * + * At this point _we have the table(ht)_ in which we will insert this + * entry. We carry the table's id in variable "htid". + * Note that earlier code picked the ht selection either by a) the user + * providing the htid specified via TCA_U32_HASH attribute or b) when + * no such attribute is passed then the root ht, is default to at ID + * 0x[800][00][000]. Rule: the root table has a single bucket with ID 0. + * If OTOH the user passed us the htid, they may also pass a bucketid of + * choice. 0 is fine. For example a user htid is 0x[600][01][000] it is + * indicating hash bucketid of 1. Rule: the entry/node ID _cannot_ be + * passed via the htid, so even if it was non-zero it will be ignored. + * + * We may also have a handle, if the user passed one. The handle also + * carries the same addressing of htid(12b):bucketid(8b):node/entryid(12b). + * Rule: the bucketid on the handle is ignored even if one was passed; + * rather the value on "htid" is always assumed to be the bucketid. + */ if (handle) { + /* Rule: The htid from handle and tableid from htid must match */ if (TC_U32_HTID(handle) && TC_U32_HTID(handle ^ htid)) { NL_SET_ERR_MSG_MOD(extack, "Handle specified hash table address mismatch"); return -EINVAL; } - handle = htid | TC_U32_NODE(handle); - err = idr_alloc_u32(&ht->handle_idr, NULL, &handle, handle, - GFP_KERNEL); - if (err) - return err; - } else + /* Ok, so far we have a valid htid(12b):bucketid(8b) but we + * need to finalize the table entry identification with the last + * part - the node/entryid(12b)). Rule: Nodeid _cannot be 0_ for + * entries. Rule: nodeid of 0 is reserved only for tables(see + * earlier code which processes TC_U32_DIVISOR attribute). + * Rule: The nodeid can only be derived from the handle (and not + * htid). + * Rule: if the handle specified zero for the node id example + * 0x60000000, then pick a new nodeid from the pool of IDs + * this hash table has been allocating from. + * If OTOH it is specified (i.e for example the user passed a + * handle such as 0x60000123), then we use it generate our final + * handle which is used to uniquely identify the match entry. + */ + if (!TC_U32_NODE(handle)) { + handle = gen_new_kid(ht, htid); + } else { + handle = htid | TC_U32_NODE(handle); + err = idr_alloc_u32(&ht->handle_idr, NULL, &handle, + handle, GFP_KERNEL); + if (err) + return err; + } + } else { + /* The user did not give us a handle; lets just generate one + * from the table's pool of nodeids. + */ handle = gen_new_kid(ht, htid); + } if (tb[TCA_U32_SEL] == NULL) { NL_SET_ERR_MSG_MOD(extack, "Selector not specified"); diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index af85a73c4c54..6fdba069f6bf 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -568,7 +568,7 @@ META_COLLECTOR(int_sk_rcvtimeo) *err = -1; return; } - dst->value = sk->sk_rcvtimeo / HZ; + dst->value = READ_ONCE(sk->sk_rcvtimeo) / HZ; } META_COLLECTOR(int_sk_sndtimeo) @@ -579,7 +579,7 @@ META_COLLECTOR(int_sk_sndtimeo) *err = -1; return; } - dst->value = sk->sk_sndtimeo / HZ; + dst->value = READ_ONCE(sk->sk_sndtimeo) / HZ; } META_COLLECTOR(int_sk_sendmsg_off) diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 717ae51d94a0..8c9cfff7fd05 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -1015,6 +1015,11 @@ static const struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { TC_FP_PREEMPTIBLE), }; +static struct netlink_range_validation_signed taprio_cycle_time_range = { + .min = 0, + .max = INT_MAX, +}; + static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = { [TCA_TAPRIO_ATTR_PRIOMAP] = { .len = sizeof(struct tc_mqprio_qopt) @@ -1023,7 +1028,8 @@ static const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = { [TCA_TAPRIO_ATTR_SCHED_BASE_TIME] = { .type = NLA_S64 }, [TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY] = { .type = NLA_NESTED }, [TCA_TAPRIO_ATTR_SCHED_CLOCKID] = { .type = NLA_S32 }, - [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME] = { .type = NLA_S64 }, + [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME] = + NLA_POLICY_FULL_RANGE_SIGNED(NLA_S64, &taprio_cycle_time_range), [TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION] = { .type = NLA_S64 }, [TCA_TAPRIO_ATTR_FLAGS] = { .type = NLA_U32 }, [TCA_TAPRIO_ATTR_TXTIME_DELAY] = { .type = NLA_U32 }, @@ -1159,6 +1165,11 @@ static int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb, return -EINVAL; } + if (cycle < 0 || cycle > INT_MAX) { + NL_SET_ERR_MSG(extack, "'cycle_time' is too big"); + return -EINVAL; + } + new->cycle_time = cycle; } @@ -1347,7 +1358,7 @@ static void setup_txtime(struct taprio_sched *q, struct sched_gate_list *sched, ktime_t base) { struct sched_entry *entry; - u32 interval = 0; + u64 interval = 0; list_for_each_entry(entry, &sched->entries, list) { entry->next_txtime = ktime_add_ns(base, interval); diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index a7f887d91d89..f5834af5fad5 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -378,8 +378,8 @@ static struct sock *smc_sock_alloc(struct net *net, struct socket *sock, sk->sk_state = SMC_INIT; sk->sk_destruct = smc_destruct; sk->sk_protocol = protocol; - WRITE_ONCE(sk->sk_sndbuf, READ_ONCE(net->smc.sysctl_wmem)); - WRITE_ONCE(sk->sk_rcvbuf, READ_ONCE(net->smc.sysctl_rmem)); + WRITE_ONCE(sk->sk_sndbuf, 2 * READ_ONCE(net->smc.sysctl_wmem)); + WRITE_ONCE(sk->sk_rcvbuf, 2 * READ_ONCE(net->smc.sysctl_rmem)); smc = smc_sk(sk); INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work); INIT_WORK(&smc->connect_work, smc_connect_work); @@ -436,24 +436,9 @@ out: return rc; } -static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, - unsigned long mask) -{ - /* options we don't get control via setsockopt for */ - nsk->sk_type = osk->sk_type; - nsk->sk_sndbuf = osk->sk_sndbuf; - nsk->sk_rcvbuf = osk->sk_rcvbuf; - nsk->sk_sndtimeo = osk->sk_sndtimeo; - nsk->sk_rcvtimeo = osk->sk_rcvtimeo; - nsk->sk_mark = osk->sk_mark; - nsk->sk_priority = osk->sk_priority; - nsk->sk_rcvlowat = osk->sk_rcvlowat; - nsk->sk_bound_dev_if = osk->sk_bound_dev_if; - nsk->sk_err = osk->sk_err; - - nsk->sk_flags &= ~mask; - nsk->sk_flags |= osk->sk_flags & mask; -} +/* copy only relevant settings and flags of SOL_SOCKET level from smc to + * clc socket (since smc is not called for these options from net/core) + */ #define SK_FLAGS_SMC_TO_CLC ((1UL << SOCK_URGINLINE) | \ (1UL << SOCK_KEEPOPEN) | \ @@ -470,9 +455,55 @@ static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, (1UL << SOCK_NOFCS) | \ (1UL << SOCK_FILTER_LOCKED) | \ (1UL << SOCK_TSTAMP_NEW)) -/* copy only relevant settings and flags of SOL_SOCKET level from smc to - * clc socket (since smc is not called for these options from net/core) - */ + +/* if set, use value set by setsockopt() - else use IPv4 or SMC sysctl value */ +static void smc_adjust_sock_bufsizes(struct sock *nsk, struct sock *osk, + unsigned long mask) +{ + struct net *nnet = sock_net(nsk); + + nsk->sk_userlocks = osk->sk_userlocks; + if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) { + nsk->sk_sndbuf = osk->sk_sndbuf; + } else { + if (mask == SK_FLAGS_SMC_TO_CLC) + WRITE_ONCE(nsk->sk_sndbuf, + READ_ONCE(nnet->ipv4.sysctl_tcp_wmem[1])); + else + WRITE_ONCE(nsk->sk_sndbuf, + 2 * READ_ONCE(nnet->smc.sysctl_wmem)); + } + if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) { + nsk->sk_rcvbuf = osk->sk_rcvbuf; + } else { + if (mask == SK_FLAGS_SMC_TO_CLC) + WRITE_ONCE(nsk->sk_rcvbuf, + READ_ONCE(nnet->ipv4.sysctl_tcp_rmem[1])); + else + WRITE_ONCE(nsk->sk_rcvbuf, + 2 * READ_ONCE(nnet->smc.sysctl_rmem)); + } +} + +static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, + unsigned long mask) +{ + /* options we don't get control via setsockopt for */ + nsk->sk_type = osk->sk_type; + nsk->sk_sndtimeo = osk->sk_sndtimeo; + nsk->sk_rcvtimeo = osk->sk_rcvtimeo; + nsk->sk_mark = READ_ONCE(osk->sk_mark); + nsk->sk_priority = osk->sk_priority; + nsk->sk_rcvlowat = osk->sk_rcvlowat; + nsk->sk_bound_dev_if = osk->sk_bound_dev_if; + nsk->sk_err = osk->sk_err; + + nsk->sk_flags &= ~mask; + nsk->sk_flags |= osk->sk_flags & mask; + + smc_adjust_sock_bufsizes(nsk, osk, mask); +} + static void smc_copy_sock_settings_to_clc(struct smc_sock *smc) { smc_copy_sock_settings(smc->clcsock->sk, &smc->sk, SK_FLAGS_SMC_TO_CLC); @@ -2479,8 +2510,6 @@ static void smc_tcp_listen_work(struct work_struct *work) sock_hold(lsk); /* sock_put in smc_listen_work */ INIT_WORK(&new_smc->smc_listen_work, smc_listen_work); smc_copy_sock_settings_to_smc(new_smc); - new_smc->sk.sk_sndbuf = lsmc->sk.sk_sndbuf; - new_smc->sk.sk_rcvbuf = lsmc->sk.sk_rcvbuf; sock_hold(&new_smc->sk); /* sock_put in passive closing */ if (!queue_work(smc_hs_wq, &new_smc->smc_listen_work)) sock_put(&new_smc->sk); diff --git a/net/smc/smc.h b/net/smc/smc.h index 2eeea4cdc718..1f2b912c43d1 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -161,7 +161,7 @@ struct smc_connection { struct smc_buf_desc *sndbuf_desc; /* send buffer descriptor */ struct smc_buf_desc *rmb_desc; /* RMBE descriptor */ - int rmbe_size_short;/* compressed notation */ + int rmbe_size_comp; /* compressed notation */ int rmbe_update_limit; /* lower limit for consumer * cursor update diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index b9b8b07aa702..c90d9e5dda54 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -1007,7 +1007,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, clc->d0.gid = conn->lgr->smcd->ops->get_local_gid(conn->lgr->smcd); clc->d0.token = conn->rmb_desc->token; - clc->d0.dmbe_size = conn->rmbe_size_short; + clc->d0.dmbe_size = conn->rmbe_size_comp; clc->d0.dmbe_idx = 0; memcpy(&clc->d0.linkid, conn->lgr->id, SMC_LGR_ID_SIZE); if (version == SMC_V1) { @@ -1050,7 +1050,7 @@ static int smc_clc_send_confirm_accept(struct smc_sock *smc, clc->r0.qp_mtu = min(link->path_mtu, link->peer_mtu); break; } - clc->r0.rmbe_size = conn->rmbe_size_short; + clc->r0.rmbe_size = conn->rmbe_size_comp; clc->r0.rmb_dma_addr = conn->rmb_desc->is_vm ? cpu_to_be64((uintptr_t)conn->rmb_desc->cpu_addr) : cpu_to_be64((u64)sg_dma_address diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 3f465faf2b68..6b78075404d7 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -2309,31 +2309,30 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb) struct smc_connection *conn = &smc->conn; struct smc_link_group *lgr = conn->lgr; struct list_head *buf_list; - int bufsize, bufsize_short; + int bufsize, bufsize_comp; struct rw_semaphore *lock; /* lock buffer list */ bool is_dgraded = false; - int sk_buf_size; if (is_rmb) /* use socket recv buffer size (w/o overhead) as start value */ - sk_buf_size = smc->sk.sk_rcvbuf; + bufsize = smc->sk.sk_rcvbuf / 2; else /* use socket send buffer size (w/o overhead) as start value */ - sk_buf_size = smc->sk.sk_sndbuf; + bufsize = smc->sk.sk_sndbuf / 2; - for (bufsize_short = smc_compress_bufsize(sk_buf_size, is_smcd, is_rmb); - bufsize_short >= 0; bufsize_short--) { + for (bufsize_comp = smc_compress_bufsize(bufsize, is_smcd, is_rmb); + bufsize_comp >= 0; bufsize_comp--) { if (is_rmb) { lock = &lgr->rmbs_lock; - buf_list = &lgr->rmbs[bufsize_short]; + buf_list = &lgr->rmbs[bufsize_comp]; } else { lock = &lgr->sndbufs_lock; - buf_list = &lgr->sndbufs[bufsize_short]; + buf_list = &lgr->sndbufs[bufsize_comp]; } - bufsize = smc_uncompress_bufsize(bufsize_short); + bufsize = smc_uncompress_bufsize(bufsize_comp); /* check for reusable slot in the link group */ - buf_desc = smc_buf_get_slot(bufsize_short, lock, buf_list); + buf_desc = smc_buf_get_slot(bufsize_comp, lock, buf_list); if (buf_desc) { buf_desc->is_dma_need_sync = 0; SMC_STAT_RMB_SIZE(smc, is_smcd, is_rmb, bufsize); @@ -2377,8 +2376,8 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb) if (is_rmb) { conn->rmb_desc = buf_desc; - conn->rmbe_size_short = bufsize_short; - smc->sk.sk_rcvbuf = bufsize; + conn->rmbe_size_comp = bufsize_comp; + smc->sk.sk_rcvbuf = bufsize * 2; atomic_set(&conn->bytes_to_rcv, 0); conn->rmbe_update_limit = smc_rmb_wnd_update_limit(buf_desc->len); @@ -2386,7 +2385,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb) smc_ism_set_conn(conn); /* map RMB/smcd_dev to conn */ } else { conn->sndbuf_desc = buf_desc; - smc->sk.sk_sndbuf = bufsize; + smc->sk.sk_sndbuf = bufsize * 2; atomic_set(&conn->sndbuf_space, bufsize); } return 0; diff --git a/net/smc/smc_sysctl.c b/net/smc/smc_sysctl.c index b6f79fabb9d3..0b2a957ca5f5 100644 --- a/net/smc/smc_sysctl.c +++ b/net/smc/smc_sysctl.c @@ -21,6 +21,10 @@ static int min_sndbuf = SMC_BUF_MIN_SIZE; static int min_rcvbuf = SMC_BUF_MIN_SIZE; +static int max_sndbuf = INT_MAX / 2; +static int max_rcvbuf = INT_MAX / 2; +static const int net_smc_wmem_init = (64 * 1024); +static const int net_smc_rmem_init = (64 * 1024); static struct ctl_table smc_table[] = { { @@ -53,6 +57,7 @@ static struct ctl_table smc_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = &min_sndbuf, + .extra2 = &max_sndbuf, }, { .procname = "rmem", @@ -61,6 +66,7 @@ static struct ctl_table smc_table[] = { .mode = 0644, .proc_handler = proc_dointvec_minmax, .extra1 = &min_rcvbuf, + .extra2 = &max_rcvbuf, }, { } }; @@ -88,8 +94,8 @@ int __net_init smc_sysctl_net_init(struct net *net) net->smc.sysctl_autocorking_size = SMC_AUTOCORKING_DEFAULT_SIZE; net->smc.sysctl_smcr_buf_type = SMCR_PHYS_CONT_BUFS; net->smc.sysctl_smcr_testlink_time = SMC_LLC_TESTLINK_DEFAULT_TIME; - WRITE_ONCE(net->smc.sysctl_wmem, READ_ONCE(net->ipv4.sysctl_tcp_wmem[1])); - WRITE_ONCE(net->smc.sysctl_rmem, READ_ONCE(net->ipv4.sysctl_tcp_rmem[1])); + WRITE_ONCE(net->smc.sysctl_wmem, net_smc_wmem_init); + WRITE_ONCE(net->smc.sysctl_rmem, net_smc_rmem_init); return 0; diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 2021fe557e50..529101eb20bd 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -52,6 +52,8 @@ static LIST_HEAD(tls_device_list); static LIST_HEAD(tls_device_down_list); static DEFINE_SPINLOCK(tls_device_lock); +static struct page *dummy_page; + static void tls_device_free_ctx(struct tls_context *ctx) { if (ctx->tx_conf == TLS_HW) { @@ -312,36 +314,33 @@ static int tls_push_record(struct sock *sk, return tls_push_sg(sk, ctx, offload_ctx->sg_tx_data, 0, flags); } -static int tls_device_record_close(struct sock *sk, - struct tls_context *ctx, - struct tls_record_info *record, - struct page_frag *pfrag, - unsigned char record_type) +static void tls_device_record_close(struct sock *sk, + struct tls_context *ctx, + struct tls_record_info *record, + struct page_frag *pfrag, + unsigned char record_type) { struct tls_prot_info *prot = &ctx->prot_info; - int ret; + struct page_frag dummy_tag_frag; /* append tag * device will fill in the tag, we just need to append a placeholder * use socket memory to improve coalescing (re-using a single buffer * increases frag count) - * if we can't allocate memory now, steal some back from data + * if we can't allocate memory now use the dummy page */ - if (likely(skb_page_frag_refill(prot->tag_size, pfrag, - sk->sk_allocation))) { - ret = 0; - tls_append_frag(record, pfrag, prot->tag_size); - } else { - ret = prot->tag_size; - if (record->len <= prot->overhead_size) - return -ENOMEM; + if (unlikely(pfrag->size - pfrag->offset < prot->tag_size) && + !skb_page_frag_refill(prot->tag_size, pfrag, sk->sk_allocation)) { + dummy_tag_frag.page = dummy_page; + dummy_tag_frag.offset = 0; + pfrag = &dummy_tag_frag; } + tls_append_frag(record, pfrag, prot->tag_size); /* fill prepend */ tls_fill_prepend(ctx, skb_frag_address(&record->frags[0]), record->len - prot->overhead_size, record_type); - return ret; } static int tls_create_new_record(struct tls_offload_context_tx *offload_ctx, @@ -541,18 +540,8 @@ last_record: if (done || record->len >= max_open_record_len || (record->num_frags >= MAX_SKB_FRAGS - 1)) { - rc = tls_device_record_close(sk, tls_ctx, record, - pfrag, record_type); - if (rc) { - if (rc > 0) { - size += rc; - } else { - size = orig_size; - destroy_record(record); - ctx->open_record = NULL; - break; - } - } + tls_device_record_close(sk, tls_ctx, record, + pfrag, record_type); rc = tls_push_record(sk, tls_ctx, @@ -1450,14 +1439,26 @@ int __init tls_device_init(void) { int err; - destruct_wq = alloc_workqueue("ktls_device_destruct", 0, 0); - if (!destruct_wq) + dummy_page = alloc_page(GFP_KERNEL); + if (!dummy_page) return -ENOMEM; + destruct_wq = alloc_workqueue("ktls_device_destruct", 0, 0); + if (!destruct_wq) { + err = -ENOMEM; + goto err_free_dummy; + } + err = register_netdevice_notifier(&tls_dev_notifier); if (err) - destroy_workqueue(destruct_wq); + goto err_destroy_wq; + return 0; + +err_destroy_wq: + destroy_workqueue(destruct_wq); +err_free_dummy: + put_page(dummy_page); return err; } @@ -1466,4 +1467,5 @@ void __exit tls_device_cleanup(void) unregister_netdevice_notifier(&tls_dev_notifier); destroy_workqueue(destruct_wq); clean_acked_data_flush(); + put_page(dummy_page); } diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index b6896126bb92..4a8ee2f6badb 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -139,9 +139,6 @@ int tls_push_sg(struct sock *sk, ctx->splicing_pages = true; while (1) { - if (sg_is_last(sg)) - msg.msg_flags = flags; - /* is sending application-limited? */ tcp_rate_check_app_limited(sk); p = sg_page(sg); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 78585217f61a..86930a8ed012 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -790,7 +790,7 @@ static int unix_set_peek_off(struct sock *sk, int val) if (mutex_lock_interruptible(&u->iolock)) return -EINTR; - sk->sk_peek_off = val; + WRITE_ONCE(sk->sk_peek_off, val); mutex_unlock(&u->iolock); return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0da2e6a2a7ea..8bcf8e293308 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5430,8 +5430,11 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) if (!wiphy->mbssid_max_interfaces) return ERR_PTR(-EINVAL); - nla_for_each_nested(nl_elems, attrs, rem_elems) + nla_for_each_nested(nl_elems, attrs, rem_elems) { + if (num_elems >= 255) + return ERR_PTR(-EINVAL); num_elems++; + } elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); if (!elems) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 8bf00caf5d29..0cf1ce7b6934 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -657,7 +657,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp); if (ret) - return ret; + return 0; for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data, ies->len) { diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 31dca4ecb2c5..10ea85c03147 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -505,7 +505,7 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, skb->dev = dev; skb->priority = xs->sk.sk_priority; - skb->mark = xs->sk.sk_mark; + skb->mark = READ_ONCE(xs->sk.sk_mark); skb_shinfo(skb)->destructor_arg = (void *)(long)desc->addr; skb->destructor = xsk_destruct_skb; @@ -994,6 +994,7 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len) err = xp_alloc_tx_descs(xs->pool, xs); if (err) { xp_put_pool(xs->pool); + xs->pool = NULL; sockfd_put(sock); goto out_unlock; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e7617c9959c3..d6b405782b63 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2250,7 +2250,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, match = xfrm_selector_match(&pol->selector, fl, family); if (match) { - if ((sk->sk_mark & pol->mark.m) != pol->mark.v || + if ((READ_ONCE(sk->sk_mark) & pol->mark.m) != pol->mark.v || pol->if_id != if_id) { pol = NULL; goto out; diff --git a/rust/Makefile b/rust/Makefile index 7c9d9f11aec5..4124bfa01798 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -257,7 +257,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fno-partial-inlining -fplugin-arg-arm_ssp_per_task_plugin-% \ -fno-reorder-blocks -fno-allow-store-data-races -fasan-shadow-offset=% \ -fzero-call-used-regs=% -fno-stack-clash-protection \ - -fno-inline-functions-called-once \ + -fno-inline-functions-called-once -fsanitize=bounds-strict \ --param=% --param asan-% # Derived from `scripts/Makefile.clang`. diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 3e601ce2548d..058954961bfc 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -13,5 +13,6 @@ #include <linux/sched.h> /* `bindgen` gets confused at certain things. */ +const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO; diff --git a/rust/kernel/allocator.rs b/rust/kernel/allocator.rs index 397a3dd57a9b..9363b527be66 100644 --- a/rust/kernel/allocator.rs +++ b/rust/kernel/allocator.rs @@ -9,6 +9,36 @@ use crate::bindings; struct KernelAllocator; +/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment. +/// +/// # Safety +/// +/// - `ptr` can be either null or a pointer which has been allocated by this allocator. +/// - `new_layout` must have a non-zero size. +unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gfp_t) -> *mut u8 { + // Customized layouts from `Layout::from_size_align()` can have size < align, so pad first. + let layout = new_layout.pad_to_align(); + + let mut size = layout.size(); + + if layout.align() > bindings::BINDINGS_ARCH_SLAB_MINALIGN { + // The alignment requirement exceeds the slab guarantee, thus try to enlarge the size + // to use the "power-of-two" size/alignment guarantee (see comments in `kmalloc()` for + // more information). + // + // Note that `layout.size()` (after padding) is guaranteed to be a multiple of + // `layout.align()`, so `next_power_of_two` gives enough alignment guarantee. + size = size.next_power_of_two(); + } + + // SAFETY: + // - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the + // function safety requirement. + // - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero + // according to the function safety requirement) or a result from `next_power_of_two()`. + unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 } +} + unsafe impl GlobalAlloc for KernelAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // `krealloc()` is used instead of `kmalloc()` because the latter is @@ -30,10 +60,20 @@ static ALLOCATOR: KernelAllocator = KernelAllocator; // to extract the object file that has them from the archive. For the moment, // let's generate them ourselves instead. // +// Note: Although these are *safe* functions, they are called by the compiler +// with parameters that obey the same `GlobalAlloc` function safety +// requirements: size and align should form a valid layout, and size is +// greater than 0. +// // Note that `#[no_mangle]` implies exported too, nowadays. #[no_mangle] -fn __rust_alloc(size: usize, _align: usize) -> *mut u8 { - unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL) as *mut u8 } +fn __rust_alloc(size: usize, align: usize) -> *mut u8 { + // SAFETY: See assumption above. + let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; + + // SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater + // than 0. + unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) } } #[no_mangle] @@ -42,23 +82,27 @@ fn __rust_dealloc(ptr: *mut u8, _size: usize, _align: usize) { } #[no_mangle] -fn __rust_realloc(ptr: *mut u8, _old_size: usize, _align: usize, new_size: usize) -> *mut u8 { - unsafe { - bindings::krealloc( - ptr as *const core::ffi::c_void, - new_size, - bindings::GFP_KERNEL, - ) as *mut u8 - } +fn __rust_realloc(ptr: *mut u8, _old_size: usize, align: usize, new_size: usize) -> *mut u8 { + // SAFETY: See assumption above. + let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, align) }; + + // SAFETY: Per assumption above, `ptr` is allocated by `__rust_*` before, and the size of + // `new_layout` is greater than 0. + unsafe { krealloc_aligned(ptr, new_layout, bindings::GFP_KERNEL) } } #[no_mangle] -fn __rust_alloc_zeroed(size: usize, _align: usize) -> *mut u8 { +fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 { + // SAFETY: See assumption above. + let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; + + // SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater + // than 0. unsafe { - bindings::krealloc( - core::ptr::null(), - size, + krealloc_aligned( + ptr::null_mut(), + layout, bindings::GFP_KERNEL | bindings::__GFP_ZERO, - ) as *mut u8 + ) } } diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index a89843cacaad..172f563976a9 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -243,8 +243,7 @@ impl<T: 'static> ForeignOwnable for Arc<T> { let inner = NonNull::new(ptr as *mut ArcInner<T>).unwrap(); // SAFETY: The safety requirements of `from_foreign` ensure that the object remains alive - // for the lifetime of the returned value. Additionally, the safety requirements of - // `ForeignOwnable::borrow_mut` ensure that no new mutable references are created. + // for the lifetime of the returned value. unsafe { ArcBorrow::new(inner) } } diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index 1e5380b16ed5..d479f8da8f38 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -35,34 +35,16 @@ pub trait ForeignOwnable: Sized { /// /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. - /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow_mut`] - /// for this object must have been dropped. unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Self::Borrowed<'a>; - /// Mutably borrows a foreign-owned object. - /// - /// # Safety - /// - /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for - /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. - /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] and - /// [`ForeignOwnable::borrow_mut`] for this object must have been dropped. - unsafe fn borrow_mut(ptr: *const core::ffi::c_void) -> ScopeGuard<Self, fn(Self)> { - // SAFETY: The safety requirements ensure that `ptr` came from a previous call to - // `into_foreign`. - ScopeGuard::new_with_data(unsafe { Self::from_foreign(ptr) }, |d| { - d.into_foreign(); - }) - } - /// Converts a foreign-owned object back to a Rust-owned one. /// /// # Safety /// /// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for /// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet. - /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] and - /// [`ForeignOwnable::borrow_mut`] for this object must have been dropped. + /// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] for + /// this object must have been dropped. unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self; } diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 16c87938b316..653b92f6d4c8 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -129,6 +129,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len) ssize_t readlen; struct sym_entry *sym; + errno = 0; readlen = getline(buf, buf_len, in); if (readlen < 0) { if (errno) { diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index b72b82bb20c6..b348e1679d5d 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -9,7 +9,7 @@ #include <linux/sysctl.h> #include "internal.h" -struct ctl_table key_sysctls[] = { +static struct ctl_table key_sysctls[] = { { .procname = "maxkeys", .data = &key_quota_maxkeys, diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c index b0262541802a..c8db6c75d133 100644 --- a/sound/drivers/serial-generic.c +++ b/sound/drivers/serial-generic.c @@ -366,7 +366,7 @@ MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids); static struct serdev_device_driver snd_serial_generic_driver = { .driver = { .name = "snd-serial-generic", - .of_match_table = of_match_ptr(snd_serial_generic_dt_ids), + .of_match_table = snd_serial_generic_dt_ids, }, .probe = snd_serial_generic_probe, }; diff --git a/sound/soc/adi/axi-i2s.c b/sound/soc/adi/axi-i2s.c index d5b6f5187f8e..7b2563075743 100644 --- a/sound/soc/adi/axi-i2s.c +++ b/sound/soc/adi/axi-i2s.c @@ -147,6 +147,7 @@ static int axi_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axi_i2s_dai_ops = { + .probe = axi_i2s_dai_probe, .startup = axi_i2s_startup, .shutdown = axi_i2s_shutdown, .trigger = axi_i2s_trigger, @@ -154,7 +155,6 @@ static const struct snd_soc_dai_ops axi_i2s_dai_ops = { }; static struct snd_soc_dai_driver axi_i2s_dai = { - .probe = axi_i2s_dai_probe, .ops = &axi_i2s_dai_ops, .symmetric_rate = 1, }; diff --git a/sound/soc/adi/axi-spdif.c b/sound/soc/adi/axi-spdif.c index e4c99bbc9cdd..10545bd99704 100644 --- a/sound/soc/adi/axi-spdif.c +++ b/sound/soc/adi/axi-spdif.c @@ -148,6 +148,7 @@ static void axi_spdif_shutdown(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops axi_spdif_dai_ops = { + .probe = axi_spdif_dai_probe, .startup = axi_spdif_startup, .shutdown = axi_spdif_shutdown, .trigger = axi_spdif_trigger, @@ -155,7 +156,6 @@ static const struct snd_soc_dai_ops axi_spdif_dai_ops = { }; static struct snd_soc_dai_driver axi_spdif_dai = { - .probe = axi_spdif_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 7464ca2b596c..9e3133bec2b1 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -28,6 +28,21 @@ #define RT5682_PLL_FREQ (48000 * 512) static struct snd_soc_jack cz_jack; +static struct snd_soc_jack_pin cz_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, +}; + static struct clk *da7219_dai_wclk; static struct clk *da7219_dai_bclk; static struct clk *rt5682_dai_wclk; @@ -66,11 +81,13 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) if (IS_ERR(da7219_dai_bclk)) return PTR_ERR(da7219_dai_bclk); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -146,11 +163,13 @@ static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd) if (IS_ERR(rt5682_dai_bclk)) return PTR_ERR(rt5682_dai_bclk); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; @@ -631,6 +650,7 @@ static struct snd_soc_dai_link cz_dai_5682_98357[] = { static const struct snd_soc_dapm_widget cz_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), }; @@ -654,6 +674,7 @@ static const struct snd_soc_dapm_route cz_rt5682_audio_route[] = { static const struct snd_kcontrol_new cz_mc_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"), }; diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c index 532aa98a2241..c8ed1e0b1ccd 100644 --- a/sound/soc/amd/acp-rt5645.c +++ b/sound/soc/amd/acp-rt5645.c @@ -42,6 +42,16 @@ #define CZ_PLAT_CLK 24000000 static struct snd_soc_jack cz_jack; +static struct snd_soc_jack_pin cz_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static int cz_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -76,11 +86,13 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd) codec = asoc_rtd_to_codec(rtd, 0)->component; card = rtd->card; - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &cz_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, + cz_jack_pins, + ARRAY_SIZE(cz_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 09dc5f2c0bfc..df350014966a 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -539,17 +539,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d return 0; } -const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { - .startup = acp_i2s_startup, - .hw_params = acp_i2s_hwparams, - .prepare = acp_i2s_prepare, - .trigger = acp_i2s_trigger, - .set_fmt = acp_i2s_set_fmt, - .set_tdm_slot = acp_i2s_set_tdm_slot, -}; -EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); - -int asoc_acp_i2s_probe(struct snd_soc_dai *dai) +static int acp_i2s_probe(struct snd_soc_dai *dai) { struct device *dev = dai->component->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); @@ -569,7 +559,17 @@ int asoc_acp_i2s_probe(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_NS_GPL(asoc_acp_i2s_probe, SND_SOC_ACP_COMMON); + +const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = { + .probe = acp_i2s_probe, + .startup = acp_i2s_startup, + .hw_params = acp_i2s_hwparams, + .prepare = acp_i2s_prepare, + .trigger = acp_i2s_trigger, + .set_fmt = acp_i2s_set_fmt, + .set_tdm_slot = acp_i2s_set_tdm_slot, +}; +EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON); MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS(DRV_NAME); diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c index 676ad50638d0..6d57d17ddfd7 100644 --- a/sound/soc/amd/acp/acp-legacy-mach.c +++ b/sound/soc/amd/acp/acp-legacy-mach.c @@ -75,23 +75,6 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = { .tdm_mode = false, }; -static const struct snd_kcontrol_new acp_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Spk"), - SOC_DAPM_PIN_SWITCH("Left Spk"), - SOC_DAPM_PIN_SWITCH("Right Spk"), - -}; - -static const struct snd_soc_dapm_widget acp_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), -}; - static int acp_asoc_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -110,11 +93,8 @@ static int acp_asoc_probe(struct platform_device *pdev) card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->dapm_widgets = acp_widgets; - card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); - card->controls = acp_controls; - card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + /* Widgets and controls added per-codec in acp-mach-common.c */ acp_card_drvdata = card->drvdata; dmi_id = dmi_first_match(acp_quirk_table); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index ff5cbc4a6427..a06af82b8056 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -28,7 +28,6 @@ #include "../../codecs/nau8821.h" #include "acp-mach.h" -static struct snd_soc_jack vg_headset; #define PCO_PLAT_CLK 48000000 #define RT5682_PLL_FREQ (48000 * 512) #define DUAL_CHANNEL 2 @@ -52,8 +51,6 @@ const struct dmi_system_id acp_quirk_table[] = { }; EXPORT_SYMBOL_GPL(acp_quirk_table); -static struct snd_soc_jack pco_jack; - static const unsigned int channels[] = { DUAL_CHANNEL, }; @@ -87,6 +84,28 @@ static int acp_clk_enable(struct acp_card_drvdata *drvdata, SND_SOC_DAILINK_DEF(rt5682, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1"))); +static struct snd_soc_jack rt5682_jack; +static struct snd_soc_jack_pin rt5682_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new rt5682_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget rt5682_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route rt5682_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -110,22 +129,38 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd) drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk"); drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_dapm_new_controls(&card->dapm, rt5682_widgets, + ARRAY_SIZE(rt5682_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt5682_controls, + ARRAY_SIZE(rt5682_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rt5682_jack, + rt5682_jack_pins, + ARRAY_SIZE(rt5682_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &rt5682_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -275,6 +310,28 @@ static const struct snd_soc_ops acp_card_rt5682_ops = { SND_SOC_DAILINK_DEF(rt5682s, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1"))); +static struct snd_soc_jack rt5682s_jack; +static struct snd_soc_jack_pin rt5682s_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new rt5682s_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget rt5682s_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route rt5682s_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -299,22 +356,38 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd) drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk"); } - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_dapm_new_controls(&card->dapm, rt5682s_widgets, + ARRAY_SIZE(rt5682s_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt5682s_controls, + ARRAY_SIZE(rt5682s_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &rt5682s_jack, + rt5682s_jack_pins, + ARRAY_SIZE(rt5682s_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &rt5682s_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -440,6 +513,16 @@ SND_SOC_DAILINK_DEF(rt1019, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"), COMP_CODEC("i2c-10EC1019:01", "rt1019-aif"))); +static const struct snd_kcontrol_new rt1019_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget rt1019_widgets[] = { + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), +}; + static const struct snd_soc_dapm_route rt1019_map_lr[] = { { "Left Spk", NULL, "Left SPO" }, { "Right Spk", NULL, "Right SPO" }, @@ -460,10 +543,25 @@ static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; + int ret; if (drvdata->amp_codec_id != RT1019) return -EINVAL; + ret = snd_soc_dapm_new_controls(&card->dapm, rt1019_widgets, + ARRAY_SIZE(rt1019_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, rt1019_controls, + ARRAY_SIZE(rt1019_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + return snd_soc_dapm_add_routes(&rtd->card->dapm, rt1019_map_lr, ARRAY_SIZE(rt1019_map_lr)); } @@ -594,6 +692,14 @@ static const struct snd_soc_ops acp_card_rt1019_ops = { SND_SOC_DAILINK_DEF(max98360a, DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi"))); +static const struct snd_kcontrol_new max98360a_controls[] = { + SOC_DAPM_PIN_SWITCH("Spk"), +}; + +static const struct snd_soc_dapm_widget max98360a_widgets[] = { + SND_SOC_DAPM_SPK("Spk", NULL), +}; + static const struct snd_soc_dapm_route max98360a_map[] = { {"Spk", NULL, "Speaker"}, }; @@ -602,10 +708,25 @@ static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; struct acp_card_drvdata *drvdata = card->drvdata; + int ret; if (drvdata->amp_codec_id != MAX98360A) return -EINVAL; + ret = snd_soc_dapm_new_controls(&card->dapm, max98360a_widgets, + ARRAY_SIZE(max98360a_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, max98360a_controls, + ARRAY_SIZE(max98360a_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + return snd_soc_dapm_add_routes(&rtd->card->dapm, max98360a_map, ARRAY_SIZE(max98360a_map)); } @@ -671,13 +792,19 @@ SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"), COMP_CODEC("i2c-ADS8388:01", "max98388-aif1"))); +static const struct snd_kcontrol_new max98388_controls[] = { + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + static const struct snd_soc_dapm_widget max98388_widgets[] = { - SND_SOC_DAPM_SPK("SPK", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), }; static const struct snd_soc_dapm_route max98388_map[] = { - { "SPK", NULL, "Left BE_OUT" }, - { "SPK", NULL, "Right BE_OUT" }, + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, }; static struct snd_soc_codec_conf max98388_conf[] = { @@ -730,6 +857,14 @@ static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd) /* Don't need to add routes if widget addition failed */ return ret; } + + ret = snd_soc_add_card_controls(card, max98388_controls, + ARRAY_SIZE(max98388_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + return snd_soc_dapm_add_routes(&rtd->card->dapm, max98388_map, ARRAY_SIZE(max98388_map)); } @@ -762,6 +897,28 @@ static const struct snd_soc_ops acp_max98388_ops = { SND_SOC_DAILINK_DEF(nau8825, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi"))); +static struct snd_soc_jack nau8825_jack; +static struct snd_soc_jack_pin nau8825_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const struct snd_soc_dapm_widget nau8825_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + static const struct snd_soc_dapm_route nau8825_map[] = { { "Headphone Jack", NULL, "HPOL" }, { "Headphone Jack", NULL, "HPOR" }, @@ -780,22 +937,38 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd) if (drvdata->hs_codec_id != NAU8825) return -EINVAL; - ret = snd_soc_card_jack_new(card, "Headset Jack", + ret = snd_soc_dapm_new_controls(&card->dapm, nau8825_widgets, + ARRAY_SIZE(nau8825_widgets)); + if (ret) { + dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, nau8825_controls, + ARRAY_SIZE(nau8825_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + &nau8825_jack, + nau8825_jack_pins, + ARRAY_SIZE(nau8825_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - ret = snd_soc_component_set_jack(component, &pco_jack, NULL); + ret = snd_soc_component_set_jack(component, &nau8825_jack, NULL); if (ret) { dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret); return ret; @@ -921,8 +1094,25 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } +static struct snd_soc_jack nau8821_jack; +static struct snd_soc_jack_pin nau8821_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static const struct snd_kcontrol_new nau8821_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static const struct snd_soc_dapm_widget nau8821_widgets[] = { - SND_SOC_DAPM_HP("Headphone jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, @@ -932,12 +1122,12 @@ static const struct snd_soc_dapm_widget nau8821_widgets[] = { static const struct snd_soc_dapm_route nau8821_audio_route[] = { /* HP jack connectors - unknown if we have jack detection */ - { "Headphone jack", NULL, "HPOL" }, - { "Headphone jack", NULL, "HPOR" }, + { "Headphone Jack", NULL, "HPOL" }, + { "Headphone Jack", NULL, "HPOR" }, { "MICL", NULL, "Headset Mic" }, { "MICR", NULL, "Headset Mic" }, { "DMIC", NULL, "Int Mic" }, - { "Headphone jack", NULL, "Platform Clock" }, + { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, { "Int Mic", NULL, "Platform Clock" }, }; @@ -966,21 +1156,31 @@ static int acp_8821_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &vg_headset); + ret = snd_soc_add_card_controls(card, nau8821_controls, + ARRAY_SIZE(nau8821_controls)); + if (ret) { + dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &nau8821_jack, + nau8821_jack_pins, + ARRAY_SIZE(nau8821_jack_pins)); if (ret) { dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; } - snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); - nau8821_enable_jack_detect(component, &vg_headset); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + nau8821_enable_jack_detect(component, &nau8821_jack); return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8821_audio_route, ARRAY_SIZE(nau8821_audio_route)); diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index cc8284f417c0..1bf7b2e68a11 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -98,7 +98,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-bt", @@ -124,7 +123,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-hs", @@ -150,7 +148,6 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-pdm-dmic", diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 1899658ab25d..54235cad9cc9 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -97,7 +97,6 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-i2s-bt", @@ -123,7 +122,6 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { .rate_max = 48000, }, .ops = &asoc_acp_cpu_dai_ops, - .probe = &asoc_acp_i2s_probe, }, { .name = "acp-pdm-dmic", diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index a1c893f33f74..5223033a122f 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -94,22 +94,6 @@ static struct acp_card_drvdata sof_nau8821_max98388_data = { .tdm_mode = false, }; -static const struct snd_kcontrol_new acp_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone Jack"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Spk"), - SOC_DAPM_PIN_SWITCH("Left Spk"), - SOC_DAPM_PIN_SWITCH("Right Spk"), -}; - -static const struct snd_soc_dapm_widget acp_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_SPK("Spk", NULL), - SND_SOC_DAPM_SPK("Left Spk", NULL), - SND_SOC_DAPM_SPK("Right Spk", NULL), -}; - static int acp_sof_probe(struct platform_device *pdev) { struct snd_soc_card *card = NULL; @@ -128,11 +112,8 @@ static int acp_sof_probe(struct platform_device *pdev) card->dev = dev; card->owner = THIS_MODULE; card->name = pdev->id_entry->name; - card->dapm_widgets = acp_widgets; - card->num_dapm_widgets = ARRAY_SIZE(acp_widgets); - card->controls = acp_controls; - card->num_controls = ARRAY_SIZE(acp_controls); card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data; + /* Widgets and controls added per-codec in acp-mach-common.c */ acp_card_drvdata = card->drvdata; dmi_id = dmi_first_match(acp_quirk_table); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 2ebe2099cbb5..d6cfae6ec5f7 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -198,7 +198,6 @@ union acp_i2stdm_mstrclkgen { extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops; extern const struct snd_soc_dai_ops acp_dmic_dai_ops; -int asoc_acp_i2s_probe(struct snd_soc_dai *dai); int acp_platform_register(struct device *dev); int acp_platform_unregister(struct device *dev); diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index 0543dda75b99..28ad5f5b9a76 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -28,6 +28,17 @@ #define DUAL_CHANNEL 2 static struct snd_soc_jack pco_jack; +static struct snd_soc_jack_pin pco_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static struct clk *rt5682_dai_wclk; static struct clk *rt5682_dai_bclk; static struct gpio_desc *dmic_sel; @@ -86,11 +97,13 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd) rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk"); rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk"); - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pco_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pco_jack, + pco_jack_pins, + ARRAY_SIZE(pco_jack_pins)); if (ret) { dev_err(card->dev, "HP jack creation failed %d\n", ret); return ret; diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index 125a8e93478d..eda464545866 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -170,6 +170,9 @@ static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME); int ret, bclk; + if (!dai) + return -EINVAL; + ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN); if (ret < 0) dev_err(card->dev, "can't set FS clock %d\n", ret); diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c index 69a88dc65165..6c20c643f321 100644 --- a/sound/soc/atmel/atmel-i2s.c +++ b/sound/soc/atmel/atmel-i2s.c @@ -532,13 +532,6 @@ static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return err; } -static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { - .prepare = atmel_i2s_prepare, - .trigger = atmel_i2s_trigger, - .hw_params = atmel_i2s_hw_params, - .set_fmt = atmel_i2s_set_dai_fmt, -}; - static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) { struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -547,8 +540,15 @@ static int atmel_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops atmel_i2s_dai_ops = { + .probe = atmel_i2s_dai_probe, + .prepare = atmel_i2s_prepare, + .trigger = atmel_i2s_trigger, + .hw_params = atmel_i2s_hw_params, + .set_fmt = atmel_i2s_set_dai_fmt, +}; + static struct snd_soc_dai_driver atmel_i2s_dai = { - .probe = atmel_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -730,7 +730,7 @@ static void atmel_i2s_remove(struct platform_device *pdev) static struct platform_driver atmel_i2s_driver = { .driver = { .name = "atmel_i2s", - .of_match_table = of_match_ptr(atmel_i2s_dt_ids), + .of_match_table = atmel_i2s_dt_ids, }, .probe = atmel_i2s_probe, .remove_new = atmel_i2s_remove, diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c index efcbdd1a629f..0db7815d230c 100644 --- a/sound/soc/atmel/atmel-pdmic.c +++ b/sound/soc/atmel/atmel-pdmic.c @@ -690,7 +690,7 @@ unregister_codec: static struct platform_driver atmel_pdmic_driver = { .driver = { .name = "atmel-pdmic", - .of_match_table = of_match_ptr(atmel_pdmic_of_match), + .of_match_table = atmel_pdmic_of_match, .pm = &snd_soc_pm_ops, }, .probe = atmel_pdmic_probe, diff --git a/sound/soc/atmel/mchp-i2s-mcc.c b/sound/soc/atmel/mchp-i2s-mcc.c index 7c83d48ca1a0..25ed0b953bfd 100644 --- a/sound/soc/atmel/mchp-i2s-mcc.c +++ b/sound/soc/atmel/mchp-i2s-mcc.c @@ -870,17 +870,6 @@ static int mchp_i2s_mcc_startup(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { - .set_sysclk = mchp_i2s_mcc_set_sysclk, - .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio, - .startup = mchp_i2s_mcc_startup, - .trigger = mchp_i2s_mcc_trigger, - .hw_params = mchp_i2s_mcc_hw_params, - .hw_free = mchp_i2s_mcc_hw_free, - .set_fmt = mchp_i2s_mcc_set_dai_fmt, - .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, -}; - static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) { struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -895,6 +884,18 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_i2s_mcc_dai_ops = { + .probe = mchp_i2s_mcc_dai_probe, + .set_sysclk = mchp_i2s_mcc_set_sysclk, + .set_bclk_ratio = mchp_i2s_mcc_set_bclk_ratio, + .startup = mchp_i2s_mcc_startup, + .trigger = mchp_i2s_mcc_trigger, + .hw_params = mchp_i2s_mcc_hw_params, + .hw_free = mchp_i2s_mcc_hw_free, + .set_fmt = mchp_i2s_mcc_set_dai_fmt, + .set_tdm_slot = mchp_i2s_mcc_set_dai_tdm_slot, +}; + #define MCHP_I2SMCC_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_I2SMCC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -906,7 +907,6 @@ static int mchp_i2s_mcc_dai_probe(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver mchp_i2s_mcc_dai = { - .probe = mchp_i2s_mcc_dai_probe, .playback = { .stream_name = "I2SMCC-Playback", .channels_min = 1, @@ -1098,7 +1098,7 @@ static void mchp_i2s_mcc_remove(struct platform_device *pdev) static struct platform_driver mchp_i2s_mcc_driver = { .driver = { .name = "mchp_i2s_mcc", - .of_match_table = of_match_ptr(mchp_i2s_mcc_dt_ids), + .of_match_table = mchp_i2s_mcc_dt_ids, }, .probe = mchp_i2s_mcc_probe, .remove_new = mchp_i2s_mcc_remove, diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index 911bc334b41e..afe213a71212 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -698,13 +698,6 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { - .set_fmt = mchp_pdmc_set_fmt, - .startup = mchp_pdmc_startup, - .hw_params = mchp_pdmc_hw_params, - .trigger = mchp_pdmc_trigger, -}; - static int mchp_pdmc_add_chmap_ctls(struct snd_pcm *pcm, struct mchp_pdmc *dd) { struct mchp_pdmc_chmap *info; @@ -757,8 +750,16 @@ static int mchp_pdmc_pcm_new(struct snd_soc_pcm_runtime *rtd, return ret; } +static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = { + .probe = mchp_pdmc_dai_probe, + .set_fmt = mchp_pdmc_set_fmt, + .startup = mchp_pdmc_startup, + .hw_params = mchp_pdmc_hw_params, + .trigger = mchp_pdmc_trigger, + .pcm_new = &mchp_pdmc_pcm_new, +}; + static struct snd_soc_dai_driver mchp_pdmc_dai = { - .probe = mchp_pdmc_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 1, @@ -769,7 +770,6 @@ static struct snd_soc_dai_driver mchp_pdmc_dai = { .formats = SNDRV_PCM_FMTBIT_S24_LE, }, .ops = &mchp_pdmc_dai_ops, - .pcm_new = &mchp_pdmc_pcm_new, }; /* PDMC interrupt handler */ diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index ff6aba143aee..33ce5e54482b 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -503,11 +503,6 @@ unlock: return ret; } -static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { - .trigger = mchp_spdifrx_trigger, - .hw_params = mchp_spdifrx_hw_params, -}; - #define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -1009,10 +1004,15 @@ static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { + .probe = mchp_spdifrx_dai_probe, + .remove = mchp_spdifrx_dai_remove, + .trigger = mchp_spdifrx_trigger, + .hw_params = mchp_spdifrx_hw_params, +}; + static struct snd_soc_dai_driver mchp_spdifrx_dai = { .name = "mchp-spdifrx", - .probe = mchp_spdifrx_dai_probe, - .remove = mchp_spdifrx_dai_remove, .capture = { .stream_name = "S/PDIF Capture", .channels_min = SPDIFRX_CHANNELS, @@ -1197,7 +1197,7 @@ static struct platform_driver mchp_spdifrx_driver = { .remove_new = mchp_spdifrx_remove, .driver = { .name = "mchp_spdifrx", - .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids), + .of_match_table = mchp_spdifrx_dt_ids, .pm = pm_ptr(&mchp_spdifrx_pm_ops), }, }; diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c index 1d3e17119888..a201a96fa690 100644 --- a/sound/soc/atmel/mchp-spdiftx.c +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -516,14 +516,6 @@ static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); } -static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { - .startup = mchp_spdiftx_dai_startup, - .shutdown = mchp_spdiftx_dai_shutdown, - .trigger = mchp_spdiftx_trigger, - .hw_params = mchp_spdiftx_hw_params, - .hw_free = mchp_spdiftx_hw_free, -}; - #define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 #define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -703,9 +695,17 @@ static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { + .probe = mchp_spdiftx_dai_probe, + .startup = mchp_spdiftx_dai_startup, + .shutdown = mchp_spdiftx_dai_shutdown, + .trigger = mchp_spdiftx_trigger, + .hw_params = mchp_spdiftx_hw_params, + .hw_free = mchp_spdiftx_hw_free, +}; + static struct snd_soc_dai_driver mchp_spdiftx_dai = { .name = "mchp-spdiftx", - .probe = mchp_spdiftx_dai_probe, .playback = { .stream_name = "S/PDIF Playback", .channels_min = 1, @@ -891,7 +891,7 @@ static struct platform_driver mchp_spdiftx_driver = { .remove_new = mchp_spdiftx_remove, .driver = { .name = "mchp_spdiftx", - .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids), + .of_match_table = mchp_spdiftx_dt_ids, .pm = pm_ptr(&mchp_spdiftx_pm_ops) }, }; diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c index efead272d92b..c809b121037f 100644 --- a/sound/soc/atmel/tse850-pcm5142.c +++ b/sound/soc/atmel/tse850-pcm5142.c @@ -430,7 +430,7 @@ MODULE_DEVICE_TABLE(of, tse850_dt_ids); static struct platform_driver tse850_driver = { .driver = { .name = "axentia-tse850-pcm5142", - .of_match_table = of_match_ptr(tse850_dt_ids), + .of_match_table = tse850_dt_ids, }, .probe = tse850_probe, .remove_new = tse850_remove, diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c index a11d6841afc2..b0e1a1253e10 100644 --- a/sound/soc/au1x/ac97c.c +++ b/sound/soc/au1x/ac97c.c @@ -195,18 +195,18 @@ static int alchemy_ac97c_startup(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops alchemy_ac97c_ops = { - .startup = alchemy_ac97c_startup, -}; - static int au1xac97c_dai_probe(struct snd_soc_dai *dai) { return ac97c_workdata ? 0 : -ENODEV; } +static const struct snd_soc_dai_ops alchemy_ac97c_ops = { + .probe = au1xac97c_dai_probe, + .startup = alchemy_ac97c_startup, +}; + static struct snd_soc_dai_driver au1xac97c_dai_driver = { .name = "alchemy-ac97c", - .probe = au1xac97c_dai_probe, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index 9fd91aea7d1a..5d50ebc2bdd5 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -333,13 +333,13 @@ static int au1xpsc_ac97_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = { + .probe = au1xpsc_ac97_probe, .startup = au1xpsc_ac97_startup, .trigger = au1xpsc_ac97_trigger, .hw_params = au1xpsc_ac97_hw_params, }; static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = { - .probe = au1xpsc_ac97_probe, .playback = { .rates = AC97_RATES, .formats = AC97_FMTS, diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 85f705afcdbb..9bda6499e66e 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -737,7 +737,19 @@ static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream, bcm2835_i2s_stop_clock(dev); } +static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, + &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { + .probe = bcm2835_i2s_dai_probe, .startup = bcm2835_i2s_startup, .shutdown = bcm2835_i2s_shutdown, .prepare = bcm2835_i2s_prepare, @@ -748,20 +760,8 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; -static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) -{ - struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_init_dma_data(dai, - &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK], - &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]); - - return 0; -} - static struct snd_soc_dai_driver bcm2835_i2s_dai = { .name = "bcm2835-i2s", - .probe = bcm2835_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h index 74152b2d770d..4925e03c3c30 100644 --- a/sound/soc/bcm/cygnus-ssp.h +++ b/sound/soc/bcm/cygnus-ssp.h @@ -117,8 +117,6 @@ struct cygnus_audio { unsigned long vco_rate; }; -extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai); -extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd); extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len); extern int cygnus_soc_platform_register(struct device *dev, diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index afc6b5b570ea..522de4b80293 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -407,6 +407,7 @@ static int ep93xx_i2s_resume(struct snd_soc_component *component) #endif static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { + .probe = ep93xx_i2s_dai_probe, .startup = ep93xx_i2s_startup, .shutdown = ep93xx_i2s_shutdown, .hw_params = ep93xx_i2s_hw_params, @@ -418,7 +419,6 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rate = 1, - .probe = ep93xx_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 3574c68e0dda..d99b674d574b 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -143,7 +143,7 @@ struct pm860x_priv { struct pm860x_det det; int irq[4]; - unsigned char name[4][MAX_NAME_LEN+1]; + unsigned char name[4][MAX_NAME_LEN]; }; /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ @@ -1373,7 +1373,7 @@ static int pm860x_codec_probe(struct platform_device *pdev) return -EINVAL; } pm860x->irq[i] = res->start + chip->irq_base; - strncpy(pm860x->name[i], res->name, MAX_NAME_LEN); + strscpy(pm860x->name[i], res->name, MAX_NAME_LEN); } ret = devm_snd_soc_register_component(&pdev->dev, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 88c3dbe47d71..95b5bd883215 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -15,7 +15,6 @@ config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" depends on COMPILE_TEST imply SND_SOC_88PM860X - imply SND_SOC_L3 imply SND_SOC_AB8500_CODEC imply SND_SOC_AC97_CODEC imply SND_SOC_AD1836 @@ -56,6 +55,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AUDIO_IIO_AUX imply SND_SOC_AW8738 imply SND_SOC_AW88395 + imply SND_SOC_AW88261 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CHV3_CODEC @@ -75,6 +75,8 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS35L56_SDW imply SND_SOC_CS42L42 imply SND_SOC_CS42L42_SDW + imply SND_SOC_CS42L43 + imply SND_SOC_CS42L43_SDW imply SND_SOC_CS42L51_I2C imply SND_SOC_CS42L52 imply SND_SOC_CS42L56 @@ -184,6 +186,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT1015 imply SND_SOC_RT1015P imply SND_SOC_RT1016 + imply SND_SOC_RT1017_SDCA_SDW imply SND_SOC_RT1019 imply SND_SOC_RT1305 imply SND_SOC_RT1308 @@ -266,7 +269,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TWL4030 imply SND_SOC_TWL6040 imply SND_SOC_UDA1334 - imply SND_SOC_UDA134X imply SND_SOC_UDA1380 imply SND_SOC_WCD9335 imply SND_SOC_WCD934X @@ -652,6 +654,20 @@ config SND_SOC_AW88395 digital Smart K audio amplifier with an integrated 10V smart boost convert. +config SND_SOC_AW88261 + tristate "Soc Audio for awinic aw88261" + depends on I2C + select CRC8 + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + This option enables support for aw88261 Smart PA. + The awinic AW88261 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier. The output voltage of + boost converter can be adjusted smartly according to + the input amplitude. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help @@ -801,6 +817,20 @@ config SND_SOC_CS42L42_SDW help Enable support for Cirrus Logic CS42L42 codec with Soundwire control +config SND_SOC_CS42L43 + tristate "Cirrus Logic CS42L43 CODEC" + depends on MFD_CS42L43 + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC. + +config SND_SOC_CS42L43_SDW + tristate "Cirrus Logic CS42L43 CODEC (SoundWire)" + depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC over SoundWire. + config SND_SOC_CS42L51 tristate @@ -978,9 +1008,6 @@ config SND_SOC_JZ4770_CODEC This driver can also be built as a module. If so, the module will be called snd-soc-jz4770-codec. -config SND_SOC_L3 - tristate - config SND_SOC_DA7210 tristate depends on SND_SOC_I2C_AND_SPI @@ -1428,6 +1455,11 @@ config SND_SOC_RT1016 tristate depends on I2C +config SND_SOC_RT1017_SDCA_SDW + tristate "Realtek RT1017 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + config SND_SOC_RT1019 tristate depends on I2C @@ -1932,9 +1964,6 @@ config SND_SOC_UDA1334 and has basic features such as de-emphasis (at 44.1 kHz sampling rate) and mute. -config SND_SOC_UDA134X - tristate - config SND_SOC_UDA1380 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 32dcc6de58bd..c8502a49b40a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -50,6 +50,7 @@ snd-soc-aw8738-objs := aw8738.o snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o +snd-soc-aw88261-objs := aw88261.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-chv3-codec-objs := chv3-codec.o @@ -76,6 +77,8 @@ snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o +snd-soc-cs42l43-objs := cs42l43.o cs42l43-jack.o +snd-soc-cs42l43-sdw-objs := cs42l43-sdw.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -128,7 +131,6 @@ snd-soc-jz4740-codec-objs := jz4740.o snd-soc-jz4725b-codec-objs := jz4725b.o snd-soc-jz4760-codec-objs := jz4760.o snd-soc-jz4770-codec-objs := jz4770.o -snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o snd-soc-lochnagar-sc-objs := lochnagar-sc.o @@ -205,6 +207,7 @@ snd-soc-rt1011-objs := rt1011.o snd-soc-rt1015-objs := rt1015.o snd-soc-rt1015p-objs := rt1015p.o snd-soc-rt1016-objs := rt1016.o +snd-soc-rt1017-sdca-objs := rt1017-sdca-sdw.o snd-soc-rt1019-objs := rt1019.o snd-soc-rt1305-objs := rt1305.o snd-soc-rt1308-objs := rt1308.o @@ -299,7 +302,6 @@ snd-soc-ts3a227e-objs := ts3a227e.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda1334-objs := uda1334.o -snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o @@ -433,6 +435,7 @@ obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o @@ -459,6 +462,8 @@ obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o +obj-$(CONFIG_SND_SOC_CS42L43) += snd-soc-cs42l43.o +obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-sdw.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o @@ -511,7 +516,6 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o -obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o @@ -583,6 +587,7 @@ obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o +obj-$(CONFIG_SND_SOC_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.o obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o @@ -679,7 +684,6 @@ obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o -obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index ad56caec9dac..619a817ee91c 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -880,20 +880,11 @@ static void ak4613_parse_of(struct ak4613_priv *priv, static int ak4613_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; - struct device_node *np = dev->of_node; const struct regmap_config *regmap_cfg; struct regmap *regmap; struct ak4613_priv *priv; - regmap_cfg = NULL; - if (np) - regmap_cfg = of_device_get_match_data(dev); - else { - const struct i2c_device_id *id = - i2c_match_id(ak4613_i2c_id, i2c); - regmap_cfg = (const struct regmap_config *)id->driver_data; - } - + regmap_cfg = i2c_get_match_data(i2c); if (!regmap_cfg) return -EINVAL; diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c new file mode 100644 index 000000000000..a697b5006b45 --- /dev/null +++ b/sound/soc/codecs/aw88261.c @@ -0,0 +1,1300 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.c -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang <zhangjianming@awinic.com> +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw88261.h" +#include "aw88395/aw88395_data_type.h" +#include "aw88395/aw88395_device.h" + +static const struct regmap_config aw88261_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88261_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int real_value, volume; + unsigned int reg_value; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL); + real_value = DB_TO_REG_VAL(volume); + + regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, ®_value); + + real_value = (real_value | (reg_value & AW88261_VOL_START_MASK)); + + dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value); + + regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value); +} + +static void aw88261_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw88261_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, + aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw88261_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw88261_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88261_MUTE_VOL) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + if (flag) + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE); +} + +static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + if (pwd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE); +} + +static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + if (amppd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE); +} + +static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw88261_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE); + aw88261_dev_fade_in(aw_dev); + } +} + +static void aw88261_dev_clear_int_status(struct aw_device *aw_dev) +{ + unsigned int int_status; + + /* read int status and clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + /* make sure int status is clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status); +} + +static int aw88261_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return ret; +} + +static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return ret; + } + } + + return -EPERM; +} + +static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88261_CCO_MUX_MASK); + if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw88261_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw88261_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw88261_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) + & AW88261_BIT_SYSST_CHECK; + if (check_val != AW88261_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", + reg_val, AW88261_BIT_SYSST_CHECK); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute) +{ + if (uls_hmute) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_DISABLE_VALUE); +} + +static void aw88261_reg_force_set(struct aw88261 *aw88261) +{ + if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { + /* set FORCE_PWM */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, + AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /* set BOOST_OS_WIDTH */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, + AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + /* set BURST_LOOPR */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, + AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + /* set RSQN_DLY */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, + AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + /* set BURST_SSMODE */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, + AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + /* set BST_BURST */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, + AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + } else { + dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); + } +} + +static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + u16 reg_icalk, reg_icalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, ®_val); + if (ret) + return ret; + + reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK); + + reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT); + + if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + u16 reg_vcalk, reg_vcalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + + reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK); + + reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT); + + if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG); + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw88261_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vcalk, vcalb; + u32 reg_val; + int ret; + + ret = aw88261_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + + ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + + icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val; + vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val; + if (!vcalk) + return -EINVAL; + + vcalb = AW88261_VCAL_FACTOR * icalk / vcalk; + reg_val = (unsigned int)vcalb; + + dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val); + + return ret; +} + +static int aw88261_dev_reg_update(struct aw88261 *aw88261, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val, efcheck_val, read_vol; + int data_len, i, ret; + int16_t *reg_data; + u16 reg_val; + u8 reg_addr; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88261_SYSCTRL_REG) { + aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK); + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + + read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) | + (~AW88261_HMUTE_MASK); + reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK); + reg_val |= read_val; + + /* enable uls hmute */ + reg_val &= AW88261_ULS_HMUTE_MASK; + reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE; + } + + if (reg_addr == AW88261_DBGCTRL_REG) { + efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK); + if (efcheck_val == AW88261_OR_VALUE) + aw88261->efuse_check = AW88261_EF_OR_CHECK; + else + aw88261->efuse_check = AW88261_EF_AND_CHECK; + } + + /* i2stxen */ + if (reg_addr == AW88261_I2SCTRL3_REG) { + /* close tx */ + reg_val &= AW88261_I2STXEN_MASK; + reg_val |= AW88261_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88261_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88261_VOL_MASK)) >> + AW88261_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + REG_VAL_TO_DB(read_vol); + } + + if (reg_addr == AW88261_VSNTM1_REG) + continue; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + } + + ret = aw88261_dev_set_vcalb(aw_dev); + if (ret) + return ret; + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + + /* keep min volume */ + aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume); + + return ret; +} + +static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} + +static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88261_dev_fw_update(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index); + if (!prof_name) { + dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static int aw88261_dev_start(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (aw_dev->status == AW88261_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /* power on */ + aw88261_dev_pwd(aw_dev, false); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + + ret = aw88261_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw88261_dev_amppd(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 50); + + /* check i2s status */ + ret = aw88261_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + /* enable tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, true); + + if (aw88261->amppd_st) + aw88261_dev_amppd(aw_dev, true); + + aw88261_reg_force_set(aw88261); + + /* close uls mute */ + aw88261_dev_uls_hmute(aw_dev, false); + + /* close mute */ + if (!aw88261->mute_st) + aw88261_dev_mute(aw_dev, false); + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + aw_dev->status = AW88261_DEV_PW_ON; + + return 0; + +sysst_check_fail: + aw88261_dev_i2s_tx_enable(aw_dev, false); + aw88261_dev_clear_int_status(aw_dev); + aw88261_dev_amppd(aw_dev, true); +pll_check_fail: + aw88261_dev_pwd(aw_dev, true); + aw_dev->status = AW88261_DEV_PW_OFF; + + return ret; +} + +static int aw88261_dev_stop(struct aw_device *aw_dev) +{ + if (aw_dev->status == AW88261_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88261_DEV_PW_OFF; + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + /* set mute */ + aw88261_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + /* enable amppd */ + aw88261_dev_amppd(aw_dev, true); + + /* set power down */ + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_reg_update(struct aw88261 *aw88261, bool force) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (force) { + ret = regmap_write(aw_dev->regmap, + AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + if (aw_dev->prof_cur != aw_dev->prof_index) { + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + ret = 0; + } + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static void aw88261_start_pa(struct aw88261 *aw88261) +{ + int ret, i; + + for (i = 0; i < AW88261_START_RETRIES; i++) { + ret = aw88261_reg_update(aw88261, aw88261->phase_sync); + if (ret) { + dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i); + continue; + } + ret = aw88261_dev_start(aw88261); + if (ret) { + dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i); + continue; + } else { + dev_info(aw88261->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88261_startup_work(struct work_struct *work) +{ + struct aw88261 *aw88261 = + container_of(work, struct aw88261, start_work.work); + + mutex_lock(&aw88261->lock); + aw88261_start_pa(aw88261); + mutex_unlock(&aw88261->lock); +} + +static void aw88261_start(struct aw88261 *aw88261, bool sync_start) +{ + if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK) + return; + + if (aw88261->aw_pa->status == AW88261_DEV_PW_ON) + return; + + if (sync_start == AW88261_SYNC_START) + aw88261_start_pa(aw88261); + else + queue_delayed_work(system_wq, + &aw88261->start_work, + AW88261_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88261_dai[] = { + { + .name = "aw88261-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + }, +}; + +static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EPERM; + + aw_dev->prof_index = index; + + return 0; +} + +static int aw88261_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88261->aw_pa->prof_info.count; + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88261_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index; + + return 0; +} + +static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88261->lock); + ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88261->lock); + return 0; + } + + if (aw88261->aw_pa->status) { + aw88261_dev_stop(aw88261->aw_pa); + aw88261_start(aw88261, AW88261_SYNC_START); + } + + mutex_unlock(&aw88261->lock); + + return 1; +} + +static int aw88261_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88261_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step; + + return 0; +} + +static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88261->aw_pa->fade_step != value) { + aw88261->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88261_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG, + 6, AW88261_MUTE_VOL, 0, aw88261_volume_get, + aw88261_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0, + aw88261_get_fade_step, aw88261_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_in_time, aw88261_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_out_time, aw88261_set_fade_out_time), + AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info, + aw88261_profile_get, aw88261_profile_set), +}; + +static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88261->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88261_start(aw88261, AW88261_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88261_dev_stop(aw88261->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88261->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88261_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88261_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88261_frcset_check(struct aw88261 *aw88261) +{ + unsigned int reg_val; + u16 temh, teml, tem; + int ret; + + ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + temh = ((u16)reg_val & (~AW88261_TEMH_MASK)); + + ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + teml = ((u16)reg_val & (~AW88261_TEML_MASK)); + + if (aw88261->efuse_check == AW88261_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW88261_DEFAULT_CFG) + aw88261->frcset_en = AW88261_FRCSET_ENABLE; + else + aw88261->frcset_en = AW88261_FRCSET_DISABLE; + + dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d", + tem, aw88261->frcset_en); + + return ret; +} + +static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + + ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + aw_dev->fade_in_time = AW88261_500_US; + aw_dev->fade_out_time = AW88261_500_US; + aw_dev->prof_cur = AW88261_INIT_PROFILE; + aw_dev->prof_index = AW88261_INIT_PROFILE; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + ret = aw88261_frcset_check(aw88261); + if (ret) { + dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret); + return ret; + } + + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + + aw88261_dev_mute(aw_dev, true); + + aw88261_dev_i2s_tx_enable(aw_dev, false); + + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + aw88261_dev_amppd(aw_dev, true); + + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_request_firmware_file(struct aw88261 *aw88261) +{ + const struct firmware *cont = NULL; + int ret; + + aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "load [%s] failed!", AW88261_ACF_FILE); + + dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n", + AW88261_ACF_FILE, cont ? cont->size : 0); + + aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88261->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88261->aw_cfg->len = (int)cont->size; + memcpy(aw88261->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg); + if (ret) { + dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE); + return ret; + } + + mutex_lock(&aw88261->lock); + /* aw device init */ + ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); + if (ret) + dev_err(aw88261->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88261->lock); + + return ret; +} + +static int aw88261_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work); + + ret = aw88261_request_firmware_file(aw88261); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "aw88261_request_firmware_file failed\n"); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets, + ARRAY_SIZE(aw88261_dapm_widgets)); + if (ret) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map, + ARRAY_SIZE(aw88261_audio_map)); + if (ret) + return ret; + + ret = snd_soc_add_component_controls(component, aw88261_controls, + ARRAY_SIZE(aw88261_controls)); + + return ret; +} + +static void aw88261_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88261->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88261 = { + .probe = aw88261_codec_probe, + .remove = aw88261_codec_remove, +}; + +static void aw88261_hw_reset(struct aw88261 *aw88261) +{ + gpiod_set_value_cansleep(aw88261->reset_gpio, 0); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); + gpiod_set_value_cansleep(aw88261->reset_gpio, 1); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); +} + +static void aw88261_parse_channel_dt(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value = AW88261_DEV_DEFAULT_CH; + u32 sync_enable = false; + + of_property_read_u32(np, "sound-channel", &channel_value); + of_property_read_u32(np, "sync-flag", &sync_enable); + + aw_dev->channel = channel_value; + aw88261->phase_sync = sync_enable; +} + +static int aw88261_init(struct aw88261 **aw88261, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + /* read chip id */ + ret = regmap_read(regmap, AW88261_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88261_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + + dev_info(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + + (*aw88261)->aw_pa = aw_dev; + aw_dev->i2c = i2c; + aw_dev->regmap = regmap; + aw_dev->dev = &i2c->dev; + aw_dev->chip_id = AW88261_CHIP_ID; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88261_DEV_FW_FAILED; + aw_dev->fade_step = AW88261_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE; + aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL; + aw88261_parse_channel_dt(*aw88261); + + return ret; +} + +static int aw88261_i2c_probe(struct i2c_client *i2c) +{ + struct aw88261 *aw88261; + int ret; + + ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed"); + + aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL); + if (!aw88261) + return -ENOMEM; + + mutex_init(&aw88261->lock); + + i2c_set_clientdata(i2c, aw88261); + + aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88261->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + else + aw88261_hw_reset(aw88261); + + aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config); + if (IS_ERR(aw88261->regmap)) { + ret = PTR_ERR(aw88261->regmap); + return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret); + } + + /* aw pa init */ + ret = aw88261_init(&aw88261, i2c, aw88261->regmap); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88261, + aw88261_dai, ARRAY_SIZE(aw88261_dai)); + if (ret) + dev_err(&i2c->dev, "failed to register aw88261: %d", ret); + + return ret; +} + +static const struct i2c_device_id aw88261_i2c_id[] = { + { AW88261_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); + +static struct i2c_driver aw88261_i2c_driver = { + .driver = { + .name = AW88261_I2C_NAME, + }, + .probe = aw88261_i2c_probe, + .id_table = aw88261_i2c_id, +}; +module_i2c_driver(aw88261_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h new file mode 100644 index 000000000000..4f3dbf438510 --- /dev/null +++ b/sound/soc/codecs/aw88261.h @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.h -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang <zhangjianming@awinic.com> +// Author: Weidong Wang <wangweidong.a@awinic.com> +// + +#ifndef __AW88261_H__ +#define __AW88261_H__ + +#define AW88261_ID_REG (0x00) +#define AW88261_SYSST_REG (0x01) +#define AW88261_SYSINT_REG (0x02) +#define AW88261_SYSINTM_REG (0x03) +#define AW88261_SYSCTRL_REG (0x04) +#define AW88261_SYSCTRL2_REG (0x05) +#define AW88261_I2SCTRL1_REG (0x06) +#define AW88261_I2SCTRL2_REG (0x07) +#define AW88261_I2SCTRL3_REG (0x08) +#define AW88261_DACCFG1_REG (0x09) +#define AW88261_DACCFG2_REG (0x0A) +#define AW88261_DACCFG3_REG (0x0B) +#define AW88261_DACCFG4_REG (0x0C) +#define AW88261_DACCFG5_REG (0x0D) +#define AW88261_DACCFG6_REG (0x0E) +#define AW88261_DACCFG7_REG (0x0F) +#define AW88261_DACCFG8_REG (0x10) +#define AW88261_PWMCTRL1_REG (0x11) +#define AW88261_PWMCTRL2_REG (0x12) +#define AW88261_I2SCFG1_REG (0x13) +#define AW88261_DBGCTRL_REG (0x14) +#define AW88261_DACCFG9_REG (0x15) +#define AW88261_DACCFG10_REG (0x16) +#define AW88261_DACST_REG (0x20) +#define AW88261_VBAT_REG (0x21) +#define AW88261_TEMP_REG (0x22) +#define AW88261_PVDD_REG (0x23) +#define AW88261_ISNDAT_REG (0x24) +#define AW88261_VSNDAT_REG (0x25) +#define AW88261_I2SINT_REG (0x26) +#define AW88261_I2SCAPCNT_REG (0x27) +#define AW88261_ANASTA1_REG (0x28) +#define AW88261_ANASTA2_REG (0x29) +#define AW88261_ANASTA3_REG (0x2A) +#define AW88261_TESTDET_REG (0x2B) +#define AW88261_DSMCFG1_REG (0x30) +#define AW88261_DSMCFG2_REG (0x31) +#define AW88261_DSMCFG3_REG (0x32) +#define AW88261_DSMCFG4_REG (0x33) +#define AW88261_DSMCFG5_REG (0x34) +#define AW88261_DSMCFG6_REG (0x35) +#define AW88261_DSMCFG7_REG (0x36) +#define AW88261_DSMCFG8_REG (0x37) +#define AW88261_TESTIN_REG (0x38) +#define AW88261_TESTOUT_REG (0x39) +#define AW88261_SADCCTRL1_REG (0x3A) +#define AW88261_SADCCTRL2_REG (0x3B) +#define AW88261_SADCCTRL3_REG (0x3C) +#define AW88261_SADCCTRL4_REG (0x3D) +#define AW88261_SADCCTRL5_REG (0x3E) +#define AW88261_SADCCTRL6_REG (0x3F) +#define AW88261_SADCCTRL7_REG (0x40) +#define AW88261_VSNTM1_REG (0x50) +#define AW88261_VSNTM2_REG (0x51) +#define AW88261_ISNCTRL1_REG (0x52) +#define AW88261_ISNCTRL2_REG (0x53) +#define AW88261_PLLCTRL1_REG (0x54) +#define AW88261_PLLCTRL2_REG (0x55) +#define AW88261_PLLCTRL3_REG (0x56) +#define AW88261_CDACTRL1_REG (0x57) +#define AW88261_CDACTRL2_REG (0x58) +#define AW88261_DITHERCFG1_REG (0x59) +#define AW88261_DITHERCFG2_REG (0x5A) +#define AW88261_DITHERCFG3_REG (0x5B) +#define AW88261_CPCTRL_REG (0x5C) +#define AW88261_BSTCTRL1_REG (0x60) +#define AW88261_BSTCTRL2_REG (0x61) +#define AW88261_BSTCTRL3_REG (0x62) +#define AW88261_BSTCTRL4_REG (0x63) +#define AW88261_BSTCTRL5_REG (0x64) +#define AW88261_BSTCTRL6_REG (0x65) +#define AW88261_BSTCTRL7_REG (0x66) +#define AW88261_BSTCTRL8_REG (0x67) +#define AW88261_BSTCTRL9_REG (0x68) +#define AW88261_TM_REG (0x6F) +#define AW88261_TESTCTRL1_REG (0x70) +#define AW88261_TESTCTRL2_REG (0x71) +#define AW88261_EFCTRL1_REG (0x72) +#define AW88261_EFCTRL2_REG (0x73) +#define AW88261_EFWH_REG (0x74) +#define AW88261_EFWM2_REG (0x75) +#define AW88261_EFWM1_REG (0x76) +#define AW88261_EFWL_REG (0x77) +#define AW88261_EFRH4_REG (0x78) +#define AW88261_EFRH3_REG (0x79) +#define AW88261_EFRH2_REG (0x7A) +#define AW88261_EFRH1_REG (0x7B) +#define AW88261_EFRL4_REG (0x7C) +#define AW88261_EFRL3_REG (0x7D) +#define AW88261_EFRL2_REG (0x7E) +#define AW88261_EFRL1_REG (0x7F) + +#define AW88261_REG_MAX (0x80) +#define AW88261_EF_DBMD_MASK (0xfff7) +#define AW88261_OR_VALUE (0x0008) + +#define AW88261_TEMH_MASK (0x83ff) +#define AW88261_TEML_MASK (0x83ff) +#define AW88261_DEFAULT_CFG (0x0000) + +#define AW88261_ICALK_SHIFT (0) +#define AW88261_ICALKL_SHIFT (0) +#define AW88261_VCALK_SHIFT (0) +#define AW88261_VCALKL_SHIFT (0) + +#define AW88261_AMPPD_START_BIT (1) +#define AW88261_AMPPD_BITS_LEN (1) +#define AW88261_AMPPD_MASK \ + (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT)) + +#define AW88261_UVLS_START_BIT (14) +#define AW88261_UVLS_NORMAL (0) +#define AW88261_UVLS_NORMAL_VALUE \ + (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT) + +#define AW88261_BSTOCS_START_BIT (11) +#define AW88261_BSTOCS_OVER_CURRENT (1) +#define AW88261_BSTOCS_OVER_CURRENT_VALUE \ + (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT) + +#define AW88261_BSTS_START_BIT (9) +#define AW88261_BSTS_FINISHED (1) +#define AW88261_BSTS_FINISHED_VALUE \ + (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT) + +#define AW88261_SWS_START_BIT (8) +#define AW88261_SWS_SWITCHING (1) +#define AW88261_SWS_SWITCHING_VALUE \ + (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT) + +#define AW88261_NOCLKS_START_BIT (5) +#define AW88261_NOCLKS_NO_CLOCK (1) +#define AW88261_NOCLKS_NO_CLOCK_VALUE \ + (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT) + +#define AW88261_CLKS_START_BIT (4) +#define AW88261_CLKS_STABLE (1) +#define AW88261_CLKS_STABLE_VALUE \ + (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT) + +#define AW88261_OCDS_START_BIT (3) +#define AW88261_OCDS_OC (1) +#define AW88261_OCDS_OC_VALUE \ + (AW88261_OCDS_OC << AW88261_OCDS_START_BIT) + +#define AW88261_OTHS_START_BIT (1) +#define AW88261_OTHS_OT (1) +#define AW88261_OTHS_OT_VALUE \ + (AW88261_OTHS_OT << AW88261_OTHS_START_BIT) + +#define AW88261_PLLS_START_BIT (0) +#define AW88261_PLLS_LOCKED (1) +#define AW88261_PLLS_LOCKED_VALUE \ + (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT) + +#define AW88261_BIT_PLL_CHECK \ + (AW88261_CLKS_STABLE_VALUE | \ + AW88261_PLLS_LOCKED_VALUE) + +#define AW88261_BIT_SYSST_CHECK_MASK \ + (~(AW88261_UVLS_NORMAL_VALUE | \ + AW88261_BSTOCS_OVER_CURRENT_VALUE | \ + AW88261_BSTS_FINISHED_VALUE | \ + AW88261_SWS_SWITCHING_VALUE | \ + AW88261_NOCLKS_NO_CLOCK_VALUE | \ + AW88261_CLKS_STABLE_VALUE | \ + AW88261_OCDS_OC_VALUE | \ + AW88261_OTHS_OT_VALUE | \ + AW88261_PLLS_LOCKED_VALUE)) + +#define AW88261_BIT_SYSST_CHECK \ + (AW88261_BSTS_FINISHED_VALUE | \ + AW88261_SWS_SWITCHING_VALUE | \ + AW88261_CLKS_STABLE_VALUE | \ + AW88261_PLLS_LOCKED_VALUE) + +#define AW88261_ULS_HMUTE_START_BIT (14) +#define AW88261_ULS_HMUTE_BITS_LEN (1) +#define AW88261_ULS_HMUTE_MASK \ + (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT)) + +#define AW88261_ULS_HMUTE_DISABLE (0) +#define AW88261_ULS_HMUTE_DISABLE_VALUE \ + (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT) + +#define AW88261_ULS_HMUTE_ENABLE (1) +#define AW88261_ULS_HMUTE_ENABLE_VALUE \ + (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT) + +#define AW88261_HMUTE_START_BIT (8) +#define AW88261_HMUTE_BITS_LEN (1) +#define AW88261_HMUTE_MASK \ + (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT)) + +#define AW88261_HMUTE_DISABLE (0) +#define AW88261_HMUTE_DISABLE_VALUE \ + (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT) + +#define AW88261_HMUTE_ENABLE (1) +#define AW88261_HMUTE_ENABLE_VALUE \ + (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT) + +#define AW88261_AMPPD_START_BIT (1) +#define AW88261_AMPPD_BITS_LEN (1) +#define AW88261_AMPPD_MASK \ + (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT)) + +#define AW88261_AMPPD_WORKING (0) +#define AW88261_AMPPD_WORKING_VALUE \ + (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT) + +#define AW88261_AMPPD_POWER_DOWN (1) +#define AW88261_AMPPD_POWER_DOWN_VALUE \ + (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT) + +#define AW88261_PWDN_START_BIT (0) +#define AW88261_PWDN_BITS_LEN (1) +#define AW88261_PWDN_MASK \ + (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT)) + +#define AW88261_PWDN_WORKING (0) +#define AW88261_PWDN_WORKING_VALUE \ + (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT) + +#define AW88261_PWDN_POWER_DOWN (1) +#define AW88261_PWDN_POWER_DOWN_VALUE \ + (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT) + +#define AW88261_MUTE_VOL (90 * 8) +#define AW88261_VOLUME_STEP_DB (6 * 8) + +#define AW88261_VOL_6DB_START (6) + +#define AW88261_VOL_START_BIT (0) +#define AW88261_VOL_BITS_LEN (10) +#define AW88261_VOL_MASK \ + (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT)) + +#define AW88261_VOL_DEFAULT_VALUE (0) + +#define AW88261_I2STXEN_START_BIT (6) +#define AW88261_I2STXEN_BITS_LEN (1) +#define AW88261_I2STXEN_MASK \ + (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT)) + +#define AW88261_I2STXEN_DISABLE (0) +#define AW88261_I2STXEN_DISABLE_VALUE \ + (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT) + +#define AW88261_I2STXEN_ENABLE (1) +#define AW88261_I2STXEN_ENABLE_VALUE \ + (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT) + +#define AW88261_CCO_MUX_START_BIT (14) +#define AW88261_CCO_MUX_BITS_LEN (1) +#define AW88261_CCO_MUX_MASK \ + (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT)) + +#define AW88261_CCO_MUX_DIVIDED (0) +#define AW88261_CCO_MUX_DIVIDED_VALUE \ + (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT) + +#define AW88261_CCO_MUX_BYPASS (1) +#define AW88261_CCO_MUX_BYPASS_VALUE \ + (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT) + +#define AW88261_EF_VSN_GESLP_H_START_BIT (0) +#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10) +#define AW88261_EF_VSN_GESLP_H_MASK \ + (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT)) + +#define AW88261_EF_VSN_GESLP_L_START_BIT (0) +#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10) +#define AW88261_EF_VSN_GESLP_L_MASK \ + (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT)) + +#define AW88261_FORCE_PWM_START_BIT (12) +#define AW88261_FORCE_PWM_BITS_LEN (1) +#define AW88261_FORCE_PWM_MASK \ + (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT)) + +#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1) +#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \ + (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT) + +#define AW88261_BST_OS_WIDTH_START_BIT (0) +#define AW88261_BST_OS_WIDTH_BITS_LEN (3) +#define AW88261_BST_OS_WIDTH_MASK \ + (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT)) + +#define AW88261_BST_OS_WIDTH_50NS (4) +#define AW88261_BST_OS_WIDTH_50NS_VALUE \ + (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT) + +/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */ +#define AW88261_BST_LOOPR_START_BIT (0) +#define AW88261_BST_LOOPR_BITS_LEN (2) +#define AW88261_BST_LOOPR_MASK \ + (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT)) + +#define AW88261_BST_LOOPR_340K (2) +#define AW88261_BST_LOOPR_340K_VALUE \ + (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT) + +/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */ +#define AW88261_RSQN_DLY_START_BIT (14) +#define AW88261_RSQN_DLY_BITS_LEN (2) +#define AW88261_RSQN_DLY_MASK \ + (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT)) + +#define AW88261_RSQN_DLY_35NS (2) +#define AW88261_RSQN_DLY_35NS_VALUE \ + (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT) + +/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */ +#define AW88261_BURST_SSMODE_START_BIT (3) +#define AW88261_BURST_SSMODE_BITS_LEN (1) +#define AW88261_BURST_SSMODE_MASK \ + (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT)) + +#define AW88261_BURST_SSMODE_FAST (0) +#define AW88261_BURST_SSMODE_FAST_VALUE \ + (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT) + +/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */ +#define AW88261_BST_BURST_START_BIT (7) +#define AW88261_BST_BURST_BITS_LEN (3) +#define AW88261_BST_BURST_MASK \ + (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT)) + +#define AW88261_BST_BURST_30MA (2) +#define AW88261_BST_BURST_30MA_VALUE \ + (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT) + +#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200) +#define AW88261_EF_VSN_GESLP_NEG (~0xfc00) + +#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200) +#define AW88261_EF_ISN_GESLP_NEG (~0xfc00) + +#define AW88261_EF_ISN_GESLP_H_START_BIT (0) +#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10) +#define AW88261_EF_ISN_GESLP_H_MASK \ + (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT)) + +#define AW88261_EF_ISN_GESLP_L_START_BIT (0) +#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10) +#define AW88261_EF_ISN_GESLP_L_MASK \ + (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT)) + +#define AW88261_CABL_BASE_VALUE (1000) +#define AW88261_ICABLK_FACTOR (1) +#define AW88261_VCABLK_FACTOR (1) + +#define AW88261_VCAL_FACTOR (1<<13) + +#define AW88261_START_RETRIES (5) +#define AW88261_START_WORK_DELAY_MS (0) + +#define AW88261_I2C_NAME "aw88261_smartpa" + +#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW88261_DEV_DEFAULT_CH (0) +#define AW88261_ACF_FILE "aw88261_acf.bin" +#define AW88261_DEV_SYSST_CHECK_MAX (10) +#define AW88261_SOFT_RESET_VALUE (0x55aa) +#define AW88261_REG_TO_DB (0x3f) +#define AW88261_VOL_START_MASK (0xfc00) +#define AW88261_INIT_PROFILE (0) + +#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \ + AW88261_VOLUME_STEP_DB) + \ + ((value) & AW88261_REG_TO_DB)) +#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \ + AW88261_VOL_6DB_START) + \ + ((value) % AW88261_VOLUME_STEP_DB)) + +#define AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88261_SYNC_START = 0, + AW88261_ASYNC_START, +}; + +enum aw88261_id { + AW88261_CHIP_ID = 0x2113, +}; + +enum { + AW88261_500_US = 500, + AW88261_1000_US = 1000, + AW88261_2000_US = 2000, +}; + +enum { + AW88261_DEV_PW_OFF = 0, + AW88261_DEV_PW_ON, +}; + +enum { + AW88261_DEV_FW_FAILED = 0, + AW88261_DEV_FW_OK, +}; + +enum { + AW88261_EF_AND_CHECK = 0, + AW88261_EF_OR_CHECK, +}; + +enum { + AW88261_FRCSET_DISABLE = 0, + AW88261_FRCSET_ENABLE, +}; + +struct aw88261 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + int efuse_check; + int frcset_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char phase_sync; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 05bcf49da857..8ee1baa03269 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -11,6 +11,7 @@ #include <linux/i2c.h> #include "aw88395_lib.h" #include "aw88395_device.h" +#include "aw88395_reg.h" #define AW88395_CRC8_POLYNOMIAL 0x8C DECLARE_CRC8_TABLE(aw_crc8_table); @@ -429,6 +430,53 @@ parse_bin_failed: return ret; } +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + dev_err(aw_dev->dev, "bin num or type error"); + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + dev_err(aw_dev->dev, "bin data len get error!"); + goto parse_bin_failed; + } + + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW88395_PROFILE_OK; + + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + return ret; +} + static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) { @@ -447,6 +495,9 @@ static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg return aw_dev_prof_parse_multi_bin( aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); default: dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); break; @@ -527,7 +578,49 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, return 0; } -static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev, +static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) + prof_info->count++; + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + +static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; @@ -606,9 +699,22 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, goto exit; } - ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); - if (ret < 0) - goto exit; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + case AW88261_CHIP_ID: + ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + default: + dev_err(aw_dev->dev, "valid prof unsupported"); + ret = -EINVAL; + break; + } aw_dev->prof_info.prof_name_list = profile_name; @@ -679,16 +785,37 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && - (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) - (*scene_num)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, @@ -699,15 +826,35 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->channel == cfg_dde[i].dev_index)) - (*scene_num)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, @@ -756,6 +903,18 @@ static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; (*cur_scene_id)++; break; + case ACF_SEC_TYPE_HDR_REG: + ret = aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse reg bin with hdr failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; default: dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); return -EINVAL; diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h index e64f24e97150..e7a7c02efaf3 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -96,6 +96,7 @@ enum aw88395_id { AW88395_CHIP_ID = 0x2049, + AW88261_CHIP_ID = 0x2113, }; #define AW88395_REG_MAX (0x7D) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index ea3a4557619a..600b79c62ec4 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -357,22 +357,11 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f return 0; } -static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56, - unsigned int reg, unsigned long mask) +static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask) { - unsigned int reg_val, channel_shift; + unsigned int channel_shift; int bit_num; - /* Init all slots to 63 */ - switch (reg) { - case CS35L56_ASP1_FRAME_CONTROL1: - reg_val = 0x3f3f3f3f; - break; - case CS35L56_ASP1_FRAME_CONTROL5: - reg_val = 0x3f3f3f; - break; - } - /* Enable consecutive TX1..TXn for each of the slots set in mask */ channel_shift = 0; for_each_set_bit(bit_num, &mask, 32) { @@ -381,7 +370,7 @@ static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56, channel_shift += 8; } - regmap_write(cs35l56->base.regmap, reg, reg_val); + return reg_val; } static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, @@ -417,8 +406,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx if (rx_mask == 0) rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3 - cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask); - cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask); + /* Default unused slots to 63 */ + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1, + cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask)); + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5, + cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask)); dev_dbg(cs35l56->base.dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n", cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask); @@ -663,7 +655,7 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) int ret; /* Use wm_adsp to load and apply the firmware patch and coefficient files */ - ret = wm_adsp_power_up(&cs35l56->dsp); + ret = wm_adsp_power_up(&cs35l56->dsp, true); if (ret) dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); else @@ -672,8 +664,17 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) static void cs35l56_patch(struct cs35l56_private *cs35l56) { + unsigned int firmware_missing; int ret; + ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); + if (ret) { + dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + return; + } + + firmware_missing &= CS35L56_FIRMWARE_MISSING; + /* * Disable SoundWire interrupts to prevent race with IRQ work. * Setting sdw_irq_no_unmask prevents the handler re-enabling @@ -692,8 +693,12 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56) if (ret) goto err; - /* Use wm_adsp to load and apply the firmware patch and coefficient files */ - ret = wm_adsp_power_up(&cs35l56->dsp); + /* + * Use wm_adsp to load and apply the firmware patch and coefficient files, + * but only if firmware is missing. If firmware is already patched just + * power-up wm_adsp without downloading firmware. + */ + ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing); if (ret) { dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); goto err; @@ -959,6 +964,12 @@ int cs35l56_system_resume(struct device *dev) dev_dbg(dev, "system_resume\n"); + /* + * We might have done a hard reset or the CS35L56 was power-cycled + * so wait for control port to be ready. + */ + cs35l56_wait_control_port_ready(); + /* Undo pm_runtime_force_suspend() before re-enabling the irq */ ret = pm_runtime_force_resume(dev); if (cs35l56->base.irq) @@ -977,6 +988,7 @@ int cs35l56_system_resume(struct device *dev) return ret; cs35l56->base.fw_patched = false; + wm_adsp_power_down(&cs35l56->dsp); queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); /* @@ -1072,6 +1084,8 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n"); if (cs35l56->base.reset_gpio) { + /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); cs35l56_wait_min_reset_pulse(); gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); } diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c new file mode 100644 index 000000000000..92e37bc1df9d --- /dev/null +++ b/sound/soc/codecs/cs42l43-jack.c @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver jack handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/build_bug.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <sound/control.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-component.h> +#include <sound/soc.h> + +#include "cs42l43.h" + +static const unsigned int cs42l43_accdet_us[] = { + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 +}; + +static const unsigned int cs42l43_accdet_db_ms[] = { + 0, 125, 250, 500, 750, 1000, 1250, 1500 +}; + +static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; + +static const unsigned int cs42l43_accdet_bias_sense[] = { + 14, 23, 41, 50, 60, 68, 86, 95, 0, +}; + +static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop, + unsigned int defval, unsigned int *val, + const unsigned int *values, const int nvalues) +{ + struct cs42l43 *cs42l43 = priv->core; + int i, ret; + + ret = device_property_read_u32(cs42l43->dev, prop, &defval); + if (ret != -EINVAL && ret < 0) { + dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret); + return ret; + } + + if (val) + *val = defval; + + for (i = 0; i < nvalues; i++) + if (defval == values[i]) + return i; + + dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval); + return -EINVAL; +} + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + /* This tip sense invert is always set, HW wants an inverted signal */ + unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK; + unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT; + unsigned int autocontrol = 0, pdncntl = 0; + int ret; + + dev_dbg(priv->dev, "Configure accessory detect\n"); + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); + return ret; + } + + mutex_lock(&priv->jack_lock); + + priv->jack_hp = jack; + + if (!jack) + goto done; + + ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); + if (ret != -EINVAL) { + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", + ret); + goto error; + } + + if (ret > CS42L43_N_BUTTONS) { + ret = -EINVAL; + dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); + goto error; + } + + device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + } else { + priv->buttons[0] = 70; + priv->buttons[1] = 185; + priv->buttons[2] = 355; + priv->buttons[3] = 735; + } + + ret = cs42l43_find_index(priv, "cirrus,detect-us", 10000, &priv->detect_us, + cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; + + priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low"); + + ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170, + &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, + ARRAY_SIZE(cs42l43_accdet_ramp_ms)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 0, + &priv->bias_sense_ua, cs42l43_accdet_bias_sense, + ARRAY_SIZE(cs42l43_accdet_bias_sense)); + if (ret < 0) + goto error; + + if (priv->bias_sense_ua) + autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; + + if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute")) + autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK; + + ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms", + &priv->tip_debounce_ms); + if (ret < 0 && ret != -EINVAL) { + dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); + goto error; + } + + /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert")) + autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup")) + autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT; + else + autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) { + unsigned int ring_deb = 0; + + priv->use_ring_sense = true; + + /* HW wants an inverted signal, so invert the invert */ + if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert")) + ring_deb |= CS42L43_RINGSENSE_INV_MASK; + + if (!device_property_read_bool(cs42l43->dev, + "cirrus,ring-disable-pullup")) + ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK; + + ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; + pdncntl |= CS42L43_RING_SENSE_EN_MASK; + + regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL, + CS42L43_RINGSENSE_INV_MASK | + CS42L43_RINGSENSE_PULLUP_PDNB_MASK | + CS42L43_RINGSENSE_FALLING_DB_TIME_MASK | + CS42L43_RINGSENSE_RISING_DB_TIME_MASK, + ring_deb); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL, + CS42L43_TIPSENSE_INV_MASK | + CS42L43_TIPSENSE_FALLING_DB_TIME_MASK | + CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | + CS42L43_AUTO_HSDET_TIME_MASK, hs2); + +done: + ret = 0; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | + CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); + regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, + CS42L43_RING_SENSE_EN_MASK, pdncntl); + + dev_dbg(priv->dev, "Successfully configured accessory detect\n"); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT; + + dev_dbg(priv->dev, "Start headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + if (!force_high && priv->bias_low) + val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, val); + + msleep(priv->bias_ramp_ms); +} + +static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); +} + +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_delayed_work(system_wq, &priv->bias_sense_timeout, + msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +#define CS42L43_JACK_PRESENT 0x3 +#define CS42L43_JACK_ABSENT 0x0 + +#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) +#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) +#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) +#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN) +#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL) +#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \ + SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5) + +static inline bool cs42l43_jack_present(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + return sts == CS42L43_JACK_PRESENT; +} + +static void cs42l43_start_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + dev_dbg(priv->dev, "Start button detect\n"); + + priv->button_detect_running = true; + + if (priv->bias_low) + val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, val); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } +} + +static void cs42l43_stop_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop button detect\n"); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, + CS42L43_MIC_LVL_DET_DISABLE_MASK); + + priv->button_detect_running = false; +} + +#define CS42L43_BUTTON_COMB_MAX 512 +#define CS42L43_BUTTON_ROUT 2210 + +void cs42l43_button_press_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_press_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int buttons = 0; + unsigned int val = 0; + int i, ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button press IRQ\n"); + goto error; + } + + regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val); + + /* Bail if jack removed, the button is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) { + dev_dbg(priv->dev, "Button ignored due to removal\n"); + goto error; + } + + if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { + dev_dbg(priv->dev, "Button ignored due to bias sense\n"); + goto error; + } + + val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; + val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20); + if (val) + val = (CS42L43_BUTTON_ROUT << 20) / val; + else + val = UINT_MAX; + + for (i = 0; i < CS42L43_N_BUTTONS; i++) { + if (val < priv->buttons[i]) { + buttons = SND_JACK_BTN_0 >> i; + dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val); + break; + } + } + + if (!buttons) + dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val); + + snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_press(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + // Wait for 2 full cycles of comb filter to ensure good reading + queue_delayed_work(system_wq, &priv->button_press_work, + msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +void cs42l43_button_release_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_release_work); + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (priv->button_detect_running) { + dev_dbg(priv->dev, "Button release IRQ\n"); + + snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + } else { + dev_dbg(priv->dev, "Spurious button release IRQ\n"); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_release(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_work(system_wq, &priv->button_release_work); + + return IRQ_HANDLED; +} + +void cs42l43_bias_sense_timeout(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + bias_sense_timeout.work); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (cs42l43_jack_present(priv) && priv->button_detect_running) { + dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); + + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +static void cs42l43_start_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Start load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + priv->load_detect_running = true; + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_shutdown); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, 0); + + time_left = wait_for_completion_timeout(&priv->hp_shutdown, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP power down timed out\n"); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, + CS42L43_HPLOAD_DET_EN_MASK); + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, + 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, + CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, + priv->adc_ena); + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_startup); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, priv->hp_ena); + + time_left = wait_for_completion_timeout(&priv->hp_startup, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP restore timed out\n"); + } + + priv->load_detect_running = false; + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0; + unsigned long time_left; + + reinit_completion(&priv->load_detect); + + cs42l43_start_load_detect(priv); + time_left = wait_for_completion_timeout(&priv->load_detect, + msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS)); + cs42l43_stop_load_detect(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val); + + dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val); + + /* Bail if jack removed, the load is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + if (mic) { + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + + return CS42L43_JACK_HEADSET; + } + + switch (val & CS42L43_AMP3_RES_DET_MASK) { + case 0x0: // low impedance + case 0x1: // high impedance + return CS42L43_JACK_HEADPHONE; + case 0x2: // lineout + case 0x3: // Open circuit + return CS42L43_JACK_LINEOUT; + default: + return -EINVAL; + } +} + +static int cs42l43_run_type_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + unsigned int type = 0xff; + unsigned long time_left; + + reinit_completion(&priv->type_detect); + + cs42l43_start_hs_bias(priv, true); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT); + + time_left = wait_for_completion_timeout(&priv->type_detect, + msecs_to_jiffies(timeout_ms)); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); + cs42l43_stop_hs_bias(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type); + + dev_dbg(priv->dev, "Type detect: 0x%x\n", type); + + /* Bail if jack removed, the type is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + switch (type & CS42L43_HSDET_TYPE_STS_MASK) { + case 0x0: // CTIA + case 0x1: // OMTP + return cs42l43_run_load_detect(priv, true); + case 0x2: // 3-pole + return cs42l43_run_load_detect(priv, false); + case 0x3: // Open-circuit + return CS42L43_JACK_EXTENSION; + default: + return -EINVAL; + } +} + +static void cs42l43_clear_jack(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + cs42l43_stop_button_detect(priv); + cs42l43_stop_hs_bias(priv); + + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_JACK_STEREO_CONFIG_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, + 0x2 << CS42L43_HSDET_MODE_SHIFT); + + snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); +} + +void cs42l43_tip_sense_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + tip_sense_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + unsigned int tip, ring; + int ret, report; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts); + + tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + if (tip == CS42L43_JACK_PRESENT) { + if (cs42l43->sdw && !priv->jack_present) { + priv->jack_present = true; + pm_runtime_get(priv->dev); + } + + if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) { + report = CS42L43_JACK_OPTICAL; + } else { + report = cs42l43_run_type_detect(priv); + if (report < 0) { + dev_err(priv->dev, "Jack detect failed: %d\n", report); + goto error; + } + } + + snd_soc_jack_report(priv->jack_hp, report, report); + } else { + priv->jack_override = 0; + + cs42l43_clear_jack(priv); + + if (cs42l43->sdw && priv->jack_present) { + pm_runtime_put(priv->dev); + priv->jack_present = false; + } + } + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_tip_sense(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + cancel_delayed_work(&priv->bias_sense_timeout); + cancel_delayed_work(&priv->tip_sense_work); + cancel_delayed_work(&priv->button_press_work); + cancel_work(&priv->button_release_work); + + queue_delayed_work(system_long_wq, &priv->tip_sense_work, + msecs_to_jiffies(priv->tip_debounce_ms)); + + return IRQ_HANDLED; +} + +enum cs42l43_raw_jack { + CS42L43_JACK_RAW_CTIA = 0, + CS42L43_JACK_RAW_OMTP, + CS42L43_JACK_RAW_HEADPHONE, + CS42L43_JACK_RAW_LINE_OUT, + CS42L43_JACK_RAW_LINE_IN, + CS42L43_JACK_RAW_MICROPHONE, + CS42L43_JACK_RAW_OPTICAL, +}; + +#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \ + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \ + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSGND_HS3_SEL_MASK | \ + CS42L43_HSGND_HS4_SEL_MASK) + +static const struct cs42l43_jack_override_mode { + unsigned int hsdet_mode; + unsigned int mic_ctrl; + unsigned int clamp_ctrl; + int report; +} cs42l43_jack_override_modes[] = { + [CS42L43_JACK_RAW_CTIA] = { + .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_OMTP] = { + .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_HEADPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADPHONE, + }, + [CS42L43_JACK_RAW_LINE_OUT] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_LINEOUT, + }, + [CS42L43_JACK_RAW_LINE_IN] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_MICROPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | + CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_OPTICAL] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_OPTICAL, + }, +}; + +static const char * const cs42l43_jack_text[] = { + "None", "CTIA", "OMTP", "Headphone", "Line-Out", + "Line-In", "Microphone", "Optical", +}; + +SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); + +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->jack_lock); + ucontrol->value.integer.value[0] = priv->jack_override; + mutex_unlock(&priv->jack_lock); + + return 0; +} + +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int override = ucontrol->value.integer.value[0]; + + BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != + ARRAY_SIZE(cs42l43_jack_text) - 1); + + if (override >= e->items) + return -EINVAL; + + mutex_lock(&priv->jack_lock); + + if (!cs42l43_jack_present(priv)) { + mutex_unlock(&priv->jack_lock); + return -EBUSY; + } + + if (override == priv->jack_override) { + mutex_unlock(&priv->jack_lock); + return 0; + } + + priv->jack_override = override; + + cs42l43_clear_jack(priv); + + if (!override) { + queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0); + } else { + override--; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | + CS42L43_HSDET_MANUAL_MODE_MASK | + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + cs42l43_jack_override_modes[override].hsdet_mode); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK | + CS42L43_JACK_STEREO_CONFIG_MASK, + cs42l43_jack_override_modes[override].mic_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + cs42l43_jack_override_modes[override].clamp_ctrl); + + switch (override) { + case CS42L43_JACK_RAW_CTIA: + case CS42L43_JACK_RAW_OMTP: + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + break; + case CS42L43_JACK_RAW_LINE_IN: + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + break; + case CS42L43_JACK_RAW_MICROPHONE: + cs42l43_start_hs_bias(priv, false); + break; + default: + break; + } + + snd_soc_jack_report(priv->jack_hp, + cs42l43_jack_override_modes[override].report, + cs42l43_jack_override_modes[override].report); + } + + mutex_unlock(&priv->jack_lock); + + return 1; +} diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c new file mode 100644 index 000000000000..55ac5fe8c3db --- /dev/null +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver SoundWire handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/errno.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/sdw.h> +#include <sound/soc-component.h> +#include <sound/soc-dai.h> +#include <sound/soc.h> + +#include "cs42l43.h" + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + struct sdw_stream_config sconfig = {0}; + struct sdw_port_config pconfig = {0}; + int ret; + + if (!sdw_stream) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &sconfig, &pconfig); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pconfig.num = dai->id; + else + pconfig.num = dai->id; + + ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream); + if (ret) { + dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + + if (!sdw_stream) + return -EINVAL; + + return sdw_stream_remove_slave(sdw, sdw_stream); +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c new file mode 100644 index 000000000000..24e718e51174 --- /dev/null +++ b/sound/soc/codecs/cs42l43.c @@ -0,0 +1,2278 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gcd.h> +#include <linux/irq.h> +#include <linux/jiffies.h> +#include <linux/mfd/cs42l43.h> +#include <linux/mfd/cs42l43-regs.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/string.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-component.h> +#include <sound/soc-dapm.h> +#include <sound/soc-dai.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "cs42l43.h" + +#define CS42L43_DECL_MUX(name, reg) \ +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \ + 0, CS42L43_MIXER_SRC_MASK, \ + cs42l43_mixer_texts, cs42l43_mixer_values); \ +static const struct snd_kcontrol_new cs42l43_##name##_mux = \ + SOC_DAPM_ENUM("Route", cs42l43_##name##_enum) + +#define CS42L43_DECL_MIXER(name, reg) \ + CS42L43_DECL_MUX(name##_in1, reg); \ + CS42L43_DECL_MUX(name##_in2, reg + 0x4); \ + CS42L43_DECL_MUX(name##_in3, reg + 0x8); \ + CS42L43_DECL_MUX(name##_in4, reg + 0xC) + +#define CS42L43_DAPM_MUX(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux) + +#define CS42L43_DAPM_MIXER(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define CS42L43_BASE_ROUTES(name_str) \ + { name_str, "Tone Generator 1", "Tone 1" }, \ + { name_str, "Tone Generator 2", "Tone 2" }, \ + { name_str, "Decimator 1", "Decimator 1" }, \ + { name_str, "Decimator 2", "Decimator 2" }, \ + { name_str, "Decimator 3", "Decimator 3" }, \ + { name_str, "Decimator 4", "Decimator 4" }, \ + { name_str, "ASPRX1", "ASPRX1" }, \ + { name_str, "ASPRX2", "ASPRX2" }, \ + { name_str, "ASPRX3", "ASPRX3" }, \ + { name_str, "ASPRX4", "ASPRX4" }, \ + { name_str, "ASPRX5", "ASPRX5" }, \ + { name_str, "ASPRX6", "ASPRX6" }, \ + { name_str, "DP5RX1", "DP5RX1" }, \ + { name_str, "DP5RX2", "DP5RX2" }, \ + { name_str, "DP6RX1", "DP6RX1" }, \ + { name_str, "DP6RX2", "DP6RX2" }, \ + { name_str, "DP7RX1", "DP7RX1" }, \ + { name_str, "DP7RX2", "DP7RX2" }, \ + { name_str, "ASRC INT1", "ASRC_INT1" }, \ + { name_str, "ASRC INT2", "ASRC_INT2" }, \ + { name_str, "ASRC INT3", "ASRC_INT3" }, \ + { name_str, "ASRC INT4", "ASRC_INT4" }, \ + { name_str, "ASRC DEC1", "ASRC_DEC1" }, \ + { name_str, "ASRC DEC2", "ASRC_DEC2" }, \ + { name_str, "ASRC DEC3", "ASRC_DEC3" }, \ + { name_str, "ASRC DEC4", "ASRC_DEC4" }, \ + { name_str, "ISRC1 INT1", "ISRC1INT1" }, \ + { name_str, "ISRC1 INT2", "ISRC1INT2" }, \ + { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \ + { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \ + { name_str, "ISRC2 INT1", "ISRC2INT1" }, \ + { name_str, "ISRC2 INT2", "ISRC2INT2" }, \ + { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \ + { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \ + { name_str, "EQ1", "EQ" }, \ + { name_str, "EQ2", "EQ" } + +#define CS42L43_MUX_ROUTES(name_str, widget) \ + { widget, NULL, name_str " Input" }, \ + { name_str " Input", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input") + +#define CS42L43_MIXER_ROUTES(name_str, widget) \ + { name_str " Mixer", NULL, name_str " Input 1" }, \ + { name_str " Mixer", NULL, name_str " Input 2" }, \ + { name_str " Mixer", NULL, name_str " Input 3" }, \ + { name_str " Mixer", NULL, name_str " Input 4" }, \ + { widget, NULL, name_str " Mixer" }, \ + { name_str " Mixer", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input 1"), \ + CS42L43_BASE_ROUTES(name_str " Input 2"), \ + CS42L43_BASE_ROUTES(name_str " Input 3"), \ + CS42L43_BASE_ROUTES(name_str " Input 4") + +#define CS42L43_MIXER_VOLUMES(name_str, base) \ + SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv) + +#define CS42L43_IRQ_ERROR(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_err(priv->dev, "Error " #name " IRQ\n"); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_ERROR(pll_lost_lock) +CS42L43_IRQ_ERROR(spkr_clock_stop) +CS42L43_IRQ_ERROR(spkl_clock_stop) +CS42L43_IRQ_ERROR(spkr_brown_out) +CS42L43_IRQ_ERROR(spkl_brown_out) +CS42L43_IRQ_ERROR(spkr_therm_shutdown) +CS42L43_IRQ_ERROR(spkl_therm_shutdown) +CS42L43_IRQ_ERROR(spkr_therm_warm) +CS42L43_IRQ_ERROR(spkl_therm_warm) +CS42L43_IRQ_ERROR(spkr_sc_detect) +CS42L43_IRQ_ERROR(spkl_sc_detect) +CS42L43_IRQ_ERROR(hp_ilimit) + +#define CS42L43_IRQ_COMPLETE(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_dbg(priv->dev, #name " completed\n"); \ + complete(&priv->name); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_COMPLETE(pll_ready) +CS42L43_IRQ_COMPLETE(hp_startup) +CS42L43_IRQ_COMPLETE(hp_shutdown) +CS42L43_IRQ_COMPLETE(type_detect) +CS42L43_IRQ_COMPLETE(spkr_shutdown) +CS42L43_IRQ_COMPLETE(spkl_shutdown) +CS42L43_IRQ_COMPLETE(spkr_startup) +CS42L43_IRQ_COMPLETE(spkl_startup) +CS42L43_IRQ_COMPLETE(load_detect) + +static irqreturn_t cs42l43_mic_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + const char * const controls[] = { + "Decimator 1 Switch", + "Decimator 2 Switch", + "Decimator 3 Switch", + "Decimator 4 Switch", + }; + int i, ret; + + dev_dbg(priv->dev, "Microphone shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(controls); i++) { + ret = snd_soc_component_notify_control(priv->component, + controls[i]); + if (ret) + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t cs42l43_spk_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + int ret; + + dev_dbg(priv->dev, "Speaker shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + ret = snd_soc_component_notify_control(priv->component, + "Speaker Digital Switch"); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static const unsigned int cs42l43_sample_rates[] = { + 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000, +}; + +#define CS42L43_CONSUMER_RATE_MASK 0xFF +#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer + +static const struct snd_pcm_hw_constraint_list cs42l43_constraint = { + .count = ARRAY_SIZE(cs42l43_sample_rates), + .list = cs42l43_sample_rates, +}; + +static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + + if (provider) + priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK; + else + priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &priv->constraint); +} + +static int cs42l43_convert_sample_rate(unsigned int rate) +{ + switch (rate) { + case 8000: + return 0x11; + case 16000: + return 0x12; + case 24000: + return 0x02; + case 32000: + return 0x13; + case 44100: + return 0x0B; + case 48000: + return 0x03; + case 96000: + return 0x04; + case 192000: + return 0x05; + default: + return -EINVAL; + } +} + +static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = cs42l43_convert_sample_rate(params_rate(params)); + if (ret < 0) { + dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret); + return ret; + } + + //FIXME: For now lets just set sample rate 1, this needs expanded in the future + regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1, + CS42L43_SAMPLE_RATE_MASK, ret); + + return 0; +} + +static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK); + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + int n_chans = params_channels(params); + int data_width = params_width(params); + int n_slots = n_chans; + int slot_width = data_width; + int frame, bclk_target, i; + unsigned int reg; + int *slots; + + if (priv->n_slots) { + n_slots = priv->n_slots; + slot_width = priv->slot_width; + } + + if (!dsp_mode && (n_slots & 0x1)) { + dev_dbg(priv->dev, "Forcing balanced channels on ASP\n"); + n_slots++; + } + + frame = n_slots * slot_width; + bclk_target = params_rate(params) * frame; + + if (provider) { + unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK); + int n = bclk_target / gcd_nm; + int m = CS42L43_INTERNAL_SYSCLK / gcd_nm; + + if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) || + m > CS42L43_ASP_BCLK_M_MASK) { + dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target); + return -EINVAL; + } + + dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n", + n, m, bclk_target, n_slots, slot_width); + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1, + CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK, + n << CS42L43_ASP_BCLK_N_SHIFT | + m << CS42L43_ASP_BCLK_M_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1, + CS42L43_ASP_FSYNC_M_MASK, frame); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4, + CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK, + frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + reg = CS42L43_ASP_TX_CH1_CTRL; + slots = priv->tx_slots; + } else { + reg = CS42L43_ASP_RX_CH1_CTRL; + slots = priv->rx_slots; + } + + for (i = 0; i < n_chans; i++, reg += 4) { + int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK); + int slot_pos; + + if (dsp_mode) + slot_pos = slots[i] * slot_width; + else + slot_pos = (slots[i] / 2) * slot_width; + + dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n", + i, slots[i], slot_pos, slot_phase); + + regmap_update_bits(cs42l43->regmap, reg, + CS42L43_ASP_CH_WIDTH_MASK | + CS42L43_ASP_CH_SLOT_MASK | + CS42L43_ASP_CH_SLOT_PHASE_MASK, + ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) | + (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) | + slot_phase); + } + + return cs42l43_set_sample_rate(substream, params, dai); +} + +static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + struct snd_soc_dapm_route routes[] = { + { "BCLK", NULL, "FSYNC" }, + }; + unsigned int asp_ctrl = 0; + unsigned int data_ctrl = 0; + unsigned int fsync_ctrl = 0; + unsigned int clk_config = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK; + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + case SND_SOC_DAIFMT_I2S: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + break; + case SND_SOC_DAIFMT_LEFT_J: + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + if (provider) + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); + break; + case SND_SOC_DAIFMT_CBP_CFP: + if (!provider) + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + clk_config |= CS42L43_ASP_MASTER_MODE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported ASP mode 0x%x\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */ + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + default: + dev_err(priv->dev, "Unsupported invert mode 0x%x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK, + asp_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL, + CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK | + CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK, + data_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK | + CS42L43_ASP_BCLK_INV_MASK, + clk_config); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3, + CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK, + fsync_ctrl); + + return 0; +} + +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +{ + int i; + + for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { + int slot = ffs(mask) - 1; + + if (slot < 0) + return; + + slots[i] = slot; + + mask &= ~(1 << slot); + } + + if (mask) + dev_warn(priv->dev, "Too many channels in TDM mask\n"); +} + +static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + priv->n_slots = slots; + priv->slot_width = slot_width; + + if (!slots) { + tx_mask = CS42L43_DEFAULT_SLOTS; + rx_mask = CS42L43_DEFAULT_SLOTS; + } + + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + + return 0; +} + +static const struct snd_soc_dai_ops cs42l43_asp_ops = { + .startup = cs42l43_startup, + .hw_params = cs42l43_asp_hw_params, + .set_fmt = cs42l43_asp_set_fmt, + .set_tdm_slot = cs42l43_asp_set_tdm_slot, +}; + +static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret; + + ret = cs42l43_sdw_add_peripheral(substream, params, dai); + if (ret) + return ret; + + return cs42l43_set_sample_rate(substream, params, dai); +}; + +static const struct snd_soc_dai_ops cs42l43_sdw_ops = { + .startup = cs42l43_startup, + .set_stream = cs42l43_sdw_set_stream, + .hw_params = cs42l43_sdw_hw_params, + .hw_free = cs42l43_sdw_remove_peripheral, +}; + +#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver cs42l43_dais[] = { + { + .name = "cs42l43-asp", + .ops = &cs42l43_asp_ops, + .symmetric_rate = 1, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + .playback = { + .stream_name = "ASP Playback", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + }, + { + .name = "cs42l43-dp1", + .id = 1, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp2", + .id = 2, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp3", + .id = 3, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp4", + .id = 4, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp5", + .id = 5, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP5 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp6", + .id = 6, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP6 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp7", + .id = 7, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP7 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, +}; + +static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0); + +static const char * const cs42l43_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" }; + +static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1, + CS42L43_ADC_AIN_SEL_SHIFT, + cs42l43_adc1_input_text); + +static const struct snd_kcontrol_new cs42l43_adc1_input_ctl = + SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input); + +static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text); +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text); + +static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = { + SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode), + SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode), +}; + +static const char * const cs42l43_pdm_clk_text[] = { + "3.072MHz", "1.536MHz", "768kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0); +static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0); + +static const char * const cs42l43_wnf_corner_text[] = { + "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); + +static const char * const cs42l43_hpf_corner_text[] = { + "3Hz", "12Hz", "48Hz", "96Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1); + +static const char * const cs42l43_headphone_ramp_text[] = { + "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44", + "48", "66", "72", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_RAMP_SHIFT, + cs42l43_headphone_ramp_text); + +static const char * const cs42l43_tone_freq_text[] = { + "1kHz", "2kHz", "4kHz", "6kHz", "8kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static const char * const cs42l43_mixer_texts[] = { + "None", + "Tone Generator 1", "Tone Generator 2", + "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4", + "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6", + "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2", + "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4", + "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4", + "ISRC1 INT1", "ISRC1 INT2", + "ISRC1 DEC1", "ISRC1 DEC2", + "ISRC2 INT1", "ISRC2 INT2", + "ISRC2 DEC1", "ISRC2 DEC2", + "EQ1", "EQ2", +}; + +static const unsigned int cs42l43_mixer_values[] = { + 0x00, // None + 0x04, 0x05, // Tone Generator 1, 2 + 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6 + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2 + 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4 + 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4 + 0x50, 0x51, // ISRC1 INT1, 2 + 0x52, 0x53, // ISRC1 DEC1, 2 + 0x54, 0x55, // ISRC2 INT1, 2 + 0x56, 0x57, // ISRC2 DEC1, 2 + 0x58, 0x59, // EQ1, 2 +}; + +CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT); +CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT); +CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT); +CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT); +CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT); +CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT); + +CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT); +CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT); +CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT); +CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT); +CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT); +CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT); +CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT); +CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT); +CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT); +CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT); + +CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1); +CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1); +CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1); +CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1); +CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1); +CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1); +CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1); +CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1); + +CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1); +CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1); +CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1); +CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1); +CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1); +CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1); +CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1); +CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1); + +CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1); +CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1); + +CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1); +CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1); +CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1); +CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1); + +static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs)); + + return 0; +} + +static int cs42l43_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + snd_soc_dapm_mutex_lock(dapm); + + memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs)); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + mutex_lock(&priv->spk_vu_lock); + + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, 0); + + mutex_unlock(&priv->spk_vu_lock); +} + +static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret); + return ret; + } + + /* + * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must + * be cached for the non-volatiles, so drop it from the cache here so + * we force a read. + */ + ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, + CS42L43_SHUTTER_CONTROL); + if (ret) { + dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret); + goto error; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter status: %d\n", ret); + goto error; + } + + ret = !(val & BIT(shift)); + + dev_dbg(priv->dev, "%s shutter is %s\n", + BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker", + ret ? "open" : "closed"); + +error: + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret > 0) + cs42l43_spk_vu_sync(priv); + + return ret; +} + +static const struct snd_kcontrol_new cs42l43_controls[] = { + SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum, + cs42l43_jack_get, cs42l43_jack_put), + + SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2, + CS42L43_ADC_PGA_GAIN_SHIFT, + 0xF, 5, cs42l43_adc_tlv), + + SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0), + SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0), + SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk), + SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk), + + SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner), + SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner), + SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner), + SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner), + + SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner), + SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner), + SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner), + SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner), + + SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + + SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + + SOC_DOUBLE_R_EXT("Speaker Digital Switch", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_MUTE_SHIFT, 1, 1, + cs42l43_spk_get, cs42l43_spk_put), + + SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_VOL_SHIFT, + 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put, + cs42l43_speaker_tlv), + + SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up), + SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down), + + CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1), + + SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL, + CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT, + 0x11B, 229, cs42l43_headphone_tlv), + + SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1, + CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0), + + SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0), + SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0), + SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp), + + CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1), + + SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq), + SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq), + + SOC_DOUBLE_EXT("EQ Switch", + CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT, + CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1, + cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw), + + SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS, + cs42l43_eq_get, cs42l43_eq_put), + + CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1), +}; + +static int cs42l43_eq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int i, ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK); + + for (i = 0; i < CS42L43_N_EQ_COEFFS; i++) + regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0, + priv->eq_coeffs[i]); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, 0); + + return 0; + case SND_SOC_DAPM_POST_PMU: + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0, + val, (val & CS42L43_INITIALIZE_DONE_MASK), + 2000, 10000); + if (ret) + dev_err(priv->dev, "Failed to start EQs: %d\n", ret); + + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0); + return ret; + default: + return 0; + } +} + +struct cs42l43_pll_config { + unsigned int freq; + + unsigned int div; + unsigned int mode; + unsigned int cal; +}; + +static const struct cs42l43_pll_config cs42l43_pll_configs[] = { + { 2400000, 0x50000000, 0x1, 0xA4 }, + { 3000000, 0x40000000, 0x1, 0x83 }, + { 3072000, 0x40000000, 0x3, 0x80 }, +}; + +static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src, + unsigned int freq) +{ + struct cs42l43 *cs42l43 = priv->core; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == src && priv->refclk_freq == freq) + return 0; + + if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) { + dev_err(priv->dev, "PLL active, can't change configuration\n"); + return -EBUSY; + } + + switch (src) { + case CS42L43_SYSCLK_MCLK: + case CS42L43_SYSCLK_SDW: + dev_dbg(priv->dev, "Source PLL from %s at %uHz\n", + src ? "SoundWire" : "MCLK", freq); + + priv->refclk_src = src; + priv->refclk_freq = freq; + + return 0; + default: + dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src); + return -EINVAL; + } +} + +static int cs42l43_enable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence enable_seq[] = { + { CS42L43_OSC_DIV_SEL, 0x0, }, + { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, }, + }; + struct cs42l43 *cs42l43 = priv->core; + const struct cs42l43_pll_config *config = NULL; + unsigned int div = 0; + unsigned int freq = priv->refclk_freq; + unsigned long time_left; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) { + if (!freq) + freq = cs42l43->sdw_freq; + else if (!cs42l43->sdw_freq) + cs42l43->sdw_freq = freq; + } + + dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); + + while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { + div++; + freq /= 2; + } + + if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) { + if (freq == cs42l43_pll_configs[i].freq) { + config = &cs42l43_pll_configs[i]; + break; + } + } + } + + if (!config) { + dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK, + div << CS42L43_PLL_REFCLK_DIV_SHIFT | + priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT); + regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_MODE_BYPASS_500_MASK | + CS42L43_PLL_MODE_BYPASS_1029_MASK, + config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO, + CS42L43_PLL_CAL_RATIO_MASK, config->cal); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK); + + reinit_completion(&priv->pll_ready); + + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK); + + time_left = wait_for_completion_timeout(&priv->pll_ready, + msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS)); + if (!time_left) { + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + dev_err(priv->dev, "Timeout out waiting for PLL\n"); + return -ETIMEDOUT; + } + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) + cs42l43->sdw_pll_active = true; + + dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left)); + + /* + * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL, + * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole + * change over happens under the regmap lock to prevent any reads. + */ + regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq)); + + return 0; +} + +static int cs42l43_disable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence disable_seq[] = { + { CS42L43_MCLK_SRC_SEL, 0x0, 5, }, + { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, }, + }; + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Disabling PLL\n"); + + lockdep_assert_held(&cs42l43->pll_lock); + + regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq)); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + cs42l43->sdw_pll_active = false; + + return 0; +} + +static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret); + break; + } + } + + ret = cs42l43_enable_pll(priv); + break; + case SND_SOC_DAPM_POST_PMD: + ret = cs42l43_disable_pll(priv); + + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) + clk_disable_unprepare(priv->mclk); + break; + default: + ret = 0; + break; + } + + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd, + int event, int timeout_ms) +{ + unsigned long time_left; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + reinit_completion(pmu); + return 0; + case SND_SOC_DAPM_PRE_PMD: + reinit_completion(pmd); + return 0; + case SND_SOC_DAPM_POST_PMU: + time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms)); + break; + case SND_SOC_DAPM_POST_PMD: + time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms)); + break; + default: + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + else + return 0; +} + +static int cs42l43_spkr_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkr_startup, + &priv->spkr_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_spkl_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkl_startup, + &priv->spkl_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->hp_ena &= ~mask; + priv->hp_ena |= val; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + mask, val); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + if (priv->load_detect_running) + break; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + break; + default: + break; + } + + return 0; +} + +static int cs42l43_mic_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int reg, ramp, mute; + unsigned int *val; + int ret; + + switch (w->shift) { + case CS42L43_ADC1_EN_SHIFT: + case CS42L43_PDM1_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM1_VD_RAMP_MASK; + mute = CS42L43_DECIM1_MUTE_MASK; + val = &priv->decim_cache[0]; + break; + case CS42L43_ADC2_EN_SHIFT: + case CS42L43_PDM1_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM2_VD_RAMP_MASK; + mute = CS42L43_DECIM2_MUTE_MASK; + val = &priv->decim_cache[1]; + break; + case CS42L43_PDM2_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM3_VD_RAMP_MASK; + mute = CS42L43_DECIM3_MUTE_MASK; + val = &priv->decim_cache[2]; + break; + case CS42L43_PDM2_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM4_VD_RAMP_MASK; + mute = CS42L43_DECIM4_MUTE_MASK; + val = &priv->decim_cache[3]; + break; + default: + dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(cs42l43->regmap, reg, val); + if (ret) { + dev_err(priv->dev, + "Failed to cache decimator settings: %d\n", + ret); + return ret; + } + + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute); + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val); + break; + default: + break; + } + + return 0; +} + +static int cs42l43_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + ret = cs42l43_mic_ev(w, kcontrol, event); + if (ret) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->adc_ena &= ~mask; + priv->adc_ena |= val; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + mask, val); + fallthrough; + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs42l43_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN1_P"), + SND_SOC_DAPM_INPUT("ADC1_IN1_N"), + SND_SOC_DAPM_INPUT("ADC1_IN2_P"), + SND_SOC_DAPM_INPUT("ADC1_IN2_N"), + SND_SOC_DAPM_INPUT("ADC2_IN_P"), + SND_SOC_DAPM_INPUT("ADC2_IN_N"), + + SND_SOC_DAPM_INPUT("PDM1_DIN"), + SND_SOC_DAPM_INPUT("PDM2_DIN"), + + SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl), + + SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[0]), + SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[1]), + + SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0), + + SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0, + cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0, + cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"), + + SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPDIF_TX"), + + SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0, + cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("AMP3_OUT"), + SND_SOC_DAPM_OUTPUT("AMP4_OUT"), + + SND_SOC_DAPM_SIGGEN("Tone"), + SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + + SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4, + CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4, + CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT, + 0, NULL, 0, cs42l43_eq_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT, + 0, NULL, 0), + CS42L43_DAPM_MUX("ASPTX1", asptx1), + CS42L43_DAPM_MUX("ASPTX2", asptx2), + CS42L43_DAPM_MUX("ASPTX3", asptx3), + CS42L43_DAPM_MUX("ASPTX4", asptx4), + CS42L43_DAPM_MUX("ASPTX5", asptx5), + CS42L43_DAPM_MUX("ASPTX6", asptx6), + + CS42L43_DAPM_MUX("DP1TX1", dp1tx1), + CS42L43_DAPM_MUX("DP1TX2", dp1tx2), + CS42L43_DAPM_MUX("DP1TX3", dp1tx3), + CS42L43_DAPM_MUX("DP1TX4", dp1tx4), + CS42L43_DAPM_MUX("DP2TX1", dp2tx1), + CS42L43_DAPM_MUX("DP2TX2", dp2tx2), + CS42L43_DAPM_MUX("DP3TX1", dp3tx1), + CS42L43_DAPM_MUX("DP3TX2", dp3tx2), + CS42L43_DAPM_MUX("DP4TX1", dp4tx1), + CS42L43_DAPM_MUX("DP4TX2", dp4tx2), + + CS42L43_DAPM_MUX("ASRC INT1", asrcint1), + CS42L43_DAPM_MUX("ASRC INT2", asrcint2), + CS42L43_DAPM_MUX("ASRC INT3", asrcint3), + CS42L43_DAPM_MUX("ASRC INT4", asrcint4), + CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1), + CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2), + CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3), + CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4), + + CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1), + CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2), + CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1), + CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2), + CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1), + CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2), + CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1), + CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2), + + CS42L43_DAPM_MUX("SPDIF1", spdif1), + CS42L43_DAPM_MUX("SPDIF2", spdif2), + + CS42L43_DAPM_MIXER("EQ1", eq1), + CS42L43_DAPM_MIXER("EQ2", eq2), + + CS42L43_DAPM_MIXER("Speaker L", amp1), + CS42L43_DAPM_MIXER("Speaker R", amp2), + + CS42L43_DAPM_MIXER("Headphone L", amp3), + CS42L43_DAPM_MIXER("Headphone R", amp4), +}; + +static const struct snd_soc_dapm_route cs42l43_routes[] = { + { "ADC1_IN1_P", NULL, "PLL" }, + { "ADC1_IN1_N", NULL, "PLL" }, + { "ADC1_IN2_P", NULL, "PLL" }, + { "ADC1_IN2_N", NULL, "PLL" }, + { "ADC2_IN_P", NULL, "PLL" }, + { "ADC2_IN_N", NULL, "PLL" }, + { "PDM1_DIN", NULL, "PLL" }, + { "PDM2_DIN", NULL, "PLL" }, + { "AMP1_OUT_P", NULL, "PLL" }, + { "AMP1_OUT_N", NULL, "PLL" }, + { "AMP2_OUT_P", NULL, "PLL" }, + { "AMP2_OUT_N", NULL, "PLL" }, + { "SPDIF_TX", NULL, "PLL" }, + { "HP", NULL, "PLL" }, + { "AMP3_OUT", NULL, "PLL" }, + { "AMP4_OUT", NULL, "PLL" }, + { "Tone 1", NULL, "PLL" }, + { "Tone 2", NULL, "PLL" }, + { "ASP Playback", NULL, "PLL" }, + { "ASP Capture", NULL, "PLL" }, + { "DP1 Capture", NULL, "PLL" }, + { "DP2 Capture", NULL, "PLL" }, + { "DP3 Capture", NULL, "PLL" }, + { "DP4 Capture", NULL, "PLL" }, + { "DP5 Playback", NULL, "PLL" }, + { "DP6 Playback", NULL, "PLL" }, + { "DP7 Playback", NULL, "PLL" }, + + { "ADC1 Input", "IN1", "ADC1_IN1_P" }, + { "ADC1 Input", "IN1", "ADC1_IN1_N" }, + { "ADC1 Input", "IN2", "ADC1_IN2_P" }, + { "ADC1 Input", "IN2", "ADC1_IN2_N" }, + + { "ADC1", NULL, "ADC1 Input" }, + { "ADC2", NULL, "ADC2_IN_P" }, + { "ADC2", NULL, "ADC2_IN_N" }, + + { "PDM1L", NULL, "PDM1_DIN" }, + { "PDM1R", NULL, "PDM1_DIN" }, + { "PDM2L", NULL, "PDM2_DIN" }, + { "PDM2R", NULL, "PDM2_DIN" }, + + { "Decimator 1 Mode", "PDM", "PDM1L" }, + { "Decimator 1 Mode", "ADC", "ADC1" }, + { "Decimator 2 Mode", "PDM", "PDM1R" }, + { "Decimator 2 Mode", "ADC", "ADC2" }, + + { "Decimator 1", NULL, "Decimator 1 Mode" }, + { "Decimator 2", NULL, "Decimator 2 Mode" }, + { "Decimator 3", NULL, "PDM2L" }, + { "Decimator 4", NULL, "PDM2R" }, + + { "ASP Capture", NULL, "ASPTX1" }, + { "ASP Capture", NULL, "ASPTX2" }, + { "ASP Capture", NULL, "ASPTX3" }, + { "ASP Capture", NULL, "ASPTX4" }, + { "ASP Capture", NULL, "ASPTX5" }, + { "ASP Capture", NULL, "ASPTX6" }, + { "ASPTX1", NULL, "BCLK" }, + { "ASPTX2", NULL, "BCLK" }, + { "ASPTX3", NULL, "BCLK" }, + { "ASPTX4", NULL, "BCLK" }, + { "ASPTX5", NULL, "BCLK" }, + { "ASPTX6", NULL, "BCLK" }, + + { "ASPRX1", NULL, "ASP Playback" }, + { "ASPRX2", NULL, "ASP Playback" }, + { "ASPRX3", NULL, "ASP Playback" }, + { "ASPRX4", NULL, "ASP Playback" }, + { "ASPRX5", NULL, "ASP Playback" }, + { "ASPRX6", NULL, "ASP Playback" }, + { "ASPRX1", NULL, "BCLK" }, + { "ASPRX2", NULL, "BCLK" }, + { "ASPRX3", NULL, "BCLK" }, + { "ASPRX4", NULL, "BCLK" }, + { "ASPRX5", NULL, "BCLK" }, + { "ASPRX6", NULL, "BCLK" }, + + { "DP1 Capture", NULL, "DP1TX1" }, + { "DP1 Capture", NULL, "DP1TX2" }, + { "DP1 Capture", NULL, "DP1TX3" }, + { "DP1 Capture", NULL, "DP1TX4" }, + + { "DP2 Capture", NULL, "DP2TX1" }, + { "DP2 Capture", NULL, "DP2TX2" }, + + { "DP3 Capture", NULL, "DP3TX1" }, + { "DP3 Capture", NULL, "DP3TX2" }, + + { "DP4 Capture", NULL, "DP4TX1" }, + { "DP4 Capture", NULL, "DP4TX2" }, + + { "DP5RX1", NULL, "DP5 Playback" }, + { "DP5RX2", NULL, "DP5 Playback" }, + + { "DP6RX1", NULL, "DP6 Playback" }, + { "DP6RX2", NULL, "DP6 Playback" }, + + { "DP7RX1", NULL, "DP7 Playback" }, + { "DP7RX2", NULL, "DP7 Playback" }, + + { "AMP1", NULL, "vdd-amp" }, + { "AMP2", NULL, "vdd-amp" }, + + { "AMP1_OUT_P", NULL, "AMP1" }, + { "AMP1_OUT_N", NULL, "AMP1" }, + { "AMP2_OUT_P", NULL, "AMP2" }, + { "AMP2_OUT_N", NULL, "AMP2" }, + + { "SPDIF_TX", NULL, "SPDIF" }, + + { "AMP3_OUT", NULL, "HP" }, + { "AMP4_OUT", NULL, "HP" }, + + { "Tone 1", NULL, "Tone" }, + { "Tone 1", NULL, "Tone Generator" }, + { "Tone 2", NULL, "Tone" }, + { "Tone 2", NULL, "Tone Generator" }, + + { "ISRC1INT2", NULL, "ISRC1" }, + { "ISRC1INT1", NULL, "ISRC1" }, + { "ISRC1DEC2", NULL, "ISRC1" }, + { "ISRC1DEC1", NULL, "ISRC1" }, + + { "ISRC2INT2", NULL, "ISRC2" }, + { "ISRC2INT1", NULL, "ISRC2" }, + { "ISRC2DEC2", NULL, "ISRC2" }, + { "ISRC2DEC1", NULL, "ISRC2" }, + + { "ASRC_INT1", NULL, "ASRC_INT" }, + { "ASRC_INT2", NULL, "ASRC_INT" }, + { "ASRC_INT3", NULL, "ASRC_INT" }, + { "ASRC_INT4", NULL, "ASRC_INT" }, + { "ASRC_DEC1", NULL, "ASRC_DEC" }, + { "ASRC_DEC2", NULL, "ASRC_DEC" }, + { "ASRC_DEC3", NULL, "ASRC_DEC" }, + { "ASRC_DEC4", NULL, "ASRC_DEC" }, + + { "EQ", NULL, "EQ Clock" }, + + CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"), + CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"), + CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"), + CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"), + CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"), + CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"), + + CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"), + CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"), + CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"), + CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"), + CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"), + CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"), + CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"), + CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"), + CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"), + CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"), + + CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"), + CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"), + CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"), + CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"), + CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"), + CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"), + CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"), + CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"), + + CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"), + CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"), + + CS42L43_MIXER_ROUTES("EQ1", "EQ"), + CS42L43_MIXER_ROUTES("EQ2", "EQ"), + + CS42L43_MIXER_ROUTES("Speaker L", "AMP1"), + CS42L43_MIXER_ROUTES("Speaker R", "AMP2"), + + CS42L43_MIXER_ROUTES("Headphone L", "HP"), + CS42L43_MIXER_ROUTES("Headphone R", "HP"), +}; + +static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, + int src, unsigned int freq, int dir) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + ret = cs42l43_set_pll(priv, src, freq); + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_component_probe(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + + snd_soc_component_init_regmap(component, cs42l43->regmap); + + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + + priv->component = component; + priv->constraint = cs42l43_constraint; + + return 0; +} + +static const struct snd_soc_component_driver cs42l43_component_drv = { + .name = "cs42l43-codec", + + .probe = cs42l43_component_probe, + .set_sysclk = cs42l43_set_sysclk, + .set_jack = cs42l43_set_jack, + + .endianness = 1, + + .controls = cs42l43_controls, + .num_controls = ARRAY_SIZE(cs42l43_controls), + .dapm_widgets = cs42l43_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets), + .dapm_routes = cs42l43_routes, + .num_dapm_routes = ARRAY_SIZE(cs42l43_routes), +}; + +struct cs42l43_irq { + unsigned int irq; + const char *name; + irq_handler_t handler; +}; + +static const struct cs42l43_irq cs42l43_irqs[] = { + { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock }, + { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready }, + { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup }, + { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown }, + { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect }, + { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense }, + { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense }, + { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press }, + { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release }, + { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp }, + { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop }, + { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop }, + { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out }, + { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out }, + { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown }, + { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown }, + { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup }, + { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup }, + { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown }, + { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown }, + { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm }, + { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm }, + { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect }, + { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect }, + { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit }, + { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect }, +}; + +static int cs42l43_request_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, const char * const name, + unsigned int irq, irq_handler_t handler) +{ + int ret; + + ret = irq_create_mapping(dom, irq); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name); + + dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name); + + ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, IRQF_ONESHOT, + name, priv); + if (ret) + return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name); + + return 0; +} + +static int cs42l43_shutter_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, unsigned int shutter, + const char * const open_name, + const char * const close_name, + irq_handler_t handler) +{ + unsigned int open_irq, close_irq; + int ret; + + switch (shutter) { + case 0x1: + dev_warn(priv->dev, "Manual shutters, notifications not available\n"); + return 0; + case 0x2: + open_irq = CS42L43_GPIO1_RISE; + close_irq = CS42L43_GPIO1_FALL; + break; + case 0x4: + open_irq = CS42L43_GPIO2_RISE; + close_irq = CS42L43_GPIO2_FALL; + break; + case 0x8: + open_irq = CS42L43_GPIO3_RISE; + close_irq = CS42L43_GPIO3_FALL; + break; + default: + return 0; + } + + ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler); + if (ret) + return ret; + + return cs42l43_request_irq(priv, dom, open_name, open_irq, handler); +} + +static int cs42l43_codec_probe(struct platform_device *pdev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); + struct cs42l43_codec *priv; + struct irq_domain *dom; + unsigned int val; + int i, ret; + + dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY); + if (!dom) + return -EPROBE_DEFER; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->core = cs42l43; + + platform_set_drvdata(pdev, priv); + + mutex_init(&priv->jack_lock); + mutex_init(&priv->spk_vu_lock); + + init_completion(&priv->hp_startup); + init_completion(&priv->hp_shutdown); + init_completion(&priv->spkr_shutdown); + init_completion(&priv->spkl_shutdown); + init_completion(&priv->spkr_startup); + init_completion(&priv->spkl_startup); + init_completion(&priv->pll_ready); + init_completion(&priv->type_detect); + init_completion(&priv->load_detect); + + INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); + INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); + INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); + INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); + + pm_runtime_set_autosuspend_delay(priv->dev, 100); + pm_runtime_use_autosuspend(priv->dev); + pm_runtime_set_active(priv->dev); + pm_runtime_get_noresume(priv->dev); + devm_pm_runtime_enable(priv->dev); + + for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) { + ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name, + cs42l43_irqs[i].irq, cs42l43_irqs[i].handler); + if (ret) + goto err_pm; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter source: %d\n", ret); + goto err_pm; + } + + ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK, + "mic shutter open", "mic shutter close", + cs42l43_mic_shutter); + if (ret) + goto err_pm; + + ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> + CS42L43_SPK_SHUTTER_CFG_SHIFT, + "spk shutter open", "spk shutter close", + cs42l43_spk_shutter); + if (ret) + goto err_pm; + + // Don't use devm as we need to get against the MFD device + priv->mclk = clk_get_optional(cs42l43->dev, "mclk"); + if (IS_ERR(priv->mclk)) { + dev_err_probe(priv->dev, PTR_ERR(priv->mclk), "Failed to get mclk\n"); + goto err_pm; + } + + ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv, + cs42l43_dais, ARRAY_SIZE(cs42l43_dais)); + if (ret) { + dev_err_probe(priv->dev, ret, "Failed to register component\n"); + goto err_clk; + } + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return 0; + +err_clk: + clk_put(priv->mclk); +err_pm: + pm_runtime_put_sync(priv->dev); + + return ret; +} + +static int cs42l43_codec_remove(struct platform_device *pdev) +{ + struct cs42l43_codec *priv = platform_get_drvdata(pdev); + + clk_put(priv->mclk); + + return 0; +} + +static int cs42l43_codec_runtime_resume(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + + dev_dbg(priv->dev, "Runtime resume\n"); + + // Toggle the speaker volume update incase the speaker volume was synced + cs42l43_spk_vu_sync(priv); + + return 0; +} + +DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL, + cs42l43_codec_runtime_resume, NULL); + +static const struct platform_device_id cs42l43_codec_id_table[] = { + { "cs42l43-codec", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); + +static struct platform_driver cs42l43_codec_driver = { + .driver = { + .name = "cs42l43-codec", + .pm = &cs42l43_codec_pm_ops, + }, + + .probe = cs42l43_codec_probe, + .remove = cs42l43_codec_remove, + .id_table = cs42l43_codec_id_table, +}; +module_platform_driver(cs42l43_codec_driver); + +MODULE_IMPORT_NS(SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC Driver"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h new file mode 100644 index 000000000000..bf4f728eea3e --- /dev/null +++ b/sound/soc/codecs/cs42l43.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 CODEC driver internal data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/types.h> +#include <sound/cs42l43.h> +#include <sound/pcm.h> +#include <sound/soc-jack.h> + +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + +#define CS42L43_INTERNAL_SYSCLK 24576000 +#define CS42L43_DEFAULT_SLOTS 0x3F + +#define CS42L43_PLL_TIMEOUT_MS 200 +#define CS42L43_SPK_TIMEOUT_MS 100 +#define CS42L43_HP_TIMEOUT_MS 2000 +#define CS42L43_LOAD_TIMEOUT_MS 1000 + +#define CS42L43_ASP_MAX_CHANNELS 6 +#define CS42L43_N_EQ_COEFFS 15 + +#define CS42L43_N_BUTTONS 6 + +struct cs42l43_codec { + struct device *dev; + struct cs42l43 *core; + struct snd_soc_component *component; + + struct clk *mclk; + + int n_slots; + int slot_width; + int tx_slots[CS42L43_ASP_MAX_CHANNELS]; + int rx_slots[CS42L43_ASP_MAX_CHANNELS]; + struct snd_pcm_hw_constraint_list constraint; + + u32 eq_coeffs[CS42L43_N_EQ_COEFFS]; + + unsigned int refclk_src; + unsigned int refclk_freq; + struct completion pll_ready; + + unsigned int decim_cache[4]; + unsigned int adc_ena; + unsigned int hp_ena; + + struct completion hp_startup; + struct completion hp_shutdown; + struct completion spkr_shutdown; + struct completion spkl_shutdown; + struct completion spkr_startup; + struct completion spkl_startup; + // Lock to ensure speaker VU updates don't clash + struct mutex spk_vu_lock; + + // Lock for all jack detect operations + struct mutex jack_lock; + struct snd_soc_jack *jack_hp; + + bool use_ring_sense; + unsigned int tip_debounce_ms; + unsigned int bias_low; + unsigned int bias_sense_ua; + unsigned int bias_ramp_ms; + unsigned int detect_us; + unsigned int buttons[CS42L43_N_BUTTONS]; + + struct delayed_work tip_sense_work; + struct delayed_work bias_sense_timeout; + struct delayed_work button_press_work; + struct work_struct button_release_work; + struct completion type_detect; + struct completion load_detect; + + bool load_detect_running; + bool button_detect_running; + bool jack_present; + int jack_override; +}; + +#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW) + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction); + +#else + +static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EINVAL; +} + +#define cs42l43_sdw_remove_peripheral NULL +#define cs42l43_sdw_set_stream NULL + +#endif + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d); +void cs42l43_bias_sense_timeout(struct work_struct *work); +void cs42l43_tip_sense_work(struct work_struct *work); +void cs42l43_button_press_work(struct work_struct *work); +void cs42l43_button_release_work(struct work_struct *work); +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data); +irqreturn_t cs42l43_button_press(int irq, void *data); +irqreturn_t cs42l43_button_release(int irq, void *data); +irqreturn_t cs42l43_tip_sense(int irq, void *data); +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum cs42l43_jack_enum; + +#endif /* CS42L43_ASOC_INT_H */ diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index e7db7bcd0296..5ed2ef83dcdb 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -19,7 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); -const struct of_device_id cs42l51_of_match[] = { +static const struct of_device_id cs42l51_of_match[] = { { .compatible = "cirrus,cs42l51", }, { } }; diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index a6538dab6639..1245e1a4f2a5 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -1143,6 +1143,10 @@ static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l15_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l15_dai[] = { { .name = "cs47l15-aif1", @@ -1219,7 +1223,7 @@ static struct snd_soc_dai_driver cs47l15_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l15_dai_ops, }, { .name = "cs47l15-dsp-trace", diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index a07b621d463e..cfa1d34f6ebd 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -957,6 +957,10 @@ static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id, #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops cs47l24_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l24_dai[] = { { .name = "cs47l24-aif1", @@ -1033,7 +1037,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .rates = CS47L24_RATES, .formats = CS47L24_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l24_dai_ops, }, { .name = "cs47l24-dsp-voicectrl", @@ -1054,7 +1058,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = { .rates = CS47L24_RATES, .formats = CS47L24_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l24_dai_ops, }, { .name = "cs47l24-dsp-trace", diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index c05c80c16c84..a953f2ede1ee 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -1348,6 +1348,10 @@ static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l35_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l35_dai[] = { { .name = "cs47l35-aif1", @@ -1462,7 +1466,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l35_dai_ops, }, { .name = "cs47l35-dsp-voicectrl", @@ -1483,7 +1487,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l35_dai_ops, }, { .name = "cs47l35-dsp-trace", diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c index dd7997a53e70..827685481859 100644 --- a/sound/soc/codecs/cs47l85.c +++ b/sound/soc/codecs/cs47l85.c @@ -2249,6 +2249,10 @@ static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l85_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l85_dai[] = { { .name = "cs47l85-aif1", @@ -2404,7 +2408,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l85_dai_ops, }, { .name = "cs47l85-dsp-voicectrl", @@ -2425,7 +2429,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l85_dai_ops, }, { .name = "cs47l85-dsp-trace", diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c index cdd5e7e20b5d..2c9a5372cf51 100644 --- a/sound/soc/codecs/cs47l90.c +++ b/sound/soc/codecs/cs47l90.c @@ -2168,6 +2168,10 @@ static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l90_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l90_dai[] = { { .name = "cs47l90-aif1", @@ -2323,7 +2327,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l90_dai_ops, }, { .name = "cs47l90-dsp-voicectrl", @@ -2344,7 +2348,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = &snd_soc_new_compress, + .ops = &cs47l90_dai_ops, }, { .name = "cs47l90-dsp-trace", diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c index bc4d311d4778..352deeaff1ca 100644 --- a/sound/soc/codecs/cs47l92.c +++ b/sound/soc/codecs/cs47l92.c @@ -1690,6 +1690,10 @@ static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id, } } +static const struct snd_soc_dai_ops cs47l92_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver cs47l92_dai[] = { { .name = "cs47l92-aif1", @@ -1823,7 +1827,7 @@ static struct snd_soc_dai_driver cs47l92_dai[] = { .rates = MADERA_RATES, .formats = MADERA_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &cs47l92_dai_ops, }, { .name = "cs47l92-dsp-trace", diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 082231088a26..f8b128084015 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1546,6 +1546,14 @@ static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops cx2072x_dai_ops2 = { + .probe = cx2072x_dsp_dai_probe, + .set_sysclk = cx2072x_set_dai_sysclk, + .set_fmt = cx2072x_set_dai_fmt, + .hw_params = cx2072x_hw_params, + .set_bclk_ratio = cx2072x_set_dai_bclk_ratio, +}; + #define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { @@ -1572,7 +1580,6 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { { /* plabayck only, return echo reference to Conexant DSP chip */ .name = "cx2072x-dsp", .id = CX2072X_DAI_DSP, - .probe = cx2072x_dsp_dai_probe, .playback = { .stream_name = "DSP Playback", .channels_min = 2, @@ -1580,7 +1587,7 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { .rates = CX2072X_RATES_DSP, .formats = CX2072X_FORMATS, }, - .ops = &cx2072x_dai_ops, + .ops = &cx2072x_dai_ops2, }, { /* plabayck only, return echo reference through I2S TX */ .name = "cx2072x-aec", diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index a7fbb758eeee..6c263086c44d 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -41,6 +41,8 @@ struct es8326_priv { bool calibrated; int version; + int hp; + int jack_remove_retry; }; static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); @@ -158,20 +160,25 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = { {"HPOR", NULL, "RHPMIX"}, }; -static const struct regmap_range es8326_volatile_ranges[] = { - regmap_reg_range(ES8326_HP_DETECT, ES8326_HP_DETECT), -}; - -static const struct regmap_access_table es8326_volatile_table = { - .yes_ranges = es8326_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(es8326_volatile_ranges), -}; +static bool es8326_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8326_HPL_OFFSET_INI: + case ES8326_HPR_OFFSET_INI: + case ES8326_HPDET_STA: + case ES8326_CTIA_OMTP_STA: + case ES8326_CSM_MUTE_STA: + return true; + default: + return false; + } +} static const struct regmap_config es8326_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, - .volatile_table = &es8326_volatile_table, + .volatile_reg = es8326_volatile_register, .cache_type = REGCACHE_RBTREE, }; @@ -383,6 +390,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) { struct snd_soc_component *component = dai->component; struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int offset_l, offset_r; if (mute) { regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); @@ -393,10 +401,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) if (!es8326->calibrated) { regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); msleep(30); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); es8326->calibrated = true; } regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0); - regmap_write(es8326->regmap, ES8326_HP_VOL, 0x00); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, ~(ES8326_MUTE)); @@ -415,15 +429,17 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, ret = clk_prepare_enable(es8326->mclk); if (ret) return ret; - regmap_write(es8326->regmap, ES8326_RESET, ES8326_PWRUP_SEQ_EN); - regmap_write(es8326->regmap, ES8326_INTOUT_IO, 0x45); + + regmap_write(es8326->regmap, ES8326_RESET, 0x9f); + msleep(20); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); - regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); - regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x02); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0xAA); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); break; case SND_SOC_BIAS_PREPARE: @@ -432,15 +448,10 @@ static int es8326_set_bias_level(struct snd_soc_component *codec, break; case SND_SOC_BIAS_OFF: clk_disable_unprepare(es8326->mclk); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x11); - regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_OFF); - regmap_write(es8326->regmap, ES8326_PGA_PDN, 0xF8); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x08); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00); regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); - regmap_write(es8326->regmap, ES8326_RESET, - ES8326_CODEC_RESET | ES8326_PWRUP_SEQ_EN); break; } @@ -519,13 +530,14 @@ static void es8326_jack_button_handler(struct work_struct *work) return; mutex_lock(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HP_DETECT); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); switch (iface) { case 0x93: /* pause button detected */ cur_button = SND_JACK_BTN_0; break; case 0x6f: + case 0x4b: /* button volume up */ cur_button = SND_JACK_BTN_1; break; @@ -534,6 +546,7 @@ static void es8326_jack_button_handler(struct work_struct *work) cur_button = SND_JACK_BTN_2; break; case 0x1e: + case 0xe2: /* button released or not pressed */ cur_button = 0; break; @@ -543,20 +556,20 @@ static void es8326_jack_button_handler(struct work_struct *work) if ((prev_button == cur_button) && (cur_button != 0)) { press_count++; - if (press_count > 10) { - /* report a press every 500ms */ + if (press_count > 3) { + /* report a press every 120ms */ snd_soc_jack_report(es8326->jack, cur_button, SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2); press_count = 0; } button_to_report = cur_button; queue_delayed_work(system_wq, &es8326->button_press_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(35)); } else if (prev_button != cur_button) { /* mismatch, detect again */ prev_button = cur_button; queue_delayed_work(system_wq, &es8326->button_press_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(35)); } else { /* released or no pressed */ if (button_to_report != 0) { @@ -578,34 +591,98 @@ static void es8326_jack_detect_handler(struct work_struct *work) unsigned int iface; mutex_lock(&es8326->lock); - iface = snd_soc_component_read(comp, ES8326_HP_DETECT); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); dev_dbg(comp->dev, "gpio flag %#04x", iface); + + if (es8326->jack_remove_retry == 1) { + if (iface & ES8326_HPINSERT_FLAG) + es8326->jack_remove_retry = 2; + else + es8326->jack_remove_retry = 0; + + dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n", + es8326->jack_remove_retry); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + ~es8326->jack_pol : es8326->jack_pol)); + goto exit; + } + if ((iface & ES8326_HPINSERT_FLAG) == 0) { /* Jack unplugged or spurious IRQ */ - dev_dbg(comp->dev, "No headset detected"); + dev_dbg(comp->dev, "No headset detected\n"); + es8326_disable_micbias(es8326->component); if (es8326->jack->status & SND_JACK_HEADPHONE) { + dev_dbg(comp->dev, "Report hp remove event\n"); snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); - snd_soc_component_write(comp, ES8326_ADC1_SRC, es8326->mic2_src); - es8326_disable_micbias(comp); + /* mute adc when mic path switch */ + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66); + es8326->hp = 0; + } + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + if (es8326->jack_remove_retry == 0) { + es8326->jack_remove_retry = 1; + dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n", + es8326->jack_remove_retry); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + es8326->jack_pol : ~es8326->jack_pol)); + + } else { + es8326->jack_remove_retry = 0; } } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) { + es8326->jack_remove_retry = 0; + if (es8326->hp == 0) { + dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n"); + /* + * set auto-check mode, then restart jack_detect_work after 100ms. + * Don't report jack status. + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + usleep_range(50000, 70000); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + queue_delayed_work(system_wq, &es8326->jack_detect_work, + msecs_to_jiffies(100)); + es8326->hp = 1; + goto exit; + } if (es8326->jack->status & SND_JACK_HEADSET) { /* detect button */ + dev_dbg(comp->dev, "button pressed\n"); queue_delayed_work(system_wq, &es8326->button_press_work, 10); + goto exit; + } + if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { + dev_dbg(comp->dev, "Headphone detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADPHONE, SND_JACK_HEADSET); } else { - if ((iface & ES8326_HPBUTTON_FLAG) == 0x00) { - dev_dbg(comp->dev, "Headset detected"); - snd_soc_jack_report(es8326->jack, - SND_JACK_HEADSET, SND_JACK_HEADSET); - snd_soc_component_write(comp, - ES8326_ADC1_SRC, es8326->mic1_src); - } else { - dev_dbg(comp->dev, "Headphone detected"); - snd_soc_jack_report(es8326->jack, - SND_JACK_HEADPHONE, SND_JACK_HEADSET); - } + dev_dbg(comp->dev, "Headset detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADSET, SND_JACK_HEADSET); + + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x08); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, + 0x80, 0x80); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x00); + usleep_range(10000, 15000); } } +exit: mutex_unlock(&es8326->lock); } @@ -624,51 +701,138 @@ static irqreturn_t es8326_irq(int irq, void *dev_id) msecs_to_jiffies(10)); else queue_delayed_work(system_wq, &es8326->jack_detect_work, - msecs_to_jiffies(300)); + msecs_to_jiffies(600)); out: return IRQ_HANDLED; } -static int es8326_resume(struct snd_soc_component *component) +static int es8326_calibrate(struct snd_soc_component *component) { struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); unsigned int reg; + unsigned int offset_l, offset_r; + + regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); + es8326->version = reg; + + if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) { + dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n"); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0); + regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01); + regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30); + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed); + regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1); + regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f); + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33); + regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_RESET, 0xc0); + usleep_range(15000, 20000); + + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, ®); + if ((reg & 0xf0) != 0x40) + msleep(50); + + regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); + + es8326->calibrated = true; + } + + return 0; +} + +static int es8326_resume(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); regcache_cache_only(es8326->regmap, false); regcache_sync(es8326->regmap); + /* reset internal clock state */ + regmap_write(es8326->regmap, ES8326_RESET, 0x1f); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + usleep_range(10000, 15000); + regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); + /* set headphone default type and detect pin */ + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); + + /* set internal oscillator as clock source of headpone cp */ + regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84); regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); - /* Two channel ADC */ - regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02); + /* clock manager reset release */ + regmap_write(es8326->regmap, ES8326_RESET, 0x17); + /* set headphone detection as half scan mode */ + regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08); + regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00); + + /* enable headphone driver */ + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); - regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F); - regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8); - regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88); - regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00); + /* calibrate for B version */ + es8326_calibrate(component); + /* turn off headphone out */ + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00); + /* set ADC and DAC in low power mode */ + regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); + + /* force micbias on */ + regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f); regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08); - regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22); - regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src); - regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src); - regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); - regmap_write(es8326->regmap, ES8326_HP_DET, - ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol); - regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src); - regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); + /* select vdda as micbias source */ + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); + /* set dac dsmclip = 1 */ + regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08); + regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15); + + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, + es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, + (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); + regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); + + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); - snd_soc_component_update_bits(component, ES8326_PGAGAIN, - ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, + ES8326_MIC1_SEL); - regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); - if ((reg & ES8326_VERSION_B) == 1) { - regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD); - regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); - regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F); - /* enable button detect */ - regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0); - } + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, + ES8326_MUTE); - es8326_irq(es8326->irq, es8326); + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | + ((es8326->version == ES8326_VERSION_B) ? + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + + es8326->jack_remove_retry = 0; + es8326->hp = 0; return 0; } @@ -683,6 +847,10 @@ static int es8326_suspend(struct snd_soc_component *component) regcache_cache_only(es8326->regmap, true); regcache_mark_dirty(es8326->regmap); + /* reset register value to default */ + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01); + usleep_range(1000, 3000); + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00); return 0; } @@ -712,7 +880,7 @@ static int es8326_probe(struct snd_soc_component *component) ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); if (ret != 0) { dev_dbg(component->dev, "jack-pol return %d", ret); - es8326->jack_pol = ES8326_HP_DET_BUTTON_POL | ES8326_HP_TYPE_OMTP; + es8326->jack_pol = ES8326_HP_TYPE_AUTO; } dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol); @@ -743,7 +911,7 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component, mutex_lock(&es8326->lock); if (es8326->jd_inverted) - snd_soc_component_update_bits(component, ES8326_HP_DET, + snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); es8326->jack = jack; diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h index 3f8f7da58062..90a08351d6ac 100644 --- a/sound/soc/codecs/es8326.h +++ b/sound/soc/codecs/es8326.h @@ -73,15 +73,19 @@ #define ES8326_DRC_RECOVERY 0x53 #define ES8326_DRC_WINSIZE 0x54 #define ES8326_HPJACK_TIMER 0x56 -#define ES8326_HP_DET 0x57 +#define ES8326_HPDET_TYPE 0x57 #define ES8326_INT_SOURCE 0x58 #define ES8326_INTOUT_IO 0x59 #define ES8326_SDINOUT1_IO 0x5A #define ES8326_SDINOUT23_IO 0x5B #define ES8326_JACK_PULSE 0x5C +#define ES8326_HP_MISC 0xF7 +#define ES8326_CTIA_OMTP_STA 0xF8 #define ES8326_PULLUP_CTL 0xF9 -#define ES8326_HP_DETECT 0xFB +#define ES8326_CSM_I2C_STA 0xFA +#define ES8326_HPDET_STA 0xFB +#define ES8326_CSM_MUTE_STA 0xFC #define ES8326_CHIP_ID1 0xFD #define ES8326_CHIP_ID2 0xFE #define ES8326_CHIP_VERSION 0xFF @@ -146,7 +150,7 @@ #define ES8326_ADC3_SHIFT 0 #define ES8326_ADC4_SHIFT 3 -/* ES8326_HP_DET */ +/* ES8326_HPDET_TYPE */ #define ES8326_HP_DET_SRC_PIN27 (1 << 5) #define ES8326_HP_DET_SRC_PIN9 (1 << 4) #define ES8326_HP_DET_JACK_POL (1 << 3) @@ -156,6 +160,13 @@ #define ES8326_HP_TYPE_AUTO (1 << 0) #define ES8326_HP_TYPE_AUTO_INV (0 << 0) +/* ES8326_INT_SOURCE */ +#define ES8326_INT_SRC_DAC_MOZ (1 << 0) +#define ES8326_INT_SRC_ADC_MOZ (1 << 1) +#define ES8326_INT_SRC_BUTTON (1 << 2) +#define ES8326_INT_SRC_PIN9 (1 << 3) +#define ES8326_INT_SRC_PIN27 (1 << 4) + /* ES8326_SDINOUT1_IO */ #define ES8326_IO_INPUT (0 << 0) #define ES8326_IO_SDIN_SLOT0 (1 << 0) @@ -174,12 +185,12 @@ #define ES8326_SDINOUT2_SHIFT 4 #define ES8326_SDINOUT3_SHIFT 0 -/* ES8326_HP_DETECT */ +/* ES8326_HPDET_STA */ #define ES8326_HPINSERT_FLAG (1 << 1) #define ES8326_HPBUTTON_FLAG (1 << 0) /* ES8326_CHIP_VERSION 0xFF */ -#define ES8326_VERSION_B (1 << 0) +#define ES8326_VERSION (1 << 0) +#define ES8326_VERSION_B (3 << 0) #endif - diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index d21f69f05342..13689e718d36 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -723,24 +723,6 @@ static u64 hdmi_codec_formats = SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_AC97; -static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { - .startup = hdmi_codec_startup, - .shutdown = hdmi_codec_shutdown, - .hw_params = hdmi_codec_hw_params, - .prepare = hdmi_codec_prepare, - .set_fmt = hdmi_codec_i2s_set_fmt, - .mute_stream = hdmi_codec_mute, - .auto_selectable_formats = &hdmi_codec_formats, - .num_auto_selectable_formats = 1, -}; - -static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { - .startup = hdmi_codec_startup, - .shutdown = hdmi_codec_shutdown, - .hw_params = hdmi_codec_hw_params, - .mute_stream = hdmi_codec_mute, -}; - #define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ @@ -921,10 +903,31 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { + .probe = hdmi_dai_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .prepare = hdmi_codec_prepare, + .set_fmt = hdmi_codec_i2s_set_fmt, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, + .auto_selectable_formats = &hdmi_codec_formats, + .num_auto_selectable_formats = 1, +}; + +static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { + .probe = hdmi_dai_spdif_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, +}; + static const struct snd_soc_dai_driver hdmi_i2s_dai = { .name = "i2s-hifi", .id = DAI_ID_I2S, - .probe = hdmi_dai_probe, .playback = { .stream_name = "I2S Playback", .channels_min = 2, @@ -942,13 +945,11 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = { .sig_bits = 24, }, .ops = &hdmi_codec_i2s_dai_ops, - .pcm_new = hdmi_codec_pcm_new, }; static const struct snd_soc_dai_driver hdmi_spdif_dai = { .name = "spdif-hifi", .id = DAI_ID_SPDIF, - .probe = hdmi_dai_spdif_probe, .playback = { .stream_name = "SPDIF Playback", .channels_min = 2, @@ -964,7 +965,6 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = { .formats = SPDIF_FORMATS, }, .ops = &hdmi_codec_spdif_dai_ops, - .pcm_new = hdmi_codec_pcm_new, }; static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 7c25acf6ff0d..d1cea93bdb59 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -301,7 +301,7 @@ static const struct regmap_config jz4740_codec_regmap_config = { .reg_defaults = jz4740_codec_reg_defaults, .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int jz4740_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c deleted file mode 100644 index b84f6f1f6800..000000000000 --- a/sound/soc/codecs/l3.c +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * L3 code - * - * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org> - * - * based on: - * - * L3 bus algorithm module. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/gpio.h> - -#include <sound/l3.h> - -/* - * Send one byte of data to the chip. Data is latched into the chip on - * the rising edge of the clock. - */ -static void sendbyte(struct l3_pins *adap, unsigned int byte) -{ - int i; - - for (i = 0; i < 8; i++) { - adap->setclk(adap, 0); - udelay(adap->data_hold); - adap->setdat(adap, byte & 1); - udelay(adap->data_setup); - adap->setclk(adap, 1); - udelay(adap->clock_high); - byte >>= 1; - } -} - -/* - * Send a set of bytes to the chip. We need to pulse the MODE line - * between each byte, but never at the start nor at the end of the - * transfer. - */ -static void sendbytes(struct l3_pins *adap, const u8 *buf, - int len) -{ - int i; - - for (i = 0; i < len; i++) { - if (i) { - udelay(adap->mode_hold); - adap->setmode(adap, 0); - udelay(adap->mode); - } - adap->setmode(adap, 1); - udelay(adap->mode_setup); - sendbyte(adap, buf[i]); - } -} - -int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len) -{ - adap->setclk(adap, 1); - adap->setdat(adap, 1); - adap->setmode(adap, 1); - udelay(adap->mode); - - adap->setmode(adap, 0); - udelay(adap->mode_setup); - sendbyte(adap, addr); - udelay(adap->mode_hold); - - sendbytes(adap, data, len); - - adap->setclk(adap, 1); - adap->setdat(adap, 1); - adap->setmode(adap, 0); - - return len; -} -EXPORT_SYMBOL_GPL(l3_write); - - -static void l3_set_clk(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_clk, val); -} - -static void l3_set_data(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_data, val); -} - -static void l3_set_mode(struct l3_pins *adap, int val) -{ - gpio_set_value(adap->gpio_mode, val); -} - -int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap) -{ - int ret; - - if (!adap->use_gpios) - return -EINVAL; - - ret = devm_gpio_request_one(dev, adap->gpio_data, - GPIOF_OUT_INIT_LOW, "l3_data"); - if (ret < 0) - return ret; - adap->setdat = l3_set_data; - - ret = devm_gpio_request_one(dev, adap->gpio_clk, - GPIOF_OUT_INIT_LOW, "l3_clk"); - if (ret < 0) - return ret; - adap->setclk = l3_set_clk; - - ret = devm_gpio_request_one(dev, adap->gpio_mode, - GPIOF_OUT_INIT_LOW, "l3_mode"); - if (ret < 0) - return ret; - adap->setmode = l3_set_mode; - - return 0; -} -EXPORT_SYMBOL_GPL(l3_set_gpio_ops); - -MODULE_DESCRIPTION("L3 bit-banging driver"); -MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 2dfaf4fcfbd3..a2cca3436c68 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -160,28 +160,17 @@ static int max98363_io_init(struct sdw_slave *slave) struct max98363_priv *max98363 = dev_get_drvdata(dev); int ret, reg; - if (max98363->first_hw_init) { - regcache_cache_only(max98363->regmap, false); + regcache_cache_only(max98363->regmap, false); + if (max98363->first_hw_init) regcache_cache_bypass(max98363->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!max98363->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - + if (!max98363->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(dev); - - pm_runtime_enable(dev); - } - pm_runtime_get_noresume(dev); ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, ®); @@ -410,6 +399,8 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap) max98363->regmap = regmap; max98363->slave = slave; + regcache_cache_only(max98363->regmap, true); + max98363->hw_init = false; max98363->first_hw_init = false; @@ -417,10 +408,26 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363, max98363_dai, ARRAY_SIZE(max98363_dai)); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + return 0; } static int max98363_sdw_probe(struct sdw_slave *slave, diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index df92242af960..b5cb951af570 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -361,28 +361,17 @@ static int max98373_io_init(struct sdw_slave *slave) struct device *dev = &slave->dev; struct max98373_priv *max98373 = dev_get_drvdata(dev); - if (max98373->first_hw_init) { - regcache_cache_only(max98373->regmap, false); + regcache_cache_only(max98373->regmap, false); + if (max98373->first_hw_init) regcache_cache_bypass(max98373->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!max98373->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(dev, 3000); - pm_runtime_use_autosuspend(dev); - + if (!max98373->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(dev); - - pm_runtime_enable(dev); - } - pm_runtime_get_noresume(dev); /* Software Reset */ @@ -753,6 +742,8 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) max98373->regmap = regmap; max98373->slave = slave; + regcache_cache_only(max98373->regmap, true); + max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg); max98373->cache = devm_kcalloc(dev, max98373->cache_num, sizeof(*max98373->cache), @@ -773,10 +764,27 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw, max98373_sdw_dai, ARRAY_SIZE(max98373_sdw_dai)); - if (ret < 0) + if (ret < 0) { dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; } static int max98373_update_status(struct sdw_slave *slave, @@ -834,10 +842,7 @@ static int max98373_sdw_probe(struct sdw_slave *slave, static int max98373_sdw_remove(struct sdw_slave *slave) { - struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev); - - if (max98373->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index bdc508e23e59..922ce0dc4e60 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -528,7 +528,8 @@ static int max98926_i2c_probe(struct i2c_client *i2c) "Failed to allocate regmap: %d\n", ret); goto err_out; } - if (of_property_read_bool(i2c->dev.of_node, "interleave-mode")) + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") || + of_property_read_bool(i2c->dev.of_node, "interleave-mode")) max98926->interleave_mode = true; if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 0aaf2e6ae78d..776f23d38ac5 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -162,10 +162,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0021_PCM_MASTER_MODE, - MAX98927_PCM_MASTER_MODE_MASK, - mode); + regmap_update_bits(max98927->regmap, MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MASK, mode); switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -178,10 +176,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, - invert); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, invert); /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -207,36 +203,31 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) if (!use_pdm) { /* pcm channel configuration */ - regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_FORMAT_MASK, - format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); - regmap_update_bits(max98927->regmap, - MAX98927_R003B_SPK_SRC_SEL, - MAX98927_SPK_SRC_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); } else { /* pdm channel configuration */ - regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 1); - regmap_update_bits(max98927->regmap, - MAX98927_R003B_SPK_SRC_SEL, - MAX98927_SPK_SRC_MASK, 3); + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 3); - regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + 0); } return 0; } @@ -283,9 +274,9 @@ static int max98927_set_clock(struct max98927_priv *max98927, return -EINVAL; } regmap_update_bits(max98927->regmap, - MAX98927_R0021_PCM_MASTER_MODE, - MAX98927_PCM_MASTER_MODE_MCLK_MASK, - i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); + MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MCLK_MASK, + i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); } if (!max98927->tdm_mode) { @@ -298,9 +289,8 @@ static int max98927_set_clock(struct max98927_priv *max98927, } regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - value); + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, value); } return 0; } @@ -333,9 +323,8 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream, max98927->ch_size = snd_pcm_format_width(params_format(params)); - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); dev_dbg(component->dev, "format supported %d", params_format(params)); @@ -375,27 +364,24 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream, goto err; } /* set DAI_SR to correct LRCLK frequency */ - regmap_update_bits(max98927->regmap, - MAX98927_R0023_PCM_SR_SETUP1, - MAX98927_PCM_SR_SET1_SR_MASK, - sampling_rate); - regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_SR_MASK, - sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); + regmap_update_bits(max98927->regmap, MAX98927_R0023_PCM_SR_SETUP1, + MAX98927_PCM_SR_SET1_SR_MASK, sampling_rate); + regmap_update_bits(max98927->regmap, MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); /* set sampling rate of IV */ if (max98927->interleave_mode && sampling_rate > MAX98927_PCM_SR_SET1_SR_16000) regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_IVADC_SR_MASK, - sampling_rate - 3); + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); else regmap_update_bits(max98927->regmap, - MAX98927_R0024_PCM_SR_SETUP2, - MAX98927_PCM_SR_SET2_IVADC_SR_MASK, - sampling_rate); + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); return max98927_set_clock(max98927, params); err: return -EINVAL; @@ -420,10 +406,8 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - bsel); + regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel); /* Channel size configuration */ switch (slot_width) { @@ -442,33 +426,26 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - regmap_update_bits(max98927->regmap, - MAX98927_R0020_PCM_MODE_CFG, - MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); /* Rx slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - rx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R0019_PCM_RX_EN_B, - (rx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R0019_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); /* Tx slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - tx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - (tx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + (tx_mask & 0xFF00) >> 8); /* Tx slot Hi-Z configuration */ - regmap_write(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - ~tx_mask & 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - (~tx_mask & 0xFF00) >> 8); + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); return 0; } @@ -506,20 +483,16 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w, max98927->tdm_mode = false; break; case SND_SOC_DAPM_POST_PMU: - regmap_update_bits(max98927->regmap, - MAX98927_R003A_AMP_EN, - MAX98927_AMP_EN_MASK, 1); - regmap_update_bits(max98927->regmap, - MAX98927_R00FF_GLOBAL_SHDN, - MAX98927_GLOBAL_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 1); break; case SND_SOC_DAPM_POST_PMD: - regmap_update_bits(max98927->regmap, - MAX98927_R00FF_GLOBAL_SHDN, - MAX98927_GLOBAL_EN_MASK, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R003A_AMP_EN, - MAX98927_AMP_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 0); break; default: return 0; @@ -532,8 +505,8 @@ static const char * const max98927_switch_text[] = { static const struct soc_enum dai_sel_enum = SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, - MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, - 3, max98927_switch_text); + MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, 3, + max98927_switch_text); static const struct snd_kcontrol_new max98927_dai_controls = SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); @@ -543,17 +516,17 @@ static const struct snd_kcontrol_new max98927_vi_control = static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN, - 0, 0, max98927_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + 0, 0, max98927_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, - &max98927_dai_controls), + &max98927_dai_controls), SND_SOC_DAPM_OUTPUT("BE_OUT"), SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, - MAX98927_R003E_MEAS_EN, 0, 0), + MAX98927_R003E_MEAS_EN, 0, 0), SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, - MAX98927_R003E_MEAS_EN, 1, 0), + MAX98927_R003E_MEAS_EN, 1, 0), SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, - &max98927_vi_control), + &max98927_vi_control), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON"), }; @@ -623,20 +596,19 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit, max98927_current_limit_text); static const struct snd_kcontrol_new max98927_snd_controls[] = { - SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, - 0, 6, 0, - max98927_spk_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, 0, 6, 0, + max98927_spk_tlv), SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL, - 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0, - max98927_digital_tlv), + 0, (1 << MAX98927_AMP_VOL_WIDTH) - 1, 0, + max98927_digital_tlv), SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN, - MAX98927_BROWNOUT_DSP_SHIFT, 1, 0), + MAX98927_BROWNOUT_DSP_SHIFT, 1, 0), SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG, - MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0), - SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, - MAX98927_DRE_EN_SHIFT, 1, 0), + MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0), + SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, MAX98927_DRE_EN_SHIFT, + 1, 0), SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL, - MAX98927_AMP_VOL_SEL_SHIFT, 1, 0), + MAX98927_AMP_VOL_SEL_SHIFT, 1, 0), SOC_ENUM("Boost Output Voltage", max98927_boost_voltage), SOC_ENUM("Current Limit", max98927_current_limit), }; @@ -682,117 +654,82 @@ static int max98927_probe(struct snd_soc_component *component) max98927->component = component; /* Software Reset */ - regmap_write(max98927->regmap, - MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); /* IV default slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 0xFF); - regmap_write(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 0xFF); - regmap_write(max98927->regmap, - MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, - 0x80); - regmap_write(max98927->regmap, - MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, - 0x1); + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0xFF); + regmap_write(max98927->regmap, MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + 0x80); + regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, + 0x1); /* Set inital volume (+13dB) */ - regmap_write(max98927->regmap, - MAX98927_R0036_AMP_VOL_CTRL, - 0x38); - regmap_write(max98927->regmap, - MAX98927_R003C_SPK_GAIN, - 0x05); + regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38); + regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05); /* Enable DC blocker */ - regmap_write(max98927->regmap, - MAX98927_R0037_AMP_DSP_CFG, - 0x03); + regmap_write(max98927->regmap, MAX98927_R0037_AMP_DSP_CFG, 0x03); /* Enable IMON VMON DC blocker */ - regmap_write(max98927->regmap, - MAX98927_R003F_MEAS_DSP_CFG, - 0xF7); + regmap_write(max98927->regmap, MAX98927_R003F_MEAS_DSP_CFG, 0xF7); /* Boost Output Voltage & Current limit */ - regmap_write(max98927->regmap, - MAX98927_R0040_BOOST_CTRL0, - 0x1C); - regmap_write(max98927->regmap, - MAX98927_R0042_BOOST_CTRL1, - 0x3E); + regmap_write(max98927->regmap, MAX98927_R0040_BOOST_CTRL0, 0x1C); + regmap_write(max98927->regmap, MAX98927_R0042_BOOST_CTRL1, 0x3E); /* Measurement ADC config */ - regmap_write(max98927->regmap, - MAX98927_R0043_MEAS_ADC_CFG, - 0x04); - regmap_write(max98927->regmap, - MAX98927_R0044_MEAS_ADC_BASE_MSB, - 0x00); - regmap_write(max98927->regmap, - MAX98927_R0045_MEAS_ADC_BASE_LSB, - 0x24); + regmap_write(max98927->regmap, MAX98927_R0043_MEAS_ADC_CFG, 0x04); + regmap_write(max98927->regmap, MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00); + regmap_write(max98927->regmap, MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x24); /* Brownout Level */ - regmap_write(max98927->regmap, - MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, - 0x06); + regmap_write(max98927->regmap, MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, + 0x06); /* Envelope Tracking configuration */ - regmap_write(max98927->regmap, - MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, - 0x08); - regmap_write(max98927->regmap, - MAX98927_R0086_ENV_TRACK_CTRL, - 0x01); - regmap_write(max98927->regmap, - MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, - 0x10); + regmap_write(max98927->regmap, MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, + 0x08); + regmap_write(max98927->regmap, MAX98927_R0086_ENV_TRACK_CTRL, 0x01); + regmap_write(max98927->regmap, MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, + 0x10); /* voltage, current slot configuration */ - regmap_write(max98927->regmap, - MAX98927_R001E_PCM_TX_CH_SRC_A, - (max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT| - max98927->v_l_slot)&0xFF); + regmap_write(max98927->regmap, MAX98927_R001E_PCM_TX_CH_SRC_A, + (max98927->i_l_slot << MAX98927_PCM_TX_CH_SRC_A_I_SHIFT | max98927->v_l_slot) & 0xFF); if (max98927->v_l_slot < 8) { regmap_update_bits(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 1 << max98927->v_l_slot, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - 1 << max98927->v_l_slot, - 1 << max98927->v_l_slot); + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->v_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->v_l_slot, + 1 << max98927->v_l_slot); } else { regmap_update_bits(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 1 << (max98927->v_l_slot - 8), 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - 1 << (max98927->v_l_slot - 8), - 1 << (max98927->v_l_slot - 8)); + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->v_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->v_l_slot - 8), + 1 << (max98927->v_l_slot - 8)); } if (max98927->i_l_slot < 8) { regmap_update_bits(max98927->regmap, - MAX98927_R001C_PCM_TX_HIZ_CTRL_A, - 1 << max98927->i_l_slot, 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001A_PCM_TX_EN_A, - 1 << max98927->i_l_slot, - 1 << max98927->i_l_slot); + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->i_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->i_l_slot, + 1 << max98927->i_l_slot); } else { regmap_update_bits(max98927->regmap, - MAX98927_R001D_PCM_TX_HIZ_CTRL_B, - 1 << (max98927->i_l_slot - 8), 0); - regmap_update_bits(max98927->regmap, - MAX98927_R001B_PCM_TX_EN_B, - 1 << (max98927->i_l_slot - 8), - 1 << (max98927->i_l_slot - 8)); + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->i_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->i_l_slot - 8), + 1 << (max98927->i_l_slot - 8)); } /* Set interleave mode */ if (max98927->interleave_mode) regmap_update_bits(max98927->regmap, - MAX98927_R001F_PCM_TX_CH_SRC_B, - MAX98927_PCM_TX_CH_INTERLEAVE_MASK, - MAX98927_PCM_TX_CH_INTERLEAVE_MASK); + MAX98927_R001F_PCM_TX_CH_SRC_B, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK); return 0; } @@ -809,8 +746,8 @@ static int max98927_resume(struct device *dev) { struct max98927_priv *max98927 = dev_get_drvdata(dev); - regmap_write(max98927->regmap, - MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); regcache_cache_only(max98927->regmap, false); regcache_sync(max98927->regmap); return 0; @@ -869,9 +806,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c) int reg = 0; struct max98927_priv *max98927 = NULL; - max98927 = devm_kzalloc(&i2c->dev, - sizeof(*max98927), GFP_KERNEL); - + max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL); if (!max98927) { ret = -ENOMEM; return ret; @@ -879,14 +814,14 @@ static int max98927_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, max98927); /* update interleave mode info */ - if (!of_property_read_u32(i2c->dev.of_node, - "interleave_mode", &value)) { - if (value > 0) - max98927->interleave_mode = true; - else - max98927->interleave_mode = false; - } else - max98927->interleave_mode = false; + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) { + max98927->interleave_mode = true; + } else { + if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode", + &value)) + if (value > 0) + max98927->interleave_mode = true; + } /* regmap initialization */ max98927->regmap @@ -897,9 +832,9 @@ static int max98927_i2c_probe(struct i2c_client *i2c) "Failed to allocate regmap: %d\n", ret); return ret; } - - max98927->reset_gpio - = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + + max98927->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_HIGH); if (IS_ERR(max98927->reset_gpio)) { ret = PTR_ERR(max98927->reset_gpio); return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin"); @@ -912,8 +847,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c) } /* Check Revision ID */ - ret = regmap_read(max98927->regmap, - MAX98927_R01FF_REV_ID, ®); + ret = regmap_read(max98927->regmap, MAX98927_R01FF_REV_ID, ®); if (ret < 0) { dev_err(&i2c->dev, "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID); @@ -938,9 +872,8 @@ static void max98927_i2c_remove(struct i2c_client *i2c) { struct max98927_priv *max98927 = i2c_get_clientdata(i2c); - if (max98927->reset_gpio) { + if (max98927->reset_gpio) gpiod_set_value_cansleep(max98927->reset_gpio, 1); - } } static const struct i2c_device_id max98927_i2c_id[] = { diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index ca6beb2d2649..f307374834b5 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -624,6 +624,36 @@ static int system_clock_control(struct snd_soc_dapm_widget *w, return 0; } +static int nau8821_left_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (!nau8821->left_input_single_end) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, + NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, 0); + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, system_clock_control, SND_SOC_DAPM_POST_PMD), @@ -635,8 +665,10 @@ static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = { NAU8821_POWERUP_ADCL_SFT, 0), SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2, NAU8821_POWERUP_ADCR_SFT, 0), + /* single-ended design only on the left */ SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL, - NAU8821_PUP_PGA_L_SFT, 0, NULL, 0), + NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL, NAU8821_PUP_PGA_R_SFT, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL, @@ -1677,6 +1709,8 @@ static int nau8821_read_device_properties(struct device *dev, "nuvoton,jkdet-pull-up"); nau8821->key_enable = device_property_read_bool(dev, "nuvoton,key-enable"); + nau8821->left_input_single_end = device_property_read_bool(dev, + "nuvoton,left-input-single-end"); ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", &nau8821->jkdet_polarity); if (ret) @@ -1760,6 +1794,12 @@ static void nau8821_init_regs(struct nau8821 *nau8821) NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64); regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64); + if (nau8821->left_input_single_end) { + regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE, + NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN); + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN); + } } static int nau8821_setup_irq(struct nau8821 *nau8821) diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h index d962293c218e..00a888ed07ce 100644 --- a/sound/soc/codecs/nau8821.h +++ b/sound/soc/codecs/nau8821.h @@ -433,6 +433,14 @@ #define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1) #define NAU8821_DAC_CAPACITOR_LSB 0x1 +/* MUTE_MIC_L_N (0x6b) */ +#define NAU8821_MUTE_MICNL_SFT 5 +#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT) +#define NAU8821_MUTE_MICNR_SFT 4 +#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT) +#define NAU8821_MUTE_MICRP_SFT 2 +#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT) + /* ANALOG_ADC_1 (0x71) */ #define NAU8821_MICDET_EN_SFT 0 #define NAU8821_MICDET_MASK 0x1 @@ -463,23 +471,39 @@ /* MIC_BIAS (0x74) */ #define NAU8821_MICBIAS_JKR2 (0x1 << 12) +#define NAU8821_MICBIAS_LOWNOISE_SFT 10 +#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT) #define NAU8821_MICBIAS_POWERUP_SFT 8 +#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT) #define NAU8821_MICBIAS_VOLTAGE_SFT 0 #define NAU8821_MICBIAS_VOLTAGE_MASK 0x7 /* BOOST (0x76) */ #define NAU8821_PRECHARGE_DIS (0x1 << 13) #define NAU8821_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8821_HP_BOOST_DISCHRG_SFT 11 +#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT) #define NAU8821_HP_BOOST_DIS_SFT 9 #define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT) #define NAU8821_HP_BOOST_G_DIS (0x1 << 8) #define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6) /* FEPGA (0x77) */ +#define NAU8821_ACDC_CTRL_SFT 14 +#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT) #define NAU8821_FEPGA_MODEL_SFT 4 #define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT) #define NAU8821_FEPGA_MODER_SFT 0 #define NAU8821_FEPGA_MODER_MASK 0xf +#define NAU8821_FEPGA_MODER_AAF 0x1 +#define NAU8821_FEPGA_MODER_DIS 0x2 +#define NAU8821_FEPGA_MODER_IMP12K 0x8 + /* PGA_GAIN (0x7e) */ #define NAU8821_PGA_GAIN_L_SFT 8 @@ -543,6 +567,7 @@ struct nau8821 { bool jkdet_enable; bool jkdet_pull_enable; bool jkdet_pull_up; + bool left_input_single_end; int jkdet_polarity; int jack_insert_debounce; int jack_eject_debounce; diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index 42bac8150f62..d5285baad53a 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -13,11 +13,9 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/acpi.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <sound/core.h> diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 38d9f69b08d6..99ec0f9a8362 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/fs.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c index 06800dad8798..44e7fe3c32da 100644 --- a/sound/soc/codecs/rt1015p.c +++ b/sound/soc/codecs/rt1015p.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index b1e69fa290b2..919a1f25e584 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -16,7 +16,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c new file mode 100644 index 000000000000..7295f44c77eb --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "rt1017-sdca-sdw.h" + +static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0x3206: + case 0xc000: + case 0xc001: + case 0xc022: + case 0xc030: + case 0xc104: + case 0xc10b: + case 0xc10c: + case 0xc110: + case 0xc112: + case 0xc300: + case 0xc301: + case 0xc318: + case 0xc325 ... 0xc328: + case 0xc331: + case 0xc340: + case 0xc350 ... 0xc351: + case 0xc500: + case 0xc502: + case 0xc504: + case 0xc507: + case 0xc509: + case 0xc510: + case 0xc512: + case 0xc518: + case 0xc51b: + case 0xc51d: + case 0xc520: + case 0xc540 ... 0xc542: + case 0xc550 ... 0xc552: + case 0xc600: + case 0xc602: + case 0xc612: + case 0xc622: + case 0xc632: + case 0xc642: + case 0xc651: + case 0xca00: + case 0xca09 ... 0xca0c: + case 0xca0e ... 0xca0f: + case 0xca10 ... 0xca11: + case 0xca16 ... 0xca17: + case 0xcb00: + case 0xcc00: + case 0xcc02: + case 0xd017: + case 0xd01a ... 0xd01c: + case 0xd101: + case 0xd20c: + case 0xd300: + case 0xd370: + case 0xd500: + case 0xd545 ... 0xd548: + case 0xd5a5 ... 0xd5a8: + case 0xd5aa ... 0xd5ad: + case 0xda04 ... 0xda07: + case 0xda09 ... 0xda0a: + case 0xda0c ... 0xda0f: + case 0xda11 ... 0xda14: + case 0xda16 ... 0xda19: + case 0xdab6 ... 0xdabb: + case 0xdb09 ... 0xdb0a: + case 0xdb14: + + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + return true; + default: + return false; + } +} + +static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0xc000: + case 0xc022: + case 0xc351: + case 0xc518: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + return true; + default: + return false; + } +} + +static const struct reg_sequence rt1017_blind_write[] = { + { 0xc001, 0x43 }, + { 0x2f55, 0x02 }, + { 0x3206, 0x80 }, + { 0x005f, 0x7f }, + { 0xd101, 0xa0 }, + { 0xc112, 0xc0 }, + { 0xc104, 0xaa }, + { 0xc110, 0x59 }, + { 0xc112, 0xc0 }, + { 0xc340, 0x80 }, + { 0xd017, 0x2c }, + { 0xd01a, 0xc8 }, + { 0xd01b, 0xcf }, + { 0xd01c, 0x0c }, + { 0xd20c, 0x14 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0x7f }, + { 0xdb14, 0x03 }, + { 0xcb00, 0x31 }, + { 0xc318, 0x44 }, + { 0xc325, 0xce }, + { 0xc326, 0x13 }, + { 0xc327, 0x5f }, + { 0xc328, 0xf3 }, + { 0xc350, 0xe1 }, + { 0xc351, 0x88 }, + { 0xc030, 0x14 }, + { 0xc331, 0xf2 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc651, 0xc0 }, + { 0xc550, 0xd0 }, + { 0xc612, 0x00 }, + { 0xc622, 0x00 }, + { 0xc632, 0x00 }, + { 0xc642, 0x00 }, + { 0xc602, 0xf0 }, + { 0xc600, 0xd0 }, + { 0xcc02, 0x78 }, + { 0xcc00, 0x90 }, + { 0xc300, 0x3f }, + { 0xc301, 0x1d }, + { 0xc10b, 0x2e }, + { 0xc10c, 0x36 }, + + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x6a }, + { 0xd5a7, 0xaa }, + { 0xd5a8, 0xaa }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x16 }, + { 0xd5ac, 0xdb }, + { 0xd5ad, 0x6d }, + { 0xd545, 0x09 }, + { 0xd546, 0x30 }, + { 0xd547, 0xf0 }, + { 0xd548, 0xf0 }, + { 0xd500, 0x20 }, + { 0xc504, 0x3f }, + { 0xc540, 0x00 }, + { 0xc541, 0x0a }, + { 0xc542, 0x1a }, + { 0xc512, 0x00 }, + { 0xc520, 0x40 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc500, 0x40 }, + { 0xc502, 0xde }, + { 0xc507, 0x05 }, + { 0xc509, 0x05 }, + { 0xc510, 0x40 }, + { 0xc518, 0xc0 }, + { 0xc500, 0xc0 }, + + { 0xda0c, 0x00 }, + { 0xda0d, 0x0b }, + { 0xda0e, 0x55 }, + { 0xda0f, 0x55 }, + { 0xda04, 0x00 }, + { 0xda05, 0x51 }, + { 0xda06, 0xeb }, + { 0xda07, 0x85 }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda11, 0x26 }, + { 0xda12, 0x66 }, + { 0xda13, 0x66 }, + { 0xda14, 0x66 }, + { 0xda16, 0x79 }, + { 0xda17, 0x99 }, + { 0xda18, 0x99 }, + { 0xda19, 0x99 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x07 }, + { 0xca0b, 0x89 }, + { 0xca0c, 0x61 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x03 }, + { 0xca10, 0xc4 }, + { 0xca11, 0xb0 }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x00 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x00 }, + { 0xd017, 0x0e }, + { 0xca00, 0xcd }, + { 0xc022, 0x84 }, +}; + +#define RT1017_MAX_REG_NUM 0x4108ffff + +static const struct regmap_config rt1017_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1017_sdca_readable_register, + .volatile_reg = rt1017_sdca_volatile_register, + .max_register = RT1017_MAX_REG_NUM, + .reg_defaults = rt1017_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1017_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists + * port = 1 for AMP playback + * port = 2 for IV capture + */ + prop->source_ports = BIT(2); /* BITMAP: 00000100 */ + prop->sink_ports = BIT(1); /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 64; + + return 0; +} + +static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (rt1017->hw_init) + return 0; + + if (rt1017->first_hw_init) { + regcache_cache_only(rt1017->regmap, false); + regcache_cache_bypass(rt1017->regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1017->regmap, 0xc000, 0x02); + + /* initial settings - blind write */ + regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write, + ARRAY_SIZE(rt1017_blind_write)); + + if (rt1017->first_hw_init) { + regcache_cache_bypass(rt1017->regmap, false); + regcache_mark_dirty(rt1017->regmap); + } else + rt1017->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1017->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "hw_init complete\n"); + return 0; +} + +static int rt1017_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1017->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1017_sdca_io_init(&slave->dev, slave); +} + +static const char * const rt1017_rx_data_ch_select[] = { + "Bypass", + "CN1", + "CN2", + "CN3", + "CN4", + "(1+2)/2", + "(1+3)/2", + "(1+4)/2", + "(2+3)/2", + "(2+4)/2", + "(3+4)/2", +}; + +static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), + 0, rt1017_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1017_sdca_controls[] = { + /* UDMPU Cluster Selection */ + SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1017_sto_dac = + SOC_DAPM_SINGLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1), + 0, 1, 1); + +static int rt1017_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + default: + break; + } + return 0; +} + +static int rt1017_sdca_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1, + RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG); + regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10); + break; + default: + break; + } + + return 0; +} + +static int rt1017_sdca_feedback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0, + rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPO"), + + SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0, + rt1017_sdca_pde23_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("I Gen"), + SND_SOC_DAPM_SIGGEN("V Gen"), +}; + +static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { + + { "DAC", "Switch", "DP1RX" }, + { "CLASS D", NULL, "DAC" }, + { "CLASS D", NULL, "PDE23" }, + { "SPO", NULL, "CLASS D" }, + + { "I Sense", NULL, "I Gen" }, + { "V Sense", NULL, "V Gen" }, + { "I Sense", NULL, "PDE23" }, + { "V Sense", NULL, "PDE23" }, + { "DP2TX", NULL, "I Sense" }, + { "DP2TX", NULL, "V Sense" }, +}; + +static struct sdw_slave_ops rt1017_sdca_slave_ops = { + .read_prop = rt1017_sdca_read_prop, + .update_status = rt1017_sdca_update_status, +}; + +static int rt1017_sdca_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static void rt1017_sdca_component_remove(struct snd_soc_component *component) +{ + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1017->regmap, true); +} + +static const struct snd_soc_component_driver soc_sdca_component_rt1017 = { + .probe = rt1017_sdca_component_probe, + .remove = rt1017_sdca_component_remove, + .controls = rt1017_sdca_controls, + .num_controls = ARRAY_SIZE(rt1017_sdca_controls), + .dapm_widgets = rt1017_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets), + .dapm_routes = rt1017_sdca_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes), + .endianness = 1, +}; + +static int rt1017_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1017_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels, ch_mask; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1017->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + num_channels = params_channels(params); + ch_mask = (1 << num_channels) - 1; + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = num_channels; + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + port_config.ch_mask = ch_mask; + port_config.num = port; + + dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", + params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), + direction, ch_mask, port); + + retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT1017_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1017_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1017_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1017_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1017->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops rt1017_sdca_ops = { + .hw_params = rt1017_sdca_pcm_hw_params, + .hw_free = rt1017_sdca_pcm_hw_free, + .set_stream = rt1017_sdca_set_sdw_stream, + .shutdown = rt1017_sdca_shutdown, +}; + +#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1017_sdca_dai[] = { + { + .name = "rt1017-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .ops = &rt1017_sdca_ops, + }, +}; + +static int rt1017_sdca_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017; + int ret; + + rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL); + if (!rt1017) + return -ENOMEM; + + dev_set_drvdata(dev, rt1017); + rt1017->sdw_slave = slave; + rt1017->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1017->hw_init = false; + rt1017->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_component_rt1017, + rt1017_sdca_dai, + ARRAY_SIZE(rt1017_sdca_dai)); + + return ret; +} + +static int rt1017_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1017_sdca_init(&slave->dev, regmap, slave); +} + +static int rt1017_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (rt1017->first_hw_init) + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1017_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id); + +static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (!rt1017->hw_init) + return 0; + + regcache_cache_only(rt1017->regmap, true); + + return 0; +} + +#define RT1017_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1017_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1017->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1017_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1017->regmap, false); + regcache_sync(rt1017->regmap); + + return 0; +} + +static const struct dev_pm_ops rt1017_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt1017_sdca_sdw_driver = { + .driver = { + .name = "rt1017-sdca", + .owner = THIS_MODULE, + .pm = &rt1017_sdca_pm, + }, + .probe = rt1017_sdca_sdw_probe, + .remove = rt1017_sdca_sdw_remove, + .ops = &rt1017_sdca_slave_ops, + .id_table = rt1017_sdca_id, +}; +module_sdw_driver(rt1017_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW"); +MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h new file mode 100644 index 000000000000..4932b5dbe3c0 --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT1017_SDW_H__ +#define __RT1017_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/soc.h> + +/* RT1017 SDCA Control - function number */ +#define FUNC_NUM_SMART_AMP 0x04 + +/* RT1017 SDCA entity */ +#define RT1017_SDCA_ENT_PDE23 0x31 +#define RT1017_SDCA_ENT_PDE22 0x33 +#define RT1017_SDCA_ENT_CS21 0x21 +#define RT1017_SDCA_ENT_SAPU29 0x29 +#define RT1017_SDCA_ENT_XU22 0x22 +#define RT1017_SDCA_ENT_FU 0x03 +#define RT1017_SDCA_ENT_UDMPU21 0x02 + +/* RT1017 SDCA control */ +#define RT1017_SDCA_CTL_FS_INDEX 0x10 +#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1017_SDCA_CTL_PROT_STAT 0x11 +#define RT1017_SDCA_CTL_BYPASS 0x01 +#define RT1017_SDCA_CTL_FU_MUTE 0x01 +#define RT1017_SDCA_CTL_FU_VOLUME 0x02 +#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10 + + +#define RT1017_CLASSD_INT_1 0xd300 +#define RT1017_PWM_TRIM_1 0xd370 + + +#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2) + +enum { + RT1017_SDCA_RATE_44100HZ = 0x8, + RT1017_SDCA_RATE_48000HZ = 0x9, + RT1017_SDCA_RATE_96000HZ = 0xb, + RT1017_SDCA_RATE_192000HZ = 0xd, +}; + +struct rt1017_sdca_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +static const struct reg_default rt1017_sdca_reg_defaults[] = { + { 0x3206, 0x00 }, + { 0xc001, 0x43 }, + { 0xc030, 0x54 }, + { 0xc104, 0x8a }, + { 0xc10b, 0x2f }, + { 0xc10c, 0x2f }, + { 0xc110, 0x49 }, + { 0xc112, 0x10 }, + { 0xc300, 0xff }, + { 0xc301, 0xdd }, + { 0xc318, 0x40 }, + { 0xc325, 0x00 }, + { 0xc326, 0x00 }, + { 0xc327, 0x00 }, + { 0xc328, 0x02 }, + { 0xc331, 0xb2 }, + { 0xc340, 0x02 }, + { 0xc350, 0x21 }, + { 0xc500, 0x00 }, + { 0xc502, 0x00 }, + { 0xc504, 0x3f }, + { 0xc507, 0x1f }, + { 0xc509, 0x1f }, + { 0xc510, 0x40 }, + { 0xc512, 0x00 }, + { 0xc518, 0x02 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc520, 0x00 }, + { 0xc540, 0x80 }, + { 0xc541, 0x00 }, + { 0xc542, 0x0a }, + { 0xc550, 0x80 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc600, 0x10 }, + { 0xc602, 0x83 }, + { 0xc612, 0x40 }, + { 0xc622, 0x40 }, + { 0xc632, 0x40 }, + { 0xc642, 0x40 }, + { 0xc651, 0x00 }, + { 0xca00, 0xc1 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x51 }, + { 0xca0b, 0xeb }, + { 0xca0c, 0x85 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x10 }, + { 0xca10, 0x62 }, + { 0xca11, 0x4d }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xcb00, 0x10 }, + { 0xcc00, 0x10 }, + { 0xcc02, 0x0b }, + { 0xd017, 0x09 }, + { 0xd01a, 0x00 }, + { 0xd01b, 0x00 }, + { 0xd01c, 0x00 }, + { 0xd101, 0xa0 }, + { 0xd20c, 0x14 }, + { 0xd300, 0x0f }, + { 0xd370, 0x18 }, + { 0xd500, 0x00 }, + { 0xd545, 0x0b }, + { 0xd546, 0xf9 }, + { 0xd547, 0xb2 }, + { 0xd548, 0xa9 }, + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x00 }, + { 0xd5a7, 0x00 }, + { 0xd5a8, 0x00 }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x00 }, + { 0xd5ac, 0x00 }, + { 0xd5ad, 0x00 }, + { 0xda04, 0x03 }, + { 0xda05, 0x33 }, + { 0xda06, 0x33 }, + { 0xda07, 0x33 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda0c, 0x00 }, + { 0xda0d, 0x01 }, + { 0xda0e, 0x5d }, + { 0xda0f, 0x86 }, + { 0xda11, 0x20 }, + { 0xda12, 0x00 }, + { 0xda13, 0x00 }, + { 0xda14, 0x00 }, + { 0xda16, 0x7f }, + { 0xda17, 0xff }, + { 0xda18, 0xff }, + { 0xda19, 0xff }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x01 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x01 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0xff }, + { 0xdb14, 0x00 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +#endif /* __RT1017_SDW_H__ */ diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index fd55049920c1..ceb8baa6a20d 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -18,7 +18,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 59895131e6e0..80888cbcf49c 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -12,10 +12,8 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/acpi.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <sound/core.h> diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index e566c8ddd3e9..63d4abf964d4 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -219,28 +219,17 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) if (rt1308->hw_init) return 0; - if (rt1308->first_hw_init) { - regcache_cache_only(rt1308->regmap, false); + regcache_cache_only(rt1308->regmap, false); + if (rt1308->first_hw_init) regcache_cache_bypass(rt1308->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt1308->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt1308->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag); @@ -637,6 +626,9 @@ static int rt1308_sdw_component_probe(struct snd_soc_component *component) rt1308->component = component; rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev); + if (!rt1308->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -699,6 +691,8 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, rt1308->sdw_slave = slave; rt1308->regmap = regmap; + regcache_cache_only(rt1308->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -710,10 +704,27 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1308, rt1308_sdw_dai, ARRAY_SIZE(rt1308_sdw_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static int rt1308_sdw_probe(struct sdw_slave *slave, @@ -726,17 +737,12 @@ static int rt1308_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt1308_sdw_init(&slave->dev, regmap, slave); - - return 0; + return rt1308_sdw_init(&slave->dev, regmap, slave); } static int rt1308_sdw_remove(struct sdw_slave *slave) { - struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - - if (rt1308->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index 9d07756cda40..86afb429d423 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -11,10 +11,8 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> -#include <linux/of_gpio.h> #include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/firmware.h> diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 721821d9e9af..47511f70119a 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -272,25 +272,16 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave) if (rt1316->hw_init) return 0; + regcache_cache_only(rt1316->regmap, false); if (rt1316->first_hw_init) { - regcache_cache_only(rt1316->regmap, false); regcache_cache_bypass(rt1316->regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -424,6 +415,15 @@ static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum, SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0, rt1316_rx_data_ch_select); +static const char * const rt1316_dac_output_vol_select[] = { + "immediately", + "zero crossing", + "zero crossing with soft ramp", +}; + +static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum, + 0xc010, 6, rt1316_dac_output_vol_select); + static const struct snd_kcontrol_new rt1316_snd_controls[] = { /* I2S Data Channel Selection */ @@ -442,6 +442,9 @@ static const struct snd_kcontrol_new rt1316_snd_controls[] = { /* IV mixer Control */ SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1), SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1), + + /* DAC Output Volume Control */ + SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum), }; static const struct snd_kcontrol_new rt1316_sto_dac = @@ -595,6 +598,9 @@ static int rt1316_sdw_component_probe(struct snd_soc_component *component) rt1316->component = component; rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev); + if (!rt1316->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -662,6 +668,8 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap, rt1316->sdw_slave = slave; rt1316->regmap = regmap; + regcache_cache_only(rt1316->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -673,10 +681,27 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1316, rt1316_sdw_dai, ARRAY_SIZE(rt1316_sdw_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static int rt1316_sdw_probe(struct sdw_slave *slave, @@ -694,10 +719,7 @@ static int rt1316_sdw_probe(struct sdw_slave *slave, static int rt1316_sdw_remove(struct sdw_slave *slave) { - struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); - - if (rt1316->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 16d750102c8c..ff364bde4a08 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -408,25 +408,15 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave) if (rt1318->hw_init) return 0; + regcache_cache_only(rt1318->regmap, false); if (rt1318->first_hw_init) { - regcache_cache_only(rt1318->regmap, false); regcache_cache_bypass(rt1318->regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -686,6 +676,9 @@ static int rt1318_sdw_component_probe(struct snd_soc_component *component) rt1318->component = component; + if (!rt1318->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret); if (ret < 0 && ret != -EACCES) @@ -752,6 +745,8 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap, rt1318->sdw_slave = slave; rt1318->regmap = regmap; + regcache_cache_only(rt1318->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -763,8 +758,25 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap, &soc_component_sdw_rt1318, rt1318_sdw_dai, ARRAY_SIZE(rt1318_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); - dev_dbg(&slave->dev, "%s\n", __func__); + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -784,10 +796,7 @@ static int rt1318_sdw_probe(struct sdw_slave *slave, static int rt1318_sdw_remove(struct sdw_slave *slave) { - struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); - - if (rt1318->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 362663abcb89..3ee6d85268ba 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -15,7 +15,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/sched.h> #include <linux/uaccess.h> #include <linux/regulator/consumer.h> diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index b3aac2373357..43fc7814fdde 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -17,7 +17,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/firmware.h> -#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index eceed8209787..15e1a62b9e57 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -12,11 +12,10 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> @@ -2571,7 +2570,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { - dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640_disable_jack_detect(component); return; } @@ -2626,7 +2625,7 @@ static void rt5640_enable_hda_jack_detect( NULL, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { - dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret); + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640->irq = -ENXIO; return; } @@ -2812,8 +2811,8 @@ static int rt5640_suspend(struct snd_soc_component *component) rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); regcache_mark_dirty(rt5640->regmap); - if (gpio_is_valid(rt5640->ldo1_en)) - gpio_set_value_cansleep(rt5640->ldo1_en, 0); + if (rt5640->ldo1_en) + gpiod_set_value_cansleep(rt5640->ldo1_en, 0); return 0; } @@ -2822,8 +2821,8 @@ static int rt5640_resume(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); - if (gpio_is_valid(rt5640->ldo1_en)) { - gpio_set_value_cansleep(rt5640->ldo1_en, 1); + if (rt5640->ldo1_en) { + gpiod_set_value_cansleep(rt5640->ldo1_en, 1); msleep(400); } @@ -2986,22 +2985,6 @@ static const struct acpi_device_id rt5640_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); #endif -static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) -{ - rt5640->ldo1_en = of_get_named_gpio(np, "realtek,ldo1-en-gpios", 0); - /* - * LDO1_EN is optional (it may be statically tied on the board). - * -ENOENT means that the property doesn't exist, i.e. there is no - * GPIO, so is not an error. Any other error code means the property - * exists, but could not be parsed. - */ - if (!gpio_is_valid(rt5640->ldo1_en) && - (rt5640->ldo1_en != -ENOENT)) - return rt5640->ldo1_en; - - return 0; -} - static int rt5640_i2c_probe(struct i2c_client *i2c) { struct rt5640_priv *rt5640; @@ -3015,12 +2998,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) return -ENOMEM; i2c_set_clientdata(i2c, rt5640); - if (i2c->dev.of_node) { - ret = rt5640_parse_dt(rt5640, i2c->dev.of_node); - if (ret) - return ret; - } else - rt5640->ldo1_en = -EINVAL; + rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5640->ldo1_en)) + return PTR_ERR(rt5640->ldo1_en); + + if (rt5640->ldo1_en) { + gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN"); + msleep(400); + } rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); if (IS_ERR(rt5640->regmap)) { @@ -3030,18 +3017,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5640->ldo1_en)) { - ret = devm_gpio_request_one(&i2c->dev, rt5640->ldo1_en, - GPIOF_OUT_INIT_HIGH, - "RT5640 LDO1_EN"); - if (ret < 0) { - dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n", - rt5640->ldo1_en, ret); - return ret; - } - msleep(400); - } - regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); if (val != RT5640_DEVICE_ID) { dev_err(&i2c->dev, diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index 9847a1ae01f4..94b9a502f7f9 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -2138,7 +2138,7 @@ struct rt5640_priv { struct regmap *regmap; struct clk *mclk; - int ldo1_en; /* GPIO for LDO1_EN */ + struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */ int irq; int jd_gpio_irq; int sysclk; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 5be5ec0260e9..038d93e20883 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -14,7 +14,6 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/acpi.h> #include <linux/dmi.h> @@ -81,6 +80,7 @@ static const struct reg_sequence init_list[] = { static const struct reg_sequence rt5650_init_list[] = { {0xf6, 0x0100}, + {RT5645_PWR_ANLG1, 0x02}, }; static const struct reg_default rt5645_reg[] = { @@ -1697,6 +1697,9 @@ static void hp_amp_power(struct snd_soc_component *component, int on) regmap_write(rt5645->regmap, RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00); snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, + RT5645_PWR_HP_L | RT5645_PWR_HP_R); msleep(90); } else { /* depop parameters */ @@ -1744,7 +1747,8 @@ static void hp_amp_power(struct snd_soc_component *component, int on) snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); msleep(100); snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001); - + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0); } else { snd_soc_component_update_bits(component, RT5645_DEPOP_M1, RT5645_HP_SG_MASK | @@ -3151,7 +3155,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse unsigned int val; if (jack_insert) { - regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206); /* for jack type detect */ snd_soc_dapm_force_enable_pin(dapm, "LDO2"); @@ -3196,6 +3200,8 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse if (rt5645->pdata.level_trigger_irq) regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); + + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); } else { /* jack out */ rt5645->jack_type = 0; @@ -4004,13 +4010,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c) regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080); - ret = regmap_register_patch(rt5645->regmap, init_list, + ret = regmap_multi_reg_write(rt5645->regmap, init_list, ARRAY_SIZE(init_list)); if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); if (rt5645->codec_type == CODEC_TYPE_RT5650) { - ret = regmap_register_patch(rt5645->regmap, rt5650_init_list, + ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list, ARRAY_SIZE(rt5650_init_list)); if (ret != 0) dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n", @@ -4221,8 +4227,7 @@ static int __maybe_unused rt5645_sys_resume(struct device *dev) if (rt5645->hp_jack) { rt5645->jack_type = 0; - queue_delayed_work(system_power_efficient_wq, - &rt5645->jack_detect_work, msecs_to_jiffies(0)); + rt5645_jack_detect_work(&rt5645->jack_detect_work.work); } return 0; } diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index df6f0d769bbd..a061028a16d8 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -16,7 +16,6 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index eade087b2d8b..0cecfd602415 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -11,11 +11,9 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 525713c33d71..a39de4a7df00 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <sound/core.h> @@ -4659,9 +4658,6 @@ static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev) of_property_read_u32(dev->of_node, "realtek,jd-src", &rt5665->pdata.jd_src); - rt5665->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - return 0; } @@ -4795,10 +4791,13 @@ static int rt5665_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5665->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5665->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5665")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + + rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5665->gpiod_ldo1_en)) { + dev_err(&i2c->dev, "Failed gpio request ldo1_en\n"); + return PTR_ERR(rt5665->gpiod_ldo1_en); } /* Sleep for 300 ms miniumum */ diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index f04c810fd710..4623b3e62487 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <sound/core.h> @@ -43,6 +42,7 @@ static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = { struct rt5668_priv { struct snd_soc_component *component; struct rt5668_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct snd_soc_jack *hs_jack; struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES]; @@ -2393,9 +2393,6 @@ static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev) of_property_read_u32(dev->of_node, "realtek,jd-src", &rt5668->pdata.jd_src); - rt5668->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - return 0; } @@ -2497,10 +2494,12 @@ static int rt5668_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5668->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5668->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5668")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5668->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5668->ldo1_en); } /* Sleep for 300 ms miniumum */ diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index fb8ffb5b2ff6..b05b4f73d8aa 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -170,11 +169,9 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5682->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5682")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); - } + ret = rt5682_get_ldo1(rt5682, &i2c->dev); + if (ret) + return ret; /* Sleep for 300 ms miniumum */ usleep_range(300000, 350000); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 4968a8c0064d..e67c2e19cb1a 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -12,8 +12,6 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> @@ -322,6 +320,14 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, return ret; } + + ret = rt5682_get_ldo1(rt5682, dev); + if (ret) + return ret; + + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -336,7 +342,25 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, ret = devm_snd_soc_register_component(dev, &rt5682_soc_component_dev, rt5682_dai, ARRAY_SIZE(rt5682_dai)); - dev_dbg(&slave->dev, "%s\n", __func__); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -352,30 +376,20 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) if (rt5682->hw_init) return 0; + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); + if (rt5682->first_hw_init) + regcache_cache_bypass(rt5682->regmap, true); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt5682->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt5682->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); - if (rt5682->first_hw_init) { - regcache_cache_only(rt5682->regmap, false); - regcache_cache_bypass(rt5682->regmap, true); - } - while (loop > 0) { regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); if (val == DEVICE_ID) @@ -674,9 +688,7 @@ static int rt5682_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return -EINVAL; - rt5682_sdw_init(&slave->dev, regmap, slave); - - return 0; + return rt5682_sdw_init(&slave->dev, regmap, slave); } static int rt5682_sdw_remove(struct sdw_slave *slave) @@ -686,8 +698,7 @@ static int rt5682_sdw_remove(struct sdw_slave *slave) if (rt5682->hw_init) cancel_delayed_work_sync(&rt5682->jack_detect_work); - if (rt5682->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } @@ -707,6 +718,7 @@ static int __maybe_unused rt5682_dev_suspend(struct device *dev) cancel_delayed_work_sync(&rt5682->jack_detect_work); + regcache_cache_only(rt5682->sdw_regmap, true); regcache_cache_only(rt5682->regmap, true); regcache_mark_dirty(rt5682->regmap); @@ -771,6 +783,7 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev) regmap_sync: slave->unattach_request = 0; + regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); regcache_sync(rt5682->regmap); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 5d992543b791..e3aca9c785a0 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -1017,6 +1016,9 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component, rt5682->hs_jack = hs_jack; + if (rt5682->is_sdw && !rt5682->first_hw_init) + return 0; + if (!hs_jack) { regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, RT5682_JD1_EN_MASK, RT5682_JD1_DIS); @@ -3091,9 +3093,6 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) device_property_read_u32(dev, "realtek,dmic-delay-ms", &rt5682->pdata.dmic_delay); - rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - if (device_property_read_string_array(dev, "clock-output-names", rt5682->pdata.dai_clk_names, RT5682_DAI_NUM_CLKS) < 0) @@ -3108,6 +3107,20 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) } EXPORT_SYMBOL_GPL(rt5682_parse_dt); +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev) +{ + rt5682->ldo1_en = devm_gpiod_get_optional(dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682->ldo1_en)) { + dev_err(dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682->ldo1_en); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_get_ldo1); + void rt5682_calibrate(struct rt5682_priv *rt5682) { int value, count; diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 1a43d595f341..b2d9e87af259 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -11,6 +11,7 @@ #include <sound/rt5682.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> @@ -1430,6 +1431,7 @@ struct rt5682_priv { struct snd_soc_component *component; struct device *i2c_dev; struct rt5682_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct regmap *sdw_regmap; struct snd_soc_jack *hs_jack; @@ -1481,6 +1483,7 @@ int rt5682_register_component(struct device *dev); void rt5682_calibrate(struct rt5682_priv *rt5682); void rt5682_reset(struct rt5682_priv *rt5682); int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev); +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev); int rt5682_register_dai_clks(struct rt5682_priv *rt5682); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index c77c675bd5f5..68ac5ea50396 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -15,8 +15,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> @@ -2973,9 +2972,6 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) device_property_read_u32(dev, "realtek,amic-delay-ms", &rt5682s->pdata.amic_delay); - rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node, - "realtek,ldo1-en-gpios", 0); - if (device_property_read_string_array(dev, "clock-output-names", rt5682s->pdata.dai_clk_names, RT5682S_DAI_NUM_CLKS) < 0) @@ -3172,10 +3168,12 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c) return ret; } - if (gpio_is_valid(rt5682s->pdata.ldo1_en)) { - if (devm_gpio_request_one(&i2c->dev, rt5682s->pdata.ldo1_en, - GPIOF_OUT_INIT_HIGH, "rt5682s")) - dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n"); + rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682s->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682s->ldo1_en); } /* Sleep for 50 ms minimum */ diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h index caa7733b430f..1d79d432d0d8 100644 --- a/sound/soc/codecs/rt5682s.h +++ b/sound/soc/codecs/rt5682s.h @@ -11,6 +11,7 @@ #include <sound/rt5682s.h> #include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/clk-provider.h> @@ -1446,6 +1447,7 @@ enum { struct rt5682s_priv { struct snd_soc_component *component; struct rt5682s_platform_data pdata; + struct gpio_desc *ldo1_en; struct regmap *regmap; struct snd_soc_jack *hs_jack; struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES]; diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 8b28e47775cc..52c33d56b143 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -452,9 +452,7 @@ static int rt700_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt700_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt700_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt700_sdw_remove(struct sdw_slave *slave) @@ -466,8 +464,7 @@ static int rt700_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt700->jack_btn_check_work); } - if (rt700->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index a04b9246256b..0ebf344a1b60 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -320,6 +320,10 @@ static int rt700_set_jack_detect(struct snd_soc_component *component, rt700->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt700->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -823,6 +827,9 @@ static int rt700_probe(struct snd_soc_component *component) rt700->component = component; + if (!rt700->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1099,6 +1106,8 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, rt700->sdw_regmap = sdw_regmap; rt700->regmap = regmap; + regcache_cache_only(rt700->regmap, true); + mutex_init(&rt700->disable_irq_lock); INIT_DELAYED_WORK(&rt700->jack_detect_work, @@ -1117,10 +1126,26 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ dev_dbg(&slave->dev, "%s\n", __func__); - return ret; + return 0; } int rt700_io_init(struct device *dev, struct sdw_slave *slave) @@ -1132,28 +1157,17 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave) if (rt700->hw_init) return 0; - if (rt700->first_hw_init) { - regcache_cache_only(rt700->regmap, false); + regcache_cache_only(rt700->regmap, false); + if (rt700->first_hw_init) regcache_cache_bypass(rt700->regmap, true); - } /* * PM runtime is only enabled when a Slave reports as Attached */ - if (!rt700->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - - /* update count of parent 'active' children */ + if (!rt700->first_hw_init) + /* PM runtime status is marked as 'active' only when a Slave reports as Attached */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); /* reset */ diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 23f23f714b39..935e597022d3 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -366,8 +366,7 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt711->jack_btn_check_work); } - if (rt711->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 07640d2f6e56..447154cb6010 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -507,6 +507,10 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, rt711->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -1215,6 +1219,9 @@ static int rt711_sdca_probe(struct snd_soc_component *component) rt711_sdca_parse_dt(rt711, &rt711->slave->dev); rt711->component = component; + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1406,6 +1413,9 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, rt711->regmap = regmap; rt711->mbq_regmap = mbq_regmap; + regcache_cache_only(rt711->regmap, true); + regcache_cache_only(rt711->mbq_regmap, true); + mutex_init(&rt711->calibrate_mutex); mutex_init(&rt711->disable_irq_lock); @@ -1431,9 +1441,27 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap, rt711_sdca_dai, ARRAY_SIZE(rt711_sdca_dai)); - dev_dbg(&slave->dev, "%s\n", __func__); + if (ret < 0) + return ret; - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711) @@ -1500,27 +1528,19 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; + regcache_cache_only(rt711->regmap, false); + regcache_cache_only(rt711->mbq_regmap, false); + if (rt711->first_hw_init) { - regcache_cache_only(rt711->regmap, false); regcache_cache_bypass(rt711->regmap, true); - regcache_cache_only(rt711->mbq_regmap, false); regcache_cache_bypass(rt711->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 33dced388f9e..3f5773310ae8 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -453,9 +453,7 @@ static int rt711_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt711_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt711_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt711_sdw_remove(struct sdw_slave *slave) @@ -468,8 +466,7 @@ static int rt711_sdw_remove(struct sdw_slave *slave) cancel_work_sync(&rt711->calibration_work); } - if (rt711->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index af53cbcc7bf2..66eaed13b0d6 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -462,6 +462,10 @@ static int rt711_set_jack_detect(struct snd_soc_component *component, rt711->hs_jack = hs_jack; + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -941,6 +945,9 @@ static int rt711_probe(struct snd_soc_component *component) rt711_parse_dt(rt711, &rt711->slave->dev); rt711->component = component; + if (!rt711->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1183,6 +1190,8 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, rt711->sdw_regmap = sdw_regmap; rt711->regmap = regmap; + regcache_cache_only(rt711->regmap, true); + mutex_init(&rt711->calibrate_mutex); mutex_init(&rt711->disable_irq_lock); @@ -1204,8 +1213,25 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt711, rt711_dai, ARRAY_SIZE(rt711_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - dev_dbg(&slave->dev, "%s\n", __func__); + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -1219,28 +1245,17 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave) if (rt711->hw_init) return 0; - if (rt711->first_hw_init) { - regcache_cache_only(rt711->regmap, false); + regcache_cache_only(rt711->regmap, false); + if (rt711->first_hw_init) regcache_cache_bypass(rt711->regmap, true); - } /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt711->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt711->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); rt711_reset(rt711->regmap); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 869cc7bfd178..ba08d03e717c 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -182,27 +182,18 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hw_init) return 0; + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); if (rt712->first_hw_init) { - regcache_cache_only(rt712->regmap, false); regcache_cache_bypass(rt712->regmap, true); - regcache_cache_only(rt712->mbq_regmap, false); regcache_cache_bypass(rt712->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); @@ -608,6 +599,9 @@ static int rt712_sdca_dmic_probe(struct snd_soc_component *component) rt712->component = component; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -777,6 +771,9 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap, rt712->regmap = regmap; rt712->mbq_regmap = mbq_regmap; + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -791,10 +788,27 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap, &soc_sdca_dev_rt712_dmic, rt712_sdca_dmic_dai, ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } @@ -954,10 +968,7 @@ static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave, static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) { - struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); - - if (rt712->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 6bc50396a0f6..6b644a89c589 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -363,8 +363,7 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt712->jack_btn_check_work); } - if (rt712->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); mutex_destroy(&rt712->calibrate_mutex); mutex_destroy(&rt712->disable_irq_lock); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 89d245655ca4..7077ff6ba1f4 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -453,6 +453,9 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, rt712->hs_jack = hs_jack; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume_and_get(component->dev); if (ret < 0) { if (ret != -EACCES) { @@ -960,6 +963,9 @@ static int rt712_sdca_probe(struct snd_soc_component *component) rt712_sdca_parse_dt(rt712, &rt712->slave->dev); rt712->component = component; + if (!rt712->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -1183,6 +1189,9 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, rt712->regmap = regmap; rt712->mbq_regmap = mbq_regmap; + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + mutex_init(&rt712->calibrate_mutex); mutex_init(&rt712->disable_irq_lock); @@ -1207,10 +1216,27 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap, else ret = devm_snd_soc_register_component(dev, &soc_sdca_dev_rt712, rt712_sdca_dai, 1); + if (ret < 0) + return ret; - dev_dbg(&slave->dev, "%s\n", __func__); + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); - return ret; + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; } int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) @@ -1224,27 +1250,18 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt712->hw_init) return 0; + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); if (rt712->first_hw_init) { - regcache_cache_only(rt712->regmap, false); regcache_cache_bypass(rt712->regmap, true); - regcache_cache_only(rt712->mbq_regmap, false); regcache_cache_bypass(rt712->mbq_regmap, true); } else { /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); } pm_runtime_get_noresume(&slave->dev); diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index df10916bab46..ab54a67a27eb 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -193,10 +193,7 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave, static int rt715_sdca_sdw_remove(struct sdw_slave *slave) { - struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - - if (rt715->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index b989f907784b..9fa96fd83d4a 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -761,8 +761,12 @@ static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = { static int rt715_sdca_probe(struct snd_soc_component *component) { + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); int ret; + if (!rt715->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -977,6 +981,10 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, rt715->regmap = regmap; rt715->mbq_regmap = mbq_regmap; rt715->hw_sdw_ver = slave->id.sdw_version; + + regcache_cache_only(rt715->regmap, true); + regcache_cache_only(rt715->mbq_regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -988,6 +996,25 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, &soc_codec_dev_rt715_sdca, rt715_sdca_dai, ARRAY_SIZE(rt715_sdca_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); return ret; } @@ -1000,22 +1027,16 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave) if (rt715->hw_init) return 0; + regcache_cache_only(rt715->regmap, false); + regcache_cache_only(rt715->mbq_regmap, false); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ if (!rt715->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - rt715->first_hw_init = true; } diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 6db87442b783..21f37babd148 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -508,17 +508,12 @@ static int rt715_sdw_probe(struct sdw_slave *slave, if (IS_ERR(regmap)) return PTR_ERR(regmap); - rt715_init(&slave->dev, sdw_regmap, regmap, slave); - - return 0; + return rt715_init(&slave->dev, sdw_regmap, regmap, slave); } static int rt715_sdw_remove(struct sdw_slave *slave) { - struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); - - if (rt715->first_hw_init) - pm_runtime_disable(&slave->dev); + pm_runtime_disable(&slave->dev); return 0; } diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 6c2e165dd621..b59230c8fd32 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -16,14 +16,11 @@ #include <linux/pm_runtime.h> #include <linux/pm.h> #include <linux/soundwire/sdw.h> -#include <linux/gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -52,6 +49,60 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg, return ret; } +static int rt715_index_write_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt715_index_read_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp, orig; + int ret; + + ret = rt715_index_read_nid(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + return rt715_index_write_nid(regmap, nid, reg, tmp); +} + +static void rt715_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT715_FUNC_RESET, 0); + rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS, + RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG, + RT715_CLEAR_HIDDEN_REG); +} + + static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, unsigned int addr_l, unsigned int val_h, unsigned int *r_val, unsigned int *l_val) @@ -740,8 +791,12 @@ static int rt715_set_bias_level(struct snd_soc_component *component, static int rt715_probe(struct snd_soc_component *component) { + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); int ret; + if (!rt715->first_hw_init) + return 0; + ret = pm_runtime_resume(component->dev); if (ret < 0 && ret != -EACCES) return ret; @@ -984,6 +1039,8 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, rt715->regmap = regmap; rt715->sdw_regmap = sdw_regmap; + regcache_cache_only(rt715->regmap, true); + /* * Mark hw_init to false * HW init will be performed when device reports present @@ -995,8 +1052,25 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap, &soc_codec_dev_rt715, rt715_dai, ARRAY_SIZE(rt715_dai)); + if (ret < 0) + return ret; - return ret; + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; } int rt715_io_init(struct device *dev, struct sdw_slave *slave) @@ -1006,25 +1080,19 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave) if (rt715->hw_init) return 0; + regcache_cache_only(rt715->regmap, false); + /* - * PM runtime is only enabled when a Slave reports as Attached + * PM runtime status is marked as 'active' only when a Slave reports as Attached */ - if (!rt715->first_hw_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - + if (!rt715->first_hw_init) /* update count of parent 'active' children */ pm_runtime_set_active(&slave->dev); - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - } - pm_runtime_get_noresume(&slave->dev); + rt715_reset(rt715->regmap); + /* Mute nid=08h/09h */ regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080); regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 12a0ae656d09..6e37bf64e12f 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -48,6 +48,7 @@ struct rt715_priv { #define RT715_INLINE_CMD 0x55 /* Index (NID:20h) */ +#define RT715_VD_CLEAR_CTRL 0x01 #define RT715_SDW_INPUT_SEL 0x39 #define RT715_EXT_DMIC_CLK_CTRL2 0x54 @@ -71,6 +72,8 @@ struct rt715_priv { #define RT715_READ_HDA_0 0x2015 #define RT715_PRIV_INDEX_W_H 0x7520 #define RT715_PRIV_INDEX_W_L 0x85a0 +#define RT715_PRIV_INDEX_W_H_2 0x7500 +#define RT715_PRIV_INDEX_W_L_2 0x8580 #define RT715_PRIV_DATA_W_H 0x7420 #define RT715_PRIV_DATA_W_L 0x84a0 #define RT715_PRIV_INDEX_R_H 0x9d20 @@ -198,6 +201,10 @@ struct rt715_priv { #define RT715_SET_DMIC4_CONFIG_DEFAULT4\ (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4) +/* vendor register clear ctrl-1 (0x01)(NID:20h) */ +#define RT715_CLEAR_HIDDEN_REG (0x1 << 15) + + #define RT715_MUTE_SFT 7 #define RT715_DIR_IN_SFT 6 #define RT715_DIR_OUT_SFT 7 diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c index 008cb3eb5758..8c6665677a17 100644 --- a/sound/soc/codecs/ssm3515.c +++ b/sound/soc/codecs/ssm3515.c @@ -437,7 +437,7 @@ MODULE_DEVICE_TABLE(of, ssm3515_of_match); static struct i2c_driver ssm3515_i2c_driver = { .driver = { .name = "ssm3515", - .of_match_table = of_match_ptr(ssm3515_of_match), + .of_match_table = ssm3515_of_match, }, .probe = ssm3515_i2c_probe, }; diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 4a694d0bfd68..34ffd32ab9dc 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1022,7 +1022,7 @@ static const struct regmap_config sta32x_regmap = { .max_register = STA32X_FDRC2, .reg_defaults = sta32x_regs, .num_reg_defaults = ARRAY_SIZE(sta32x_regs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &sta32x_write_regs, .rd_table = &sta32x_read_regs, .volatile_table = &sta32x_volatile_regs, diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index d05f3fd57661..e4a9e9241c60 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1065,7 +1065,7 @@ static const struct regmap_config sta350_regmap = { .max_register = STA350_MISC2, .reg_defaults = sta350_regs, .num_reg_defaults = ARRAY_SIZE(sta350_regs), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .wr_table = &sta350_write_regs, .rd_table = &sta350_read_regs, .volatile_table = &sta350_volatile_regs, diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 0ac08478ddac..eedafef775e5 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -331,7 +331,7 @@ static const struct regmap_config sta529_regmap = { .max_register = STA529_MAX_REGISTER, .readable_reg = sta529_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = sta529_reg_defaults, .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), }; diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c index 1824a71fe053..2f9f10a4dfed 100644 --- a/sound/soc/codecs/stac9766.c +++ b/sound/soc/codecs/stac9766.c @@ -67,7 +67,7 @@ static const struct regmap_config stac9766_regmap_config = { .reg_stride = 2, .val_bits = 16, .max_register = 0x78, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = regmap_ac97_default_volatile, diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c index 99545bcb2ba9..c421906a0694 100644 --- a/sound/soc/codecs/sti-sas.c +++ b/sound/soc/codecs/sti-sas.c @@ -316,7 +316,7 @@ static const struct regmap_config stih407_sas_regmap = { .reg_defaults = stih407_sas_reg_defaults, .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), .volatile_reg = sti_sas_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_read = sti_sas_read_reg, .reg_write = sti_sas_write_reg, }; diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index aca3756ffab6..3b53eba38a0b 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -520,12 +520,11 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c) } tas5805m->dsp_cfg_len = fw->size; - tas5805m->dsp_cfg_data = devm_kmalloc(dev, fw->size, GFP_KERNEL); + tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); if (!tas5805m->dsp_cfg_data) { release_firmware(fw); return -ENOMEM; } - memcpy(tas5805m->dsp_cfg_data, fw->data, fw->size); release_firmware(fw); diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index c116e82f712d..5c0a76a4a106 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -321,7 +321,7 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, u8 divisor; divisor = DIV_ROUND_UP(parent_rate, rate); - if (divisor > 128) + if (divisor > AIC32X4_DIV_MAX) return -EINVAL; return regmap_update_bits(div->regmap, div->reg, @@ -334,7 +334,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, unsigned long divisor; divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate); - if (divisor > 128) + if (divisor > AIC32X4_DIV_MAX) return -EINVAL; req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor); @@ -345,12 +345,18 @@ static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_aic32x4 *div = to_clk_aic32x4(hw); - unsigned int val; + int err; + + err = regmap_read(div->regmap, div->reg, &val); + if (err) + return 0; - regmap_read(div->regmap, div->reg, &val); + val &= AIC32X4_DIV_MASK; + if (!val) + val = AIC32X4_DIV_MAX; - return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK); + return DIV_ROUND_UP(parent_rate, val); } static const struct clk_ops aic32x4_div_ops = { diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index ffe1828a4b7e..6829834a412f 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1349,7 +1349,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) return -ENOMEM; aic32x4->dev = dev; - aic32x4->type = (enum aic32x4_type)dev_get_drvdata(dev); + aic32x4->type = (uintptr_t)dev_get_drvdata(dev); dev_set_drvdata(dev, aic32x4); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 4de5bd9e8cc5..d6101ce73f80 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -223,8 +223,9 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_REFPOWERUP_120MS 0x07 /* Common mask and enable for all of the dividers */ -#define AIC32X4_DIVEN BIT(7) -#define AIC32X4_DIV_MASK GENMASK(6, 0) +#define AIC32X4_DIVEN BIT(7) +#define AIC32X4_DIV_MASK GENMASK(6, 0) +#define AIC32X4_DIV_MAX 128 /* Clock Limits */ #define AIC32X4_MAX_DOSR_FREQ 6200000 diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c deleted file mode 100644 index 1a62bec94005..000000000000 --- a/sound/soc/codecs/uda134x.c +++ /dev/null @@ -1,587 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * uda134x.c -- UDA134X ALSA SoC Codec driver - * - * Modifications by Christian Pellegrin <chripell@evolware.org> - * - * Copyright 2007 Dension Audio Systems Ltd. - * Author: Zoltan Devai - * - * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/initval.h> - -#include <sound/uda134x.h> -#include <sound/l3.h> - -#include "uda134x.h" - - -#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000 -#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) - -struct uda134x_priv { - int sysclk; - int dai_fmt; - - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; - - struct regmap *regmap; - struct uda134x_platform_data *pd; -}; - -static const struct reg_default uda134x_reg_defaults[] = { - { UDA134X_EA000, 0x04 }, - { UDA134X_EA001, 0x04 }, - { UDA134X_EA010, 0x04 }, - { UDA134X_EA011, 0x00 }, - { UDA134X_EA100, 0x00 }, - { UDA134X_EA101, 0x00 }, - { UDA134X_EA110, 0x00 }, - { UDA134X_EA111, 0x00 }, - { UDA134X_STATUS0, 0x00 }, - { UDA134X_STATUS1, 0x03 }, - { UDA134X_DATA000, 0x00 }, - { UDA134X_DATA001, 0x00 }, - { UDA134X_DATA010, 0x00 }, - { UDA134X_DATA011, 0x00 }, - { UDA134X_DATA1, 0x00 }, -}; - -/* - * Write to the uda134x registers - * - */ -static int uda134x_regmap_write(void *context, unsigned int reg, - unsigned int value) -{ - struct uda134x_platform_data *pd = context; - int ret; - u8 addr; - u8 data = value; - - switch (reg) { - case UDA134X_STATUS0: - case UDA134X_STATUS1: - addr = UDA134X_STATUS_ADDR; - data |= (reg - UDA134X_STATUS0) << 7; - break; - case UDA134X_DATA000: - case UDA134X_DATA001: - case UDA134X_DATA010: - case UDA134X_DATA011: - addr = UDA134X_DATA0_ADDR; - data |= (reg - UDA134X_DATA000) << 6; - break; - case UDA134X_DATA1: - addr = UDA134X_DATA1_ADDR; - break; - default: - /* It's an extended address register */ - addr = (reg | UDA134X_EXTADDR_PREFIX); - - ret = l3_write(&pd->l3, - UDA134X_DATA0_ADDR, &addr, 1); - if (ret != 1) - return -EIO; - - addr = UDA134X_DATA0_ADDR; - data = (value | UDA134X_EXTDATA_PREFIX); - break; - } - - ret = l3_write(&pd->l3, - addr, &data, 1); - if (ret != 1) - return -EIO; - - return 0; -} - -static inline void uda134x_reset(struct snd_soc_component *component) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - unsigned int mask = 1<<6; - - regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask); - msleep(1); - regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0); -} - -static int uda134x_mute(struct snd_soc_dai *dai, int mute, int direction) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component); - unsigned int mask = 1<<2; - unsigned int val; - - pr_debug("%s mute: %d\n", __func__, mute); - - if (mute) - val = mask; - else - val = 0; - - return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val); -} - -static int uda134x_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct snd_pcm_runtime *master_runtime; - - if (uda134x->master_substream) { - master_runtime = uda134x->master_substream->runtime; - - pr_debug("%s constraining to %d bits at %d\n", __func__, - master_runtime->sample_bits, - master_runtime->rate); - - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - master_runtime->rate); - - snd_pcm_hw_constraint_single(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits); - - uda134x->slave_substream = substream; - } else - uda134x->master_substream = substream; - - return 0; -} - -static void uda134x_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - if (uda134x->master_substream == substream) - uda134x->master_substream = uda134x->slave_substream; - - uda134x->slave_substream = NULL; -} - -static int uda134x_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - unsigned int hw_params = 0; - - if (substream == uda134x->slave_substream) { - pr_debug("%s ignoring hw_params for slave substream\n", - __func__); - return 0; - } - - pr_debug("%s sysclk: %d, rate:%d\n", __func__, - uda134x->sysclk, params_rate(params)); - - /* set SYSCLK / fs ratio */ - switch (uda134x->sysclk / params_rate(params)) { - case 512: - break; - case 384: - hw_params |= (1<<4); - break; - case 256: - hw_params |= (1<<5); - break; - default: - printk(KERN_ERR "%s unsupported fs\n", __func__); - return -EINVAL; - } - - pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__, - uda134x->dai_fmt, params_format(params)); - - /* set DAI format and word length */ - switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - break; - case SND_SOC_DAIFMT_RIGHT_J: - switch (params_width(params)) { - case 16: - hw_params |= (1<<1); - break; - case 18: - hw_params |= (1<<2); - break; - case 20: - hw_params |= ((1<<2) | (1<<1)); - break; - default: - printk(KERN_ERR "%s unsupported format (right)\n", - __func__); - return -EINVAL; - } - break; - case SND_SOC_DAIFMT_LEFT_J: - hw_params |= (1<<3); - break; - default: - printk(KERN_ERR "%s unsupported format\n", __func__); - return -EINVAL; - } - - return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, - STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params); -} - -static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai, - int clk_id, unsigned int freq, int dir) -{ - struct snd_soc_component *component = codec_dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__, - clk_id, freq, dir); - - /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable - because the codec is slave. Of course limitations of the clock - master (the IIS controller) apply. - We'll error out on set_hw_params if it's not OK */ - if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { - uda134x->sysclk = freq; - return 0; - } - - printk(KERN_ERR "%s unsupported sysclk\n", __func__); - return -EINVAL; -} - -static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_component *component = codec_dai->component; - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - - pr_debug("%s fmt: %08X\n", __func__, fmt); - - /* codec supports only full consumer mode */ - if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { - printk(KERN_ERR "%s unsupported clocking mode\n", __func__); - return -EINVAL; - } - - /* no support for clock inversion */ - if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { - printk(KERN_ERR "%s unsupported clock inversion\n", __func__); - return -EINVAL; - } - - /* We can't setup DAI format here as it depends on the word bit num */ - /* so let's just store the value for later */ - uda134x->dai_fmt = fmt; - - return 0; -} - -static int uda134x_set_bias_level(struct snd_soc_component *component, - enum snd_soc_bias_level level) -{ - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct uda134x_platform_data *pd = uda134x->pd; - pr_debug("%s bias level %d\n", __func__, level); - - switch (level) { - case SND_SOC_BIAS_ON: - break; - case SND_SOC_BIAS_PREPARE: - /* power on */ - if (pd->power) { - pd->power(1); - regcache_sync(uda134x->regmap); - } - break; - case SND_SOC_BIAS_STANDBY: - break; - case SND_SOC_BIAS_OFF: - /* power off */ - if (pd->power) { - pd->power(0); - regcache_mark_dirty(uda134x->regmap); - } - break; - } - return 0; -} - -static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1", - "Minimum2", "Maximum"}; -static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; -static const char *uda134x_mixmode[] = {"Differential", "Analog1", - "Analog2", "Both"}; - -static const struct soc_enum uda134x_mixer_enum[] = { -SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting), -SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph), -SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode), -}; - -static const struct snd_kcontrol_new uda1341_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), -SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0), -SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1), -SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1), - -SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0), -SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0), - -SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), -SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), - -SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), -SOC_ENUM("Input Mux", uda134x_mixer_enum[2]), - -SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0), -SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1), -SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0), - -SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0), -SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0), -SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0), -SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0), -SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0), -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -static const struct snd_kcontrol_new uda1340_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), - -SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), -SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), - -SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), - -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -static const struct snd_kcontrol_new uda1345_snd_controls[] = { -SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), - -SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), - -SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), -}; - -/* UDA1341 has the DAC/ADC power down in STATUS1 */ -static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0), -}; - -/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */ -static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0), - SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0), -}; - -/* Common DAPM widgets */ -static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("VINL1"), - SND_SOC_DAPM_INPUT("VINR1"), - SND_SOC_DAPM_INPUT("VINL2"), - SND_SOC_DAPM_INPUT("VINR2"), - SND_SOC_DAPM_OUTPUT("VOUTL"), - SND_SOC_DAPM_OUTPUT("VOUTR"), -}; - -static const struct snd_soc_dapm_route uda134x_dapm_routes[] = { - { "ADC", NULL, "VINL1" }, - { "ADC", NULL, "VINR1" }, - { "ADC", NULL, "VINL2" }, - { "ADC", NULL, "VINR2" }, - { "VOUTL", NULL, "DAC" }, - { "VOUTR", NULL, "DAC" }, -}; - -static const struct snd_soc_dai_ops uda134x_dai_ops = { - .startup = uda134x_startup, - .shutdown = uda134x_shutdown, - .hw_params = uda134x_hw_params, - .mute_stream = uda134x_mute, - .set_sysclk = uda134x_set_dai_sysclk, - .set_fmt = uda134x_set_dai_fmt, - .no_capture_mute = 1, -}; - -static struct snd_soc_dai_driver uda134x_dai = { - .name = "uda134x-hifi", - /* playback capabilities */ - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = UDA134X_RATES, - .formats = UDA134X_FORMATS, - }, - /* capture capabilities */ - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = UDA134X_RATES, - .formats = UDA134X_FORMATS, - }, - /* pcm operations */ - .ops = &uda134x_dai_ops, -}; - -static int uda134x_soc_probe(struct snd_soc_component *component) -{ - struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); - struct uda134x_platform_data *pd = uda134x->pd; - const struct snd_soc_dapm_widget *widgets; - unsigned int num_widgets; - int ret; - - printk(KERN_INFO "UDA134X SoC Audio Codec\n"); - - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1341: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", - pd->model); - return -EINVAL; - } - - if (pd->power) - pd->power(1); - - uda134x_reset(component); - - if (pd->model == UDA134X_UDA1341) { - widgets = uda1341_dapm_widgets; - num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); - } else { - widgets = uda1340_dapm_widgets; - num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); - } - - ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); - if (ret) { - printk(KERN_ERR "%s failed to register dapm controls: %d", - __func__, ret); - return ret; - } - - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - ret = snd_soc_add_component_controls(component, uda1340_snd_controls, - ARRAY_SIZE(uda1340_snd_controls)); - break; - case UDA134X_UDA1341: - ret = snd_soc_add_component_controls(component, uda1341_snd_controls, - ARRAY_SIZE(uda1341_snd_controls)); - break; - case UDA134X_UDA1345: - ret = snd_soc_add_component_controls(component, uda1345_snd_controls, - ARRAY_SIZE(uda1345_snd_controls)); - break; - default: - printk(KERN_ERR "%s unknown codec type: %d", - __func__, pd->model); - return -EINVAL; - } - - if (ret < 0) { - printk(KERN_ERR "UDA134X: failed to register controls\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_component_driver soc_component_dev_uda134x = { - .probe = uda134x_soc_probe, - .set_bias_level = uda134x_set_bias_level, - .dapm_widgets = uda134x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), - .dapm_routes = uda134x_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), - .suspend_bias_off = 1, - .idle_bias_on = 1, - .use_pmdown_time = 1, - .endianness = 1, -}; - -static const struct regmap_config uda134x_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = UDA134X_DATA1, - .reg_defaults = uda134x_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults), - .cache_type = REGCACHE_RBTREE, - - .reg_write = uda134x_regmap_write, -}; - -static int uda134x_codec_probe(struct platform_device *pdev) -{ - struct uda134x_platform_data *pd = pdev->dev.platform_data; - struct uda134x_priv *uda134x; - int ret; - - if (!pd) { - dev_err(&pdev->dev, "Missing L3 bitbang function\n"); - return -ENODEV; - } - - uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL); - if (!uda134x) - return -ENOMEM; - - uda134x->pd = pd; - platform_set_drvdata(pdev, uda134x); - - if (pd->l3.use_gpios) { - ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3); - if (ret < 0) - return ret; - } - - uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, - &uda134x_regmap_config); - if (IS_ERR(uda134x->regmap)) - return PTR_ERR(uda134x->regmap); - - return devm_snd_soc_register_component(&pdev->dev, - &soc_component_dev_uda134x, &uda134x_dai, 1); -} - -static struct platform_driver uda134x_codec_driver = { - .driver = { - .name = "uda134x-codec", - }, - .probe = uda134x_codec_probe, -}; - -module_platform_driver(uda134x_codec_driver); - -MODULE_DESCRIPTION("UDA134X ALSA soc codec driver"); -MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h deleted file mode 100644 index 664618c2571c..000000000000 --- a/sound/soc/codecs/uda134x.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _UDA134X_CODEC_H -#define _UDA134X_CODEC_H - -#define UDA134X_L3ADDR 5 -#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0) -#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1) -#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2) - -#define UDA134X_EXTADDR_PREFIX 0xC0 -#define UDA134X_EXTDATA_PREFIX 0xE0 - -/* UDA134X registers */ -#define UDA134X_EA000 0 -#define UDA134X_EA001 1 -#define UDA134X_EA010 2 -#define UDA134X_EA011 3 -#define UDA134X_EA100 4 -#define UDA134X_EA101 5 -#define UDA134X_EA110 6 -#define UDA134X_EA111 7 -#define UDA134X_STATUS0 8 -#define UDA134X_STATUS1 9 -#define UDA134X_DATA000 10 -#define UDA134X_DATA001 11 -#define UDA134X_DATA010 12 -#define UDA134X_DATA011 13 -#define UDA134X_DATA1 14 - -#define STATUS0_DAIFMT_MASK (~(7<<1)) -#define STATUS0_SYSCLK_MASK (~(3<<4)) - -#endif diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 36cdf97993a5..9679906c6bd5 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1770,11 +1770,6 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops wm2200_dai_ops = { - .set_fmt = wm2200_set_fmt, - .hw_params = wm2200_hw_params, -}; - static int wm2200_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -2068,6 +2063,12 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops wm2200_dai_ops = { + .probe = wm2200_dai_probe, + .set_fmt = wm2200_set_fmt, + .hw_params = wm2200_hw_params, +}; + #define WM2200_RATES SNDRV_PCM_RATE_8000_48000 #define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ @@ -2075,7 +2076,6 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver wm2200_dai = { .name = "wm2200", - .probe = wm2200_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 3bdbdf3770b5..4ecf07c7448c 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1773,6 +1773,10 @@ static int wm5102_set_fll(struct snd_soc_component *component, int fll_id, #define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops wm5102_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver wm5102_dai[] = { { .name = "wm5102-aif1", @@ -1906,7 +1910,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = { .rates = WM5102_RATES, .formats = WM5102_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5102_dai_ops, }, { .name = "wm5102-dsp-trace", diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index ad670300de8d..ac1f2c850346 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2073,6 +2073,10 @@ static int wm5110_set_fll(struct snd_soc_component *component, int fll_id, #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static const struct snd_soc_dai_ops wm5110_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver wm5110_dai[] = { { .name = "wm5110-aif1", @@ -2206,7 +2210,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5110_dai_ops, }, { .name = "wm5110-dsp-voicectrl", @@ -2227,7 +2231,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .rates = WM5110_RATES, .formats = WM5110_FORMATS, }, - .compress_new = snd_soc_new_compress, + .ops = &wm5110_dai_ops, }, { .name = "wm5110-dsp-trace", diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 60319b468fb2..829bf055622a 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2202,7 +2202,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c) match = of_match_node(wm8904_of_match, i2c->dev.of_node); if (match == NULL) return -EINVAL; - wm8904->devtype = (enum wm8904_type)match->data; + wm8904->devtype = (uintptr_t)match->data; } else { const struct i2c_device_id *id = i2c_match_id(wm8904_i2c_id, i2c); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index c2bd9ef41ebb..0a50180750e8 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -120,6 +120,15 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg) } } +#define WM8960_NUM_SUPPLIES 5 +static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "SPKVDD1", + "SPKVDD2", +}; + struct wm8960_priv { struct clk *mclk; struct regmap *regmap; @@ -137,6 +146,7 @@ struct wm8960_priv { bool is_stream_in_use[2]; struct wm8960_data pdata; ktime_t dsch_start; + struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES]; }; #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) @@ -1417,6 +1427,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) { struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); struct wm8960_priv *wm8960; + unsigned int i; int ret; u8 val; @@ -1429,6 +1440,31 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) if (IS_ERR(wm8960->mclk)) { if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; + } else { + ret = clk_get_rate(wm8960->mclk); + if (ret >= 0) { + wm8960->freq_in = ret; + } else { + dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n", + ret); + } + } + + for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++) + wm8960->supplies[i].supply = wm8960_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; } wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); @@ -1497,7 +1533,11 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) } static void wm8960_i2c_remove(struct i2c_client *client) -{} +{ + struct wm8960_priv *wm8960 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); +} static const struct i2c_device_id wm8960_i2c_id[] = { { "wm8960", 0 }, diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h index 63ba6c03c488..e8ff33b188e9 100644 --- a/sound/soc/codecs/wm8960.h +++ b/sound/soc/codecs/wm8960.h @@ -77,9 +77,9 @@ #define WM8960_SYSCLK_DIV_1 (0 << 1) #define WM8960_SYSCLK_DIV_2 (2 << 1) -#define WM8960_SYSCLK_MCLK (0 << 0) +#define WM8960_SYSCLK_AUTO (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) -#define WM8960_SYSCLK_AUTO (2 << 0) +#define WM8960_SYSCLK_MCLK (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index bca3ebe0dac4..a48e904a9740 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3215,6 +3215,7 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { }; static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { + .probe = wm8994_aif2_probe, .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, @@ -3269,7 +3270,6 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .formats = WM8994_FORMATS, .sig_bits = 24, }, - .probe = wm8994_aif2_probe, .ops = &wm8994_aif2_dai_ops, }, { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 5a89abfe8784..6fc34f41b175 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -998,7 +998,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); -int wm_adsp_power_up(struct wm_adsp *dsp) +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) { int ret = 0; char *wmfw_filename = NULL; @@ -1006,11 +1006,13 @@ int wm_adsp_power_up(struct wm_adsp *dsp) char *coeff_filename = NULL; const struct firmware *coeff_firmware = NULL; - ret = wm_adsp_request_firmware_files(dsp, - &wmfw_firmware, &wmfw_filename, - &coeff_firmware, &coeff_filename); - if (ret) - return ret; + if (load_firmware) { + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + return ret; + } ret = cs_dsp_power_up(&dsp->cs_dsp, wmfw_firmware, wmfw_filename, @@ -1025,13 +1027,19 @@ int wm_adsp_power_up(struct wm_adsp *dsp) } EXPORT_SYMBOL_GPL(wm_adsp_power_up); +void wm_adsp_power_down(struct wm_adsp *dsp) +{ + cs_dsp_power_down(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_power_down); + static void wm_adsp_boot_work(struct work_struct *work) { struct wm_adsp *dsp = container_of(work, struct wm_adsp, boot_work); - wm_adsp_power_up(dsp); + wm_adsp_power_up(dsp, true); } int wm_adsp_early_event(struct snd_soc_dapm_widget *w, @@ -1046,7 +1054,7 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w, queue_work(system_unbound_wq, &dsp->boot_work); break; case SND_SOC_DAPM_PRE_PMD: - cs_dsp_power_down(&dsp->cs_dsp); + wm_adsp_power_down(dsp); break; default: break; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 769904d34a87..067d807a7ca8 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -91,7 +91,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int wm_adsp_early_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); -int wm_adsp_power_up(struct wm_adsp *dsp); +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware); +void wm_adsp_power_down(struct wm_adsp *dsp); irqreturn_t wm_adsp2_bus_error(int irq, void *data); irqreturn_t wm_halo_bus_error(int irq, void *data); diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 1f1ee14b04e6..5ab1b3eb2d28 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/interrupt.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/reset.h> #include <linux/slab.h> @@ -198,7 +199,8 @@ static void i2s_start(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 1); - if (dev->use_pio) + /* I2S needs to enable IRQ to make a handshake with DMAC on the JH7110 SoC */ + if (dev->use_pio || dev->is_jh7110) i2s_enable_irqs(dev, substream->stream, config->chan_nr); else i2s_enable_dma(dev, substream->stream); @@ -216,7 +218,7 @@ static void i2s_stop(struct dw_i2s_dev *dev, else i2s_write_reg(dev->i2s_base, IRER, 0); - if (dev->use_pio) + if (dev->use_pio || dev->is_jh7110) i2s_disable_irqs(dev, substream->stream, 8); else i2s_disable_dma(dev, substream->stream); @@ -227,6 +229,21 @@ static void i2s_stop(struct dw_i2s_dev *dev, } } +static int dw_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + + if (dev->is_jh7110) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + dai_link->trigger_stop = SND_SOC_TRIGGER_ORDER_LDC; + } + + return 0; +} + static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) { u32 ch_reg; @@ -443,7 +460,17 @@ static int dw_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, unsigned int tx_mask return 0; } +static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); + return 0; +} + static const struct snd_soc_dai_ops dw_i2s_dai_ops = { + .probe = dw_i2s_dai_probe, + .startup = dw_i2s_startup, .hw_params = dw_i2s_hw_params, .prepare = dw_i2s_prepare, .trigger = dw_i2s_trigger, @@ -628,17 +655,39 @@ static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) idx = 1; - /* Set DMA slaves info */ - dev->play_dma_data.pd.data = pdata->play_dma_data; - dev->capture_dma_data.pd.data = pdata->capture_dma_data; - dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; - dev->play_dma_data.pd.max_burst = 16; - dev->capture_dma_data.pd.max_burst = 16; - dev->play_dma_data.pd.addr_width = bus_widths[idx]; - dev->capture_dma_data.pd.addr_width = bus_widths[idx]; - dev->play_dma_data.pd.filter = pdata->filter; - dev->capture_dma_data.pd.filter = pdata->filter; + + if (dev->is_jh7110) { + /* Use platform data and snd_dmaengine_dai_dma_data struct at the same time */ + u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); + u32 idx2; + + if (COMP1_TX_ENABLED(comp1)) { + idx2 = COMP1_TX_WORDSIZE_0(comp1); + dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; + dev->play_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2]) >> 8; + dev->play_dma_data.dt.maxburst = 16; + } + if (COMP1_RX_ENABLED(comp1)) { + idx2 = COMP2_RX_WORDSIZE_0(comp2); + dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; + dev->capture_dma_data.dt.fifo_size = dev->fifo_th * 2 * + (fifo_width[idx2] >> 8); + dev->capture_dma_data.dt.maxburst = 16; + } + } else { + /* Set DMA slaves info */ + dev->play_dma_data.pd.data = pdata->play_dma_data; + dev->capture_dma_data.pd.data = pdata->capture_dma_data; + dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; + dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; + dev->play_dma_data.pd.max_burst = 16; + dev->capture_dma_data.pd.max_burst = 16; + dev->play_dma_data.pd.addr_width = bus_widths[idx]; + dev->capture_dma_data.pd.addr_width = bus_widths[idx]; + dev->play_dma_data.pd.filter = pdata->filter; + dev->capture_dma_data.pd.filter = pdata->filter; + } return 0; } @@ -680,17 +729,193 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, } -static int dw_i2s_dai_probe(struct snd_soc_dai *dai) +/* clocks initialization with master mode on JH7110 SoC */ +static int jh7110_i2s_crg_master_init(struct dw_i2s_dev *dev) { - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "mclk_inner" }, + { .id = "apb" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + mclk_inner = clks[2].clk; + pclk = clks[3].clk; + bclk_mst = clks[4].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + /* Use inner mclk first and avoid uninitialized gpio for external mclk */ + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + /* deassert resets before set clock parent */ + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* external clock (12.288MHz) for Audio */ + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; - snd_soc_dai_init_dma_data(dai, &dev->play_dma_data, &dev->capture_dma_data); return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; +} + +/* clocks initialization with slave mode on JH7110 SoC */ +static int jh7110_i2s_crg_slave_init(struct dw_i2s_dev *dev) +{ + static struct clk_bulk_data clks[] = { + { .id = "mclk" }, + { .id = "mclk_ext" }, + { .id = "apb" }, + { .id = "bclk_ext" }, + { .id = "lrck_ext" }, + { .id = "bclk" }, + { .id = "lrck" }, + { .id = "mclk_inner" }, + { .id = "i2sclk" }, + }; + struct reset_control *resets = devm_reset_control_array_get_exclusive(dev->dev); + int ret; + struct clk *pclk; + struct clk *bclk_mst; + struct clk *bclk_ext; + struct clk *lrck_ext; + struct clk *bclk; + struct clk *lrck; + struct clk *mclk; + struct clk *mclk_ext; + struct clk *mclk_inner; + + if (IS_ERR(resets)) + return dev_err_probe(dev->dev, PTR_ERR(resets), "failed to get i2s resets\n"); + + ret = clk_bulk_get(dev->dev, ARRAY_SIZE(clks), clks); + if (ret) + return dev_err_probe(dev->dev, ret, "failed to get i2s clocks\n"); + + mclk = clks[0].clk; + mclk_ext = clks[1].clk; + pclk = clks[2].clk; + bclk_ext = clks[3].clk; + lrck_ext = clks[4].clk; + bclk = clks[5].clk; + lrck = clks[6].clk; + mclk_inner = clks[7].clk; + bclk_mst = clks[8].clk; + + ret = clk_prepare_enable(pclk); + if (ret) + goto exit; + + ret = clk_set_parent(mclk, mclk_inner); + if (ret) + goto err_dis_pclk; + + ret = clk_prepare_enable(bclk_mst); + if (ret) + goto err_dis_pclk; + + ret = reset_control_deassert(resets); + if (ret) + goto err_dis_all; + + /* The sources of BCLK and LRCK are the external codec. */ + ret = clk_set_parent(bclk, bclk_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(lrck, lrck_ext); + if (ret) + goto err_dis_all; + + ret = clk_set_parent(mclk, mclk_ext); + if (ret) + goto err_dis_all; + + /* The i2sclk will be got and enabled repeatedly later and should be disabled now. */ + clk_disable_unprepare(bclk_mst); + clk_bulk_put(ARRAY_SIZE(clks), clks); + dev->is_jh7110 = true; + + return 0; + +err_dis_all: + clk_disable_unprepare(bclk_mst); +err_dis_pclk: + clk_disable_unprepare(pclk); +exit: + clk_bulk_put(ARRAY_SIZE(clks), clks); + return ret; +} + +/* Special syscon initialization about RX channel with slave mode on JH7110 SoC */ +static int jh7110_i2srx_crg_init(struct dw_i2s_dev *dev) +{ + struct regmap *regmap; + unsigned int args[2]; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->dev->of_node, + "starfive,syscon", + 2, args); + if (IS_ERR(regmap)) + return dev_err_probe(dev->dev, PTR_ERR(regmap), "getting the regmap failed\n"); + + /* Enable I2Srx with syscon register, args[0]: offset, args[1]: mask */ + regmap_update_bits(regmap, args[0], args[1], args[1]); + + return jh7110_i2s_crg_slave_init(dev); +} + +static int jh7110_i2stx0_clk_cfg(struct i2s_clk_config_data *config) +{ + struct dw_i2s_dev *dev = container_of(config, struct dw_i2s_dev, config); + u32 bclk_rate = config->sample_rate * 64; + + return clk_set_rate(dev->clk, bclk_rate); } static int dw_i2s_probe(struct platform_device *pdev) { - const struct i2s_platform_data *pdata = pdev->dev.platform_data; + const struct i2s_platform_data *pdata = of_device_get_match_data(&pdev->dev); struct dw_i2s_dev *dev; struct resource *res; int ret, irq; @@ -706,21 +931,30 @@ static int dw_i2s_probe(struct platform_device *pdev) return -ENOMEM; dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->probe = dw_i2s_dai_probe; dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(dev->i2s_base)) return PTR_ERR(dev->i2s_base); - dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); - if (IS_ERR(dev->reset)) - return PTR_ERR(dev->reset); + dev->dev = &pdev->dev; + dev->is_jh7110 = false; + if (pdata) { + if (pdata->i2s_pd_init) { + ret = pdata->i2s_pd_init(dev); + if (ret) + return ret; + } + } - ret = reset_control_deassert(dev->reset); - if (ret) - return ret; + if (!dev->is_jh7110) { + dev->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(dev->reset)) + return PTR_ERR(dev->reset); - dev->dev = &pdev->dev; + ret = reset_control_deassert(dev->reset); + if (ret) + return ret; + } irq = platform_get_irq_optional(pdev, 0); if (irq >= 0) { @@ -779,7 +1013,7 @@ static int dw_i2s_probe(struct platform_device *pdev) goto err_clk_disable; } - if (!pdata) { + if (!pdata || dev->is_jh7110) { if (irq >= 0) { ret = dw_pcm_register(pdev); dev->use_pio = true; @@ -821,8 +1055,36 @@ static void dw_i2s_remove(struct platform_device *pdev) } #ifdef CONFIG_OF +static const struct i2s_platform_data jh7110_i2stx0_data = { + .cap = DWC_I2S_PLAY | DW_I2S_MASTER, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .i2s_clk_cfg = jh7110_i2stx0_clk_cfg, + .i2s_pd_init = jh7110_i2s_crg_master_init, +}; + +static const struct i2s_platform_data jh7110_i2stx1_data = { + .cap = DWC_I2S_PLAY | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2s_crg_slave_init, +}; + +static const struct i2s_platform_data jh7110_i2srx_data = { + .cap = DWC_I2S_RECORD | DW_I2S_SLAVE, + .channel = TWO_CHANNEL_SUPPORT, + .snd_fmts = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .snd_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_pd_init = jh7110_i2srx_crg_init, +}; + static const struct of_device_id dw_i2s_of_match[] = { { .compatible = "snps,designware-i2s", }, + { .compatible = "starfive,jh7110-i2stx0", .data = &jh7110_i2stx0_data, }, + { .compatible = "starfive,jh7110-i2stx1", .data = &jh7110_i2stx1_data,}, + { .compatible = "starfive,jh7110-i2srx", .data = &jh7110_i2srx_data,}, {}, }; diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h index 4ce96bac2f39..dce88c9ad5f3 100644 --- a/sound/soc/dwc/local.h +++ b/sound/soc/dwc/local.h @@ -123,6 +123,7 @@ struct dw_i2s_dev { u32 fifo_th; u32 l_reg; u32 r_reg; + bool is_jh7110; /* Flag for StarFive JH7110 SoC */ /* data related to DMA transfers b/w i2s and DMAC */ union dw_i2s_snd_dma_data play_dma_data; diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index adb8a59de2bd..b793263291dc 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -780,13 +780,6 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_asrc_dai_ops = { - .startup = fsl_asrc_dai_startup, - .hw_params = fsl_asrc_dai_hw_params, - .hw_free = fsl_asrc_dai_hw_free, - .trigger = fsl_asrc_dai_trigger, -}; - static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) { struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai); @@ -797,12 +790,19 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_asrc_dai_ops = { + .probe = fsl_asrc_dai_probe, + .startup = fsl_asrc_dai_startup, + .hw_params = fsl_asrc_dai_hw_params, + .hw_free = fsl_asrc_dai_hw_free, + .trigger = fsl_asrc_dai_trigger, +}; + #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_3LE) static struct snd_soc_dai_driver fsl_asrc_dai = { - .probe = fsl_asrc_dai_probe, .playback = { .stream_name = "ASRC-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index 46b0c5dcc4a5..fc56f6ade368 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -49,10 +49,6 @@ static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { - .trigger = fsl_aud2htx_trigger, -}; - static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev); @@ -84,8 +80,12 @@ static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { + .probe = fsl_aud2htx_dai_probe, + .trigger = fsl_aud2htx_trigger, +}; + static struct snd_soc_dai_driver fsl_aud2htx_dai = { - .probe = fsl_aud2htx_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c index 670cbdb361b6..ba62995c909a 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -1531,13 +1531,6 @@ static int fsl_easrc_hw_free(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_easrc_dai_ops = { - .startup = fsl_easrc_startup, - .trigger = fsl_easrc_trigger, - .hw_params = fsl_easrc_hw_params, - .hw_free = fsl_easrc_hw_free, -}; - static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_asrc *easrc = dev_get_drvdata(cpu_dai->dev); @@ -1548,8 +1541,15 @@ static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_easrc_dai_ops = { + .probe = fsl_easrc_dai_probe, + .startup = fsl_easrc_startup, + .trigger = fsl_easrc_trigger, + .hw_params = fsl_easrc_hw_params, + .hw_free = fsl_easrc_hw_free, +}; + static struct snd_soc_dai_driver fsl_easrc_dai = { - .probe = fsl_easrc_dai_probe, .playback = { .stream_name = "ASRC-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 936f0cd4b06d..d0d8a01da9bd 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -785,15 +785,6 @@ static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops fsl_esai_dai_ops = { - .startup = fsl_esai_startup, - .trigger = fsl_esai_trigger, - .hw_params = fsl_esai_hw_params, - .set_sysclk = fsl_esai_set_dai_sysclk, - .set_fmt = fsl_esai_set_dai_fmt, - .set_tdm_slot = fsl_esai_set_dai_tdm_slot, -}; - static int fsl_esai_dai_probe(struct snd_soc_dai *dai) { struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); @@ -804,8 +795,17 @@ static int fsl_esai_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_esai_dai_ops = { + .probe = fsl_esai_dai_probe, + .startup = fsl_esai_startup, + .trigger = fsl_esai_trigger, + .hw_params = fsl_esai_hw_params, + .set_sysclk = fsl_esai_set_dai_sysclk, + .set_fmt = fsl_esai_set_dai_fmt, + .set_tdm_slot = fsl_esai_set_dai_tdm_slot, +}; + static struct snd_soc_dai_driver fsl_esai_dai = { - .probe = fsl_esai_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c index 9d01225dedd9..0d37edb70261 100644 --- a/sound/soc/fsl/fsl_micfil.c +++ b/sound/soc/fsl/fsl_micfil.c @@ -56,6 +56,8 @@ struct fsl_micfil { int vad_init_mode; int vad_enabled; int vad_detected; + struct fsl_micfil_verid verid; + struct fsl_micfil_param param; }; struct fsl_micfil_soc_data { @@ -64,6 +66,7 @@ struct fsl_micfil_soc_data { unsigned int dataline; bool imx; bool use_edma; + bool use_verid; u64 formats; }; @@ -90,6 +93,7 @@ static struct fsl_micfil_soc_data fsl_micfil_imx93 = { .dataline = 0xf, .formats = SNDRV_PCM_FMTBIT_S32_LE, .use_edma = true, + .use_verid = true, }; static const struct of_device_id fsl_micfil_dt_ids[] = { @@ -356,6 +360,49 @@ static const struct snd_kcontrol_new fsl_micfil_snd_controls[] = { SOC_SINGLE_BOOL_EXT("VAD Detected", 0, hwvad_detected, NULL), }; +static int fsl_micfil_use_verid(struct device *dev) +{ + struct fsl_micfil *micfil = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (!micfil->soc->use_verid) + return 0; + + ret = regmap_read(micfil->regmap, REG_MICFIL_VERID, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "VERID: 0x%016X\n", val); + + micfil->verid.version = val & + (MICFIL_VERID_MAJOR_MASK | MICFIL_VERID_MINOR_MASK); + micfil->verid.version >>= MICFIL_VERID_MINOR_SHIFT; + micfil->verid.feature = val & MICFIL_VERID_FEATURE_MASK; + + ret = regmap_read(micfil->regmap, REG_MICFIL_PARAM, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "PARAM: 0x%016X\n", val); + + micfil->param.hwvad_num = (val & MICFIL_PARAM_NUM_HWVAD_MASK) >> + MICFIL_PARAM_NUM_HWVAD_SHIFT; + micfil->param.hwvad_zcd = val & MICFIL_PARAM_HWVAD_ZCD; + micfil->param.hwvad_energy_mode = val & MICFIL_PARAM_HWVAD_ENERGY_MODE; + micfil->param.hwvad = val & MICFIL_PARAM_HWVAD; + micfil->param.dc_out_bypass = val & MICFIL_PARAM_DC_OUT_BYPASS; + micfil->param.dc_in_bypass = val & MICFIL_PARAM_DC_IN_BYPASS; + micfil->param.low_power = val & MICFIL_PARAM_LOW_POWER; + micfil->param.fil_out_width = val & MICFIL_PARAM_FIL_OUT_WIDTH; + micfil->param.fifo_ptrwid = (val & MICFIL_PARAM_FIFO_PTRWID_MASK) >> + MICFIL_PARAM_FIFO_PTRWID_SHIFT; + micfil->param.npair = (val & MICFIL_PARAM_NPAIR_MASK) >> + MICFIL_PARAM_NPAIR_SHIFT; + + return 0; +} + /* The SRES is a self-negated bit which provides the CPU with the * capability to initialize the PDM Interface module through the * slave-bus interface. This bit always reads as zero, and this @@ -717,12 +764,6 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { - .startup = fsl_micfil_startup, - .trigger = fsl_micfil_trigger, - .hw_params = fsl_micfil_hw_params, -}; - static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_micfil *micfil = dev_get_drvdata(cpu_dai->dev); @@ -760,8 +801,14 @@ static int fsl_micfil_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_micfil_dai_ops = { + .probe = fsl_micfil_dai_probe, + .startup = fsl_micfil_startup, + .trigger = fsl_micfil_trigger, + .hw_params = fsl_micfil_hw_params, +}; + static struct snd_soc_dai_driver fsl_micfil_dai = { - .probe = fsl_micfil_dai_probe, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, @@ -825,6 +872,9 @@ static bool fsl_micfil_readable_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: + case REG_MICFIL_FSYNC_CTRL: + case REG_MICFIL_VERID: + case REG_MICFIL_PARAM: case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: @@ -849,6 +899,7 @@ static bool fsl_micfil_writeable_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DC_CTRL: case REG_MICFIL_OUT_CTRL: case REG_MICFIL_OUT_STAT: /* Write 1 to Clear */ + case REG_MICFIL_FSYNC_CTRL: case REG_MICFIL_VAD0_CTRL1: case REG_MICFIL_VAD0_CTRL2: case REG_MICFIL_VAD0_STAT: /* Write 1 to Clear */ @@ -873,6 +924,8 @@ static bool fsl_micfil_volatile_reg(struct device *dev, unsigned int reg) case REG_MICFIL_DATACH5: case REG_MICFIL_DATACH6: case REG_MICFIL_DATACH7: + case REG_MICFIL_VERID: + case REG_MICFIL_PARAM: case REG_MICFIL_VAD0_STAT: case REG_MICFIL_VAD0_NDATA: return true; @@ -1031,6 +1084,9 @@ static irqreturn_t hwvad_err_isr(int irq, void *devid) return IRQ_HANDLED; } +static int fsl_micfil_runtime_suspend(struct device *dev); +static int fsl_micfil_runtime_resume(struct device *dev); + static int fsl_micfil_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1044,7 +1100,7 @@ static int fsl_micfil_probe(struct platform_device *pdev) return -ENOMEM; micfil->pdev = pdev; - strncpy(micfil->name, np->name, sizeof(micfil->name) - 1); + strscpy(micfil->name, np->name, sizeof(micfil->name)); micfil->soc = of_device_get_match_data(&pdev->dev); @@ -1150,6 +1206,25 @@ static int fsl_micfil_probe(struct platform_device *pdev) platform_set_drvdata(pdev, micfil); pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = fsl_micfil_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) + goto err_pm_get_sync; + + /* Get micfil version */ + ret = fsl_micfil_use_verid(&pdev->dev); + if (ret < 0) + dev_warn(&pdev->dev, "Error reading MICFIL version: %d\n", ret); + + ret = pm_runtime_put_sync(&pdev->dev); + if (ret < 0 && ret != -ENOSYS) + goto err_pm_get_sync; + regcache_cache_only(micfil->regmap, true); /* @@ -1174,6 +1249,9 @@ static int fsl_micfil_probe(struct platform_device *pdev) return ret; +err_pm_get_sync: + if (!pm_runtime_status_suspended(&pdev->dev)) + fsl_micfil_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); @@ -1185,7 +1263,7 @@ static void fsl_micfil_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) +static int fsl_micfil_runtime_suspend(struct device *dev) { struct fsl_micfil *micfil = dev_get_drvdata(dev); @@ -1197,7 +1275,7 @@ static int __maybe_unused fsl_micfil_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) +static int fsl_micfil_runtime_resume(struct device *dev) { struct fsl_micfil *micfil = dev_get_drvdata(dev); int ret; @@ -1219,26 +1297,12 @@ static int __maybe_unused fsl_micfil_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused fsl_micfil_suspend(struct device *dev) -{ - pm_runtime_force_suspend(dev); - - return 0; -} - -static int __maybe_unused fsl_micfil_resume(struct device *dev) -{ - pm_runtime_force_resume(dev); - - return 0; -} - static const struct dev_pm_ops fsl_micfil_pm_ops = { SET_RUNTIME_PM_OPS(fsl_micfil_runtime_suspend, fsl_micfil_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(fsl_micfil_suspend, - fsl_micfil_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static struct platform_driver fsl_micfil_driver = { diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h index fee9fe3d9119..c6b902ba0a53 100644 --- a/sound/soc/fsl/fsl_micfil.h +++ b/sound/soc/fsl/fsl_micfil.h @@ -24,6 +24,9 @@ #define REG_MICFIL_DC_CTRL 0x64 #define REG_MICFIL_OUT_CTRL 0x74 #define REG_MICFIL_OUT_STAT 0x7C +#define REG_MICFIL_FSYNC_CTRL 0x80 +#define REG_MICFIL_VERID 0x84 +#define REG_MICFIL_PARAM 0x88 #define REG_MICFIL_VAD0_CTRL1 0x90 #define REG_MICFIL_VAD0_CTRL2 0x94 #define REG_MICFIL_VAD0_STAT 0x98 @@ -39,6 +42,8 @@ #define MICFIL_CTRL1_DBG BIT(28) #define MICFIL_CTRL1_SRES BIT(27) #define MICFIL_CTRL1_DBGE BIT(26) +#define MICFIL_CTRL1_DECFILS BIT(20) +#define MICFIL_CTRL1_FSYNCEN BIT(16) #define MICFIL_CTRL1_DISEL_DISABLE 0 #define MICFIL_CTRL1_DISEL_DMA 1 @@ -82,6 +87,29 @@ #define MICFIL_DC_CUTOFF_152Hz 2 #define MICFIL_DC_BYPASS 3 +/* MICFIL VERID Register -- REG_MICFIL_VERID */ +#define MICFIL_VERID_MAJOR_SHIFT 24 +#define MICFIL_VERID_MAJOR_MASK GENMASK(31, 24) +#define MICFIL_VERID_MINOR_SHIFT 16 +#define MICFIL_VERID_MINOR_MASK GENMASK(23, 16) +#define MICFIL_VERID_FEATURE_SHIFT 0 +#define MICFIL_VERID_FEATURE_MASK GENMASK(15, 0) + +/* MICFIL PARAM Register -- REG_MICFIL_PARAM */ +#define MICFIL_PARAM_NUM_HWVAD_SHIFT 24 +#define MICFIL_PARAM_NUM_HWVAD_MASK GENMASK(27, 24) +#define MICFIL_PARAM_HWVAD_ZCD BIT(19) +#define MICFIL_PARAM_HWVAD_ENERGY_MODE BIT(17) +#define MICFIL_PARAM_HWVAD BIT(16) +#define MICFIL_PARAM_DC_OUT_BYPASS BIT(11) +#define MICFIL_PARAM_DC_IN_BYPASS BIT(10) +#define MICFIL_PARAM_LOW_POWER BIT(9) +#define MICFIL_PARAM_FIL_OUT_WIDTH BIT(8) +#define MICFIL_PARAM_FIFO_PTRWID_SHIFT 4 +#define MICFIL_PARAM_FIFO_PTRWID_MASK GENMASK(7, 4) +#define MICFIL_PARAM_NPAIR_SHIFT 0 +#define MICFIL_PARAM_NPAIR_MASK GENMASK(3, 0) + /* MICFIL HWVAD0 Control 1 Register -- REG_MICFIL_VAD0_CTRL1*/ #define MICFIL_VAD0_CTRL1_CHSEL GENMASK(26, 24) #define MICFIL_VAD0_CTRL1_CICOSR GENMASK(19, 16) @@ -146,4 +174,40 @@ #define MICFIL_HWVAD_ENVELOPE_MODE 0 #define MICFIL_HWVAD_ENERGY_MODE 1 +/** + * struct fsl_micfil_verid - version id data + * @version: version number + * @feature: feature specification number + */ +struct fsl_micfil_verid { + u32 version; + u32 feature; +}; + +/** + * struct fsl_micfil_param - parameter data + * @hwvad_num: the number of HWVADs + * @hwvad_zcd: HWVAD zero-cross detector is active + * @hwvad_energy_mode: HWVAD energy mode is active + * @hwvad: HWVAD is active + * @dc_out_bypass: points out if the output DC remover is disabled + * @dc_in_bypass: points out if the input DC remover is disabled + * @low_power: low power decimation filter + * @fil_out_width: filter output width + * @fifo_ptrwid: FIFO pointer width + * @npair: number of microphone pairs + */ +struct fsl_micfil_param { + u32 hwvad_num; + bool hwvad_zcd; + bool hwvad_energy_mode; + bool hwvad; + bool dc_out_bypass; + bool dc_in_bypass; + bool low_power; + bool fil_out_width; + u32 fifo_ptrwid; + u32 npair; +}; + #endif /* _FSL_MICFIL_H */ diff --git a/sound/soc/fsl/fsl_qmc_audio.c b/sound/soc/fsl/fsl_qmc_audio.c index 7cbb8e4758cc..56d6b0b039a2 100644 --- a/sound/soc/fsl/fsl_qmc_audio.c +++ b/sound/soc/fsl/fsl_qmc_audio.c @@ -372,8 +372,8 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); unsigned int channels = params_channels(params); unsigned int slot_width; + snd_pcm_format_t format; struct snd_mask f_new; - unsigned int i; if (!channels || channels > nb_ts) { dev_err(qmc_dai->dev, "channels %u not supported\n", @@ -384,10 +384,10 @@ static int qmc_dai_hw_rule_format_by_channels(struct qmc_dai *qmc_dai, slot_width = (nb_ts / channels) * 8; snd_mask_none(&f_new); - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(f_old, i)) { - if (snd_pcm_format_physical_width(i) <= slot_width) - snd_mask_set(&f_new, i); + pcm_for_each_format(format) { + if (snd_mask_test_format(f_old, format)) { + if (snd_pcm_format_physical_width(format) <= slot_width) + snd_mask_set_format(&f_new, format); } } @@ -551,26 +551,26 @@ static const struct snd_soc_dai_ops qmc_dai_ops = { static u64 qmc_audio_formats(u8 nb_ts) { - u64 formats; - unsigned int chan_width; unsigned int format_width; - int i; + unsigned int chan_width; + snd_pcm_format_t format; + u64 formats_mask; if (!nb_ts) return 0; - formats = 0; + formats_mask = 0; chan_width = nb_ts * 8; - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + pcm_for_each_format(format) { /* * Support format other than little-endian (ie big-endian or * without endianness such as 8bit formats) */ - if (snd_pcm_format_little_endian(i) == 1) + if (snd_pcm_format_little_endian(format) == 1) continue; /* Support physical width multiple of 8bit */ - format_width = snd_pcm_format_physical_width(i); + format_width = snd_pcm_format_physical_width(format); if (format_width == 0 || format_width % 8) continue; @@ -581,9 +581,9 @@ static u64 qmc_audio_formats(u8 nb_ts) if (format_width > chan_width || chan_width % format_width) continue; - formats |= (1ULL << i); + formats_mask |= pcm_format_to_bits(format); } - return formats; + return formats_mask; } static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node *np, diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index f7676d30c82f..1e4020fae05a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -849,17 +849,6 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, return ret; } -static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { - .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, - .set_sysclk = fsl_sai_set_dai_sysclk, - .set_fmt = fsl_sai_set_dai_fmt, - .set_tdm_slot = fsl_sai_set_dai_tdm_slot, - .hw_params = fsl_sai_hw_params, - .hw_free = fsl_sai_hw_free, - .trigger = fsl_sai_trigger, - .startup = fsl_sai_startup, -}; - static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); @@ -885,6 +874,18 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { + .probe = fsl_sai_dai_probe, + .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, + .set_sysclk = fsl_sai_set_dai_sysclk, + .set_fmt = fsl_sai_set_dai_fmt, + .set_tdm_slot = fsl_sai_set_dai_tdm_slot, + .hw_params = fsl_sai_hw_params, + .hw_free = fsl_sai_hw_free, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, +}; + static int fsl_sai_dai_resume(struct snd_soc_component *component) { struct fsl_sai *sai = snd_soc_component_get_drvdata(component); @@ -903,7 +904,6 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component) } static struct snd_soc_dai_driver fsl_sai_dai_template = { - .probe = fsl_sai_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 95bb8b10494a..78d9dfbe6548 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -765,14 +765,6 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops fsl_spdif_dai_ops = { - .startup = fsl_spdif_startup, - .hw_params = fsl_spdif_hw_params, - .trigger = fsl_spdif_trigger, - .shutdown = fsl_spdif_shutdown, -}; - - /* * FSL SPDIF IEC958 controller(mixer) functions * @@ -1283,8 +1275,15 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_spdif_dai_ops = { + .probe = fsl_spdif_dai_probe, + .startup = fsl_spdif_startup, + .hw_params = fsl_spdif_hw_params, + .trigger = fsl_spdif_trigger, + .shutdown = fsl_spdif_shutdown, +}; + static struct snd_soc_dai_driver fsl_spdif_dai = { - .probe = &fsl_spdif_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 2, diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 53ed3701b0b0..079ac04272b8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1152,6 +1152,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { + .probe = fsl_ssi_dai_probe, .startup = fsl_ssi_startup, .shutdown = fsl_ssi_shutdown, .hw_params = fsl_ssi_hw_params, @@ -1162,7 +1163,6 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { }; static struct snd_soc_dai_driver fsl_ssi_dai_template = { - .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, @@ -1187,7 +1187,6 @@ static const struct snd_soc_component_driver fsl_ssi_component = { static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { .symmetric_channels = 1, - .probe = fsl_ssi_dai_probe, .playback = { .stream_name = "CPU AC97 Playback", .channels_min = 2, diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 318fe77683f5..fa0a15263c66 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -888,13 +888,6 @@ static struct snd_kcontrol_new fsl_xcvr_tx_ctls[] = { }, }; -static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = { - .prepare = fsl_xcvr_prepare, - .startup = fsl_xcvr_startup, - .shutdown = fsl_xcvr_shutdown, - .trigger = fsl_xcvr_trigger, -}; - static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) { struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); @@ -915,8 +908,15 @@ static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops fsl_xcvr_dai_ops = { + .probe = fsl_xcvr_dai_probe, + .prepare = fsl_xcvr_prepare, + .startup = fsl_xcvr_startup, + .shutdown = fsl_xcvr_shutdown, + .trigger = fsl_xcvr_trigger, +}; + static struct snd_soc_dai_driver fsl_xcvr_dai = { - .probe = fsl_xcvr_dai_probe, .ops = &fsl_xcvr_dai_ops, .playback = { .stream_name = "CPU-Playback", diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c index d5234ac4b09b..289e47c03d40 100644 --- a/sound/soc/fsl/imx-audio-rpmsg.c +++ b/sound/soc/fsl/imx-audio-rpmsg.c @@ -116,7 +116,6 @@ static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = { static struct rpmsg_driver imx_audio_rpmsg_driver = { .drv.name = "imx_audio_rpmsg", - .drv.owner = THIS_MODULE, .id_table = imx_audio_rpmsg_id_table, .probe = imx_audio_rpmsg_probe, .callback = imx_audio_rpmsg_cb, diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 85bd36fb68a2..6f0d031c1d5f 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -37,6 +37,16 @@ static struct snd_soc_jack_gpio headset_jack_gpios[] = { }; static struct snd_soc_jack headset_jack; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) { @@ -46,9 +56,11 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) /* Headphone jack detection */ if (gpio_is_valid(data->jack_gpio)) { - ret = snd_soc_card_jack_new(rtd->card, "Headphone", - SND_JACK_HEADPHONE | SND_JACK_BTN_0, - &headset_jack); + ret = snd_soc_card_jack_new_pins(rtd->card, "Headphone", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) return ret; @@ -68,6 +80,11 @@ static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), }; +static const struct snd_kcontrol_new imx_es8328_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + static int imx_es8328_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -183,6 +200,8 @@ static int imx_es8328_probe(struct platform_device *pdev) data->card.dev = dev; data->card.dapm_widgets = imx_es8328_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); + data->card.controls = imx_es8328_controls; + data->card.num_controls = ARRAY_SIZE(imx_es8328_controls); ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) { dev_err(dev, "Unable to parse card name\n"); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 40a4a2667394..1671bcd4ee3d 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -222,6 +222,7 @@ static int psc_ac97_probe(struct snd_soc_dai *cpu_dai) * psc_ac97_dai_template: template CPU Digital Audio Interface */ static const struct snd_soc_dai_ops psc_ac97_analog_ops = { + .probe = psc_ac97_probe, .hw_params = psc_ac97_hw_analog_params, .trigger = psc_ac97_trigger, }; @@ -233,7 +234,6 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = { static struct snd_soc_dai_driver psc_ac97_dai[] = { { .name = "mpc5200-psc-ac97.0", - .probe = psc_ac97_probe, .playback = { .stream_name = "AC97 Playback", .channels_min = 1, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 0b8258b6bd8e..844a2ef15948 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -60,7 +60,7 @@ static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc) struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); if (dai && (dai->component->driver->pcm_construct || - dai->driver->pcm_new)) + (dai->driver->ops && dai->driver->ops->pcm_new))) return true; return false; @@ -425,77 +425,6 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, return ret; } -static int graph_get_dais_count(struct asoc_simple_priv *priv, - struct link_info *li); - -int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) -{ - struct snd_soc_card *card = simple_priv_to_card(priv); - struct link_info *li; - int ret; - - li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); - if (!li) - return -ENOMEM; - - card->owner = THIS_MODULE; - card->dev = dev; - - ret = graph_get_dais_count(priv, li); - if (ret < 0) - return ret; - - if (!li->link) - return -EINVAL; - - ret = asoc_simple_init_priv(priv, li); - if (ret < 0) - return ret; - - priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); - if (IS_ERR(priv->pa_gpio)) { - ret = PTR_ERR(priv->pa_gpio); - dev_err(dev, "failed to get amplifier gpio: %d\n", ret); - return ret; - } - - ret = asoc_simple_parse_widgets(card, NULL); - if (ret < 0) - return ret; - - ret = asoc_simple_parse_routing(card, NULL); - if (ret < 0) - return ret; - - memset(li, 0, sizeof(*li)); - ret = graph_for_each_link(priv, li, - graph_dai_link_of, - graph_dai_link_of_dpcm); - if (ret < 0) - goto err; - - ret = asoc_simple_parse_card_name(card, NULL); - if (ret < 0) - goto err; - - snd_soc_card_set_drvdata(card, priv); - - asoc_simple_debug_info(priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - devm_kfree(dev, li); - return 0; - -err: - asoc_simple_clean_reference(card); - - return dev_err_probe(dev, ret, "parse error\n"); -} -EXPORT_SYMBOL_GPL(audio_graph_parse_of); - static int graph_count_noml(struct asoc_simple_priv *priv, struct device_node *cpu_ep, struct device_node *codec_ep, @@ -612,6 +541,74 @@ static int graph_get_dais_count(struct asoc_simple_priv *priv, graph_count_dpcm); } +int audio_graph_parse_of(struct asoc_simple_priv *priv, struct device *dev) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct link_info *li; + int ret; + + li = devm_kzalloc(dev, sizeof(*li), GFP_KERNEL); + if (!li) + return -ENOMEM; + + card->owner = THIS_MODULE; + card->dev = dev; + + ret = graph_get_dais_count(priv, li); + if (ret < 0) + return ret; + + if (!li->link) + return -EINVAL; + + ret = asoc_simple_init_priv(priv, li); + if (ret < 0) + return ret; + + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); + if (IS_ERR(priv->pa_gpio)) { + ret = PTR_ERR(priv->pa_gpio); + dev_err(dev, "failed to get amplifier gpio: %d\n", ret); + return ret; + } + + ret = asoc_simple_parse_widgets(card, NULL); + if (ret < 0) + return ret; + + ret = asoc_simple_parse_routing(card, NULL); + if (ret < 0) + return ret; + + memset(li, 0, sizeof(*li)); + ret = graph_for_each_link(priv, li, + graph_dai_link_of, + graph_dai_link_of_dpcm); + if (ret < 0) + goto err; + + ret = asoc_simple_parse_card_name(card, NULL); + if (ret < 0) + goto err; + + snd_soc_card_set_drvdata(card, priv); + + asoc_simple_debug_info(priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret < 0) + goto err; + + devm_kfree(dev, li); + return 0; + +err: + asoc_simple_clean_reference(card); + + return dev_err_probe(dev, ret, "parse error\n"); +} +EXPORT_SYMBOL_GPL(audio_graph_parse_of); + static int graph_probe(struct platform_device *pdev) { struct asoc_simple_priv *priv; diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi index 2ac0de3c21da..8acaa2ddb335 100644 --- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi +++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi @@ -34,6 +34,13 @@ * ... * }; * + * + * Below sample doesn't use "format" property, + * because test-component driver (test-cpu/test-codec) is supporting + * snd_soc_dai_ops :: .auto_selectable_formats. + * see + * snd_soc_runtime_get_dai_fmt() + * linux/sound/soc/generic/test-component.c :: test_dai_formats */ / { /* diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 98732468a992..b1c675c6b6db 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -47,6 +47,18 @@ see graph_parse_daifmt(). + "format" property is no longer needed on DT if both CPU/Codec drivers are + supporting snd_soc_dai_ops :: .auto_selectable_formats. + see + snd_soc_runtime_get_dai_fmt() + + sample driver + linux/sound/soc/sh/rcar/core.c + linux/sound/soc/codecs/ak4613.c + linux/sound/soc/codecs/pcm3168a.c + linux/sound/soc/soc-utils.c + linux/sound/soc/generic/test-component.c + ************************************ Normal Audio-Graph ************************************ diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c index 27219a9e7d0d..dd7d2a077248 100644 --- a/sound/soc/hisilicon/hi6210-i2s.c +++ b/sound/soc/hisilicon/hi6210-i2s.c @@ -511,6 +511,7 @@ static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = { + .probe = hi6210_i2s_dai_probe, .trigger = hi6210_i2s_trigger, .hw_params = hi6210_i2s_hw_params, .set_fmt = hi6210_i2s_set_fmt, @@ -519,7 +520,6 @@ static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = { }; static const struct snd_soc_dai_driver hi6210_i2s_dai_init = { - .probe = hi6210_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index b7ab8467b5cf..b6b6339c164b 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -370,12 +370,6 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { - .trigger = img_i2s_in_trigger, - .hw_params = img_i2s_in_hw_params, - .set_fmt = img_i2s_in_set_fmt -}; - static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) { struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); @@ -385,6 +379,13 @@ static int img_i2s_in_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_i2s_in_dai_ops = { + .probe = img_i2s_in_dai_probe, + .trigger = img_i2s_in_trigger, + .hw_params = img_i2s_in_hw_params, + .set_fmt = img_i2s_in_set_fmt +}; + static const struct snd_soc_component_driver img_i2s_in_component = { .name = "img-i2s-in", .legacy_dai_naming = 1, @@ -468,7 +469,6 @@ static int img_i2s_in_probe(struct platform_device *pdev) i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; i2s->dma_data.addr_width = 4; - i2s->dai_driver.probe = img_i2s_in_dai_probe; i2s->dai_driver.capture.channels_min = 2; i2s->dai_driver.capture.channels_max = i2s->max_i2s_chan * 2; i2s->dai_driver.capture.rates = SNDRV_PCM_RATE_8000_192000; diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index fe95ddfb8407..41ea5ba52181 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -376,12 +376,6 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { - .trigger = img_i2s_out_trigger, - .hw_params = img_i2s_out_hw_params, - .set_fmt = img_i2s_out_set_fmt -}; - static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) { struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); @@ -391,6 +385,13 @@ static int img_i2s_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_i2s_out_dai_ops = { + .probe = img_i2s_out_dai_probe, + .trigger = img_i2s_out_trigger, + .hw_params = img_i2s_out_hw_params, + .set_fmt = img_i2s_out_set_fmt +}; + static const struct snd_soc_component_driver img_i2s_out_component = { .name = "img-i2s-out", .legacy_dai_naming = 1, @@ -504,7 +505,6 @@ static int img_i2s_out_probe(struct platform_device *pdev) i2s->dma_data.addr_width = 4; i2s->dma_data.maxburst = 4; - i2s->dai_driver.probe = img_i2s_out_dai_probe; i2s->dai_driver.playback.channels_min = 2; i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2; i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000; diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index df1291ee2b3b..815e68a7048c 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -174,12 +174,6 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops img_prl_out_dai_ops = { - .trigger = img_prl_out_trigger, - .hw_params = img_prl_out_hw_params, - .set_fmt = img_prl_out_set_fmt -}; - static int img_prl_out_dai_probe(struct snd_soc_dai *dai) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); @@ -189,8 +183,14 @@ static int img_prl_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_prl_out_dai_ops = { + .probe = img_prl_out_dai_probe, + .trigger = img_prl_out_trigger, + .hw_params = img_prl_out_hw_params, + .set_fmt = img_prl_out_set_fmt +}; + static struct snd_soc_dai_driver img_prl_out_dai = { - .probe = img_prl_out_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 558062a1804a..9646e9d3f0bc 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -682,11 +682,6 @@ static int img_spdif_in_hw_params(struct snd_pcm_substream *substream, return img_spdif_in_do_clkgen_single(spdif, rate); } -static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { - .trigger = img_spdif_in_trigger, - .hw_params = img_spdif_in_hw_params -}; - static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) { struct img_spdif_in *spdif = snd_soc_dai_get_drvdata(dai); @@ -699,8 +694,13 @@ static int img_spdif_in_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_spdif_in_dai_ops = { + .probe = img_spdif_in_dai_probe, + .trigger = img_spdif_in_trigger, + .hw_params = img_spdif_in_hw_params +}; + static struct snd_soc_dai_driver img_spdif_in_dai = { - .probe = img_spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index b13e128e50d6..dfa72afa946e 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -287,11 +287,6 @@ static int img_spdif_out_hw_params(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { - .trigger = img_spdif_out_trigger, - .hw_params = img_spdif_out_hw_params -}; - static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) { struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); @@ -304,8 +299,13 @@ static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { + .probe = img_spdif_out_dai_probe, + .trigger = img_spdif_out_trigger, + .hw_params = img_spdif_out_hw_params +}; + static struct snd_soc_dai_driver img_spdif_out_dai = { - .probe = img_spdif_out_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index ba4597bdf32e..6f986c7bbc8b 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -467,6 +467,7 @@ static const struct snd_soc_dai_ops sst_media_dai_ops = { }; static const struct snd_soc_dai_ops sst_compr_dai_ops = { + .compress_new = snd_soc_new_compress, .mute_stream = sst_media_digital_mute, }; @@ -510,7 +511,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = { }, { .name = "compress-cpu-dai", - .compress_new = snd_soc_new_compress, .ops = &sst_compr_dai_ops, .playback = { .stream_name = "Compress Playback", diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 862a19ae5429..288221db7323 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -173,10 +173,11 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, u32 length; int pvt_id, ret = 0; struct sst_block *block = NULL; + u8 bytes_block = bytes->block; dev_dbg(sst_drv_ctx->dev, "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", - bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id, + bytes->type, bytes->ipc_msg, bytes_block, bytes->task_id, bytes->pipe_id, bytes->len); if (sst_create_ipc_msg(&msg, true)) @@ -185,12 +186,12 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, pvt_id = sst_assign_pvt_id(sst_drv_ctx); sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, bytes->task_id, 1, pvt_id); - msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; + msg->mrfld_header.p.header_high.part.res_rqd = bytes_block; length = bytes->len; msg->mrfld_header.p.header_low_payload = length; dev_dbg(sst_drv_ctx->dev, "length is %d\n", length); memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); - if (bytes->block) { + if (bytes_block) { block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); if (block == NULL) { kfree(msg); @@ -203,7 +204,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d", msg->mrfld_header.p.header_low_payload); - if (bytes->block) { + if (bytes_block) { ret = sst_wait_timeout(sst_drv_ctx, block); if (ret) { dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret); @@ -216,7 +217,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, * copy the reply and send back * we need to update only sz and payload */ - if (bytes->block) { + if (bytes_block) { unsigned char *r = block->data; dev_dbg(sst_drv_ctx->dev, "read back %d bytes", @@ -224,7 +225,7 @@ int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, memcpy(bytes->bytes, r, bytes->len); } } - if (bytes->block) + if (bytes_block) sst_free_block(sst_drv_ctx, block); out: test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id); diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c index 964a763732ab..85014d98f7e8 100644 --- a/sound/soc/intel/avs/boards/da7219.c +++ b/sound/soc/intel/avs/boards/da7219.c @@ -22,6 +22,7 @@ static const struct snd_kcontrol_new card_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static int platform_clock_control(struct snd_soc_dapm_widget *w, @@ -55,6 +56,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget card_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), }; @@ -68,6 +70,22 @@ static const struct snd_soc_dapm_route card_base_routes[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, +}; + +static const struct snd_soc_jack_pin card_headset_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) @@ -75,7 +93,9 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0); struct snd_soc_component *component = codec_dai->component; struct snd_soc_card *card = runtime->card; + struct snd_soc_jack_pin *pins; struct snd_soc_jack *jack; + int num_pins; int clk_freq; int ret; @@ -91,14 +111,20 @@ static int avs_da7219_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } + num_pins = ARRAY_SIZE(card_headset_pins); + pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL); + if (!pins) + return -ENOMEM; + /* * Headset buttons map to the google Reference headset. * These can be configured by userspace. */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_LINEOUT, jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3 | SND_JACK_LINEOUT, + jack, pins, num_pins); if (ret) { dev_err(card->dev, "Headset Jack creation failed: %d\n", ret); return ret; diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c index 275928281c6c..4cab8c6c4576 100644 --- a/sound/soc/intel/avs/probes.c +++ b/sound/soc/intel/avs/probes.c @@ -249,7 +249,7 @@ static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr return count; } -static const struct snd_soc_cdai_ops avs_probe_dai_ops = { +static const struct snd_soc_cdai_ops avs_probe_cdai_ops = { .startup = avs_probe_compr_open, .shutdown = avs_probe_compr_free, .set_params = avs_probe_compr_set_params, @@ -257,6 +257,10 @@ static const struct snd_soc_cdai_ops avs_probe_dai_ops = { .pointer = avs_probe_compr_pointer, }; +static const struct snd_soc_dai_ops avs_probe_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static const struct snd_compress_ops avs_probe_compress_ops = { .copy = avs_probe_compr_copy, }; @@ -264,8 +268,8 @@ static const struct snd_compress_ops avs_probe_compress_ops = { static struct snd_soc_dai_driver probe_cpu_dais[] = { { .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, - .cops = &avs_probe_dai_ops, + .cops = &avs_probe_cdai_ops, + .ops = &avs_probe_dai_ops, .capture = { .stream_name = "Probe Extraction", .channels_min = 1, diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index cdb4ec500261..45d0eb2a8e71 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -1388,12 +1388,12 @@ static int avs_route_load(struct snd_soc_component *comp, int index, port = __ffs(mach->mach_params.i2s_link_mask); snprintf(buf, len, route->source, port); - strncpy((char *)route->source, buf, len); + strscpy((char *)route->source, buf, len); snprintf(buf, len, route->sink, port); - strncpy((char *)route->sink, buf, len); + strscpy((char *)route->sink, buf, len); if (route->control) { snprintf(buf, len, route->control, port); - strncpy((char *)route->control, buf, len); + strscpy((char *)route->control, buf, len); } } diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 1fe830af2b84..0ae6eecc8851 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -671,14 +671,15 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_RT711_SDCA_SDW select SND_SOC_RT712_SDCA_SDW select SND_SOC_RT712_SDCA_DMIC_SDW + select SND_SOC_RT715_SDW + select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT1308_SDW select SND_SOC_RT1308 select SND_SOC_RT1316_SDW select SND_SOC_RT1318_SDW - select SND_SOC_RT715_SDW - select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT5682_SDW select SND_SOC_CS42L42_SDW + select SND_SOC_CS35L56_SDW select SND_SOC_DMIC select SND_SOC_INTEL_HDA_DSP_COMMON select SND_SOC_INTEL_SOF_MAXIM_COMMON diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 931415d9cf6f..a570b5b40f22 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -42,7 +42,7 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_rt711.o sof_sdw_rt_sdca_jack_common.o \ sof_sdw_rt712_sdca.o sof_sdw_rt715.o \ sof_sdw_rt715_sdca.o sof_sdw_dmic.o \ - sof_sdw_cs42l42.o \ + sof_sdw_cs42l42.o sof_sdw_cs_amp.o \ sof_sdw_hdmi.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_SOF_CS42L42_MACH) += snd-soc-sof_cs42l42.o diff --git a/sound/soc/intel/boards/bdw_rt286.c b/sound/soc/intel/boards/bdw_rt286.c index b7687a93a923..036579331d8f 100644 --- a/sound/soc/intel/boards/bdw_rt286.c +++ b/sound/soc/intel/boards/bdw_rt286.c @@ -187,6 +187,9 @@ static int card_suspend_pre(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1"); + if (!codec_dai) + return 0; + return snd_soc_component_set_jack(codec_dai->component, NULL, NULL); } @@ -194,6 +197,9 @@ static int card_resume_post(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, "rt286-aif1"); + if (!codec_dai) + return 0; + return snd_soc_component_set_jack(codec_dai->component, &card_headset, NULL); } diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index c593995facaa..cbfff466c5c8 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -90,6 +90,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_kcontrol_new broxton_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_kcontrol_new max98357a_controls[] = { @@ -104,6 +105,7 @@ static const struct snd_kcontrol_new max98390_controls[] = { static const struct snd_soc_dapm_widget broxton_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -150,6 +152,7 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static const struct snd_soc_dapm_route max98357a_routes[] = { @@ -194,6 +197,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c index f2382d4cb76f..5c9e06ed1a53 100644 --- a/sound/soc/intel/boards/bytcr_wm5102.c +++ b/sound/soc/intel/boards/bytcr_wm5102.c @@ -131,6 +131,7 @@ static const struct snd_soc_dapm_widget byt_wm5102_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -144,6 +145,7 @@ static const struct snd_soc_dapm_route byt_wm5102_audio_map[] = { {"Headset Mic", NULL, "Platform Clock"}, {"Internal Mic", NULL, "Platform Clock"}, {"Speaker", NULL, "Platform Clock"}, + {"Line Out", NULL, "Platform Clock"}, {"Speaker", NULL, "SPKOUTLP"}, {"Speaker", NULL, "SPKOUTLN"}, @@ -177,6 +179,7 @@ static const struct snd_kcontrol_new byt_wm5102_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Internal Mic"), SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static struct snd_soc_jack_pin byt_wm5102_pins[] = { @@ -188,6 +191,10 @@ static struct snd_soc_jack_pin byt_wm5102_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime) diff --git a/sound/soc/intel/boards/kbl_da7219_max98357a.c b/sound/soc/intel/boards/kbl_da7219_max98357a.c index 18365ce6bcba..97149513076f 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98357a.c +++ b/sound/soc/intel/boards/kbl_da7219_max98357a.c @@ -83,12 +83,14 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -107,6 +109,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static const struct snd_soc_dapm_route kabylake_map[] = { @@ -147,6 +153,7 @@ static const struct snd_soc_dapm_route kabylake_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index ad4223fee0c5..a1f8234c77bd 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -102,6 +102,7 @@ static const struct snd_kcontrol_new kabylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget kabylake_widgets[] = { @@ -109,6 +110,7 @@ static const struct snd_soc_dapm_widget kabylake_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("HDMI1", NULL), SND_SOC_DAPM_SPK("HDMI2", NULL), @@ -127,6 +129,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_MICROPHONE, + }, }; static const struct snd_soc_dapm_route kabylake_map[] = { @@ -182,6 +188,7 @@ static const struct snd_soc_dapm_route kabylake_ssp1_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, }; static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/intel/boards/sof_da7219_max98373.c b/sound/soc/intel/boards/sof_da7219_max98373.c index 740aa11cb019..bbd47e7e4343 100644 --- a/sound/soc/intel/boards/sof_da7219_max98373.c +++ b/sound/soc/intel/boards/sof_da7219_max98373.c @@ -65,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), }; @@ -72,6 +73,7 @@ static const struct snd_kcontrol_new controls[] = { static const struct snd_kcontrol_new m98360a_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Spk"), }; @@ -79,6 +81,7 @@ static const struct snd_kcontrol_new m98360a_controls[] = { static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), @@ -98,6 +101,7 @@ static const struct snd_soc_dapm_route audio_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, { "Left Spk", NULL, "Left BE_OUT" }, { "Right Spk", NULL, "Right BE_OUT" }, @@ -110,6 +114,7 @@ static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_widget max98360a_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Spk", NULL), @@ -128,6 +133,7 @@ static const struct snd_soc_dapm_route max98360a_map[] = { { "Headphone Jack", NULL, "Platform Clock" }, { "Headset Mic", NULL, "Platform Clock" }, + { "Line Out", NULL, "Platform Clock" }, {"Spk", NULL, "Speaker"}, @@ -144,6 +150,10 @@ static struct snd_soc_jack_pin jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static struct snd_soc_jack headset; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index d6c38d8ea2ff..f8a3e8a91761 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -798,6 +798,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "rpl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 112e89951da0..628b6d5d3ee4 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -4,6 +4,7 @@ #include <linux/module.h> #include <linux/string.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-acpi.h> #include <sound/soc-dai.h> @@ -11,6 +12,18 @@ #include <uapi/sound/asound.h> #include "sof_maxim_common.h" +/* helper function to get the number of specific codec */ +static unsigned int get_num_codecs(const char *hid) +{ + struct acpi_device *adev; + unsigned int dev_num = 0; + + for_each_acpi_dev_match(adev, hid, NULL, -1) + dev_num++; + + return dev_num; +} + #define MAX_98373_PIN_NAME 16 const struct snd_soc_dapm_route max_98373_dapm_routes[] = { @@ -168,17 +181,6 @@ static struct snd_soc_codec_conf max_98390_codec_conf[] = { .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), .name_prefix = "Left", }, -}; - -static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { - { - .dlc = COMP_CODEC_CONF(MAX_98390_DEV0_NAME), - .name_prefix = "Right", - }, - { - .dlc = COMP_CODEC_CONF(MAX_98390_DEV1_NAME), - .name_prefix = "Left", - }, { .dlc = COMP_CODEC_CONF(MAX_98390_DEV2_NAME), .name_prefix = "Tweeter Right", @@ -189,19 +191,7 @@ static struct snd_soc_codec_conf max_98390_4spk_codec_conf[] = { }, }; -struct snd_soc_dai_link_component max_98390_components[] = { - { - .name = MAX_98390_DEV0_NAME, - .dai_name = MAX_98390_CODEC_DAI, - }, - { - .name = MAX_98390_DEV1_NAME, - .dai_name = MAX_98390_CODEC_DAI, - }, -}; -EXPORT_SYMBOL_NS(max_98390_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); - -struct snd_soc_dai_link_component max_98390_4spk_components[] = { +static struct snd_soc_dai_link_component max_98390_components[] = { { .name = MAX_98390_DEV0_NAME, .dai_name = MAX_98390_CODEC_DAI, @@ -219,62 +209,56 @@ struct snd_soc_dai_link_component max_98390_4spk_components[] = { .dai_name = MAX_98390_CODEC_DAI, }, }; -EXPORT_SYMBOL_NS(max_98390_4spk_components, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +static const struct { + unsigned int tx; + unsigned int rx; +} max_98390_tdm_mask[] = { + {.tx = 0x01, .rx = 0x3}, + {.tx = 0x02, .rx = 0x3}, + {.tx = 0x04, .rx = 0x3}, + {.tx = 0x08, .rx = 0x3}, +}; static int max_98390_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *codec_dai; - int i; + int i, ret; for_each_rtd_codec_dais(rtd, i, codec_dai) { - if (i >= ARRAY_SIZE(max_98390_4spk_components)) { + if (i >= ARRAY_SIZE(max_98390_tdm_mask)) { dev_err(codec_dai->dev, "invalid codec index %d\n", i); return -ENODEV; } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV0_NAME)) { - /* DEV0 tdm slot configuration Right */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x01, 3, 4, 32); - } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV1_NAME)) { - /* DEV1 tdm slot configuration Left */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x02, 3, 4, 32); - } - - if (!strcmp(codec_dai->component->name, MAX_98390_DEV2_NAME)) { - /* DEVi2 tdm slot configuration Tweeter Right */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x04, 3, 4, 32); - } - if (!strcmp(codec_dai->component->name, MAX_98390_DEV3_NAME)) { - /* DEV3 tdm slot configuration Tweeter Left */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x08, 3, 4, 32); + ret = snd_soc_dai_set_tdm_slot(codec_dai, max_98390_tdm_mask[i].tx, + max_98390_tdm_mask[i].rx, 4, + params_width(params)); + if (ret < 0) { + dev_err(codec_dai->dev, "fail to set tdm slot, ret %d\n", + ret); + return ret; } } return 0; } -int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) +static int max_98390_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); int ret; - /* add regular speakers dapm route */ - ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, - ARRAY_SIZE(max_98390_dapm_routes)); - if (ret) { - dev_err(rtd->dev, "unable to add Left/Right Speaker dapm, ret %d\n", ret); - return ret; - } - - /* add widgets/controls/dapm for tweeter speakers */ - if (acpi_dev_present("MX98390", "3", -1)) { + switch (num_codecs) { + case 4: + /* add widgets/controls/dapm for tweeter speakers */ ret = snd_soc_dapm_new_controls(&card->dapm, max_98390_tt_dapm_widgets, ARRAY_SIZE(max_98390_tt_dapm_widgets)); - if (ret) { - dev_err(rtd->dev, "unable to add tweeter dapm controls, ret %d\n", ret); + dev_err(rtd->dev, "unable to add tweeter dapm widgets, ret %d\n", + ret); /* Don't need to add routes if widget addition failed */ return ret; } @@ -282,33 +266,79 @@ int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd) ret = snd_soc_add_card_controls(card, max_98390_tt_kcontrols, ARRAY_SIZE(max_98390_tt_kcontrols)); if (ret) { - dev_err(rtd->dev, "unable to add tweeter card controls, ret %d\n", ret); + dev_err(rtd->dev, "unable to add tweeter controls, ret %d\n", + ret); return ret; } ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_tt_dapm_routes, ARRAY_SIZE(max_98390_tt_dapm_routes)); - if (ret) - dev_err(rtd->dev, - "unable to add Tweeter Left/Right Speaker dapm, ret %d\n", ret); + if (ret) { + dev_err(rtd->dev, "unable to add tweeter dapm routes, ret %d\n", + ret); + return ret; + } + + fallthrough; + case 2: + /* add regular speakers dapm route */ + ret = snd_soc_dapm_add_routes(&card->dapm, max_98390_dapm_routes, + ARRAY_SIZE(max_98390_dapm_routes)); + if (ret) { + dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", + ret); + return ret; + } + break; + default: + dev_err(rtd->dev, "invalid codec number %d\n", num_codecs); + return -EINVAL; } + return ret; } -EXPORT_SYMBOL_NS(max_98390_spk_codec_init, SND_SOC_INTEL_SOF_MAXIM_COMMON); -const struct snd_soc_ops max_98390_ops = { +static const struct snd_soc_ops max_98390_ops = { .hw_params = max_98390_hw_params, }; -EXPORT_SYMBOL_NS(max_98390_ops, SND_SOC_INTEL_SOF_MAXIM_COMMON); -void max_98390_set_codec_conf(struct snd_soc_card *card, int ch) +void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link) +{ + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); + + link->codecs = max_98390_components; + + switch (num_codecs) { + case 2: + case 4: + link->num_codecs = num_codecs; + break; + default: + dev_err(dev, "invalid codec number %d for %s\n", num_codecs, + MAX_98390_ACPI_HID); + break; + } + + link->init = max_98390_init; + link->ops = &max_98390_ops; +} +EXPORT_SYMBOL_NS(max_98390_dai_link, SND_SOC_INTEL_SOF_MAXIM_COMMON); + +void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card) { - if (ch == ARRAY_SIZE(max_98390_4spk_codec_conf)) { - card->codec_conf = max_98390_4spk_codec_conf; - card->num_configs = ARRAY_SIZE(max_98390_4spk_codec_conf); - } else { - card->codec_conf = max_98390_codec_conf; - card->num_configs = ARRAY_SIZE(max_98390_codec_conf); + unsigned int num_codecs = get_num_codecs(MAX_98390_ACPI_HID); + + card->codec_conf = max_98390_codec_conf; + + switch (num_codecs) { + case 2: + case 4: + card->num_configs = num_codecs; + break; + default: + dev_err(dev, "invalid codec number %d for %s\n", num_codecs, + MAX_98390_ACPI_HID); + break; } } EXPORT_SYMBOL_NS(max_98390_set_codec_conf, SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_maxim_common.h b/sound/soc/intel/boards/sof_maxim_common.h index 7a8c53049e4d..a095b47b856b 100644 --- a/sound/soc/intel/boards/sof_maxim_common.h +++ b/sound/soc/intel/boards/sof_maxim_common.h @@ -27,18 +27,15 @@ int max_98373_trigger(struct snd_pcm_substream *substream, int cmd); /* * Maxim MAX98390 */ -#define MAX_98390_CODEC_DAI "max98390-aif1" -#define MAX_98390_DEV0_NAME "i2c-MX98390:00" -#define MAX_98390_DEV1_NAME "i2c-MX98390:01" -#define MAX_98390_DEV2_NAME "i2c-MX98390:02" -#define MAX_98390_DEV3_NAME "i2c-MX98390:03" - -extern struct snd_soc_dai_link_component max_98390_components[2]; -extern struct snd_soc_dai_link_component max_98390_4spk_components[4]; -extern const struct snd_soc_ops max_98390_ops; - -void max_98390_set_codec_conf(struct snd_soc_card *card, int ch); -int max_98390_spk_codec_init(struct snd_soc_pcm_runtime *rtd); +#define MAX_98390_ACPI_HID "MX98390" +#define MAX_98390_CODEC_DAI "max98390-aif1" +#define MAX_98390_DEV0_NAME "i2c-" MAX_98390_ACPI_HID ":00" +#define MAX_98390_DEV1_NAME "i2c-" MAX_98390_ACPI_HID ":01" +#define MAX_98390_DEV2_NAME "i2c-" MAX_98390_ACPI_HID ":02" +#define MAX_98390_DEV3_NAME "i2c-" MAX_98390_ACPI_HID ":03" + +void max_98390_dai_link(struct device *dev, struct snd_soc_dai_link *link); +void max_98390_set_codec_conf(struct device *dev, struct snd_soc_card *card); /* * Maxim MAX98357A/MAX98360A diff --git a/sound/soc/intel/boards/sof_nau8825.c b/sound/soc/intel/boards/sof_nau8825.c index 4fc6e1c6aef3..46b7ecf6f9f1 100644 --- a/sound/soc/intel/boards/sof_nau8825.c +++ b/sound/soc/intel/boards/sof_nau8825.c @@ -684,6 +684,16 @@ static const struct platform_device_id board_ids[] = { SOF_BT_OFFLOAD_SSP(2) | SOF_SSP_BT_OFFLOAD_PRESENT), }, + { + .name = "rpl_nau8318_8825", + .driver_data = (kernel_ulong_t)(SOF_NAU8825_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_NAU8318_SPEAKER_AMP_PRESENT | + SOF_NAU8825_SSP_AMP(1) | + SOF_NAU8825_NUM_HDMIDEV(4) | + SOF_BT_OFFLOAD_SSP(2) | + SOF_SSP_BT_OFFLOAD_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index b4f07bdcf8b4..fae091b9b55c 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -59,10 +59,14 @@ #define SOF_SSP_BT_OFFLOAD_PRESENT BIT(22) #define SOF_RT5682S_HEADPHONE_CODEC_PRESENT BIT(23) #define SOF_MAX98390_SPEAKER_AMP_PRESENT BIT(24) -#define SOF_MAX98390_TWEETER_SPEAKER_PRESENT BIT(25) #define SOF_RT1019_SPEAKER_AMP_PRESENT BIT(26) #define SOF_RT5650_HEADPHONE_CODEC_PRESENT BIT(27) +/* HDMI capture*/ +#define SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT 27 +#define SOF_SSP_HDMI_CAPTURE_PRESENT_MASK (GENMASK(30, 27)) +#define SOF_HDMI_CAPTURE_SSP_MASK(quirk) \ + (((quirk) << SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT) & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) /* Default: MCLK on, MCLK 19.2M, SSP0 */ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN | @@ -199,23 +203,6 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .callback = sof_rt5682_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), - DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98390_ALC5682I_I2S_4SPK"), - }, - .driver_data = (void *)(SOF_RT5682_MCLK_EN | - SOF_RT5682_SSP_CODEC(0) | - SOF_SPEAKER_AMP_PRESENT | - SOF_MAX98390_SPEAKER_AMP_PRESENT | - SOF_MAX98390_TWEETER_SPEAKER_PRESENT | - SOF_RT5682_SSP_AMP(1) | - SOF_RT5682_NUM_HDMIDEV(4) | - SOF_BT_OFFLOAD_SSP(2) | - SOF_SSP_BT_OFFLOAD_PRESENT), - - }, - { - .callback = sof_rt5682_quirk_cb, - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Brya"), DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98360_ALC5682I_I2S_AMP_SSP2"), }, .driver_data = (void *)(SOF_RT5682_MCLK_EN | @@ -688,6 +675,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, struct snd_soc_dai_link_component *cpus; struct snd_soc_dai_link *links; int i, id = 0; + int hdmi_id_offset = 0; links = devm_kcalloc(dev, sof_audio_card_rt5682.num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL); @@ -850,17 +838,7 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, sof_rt1011_dai_link(&links[id]); } else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { - if (sof_rt5682_quirk & - SOF_MAX98390_TWEETER_SPEAKER_PRESENT) { - links[id].codecs = max_98390_4spk_components; - links[id].num_codecs = ARRAY_SIZE(max_98390_4spk_components); - } else { - links[id].codecs = max_98390_components; - links[id].num_codecs = ARRAY_SIZE(max_98390_components); - } - links[id].init = max_98390_spk_codec_init; - links[id].ops = &max_98390_ops; - + max_98390_dai_link(dev, &links[id]); } else if (sof_rt5682_quirk & SOF_RT5650_HEADPHONE_CODEC_PRESENT) { links[id].codecs = &rt5650_components[1]; links[id].num_codecs = 1; @@ -919,6 +897,34 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev, links[id].num_cpus = 1; } + /* HDMI-In SSP */ + if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) { + unsigned long hdmi_in_ssp = (sof_rt5682_quirk & + SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT; + int port = 0; + + for_each_set_bit(port, &hdmi_in_ssp, 32) { + links[id].cpus = &cpus[id]; + links[id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, + "SSP%d Pin", port); + if (!links[id].cpus->dai_name) + return NULL; + links[id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", port); + if (!links[id].name) + return NULL; + links[id].id = id + hdmi_id_offset; + links[id].codecs = &asoc_dummy_dlc; + links[id].num_codecs = 1; + links[id].platforms = platform_component; + links[id].num_platforms = ARRAY_SIZE(platform_component); + links[id].dpcm_capture = 1; + links[id].no_pcm = 1; + links[id].num_cpus = 1; + id++; + } + } + return links; devm_err: return NULL; @@ -1019,17 +1025,17 @@ static int sof_audio_probe(struct platform_device *pdev) else if (sof_rt5682_quirk & SOF_RT1015P_SPEAKER_AMP_PRESENT) sof_rt1015p_codec_conf(&sof_audio_card_rt5682); else if (sof_rt5682_quirk & SOF_MAX98390_SPEAKER_AMP_PRESENT) { - if (sof_rt5682_quirk & SOF_MAX98390_TWEETER_SPEAKER_PRESENT) - max_98390_set_codec_conf(&sof_audio_card_rt5682, - ARRAY_SIZE(max_98390_4spk_components)); - else - max_98390_set_codec_conf(&sof_audio_card_rt5682, - ARRAY_SIZE(max_98390_components)); + max_98390_set_codec_conf(&pdev->dev, &sof_audio_card_rt5682); } if (sof_rt5682_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) sof_audio_card_rt5682.num_links++; + if (sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) + sof_audio_card_rt5682.num_links += + hweight32((sof_rt5682_quirk & SOF_SSP_HDMI_CAPTURE_PRESENT_MASK) >> + SOF_NO_OF_HDMI_CAPTURE_SSP_SHIFT); + dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp, dmic_be_num, hdmi_num, ctx->idisp_codec); if (!dai_links) @@ -1197,6 +1203,22 @@ static const struct platform_device_id board_ids[] = { SOF_SSP_BT_OFFLOAD_PRESENT), }, { + .name = "adl_rt5682_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(1) | + SOF_RT5682_NUM_HDMIDEV(3) | + /* SSP 0 and SSP 2 are used for HDMI IN */ + SOF_HDMI_CAPTURE_SSP_MASK(0x5)), + }, + { + .name = "rpl_mx98357_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, + { .name = "rpl_mx98360_rt5682", .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 0201029899ca..5a1c750e6ae6 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -311,6 +311,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .callback = sof_sdw_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | + RT711_JD2 | + SOF_SDW_FOUR_SPK), + }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") }, .driver_data = (void *)(SOF_SDW_TGL_HDMI | @@ -467,7 +477,9 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), DMI_MATCH(DMI_PRODUCT_NAME, "Rex"), }, - .driver_data = (void *)(SOF_SDW_PCH_DMIC), + .driver_data = (void *)(SOF_SDW_PCH_DMIC | + SOF_BT_OFFLOAD_SSP(1) | + SOF_SSP_BT_OFFLOAD_PRESENT), }, /* LunarLake devices */ { @@ -511,9 +523,8 @@ int sdw_prepare(struct snd_pcm_substream *substream) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -531,9 +542,8 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -555,7 +565,7 @@ int sdw_trigger(struct snd_pcm_substream *substream, int cmd) } if (ret) - dev_err(rtd->dev, "%s trigger %d failed: %d", __func__, cmd, ret); + dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); return ret; } @@ -619,9 +629,8 @@ int sdw_hw_free(struct snd_pcm_substream *substream) dai = asoc_rtd_to_cpu(rtd, 0); sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); - if (IS_ERR(sdw_stream)) { - dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); return PTR_ERR(sdw_stream); } @@ -895,6 +904,19 @@ static struct sof_sdw_codec_info codec_info_list[] = { .dai_num = 1, }, { + .part_id = 0x3556, + .dais = { + { + .direction = {true, true}, + .dai_name = "cs35l56-sdw1", + .dai_type = SOF_SDW_DAI_TYPE_AMP, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, + .init = sof_sdw_cs_amp_init, + }, + }, + .dai_num = 1, + }, + { .part_id = 0x4242, .dais = { { @@ -940,10 +962,10 @@ static struct sof_sdw_codec_info codec_info_list[] = { .version_id = 0, .dais = { { - .direction = {true, false}, + .direction = {true, true}, .dai_name = "sdw-mockup-aif1", .dai_type = SOF_SDW_DAI_TYPE_AMP, - .dailink = {SDW_AMP_OUT_DAI_ID, SDW_UNUSED_DAI_ID}, + .dailink = {SDW_AMP_OUT_DAI_ID, SDW_AMP_IN_DAI_ID}, .init = NULL, }, }, @@ -965,7 +987,7 @@ static struct sof_sdw_codec_info codec_info_list[] = { }, }; -static inline int find_codec_info_part(u64 adr) +static inline int find_codec_info_part(const u64 adr) { unsigned int part_id, sdw_version; int i; @@ -994,14 +1016,10 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) return -EINVAL; for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) - if (!memcmp(codec_info_list[i].acpi_id, acpi_id, - ACPI_ID_LEN)) - break; - - if (i == ARRAY_SIZE(codec_info_list)) - return -EINVAL; + if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) + return i; - return i; + return -EINVAL; } /* @@ -1009,10 +1027,10 @@ static inline int find_codec_info_acpi(const u8 *acpi_id) * Since some sdw slaves may be aggregated, the CPU DAI number * may be larger than the number of BE dailinks. */ -static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_link_adr *links, - int *sdw_be_num, int *sdw_cpu_dai_num) +static int get_dailink_info(struct device *dev, + const struct snd_soc_acpi_link_adr *adr_link, + int *sdw_be_num, int *sdw_cpu_dai_num, int *codecs_num) { - const struct snd_soc_acpi_link_adr *link; bool group_visited[SDW_MAX_GROUPS]; bool no_aggregation; int i; @@ -1022,27 +1040,45 @@ static int get_sdw_dailink_info(struct device *dev, const struct snd_soc_acpi_li *sdw_cpu_dai_num = 0; *sdw_be_num = 0; - if (!links) + if (!adr_link) return -EINVAL; for (i = 0; i < SDW_MAX_GROUPS; i++) group_visited[i] = false; - for (link = links; link->num_adr; link++) { + for (; adr_link->num_adr; adr_link++) { const struct snd_soc_acpi_endpoint *endpoint; struct sof_sdw_codec_info *codec_info; int codec_index; int stream; u64 adr; - for (i = 0; i < link->num_adr; i++) { - adr = link->adr_d[i].adr; + /* make sure the link mask has a single bit set */ + if (!is_power_of_2(adr_link->mask)) + return -EINVAL; + + for (i = 0; i < adr_link->num_adr; i++) { + adr = adr_link->adr_d[i].adr; codec_index = find_codec_info_part(adr); if (codec_index < 0) return codec_index; + codec_info = &codec_info_list[codec_index]; - endpoint = link->adr_d[i].endpoints; + *codecs_num += codec_info->dai_num; + + if (!adr_link->adr_d[i].name_prefix) { + dev_err(dev, "codec 0x%llx does not have a name prefix\n", + adr_link->adr_d[i].adr); + return -EINVAL; + } + + endpoint = adr_link->adr_d[i].endpoints; + if (endpoint->aggregated && !endpoint->group_id) { + dev_err(dev, "invalid group id on link %x\n", + adr_link->mask); + return -EINVAL; + } for (j = 0; j < codec_info->dai_num; j++) { /* count DAI number for playback and capture */ @@ -1090,17 +1126,16 @@ static void init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links dai_links->ops = ops; } -static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, +static bool is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, unsigned int sdw_version, unsigned int mfg_id, unsigned int part_id, unsigned int class_id, - int index_in_link - ) + int index_in_link) { int i; - for (i = 0; i < link->num_adr; i++) { + for (i = 0; i < adr_link->num_adr; i++) { unsigned int sdw1_version, mfg1_id, part1_id, class1_id; u64 adr; @@ -1108,7 +1143,7 @@ static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, if (i == index_in_link) continue; - adr = link->adr_d[i].adr; + adr = adr_link->adr_d[i].adr; sdw1_version = SDW_VERSION(adr); mfg1_id = SDW_MFG_ID(adr); @@ -1125,83 +1160,50 @@ static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, return true; } -static int create_codec_dai_name(struct device *dev, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link_component *codec, - int offset, - struct snd_soc_codec_conf *codec_conf, - int codec_count, - int *codec_conf_index, - int adr_index, - int dai_index) +static int fill_sdw_codec_dlc(struct device *dev, + const struct snd_soc_acpi_link_adr *adr_link, + struct snd_soc_dai_link_component *codec, + int adr_index, int dai_index) { - int _codec_index = -1; - int i; - - /* sanity check */ - if (*codec_conf_index + link->num_adr - adr_index > codec_count) { - dev_err(dev, "codec_conf: out-of-bounds access requested\n"); - return -EINVAL; - } - - for (i = adr_index; i < link->num_adr; i++) { - unsigned int sdw_version, unique_id, mfg_id; - unsigned int link_id, part_id, class_id; - int codec_index, comp_index; - char *codec_str; - u64 adr; - - adr = link->adr_d[i].adr; - - sdw_version = SDW_VERSION(adr); - link_id = SDW_DISCO_LINK_ID(adr); - unique_id = SDW_UNIQUE_ID(adr); - mfg_id = SDW_MFG_ID(adr); - part_id = SDW_PART_ID(adr); - class_id = SDW_CLASS_ID(adr); - - comp_index = i - adr_index + offset; - if (is_unique_device(link, sdw_version, mfg_id, part_id, - class_id, i)) { - codec_str = "sdw:%01x:%04x:%04x:%02x"; - codec[comp_index].name = - devm_kasprintf(dev, GFP_KERNEL, codec_str, - link_id, mfg_id, part_id, - class_id); - } else { - codec_str = "sdw:%01x:%04x:%04x:%02x:%01x"; - codec[comp_index].name = - devm_kasprintf(dev, GFP_KERNEL, codec_str, - link_id, mfg_id, part_id, - class_id, unique_id); - } - - if (!codec[comp_index].name) - return -ENOMEM; - - codec_index = find_codec_info_part(adr); - if (codec_index < 0) - return codec_index; - if (_codec_index != -1 && codec_index != _codec_index) { - dev_dbg(dev, "Different devices on the same sdw link\n"); - break; - } - _codec_index = codec_index; + unsigned int sdw_version, unique_id, mfg_id, link_id, part_id, class_id; + u64 adr = adr_link->adr_d[adr_index].adr; + int codec_index; - codec[comp_index].dai_name = - codec_info_list[codec_index].dais[dai_index].dai_name; + codec_index = find_codec_info_part(adr); + if (codec_index < 0) + return codec_index; - codec_conf[*codec_conf_index].dlc = codec[comp_index]; - codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix; + sdw_version = SDW_VERSION(adr); + link_id = SDW_DISCO_LINK_ID(adr); + unique_id = SDW_UNIQUE_ID(adr); + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + class_id = SDW_CLASS_ID(adr); + + if (codec_info_list[codec_index].codec_name) + codec->name = devm_kstrdup(dev, + codec_info_list[codec_index].codec_name, + GFP_KERNEL); + else if (is_unique_device(adr_link, sdw_version, mfg_id, part_id, + class_id, adr_index)) + codec->name = devm_kasprintf(dev, GFP_KERNEL, + "sdw:%01x:%04x:%04x:%02x", link_id, + mfg_id, part_id, class_id); + else + codec->name = devm_kasprintf(dev, GFP_KERNEL, + "sdw:%01x:%04x:%04x:%02x:%01x", link_id, + mfg_id, part_id, class_id, unique_id); + + if (!codec->name) + return -ENOMEM; - ++*codec_conf_index; - } + codec->dai_name = codec_info_list[codec_index].dais[dai_index].dai_name; return 0; } static int set_codec_init_func(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, + const struct snd_soc_acpi_link_adr *adr_link, struct snd_soc_dai_link *dai_links, bool playback, int group_id, int adr_index, int dai_index) { @@ -1212,34 +1214,34 @@ static int set_codec_init_func(struct snd_soc_card *card, * Initialize the codec. If codec is part of an aggregated * group (group_id>0), initialize all codecs belonging to * same group. - * The first link should start with link->adr_d[adr_index] + * The first link should start with adr_link->adr_d[adr_index] * because that is the device that we want to initialize and * we should end immediately if it is not aggregated (group_id=0) */ - for ( ; i < link->num_adr; i++) { + for ( ; i < adr_link->num_adr; i++) { int codec_index; - codec_index = find_codec_info_part(link->adr_d[i].adr); - + codec_index = find_codec_info_part(adr_link->adr_d[i].adr); if (codec_index < 0) return codec_index; /* The group_id is > 0 iff the codec is aggregated */ - if (link->adr_d[i].endpoints->group_id != group_id) + if (adr_link->adr_d[i].endpoints->group_id != group_id) continue; if (codec_info_list[codec_index].dais[dai_index].init) codec_info_list[codec_index].dais[dai_index].init(card, - link, + adr_link, dai_links, &codec_info_list[codec_index], playback); if (!group_id) return 0; } + i = 0; - link++; - } while (link->mask); + adr_link++; + } while (adr_link->mask); return 0; } @@ -1261,73 +1263,45 @@ static int set_codec_init_func(struct snd_soc_card *card, static int get_slave_info(const struct snd_soc_acpi_link_adr *adr_link, struct device *dev, int *cpu_dai_id, int *cpu_dai_num, int *codec_num, unsigned int *group_id, - bool *group_generated, int adr_index) + int adr_index) { - const struct snd_soc_acpi_adr_device *adr_d; - const struct snd_soc_acpi_link_adr *adr_next; - bool no_aggregation; - int index = 0; + bool no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; int i; - no_aggregation = sof_sdw_quirk & SOF_SDW_NO_AGGREGATION; - adr_d = &adr_link->adr_d[adr_index]; - - /* make sure the link mask has a single bit set */ - if (!is_power_of_2(adr_link->mask)) - return -EINVAL; - - cpu_dai_id[index++] = ffs(adr_link->mask) - 1; - if (!adr_d->endpoints->aggregated || no_aggregation) { + if (!adr_link->adr_d[adr_index].endpoints->aggregated || no_aggregation) { + cpu_dai_id[0] = ffs(adr_link->mask) - 1; *cpu_dai_num = 1; *codec_num = 1; *group_id = 0; return 0; } - *group_id = adr_d->endpoints->group_id; - - /* Count endpoints with the same group_id in the adr_link */ *codec_num = 0; - for (i = 0; i < adr_link->num_adr; i++) { - if (adr_link->adr_d[i].endpoints->aggregated && - adr_link->adr_d[i].endpoints->group_id == *group_id) - (*codec_num)++; - } + *cpu_dai_num = 0; + *group_id = adr_link->adr_d[adr_index].endpoints->group_id; - /* gather other link ID of slaves in the same group */ - for (adr_next = adr_link + 1; adr_next && adr_next->num_adr; - adr_next++) { - const struct snd_soc_acpi_endpoint *endpoint; + /* Count endpoints with the same group_id in the adr_link */ + for (; adr_link && adr_link->num_adr; adr_link++) { + unsigned int link_codecs = 0; - endpoint = adr_next->adr_d->endpoints; - if (!endpoint->aggregated || - endpoint->group_id != *group_id) - continue; + for (i = 0; i < adr_link->num_adr; i++) { + if (adr_link->adr_d[i].endpoints->aggregated && + adr_link->adr_d[i].endpoints->group_id == *group_id) + link_codecs++; + } - /* make sure the link mask has a single bit set */ - if (!is_power_of_2(adr_next->mask)) - return -EINVAL; + if (link_codecs) { + *codec_num += link_codecs; - if (index >= SDW_MAX_CPU_DAIS) { - dev_err(dev, " cpu_dai_id array overflows"); - return -EINVAL; - } + if (*cpu_dai_num >= SDW_MAX_CPU_DAIS) { + dev_err(dev, "cpu_dai_id array overflowed\n"); + return -EINVAL; + } - cpu_dai_id[index++] = ffs(adr_next->mask) - 1; - for (i = 0; i < adr_next->num_adr; i++) { - if (adr_next->adr_d[i].endpoints->aggregated && - adr_next->adr_d[i].endpoints->group_id == *group_id) - (*codec_num)++; + cpu_dai_id[(*cpu_dai_num)++] = ffs(adr_link->mask) - 1; } } - /* - * indicate CPU DAIs for this group have been generated - * to avoid generating CPU DAIs for this group again. - */ - group_generated[*group_id] = true; - *cpu_dai_num = index; - return 0; } @@ -1344,37 +1318,36 @@ static void set_dailink_map(struct snd_soc_dai_link_codec_ch_map *sdw_codec_ch_m static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"}; -static int create_sdw_dailink(struct snd_soc_card *card, - struct device *dev, int *link_index, +static int create_sdw_dailink(struct snd_soc_card *card, int *link_index, struct snd_soc_dai_link *dai_links, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, - const struct snd_soc_acpi_link_adr *link, - int *cpu_id, bool *group_generated, - struct snd_soc_codec_conf *codec_conf, - int codec_count, int *link_id, + const struct snd_soc_acpi_link_adr *adr_link, + int *cpu_id, struct snd_soc_codec_conf *codec_conf, + int codec_count, int *be_id, int *codec_conf_index, bool *ignore_pch_dmic, bool append_dai_type, int adr_index, int dai_index) { - const struct snd_soc_acpi_link_adr *link_next; + struct device *dev = card->dev; + const struct snd_soc_acpi_link_adr *adr_link_next; struct snd_soc_dai_link_component *codecs; struct sof_sdw_codec_info *codec_info; int cpu_dai_id[SDW_MAX_CPU_DAIS]; int cpu_dai_num, cpu_dai_index; unsigned int group_id; - int codec_idx = 0; + int codec_dlc_index = 0; int codec_index; int codec_num; int stream; int i = 0; + int j, k; int ret; - int k; - ret = get_slave_info(link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, - &group_id, group_generated, adr_index); + ret = get_slave_info(adr_link, dev, cpu_dai_id, &cpu_dai_num, &codec_num, + &group_id, adr_index); if (ret) return ret; @@ -1383,32 +1356,50 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -ENOMEM; /* generate codec name on different links in the same group */ - for (link_next = link; link_next && link_next->num_adr && - i < cpu_dai_num; link_next++) { - const struct snd_soc_acpi_endpoint *endpoints; - - endpoints = link_next->adr_d->endpoints; - if (group_id && (!endpoints->aggregated || - endpoints->group_id != group_id)) - continue; - + j = adr_index; + for (adr_link_next = adr_link; adr_link_next && adr_link_next->num_adr && + i < cpu_dai_num; adr_link_next++) { /* skip the link excluded by this processed group */ - if (cpu_dai_id[i] != ffs(link_next->mask) - 1) + if (cpu_dai_id[i] != ffs(adr_link_next->mask) - 1) continue; - ret = create_codec_dai_name(dev, link_next, codecs, codec_idx, - codec_conf, codec_count, codec_conf_index, - adr_index, dai_index); - if (ret < 0) - return ret; + /* j reset after loop, adr_index only applies to first link */ + for (; j < adr_link_next->num_adr; j++) { + const struct snd_soc_acpi_endpoint *endpoints; + + endpoints = adr_link_next->adr_d[j].endpoints; + + if (group_id && (!endpoints->aggregated || + endpoints->group_id != group_id)) + continue; + + /* sanity check */ + if (*codec_conf_index >= codec_count) { + dev_err(dev, "codec_conf array overflowed\n"); + return -EINVAL; + } + + ret = fill_sdw_codec_dlc(dev, adr_link_next, + &codecs[codec_dlc_index], + j, dai_index); + if (ret) + return ret; + + codec_conf[*codec_conf_index].dlc = codecs[codec_dlc_index]; + codec_conf[*codec_conf_index].name_prefix = + adr_link_next->adr_d[j].name_prefix; + + codec_dlc_index++; + (*codec_conf_index)++; + } + j = 0; /* check next link to create codec dai in the processed group */ i++; - codec_idx += link_next->num_adr; } /* find codec info to create BE DAI */ - codec_index = find_codec_info_part(link->adr_d[adr_index].adr); + codec_index = find_codec_info_part(adr_link->adr_d[adr_index].adr); if (codec_index < 0) return codec_index; codec_info = &codec_info_list[codec_index]; @@ -1431,9 +1422,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, if (!codec_info->dais[dai_index].direction[stream]) continue; - *link_id = codec_info->dais[dai_index].dailink[stream]; - if (*link_id < 0) { - dev_err(dev, "Invalid dailink id %d\n", *link_id); + *be_id = codec_info->dais[dai_index].dailink[stream]; + if (*be_id < 0) { + dev_err(dev, "Invalid dailink id %d\n", *be_id); return -EINVAL; } @@ -1466,7 +1457,7 @@ static int create_sdw_dailink(struct snd_soc_card *card, return -ENOMEM; if (cpu_dai_index >= sdw_cpu_dai_num) { - dev_err(dev, "invalid cpu dai index %d", + dev_err(dev, "invalid cpu dai index %d\n", cpu_dai_index); return -EINVAL; } @@ -1479,18 +1470,18 @@ static int create_sdw_dailink(struct snd_soc_card *card, * not be larger than sdw_be_num */ if (*link_index >= sdw_be_num) { - dev_err(dev, "invalid dai link index %d", *link_index); + dev_err(dev, "invalid dai link index %d\n", *link_index); return -EINVAL; } if (*cpu_id >= sdw_cpu_dai_num) { - dev_err(dev, " invalid cpu dai index %d", *cpu_id); + dev_err(dev, "invalid cpu dai index %d\n", *cpu_id); return -EINVAL; } playback = (stream == SNDRV_PCM_STREAM_PLAYBACK); capture = (stream == SNDRV_PCM_STREAM_CAPTURE); - init_dai_link(dev, dai_links + *link_index, (*link_id)++, name, + init_dai_link(dev, dai_links + *link_index, (*be_id)++, name, playback, capture, cpus + *cpu_id, cpu_dai_num, codecs, codec_num, @@ -1504,10 +1495,10 @@ static int create_sdw_dailink(struct snd_soc_card *card, set_dailink_map(sdw_codec_ch_maps, codec_num, cpu_dai_num); dai_links[*link_index].codec_ch_maps = sdw_codec_ch_maps; - ret = set_codec_init_func(card, link, dai_links + (*link_index)++, + ret = set_codec_init_func(card, adr_link, dai_links + (*link_index)++, playback, group_id, adr_index, dai_index); if (ret < 0) { - dev_err(dev, "failed to init codec %d", codec_index); + dev_err(dev, "failed to init codec %d\n", codec_index); return ret; } @@ -1519,65 +1510,26 @@ static int create_sdw_dailink(struct snd_soc_card *card, #define IDISP_CODEC_MASK 0x4 -static int sof_card_codec_conf_alloc(struct device *dev, - struct snd_soc_acpi_mach_params *mach_params, - struct snd_soc_codec_conf **codec_conf, - int *codec_conf_count) +static int sof_card_dai_links_create(struct snd_soc_card *card) { - const struct snd_soc_acpi_link_adr *adr_link; - struct snd_soc_codec_conf *c_conf; - int num_codecs = 0; - int codec_index; - int i; - - adr_link = mach_params->links; - if (!adr_link) - return -EINVAL; - - /* generate DAI links by each sdw link */ - for (; adr_link->num_adr; adr_link++) { - for (i = 0; i < adr_link->num_adr; i++) { - if (!adr_link->adr_d[i].name_prefix) { - dev_err(dev, "codec 0x%llx does not have a name prefix\n", - adr_link->adr_d[i].adr); - return -EINVAL; - } - codec_index = find_codec_info_part(adr_link->adr_d[i].adr); - if (codec_index < 0) - return codec_index; - num_codecs += codec_info_list[codec_index].dai_num; - } - } - - c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL); - if (!c_conf) - return -ENOMEM; - - *codec_conf = c_conf; - *codec_conf_count = num_codecs; - - return 0; -} - -static int sof_card_dai_links_create(struct device *dev, - struct snd_soc_acpi_mach *mach, - struct snd_soc_card *card) -{ - int ssp_num, sdw_be_num = 0, hdmi_num = 0, dmic_num; + struct device *dev = card->dev; + struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev); + int sdw_be_num = 0, ssp_num = 0, dmic_num = 0, hdmi_num = 0, bt_num = 0; struct mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_dai_link_component *idisp_components; struct snd_soc_dai_link_component *ssp_components; - struct snd_soc_acpi_mach_params *mach_params; - const struct snd_soc_acpi_link_adr *adr_link; + struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; + const struct snd_soc_acpi_link_adr *adr_link = mach_params->links; + bool aggregation = !(sof_sdw_quirk & SOF_SDW_NO_AGGREGATION); struct snd_soc_dai_link_component *cpus; struct snd_soc_codec_conf *codec_conf; bool append_dai_type = false; bool ignore_pch_dmic = false; - int codec_conf_count; + int codec_conf_num = 0; int codec_conf_index = 0; - bool group_generated[SDW_MAX_GROUPS]; + bool group_generated[SDW_MAX_GROUPS] = { }; int ssp_codec_index, ssp_mask; - struct snd_soc_dai_link *links; + struct snd_soc_dai_link *dai_links; int num_links, link_index = 0; char *name, *cpu_name; int total_cpu_dai_num; @@ -1585,30 +1537,15 @@ static int sof_card_dai_links_create(struct device *dev, int i, j, be_id = 0; int codec_index; int cpu_id = 0; - int comp_num; int ret; - mach_params = &mach->mach_params; - - /* allocate codec conf, will be populated when dailinks are created */ - ret = sof_card_codec_conf_alloc(dev, mach_params, &codec_conf, &codec_conf_count); - if (ret < 0) + ret = get_dailink_info(dev, adr_link, &sdw_be_num, &sdw_cpu_dai_num, + &codec_conf_num); + if (ret < 0) { + dev_err(dev, "failed to get sdw link info %d\n", ret); return ret; - - /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ - for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) - codec_info_list[i].amp_num = 0; - - if (mach_params->codec_mask & IDISP_CODEC_MASK) { - ctx->idisp_codec = true; - - if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) - hdmi_num = SOF_TGL_HDMI_COUNT; - else - hdmi_num = SOF_PRE_TGL_HDMI_COUNT; } - ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); /* * on generic tgl platform, I2S or sdw mode is supported * based on board rework. A ACPI device is registered in @@ -1616,54 +1553,52 @@ static int sof_card_dai_links_create(struct device *dev, * Here check ACPI ID to confirm I2S is supported. */ ssp_codec_index = find_codec_info_acpi(mach->id); - ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; - comp_num = hdmi_num + ssp_num; + if (ssp_codec_index >= 0) { + ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); + ssp_num = hweight_long(ssp_mask); + } - ret = get_sdw_dailink_info(dev, mach_params->links, - &sdw_be_num, &sdw_cpu_dai_num); - if (ret < 0) { - dev_err(dev, "failed to get sdw link info %d", ret); - return ret; + if (mach_params->codec_mask & IDISP_CODEC_MASK) { + ctx->idisp_codec = true; + + if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) + hdmi_num = SOF_TGL_HDMI_COUNT; + else + hdmi_num = SOF_PRE_TGL_HDMI_COUNT; } /* enable dmic01 & dmic16k */ - dmic_num = (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) ? 2 : 0; - comp_num += dmic_num; + if (sof_sdw_quirk & SOF_SDW_PCH_DMIC || mach_params->dmic_num) + dmic_num = 2; if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT) - comp_num++; + bt_num = 1; - dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d", sdw_be_num, ssp_num, - dmic_num, ctx->idisp_codec ? hdmi_num : 0); + dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n", + sdw_be_num, ssp_num, dmic_num, hdmi_num, bt_num); /* allocate BE dailinks */ - num_links = comp_num + sdw_be_num; - links = devm_kcalloc(dev, num_links, sizeof(*links), GFP_KERNEL); + num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; + dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); + if (!dai_links) + return -ENOMEM; /* allocated CPU DAIs */ - total_cpu_dai_num = comp_num + sdw_cpu_dai_num; - cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), - GFP_KERNEL); + total_cpu_dai_num = sdw_cpu_dai_num + ssp_num + dmic_num + hdmi_num + bt_num; + cpus = devm_kcalloc(dev, total_cpu_dai_num, sizeof(*cpus), GFP_KERNEL); + if (!cpus) + return -ENOMEM; - if (!links || !cpus) + /* allocate codec conf, will be populated when dailinks are created */ + codec_conf = devm_kcalloc(dev, codec_conf_num, sizeof(*codec_conf), + GFP_KERNEL); + if (!codec_conf) return -ENOMEM; /* SDW */ if (!sdw_be_num) goto SSP; - adr_link = mach_params->links; - if (!adr_link) - return -EINVAL; - - /* - * SoundWire Slaves aggregated in the same group may be - * located on different hardware links. Clear array to indicate - * CPU DAIs for this group have not been generated. - */ - for (i = 0; i < SDW_MAX_GROUPS; i++) - group_generated[i] = false; - for (i = 0; i < SDW_MAX_LINKS; i++) sdw_pin_index[i] = SDW_INTEL_BIDIR_PDI_BASE; @@ -1702,11 +1637,6 @@ out: const struct snd_soc_acpi_endpoint *endpoint; endpoint = adr_link->adr_d[i].endpoints; - if (endpoint->aggregated && !endpoint->group_id) { - dev_err(dev, "invalid group id on link %x", - adr_link->mask); - continue; - } /* this group has been generated */ if (endpoint->aggregated && @@ -1719,17 +1649,20 @@ out: return codec_index; for (j = 0; j < codec_info_list[codec_index].dai_num ; j++) { - ret = create_sdw_dailink(card, dev, &link_index, links, sdw_be_num, - sdw_cpu_dai_num, cpus, adr_link, - &cpu_id, group_generated, - codec_conf, codec_conf_count, + ret = create_sdw_dailink(card, &link_index, dai_links, + sdw_be_num, sdw_cpu_dai_num, cpus, + adr_link, &cpu_id, + codec_conf, codec_conf_num, &be_id, &codec_conf_index, &ignore_pch_dmic, append_dai_type, i, j); if (ret < 0) { - dev_err(dev, "failed to create dai link %d", link_index); + dev_err(dev, "failed to create dai link %d\n", link_index); return ret; } } + + if (aggregation && endpoint->aggregated) + group_generated[endpoint->group_id] = true; } } @@ -1773,13 +1706,13 @@ SSP: playback = info->dais[0].direction[SNDRV_PCM_STREAM_PLAYBACK]; capture = info->dais[0].direction[SNDRV_PCM_STREAM_CAPTURE]; - init_dai_link(dev, links + link_index, be_id, name, + init_dai_link(dev, dai_links + link_index, be_id, name, playback, capture, cpus + cpu_id, 1, ssp_components, 1, NULL, info->ops); - ret = info->dais[0].init(card, NULL, links + link_index, info, 0); + ret = info->dais[0].init(card, NULL, dai_links + link_index, info, 0); if (ret < 0) return ret; @@ -1794,7 +1727,7 @@ DMIC: goto HDMI; } cpus[cpu_id].dai_name = "DMIC01 Pin"; - init_dai_link(dev, links + link_index, be_id, "dmic01", + init_dai_link(dev, dai_links + link_index, be_id, "dmic01", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, @@ -1802,7 +1735,7 @@ DMIC: INC_ID(be_id, cpu_id, link_index); cpus[cpu_id].dai_name = "DMIC16k Pin"; - init_dai_link(dev, links + link_index, be_id, "dmic16k", + init_dai_link(dev, dai_links + link_index, be_id, "dmic16k", 0, 1, // DMIC only supports capture cpus + cpu_id, 1, dmic_component, 1, @@ -1845,7 +1778,7 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_index, be_id, name, + init_dai_link(dev, dai_links + link_index, be_id, name, 1, 0, // HDMI only supports playback cpus + cpu_id, 1, idisp_components + i, 1, @@ -1866,15 +1799,15 @@ HDMI: return -ENOMEM; cpus[cpu_id].dai_name = cpu_name; - init_dai_link(dev, links + link_index, be_id, name, 1, 1, - cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); + init_dai_link(dev, dai_links + link_index, be_id, name, 1, 1, + cpus + cpu_id, 1, &asoc_dummy_dlc, 1, NULL, NULL); } - card->dai_link = links; + card->dai_link = dai_links; card->num_links = num_links; card->codec_conf = codec_conf; - card->num_configs = codec_conf_count; + card->num_configs = codec_conf_num; return 0; } @@ -1913,15 +1846,15 @@ static struct snd_soc_card card_sof_sdw = { static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card, const char *dai_name) { - struct snd_soc_dai_link *link; + struct snd_soc_dai_link *dai_link; int i; int j; - for_each_card_prelinks(card, i, link) { - for (j = 0; j < link->num_codecs; j++) { + for_each_card_prelinks(card, i, dai_link) { + for (j = 0; j < dai_link->num_codecs; j++) { /* Check each codec in a link */ - if (!strcmp(link->codecs[j].dai_name, dai_name)) - return link; + if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) + return dai_link; } } return NULL; @@ -1929,7 +1862,7 @@ static struct snd_soc_dai_link *mc_find_codec_dai_used(struct snd_soc_card *card static void mc_dailink_exit_loop(struct snd_soc_card *card) { - struct snd_soc_dai_link *link; + struct snd_soc_dai_link *dai_link; int ret; int i, j; @@ -1942,10 +1875,11 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) * We don't need to call .exit function if there is no matched * dai link found. */ - link = mc_find_codec_dai_used(card, codec_info_list[i].dais[j].dai_name); - if (link) { + dai_link = mc_find_codec_dai_used(card, + codec_info_list[i].dais[j].dai_name); + if (dai_link) { /* Do the .exit function if the codec dai is used in the link */ - ret = codec_info_list[i].dais[j].exit(card, link); + ret = codec_info_list[i].dais[j].exit(card, dai_link); if (ret) dev_warn(card->dev, "codec exit failed %d\n", @@ -1959,34 +1893,38 @@ static void mc_dailink_exit_loop(struct snd_soc_card *card) static int mc_probe(struct platform_device *pdev) { struct snd_soc_card *card = &card_sof_sdw; - struct snd_soc_acpi_mach *mach; + struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev); struct mc_private *ctx; int amp_num = 0, i; int ret; - dev_dbg(&pdev->dev, "Entry\n"); + card->dev = &pdev->dev; + + dev_dbg(card->dev, "Entry\n"); - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(card->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + snd_soc_card_set_drvdata(card, ctx); + dmi_check_system(sof_sdw_quirk_table); if (quirk_override != -1) { - dev_info(&pdev->dev, "Overriding quirk 0x%lx => 0x%x\n", + dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n", sof_sdw_quirk, quirk_override); sof_sdw_quirk = quirk_override; } - log_quirks(&pdev->dev); - INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + log_quirks(card->dev); - card->dev = &pdev->dev; - snd_soc_card_set_drvdata(card, ctx); + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) + codec_info_list[i].amp_num = 0; - mach = pdev->dev.platform_data; - ret = sof_card_dai_links_create(&pdev->dev, mach, - card); + ret = sof_card_dai_links_create(card); if (ret < 0) return ret; @@ -2017,7 +1955,7 @@ static int mc_probe(struct platform_device *pdev) card->long_name = sdw_card_long_name; /* Register the card */ - ret = devm_snd_soc_register_card(&pdev->dev, card); + ret = devm_snd_soc_register_card(card->dev, card); if (ret) { dev_err(card->dev, "snd_soc_register_card failed %d\n", ret); mc_dailink_exit_loop(card); @@ -2036,6 +1974,12 @@ static void mc_remove(struct platform_device *pdev) mc_dailink_exit_loop(card); } +static const struct platform_device_id mc_id_table[] = { + { "sof_sdw", }, + {} +}; +MODULE_DEVICE_TABLE(platform, mc_id_table); + static struct platform_driver sof_sdw_driver = { .driver = { .name = "sof_sdw", @@ -2043,6 +1987,7 @@ static struct platform_driver sof_sdw_driver = { }, .probe = mc_probe, .remove_new = mc_remove, + .id_table = mc_id_table, }; module_platform_driver(sof_sdw_driver); @@ -2052,6 +1997,5 @@ MODULE_AUTHOR("Bard Liao <yung-chuan.liao@linux.intel.com>"); MODULE_AUTHOR("Rander Wang <rander.wang@linux.intel.com>"); MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sof_sdw"); MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_MAXIM_COMMON); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 37402170d5f9..2f4fe6bc3d5d 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -80,6 +80,7 @@ struct sof_sdw_dai_info { struct sof_sdw_codec_info { const int part_id; const int version_id; + const char *codec_name; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool ignore_pch_dmic; @@ -205,4 +206,10 @@ int sof_sdw_cs42l42_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); +/* CS AMP support */ +int sof_sdw_cs_amp_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); #endif diff --git a/sound/soc/intel/boards/sof_sdw_cs_amp.c b/sound/soc/intel/boards/sof_sdw_cs_amp.c new file mode 100644 index 000000000000..98f6546f484b --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_cs_amp.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2023 Intel Corporation + +/* + * sof_sdw_cs_amp - Helpers to handle CS35L56 from generic machine driver + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <sound/soc.h> +#include <sound/soc-acpi.h> +#include "sof_sdw_common.h" + +#define CODEC_NAME_SIZE 8 + +static int cs_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + const char *dai_name = rtd->dai_link->codecs->dai_name; + struct snd_soc_card *card = rtd->card; + char codec_name[CODEC_NAME_SIZE]; + + snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s spk:%s", + card->components, codec_name); + if (!card->components) + return -ENOMEM; + + return 0; +} + + +int sof_sdw_cs_amp_init(struct snd_soc_card *card, + const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* Count amp number and do init on playback link only. */ + if (!playback) + return 0; + + info->amp_num++; + dai_links->init = cs_spk_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c index 0aef718e82b2..5aa16fd3939b 100644 --- a/sound/soc/intel/boards/sof_ssp_amp.c +++ b/sound/soc/intel/boards/sof_ssp_amp.c @@ -472,6 +472,15 @@ static const struct platform_device_id board_ids[] = { SOF_NO_OF_HDMI_PLAYBACK(3) | SOF_HDMI_PLAYBACK_PRESENT), }, + { + .name = "rpl_lt6911_hdmi_ssp", + .driver_data = (kernel_ulong_t)(SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_NO_OF_HDMI_PLAYBACK(3) | + SOF_HDMI_PLAYBACK_PRESENT), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 30ca5416c9a3..f1a5cb825ff1 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -684,6 +684,10 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, return 0; } +static const struct snd_soc_dai_ops catpt_dai_ops = { + .pcm_new = catpt_dai_pcm_new, +}; + static struct snd_soc_dai_driver dai_drivers[] = { /* FE DAIs */ { @@ -764,7 +768,6 @@ static struct snd_soc_dai_driver dai_drivers[] = { { .name = "ssp0-port", .id = CATPT_SSP_IFACE_0, - .pcm_new = catpt_dai_pcm_new, .playback = { .channels_min = 1, .channels_max = 8, @@ -773,11 +776,11 @@ static struct snd_soc_dai_driver dai_drivers[] = { .channels_min = 1, .channels_max = 8, }, + .ops = &catpt_dai_ops, }, { .name = "ssp1-port", .id = CATPT_SSP_IFACE_1, - .pcm_new = catpt_dai_pcm_new, .playback = { .channels_min = 1, .channels_max = 8, @@ -786,6 +789,7 @@ static struct snd_soc_dai_driver dai_drivers[] = { .channels_min = 1, .channels_max = 8, }, + .ops = &catpt_dai_ops, }, }; diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index bcd66e0094b4..8e995edf4c10 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -570,6 +570,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = { }, { .comp_ids = &adl_rt5682_rt5682s_hp, + .drv_name = "adl_rt5682_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &adl_lt6911_hdmi, + .sof_tplg_filename = "sof-adl-rt5682-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &adl_rt5682_rt5682s_hp, .drv_name = "adl_rt5682", .sof_tplg_filename = "sof-adl-rt5682.tplg", }, diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index ed9821adc1d9..0304246d2922 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -161,6 +161,33 @@ static const struct snd_soc_acpi_adr_device rt1316_3_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = { + { + .adr = 0x000130025D131801ull, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + .name_prefix = "rt1318-1" + } +}; + +static const struct snd_soc_acpi_adr_device rt1318_2_group1_adr[] = { + { + .adr = 0x000232025D131801ull, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + .name_prefix = "rt1318-2" + } +}; + +static const struct snd_soc_acpi_adr_device rt714_0_adr[] = { + { + .adr = 0x000030025D071401ull, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt714" + } +}; + static const struct snd_soc_acpi_adr_device rt714_1_adr[] = { { .adr = 0x000130025D071401ull, @@ -232,6 +259,25 @@ static const struct snd_soc_acpi_link_adr mtl_3_in_1_sdca[] = { {} }; +static const struct snd_soc_acpi_link_adr mtl_sdw_rt1318_l12_rt714_l0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1318_1_group1_adr), + .adr_d = rt1318_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1318_2_group1_adr), + .adr_d = rt1318_2_group1_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt714_0_adr), + .adr_d = rt714_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_adr_device mx8363_2_adr[] = { { .adr = 0x000230019F836300ull, @@ -299,6 +345,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = { .sof_tplg_filename = "sof-mtl-rt712-l0-rt1712-l3.tplg", }, { + .link_mask = GENMASK(2, 0), + .links = mtl_sdw_rt1318_l12_rt714_l0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-mtl-rt1318-l12-rt714-l0.tplg" + }, + { .link_mask = GENMASK(3, 0), .links = mtl_3_in_1_sdca, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index 302a08018572..122673c1dae2 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -308,6 +308,15 @@ static const struct snd_soc_acpi_link_adr rpl_sdw_rt1316_link12_rt714_link0[] = {} }; +static const struct snd_soc_acpi_link_adr rpl_sdca_rvp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr rplp_crb[] = { { .mask = BIT(2), @@ -322,6 +331,16 @@ static const struct snd_soc_acpi_codecs rpl_rt5682_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs rpl_essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + +static const struct snd_soc_acpi_codecs rpl_max98357a_amp = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + static const struct snd_soc_acpi_codecs rpl_max98360a_amp = { .num_codecs = 1, .codecs = {"MX98360A"}, @@ -332,6 +351,16 @@ static const struct snd_soc_acpi_codecs rpl_max98373_amp = { .codecs = {"MX98373"} }; +static const struct snd_soc_acpi_codecs rpl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + +static const struct snd_soc_acpi_codecs rpl_nau8318_amp = { + .num_codecs = 1, + .codecs = {"NVTN2012"} +}; + static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = { .num_codecs = 1, .codecs = {"RTL1019"} @@ -340,6 +369,13 @@ static const struct snd_soc_acpi_codecs rpl_rt1019p_amp = { struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { { .comp_ids = &rpl_rt5682_hp, + .drv_name = "rpl_mx98357_rt5682", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_max98357a_amp, + .sof_tplg_filename = "sof-rpl-max98357a-rt5682.tplg", + }, + { + .comp_ids = &rpl_rt5682_hp, .drv_name = "rpl_mx98360_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_max98360a_amp, @@ -353,12 +389,39 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_machines[] = { .sof_tplg_filename = "sof-rpl-max98373-nau8825.tplg", }, { + .id = "10508825", + .drv_name = "rpl_nau8318_8825", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_nau8318_amp, + .sof_tplg_filename = "sof-rpl-nau8318-nau8825.tplg", + }, + { .comp_ids = &rpl_rt5682_hp, .drv_name = "rpl_rt1019_rt5682", .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &rpl_rt1019p_amp, .sof_tplg_filename = "sof-rpl-rt1019-rt5682.tplg", }, + { + .comp_ids = &rpl_essx_83x6, + .drv_name = "rpl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &rpl_lt6911_hdmi, + .sof_tplg_filename = "sof-rpl-es83x6-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &rpl_essx_83x6, + .drv_name = "sof-essx8336", + .sof_tplg_filename = "sof-rpl-es83x6", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, + { + .id = "INTC10B0", + .drv_name = "rpl_lt6911_hdmi_ssp", + .sof_tplg_filename = "sof-rpl-nocodec-hdmi-ssp02.tplg" + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_rpl_machines); @@ -414,6 +477,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_rpl_sdw_machines[] = { .sof_tplg_filename = "sof-rpl-rt711-l0.tplg", }, { + .link_mask = 0x1, /* link0 required */ + .links = rpl_sdca_rvp, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-rpl-rt711-l0.tplg", + }, + { .link_mask = 0x4, /* link2 required */ .links = rplp_crb, .drv_name = "sof_sdw", diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index b4893365d01d..6b06b7b5ede8 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -733,6 +733,7 @@ static int kmb_dai_hw_free(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops kmb_dai_ops = { + .probe = kmb_probe, .startup = kmb_dai_startup, .trigger = kmb_dai_trigger, .hw_params = kmb_dai_hw_params, @@ -755,7 +756,6 @@ static struct snd_soc_dai_driver intel_kmb_hdmi_dai[] = { SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; @@ -787,7 +787,6 @@ static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; @@ -807,7 +806,6 @@ static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { SNDRV_PCM_FMTBIT_S16_LE), }, .ops = &kmb_dai_ops, - .probe = kmb_probe, }, }; diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 578af21769c9..517619531615 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -328,6 +328,7 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { + .probe = jz4740_i2s_dai_probe, .startup = jz4740_i2s_startup, .shutdown = jz4740_i2s_shutdown, .trigger = jz4740_i2s_trigger, @@ -341,7 +342,6 @@ static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver jz4740_i2s_dai = { - .probe = jz4740_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -384,7 +384,6 @@ static const struct i2s_soc_info x1000_i2s_soc_info = { }; static struct snd_soc_dai_driver jz4770_i2s_dai = { - .probe = jz4740_i2s_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, diff --git a/sound/soc/loongson/loongson_card.c b/sound/soc/loongson/loongson_card.c index 9ded16329747..406ee8db1a3c 100644 --- a/sound/soc/loongson/loongson_card.c +++ b/sound/soc/loongson/loongson_card.c @@ -208,7 +208,7 @@ static struct platform_driver loongson_audio_driver = { .driver = { .name = "loongson-asoc-card", .pm = &snd_soc_pm_ops, - .of_match_table = of_match_ptr(loongson_asoc_dt_ids), + .of_match_table = loongson_asoc_dt_ids, }, }; module_platform_driver(loongson_audio_driver); diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c index b919f0fe8361..d45228a3a558 100644 --- a/sound/soc/loongson/loongson_i2s.c +++ b/sound/soc/loongson/loongson_i2s.c @@ -204,13 +204,6 @@ static int loongson_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static const struct snd_soc_dai_ops loongson_i2s_dai_ops = { - .trigger = loongson_i2s_trigger, - .hw_params = loongson_i2s_hw_params, - .set_sysclk = loongson_i2s_set_dai_sysclk, - .set_fmt = loongson_i2s_set_fmt, -}; - static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai) { struct loongson_i2s *i2s = dev_get_drvdata(cpu_dai->dev); @@ -222,9 +215,16 @@ static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } +static const struct snd_soc_dai_ops loongson_i2s_dai_ops = { + .probe = loongson_i2s_dai_probe, + .trigger = loongson_i2s_trigger, + .hw_params = loongson_i2s_hw_params, + .set_sysclk = loongson_i2s_set_dai_sysclk, + .set_fmt = loongson_i2s_set_fmt, +}; + struct snd_soc_dai_driver loongson_i2s_dai = { .name = "loongson-i2s", - .probe = loongson_i2s_dai_probe, .playback = { .stream_name = "CPU-Playback", .channels_min = 1, diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index 90db67e0ce4f..8d1bc8814486 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -54,6 +54,26 @@ config SND_SOC_MT6797_MT6351 Select Y if you have such device. If unsure select "N". +config SND_SOC_MT7986 + tristate "ASoC support for Mediatek MT7986 chip" + depends on ARCH_MEDIATEK + select SND_SOC_MEDIATEK + help + This adds ASoC platform driver support for MediaTek MT7986 chip + that can be used with other codecs. + Select Y if you have such device. + If unsure select "N". + +config SND_SOC_MT7986_WM8960 + tristate "ASoc Audio driver for MT7986 with WM8960 codec" + depends on SND_SOC_MT7986 && I2C + select SND_SOC_WM8960 + help + This adds support for ASoC machine driver for MediaTek MT7986 + boards with the WM8960 codecs. + Select Y if you have such device. + If unsure select "N". + config SND_SOC_MT8173 tristate "ASoC support for Mediatek MT8173 chip" depends on ARCH_MEDIATEK diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile index 3de38cfc69e5..3938e7f75c2e 100644 --- a/sound/soc/mediatek/Makefile +++ b/sound/soc/mediatek/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += common/ obj-$(CONFIG_SND_SOC_MT2701) += mt2701/ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/ +obj-$(CONFIG_SND_SOC_MT7986) += mt7986/ obj-$(CONFIG_SND_SOC_MT8173) += mt8173/ obj-$(CONFIG_SND_SOC_MT8183) += mt8183/ obj-$(CONFIG_SND_SOC_MT8186) += mt8186/ diff --git a/sound/soc/mediatek/mt7986/Makefile b/sound/soc/mediatek/mt7986/Makefile new file mode 100644 index 000000000000..fc4c82559b29 --- /dev/null +++ b/sound/soc/mediatek/mt7986/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +# platform driver +snd-soc-mt7986-afe-objs := \ + mt7986-afe-pcm.o \ + mt7986-dai-etdm.o + +obj-$(CONFIG_SND_SOC_MT7986) += snd-soc-mt7986-afe.o +obj-$(CONFIG_SND_SOC_MT7986_WM8960) += mt7986-wm8960.o diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-common.h b/sound/soc/mediatek/mt7986/mt7986-afe-common.h new file mode 100644 index 000000000000..fc3bb31e5167 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-afe-common.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt7986-afe-common.h -- MediaTek 7986 audio driver definitions + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#ifndef _MT_7986_AFE_COMMON_H_ +#define _MT_7986_AFE_COMMON_H_ + +#include <sound/soc.h> +#include <linux/clk.h> +#include <linux/list.h> +#include <linux/regmap.h> +#include "../common/mtk-base-afe.h" + +enum { + MT7986_MEMIF_DL1, + MT7986_MEMIF_VUL12, + MT7986_MEMIF_NUM, + MT7986_DAI_ETDM = MT7986_MEMIF_NUM, + MT7986_DAI_NUM, +}; + +enum { + MT7986_IRQ_0, + MT7986_IRQ_1, + MT7986_IRQ_2, + MT7986_IRQ_NUM, +}; + +struct mt7986_afe_private { + struct clk_bulk_data *clks; + int num_clks; + + int pm_runtime_bypass_reg_ctl; + + /* dai */ + void *dai_priv[MT7986_DAI_NUM]; +}; + +unsigned int mt7986_afe_rate_transform(struct device *dev, + unsigned int rate); + +/* dai register */ +int mt7986_dai_etdm_register(struct mtk_base_afe *afe); +#endif diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c new file mode 100644 index 000000000000..d497e1129889 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC AFE platform driver for MT7986 + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> + +#include "mt7986-afe-common.h" +#include "mt7986-reg.h" +#include "../common/mtk-afe-platform-driver.h" +#include "../common/mtk-afe-fe-dai.h" + +enum { + MTK_AFE_RATE_8K = 0, + MTK_AFE_RATE_11K = 1, + MTK_AFE_RATE_12K = 2, + MTK_AFE_RATE_16K = 4, + MTK_AFE_RATE_22K = 5, + MTK_AFE_RATE_24K = 6, + MTK_AFE_RATE_32K = 8, + MTK_AFE_RATE_44K = 9, + MTK_AFE_RATE_48K = 10, + MTK_AFE_RATE_88K = 13, + MTK_AFE_RATE_96K = 14, + MTK_AFE_RATE_176K = 17, + MTK_AFE_RATE_192K = 18, +}; + +enum { + CLK_INFRA_AUD_BUS_CK = 0, + CLK_INFRA_AUD_26M_CK, + CLK_INFRA_AUD_L_CK, + CLK_INFRA_AUD_AUD_CK, + CLK_INFRA_AUD_EG2_CK, + CLK_NUM +}; + +static const char *aud_clks[CLK_NUM] = { + [CLK_INFRA_AUD_BUS_CK] = "aud_bus_ck", + [CLK_INFRA_AUD_26M_CK] = "aud_26m_ck", + [CLK_INFRA_AUD_L_CK] = "aud_l_ck", + [CLK_INFRA_AUD_AUD_CK] = "aud_aud_ck", + [CLK_INFRA_AUD_EG2_CK] = "aud_eg2_ck", +}; + +unsigned int mt7986_afe_rate_transform(struct device *dev, unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_AFE_RATE_8K; + case 11025: + return MTK_AFE_RATE_11K; + case 12000: + return MTK_AFE_RATE_12K; + case 16000: + return MTK_AFE_RATE_16K; + case 22050: + return MTK_AFE_RATE_22K; + case 24000: + return MTK_AFE_RATE_24K; + case 32000: + return MTK_AFE_RATE_32K; + case 44100: + return MTK_AFE_RATE_44K; + case 48000: + return MTK_AFE_RATE_48K; + case 88200: + return MTK_AFE_RATE_88K; + case 96000: + return MTK_AFE_RATE_96K; + case 176400: + return MTK_AFE_RATE_176K; + case 192000: + return MTK_AFE_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", + __func__, rate, MTK_AFE_RATE_48K); + return MTK_AFE_RATE_48K; + } +} + +static const struct snd_pcm_hardware mt7986_afe_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 256, + .period_bytes_max = 4 * 48 * 1024, + .periods_min = 2, + .periods_max = 256, + .buffer_bytes_max = 8 * 48 * 1024, + .fifo_size = 0, +}; + +static int mt7986_memif_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt7986_afe_rate_transform(afe->dev, rate); +} + +static int mt7986_irq_fs(struct snd_pcm_substream *substream, + unsigned int rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME); + struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component); + + return mt7986_afe_rate_transform(afe->dev, rate); +} + +#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mt7986_memif_dai_driver[] = { + /* FE DAIs: memory intefaces to CPU */ + { + .name = "DL1", + .id = MT7986_MEMIF_DL1, + .playback = { + .stream_name = "DL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, + { + .name = "UL1", + .id = MT7986_MEMIF_VUL12, + .capture = { + .stream_name = "UL1", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_PCM_RATES, + .formats = MTK_PCM_FORMATS, + }, + .ops = &mtk_afe_fe_ops, + }, +}; + +static const struct snd_kcontrol_new o018_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I150_Switch", AFE_CONN018_4, 22, 1, 0), +}; + +static const struct snd_kcontrol_new o019_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I151_Switch", AFE_CONN019_4, 23, 1, 0), +}; + +static const struct snd_soc_dapm_widget mt7986_memif_widgets[] = { + /* DL */ + SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL */ + SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, + o018_mix, ARRAY_SIZE(o018_mix)), + SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, + o019_mix, ARRAY_SIZE(o019_mix)), +}; + +static const struct snd_soc_dapm_route mt7986_memif_routes[] = { + {"I032", NULL, "DL1"}, + {"I033", NULL, "DL1"}, + {"UL1", NULL, "O018"}, + {"UL1", NULL, "O019"}, + {"O018", "I150_Switch", "I150"}, + {"O019", "I151_Switch", "I151"}, +}; + +static const struct snd_soc_component_driver mt7986_afe_pcm_dai_component = { + .name = "mt7986-afe-pcm-dai", +}; + +static const struct mtk_base_memif_data memif_data[MT7986_MEMIF_NUM] = { + [MT7986_MEMIF_DL1] = { + .name = "DL1", + .id = MT7986_MEMIF_DL1, + .reg_ofs_base = AFE_DL0_BASE, + .reg_ofs_cur = AFE_DL0_CUR, + .reg_ofs_end = AFE_DL0_END, + .reg_ofs_base_msb = AFE_DL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_DL0_CUR_MSB, + .reg_ofs_end_msb = AFE_DL0_END_MSB, + .fs_reg = AFE_DL0_CON0, + .fs_shift = DL0_MODE_SFT, + .fs_maskbit = DL0_MODE_MASK, + .mono_reg = AFE_DL0_CON0, + .mono_shift = DL0_MONO_SFT, + .enable_reg = AFE_DL0_CON0, + .enable_shift = DL0_ON_SFT, + .hd_reg = AFE_DL0_CON0, + .hd_shift = DL0_HD_MODE_SFT, + .hd_align_reg = AFE_DL0_CON0, + .hd_align_mshift = DL0_HALIGN_SFT, + .pbuf_reg = AFE_DL0_CON0, + .pbuf_shift = DL0_PBUF_SIZE_SFT, + .minlen_reg = AFE_DL0_CON0, + .minlen_shift = DL0_MINLEN_SFT, + }, + [MT7986_MEMIF_VUL12] = { + .name = "VUL12", + .id = MT7986_MEMIF_VUL12, + .reg_ofs_base = AFE_VUL0_BASE, + .reg_ofs_cur = AFE_VUL0_CUR, + .reg_ofs_end = AFE_VUL0_END, + .reg_ofs_base_msb = AFE_VUL0_BASE_MSB, + .reg_ofs_cur_msb = AFE_VUL0_CUR_MSB, + .reg_ofs_end_msb = AFE_VUL0_END_MSB, + .fs_reg = AFE_VUL0_CON0, + .fs_shift = VUL0_MODE_SFT, + .fs_maskbit = VUL0_MODE_MASK, + .mono_reg = AFE_VUL0_CON0, + .mono_shift = VUL0_MONO_SFT, + .enable_reg = AFE_VUL0_CON0, + .enable_shift = VUL0_ON_SFT, + .hd_reg = AFE_VUL0_CON0, + .hd_shift = VUL0_HD_MODE_SFT, + .hd_align_reg = AFE_VUL0_CON0, + .hd_align_mshift = VUL0_HALIGN_SFT, + }, +}; + +static const struct mtk_base_irq_data irq_data[MT7986_IRQ_NUM] = { + [MT7986_IRQ_0] = { + .id = MT7986_IRQ_0, + .irq_cnt_reg = AFE_IRQ0_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ0_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ0_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ0_MCU_CLR_SFT, + }, + [MT7986_IRQ_1] = { + .id = MT7986_IRQ_1, + .irq_cnt_reg = AFE_IRQ1_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ1_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ1_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ1_MCU_CLR_SFT, + }, + [MT7986_IRQ_2] = { + .id = MT7986_IRQ_2, + .irq_cnt_reg = AFE_IRQ2_MCU_CFG1, + .irq_cnt_shift = AFE_IRQ_CNT_SHIFT, + .irq_cnt_maskbit = AFE_IRQ_CNT_MASK, + .irq_fs_reg = AFE_IRQ2_MCU_CFG0, + .irq_fs_shift = IRQ_MCU_MODE_SFT, + .irq_fs_maskbit = IRQ_MCU_MODE_MASK, + .irq_en_reg = AFE_IRQ2_MCU_CFG0, + .irq_en_shift = IRQ_MCU_ON_SFT, + .irq_clr_reg = AFE_IRQ_MCU_CLR, + .irq_clr_shift = IRQ2_MCU_CLR_SFT, + }, +}; + +static bool mt7986_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* + * Those auto-gen regs are read-only, so put it as volatile because + * volatile registers cannot be cached, which means that they cannot + * be set when power is off + */ + + switch (reg) { + case AFE_DL0_CUR_MSB: + case AFE_DL0_CUR: + case AFE_DL0_RCH_MON: + case AFE_DL0_LCH_MON: + case AFE_VUL0_CUR_MSB: + case AFE_VUL0_CUR: + case AFE_IRQ_MCU_STATUS: + case AFE_MEMIF_RD_MON: + case AFE_MEMIF_WR_MON: + return true; + default: + return false; + }; +} + +static const struct regmap_config mt7986_afe_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .volatile_reg = mt7986_is_volatile_reg, + .max_register = AFE_MAX_REGISTER, + .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1), +}; + +static int mt7986_init_clock(struct mtk_base_afe *afe) +{ + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret, i; + + afe_priv->clks = devm_kcalloc(afe->dev, CLK_NUM, + sizeof(*afe_priv->clks), GFP_KERNEL); + if (!afe_priv->clks) + return -ENOMEM; + afe_priv->num_clks = CLK_NUM; + + for (i = 0; i < afe_priv->num_clks; i++) + afe_priv->clks[i].id = aud_clks[i]; + + ret = devm_clk_bulk_get(afe->dev, afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to get clocks\n"); + + return 0; +} + +static irqreturn_t mt7986_afe_irq_handler(int irq_id, void *dev) +{ + struct mtk_base_afe *afe = dev; + struct mtk_base_afe_irq *irq; + u32 mcu_en, status, status_mcu; + int i, ret; + irqreturn_t irq_ret = IRQ_HANDLED; + + /* get irq that is sent to MCU */ + regmap_read(afe->regmap, AFE_IRQ_MCU_EN, &mcu_en); + + ret = regmap_read(afe->regmap, AFE_IRQ_MCU_STATUS, &status); + /* only care IRQ which is sent to MCU */ + status_mcu = status & mcu_en & AFE_IRQ_STATUS_BITS; + + if (ret || status_mcu == 0) { + dev_err(afe->dev, "%s(), irq status err, ret %d, status 0x%x, mcu_en 0x%x\n", + __func__, ret, status, mcu_en); + + irq_ret = IRQ_NONE; + goto err_irq; + } + + for (i = 0; i < MT7986_MEMIF_NUM; i++) { + struct mtk_base_afe_memif *memif = &afe->memif[i]; + + if (!memif->substream) + continue; + + if (memif->irq_usage < 0) + continue; + + irq = &afe->irqs[memif->irq_usage]; + + if (status_mcu & (1 << irq->irq_data->irq_en_shift)) + snd_pcm_period_elapsed(memif->substream); + } + +err_irq: + /* clear irq */ + regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, status_mcu); + + return irq_ret; +} + +static int mt7986_afe_runtime_suspend(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + goto skip_regmap; + + /* disable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0x3fff); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, 0); + + /* make sure all irq status are cleared, twice intended */ + regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CLR, 0xffff, 0xffff); + +skip_regmap: + clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks); + + return 0; +} + +static int mt7986_afe_runtime_resume(struct device *dev) +{ + struct mtk_base_afe *afe = dev_get_drvdata(dev); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n"); + + if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl) + return 0; + + /* enable clk*/ + regmap_update_bits(afe->regmap, AUDIO_TOP_CON4, 0x3fff, 0); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_APLL2_EN_MASK, + AUD_APLL2_EN); + regmap_update_bits(afe->regmap, AUDIO_ENGEN_CON0, AUD_26M_EN_MASK, + AUD_26M_EN); + + return 0; +} + +static int mt7986_afe_component_probe(struct snd_soc_component *component) +{ + return mtk_afe_add_sub_dai_control(component); +} + +static const struct snd_soc_component_driver mt7986_afe_component = { + .name = AFE_PCM_NAME, + .probe = mt7986_afe_component_probe, + .pointer = mtk_afe_pcm_pointer, + .pcm_construct = mtk_afe_pcm_new, +}; + +static int mt7986_dai_memif_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mt7986_memif_dai_driver; + dai->num_dai_drivers = ARRAY_SIZE(mt7986_memif_dai_driver); + + dai->dapm_widgets = mt7986_memif_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mt7986_memif_widgets); + dai->dapm_routes = mt7986_memif_routes; + dai->num_dapm_routes = ARRAY_SIZE(mt7986_memif_routes); + + return 0; +} + +typedef int (*dai_register_cb)(struct mtk_base_afe *); +static const dai_register_cb dai_register_cbs[] = { + mt7986_dai_etdm_register, + mt7986_dai_memif_register, +}; + +static int mt7986_afe_pcm_dev_probe(struct platform_device *pdev) +{ + struct mtk_base_afe *afe; + struct mt7986_afe_private *afe_priv; + struct device *dev; + int i, irq_id, ret; + + afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL); + if (!afe) + return -ENOMEM; + platform_set_drvdata(pdev, afe); + + afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv), + GFP_KERNEL); + if (!afe->platform_priv) + return -ENOMEM; + + afe_priv = afe->platform_priv; + afe->dev = &pdev->dev; + dev = afe->dev; + + afe->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(afe->base_addr)) + return PTR_ERR(afe->base_addr); + + /* initial audio related clock */ + ret = mt7986_init_clock(afe); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize clocks\n"); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* enable clock for regcache get default value from hw */ + afe_priv->pm_runtime_bypass_reg_ctl = true; + pm_runtime_get_sync(&pdev->dev); + + afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr, + &mt7986_afe_regmap_config); + + pm_runtime_put_sync(&pdev->dev); + if (IS_ERR(afe->regmap)) + return PTR_ERR(afe->regmap); + + afe_priv->pm_runtime_bypass_reg_ctl = false; + + /* init memif */ + afe->memif_size = MT7986_MEMIF_NUM; + afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif), + GFP_KERNEL); + if (!afe->memif) + return -ENOMEM; + + for (i = 0; i < afe->memif_size; i++) { + afe->memif[i].data = &memif_data[i]; + afe->memif[i].irq_usage = -1; + } + + mutex_init(&afe->irq_alloc_lock); + + /* irq initialize */ + afe->irqs_size = MT7986_IRQ_NUM; + afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs), + GFP_KERNEL); + if (!afe->irqs) + return -ENOMEM; + + for (i = 0; i < afe->irqs_size; i++) + afe->irqs[i].irq_data = &irq_data[i]; + + /* request irq */ + irq_id = platform_get_irq(pdev, 0); + if (irq_id < 0) { + ret = irq_id; + return dev_err_probe(dev, ret, "No irq found\n"); + } + ret = devm_request_irq(dev, irq_id, mt7986_afe_irq_handler, + IRQF_TRIGGER_NONE, "asys-isr", (void *)afe); + if (ret) + return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n"); + + /* init sub_dais */ + INIT_LIST_HEAD(&afe->sub_dais); + + for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) { + ret = dai_register_cbs[i](afe); + if (ret) + return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i); + } + + /* init dai_driver and component_driver */ + ret = mtk_afe_combine_sub_dai(afe); + if (ret) + return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n"); + + afe->mtk_afe_hardware = &mt7986_afe_hardware; + afe->memif_fs = mt7986_memif_fs; + afe->irq_fs = mt7986_irq_fs; + + afe->runtime_resume = mt7986_afe_runtime_resume; + afe->runtime_suspend = mt7986_afe_runtime_suspend; + + /* register component */ + ret = devm_snd_soc_register_component(&pdev->dev, + &mt7986_afe_component, + NULL, 0); + if (ret) + return dev_err_probe(dev, ret, "Cannot register AFE component\n"); + + ret = devm_snd_soc_register_component(afe->dev, + &mt7986_afe_pcm_dai_component, + afe->dai_drivers, + afe->num_dai_drivers); + if (ret) + return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n"); + + return 0; +} + +static void mt7986_afe_pcm_dev_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mt7986_afe_runtime_suspend(&pdev->dev); +} + +static const struct of_device_id mt7986_afe_pcm_dt_match[] = { + { .compatible = "mediatek,mt7986-afe" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt7986_afe_pcm_dt_match); + +static const struct dev_pm_ops mt7986_afe_pm_ops = { + SET_RUNTIME_PM_OPS(mt7986_afe_runtime_suspend, + mt7986_afe_runtime_resume, NULL) +}; + +static struct platform_driver mt7986_afe_pcm_driver = { + .driver = { + .name = "mt7986-audio", + .of_match_table = mt7986_afe_pcm_dt_match, + .pm = &mt7986_afe_pm_ops, + }, + .probe = mt7986_afe_pcm_dev_probe, + .remove_new = mt7986_afe_pcm_dev_remove, +}; +module_platform_driver(mt7986_afe_pcm_driver); + +MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA MT7986"); +MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c new file mode 100644 index 000000000000..e523d33846fe --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek ALSA SoC Audio DAI eTDM Control + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <sound/pcm_params.h> +#include "mt7986-afe-common.h" +#include "mt7986-reg.h" + +#define HOPPING_CLK 0 +#define APLL_CLK 1 +#define MTK_DAI_ETDM_FORMAT_I2S 0 +#define MTK_DAI_ETDM_FORMAT_DSPA 4 +#define MTK_DAI_ETDM_FORMAT_DSPB 5 + +enum { + MTK_ETDM_RATE_8K = 0, + MTK_ETDM_RATE_12K = 1, + MTK_ETDM_RATE_16K = 2, + MTK_ETDM_RATE_24K = 3, + MTK_ETDM_RATE_32K = 4, + MTK_ETDM_RATE_48K = 5, + MTK_ETDM_RATE_96K = 7, + MTK_ETDM_RATE_192K = 9, + MTK_ETDM_RATE_11K = 16, + MTK_ETDM_RATE_22K = 17, + MTK_ETDM_RATE_44K = 18, + MTK_ETDM_RATE_88K = 19, + MTK_ETDM_RATE_176K = 20, +}; + +struct mtk_dai_etdm_priv { + bool bck_inv; + bool lrck_inv; + bool slave_mode; + unsigned int format; +}; + +static unsigned int mt7986_etdm_rate_transform(struct device *dev, unsigned int rate) +{ + switch (rate) { + case 8000: + return MTK_ETDM_RATE_8K; + case 11025: + return MTK_ETDM_RATE_11K; + case 12000: + return MTK_ETDM_RATE_12K; + case 16000: + return MTK_ETDM_RATE_16K; + case 22050: + return MTK_ETDM_RATE_22K; + case 24000: + return MTK_ETDM_RATE_24K; + case 32000: + return MTK_ETDM_RATE_32K; + case 44100: + return MTK_ETDM_RATE_44K; + case 48000: + return MTK_ETDM_RATE_48K; + case 88200: + return MTK_ETDM_RATE_88K; + case 96000: + return MTK_ETDM_RATE_96K; + case 176400: + return MTK_ETDM_RATE_176K; + case 192000: + return MTK_ETDM_RATE_192K; + default: + dev_warn(dev, "%s(), rate %u invalid, using %d!!!\n", + __func__, rate, MTK_ETDM_RATE_48K); + return MTK_ETDM_RATE_48K; + } +} + +static int get_etdm_wlen(unsigned int bitwidth) +{ + return bitwidth <= 16 ? 16 : 32; +} + +/* dai component */ +/* interconnection */ + +static const struct snd_kcontrol_new o124_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I032_Switch", AFE_CONN124_1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new o125_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("I033_Switch", AFE_CONN125_1, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = { + + /* DL */ + SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0), + /* UL */ + SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, o124_mix, ARRAY_SIZE(o124_mix)), + SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, o125_mix, ARRAY_SIZE(o125_mix)), +}; + +static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = { + {"I150", NULL, "ETDM Capture"}, + {"I151", NULL, "ETDM Capture"}, + {"ETDM Playback", NULL, "O124"}, + {"ETDM Playback", NULL, "O125"}, + {"O124", "I032_Switch", "I032"}, + {"O125", "I033_Switch", "I033"}, +}; + +/* dai ops */ +static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + int ret; + + ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks); + if (ret) + return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n"); + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, 0); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, 0); + + return 0; +} + +static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, + CLK_OUT5_PDN); + regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, + CLK_IN5_PDN); + + clk_bulk_disable_unprepare(afe_priv->num_clks, afe_priv->clks); +} + +static unsigned int get_etdm_ch_fixup(unsigned int channels) +{ + if (channels > 16) + return 24; + else if (channels > 8) + return 16; + else if (channels > 4) + return 8; + else if (channels > 2) + return 4; + else + return 2; +} + +static int mtk_dai_etdm_config(struct mtk_base_afe *afe, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai, + int stream) +{ + struct mt7986_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id]; + unsigned int rate = params_rate(params); + unsigned int etdm_rate = mt7986_etdm_rate_transform(afe->dev, rate); + unsigned int afe_rate = mt7986_afe_rate_transform(afe->dev, rate); + unsigned int channels = params_channels(params); + unsigned int bit_width = params_width(params); + unsigned int wlen = get_etdm_wlen(bit_width); + unsigned int val = 0; + unsigned int mask = 0; + + dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n", + __func__, stream, rate, bit_width); + + /* CON0 */ + mask |= ETDM_BIT_LEN_MASK; + val |= FIELD_PREP(ETDM_BIT_LEN_MASK, bit_width - 1); + mask |= ETDM_WRD_LEN_MASK; + val |= FIELD_PREP(ETDM_WRD_LEN_MASK, wlen - 1); + mask |= ETDM_FMT_MASK; + val |= FIELD_PREP(ETDM_FMT_MASK, etdm_data->format); + mask |= ETDM_CH_NUM_MASK; + val |= FIELD_PREP(ETDM_CH_NUM_MASK, get_etdm_ch_fixup(channels) - 1); + mask |= RELATCH_SRC_MASK; + val |= FIELD_PREP(RELATCH_SRC_MASK, APLL_CLK); + + switch (stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + /* set ETDM_OUT5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, mask, val); + + /* set ETDM_OUT5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_RELATCH_MASK, OUT_RELATCH(afe_rate)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_CLK_SRC_MASK, OUT_CLK_SRC(APLL_CLK)); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON4, + OUT_SEL_FS_MASK, OUT_SEL_FS(etdm_rate)); + + /* set ETDM_OUT5_CON5 */ + regmap_update_bits(afe->regmap, ETDM_OUT5_CON5, + ETDM_CLK_DIV_MASK, ETDM_CLK_DIV); + break; + case SNDRV_PCM_STREAM_CAPTURE: + /* set ETDM_IN5_CON0 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, mask, val); + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, + ETDM_SYNC_MASK, ETDM_SYNC); + + /* set ETDM_IN5_CON2 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON2, + IN_CLK_SRC_MASK, IN_CLK_SRC(APLL_CLK)); + + /* set ETDM_IN5_CON3 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON3, + IN_SEL_FS_MASK, IN_SEL_FS(etdm_rate)); + + /* set ETDM_IN5_CON4 */ + regmap_update_bits(afe->regmap, ETDM_IN5_CON4, + IN_RELATCH_MASK, IN_RELATCH(afe_rate)); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_PLAYBACK); + mtk_dai_etdm_config(afe, params, dai, SNDRV_PCM_STREAM_CAPTURE); + + return 0; +} + +static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + + dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + ETDM_EN); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + ETDM_EN); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + regmap_update_bits(afe->regmap, ETDM_IN5_CON0, ETDM_EN_MASK, + 0); + regmap_update_bits(afe->regmap, ETDM_OUT5_CON0, ETDM_EN_MASK, + 0); + break; + default: + break; + } + + return 0; +} + +static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct mt7986_afe_private *afe_priv = afe->platform_priv; + struct mtk_dai_etdm_priv *etdm_data; + void *priv_data; + + switch (dai->id) { + case MT7986_DAI_ETDM: + break; + default: + dev_warn(afe->dev, "%s(), id %d not support\n", + __func__, dai->id); + return -EINVAL; + } + + priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv), + GFP_KERNEL); + if (!priv_data) + return -ENOMEM; + + afe_priv->dai_priv[dai->id] = priv_data; + etdm_data = afe_priv->dai_priv[dai->id]; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA; + break; + case SND_SOC_DAIFMT_DSP_B: + etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_NB_IF: + etdm_data->bck_inv = false; + etdm_data->lrck_inv = true; + break; + case SND_SOC_DAIFMT_IB_NF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = false; + break; + case SND_SOC_DAIFMT_IB_IF: + etdm_data->bck_inv = true; + etdm_data->lrck_inv = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + etdm_data->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + etdm_data->slave_mode = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .trigger = mtk_dai_etdm_trigger, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +/* dai driver */ +#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { + { + .name = "ETDM", + .id = MT7986_DAI_ETDM, + .capture = { + .stream_name = "ETDM Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .playback = { + .stream_name = "ETDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MTK_ETDM_RATES, + .formats = MTK_ETDM_FORMATS, + }, + .ops = &mtk_dai_etdm_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, +}; + +int mt7986_dai_etdm_register(struct mtk_base_afe *afe) +{ + struct mtk_base_afe_dai *dai; + + dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + list_add(&dai->list, &afe->sub_dais); + + dai->dai_drivers = mtk_dai_etdm_driver; + dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver); + + dai->dapm_widgets = mtk_dai_etdm_widgets; + dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets); + dai->dapm_routes = mtk_dai_etdm_routes; + dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes); + + return 0; +} diff --git a/sound/soc/mediatek/mt7986/mt7986-reg.h b/sound/soc/mediatek/mt7986/mt7986-reg.h new file mode 100644 index 000000000000..c2b200743c3f --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-reg.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt7986-reg.h -- MediaTek 7986 audio driver reg definition + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#ifndef _MT7986_REG_H_ +#define _MT7986_REG_H_ + +#define AUDIO_TOP_CON2 0x0008 +#define AUDIO_TOP_CON4 0x0010 +#define AUDIO_ENGEN_CON0 0x0014 +#define AFE_IRQ_MCU_EN 0x0100 +#define AFE_IRQ_MCU_STATUS 0x0120 +#define AFE_IRQ_MCU_CLR 0x0128 +#define AFE_IRQ0_MCU_CFG0 0x0140 +#define AFE_IRQ0_MCU_CFG1 0x0144 +#define AFE_IRQ1_MCU_CFG0 0x0148 +#define AFE_IRQ1_MCU_CFG1 0x014c +#define AFE_IRQ2_MCU_CFG0 0x0150 +#define AFE_IRQ2_MCU_CFG1 0x0154 +#define ETDM_IN5_CON0 0x13f0 +#define ETDM_IN5_CON1 0x13f4 +#define ETDM_IN5_CON2 0x13f8 +#define ETDM_IN5_CON3 0x13fc +#define ETDM_IN5_CON4 0x1400 +#define ETDM_OUT5_CON0 0x1570 +#define ETDM_OUT5_CON4 0x1580 +#define ETDM_OUT5_CON5 0x1584 +#define ETDM_4_7_COWORK_CON0 0x15e0 +#define ETDM_4_7_COWORK_CON1 0x15e4 +#define AFE_CONN018_1 0x1b44 +#define AFE_CONN018_4 0x1b50 +#define AFE_CONN019_1 0x1b64 +#define AFE_CONN019_4 0x1b70 +#define AFE_CONN124_1 0x2884 +#define AFE_CONN124_4 0x2890 +#define AFE_CONN125_1 0x28a4 +#define AFE_CONN125_4 0x28b0 +#define AFE_CONN_RS_0 0x3920 +#define AFE_CONN_RS_3 0x392c +#define AFE_CONN_16BIT_0 0x3960 +#define AFE_CONN_16BIT_3 0x396c +#define AFE_CONN_24BIT_0 0x3980 +#define AFE_CONN_24BIT_3 0x398c +#define AFE_MEMIF_CON0 0x3d98 +#define AFE_MEMIF_RD_MON 0x3da0 +#define AFE_MEMIF_WR_MON 0x3da4 +#define AFE_DL0_BASE_MSB 0x3e40 +#define AFE_DL0_BASE 0x3e44 +#define AFE_DL0_CUR_MSB 0x3e48 +#define AFE_DL0_CUR 0x3e4c +#define AFE_DL0_END_MSB 0x3e50 +#define AFE_DL0_END 0x3e54 +#define AFE_DL0_RCH_MON 0x3e58 +#define AFE_DL0_LCH_MON 0x3e5c +#define AFE_DL0_CON0 0x3e60 +#define AFE_VUL0_BASE_MSB 0x4220 +#define AFE_VUL0_BASE 0x4224 +#define AFE_VUL0_CUR_MSB 0x4228 +#define AFE_VUL0_CUR 0x422c +#define AFE_VUL0_END_MSB 0x4230 +#define AFE_VUL0_END 0x4234 +#define AFE_VUL0_CON0 0x4238 + +#define AFE_MAX_REGISTER AFE_VUL0_CON0 +#define AFE_IRQ_STATUS_BITS 0x7 +#define AFE_IRQ_CNT_SHIFT 0 +#define AFE_IRQ_CNT_MASK 0xffffff + +/* AUDIO_TOP_CON2 */ +#define CLK_OUT5_PDN BIT(14) +#define CLK_OUT5_PDN_MASK BIT(14) +#define CLK_IN5_PDN BIT(7) +#define CLK_IN5_PDN_MASK BIT(7) + +/* AUDIO_TOP_CON4 */ +#define PDN_APLL_TUNER2 BIT(12) +#define PDN_APLL_TUNER2_MASK BIT(12) + +/* AUDIO_ENGEN_CON0 */ +#define AUD_APLL2_EN BIT(3) +#define AUD_APLL2_EN_MASK BIT(3) +#define AUD_26M_EN BIT(0) +#define AUD_26M_EN_MASK BIT(0) + +/* AFE_DL0_CON0 */ +#define DL0_ON_SFT 28 +#define DL0_ON_MASK 0x1 +#define DL0_ON_MASK_SFT BIT(28) +#define DL0_MINLEN_SFT 20 +#define DL0_MINLEN_MASK 0xf +#define DL0_MINLEN_MASK_SFT (0xf << 20) +#define DL0_MODE_SFT 8 +#define DL0_MODE_MASK 0x1f +#define DL0_MODE_MASK_SFT (0x1f << 8) +#define DL0_PBUF_SIZE_SFT 5 +#define DL0_PBUF_SIZE_MASK 0x3 +#define DL0_PBUF_SIZE_MASK_SFT (0x3 << 5) +#define DL0_MONO_SFT 4 +#define DL0_MONO_MASK 0x1 +#define DL0_MONO_MASK_SFT BIT(4) +#define DL0_HALIGN_SFT 2 +#define DL0_HALIGN_MASK 0x1 +#define DL0_HALIGN_MASK_SFT BIT(2) +#define DL0_HD_MODE_SFT 0 +#define DL0_HD_MODE_MASK 0x3 +#define DL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_VUL0_CON0 */ +#define VUL0_ON_SFT 28 +#define VUL0_ON_MASK 0x1 +#define VUL0_ON_MASK_SFT BIT(28) +#define VUL0_MODE_SFT 8 +#define VUL0_MODE_MASK 0x1f +#define VUL0_MODE_MASK_SFT (0x1f << 8) +#define VUL0_MONO_SFT 4 +#define VUL0_MONO_MASK 0x1 +#define VUL0_MONO_MASK_SFT BIT(4) +#define VUL0_HALIGN_SFT 2 +#define VUL0_HALIGN_MASK 0x1 +#define VUL0_HALIGN_MASK_SFT BIT(2) +#define VUL0_HD_MODE_SFT 0 +#define VUL0_HD_MODE_MASK 0x3 +#define VUL0_HD_MODE_MASK_SFT (0x3 << 0) + +/* AFE_IRQ_MCU_CON */ +#define IRQ_MCU_MODE_SFT 4 +#define IRQ_MCU_MODE_MASK 0x1f +#define IRQ_MCU_MODE_MASK_SFT (0x1f << 4) +#define IRQ_MCU_ON_SFT 0 +#define IRQ_MCU_ON_MASK 0x1 +#define IRQ_MCU_ON_MASK_SFT BIT(0) +#define IRQ0_MCU_CLR_SFT 0 +#define IRQ0_MCU_CLR_MASK 0x1 +#define IRQ0_MCU_CLR_MASK_SFT BIT(0) +#define IRQ1_MCU_CLR_SFT 1 +#define IRQ1_MCU_CLR_MASK 0x1 +#define IRQ1_MCU_CLR_MASK_SFT BIT(1) +#define IRQ2_MCU_CLR_SFT 2 +#define IRQ2_MCU_CLR_MASK 0x1 +#define IRQ2_MCU_CLR_MASK_SFT BIT(2) + +/* ETDM_IN5_CON2 */ +#define IN_CLK_SRC(x) ((x) << 10) +#define IN_CLK_SRC_SFT 10 +#define IN_CLK_SRC_MASK GENMASK(12, 10) + +/* ETDM_IN5_CON3 */ +#define IN_SEL_FS(x) ((x) << 26) +#define IN_SEL_FS_SFT 26 +#define IN_SEL_FS_MASK GENMASK(30, 26) + +/* ETDM_IN5_CON4 */ +#define IN_RELATCH(x) ((x) << 20) +#define IN_RELATCH_SFT 20 +#define IN_RELATCH_MASK GENMASK(24, 20) +#define IN_CLK_INV BIT(18) +#define IN_CLK_INV_MASK BIT(18) + +/* ETDM_IN5_CON0 & ETDM_OUT5_CON0 */ +#define RELATCH_SRC_MASK GENMASK(30, 28) +#define ETDM_CH_NUM_MASK GENMASK(27, 23) +#define ETDM_WRD_LEN_MASK GENMASK(20, 16) +#define ETDM_BIT_LEN_MASK GENMASK(15, 11) +#define ETDM_FMT_MASK GENMASK(8, 6) +#define ETDM_SYNC BIT(1) +#define ETDM_SYNC_MASK BIT(1) +#define ETDM_EN BIT(0) +#define ETDM_EN_MASK BIT(0) + +/* ETDM_OUT5_CON4 */ +#define OUT_RELATCH(x) ((x) << 24) +#define OUT_RELATCH_SFT 24 +#define OUT_RELATCH_MASK GENMASK(28, 24) +#define OUT_CLK_SRC(x) ((x) << 6) +#define OUT_CLK_SRC_SFT 6 +#define OUT_CLK_SRC_MASK GENMASK(8, 6) +#define OUT_SEL_FS(x) (x) +#define OUT_SEL_FS_SFT 0 +#define OUT_SEL_FS_MASK GENMASK(4, 0) + +/* ETDM_OUT5_CON5 */ +#define ETDM_CLK_DIV BIT(12) +#define ETDM_CLK_DIV_MASK BIT(12) +#define OUT_CLK_INV BIT(9) +#define OUT_CLK_INV_MASK BIT(9) + +/* ETDM_4_7_COWORK_CON0 */ +#define OUT_SEL(x) ((x) << 12) +#define OUT_SEL_SFT 12 +#define OUT_SEL_MASK GENMASK(15, 12) +#endif diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c new file mode 100644 index 000000000000..364d13b1c426 --- /dev/null +++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mt7986-wm8960.c -- MT7986-WM8960 ALSA SoC machine driver + * + * Copyright (c) 2023 MediaTek Inc. + * Authors: Vic Wu <vic.wu@mediatek.com> + * Maso Huang <maso.huang@mediatek.com> + */ + +#include <linux/module.h> +#include <sound/soc.h> + +#include "mt7986-afe-common.h" + +struct mt7986_wm8960_priv { + struct device_node *platform_node; + struct device_node *codec_node; +}; + +static const struct snd_soc_dapm_widget mt7986_wm8960_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), +}; + +static const struct snd_kcontrol_new mt7986_wm8960_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("AMIC"), +}; + +SND_SOC_DAILINK_DEFS(playback, + DAILINK_COMP_ARRAY(COMP_CPU("DL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(capture, + DAILINK_COMP_ARRAY(COMP_CPU("UL1")), + DAILINK_COMP_ARRAY(COMP_DUMMY()), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +SND_SOC_DAILINK_DEFS(codec, + DAILINK_COMP_ARRAY(COMP_CPU("ETDM")), + DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + +static struct snd_soc_dai_link mt7986_wm8960_dai_links[] = { + /* FE */ + { + .name = "wm8960-playback", + .stream_name = "wm8960-playback", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_playback = 1, + SND_SOC_DAILINK_REG(playback), + }, + { + .name = "wm8960-capture", + .stream_name = "wm8960-capture", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dynamic = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(capture), + }, + /* BE */ + { + .name = "wm8960-codec", + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_GATED, + .dpcm_playback = 1, + .dpcm_capture = 1, + SND_SOC_DAILINK_REG(codec), + }, +}; + +static struct snd_soc_card mt7986_wm8960_card = { + .name = "mt7986-wm8960", + .owner = THIS_MODULE, + .dai_link = mt7986_wm8960_dai_links, + .num_links = ARRAY_SIZE(mt7986_wm8960_dai_links), + .controls = mt7986_wm8960_controls, + .num_controls = ARRAY_SIZE(mt7986_wm8960_controls), + .dapm_widgets = mt7986_wm8960_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt7986_wm8960_widgets), +}; + +static int mt7986_wm8960_machine_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mt7986_wm8960_card; + struct snd_soc_dai_link *dai_link; + struct device_node *platform, *codec; + struct mt7986_wm8960_priv *priv; + int ret, i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform = of_get_child_by_name(pdev->dev.of_node, "platform"); + + if (platform) { + priv->platform_node = of_parse_phandle(platform, "sound-dai", 0); + of_node_put(platform); + + if (!priv->platform_node) { + dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n"); + return -EINVAL; + } + } else { + dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platforms->name) + continue; + dai_link->platforms->of_node = priv->platform_node; + } + + card->dev = &pdev->dev; + + codec = of_get_child_by_name(pdev->dev.of_node, "codec"); + + if (codec) { + priv->codec_node = of_parse_phandle(codec, "sound-dai", 0); + of_node_put(codec); + + if (!priv->codec_node) { + of_node_put(priv->platform_node); + dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n"); + return -EINVAL; + } + } else { + of_node_put(priv->platform_node); + dev_err(&pdev->dev, "Property 'codec' missing or invalid\n"); + return -EINVAL; + } + + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codecs->name) + continue; + dai_link->codecs->of_node = priv->codec_node; + } + + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret); + goto err_of_node_put; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "%s snd_soc_register_card fail: %d\n", __func__, ret); + goto err_of_node_put; + } + +err_of_node_put: + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); + return ret; +} + +static void mt7986_wm8960_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct mt7986_wm8960_priv *priv = snd_soc_card_get_drvdata(card); + + of_node_put(priv->codec_node); + of_node_put(priv->platform_node); +} + +static const struct of_device_id mt7986_wm8960_machine_dt_match[] = { + {.compatible = "mediatek,mt7986-wm8960-sound"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt7986_wm8960_machine_dt_match); + +static struct platform_driver mt7986_wm8960_machine = { + .driver = { + .name = "mt7986-wm8960", + .of_match_table = mt7986_wm8960_machine_dt_match, + }, + .probe = mt7986_wm8960_machine_probe, + .remove_new = mt7986_wm8960_machine_remove, +}; + +module_platform_driver(mt7986_wm8960_machine); + +/* Module information */ +MODULE_DESCRIPTION("MT7986 WM8960 ALSA SoC machine driver"); +MODULE_AUTHOR("Vic Wu <vic.wu@mediatek.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("mt7986 wm8960 soc card"); diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c index c2b0619b6158..bfb2094758ff 100644 --- a/sound/soc/mediatek/mt8173/mt8173-max98090.c +++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -70,7 +70,7 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; /* enable jack detection */ - ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADPHONE, + ret = snd_soc_card_jack_new_pins(card, "Headphone", SND_JACK_HEADSET, &mt8173_max98090_jack, mt8173_max98090_jack_pins, ARRAY_SIZE(mt8173_max98090_jack_pins)); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index f803f121659d..e502cd1670ba 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -40,6 +40,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_rt5514_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -82,11 +93,13 @@ static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime) RT5645_CLK_SEL_I2S1_ASRC); /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5514_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5514_jack, + mt8173_rt5650_rt5514_jack_pins, + ARRAY_SIZE(mt8173_rt5650_rt5514_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c index 8794720cea3a..ffb094284bfb 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -44,6 +44,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_rt5676_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_rt5676_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -95,11 +106,13 @@ static int mt8173_rt5650_rt5676_init(struct snd_soc_pcm_runtime *runtime) RT5677_CLK_SEL_I2S2_ASRC); /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_rt5676_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_rt5676_jack, + mt8173_rt5650_rt5676_jack_pins, + ARRAY_SIZE(mt8173_rt5650_rt5676_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 3ece4b5eaca2..18cf84bb25c7 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -54,6 +54,17 @@ static const struct snd_kcontrol_new mt8173_rt5650_controls[] = { SOC_DAPM_PIN_SWITCH("Headset Mic"), }; +static struct snd_soc_jack_pin mt8173_rt5650_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -128,11 +139,13 @@ static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime) } /* enable jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &mt8173_rt5650_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &mt8173_rt5650_jack, + mt8173_rt5650_jack_pins, + ARRAY_SIZE(mt8173_rt5650_jack_pins)); if (ret) { dev_err(card->dev, "Can't new Headset Jack %d\n", ret); return ret; diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 87bb04846991..701fbcc0f2c9 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -29,6 +29,21 @@ struct mt8183_da7219_max98357_priv { struct snd_soc_jack headset_jack, hdmi_jack; }; +static struct snd_soc_jack_pin mt8183_da7219_max98357_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, +}; + static int mt8183_mt6358_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -572,13 +587,15 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) snd_soc_card_get_drvdata(component->card); /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(component->card, - "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_LINEOUT, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(component->card, + "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_LINEOUT, + &priv->headset_jack, + mt8183_da7219_max98357_jack_pins, + ARRAY_SIZE(mt8183_da7219_max98357_jack_pins)); if (ret) return ret; @@ -609,12 +626,18 @@ static struct snd_soc_codec_conf mt6358_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_max98357_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget mt8183_da7219_max98357_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_SPK("Line Out", NULL), SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL", "aud_tdm_out_on", "aud_tdm_out_off"), }; @@ -657,14 +680,20 @@ static struct snd_soc_codec_conf mt8183_da7219_rt1015_codec_conf[] = { }; static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Left Spk"), SOC_DAPM_PIN_SWITCH("Right Spk"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget mt8183_da7219_rt1015_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_SPK("Left Spk", NULL), SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL", "aud_tdm_out_on", "aud_tdm_out_off"), }; diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index ce9aedde7e1e..850f4d949d97 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -592,11 +592,38 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { }, }; +static const +struct snd_kcontrol_new mt8183_mt6358_ts3a227_max98357_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static const +struct snd_soc_dapm_widget mt8183_mt6358_ts3a227_max98357_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static struct snd_soc_jack_pin mt8183_mt6358_ts3a227_max98357_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static struct snd_soc_card mt8183_mt6358_ts3a227_max98357_card = { .name = "mt8183_mt6358_ts3a227_max98357", .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_card mt8183_mt6358_ts3a227_max98357b_card = { @@ -604,6 +631,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_max98357b_card = { .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_codec_conf mt8183_mt6358_ts3a227_rt1015_amp_conf[] = { @@ -624,6 +655,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015_card = { .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), .codec_conf = mt8183_mt6358_ts3a227_rt1015_amp_conf, .num_configs = ARRAY_SIZE(mt8183_mt6358_ts3a227_rt1015_amp_conf), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015p_card = { @@ -631,6 +666,10 @@ static struct snd_soc_card mt8183_mt6358_ts3a227_rt1015p_card = { .owner = THIS_MODULE, .dai_link = mt8183_mt6358_ts3a227_dai_links, .num_links = ARRAY_SIZE(mt8183_mt6358_ts3a227_dai_links), + .controls = mt8183_mt6358_ts3a227_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_snd_controls), + .dapm_widgets = mt8183_mt6358_ts3a227_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_dapm_widgets), }; static int @@ -641,12 +680,14 @@ mt8183_mt6358_ts3a227_max98357_headset_init(struct snd_soc_component *component) snd_soc_card_get_drvdata(component->card); /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(component->card, - "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(component->card, + "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &priv->headset_jack, + mt8183_mt6358_ts3a227_max98357_jack_pins, + ARRAY_SIZE(mt8183_mt6358_ts3a227_max98357_jack_pins)); if (ret) return ret; diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c index 539e3a023bc4..70ec101890d3 100644 --- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c +++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c @@ -13,8 +13,6 @@ #include "mt8186-afe-clk.h" #include "mt8186-audsys-clk.h" -static DEFINE_MUTEX(mutex_request_dram); - static const char *aud_clks[CLK_NUM] = { [CLK_AFE] = "aud_afe_clk", [CLK_DAC] = "aud_dac_clk", diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c index 0432f9d89020..aa8e00bba19b 100644 --- a/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c +++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-da7219-max98357.c @@ -46,6 +46,10 @@ static struct snd_soc_jack_pin mt8186_jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static struct snd_soc_codec_conf mt8186_mt6366_da7219_max98357_codec_conf[] = { @@ -964,6 +968,7 @@ mt8186_mt6366_da7219_max98357_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_OUTPUT("HDMI1"), SND_SOC_DAPM_MIXER(SOF_DMA_DL1, SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER(SOF_DMA_DL2, SND_SOC_NOPM, 0, 0, NULL, 0), @@ -996,6 +1001,7 @@ mt8186_mt6366_da7219_max98357_controls[] = { SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("HDMI1"), }; diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c index eedb9165f911..fd4f9f8f032d 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-etdm.c @@ -2456,25 +2456,6 @@ static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai, return mtk_dai_etdm_cal_mclk(afe, freq, dai->id); } -static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { - .startup = mtk_dai_etdm_startup, - .shutdown = mtk_dai_etdm_shutdown, - .hw_params = mtk_dai_etdm_hw_params, - .trigger = mtk_dai_etdm_trigger, - .set_sysclk = mtk_dai_etdm_set_sysclk, - .set_fmt = mtk_dai_etdm_set_fmt, - .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, -}; - -static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { - .startup = mtk_dai_hdmitx_dptx_startup, - .shutdown = mtk_dai_hdmitx_dptx_shutdown, - .hw_params = mtk_dai_hdmitx_dptx_hw_params, - .trigger = mtk_dai_hdmitx_dptx_trigger, - .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, - .set_fmt = mtk_dai_etdm_set_fmt, -}; - /* dai driver */ #define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000) @@ -2505,6 +2486,36 @@ static int mtk_dai_etdm_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = { + .startup = mtk_dai_hdmitx_dptx_startup, + .shutdown = mtk_dai_hdmitx_dptx_shutdown, + .hw_params = mtk_dai_hdmitx_dptx_hw_params, + .trigger = mtk_dai_hdmitx_dptx_trigger, + .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops2 = { + .probe = mtk_dai_etdm_probe, + .startup = mtk_dai_hdmitx_dptx_startup, + .shutdown = mtk_dai_hdmitx_dptx_shutdown, + .hw_params = mtk_dai_hdmitx_dptx_hw_params, + .trigger = mtk_dai_hdmitx_dptx_trigger, + .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, +}; + +static const struct snd_soc_dai_ops mtk_dai_etdm_ops = { + .probe = mtk_dai_etdm_probe, + .startup = mtk_dai_etdm_startup, + .shutdown = mtk_dai_etdm_shutdown, + .hw_params = mtk_dai_etdm_hw_params, + .trigger = mtk_dai_etdm_trigger, + .set_sysclk = mtk_dai_etdm_set_sysclk, + .set_fmt = mtk_dai_etdm_set_fmt, + .set_tdm_slot = mtk_dai_etdm_set_tdm_slot, +}; + static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { { .name = "DPTX", @@ -2529,7 +2540,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM2_IN", @@ -2542,7 +2552,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM1_OUT", @@ -2555,7 +2564,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM2_OUT", @@ -2568,7 +2576,6 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .formats = MTK_ETDM_FORMATS, }, .ops = &mtk_dai_etdm_ops, - .probe = mtk_dai_etdm_probe, }, { .name = "ETDM3_OUT", @@ -2580,8 +2587,7 @@ static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = { .rates = MTK_ETDM_RATES, .formats = MTK_ETDM_FORMATS, }, - .ops = &mtk_dai_hdmitx_dptx_ops, - .probe = mtk_dai_etdm_probe, + .ops = &mtk_dai_hdmitx_dptx_ops2, }, }; diff --git a/sound/soc/meson/aiu-acodec-ctrl.c b/sound/soc/meson/aiu-acodec-ctrl.c index d0f0ada5f4bc..7b04b97f7b41 100644 --- a/sound/soc/meson/aiu-acodec-ctrl.c +++ b/sound/soc/meson/aiu-acodec-ctrl.c @@ -103,6 +103,8 @@ static int aiu_acodec_ctrl_input_hw_params(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops aiu_acodec_ctrl_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = aiu_acodec_ctrl_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -130,8 +132,6 @@ static const struct snd_soc_dai_ops aiu_acodec_ctrl_output_ops = { .name = "ACODEC CTRL " xname, \ .playback = AIU_ACODEC_STREAM(xname, "Playback", 8), \ .ops = &aiu_acodec_ctrl_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define AIU_ACODEC_OUTPUT(xname) { \ diff --git a/sound/soc/meson/aiu-codec-ctrl.c b/sound/soc/meson/aiu-codec-ctrl.c index 84c10956c241..ee0ef6301010 100644 --- a/sound/soc/meson/aiu-codec-ctrl.c +++ b/sound/soc/meson/aiu-codec-ctrl.c @@ -75,6 +75,8 @@ static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = { }; static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = meson_codec_glue_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -102,8 +104,6 @@ static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = { .name = "CODEC CTRL " xname, \ .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \ .ops = &aiu_codec_ctrl_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define AIU_CODEC_CTRL_OUTPUT(xname) { \ diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c index 59e00a74b5f8..7d833500c799 100644 --- a/sound/soc/meson/aiu-fifo-i2s.c +++ b/sound/soc/meson/aiu-fifo-i2s.c @@ -140,6 +140,9 @@ static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, } const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = { + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_i2s_dai_probe, + .remove = aiu_fifo_dai_remove, .trigger = aiu_fifo_i2s_trigger, .prepare = aiu_fifo_i2s_prepare, .hw_params = aiu_fifo_i2s_hw_params, diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index ddbd2fc40185..fa91f3c53fa4 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -155,6 +155,9 @@ static int fifo_spdif_hw_params(struct snd_pcm_substream *substream, } const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { + .pcm_new = aiu_fifo_pcm_new, + .probe = aiu_fifo_spdif_dai_probe, + .remove = aiu_fifo_dai_remove, .trigger = fifo_spdif_trigger, .prepare = fifo_spdif_prepare, .hw_params = fifo_spdif_hw_params, diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c index da351a60df0c..7109b81cc3d0 100644 --- a/sound/soc/meson/aiu.c +++ b/sound/soc/meson/aiu.c @@ -121,9 +121,6 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_fifo_i2s_dai_ops, - .pcm_new = aiu_fifo_pcm_new, - .probe = aiu_fifo_i2s_dai_probe, - .remove = aiu_fifo_dai_remove, }, [CPU_SPDIF_FIFO] = { .name = "SPDIF FIFO", @@ -137,9 +134,6 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { .formats = AIU_FORMATS, }, .ops = &aiu_fifo_spdif_dai_ops, - .pcm_new = aiu_fifo_pcm_new, - .probe = aiu_fifo_spdif_dai_probe, - .remove = aiu_fifo_dai_remove, }, [CPU_I2S_ENCODER] = { .name = "I2S Encoder", diff --git a/sound/soc/meson/axg-frddr.c b/sound/soc/meson/axg-frddr.c index 61f9d417fd60..8c166a5f338c 100644 --- a/sound/soc/meson/axg-frddr.c +++ b/sound/soc/meson/axg-frddr.c @@ -100,6 +100,7 @@ static const struct snd_soc_dai_ops axg_frddr_ops = { .hw_params = axg_frddr_dai_hw_params, .startup = axg_frddr_dai_startup, .shutdown = axg_frddr_dai_shutdown, + .pcm_new = axg_frddr_pcm_new, }; static struct snd_soc_dai_driver axg_frddr_dai_drv = { @@ -112,7 +113,6 @@ static struct snd_soc_dai_driver axg_frddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &axg_frddr_ops, - .pcm_new = axg_frddr_pcm_new, }; static const char * const axg_frddr_sel_texts[] = { @@ -175,6 +175,7 @@ static const struct snd_soc_dai_ops g12a_frddr_ops = { .hw_params = axg_frddr_dai_hw_params, .startup = axg_frddr_dai_startup, .shutdown = axg_frddr_dai_shutdown, + .pcm_new = axg_frddr_pcm_new, }; static struct snd_soc_dai_driver g12a_frddr_dai_drv = { @@ -187,7 +188,6 @@ static struct snd_soc_dai_driver g12a_frddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_frddr_ops, - .pcm_new = axg_frddr_pcm_new, }; static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT, diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c index ad43cb2a1e3f..d59050914d3c 100644 --- a/sound/soc/meson/axg-pdm.c +++ b/sound/soc/meson/axg-pdm.c @@ -294,13 +294,6 @@ static void axg_pdm_shutdown(struct snd_pcm_substream *substream, clk_disable_unprepare(priv->dclk); } -static const struct snd_soc_dai_ops axg_pdm_dai_ops = { - .trigger = axg_pdm_trigger, - .hw_params = axg_pdm_hw_params, - .startup = axg_pdm_startup, - .shutdown = axg_pdm_shutdown, -}; - static void axg_pdm_set_hcic_ctrl(struct axg_pdm *priv) { const struct axg_pdm_hcic *hcic = &priv->cfg->filters->hcic; @@ -440,6 +433,15 @@ static int axg_pdm_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops axg_pdm_dai_ops = { + .probe = axg_pdm_dai_probe, + .remove = axg_pdm_dai_remove, + .trigger = axg_pdm_trigger, + .hw_params = axg_pdm_hw_params, + .startup = axg_pdm_startup, + .shutdown = axg_pdm_shutdown, +}; + static struct snd_soc_dai_driver axg_pdm_dai_drv = { .name = "PDM", .capture = { @@ -453,8 +455,6 @@ static struct snd_soc_dai_driver axg_pdm_dai_drv = { SNDRV_PCM_FMTBIT_S32_LE), }, .ops = &axg_pdm_dai_ops, - .probe = axg_pdm_dai_probe, - .remove = axg_pdm_dai_remove, }; static const struct snd_soc_component_driver axg_pdm_component_drv = { diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index e2cc4c4be758..d86880169075 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -267,6 +267,8 @@ static int axg_spdifin_dai_remove(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axg_spdifin_ops = { + .probe = axg_spdifin_dai_probe, + .remove = axg_spdifin_dai_remove, .prepare = axg_spdifin_prepare, .startup = axg_spdifin_startup, .shutdown = axg_spdifin_shutdown, @@ -429,8 +431,6 @@ axg_spdifin_get_dai_drv(struct device *dev, struct axg_spdifin *priv) drv->name = "SPDIF Input"; drv->ops = &axg_spdifin_ops; - drv->probe = axg_spdifin_dai_probe; - drv->remove = axg_spdifin_dai_remove; drv->capture.stream_name = "Capture"; drv->capture.channels_min = 1; drv->capture.channels_max = 2; diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index 5e5e4c56d505..1c3d433cefd2 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -395,6 +395,8 @@ static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops axg_tdm_iface_ops = { + .probe = axg_tdm_iface_probe_dai, + .remove = axg_tdm_iface_remove_dai, .set_sysclk = axg_tdm_iface_set_sysclk, .set_fmt = axg_tdm_iface_set_fmt, .startup = axg_tdm_iface_startup, @@ -423,8 +425,6 @@ static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { }, .id = TDM_IFACE_PAD, .ops = &axg_tdm_iface_ops, - .probe = axg_tdm_iface_probe_dai, - .remove = axg_tdm_iface_remove_dai, }, [TDM_IFACE_LOOPBACK] = { .name = "TDM Loopback", @@ -437,8 +437,6 @@ static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { }, .id = TDM_IFACE_LOOPBACK, .ops = &axg_tdm_iface_ops, - .probe = axg_tdm_iface_probe_dai, - .remove = axg_tdm_iface_remove_dai, }, }; diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index e9208e74e965..1a0be177b8fe 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -122,6 +122,7 @@ static const struct snd_soc_dai_ops axg_toddr_ops = { .hw_params = axg_toddr_dai_hw_params, .startup = axg_toddr_dai_startup, .shutdown = axg_toddr_dai_shutdown, + .pcm_new = axg_toddr_pcm_new, }; static struct snd_soc_dai_driver axg_toddr_dai_drv = { @@ -134,7 +135,6 @@ static struct snd_soc_dai_driver axg_toddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &axg_toddr_ops, - .pcm_new = axg_toddr_pcm_new, }; static const char * const axg_toddr_sel_texts[] = { @@ -217,6 +217,7 @@ static const struct snd_soc_dai_ops g12a_toddr_ops = { .hw_params = axg_toddr_dai_hw_params, .startup = g12a_toddr_dai_startup, .shutdown = axg_toddr_dai_shutdown, + .pcm_new = axg_toddr_pcm_new, }; static struct snd_soc_dai_driver g12a_toddr_dai_drv = { @@ -229,7 +230,6 @@ static struct snd_soc_dai_driver g12a_toddr_dai_drv = { .formats = AXG_FIFO_FORMATS, }, .ops = &g12a_toddr_ops, - .pcm_new = axg_toddr_pcm_new, }; static const struct snd_soc_component_driver g12a_toddr_component_drv = { diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c index ddc667956cf5..6c4503766fdc 100644 --- a/sound/soc/meson/g12a-toacodec.c +++ b/sound/soc/meson/g12a-toacodec.c @@ -162,6 +162,8 @@ static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops g12a_toacodec_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = g12a_toacodec_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -185,8 +187,6 @@ static const struct snd_soc_dai_ops g12a_toacodec_output_ops = { .id = (xid), \ .playback = TOACODEC_STREAM(xname, "Playback", 8), \ .ops = &g12a_toacodec_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define TOACODEC_OUTPUT(xname, xid) { \ diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c index 579a04ad4d19..f7ef9aa1eed8 100644 --- a/sound/soc/meson/g12a-tohdmitx.c +++ b/sound/soc/meson/g12a-tohdmitx.c @@ -140,6 +140,8 @@ static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = { }; static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = { + .probe = meson_codec_glue_input_dai_probe, + .remove = meson_codec_glue_input_dai_remove, .hw_params = meson_codec_glue_input_hw_params, .set_fmt = meson_codec_glue_input_set_fmt, }; @@ -172,8 +174,6 @@ static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { .id = (xid), \ .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \ .ops = &g12a_tohdmitx_input_ops, \ - .probe = meson_codec_glue_input_dai_probe, \ - .remove = meson_codec_glue_input_dai_remove, \ } #define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \ diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index a1ed141b8795..abfaf3cdf5bb 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -340,6 +340,7 @@ static int mmp_sspa_probe(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { + .probe = mmp_sspa_probe, .startup = mmp_sspa_startup, .shutdown = mmp_sspa_shutdown, .trigger = mmp_sspa_trigger, @@ -350,7 +351,6 @@ static const struct snd_soc_dai_ops mmp_sspa_dai_ops = { }; static struct snd_soc_dai_driver mmp_sspa_dai = { - .probe = mmp_sspa_probe, .playback = { .channels_min = 1, .channels_max = 128, diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 430dd446321e..b70034c07eee 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -591,7 +591,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (ret < 0) { const struct pxa_ssp_clock_mode *m; - int ssacd, acds; + int ssacd; for (m = pxa_ssp_clock_modes; m->rate; m++) { if (m->rate == rate) @@ -601,12 +601,6 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, if (!m->rate) return -EINVAL; - acds = m->acds; - - /* The values in the table are for 16 bits */ - if (width == 32) - acds--; - ret = pxa_ssp_set_pll(priv, bclk); if (ret < 0) return ret; @@ -819,6 +813,8 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai) #define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { + .probe = pxa_ssp_probe, + .remove = pxa_ssp_remove, .startup = pxa_ssp_startup, .shutdown = pxa_ssp_shutdown, .trigger = pxa_ssp_trigger, @@ -830,8 +826,6 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = { }; static struct snd_soc_dai_driver pxa_ssp_dai = { - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, .playback = { .channels_min = 1, .channels_max = 8, diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 10636506b622..437bfccd04f8 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -329,6 +329,8 @@ static int pxa2xx_i2s_remove(struct snd_soc_dai *dai) SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) static const struct snd_soc_dai_ops pxa_i2s_dai_ops = { + .probe = pxa2xx_i2s_probe, + .remove = pxa2xx_i2s_remove, .startup = pxa2xx_i2s_startup, .shutdown = pxa2xx_i2s_shutdown, .trigger = pxa2xx_i2s_trigger, @@ -338,8 +340,6 @@ static const struct snd_soc_dai_ops pxa_i2s_dai_ops = { }; static struct snd_soc_dai_driver pxa_i2s_dai = { - .probe = pxa2xx_i2s_probe, - .remove = pxa2xx_i2s_remove, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c index e54b8961112f..6de533d45e7d 100644 --- a/sound/soc/qcom/apq8016_sbc.c +++ b/sound/soc/qcom/apq8016_sbc.c @@ -44,6 +44,17 @@ struct apq8016_sbc_data { #define DEFAULT_MCLK_RATE 9600000 #define MI2S_BCLK_RATE 1536000 +static struct snd_soc_jack_pin apq8016_sbc_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) { struct snd_soc_dai *codec_dai; @@ -90,13 +101,15 @@ static int apq8016_dai_init(struct snd_soc_pcm_runtime *rtd, int mi2s) if (!pdata->jack_setup) { struct snd_jack *jack; - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4, - &pdata->jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4, + &pdata->jack, + apq8016_sbc_jack_pins, + ARRAY_SIZE(apq8016_sbc_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); @@ -255,8 +268,14 @@ static void msm8916_qdsp6_add_ops(struct snd_soc_card *card) } } -static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { +static const struct snd_kcontrol_new apq8016_sbc_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; +static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("Handset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Secondary Mic", NULL), @@ -285,6 +304,8 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dapm_widgets = apq8016_sbc_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets); + card->controls = apq8016_sbc_snd_controls; + card->num_controls = ARRAY_SIZE(apq8016_sbc_snd_controls); ret = qcom_snd_parse_of(card); if (ret) diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index abaf694ee9a3..f919d46e18ca 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -41,7 +41,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_SECONDARY] = { @@ -62,7 +61,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_TERTIARY] = { @@ -83,7 +81,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, [MI2S_QUATERNARY] = { @@ -119,7 +116,6 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, }; diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index dbdaaa85ce48..39571fed4001 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -404,18 +404,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, return 0; } -const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { - .set_sysclk = lpass_cpu_daiops_set_sysclk, - .startup = lpass_cpu_daiops_startup, - .shutdown = lpass_cpu_daiops_shutdown, - .hw_params = lpass_cpu_daiops_hw_params, - .trigger = lpass_cpu_daiops_trigger, - .prepare = lpass_cpu_daiops_prepare, -}; -EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); - -int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) +static int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { int ret; struct snd_soc_dai_driver *drv = dai->driver; @@ -431,9 +420,8 @@ int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, return 0; } -EXPORT_SYMBOL_GPL(lpass_cpu_pcm_new); -int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) +static int lpass_cpu_daiops_probe(struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); int ret; @@ -446,7 +434,29 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) return ret; } -EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); + +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { + .probe = lpass_cpu_daiops_probe, + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .trigger = lpass_cpu_daiops_trigger, + .prepare = lpass_cpu_daiops_prepare, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); + +const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = { + .pcm_new = lpass_cpu_daiops_pcm_new, + .probe = lpass_cpu_daiops_probe, + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .trigger = lpass_cpu_daiops_trigger, + .prepare = lpass_cpu_daiops_prepare, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2); static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, const struct of_phandle_args *args, diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index ef8a7984f232..2c97f295e394 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -51,7 +51,6 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .channels_min = 1, .channels_max = 8, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }; diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index 56db852f4eab..d16c0d83aaad 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -43,7 +43,6 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = MI2S_SECONDARY, @@ -57,9 +56,7 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, - .ops = &asoc_qcom_lpass_cpu_dai_ops, - .pcm_new = lpass_cpu_pcm_new, + .ops = &asoc_qcom_lpass_cpu_dai_ops2, }, { .id = LPASS_DP_RX, .name = "Hdmi", diff --git a/sound/soc/qcom/lpass-sc7280.c b/sound/soc/qcom/lpass-sc7280.c index bcf18fe8e14d..6b2eb25ed939 100644 --- a/sound/soc/qcom/lpass-sc7280.c +++ b/sound/soc/qcom/lpass-sc7280.c @@ -38,7 +38,6 @@ static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = MI2S_SECONDARY, @@ -52,7 +51,6 @@ static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = { .channels_min = 2, .channels_max = 2, }, - .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, { .id = LPASS_DP_RX, diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index dd78600fc7b0..bdfe66ec3314 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -402,10 +402,8 @@ int asoc_qcom_lpass_platform_register(struct platform_device *); int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev); void asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev); int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev); -int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai); extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops; -int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai); +extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2; extern const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops; #endif /* __LPASS_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index dbff55a97162..3faa7e0eb0dd 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -619,44 +619,6 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"}, }; -static const struct snd_soc_dai_ops q6hdmi_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6hdmi_hw_params, - .shutdown = q6afe_dai_shutdown, -}; - -static const struct snd_soc_dai_ops q6i2s_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6i2s_hw_params, - .set_fmt = q6i2s_set_fmt, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, -}; - -static const struct snd_soc_dai_ops q6slim_ops = { - .prepare = q6afe_dai_prepare, - .hw_params = q6slim_hw_params, - .shutdown = q6afe_dai_shutdown, - .set_channel_map = q6slim_set_channel_map, -}; - -static const struct snd_soc_dai_ops q6tdm_ops = { - .prepare = q6afe_dai_prepare, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, - .set_tdm_slot = q6tdm_set_tdm_slot, - .set_channel_map = q6tdm_set_channel_map, - .hw_params = q6tdm_hw_params, -}; - -static const struct snd_soc_dai_ops q6dma_ops = { - .prepare = q6afe_dai_prepare, - .shutdown = q6afe_dai_shutdown, - .set_sysclk = q6afe_mi2s_set_sysclk, - .set_channel_map = q6dma_set_channel_map, - .hw_params = q6dma_hw_params, -}; - static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -682,6 +644,54 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops q6hdmi_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6hdmi_hw_params, + .shutdown = q6afe_dai_shutdown, +}; + +static const struct snd_soc_dai_ops q6i2s_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6i2s_hw_params, + .set_fmt = q6i2s_set_fmt, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, +}; + +static const struct snd_soc_dai_ops q6slim_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .hw_params = q6slim_hw_params, + .shutdown = q6afe_dai_shutdown, + .set_channel_map = q6slim_set_channel_map, +}; + +static const struct snd_soc_dai_ops q6tdm_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_tdm_slot = q6tdm_set_tdm_slot, + .set_channel_map = q6tdm_set_channel_map, + .hw_params = q6tdm_hw_params, +}; + +static const struct snd_soc_dai_ops q6dma_ops = { + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, +}; + static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_IN("HDMI_RX", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("SLIMBUS_0_RX", NULL, 0, SND_SOC_NOPM, 0, 0), @@ -1041,8 +1051,6 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev) dev_set_drvdata(dev, dai_data); of_q6afe_parse_dai_data(dev, dai_data); - cfg.probe = msm_dai_q6_dai_probe; - cfg.remove = msm_dai_q6_dai_remove; cfg.q6hdmi_ops = &q6hdmi_ops; cfg.q6slim_ops = &q6slim_ops; cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h index 8ee40732ce9e..f486bd639b9f 100644 --- a/sound/soc/qcom/qdsp6/q6apm.h +++ b/sound/soc/qcom/qdsp6/q6apm.h @@ -126,7 +126,6 @@ int q6apm_graph_media_format_shmem(struct q6apm_graph *graph, struct audioreach_module_config *cfg); /* read/write related */ -int q6apm_send_eos_nowait(struct q6apm_graph *graph); int q6apm_read(struct q6apm_graph *graph); int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags); @@ -144,7 +143,6 @@ int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, /* Callback for graph specific */ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, uint32_t mid); -void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv); int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph); bool q6apm_is_adsp_ready(void); diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 5fc8088e63c8..fe0666e9fd23 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -1230,6 +1230,10 @@ static struct snd_soc_dai_driver q6asm_fe_dais_template[] = { Q6ASM_FEDAI_DRIVER(8), }; +static const struct snd_soc_dai_ops q6asm_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static int of_q6asm_parse_dai_data(struct device *dev, struct q6asm_dai_data *pdata) { @@ -1272,7 +1276,7 @@ static int of_q6asm_parse_dai_data(struct device *dev, dai_drv->playback = empty_stream; if (of_property_read_bool(node, "is-compress-dai")) - dai_drv->compress_new = snd_soc_new_compress; + dai_drv->ops = &q6asm_dai_ops; } return 0; diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index ac937a6bf909..4919001de08b 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -603,9 +603,6 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev, int i; for (i = 0; i < ARRAY_SIZE(q6dsp_audio_fe_dais); i++) { - q6dsp_audio_fe_dais[i].probe = cfg->probe; - q6dsp_audio_fe_dais[i].remove = cfg->remove; - switch (q6dsp_audio_fe_dais[i].id) { case HDMI_RX: case DISPLAY_PORT_RX: diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c index f5f7c64b23a2..57c5f35dfcc5 100644 --- a/sound/soc/qcom/sc7180.c +++ b/sound/soc/qcom/sc7180.c @@ -42,6 +42,17 @@ static void sc7180_jack_free(struct snd_jack *jack) snd_soc_component_set_jack(component, NULL, NULL); } +static struct snd_soc_jack_pin sc7180_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -51,13 +62,14 @@ static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) struct snd_jack *jack; int rval; - rval = snd_soc_card_jack_new( - card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->hs_jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->hs_jack, + sc7180_jack_pins, + ARRAY_SIZE(sc7180_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -297,6 +309,11 @@ static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), }; +static const struct snd_kcontrol_new sc7180_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = { SND_SOC_DAPM_MIC("DMIC", NULL), }; @@ -320,6 +337,11 @@ static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = { SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control), }; +static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = { {"Dmic Mux", "Front Mic", "DMIC"}, {"Dmic Mux", "Rear Mic", "DMIC"}, @@ -348,10 +370,14 @@ static int sc7180_snd_platform_probe(struct platform_device *pdev) card->dev = dev; card->dapm_widgets = sc7180_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets); + card->controls = sc7180_snd_controls; + card->num_controls = ARRAY_SIZE(sc7180_snd_controls); if (of_property_read_bool(dev->of_node, "dmic-gpios")) { card->dapm_widgets = sc7180_snd_dual_mic_widgets, card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), + card->controls = sc7180_snd_dual_mic_controls, + card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls), card->dapm_routes = sc7180_snd_dual_mic_audio_route, card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route), data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW); diff --git a/sound/soc/qcom/sc7280.c b/sound/soc/qcom/sc7280.c index 787dd49e03f6..43010e4e2242 100644 --- a/sound/soc/qcom/sc7280.c +++ b/sound/soc/qcom/sc7280.c @@ -43,6 +43,17 @@ static void sc7280_jack_free(struct snd_jack *jack) snd_soc_component_set_jack(component, NULL, NULL); } +static struct snd_soc_jack_pin sc7280_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -54,13 +65,15 @@ static int sc7280_headset_init(struct snd_soc_pcm_runtime *rtd) int rval, i; if (!pdata->jack_setup) { - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | SND_JACK_LINEOUT | - SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4 | SND_JACK_BTN_5, - &pdata->hs_jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_LINEOUT | + SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &pdata->hs_jack, + sc7280_jack_pins, + ARRAY_SIZE(sc7280_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headset Jack\n"); @@ -361,6 +374,11 @@ static const struct snd_soc_dapm_widget sc7280_snd_widgets[] = { SND_SOC_DAPM_MIC("Headset Mic", NULL), }; +static const struct snd_kcontrol_new sc7280_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static int sc7280_snd_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -396,6 +414,8 @@ static int sc7280_snd_platform_probe(struct platform_device *pdev) card->dapm_widgets = sc7280_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sc7280_snd_widgets); + card->controls = sc7280_snd_controls; + card->num_controls = ARRAY_SIZE(sc7280_snd_controls); ret = qcom_snd_parse_of(card); if (ret) diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 02612af714a8..29d23fe5dfa2 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -42,6 +42,17 @@ struct sdm845_snd_data { struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; }; +static struct snd_soc_jack_pin sdm845_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream, @@ -242,12 +253,14 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) if (!pdata->jack_setup) { - rval = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADSET | - SND_JACK_HEADPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &pdata->jack); + rval = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->jack, + sdm845_jack_pins, + ARRAY_SIZE(sdm845_jack_pins)); if (rval < 0) { dev_err(card->dev, "Unable to add Headphone Jack\n"); @@ -539,6 +552,11 @@ static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), }; +static const struct snd_kcontrol_new sdm845_snd_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + static void sdm845_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -572,6 +590,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) card->driver_name = DRIVER_NAME; card->dapm_widgets = sdm845_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); + card->controls = sdm845_snd_controls; + card->num_controls = ARRAY_SIZE(sdm845_snd_controls); card->dev = dev; card->owner = THIS_MODULE; dev_set_drvdata(dev, card); diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 5e52e9d60d44..0f704d22d21b 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -41,13 +41,17 @@ static struct snd_soc_jack_pin rockchip_sound_jack_pins[] = { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, - + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_LINE("HDMI", NULL), }; @@ -56,6 +60,7 @@ static const struct snd_kcontrol_new rockchip_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Line Out"), SOC_DAPM_PIN_SWITCH("Int Mic"), SOC_DAPM_PIN_SWITCH("HDMI"), }; diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index 575a0b9b01e9..834fbb5cf810 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -539,6 +539,7 @@ static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { + .probe = rockchip_i2s_dai_probe, .hw_params = rockchip_i2s_hw_params, .set_bclk_ratio = rockchip_i2s_set_bclk_ratio, .set_sysclk = rockchip_i2s_set_sysclk, @@ -547,7 +548,6 @@ static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = { }; static struct snd_soc_dai_driver rockchip_i2s_dai = { - .probe = rockchip_i2s_dai_probe, .ops = &rockchip_i2s_dai_ops, .symmetric_rate = 1, }; diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 166257c6ae14..d3700f3c98e6 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1114,6 +1114,7 @@ static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai, } static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = { + .probe = rockchip_i2s_tdm_dai_probe, .hw_params = rockchip_i2s_tdm_hw_params, .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio, .set_sysclk = rockchip_i2s_tdm_set_sysclk, @@ -1324,7 +1325,6 @@ static const struct of_device_id rockchip_i2s_tdm_match[] = { }; static const struct snd_soc_dai_driver i2s_tdm_dai = { - .probe = rockchip_i2s_tdm_dai_probe, .ops = &rockchip_i2s_tdm_dai_ops, }; diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 52f9aae60be8..4756cfc23218 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -379,6 +379,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = { + .probe = rockchip_pdm_dai_probe, .set_fmt = rockchip_pdm_set_fmt, .trigger = rockchip_pdm_trigger, .hw_params = rockchip_pdm_hw_params, @@ -391,7 +392,6 @@ static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = { SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_driver rockchip_pdm_dai = { - .probe = rockchip_pdm_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 2, @@ -584,7 +584,7 @@ static int rockchip_pdm_probe(struct platform_device *pdev) match = of_match_device(rockchip_pdm_match, &pdev->dev); if (match) - pdm->version = (enum rk_pdm_version)match->data; + pdm->version = (uintptr_t)match->data; if (pdm->version == RK_PDM_RK3308) { pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m"); diff --git a/sound/soc/rockchip/rockchip_rt5645.c b/sound/soc/rockchip/rockchip_rt5645.c index e73a342b7953..ef9fdf0386cb 100644 --- a/sound/soc/rockchip/rockchip_rt5645.c +++ b/sound/soc/rockchip/rockchip_rt5645.c @@ -22,6 +22,16 @@ #define DRV_NAME "rockchip-snd-rt5645" static struct snd_soc_jack headset_jack; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static const struct snd_soc_dapm_widget rk_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), @@ -103,11 +113,13 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime) int ret; /* Enable Headset and 4 Buttons Jack detection */ - ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, - &headset_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) { dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret); return ret; diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 0b73fe94e4bb..1a24b78e9e02 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -202,12 +202,12 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops rk_spdif_dai_ops = { + .probe = rk_spdif_dai_probe, .hw_params = rk_spdif_hw_params, .trigger = rk_spdif_trigger, }; static struct snd_soc_dai_driver rk_spdif_dai = { - .probe = rk_spdif_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c index 7492bb41456c..dd3cd2c9644a 100644 --- a/sound/soc/samsung/aries_wm8994.c +++ b/sound/soc/samsung/aries_wm8994.c @@ -620,10 +620,14 @@ static int aries_audio_probe(struct platform_device *pdev) /* Update card-name if provided through DT, else use default name */ snd_soc_of_parse_card_name(card, "model"); - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing invalid/unspecified\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } } aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index f3d98abd5f0d..3af48c9b5ab7 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1120,6 +1120,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { + .probe = samsung_i2s_dai_probe, + .remove = samsung_i2s_dai_remove, .trigger = i2s_trigger, .hw_params = i2s_hw_params, .set_fmt = i2s_set_fmt, @@ -1188,9 +1190,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, for (i = 0; i < num_dais; i++) { dai_drv = &priv->dai_drv[i]; - dai_drv->probe = samsung_i2s_dai_probe; - dai_drv->remove = samsung_i2s_dai_remove; - dai_drv->symmetric_rate = 1; dai_drv->ops = &samsung_i2s_dai_ops; diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 5d8118e69359..fafadcef234e 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -225,12 +225,15 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w, } static const struct snd_kcontrol_new controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("WM1250 Input"), SOC_DAPM_PIN_SWITCH("WM1250 Output"), }; static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Headset Mic", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), SND_SOC_DAPM_MIC("DMIC", NULL), @@ -255,6 +258,16 @@ static const struct snd_soc_dapm_route audio_paths[] = { }; static struct snd_soc_jack littlemill_headset; +static struct snd_soc_jack_pin littlemill_headset_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; static int littlemill_late_probe(struct snd_soc_card *card) { @@ -281,12 +294,14 @@ static int littlemill_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4 | SND_JACK_BTN_5, - &littlemill_headset); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &littlemill_headset, + littlemill_headset_pins, + ARRAY_SIZE(littlemill_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 106770be6fc5..a79df871ea13 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -22,12 +22,16 @@ static struct snd_soc_jack lowland_headset; static struct snd_soc_jack_pin lowland_headset_pins[] = { { .pin = "Headphone", - .mask = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, + .mask = SND_JACK_HEADPHONE, }, { .pin = "Headset Mic", .mask = SND_JACK_MICROPHONE, }, + { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, + }, }; static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) @@ -140,11 +144,13 @@ static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("WM1250 Input"), SOC_DAPM_PIN_SWITCH("WM1250 Output"), SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Line Out"), }; static const struct snd_soc_dapm_widget widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), SND_SOC_DAPM_SPK("Main Speaker", NULL), diff --git a/sound/soc/samsung/midas_wm1811.c b/sound/soc/samsung/midas_wm1811.c index 6931b9a45b3e..2ec7e16ddfa2 100644 --- a/sound/soc/samsung/midas_wm1811.c +++ b/sound/soc/samsung/midas_wm1811.c @@ -38,6 +38,17 @@ struct midas_priv { struct snd_soc_jack headset_jack; }; +static struct snd_soc_jack_pin headset_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int midas_start_fll1(struct snd_soc_pcm_runtime *rtd, unsigned int rate) { struct snd_soc_card *card = rtd->card; @@ -261,6 +272,7 @@ static const struct snd_soc_dapm_widget midas_dapm_widgets[] = { SND_SOC_DAPM_LINE("HDMI", NULL), SND_SOC_DAPM_LINE("FM In", midas_fm_set), + SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Main Mic", midas_mic_bias), SND_SOC_DAPM_MIC("Sub Mic", midas_submic_bias), @@ -305,11 +317,13 @@ static int midas_late_probe(struct snd_soc_card *card) return ret; } - ret = snd_soc_card_jack_new(card, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, - &priv->headset_jack); + ret = snd_soc_card_jack_new_pins(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5, + &priv->headset_jack, + headset_jack_pins, + ARRAY_SIZE(headset_jack_pins)); if (ret) return ret; @@ -462,10 +476,14 @@ static int midas_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing invalid/unspecified\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing invalid/unspecified\n"); + return ret; + } } cpu = of_get_child_by_name(dev->of_node, "cpu"); diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index a5442592bde4..c93cb5a86426 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -223,19 +223,20 @@ static int odroid_audio_probe(struct platform_device *pdev) if (ret < 0) return ret; - if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) { + if (of_property_present(dev->of_node, "samsung,audio-widgets")) { ret = snd_soc_of_parse_audio_simple_widgets(card, "samsung,audio-widgets"); if (ret < 0) return ret; } - if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) { - ret = snd_soc_of_parse_audio_routing(card, - "samsung,audio-routing"); - if (ret < 0) - return ret; - } + ret = 0; + if (of_property_present(dev->of_node, "audio-routing")) + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); + else if (of_property_present(dev->of_node, "samsung,audio-routing")) + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) + return ret; card->dai_link = odroid_card_dais; card->num_links = ARRAY_SIZE(odroid_card_dais); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 335fe5cb9cfc..d2cdc5c8e05b 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -432,14 +432,6 @@ static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai, return 0; } -static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { - .set_sysclk = s3c_pcm_set_sysclk, - .set_clkdiv = s3c_pcm_set_clkdiv, - .trigger = s3c_pcm_trigger, - .hw_params = s3c_pcm_hw_params, - .set_fmt = s3c_pcm_set_fmt, -}; - static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) { struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai); @@ -449,11 +441,19 @@ static int s3c_pcm_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops s3c_pcm_dai_ops = { + .probe = s3c_pcm_dai_probe, + .set_sysclk = s3c_pcm_set_sysclk, + .set_clkdiv = s3c_pcm_set_clkdiv, + .trigger = s3c_pcm_trigger, + .hw_params = s3c_pcm_hw_params, + .set_fmt = s3c_pcm_set_fmt, +}; + #define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000 #define S3C_PCM_DAI_DECLARE \ .symmetric_rate = 1, \ - .probe = s3c_pcm_dai_probe, \ .ops = &s3c_pcm_dai_ops, \ .playback = { \ .channels_min = 2, \ diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index d611ec9e5325..5ebf17f3de1e 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -523,10 +523,14 @@ static int tm2_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); if (ret < 0) { - dev_err(dev, "Audio routing is not specified or invalid\n"); - return ret; + /* Backwards compatible way */ + ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); + if (ret < 0) { + dev_err(dev, "Audio routing is not specified or invalid\n"); + return ret; + } } card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node, diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 0b8926600d90..230c48648af3 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -358,8 +358,6 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) ckr = 0x80000000; /* BRGB output = 48kHz */ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr); - rsnd_mod_write(adg_mod, BRRA, adg->brga); - rsnd_mod_write(adg_mod, BRRB, adg->brgb); dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", (ckr) ? 'B' : 'A', @@ -372,9 +370,16 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct rsnd_mod *adg_mod = rsnd_mod_get(adg); struct clk *clk; int i; + if (enable) { + rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr); + rsnd_mod_write(adg_mod, BRRA, adg->brga); + rsnd_mod_write(adg_mod, BRRB, adg->brgb); + } + for_each_rsnd_clkin(clk, adg, i) { if (enable) { clk_prepare_enable(clk); @@ -485,12 +490,12 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) struct device_node *np = dev->of_node; struct property *prop; u32 ckr, brgx, brga, brgb; - u32 rate, div; u32 req_rate[ADG_HZ_SIZE] = {}; uint32_t count = 0; unsigned long req_Hz[ADG_HZ_SIZE]; int clkout_size; int i, req_size; + int approximate = 0; const char *parent_clk_name = NULL; const char * const *clkout_name; int brg_table[] = { @@ -501,8 +506,8 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) }; ckr = 0; - brga = 2; /* default 1/6 */ - brgb = 2; /* default 1/6 */ + brga = 0xff; /* default */ + brgb = 0xff; /* default */ /* * ADG supports BRRA/BRRB output only @@ -537,17 +542,41 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) * rsnd_adg_ssi_clk_try_start() * rsnd_ssi_master_clk_start() */ + + /* + * [APPROXIMATE] + * + * clk_i (internal clock) can't create accurate rate, it will be approximate rate. + * + * <Note> + * + * clk_i needs x2 of required maximum rate. + * see + * - Minimum division of BRRA/BRRB + * - rsnd_ssi_clk_query() + * + * Sample Settings for TDM 8ch, 32bit width + * + * 8(ch) x 32(bit) x 44100(Hz) x 2<Note> = 22579200 + * 8(ch) x 32(bit) x 48000(Hz) x 2<Note> = 24576000 + * + * clock-frequency = <22579200 24576000>; + */ for_each_rsnd_clkin(clk, adg, i) { + u32 rate, div; + rate = clk_get_rate(clk); if (0 == rate) /* not used */ continue; /* BRGA */ - if (!adg->brg_rate[ADG_HZ_441] && (0 == rate % 44100)) { - div = 6; - if (req_Hz[ADG_HZ_441]) - div = rate / req_Hz[ADG_HZ_441]; + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_441]) * req_Hz[ADG_HZ_441]; + if (!adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441] && (0 == rate % 44100)) { + div = rate / req_Hz[ADG_HZ_441]; brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) { brga = brgx; @@ -555,14 +584,18 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) ckr |= brg_table[i] << 20; if (req_Hz[ADG_HZ_441]) parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; } } /* BRGB */ - if (!adg->brg_rate[ADG_HZ_48] && (0 == rate % 48000)) { - div = 6; - if (req_Hz[ADG_HZ_48]) - div = rate / req_Hz[ADG_HZ_48]; + + if (i == CLKI) + /* see [APPROXIMATE] */ + rate = (clk_get_rate(clk) / req_Hz[ADG_HZ_48]) * req_Hz[ADG_HZ_48]; + if (!adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48] && (0 == rate % 48000)) { + div = rate / req_Hz[ADG_HZ_48]; brgx = rsnd_adg_calculate_brgx(div); if (BRRx_MASK(brgx) == brgx) { brgb = brgx; @@ -570,10 +603,19 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv) ckr |= brg_table[i] << 16; if (req_Hz[ADG_HZ_48]) parent_clk_name = __clk_get_name(clk); + if (i == CLKI) + approximate = 1; } } } + if (!(adg->brg_rate[ADG_HZ_48] && req_Hz[ADG_HZ_48]) && + !(adg->brg_rate[ADG_HZ_441] && req_Hz[ADG_HZ_441])) + goto rsnd_adg_get_clkout_end; + + if (approximate) + dev_info(dev, "It uses CLK_I as approximate rate"); + clkout_name = clkout_name_gen2; clkout_size = ARRAY_SIZE(clkout_name_gen2); if (rsnd_is_gen4(priv)) diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6a522e6dd85a..e29c2fee9521 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1085,17 +1085,6 @@ static u64 rsnd_soc_dai_formats[] = { SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; -static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { - .startup = rsnd_soc_dai_startup, - .shutdown = rsnd_soc_dai_shutdown, - .trigger = rsnd_soc_dai_trigger, - .set_fmt = rsnd_soc_dai_set_fmt, - .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, - .prepare = rsnd_soc_dai_prepare, - .auto_selectable_formats = rsnd_soc_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), -}; - static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct device_node *dai_np) @@ -1260,13 +1249,13 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name return i; } -static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, - int *is_graph) +static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; - struct device_node *dai_node; - struct device_node *ret; + struct device_node *ports, *node; + int nr = 0; + int i = 0; *is_graph = 0; @@ -1274,26 +1263,53 @@ static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, * parse both previous dai (= rcar_sound,dai), and * graph dai (= ports/port) */ - dai_node = of_get_child_by_name(np, RSND_NODE_DAI); - if (dai_node) { - ret = dai_node; - goto of_node_compatible; - } - ret = np; + /* + * Simple-Card + */ + node = of_get_child_by_name(np, RSND_NODE_DAI); + if (!node) + goto audio_graph; - dai_node = of_graph_get_next_endpoint(np, NULL); - if (dai_node) - goto of_node_graph; + of_node_put(node); - return NULL; + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; + + priv->component_dais[i] = of_get_child_count(node); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(node); + break; + } + } + + return nr; + +audio_graph: + /* + * Audio-Graph-Card + */ + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + priv->component_dais[i] = of_graph_get_endpoint_count(ports); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + of_node_put(node); + break; + } + } -of_node_graph: *is_graph = 1; -of_node_compatible: - of_node_put(dai_node); - return ret; + return nr; } @@ -1328,8 +1344,7 @@ static int rsnd_preallocate_pages(struct snd_soc_pcm_runtime *rtd, return 0; } -static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *dai) +static int rsnd_soc_dai_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); int ret; @@ -1355,8 +1370,22 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd, return 0; } +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .pcm_new = rsnd_soc_dai_pcm_new, + .startup = rsnd_soc_dai_startup, + .shutdown = rsnd_soc_dai_shutdown, + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, + .set_tdm_slot = rsnd_soc_set_dai_tdm_slot, + .prepare = rsnd_soc_dai_prepare, + .auto_selectable_formats = rsnd_soc_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(rsnd_soc_dai_formats), +}; + static void __rsnd_dai_probe(struct rsnd_priv *priv, struct device_node *dai_np, + struct device_node *node_np, + uint32_t node_arg, int dai_i) { struct rsnd_dai_stream *io_playback; @@ -1374,10 +1403,16 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); + /* for multi Component */ + rdai->dai_args.np = node_np; + rdai->dai_args.args_count = 1; + rdai->dai_args.args[0] = node_arg; + rdai->priv = priv; drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; - drv->pcm_new = rsnd_pcm_new; + drv->id = dai_i; + drv->dai_args = &rdai->dai_args; io_playback->rdai = rdai; io_capture->rdai = rdai; @@ -1441,21 +1476,15 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, static int rsnd_dai_probe(struct rsnd_priv *priv) { - struct device_node *dai_node; - struct device_node *dai_np; struct snd_soc_dai_driver *rdrv; struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; struct rsnd_dai *rdai; - int nr; + int nr = 0; int is_graph; int dai_i; - dai_node = rsnd_dai_of_node(priv, &is_graph); - if (is_graph) - nr = of_graph_get_endpoint_count(dai_node); - else - nr = of_get_child_count(dai_node); - + nr = rsnd_dai_of_node(priv, &is_graph); if (!nr) return -EINVAL; @@ -1473,26 +1502,42 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) */ dai_i = 0; if (is_graph) { - for_each_endpoint_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); - - rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); - rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + struct device_node *ports; + struct device_node *dai_np; + + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + for_each_endpoint_of_node(ports, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } } else { - for_each_child_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); + struct device_node *node; + struct device_node *dai_np; + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; - rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); - rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + for_each_child_of_node(node, dai_np) { + __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); + rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } } @@ -1922,6 +1967,7 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dai_probe, }; int ret, i; + int ci; /* * init priv data @@ -1958,11 +2004,18 @@ static int rsnd_probe(struct platform_device *pdev) /* * asoc register */ - ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, - priv->daidrv, rsnd_rdai_nr(priv)); - if (ret < 0) { - dev_err(dev, "cannot snd dai register\n"); - goto exit_snd_probe; + ci = 0; + for (i = 0; priv->component_dais[i] > 0; i++) { + int nr = priv->component_dais[i]; + + ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv + ci, nr); + if (ret < 0) { + dev_err(dev, "cannot snd component register\n"); + goto exit_snd_probe; + } + + ci += nr; } pm_runtime_enable(dev); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 239705d52517..43c0d675cc34 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -545,6 +545,7 @@ struct rsnd_dai { struct rsnd_dai_stream capture; struct rsnd_priv *priv; struct snd_pcm_hw_constraint_list constraint; + struct of_phandle_args dai_args; int max_channels; /* 2ch - 16ch */ int ssi_lane; /* 1lane - 4lane */ @@ -702,6 +703,9 @@ struct rsnd_priv { struct snd_soc_dai_driver *daidrv; struct rsnd_dai *rdai; int rdai_nr; + +#define RSND_MAX_COMPONENT 3 + int component_dais[RSND_MAX_COMPONENT]; }; #define rsnd_priv_to_pdev(priv) ((priv)->pdev) diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c index 142476f1396f..6d693b2ad5a3 100644 --- a/sound/soc/soc-acpi.c +++ b/sound/soc/soc-acpi.c @@ -125,5 +125,78 @@ struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) } EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); +#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ + SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) + +/* Check if all Slaves defined on the link can be found */ +bool snd_soc_acpi_sdw_link_slaves_found(struct device *dev, + const struct snd_soc_acpi_link_adr *link, + struct sdw_extended_slave_id *ids, + int num_slaves) +{ + unsigned int part_id, link_id, unique_id, mfg_id, version; + int i, j, k; + + for (i = 0; i < link->num_adr; i++) { + u64 adr = link->adr_d[i].adr; + int reported_part_count = 0; + + mfg_id = SDW_MFG_ID(adr); + part_id = SDW_PART_ID(adr); + link_id = SDW_DISCO_LINK_ID(adr); + version = SDW_VERSION(adr); + + for (j = 0; j < num_slaves; j++) { + /* find out how many identical parts were reported on that link */ + if (ids[j].link_id == link_id && + ids[j].id.part_id == part_id && + ids[j].id.mfg_id == mfg_id && + ids[j].id.sdw_version == version) + reported_part_count++; + } + + for (j = 0; j < num_slaves; j++) { + int expected_part_count = 0; + + if (ids[j].link_id != link_id || + ids[j].id.part_id != part_id || + ids[j].id.mfg_id != mfg_id || + ids[j].id.sdw_version != version) + continue; + + /* find out how many identical parts are expected */ + for (k = 0; k < link->num_adr; k++) { + u64 adr2 = link->adr_d[k].adr; + + if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) + expected_part_count++; + } + + if (reported_part_count == expected_part_count) { + /* + * we have to check unique id + * if there is more than one + * Slave on the link + */ + unique_id = SDW_UNIQUE_ID(adr); + if (reported_part_count == 1 || + ids[j].id.unique_id == unique_id) { + dev_dbg(dev, "found part_id %#x at link %d\n", part_id, link_id); + break; + } + } else { + dev_dbg(dev, "part_id %#x reported %d expected %d on link %d, skipping\n", + part_id, reported_part_count, expected_part_count, link_id); + } + } + if (j == num_slaves) { + dev_dbg(dev, "Slave part_id %#x not found\n", part_id); + return false; + } + } + return true; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_sdw_link_slaves_found); + MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("ALSA SoC ACPI module"); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a5b96c17633a..cc442c52cdea 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -942,7 +942,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, * component list. */ if (!soc_find_component(dlc)) - goto component_not_find; + goto component_not_found; } /* Platform check */ @@ -963,7 +963,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, * component list. */ if (!soc_find_component(dlc)) - goto component_not_find; + goto component_not_found; } /* CPU check */ @@ -988,7 +988,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, * Defer card registration if Component is not added */ if (!soc_find_component(dlc)) - goto component_not_find; + goto component_not_found; } } @@ -1002,8 +1002,8 @@ component_empty: dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name); return -EINVAL; -component_not_find: - dev_err(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name); +component_not_found: + dev_dbg(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name); return -EPROBE_DEFER; dai_empty: diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 02dd64dea179..3f33f0630ad8 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -424,6 +424,9 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, { int ret = 0; + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return 0; + if (dai->driver->ops && dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); @@ -439,6 +442,9 @@ void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, int rollback) { + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return; + if (rollback && !soc_dai_mark_match(dai, substream, startup)) return; @@ -454,8 +460,9 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd, int num) { int ret = -ENOTSUPP; - if (dai->driver->compress_new) - ret = dai->driver->compress_new(rtd, num); + if (dai->driver->ops && + dai->driver->ops->compress_new) + ret = dai->driver->ops->compress_new(rtd, num); return soc_dai_ret(dai, ret); } @@ -539,19 +546,20 @@ int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order) int i; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->probe_order != order) - continue; - if (dai->probed) continue; - if (dai->driver->probe) { - int ret = dai->driver->probe(dai); + if (dai->driver->ops) { + if (dai->driver->ops->probe_order != order) + continue; - if (ret < 0) - return soc_dai_ret(dai, ret); - } + if (dai->driver->ops->probe) { + int ret = dai->driver->ops->probe(dai); + if (ret < 0) + return soc_dai_ret(dai, ret); + } + } dai->probed = 1; } @@ -564,16 +572,19 @@ int snd_soc_pcm_dai_remove(struct snd_soc_pcm_runtime *rtd, int order) int i, r, ret = 0; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->remove_order != order) + if (!dai->probed) continue; - if (dai->probed && - dai->driver->remove) { - r = dai->driver->remove(dai); - if (r < 0) - ret = r; /* use last error */ - } + if (dai->driver->ops) { + if (dai->driver->ops->remove_order != order) + continue; + if (dai->driver->ops->remove) { + r = dai->driver->ops->remove(dai); + if (r < 0) + ret = r; /* use last error */ + } + } dai->probed = 0; } @@ -586,8 +597,9 @@ int snd_soc_pcm_dai_new(struct snd_soc_pcm_runtime *rtd) int i; for_each_rtd_dais(rtd, i, dai) { - if (dai->driver->pcm_new) { - int ret = dai->driver->pcm_new(rtd, dai); + if (dai->driver->ops && + dai->driver->ops->pcm_new) { + int ret = dai->driver->ops->pcm_new(rtd, dai); if (ret < 0) return soc_dai_ret(dai, ret); } @@ -603,6 +615,8 @@ int snd_soc_pcm_dai_prepare(struct snd_pcm_substream *substream) int i, ret; for_each_rtd_dais(rtd, i, dai) { + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + continue; if (dai->driver->ops && dai->driver->ops->prepare) { ret = dai->driver->ops->prepare(substream, dai); @@ -619,6 +633,9 @@ static int soc_dai_trigger(struct snd_soc_dai *dai, { int ret = 0; + if (!snd_soc_dai_stream_valid(dai, substream->stream)) + return 0; + if (dai->driver->ops && dai->driver->ops->trigger) ret = dai->driver->ops->trigger(substream, cmd, dai); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3091e8160bad..f07e83678373 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -916,6 +916,8 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, return -EINVAL; } } + if (w->no_wname_in_kcontrol_name) + wname_in_long_name = false; if (wname_in_long_name && kcname_in_long_name) { /* diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index f951acb2ce36..b2cc13b9c77b 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,7 +37,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) struct snd_soc_jack_pin *pin; unsigned int sync = 0; - if (!jack) + if (!jack || !jack->jack) return; trace_snd_soc_jack_report(jack, mask, status); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 3592ff09f692..2362c282ec8b 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1560,6 +1560,10 @@ static void set_dai_flags(struct snd_soc_dai_driver *dai_drv, 1 : 0; } +static const struct snd_soc_dai_ops tplg_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static int soc_tplg_dai_create(struct soc_tplg *tplg, struct snd_soc_tplg_pcm *pcm) { @@ -1601,7 +1605,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg, } if (pcm->compress) - dai_drv->compress_new = snd_soc_new_compress; + dai_drv->ops = &tplg_dai_ops; /* pass control to component driver for optional further init */ ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig index 7dbc8df5cfe6..f2faa08f0c0e 100644 --- a/sound/soc/sof/amd/Kconfig +++ b/sound/soc/sof/amd/Kconfig @@ -2,7 +2,7 @@ # This file is provided under a dual BSD/GPLv2 license. When using or # redistributing this file, you may do so under either license. # -# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. config SND_SOC_SOF_AMD_TOPLEVEL tristate "SOF support for AMD audio DSPs" @@ -34,6 +34,16 @@ config SND_SOC_SOF_AMD_RENOIR help Select this option for SOF support on AMD Renoir platform +config SND_SOC_SOF_AMD_VANGOGH + tristate "SOF support for VANGOGH" + depends on SND_SOC_SOF_PCI + select SND_SOC_SOF_AMD_COMMON + help + Select this option for SOF support + on AMD Vangogh platform. + Say Y if you want to enable SOF on Vangogh. + If unsure select "N". + config SND_SOC_SOF_AMD_REMBRANDT tristate "SOF support for REMBRANDT" depends on SND_SOC_SOF_PCI diff --git a/sound/soc/sof/amd/Makefile b/sound/soc/sof/amd/Makefile index ef9f7df4e379..f3b375e67a6f 100644 --- a/sound/soc/sof/amd/Makefile +++ b/sound/soc/sof/amd/Makefile @@ -2,13 +2,15 @@ # This file is provided under a dual BSD/GPLv2 license. When using or # redistributing this file, you may do so under either license. # -# Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +# Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. snd-sof-amd-acp-objs := acp.o acp-loader.o acp-ipc.o acp-pcm.o acp-stream.o acp-trace.o acp-common.o snd-sof-amd-acp-$(CONFIG_SND_SOC_SOF_ACP_PROBES) = acp-probes.o snd-sof-amd-renoir-objs := pci-rn.o renoir.o snd-sof-amd-rembrandt-objs := pci-rmb.o rembrandt.o +snd-sof-amd-vangogh-objs := pci-vangogh.o vangogh.o obj-$(CONFIG_SND_SOC_SOF_AMD_COMMON) += snd-sof-amd-acp.o obj-$(CONFIG_SND_SOC_SOF_AMD_RENOIR) +=snd-sof-amd-renoir.o obj-$(CONFIG_SND_SOC_SOF_AMD_REMBRANDT) +=snd-sof-amd-rembrandt.o +obj-$(CONFIG_SND_SOC_SOF_AMD_VANGOGH) +=snd-sof-amd-vangogh.o diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h index 920155dee819..a913f1cc4c80 100644 --- a/sound/soc/sof/amd/acp-dsp-offset.h +++ b/sound/soc/sof/amd/acp-dsp-offset.h @@ -3,7 +3,7 @@ * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * - * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. * * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> */ @@ -49,27 +49,33 @@ #define ACP_CONTROL 0x1004 #define ACP3X_I2S_PIN_CONFIG 0x1400 +#define ACP5X_I2S_PIN_CONFIG 0x1400 #define ACP6X_I2S_PIN_CONFIG 0x1440 /* Registers offsets from ACP_PGFSM block */ #define ACP3X_PGFSM_BASE 0x141C +#define ACP5X_PGFSM_BASE 0x1424 #define ACP6X_PGFSM_BASE 0x1024 #define PGFSM_CONTROL_OFFSET 0x0 #define PGFSM_STATUS_OFFSET 0x4 #define ACP3X_CLKMUX_SEL 0x1424 +#define ACP5X_CLKMUX_SEL 0x142C #define ACP6X_CLKMUX_SEL 0x102C /* Registers from ACP_INTR block */ #define ACP3X_EXT_INTR_STAT 0x1808 +#define ACP5X_EXT_INTR_STAT 0x1808 #define ACP6X_EXT_INTR_STAT 0x1A0C #define ACP3X_DSP_SW_INTR_BASE 0x1814 +#define ACP5X_DSP_SW_INTR_BASE 0x1814 #define ACP6X_DSP_SW_INTR_BASE 0x1808 #define DSP_SW_INTR_CNTL_OFFSET 0x0 #define DSP_SW_INTR_STAT_OFFSET 0x4 #define DSP_SW_INTR_TRIG_OFFSET 0x8 #define ACP_ERROR_STATUS 0x18C4 #define ACP3X_AXI2DAGB_SEM_0 0x1880 +#define ACP5X_AXI2DAGB_SEM_0 0x1884 #define ACP6X_AXI2DAGB_SEM_0 0x1874 /* Registers from ACP_SHA block */ @@ -81,6 +87,7 @@ #define ACP_SHA_DMA_CMD_STS 0x1CC0 #define ACP_SHA_DMA_ERR_STATUS 0x1CC4 #define ACP_SHA_TRANSFER_BYTE_CNT 0x1CC8 +#define ACP_SHA_DMA_INCLUDE_HDR 0x1CCC #define ACP_SHA_PSP_ACK 0x1C74 #define ACP_SCRATCH_REG_0 0x10000 diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 81a2c096a185..fcb54f545fea 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -170,6 +170,8 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { snd_sof_dsp_panic(sdev, sdev->dsp_box.offset + sizeof(status), true); + status = 0; + acp_mailbox_write(sdev, sdev->dsp_box.offset, &status, sizeof(status)); return IRQ_HANDLED; } snd_sof_ipc_msgs_rx(sdev); @@ -199,6 +201,8 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context) acp_mailbox_read(sdev, sdev->debug_box.offset, &status, sizeof(u32)); if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) { snd_sof_dsp_panic(sdev, sdev->dsp_oops_offset, true); + status = 0; + acp_mailbox_write(sdev, sdev->debug_box.offset, &status, sizeof(status)); return IRQ_HANDLED; } diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c index a4bce5a3ae48..a427673cfb03 100644 --- a/sound/soc/sof/amd/acp-loader.c +++ b/sound/soc/sof/amd/acp-loader.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. // // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> @@ -158,7 +158,11 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) int ret; adata = sdev->pdata->hw_pdata; - size_fw = adata->fw_bin_size; + + if (adata->signed_fw_image) + size_fw = adata->fw_bin_size - ACP_FIRMWARE_SIGNATURE; + else + size_fw = adata->fw_bin_size; page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; adata->fw_bin_page_count = page_count; @@ -203,6 +207,7 @@ EXPORT_SYMBOL_NS(acp_dsp_pre_fw_run, SND_SOC_SOF_AMD_COMMON); int acp_sof_dsp_run(struct snd_sof_dev *sdev) { + struct acp_dev_data *adata = sdev->pdata->hw_pdata; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); int val; @@ -211,7 +216,7 @@ int acp_sof_dsp_run(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "ACP_DSP0_RUNSTALL : 0x%0x\n", val); /* Some platforms won't support fusion DSP,keep offset zero for no support */ - if (desc->fusion_dsp_offset) { + if (desc->fusion_dsp_offset && adata->enable_fw_debug) { snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset, ACP_DSP_RUN); val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->fusion_dsp_offset); dev_dbg(sdev->dev, "ACP_DSP0_FUSION_RUNSTALL : 0x%0x\n", val); @@ -219,3 +224,34 @@ int acp_sof_dsp_run(struct snd_sof_dev *sdev) return 0; } EXPORT_SYMBOL_NS(acp_sof_dsp_run, SND_SOC_SOF_AMD_COMMON); + +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct acp_dev_data *adata = plat_data->hw_pdata; + int ret; + + ret = request_firmware(&sdev->basefw.fw, adata->fw_code_bin, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "sof signed firmware code bin is missing\n"); + return ret; + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_code_bin); + } + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_IRAM, 0, + (void *)sdev->basefw.fw->data, sdev->basefw.fw->size); + + ret = request_firmware(&adata->fw_dbin, adata->fw_data_bin, sdev->dev); + if (ret < 0) { + dev_err(sdev->dev, "sof signed firmware data bin is missing\n"); + return ret; + + } else { + dev_dbg(sdev->dev, "request_firmware %s successful\n", adata->fw_data_bin); + } + + ret = snd_sof_dsp_block_write(sdev, SOF_FW_BLK_TYPE_DRAM, 0, + (void *)adata->fw_dbin->data, adata->fw_dbin->size); + return ret; +} +EXPORT_SYMBOL_NS(acp_sof_load_signed_firmware, SND_SOC_SOF_AMD_COMMON); diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index c450931ae77e..19a801908b56 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -3,7 +3,7 @@ // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // -// Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. // // Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com> // Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> @@ -20,6 +20,26 @@ #include "acp.h" #include "acp-dsp-offset.h" +#define SECURED_FIRMWARE 1 + +static bool enable_fw_debug; +module_param(enable_fw_debug, bool, 0444); +MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug"); + +const struct dmi_system_id acp_sof_quirk_table[] = { + { + /* Valve Jupiter device */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), + }, + .driver_data = (void *)SECURED_FIRMWARE, + }, + {} +}; +EXPORT_SYMBOL_GPL(acp_sof_quirk_table); + static int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) { pci_write_config_dword(dev, 0x60, smn_addr); @@ -235,6 +255,9 @@ int configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr, } } + if (adata->signed_fw_image) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER); + snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr); snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length); @@ -318,14 +341,7 @@ static irqreturn_t acp_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); - unsigned int val, count = ACP_HW_SEM_RETRY_COUNT; - - val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat); - if (val & ACP_SHA_STAT) { - /* Clear SHA interrupt raised by PSP */ - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, val); - return IRQ_HANDLED; - } + unsigned int count = ACP_HW_SEM_RETRY_COUNT; while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) { /* Wait until acquired HW Semaphore lock or timeout */ @@ -351,9 +367,9 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id) unsigned int val; val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET); - if (val) { - val |= ACP_DSP_TO_HOST_IRQ; - snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val); + if (val & ACP_DSP_TO_HOST_IRQ) { + snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, + ACP_DSP_TO_HOST_IRQ); return IRQ_WAKE_THREAD; } @@ -407,7 +423,12 @@ static int acp_reset(struct snd_sof_dev *sdev) if (ret < 0) dev_err(sdev->dev, "timeout in releasing reset\n"); - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); + if (desc->acp_clkmux_sel) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); + + if (desc->ext_intr_enb) + snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01); + return ret; } @@ -445,7 +466,6 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_resume(struct snd_sof_dev *sdev) { - const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); int ret; ret = acp_init(sdev); @@ -453,20 +473,17 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev) dev_err(sdev->dev, "ACP Init failed\n"); return ret; } - - snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK); - - ret = acp_memory_init(sdev); - - return ret; + return acp_memory_init(sdev); } EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON); int amd_sof_acp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); + struct snd_sof_pdata *plat_data = sdev->pdata; struct acp_dev_data *adata; const struct sof_amd_acp_desc *chip; + const struct dmi_system_id *dmi_id; unsigned int addr; int ret; @@ -527,6 +544,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev) sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size; sdev->debug_box.size = BOX_SIZE_1024; + adata->signed_fw_image = false; + dmi_id = dmi_first_match(acp_sof_quirk_table); + if (dmi_id && dmi_id->driver_data) { + adata->fw_code_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-code.bin", + plat_data->fw_filename_prefix, + chip->name); + adata->fw_data_bin = kasprintf(GFP_KERNEL, "%s/sof-%s-data.bin", + plat_data->fw_filename_prefix, + chip->name); + adata->signed_fw_image = dmi_id->driver_data; + + dev_dbg(sdev->dev, "fw_code_bin:%s, fw_data_bin:%s\n", adata->fw_code_bin, + adata->fw_data_bin); + } + adata->enable_fw_debug = enable_fw_debug; acp_memory_init(sdev); acp_dsp_stream_init(sdev); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 72fa0af971f0..4dcceb764769 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -3,7 +3,7 @@ * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * - * Copyright(c) 2021 Advanced Micro Devices, Inc. All rights reserved. + * Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. * * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> */ @@ -11,6 +11,8 @@ #ifndef __SOF_AMD_ACP_H #define __SOF_AMD_ACP_H +#include <linux/dmi.h> + #include "../sof-priv.h" #include "../sof-audio.h" @@ -32,6 +34,7 @@ #define ACP_DSP_INTR_EN_MASK 0x00000001 #define ACP3X_SRAM_PTE_OFFSET 0x02050000 +#define ACP5X_SRAM_PTE_OFFSET 0x02050000 #define ACP6X_SRAM_PTE_OFFSET 0x03800000 #define PAGE_SIZE_4K_ENABLE 0x2 #define ACP_PAGE_SIZE 0x1000 @@ -40,6 +43,7 @@ #define DSP_FW_RUN_ENABLE 0x01 #define ACP_SHA_RUN 0x01 #define ACP_SHA_RESET 0x02 +#define ACP_SHA_HEADER 0x01 #define ACP_DMA_CH_RST 0x01 #define ACP_DMA_CH_GRACEFUL_RST_EN 0x10 #define ACP_ATU_CACHE_INVALID 0x01 @@ -56,9 +60,11 @@ #define ACP_DSP_TO_HOST_IRQ 0x04 #define ACP_RN_PCI_ID 0x01 +#define ACP_VANGOGH_PCI_ID 0x50 #define ACP_RMB_PCI_ID 0x6F #define HOST_BRIDGE_CZN 0x1630 +#define HOST_BRIDGE_VGH 0x1645 #define HOST_BRIDGE_RMB 0x14B5 #define ACP_SHA_STAT 0x8000 #define ACP_PSP_TIMEOUT_US 1000000 @@ -79,6 +85,8 @@ #define SRAM1_SIZE 0x13A000 #define PROBE_STATUS_BIT BIT(31) +#define ACP_FIRMWARE_SIGNATURE 0x100 + enum clock_source { ACP_CLOCK_96M = 0, ACP_CLOCK_48M, @@ -163,8 +171,10 @@ struct acp_dsp_stream { struct sof_amd_acp_desc { unsigned int rev; + const char *name; unsigned int host_bridge_id; u32 pgfsm_base; + u32 ext_intr_enb; u32 ext_intr_stat; u32 dsp_intr_base; u32 sram_pte_offset; @@ -177,20 +187,25 @@ struct sof_amd_acp_desc { /* Common device data struct for ACP devices */ struct acp_dev_data { struct snd_sof_dev *dev; + const struct firmware *fw_dbin; /* DMIC device */ struct platform_device *dmic_dev; unsigned int fw_bin_size; unsigned int fw_data_bin_size; + const char *fw_code_bin; + const char *fw_data_bin; u32 fw_bin_page_count; dma_addr_t sha_dma_addr; u8 *bin_buf; dma_addr_t dma_addr; u8 *data_buf; + bool signed_fw_image; struct dma_descriptor dscr_info[ACP_MAX_DESC]; struct acp_dsp_stream stream_buf[ACP_MAX_STREAM]; struct acp_dsp_stream *dtrace_stream; struct pci_dev *smn_dev; struct acp_dsp_stream *probe_stream; + bool enable_fw_debug; }; void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes); @@ -210,6 +225,7 @@ int amd_sof_acp_remove(struct snd_sof_dev *sdev); /* DSP Loader callbacks */ int acp_sof_dsp_run(struct snd_sof_dev *sdev); int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int acp_sof_load_signed_firmware(struct snd_sof_dev *sdev); int acp_get_bar_index(struct snd_sof_dev *sdev, u32 type); /* Block IO callbacks */ @@ -253,6 +269,8 @@ extern struct snd_sof_dsp_ops sof_acp_common_ops; extern struct snd_sof_dsp_ops sof_renoir_ops; int sof_renoir_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_vangogh_ops; +int sof_vangogh_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_rembrandt_ops; int sof_rembrandt_ops_init(struct snd_sof_dev *sdev); @@ -282,4 +300,6 @@ static inline const struct sof_amd_acp_desc *get_chip_info(struct snd_sof_pdata int acp_probes_register(struct snd_sof_dev *sdev); void acp_probes_unregister(struct snd_sof_dev *sdev); +extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[]; +extern const struct dmi_system_id acp_sof_quirk_table[]; #endif diff --git a/sound/soc/sof/amd/pci-vangogh.c b/sound/soc/sof/amd/pci-vangogh.c new file mode 100644 index 000000000000..d8be42fbcb6d --- /dev/null +++ b/sound/soc/sof/amd/pci-vangogh.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com> + +/*. + * PCI interface for Vangogh ACP device + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <sound/sof.h> +#include <sound/soc-acpi.h> + +#include "../ops.h" +#include "../sof-pci-dev.h" +#include "../../amd/mach-config.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define ACP5X_FUTURE_REG_ACLK_0 0x1864 + +static const struct sof_amd_acp_desc vangogh_chip_info = { + .rev = 5, + .name = "vangogh", + .host_bridge_id = HOST_BRIDGE_VGH, + .pgfsm_base = ACP5X_PGFSM_BASE, + .ext_intr_stat = ACP5X_EXT_INTR_STAT, + .dsp_intr_base = ACP5X_DSP_SW_INTR_BASE, + .sram_pte_offset = ACP5X_SRAM_PTE_OFFSET, + .hw_semaphore_offset = ACP5X_AXI2DAGB_SEM_0, + .acp_clkmux_sel = ACP5X_CLKMUX_SEL, + .probe_reg_offset = ACP5X_FUTURE_REG_ACLK_0, +}; + +static const struct sof_dev_desc vangogh_desc = { + .machines = snd_soc_acpi_amd_vangogh_sof_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &vangogh_chip_info, + .ipc_supported_mask = BIT(SOF_IPC), + .ipc_default = SOF_IPC, + .default_fw_path = { + [SOF_IPC] = "amd/sof", + }, + .default_tplg_path = { + [SOF_IPC] = "amd/sof-tplg", + }, + .default_fw_filename = { + [SOF_IPC] = "sof-vangogh.ri", + }, + .nocodec_tplg_filename = "sof-acp.tplg", + .ops = &sof_vangogh_ops, + .ops_init = sof_vangogh_ops_init, +}; + +static int acp_pci_vgh_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + unsigned int flag; + + if (pci->revision != ACP_VANGOGH_PCI_ID) + return -ENODEV; + + flag = snd_amd_acp_find_config(pci); + if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) + return -ENODEV; + + return sof_pci_probe(pci, pci_id); +}; + +static void acp_pci_vgh_remove(struct pci_dev *pci) +{ + sof_pci_remove(pci); +} + +/* PCI IDs */ +static const struct pci_device_id vgh_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID), + .driver_data = (unsigned long)&vangogh_desc}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, vgh_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_amd_vgh_driver = { + .name = KBUILD_MODNAME, + .id_table = vgh_pci_ids, + .probe = acp_pci_vgh_probe, + .remove = acp_pci_vgh_remove, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_amd_vgh_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c new file mode 100644 index 000000000000..de15d21aa6d9 --- /dev/null +++ b/sound/soc/sof/amd/vangogh.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Advanced Micro Devices, Inc. +// +// Authors: Venkata Prasad Potturu <venkataprasad.potturu@amd.com> + +/* + * Hardware interface for Audio DSP on Vangogh platform + */ + +#include <linux/platform_device.h> +#include <linux/module.h> + +#include "../ops.h" +#include "../sof-audio.h" +#include "acp.h" +#include "acp-dsp-offset.h" + +#define I2S_HS_INSTANCE 0 +#define I2S_BT_INSTANCE 1 +#define I2S_SP_INSTANCE 2 +#define PDM_DMIC_INSTANCE 3 +#define I2S_HS_VIRTUAL_INSTANCE 4 + +static struct snd_soc_dai_driver vangogh_sof_dai[] = { + [I2S_HS_INSTANCE] = { + .id = I2S_HS_INSTANCE, + .name = "acp-sof-hs", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_BT_INSTANCE] = { + .id = I2S_BT_INSTANCE, + .name = "acp-sof-bt", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S BT controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_SP_INSTANCE] = { + .id = I2S_SP_INSTANCE, + .name = "acp-sof-sp", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S SP controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [PDM_DMIC_INSTANCE] = { + .id = PDM_DMIC_INSTANCE, + .name = "acp-sof-dmic", + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 4, + .rate_min = 8000, + .rate_max = 48000, + }, + }, + + [I2S_HS_VIRTUAL_INSTANCE] = { + .id = I2S_HS_VIRTUAL_INSTANCE, + .name = "acp-sof-hs-virtual", + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, + /* Supporting only stereo for I2S HS-Virtual controller capture */ + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + }, +}; + +/* Vangogh ops */ +struct snd_sof_dsp_ops sof_vangogh_ops; +EXPORT_SYMBOL_NS(sof_vangogh_ops, SND_SOC_SOF_AMD_COMMON); + +int sof_vangogh_ops_init(struct snd_sof_dev *sdev) +{ + const struct dmi_system_id *dmi_id; + + /* common defaults */ + memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); + + sof_vangogh_ops.drv = vangogh_sof_dai; + sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); + + dmi_id = dmi_first_match(acp_sof_quirk_table); + if (dmi_id && dmi_id->driver_data) + sof_vangogh_ops.load_firmware = acp_sof_load_signed_firmware; + + return 0; +} + +MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON); +MODULE_DESCRIPTION("VANGOGH SOF Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 69c1a370d3b6..9d0107932117 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -262,6 +262,22 @@ config SND_SOC_SOF_METEORLAKE Say Y if you have such a device. If unsure select "N". +config SND_SOC_SOF_INTEL_LNL + tristate + select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + select SND_SOC_SOF_INTEL_IPC4 + +config SND_SOC_SOF_LUNARLAKE + tristate "SOF support for Lunarlake" + default SND_SOC_SOF_PCI + select SND_SOC_SOF_INTEL_LNL + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Lunarlake processors. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_SOF_HDA_COMMON tristate select SND_SOC_SOF_INTEL_COMMON diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index fdb463c12e91..030574dbc998 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -7,7 +7,7 @@ snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-dai-ops.o hda-bus.o \ skl.o hda-loader-skl.o \ - apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o + apl.o cnl.o tgl.o icl.o mtl.o lnl.o hda-common-ops.o snd-sof-intel-hda-mlink-objs := hda-mlink.o @@ -31,6 +31,7 @@ snd-sof-pci-intel-cnl-objs := pci-cnl.o snd-sof-pci-intel-icl-objs := pci-icl.o snd-sof-pci-intel-tgl-objs := pci-tgl.o snd-sof-pci-intel-mtl-objs := pci-mtl.o +snd-sof-pci-intel-lnl-objs := pci-lnl.o obj-$(CONFIG_SND_SOC_SOF_MERRIFIELD) += snd-sof-pci-intel-tng.o obj-$(CONFIG_SND_SOC_SOF_INTEL_SKL) += snd-sof-pci-intel-skl.o @@ -39,3 +40,4 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_CNL) += snd-sof-pci-intel-cnl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_ICL) += snd-sof-pci-intel-icl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TGL) += snd-sof-pci-intel-tgl.o obj-$(CONFIG_SND_SOC_SOF_INTEL_MTL) += snd-sof-pci-intel-mtl.o +obj-$(CONFIG_SND_SOC_SOF_INTEL_LNL) += snd-sof-pci-intel-lnl.o diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index a95222e53ecf..c6fbf4285262 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -466,6 +466,7 @@ const struct sof_intel_dsp_desc cnl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -501,6 +502,7 @@ const struct sof_intel_dsp_desc jsl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f33051eac1c0..494ced2b746e 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -7,6 +7,7 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> +#include <sound/hda-mlink.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> #include "../ipc4-priv.h" @@ -144,9 +145,17 @@ static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; struct hdac_ext_stream *hext_stream; - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + /* only allocate a stream_tag for the first DAI in the dailink */ + dai = asoc_rtd_to_cpu(rtd, 0); + if (dai == cpu_dai) + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + else + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream) return NULL; @@ -159,9 +168,14 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + /* only release a stream_tag for the first DAI in the dailink */ + dai = asoc_rtd_to_cpu(rtd, 0); + if (dai == cpu_dai) + snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); - snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); } static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, @@ -219,6 +233,77 @@ static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev, return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); } +static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int format_val; + + format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + params_format(params), + params_physical_width(params), + 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), params_channels(params), params_format(params)); + + return format_val; +} + +static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int format_val; + snd_pcm_format_t format; + unsigned int channels; + unsigned int width; + + channels = params_channels(params); + format = params_format(params); + width = params_physical_width(params); + + if (format == SNDRV_PCM_FORMAT_S16_LE) { + format = SNDRV_PCM_FORMAT_S32_LE; + channels /= 2; + width = 32; + } + + format_val = snd_hdac_calc_stream_format(params_rate(params), channels, + format, + width, + 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), channels, format); + + return format_val; +} + +static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_ssp_get_hlink(bus); +} + +static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_dmic_get_hlink(bus); +} + +static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_sdw_get_hlink(bus); +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -234,6 +319,9 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + if (pipe_widget->instance_id < 0) + return 0; + mutex_lock(&ipc4_data->pipeline_state_mutex); switch (cmd) { @@ -297,6 +385,9 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + if (pipe_widget->instance_id < 0) + return 0; + mutex_lock(&ipc4_data->pipeline_state_mutex); switch (cmd) { @@ -343,6 +434,28 @@ out: return ret; } +static struct hdac_ext_stream *sdw_hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dai *dai = swidget->private; + struct sof_ipc4_copier *ipc4_copier = dai->private; + struct sof_ipc4_alh_configuration_blob *blob; + + blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; + + /* + * Starting with ACE_2_0, re-setting the device_count is mandatory to avoid using + * the multi-gateway firmware configuration. The DMA hardware can take care of + * multiple links without needing any firmware assistance + */ + blob->alh_cfg.device_count = 1; + + return hda_ipc4_get_hext_stream(sdev, cpu_dai, substream); +} + static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -357,6 +470,45 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .get_hlink = hda_get_hlink, }; +static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = { + .get_hext_stream = hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = ssp_get_hlink, +}; + +static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = { + .get_hext_stream = hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = dmic_calc_stream_format, + .get_hlink = dmic_get_hlink, +}; + +static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = { + .get_hext_stream = sdw_hda_ipc4_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .pre_trigger = hda_ipc4_pre_trigger, + .trigger = hda_trigger, + .post_trigger = hda_ipc4_post_trigger, + .calc_stream_format = generic_calc_stream_format, + .get_hlink = sdw_get_hlink, +}; + static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .get_hext_stream = hda_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, @@ -468,8 +620,13 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg case SOF_INTEL_IPC4: { struct sof_ipc4_copier *ipc4_copier = sdai->private; + const struct sof_intel_dsp_desc *chip; - if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { + chip = get_chip_info(sdev->pdata); + + switch (ipc4_copier->dai_type) { + case SOF_DAI_INTEL_HDA: + { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; @@ -478,6 +635,22 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg return &hda_ipc4_dma_ops; } + case SOF_DAI_INTEL_SSP: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &ssp_ipc4_dma_ops; + case SOF_DAI_INTEL_DMIC: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &dmic_ipc4_dma_ops; + case SOF_DAI_INTEL_ALH: + if (chip->hw_ip_version < SOF_INTEL_ACE_2_0) + return NULL; + return &sdw_ipc4_dma_ops; + + default: + break; + } break; } default: diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 863865f3d77e..f3cefd866081 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -10,6 +10,8 @@ #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> +#include <sound/hda-mlink.h> +#include <sound/hda_register.h> #include <sound/intel-nhlt.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> @@ -329,6 +331,175 @@ static const struct snd_soc_dai_ops hda_dai_ops = { #endif +static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dai *sdai = swidget->private; + struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private; + + return ipc4_copier; +} + +static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct sof_ipc4_dma_config_tlv *dma_config_tlv; + const struct hda_dai_widget_dma_ops *ops; + struct sof_ipc4_dma_config *dma_config; + struct sof_ipc4_copier *ipc4_copier; + struct hdac_ext_stream *hext_stream; + struct hdac_stream *hstream; + struct snd_sof_dev *sdev; + int stream_id; + int ret; + + ops = hda_dai_get_ops(substream, cpu_dai); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + /* use HDaudio stream handling */ + ret = hda_dai_hw_params(substream, params, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret); + return ret; + } + + /* get stream_id */ + sdev = widget_to_sdev(w); + hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); + + if (!hext_stream) { + dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__); + return -ENODEV; + } + + hstream = &hext_stream->hstream; + stream_id = hstream->stream_tag; + + if (!stream_id) { + dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__); + return -ENODEV; + } + + /* configure TLV */ + ipc4_copier = widget_to_copier(w); + + dma_config_tlv = &ipc4_copier->dma_config_tlv; + dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID; + /* dma_config_priv_size is zero */ + dma_config_tlv->length = sizeof(dma_config_tlv->dma_config); + + dma_config = &dma_config_tlv->dma_config; + + dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA; + dma_config->pre_allocated_by_host = 1; + dma_config->dma_channel_id = stream_id - 1; + dma_config->stream_id = stream_id; + dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */ + dma_config->dma_priv_config_size = 0; + + return 0; +} + +static int non_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int stream = substream->stream; + + return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); +} + +static const struct snd_soc_dai_ops ssp_dai_ops = { + .hw_params = non_hda_dai_hw_params, + .hw_free = hda_dai_hw_free, + .trigger = hda_dai_trigger, + .prepare = non_hda_dai_prepare, +}; + +static const struct snd_soc_dai_ops dmic_dai_ops = { + .hw_params = non_hda_dai_hw_params, + .hw_free = hda_dai_hw_free, + .trigger = hda_dai_trigger, + .prepare = non_hda_dai_prepare, +}; + +int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, + int link_id) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + const struct hda_dai_widget_dma_ops *ops; + struct hdac_ext_stream *hext_stream; + struct snd_sof_dev *sdev; + int ret; + + ret = non_hda_dai_hw_params(substream, params, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret); + return ret; + } + + ops = hda_dai_get_ops(substream, cpu_dai); + sdev = widget_to_sdev(w); + hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); + + if (!hext_stream) + return -ENODEV; + + /* in the case of SoundWire we need to program the PCMSyCM registers */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, + GENMASK(params_channels(params) - 1, 0), + hdac_stream(hext_stream)->stream_tag, + substream->stream); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, + int link_id) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + struct snd_sof_dev *sdev; + int ret; + + ret = hda_dai_hw_free(substream, cpu_dai); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret); + return ret; + } + + sdev = widget_to_sdev(w); + + /* in the case of SoundWire we need to reset the PCMSyCM registers */ + ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, + 0, 0, substream->stream); + if (ret < 0) { + dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + return hda_dai_trigger(substream, cmd, cpu_dai); +} + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; @@ -383,7 +554,42 @@ static int hda_dai_suspend(struct hdac_bus *bus) return 0; } -#endif +static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +{ + const struct sof_intel_dsp_desc *chip; + int i; + + chip = get_chip_info(sdev->pdata); + + if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "SSP")) + ops->drv[i].ops = &ssp_dai_ops; + } + } +} + +static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +{ + const struct sof_intel_dsp_desc *chip; + int i; + + chip = get_chip_info(sdev->pdata); + + if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "DMIC")) + ops->drv[i].ops = &dmic_dai_ops; + } + } +} + +#else + +static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} +static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} + +#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { @@ -398,6 +604,9 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) #endif } + ssp_set_dai_drv_ops(sdev, ops); + dmic_set_dai_drv_ops(sdev, ops); + if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index b7cbf66badf5..b592e687a87a 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -331,14 +331,19 @@ static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask) return !!(val & cmdsync_mask); } -static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num) +static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid) { - u32 val; + return readw(lsdiid); +} + +static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num) +{ + u16 val; - val = readl(lsdiid); + val = readw(lsdiid); val |= BIT(dev_num); - writel(val, lsdiid); + writew(val, lsdiid); } static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan, @@ -752,6 +757,22 @@ int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink) } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK); +int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid) +{ + struct hdac_ext2_link *h2link; + struct hdac_ext_link *hlink; + + h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + if (!h2link) + return -ENODEV; + + hlink = &h2link->hext_link; + + *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink)); + + return 0; +} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, SND_SOC_SOF_HDA_MLINK); + int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num) { struct hdac_ext2_link *h2link; @@ -781,6 +802,8 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, { struct hdac_ext2_link *h2link; u16 __iomem *pcmsycm; + int hchan; + int lchan; u16 val; h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW); @@ -791,17 +814,25 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, h2link->instance_offset * sublink + AZX_REG_SDW_SHIM_PCMSyCM(y); + if (channel_mask) { + hchan = __fls(channel_mask); + lchan = __ffs(channel_mask); + } else { + hchan = 0; + lchan = 0; + } + mutex_lock(&h2link->eml_lock); - hdaml_shim_map_stream_ch(pcmsycm, 0, hweight32(channel_mask), + hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan, stream_id, dir); mutex_unlock(&h2link->eml_lock); val = readw(pcmsycm); - dev_dbg(bus->dev, "channel_mask %#x stream_id %d dir %d pcmscm %#x\n", - channel_mask, stream_id, dir, val); + dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n", + sublink, channel_mask, stream_id, dir, val); return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index b13acb959653..0b0087abcc50 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -869,8 +869,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) return -ENOMEM; } - /* create capture streams */ - for (i = 0; i < num_capture; i++) { + /* create capture and playback streams */ + for (i = 0; i < num_total; i++) { struct sof_intel_hda_stream *hda_stream; hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), @@ -909,69 +909,17 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) hstream->index = i; sd_offset = SOF_STREAM_SD_OFFSET(hstream); hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; - hstream->stream_tag = i + 1; hstream->opened = false; hstream->running = false; - hstream->direction = SNDRV_PCM_STREAM_CAPTURE; - /* memory alloc for stream BDL */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, - HDA_DSP_BDL_SIZE, &hstream->bdl); - if (ret < 0) { - dev_err(sdev->dev, "error: stream bdl dma alloc failed\n"); - return -ENOMEM; - } - hstream->posbuf = (__le32 *)(bus->posbuf.area + - (hstream->index) * 8); - - list_add_tail(&hstream->list, &bus->stream_list); - } - - /* create playback streams */ - for (i = num_capture; i < num_total; i++) { - struct sof_intel_hda_stream *hda_stream; - - hda_stream = devm_kzalloc(sdev->dev, sizeof(*hda_stream), - GFP_KERNEL); - if (!hda_stream) - return -ENOMEM; - - hda_stream->sdev = sdev; - - hext_stream = &hda_stream->hext_stream; - - if (sdev->bar[HDA_DSP_PP_BAR]) { - hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + - SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i; - - hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] + - SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total + - SOF_HDA_PPLC_INTERVAL * i; - } - - hstream = &hext_stream->hstream; - - /* do we support SPIB */ - if (sdev->bar[HDA_DSP_SPIB_BAR]) { - hstream->spib_addr = sdev->bar[HDA_DSP_SPIB_BAR] + - SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + - SOF_HDA_SPIB_SPIB; - - hstream->fifo_addr = sdev->bar[HDA_DSP_SPIB_BAR] + - SOF_HDA_SPIB_BASE + SOF_HDA_SPIB_INTERVAL * i + - SOF_HDA_SPIB_MAXFIFO; + if (i < num_capture) { + hstream->stream_tag = i + 1; + hstream->direction = SNDRV_PCM_STREAM_CAPTURE; + } else { + hstream->stream_tag = i - num_capture + 1; + hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; } - hstream->bus = bus; - hstream->sd_int_sta_mask = 1 << i; - hstream->index = i; - sd_offset = SOF_STREAM_SD_OFFSET(hstream); - hstream->sd_addr = sdev->bar[HDA_DSP_HDA_BAR] + sd_offset; - hstream->stream_tag = i - num_capture + 1; - hstream->opened = false; - hstream->running = false; - hstream->direction = SNDRV_PCM_STREAM_PLAYBACK; - /* mem alloc for stream BDL */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, HDA_DSP_BDL_SIZE, &hstream->bdl); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 64bebe1a72bb..15e6779efaa3 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -71,6 +71,11 @@ static u32 hda_get_interface_mask(struct snd_sof_dev *sdev) BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); interface_mask[1] = BIT(SOF_DAI_INTEL_HDA); break; + case SOF_INTEL_ACE_2_0: + interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | + BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH); + interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */ + break; default: break; } @@ -107,6 +112,34 @@ struct sdw_intel_ops sdw_callback = { .params_stream = sdw_params_stream, }; +static int sdw_ace2x_params_stream(struct device *dev, + struct sdw_intel_stream_params_data *params_data) +{ + return sdw_hda_dai_hw_params(params_data->substream, + params_data->hw_params, + params_data->dai, + params_data->link_id); +} + +static int sdw_ace2x_free_stream(struct device *dev, + struct sdw_intel_stream_free_data *free_data) +{ + return sdw_hda_dai_hw_free(free_data->substream, + free_data->dai, + free_data->link_id); +} + +static int sdw_ace2x_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + return sdw_hda_dai_trigger(substream, cmd, dai); +} + +static struct sdw_intel_ops sdw_ace2x_callback = { + .params_stream = sdw_ace2x_params_stream, + .free_stream = sdw_ace2x_free_stream, + .trigger = sdw_ace2x_trigger, +}; + void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) { struct sof_intel_hda_dev *hdev; @@ -174,6 +207,7 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) res.shim_base = hdev->desc->sdw_shim_base; res.alh_base = hdev->desc->sdw_alh_base; res.ext = false; + res.ops = &sdw_callback; } else { /* * retrieve eml_lock needed to protect shared registers @@ -191,11 +225,13 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev) */ res.hw_ops = &sdw_intel_lnl_hw_ops; res.ext = true; + res.ops = &sdw_ace2x_callback; + } res.irq = sdev->ipc_irq; res.handle = hdev->info.handle; res.parent = sdev->dev; - res.ops = &sdw_callback; + res.dev = sdev->dev; res.clock_stop_quirks = sdw_clock_stop_quirks; res.hbus = sof_to_bus(sdev); @@ -363,14 +399,10 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context) return sdw_intel_thread(irq, context); } -static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev) { - u32 interface_mask = hda_get_interface_mask(sdev); struct sof_intel_hda_dev *hdev; - if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) - return false; - hdev = sdev->pdata->hw_pdata; if (hdev->sdw && snd_sof_dsp_read(sdev, HDA_DSP_BAR, @@ -380,6 +412,21 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) return false; } +static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev) +{ + u32 interface_mask = hda_get_interface_mask(sdev); + const struct sof_intel_dsp_desc *chip; + + if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH))) + return false; + + chip = get_chip_info(sdev->pdata); + if (chip && chip->check_sdw_wakeen_irq) + return chip->check_sdw_wakeen_irq(sdev); + + return false; +} + void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { u32 interface_mask = hda_get_interface_mask(sdev); @@ -1429,83 +1476,6 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) -#define SDW_CODEC_ADR_MASK(_adr) ((_adr) & (SDW_DISCO_LINK_ID_MASK | SDW_VERSION_MASK | \ - SDW_MFG_ID_MASK | SDW_PART_ID_MASK)) - -/* Check if all Slaves defined on the link can be found */ -static bool link_slaves_found(struct snd_sof_dev *sdev, - const struct snd_soc_acpi_link_adr *link, - struct sdw_intel_ctx *sdw) -{ - struct hdac_bus *bus = sof_to_bus(sdev); - struct sdw_intel_slave_id *ids = sdw->ids; - int num_slaves = sdw->num_slaves; - unsigned int part_id, link_id, unique_id, mfg_id, version; - int i, j, k; - - for (i = 0; i < link->num_adr; i++) { - u64 adr = link->adr_d[i].adr; - int reported_part_count = 0; - - mfg_id = SDW_MFG_ID(adr); - part_id = SDW_PART_ID(adr); - link_id = SDW_DISCO_LINK_ID(adr); - version = SDW_VERSION(adr); - - for (j = 0; j < num_slaves; j++) { - /* find out how many identical parts were reported on that link */ - if (ids[j].link_id == link_id && - ids[j].id.part_id == part_id && - ids[j].id.mfg_id == mfg_id && - ids[j].id.sdw_version == version) - reported_part_count++; - } - - for (j = 0; j < num_slaves; j++) { - int expected_part_count = 0; - - if (ids[j].link_id != link_id || - ids[j].id.part_id != part_id || - ids[j].id.mfg_id != mfg_id || - ids[j].id.sdw_version != version) - continue; - - /* find out how many identical parts are expected */ - for (k = 0; k < link->num_adr; k++) { - u64 adr2 = link->adr_d[k].adr; - - if (SDW_CODEC_ADR_MASK(adr2) == SDW_CODEC_ADR_MASK(adr)) - expected_part_count++; - } - - if (reported_part_count == expected_part_count) { - /* - * we have to check unique id - * if there is more than one - * Slave on the link - */ - unique_id = SDW_UNIQUE_ID(adr); - if (reported_part_count == 1 || - ids[j].id.unique_id == unique_id) { - dev_dbg(bus->dev, "found %x at link %d\n", - part_id, link_id); - break; - } - } else { - dev_dbg(bus->dev, "part %x reported %d expected %d on link %d, skipping\n", - part_id, reported_part_count, expected_part_count, link_id); - } - } - if (j == num_slaves) { - dev_dbg(bus->dev, - "Slave %x not found\n", - part_id); - return false; - } - } - return true; -} - static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev) { struct snd_sof_pdata *pdata = sdev->pdata; @@ -1549,7 +1519,9 @@ static struct snd_soc_acpi_mach *hda_sdw_machine_select(struct snd_sof_dev *sdev * Try next machine if any expected Slaves * are not found on this link. */ - if (!link_slaves_found(sdev, link, hdev->sdw)) + if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link, + hdev->sdw->ids, + hdev->sdw->num_slaves)) break; } /* Found if all Slaves are checked */ diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5b9e4ebcc18b..5c517ec57d4a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -785,6 +785,7 @@ int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); +bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev); void hda_sdw_process_wakeen(struct snd_sof_dev *sdev); bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev); @@ -813,6 +814,11 @@ static inline void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable) { } +static inline bool hda_sdw_check_wakeen_irq_common(struct snd_sof_dev *sdev) +{ + return false; +} + static inline void hda_sdw_process_wakeen(struct snd_sof_dev *sdev) { } @@ -824,6 +830,18 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev) #endif +int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai, + int link_id); + +int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, + int link_id); + +int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai); + /* common dai driver */ extern struct snd_soc_dai_driver skl_dai[]; int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); @@ -845,6 +863,8 @@ extern struct snd_sof_dsp_ops sof_icl_ops; int sof_icl_ops_init(struct snd_sof_dev *sdev); extern struct snd_sof_dsp_ops sof_mtl_ops; int sof_mtl_ops_init(struct snd_sof_dev *sdev); +extern struct snd_sof_dsp_ops sof_lnl_ops; +int sof_lnl_ops_init(struct snd_sof_dev *sdev); extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc apl_chip_info; @@ -856,6 +876,7 @@ extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; extern const struct sof_intel_dsp_desc adls_chip_info; extern const struct sof_intel_dsp_desc mtl_chip_info; +extern const struct sof_intel_dsp_desc lnl_chip_info; /* Probes support */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 0f249efc6a5a..7ac10167a90d 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -188,6 +188,7 @@ const struct sof_intel_dsp_desc icl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c new file mode 100644 index 000000000000..db94b45e53af --- /dev/null +++ b/sound/soc/sof/intel/lnl.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. + +/* + * Hardware interface for audio DSP on LunarLake. + */ + +#include <linux/firmware.h> +#include <sound/hda_register.h> +#include <sound/sof/ipc4/header.h> +#include <trace/events/sof_intel.h> +#include "../ipc4-priv.h" +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" +#include "mtl.h" +#include <sound/hda-mlink.h> + +/* LunarLake ops */ +struct snd_sof_dsp_ops sof_lnl_ops; +EXPORT_SYMBOL_NS(sof_lnl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, +}; + +/* this helps allows the DSP to setup DMIC/SSP */ +static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus) +{ + int ret; + + ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP, true); + if (ret < 0) + return ret; + + ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC, true); + if (ret < 0) + return ret; + + return 0; +} + +static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_probe(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_resume(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev) +{ + int ret; + + ret = hda_dsp_runtime_resume(sdev); + if (ret < 0) + return ret; + + return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev)); +} + +int sof_lnl_ops_init(struct snd_sof_dev *sdev) +{ + struct sof_ipc4_fw_data *ipc4_data; + + /* common defaults */ + memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); + + /* probe */ + sof_lnl_ops.probe = lnl_hda_dsp_probe; + + /* shutdown */ + sof_lnl_ops.shutdown = hda_dsp_shutdown; + + /* doorbell */ + sof_lnl_ops.irq_thread = mtl_ipc_irq_thread; + + /* ipc */ + sof_lnl_ops.send_msg = mtl_ipc_send_msg; + sof_lnl_ops.get_mailbox_offset = mtl_dsp_ipc_get_mailbox_offset; + sof_lnl_ops.get_window_offset = mtl_dsp_ipc_get_window_offset; + + /* debug */ + sof_lnl_ops.debug_map = lnl_dsp_debugfs; + sof_lnl_ops.debug_map_count = ARRAY_SIZE(lnl_dsp_debugfs); + sof_lnl_ops.dbg_dump = mtl_dsp_dump; + sof_lnl_ops.ipc_dump = mtl_ipc_dump; + + /* pre/post fw run */ + sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run; + sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run; + + /* parse platform specific extended manifest */ + sof_lnl_ops.parse_platform_ext_manifest = NULL; + + /* dsp core get/put */ + /* TODO: add core_get and core_put */ + + /* PM */ + sof_lnl_ops.resume = lnl_hda_dsp_resume; + sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume; + + sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; + + sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL); + if (!sdev->private) + return -ENOMEM; + + ipc4_data = sdev->private; + ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET; + + ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; + + /* External library loading support */ + ipc4_data->load_library = hda_dsp_ipc4_load_library; + + /* set DAI ops */ + hda_set_dai_drv_ops(sdev, &sof_lnl_ops); + + sof_lnl_ops.set_power_state = hda_dsp_set_power_state_ipc4; + + return 0; +}; +EXPORT_SYMBOL_NS(sof_lnl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON); + +/* Check if an SDW IRQ occurred */ +static bool lnl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + return hdac_bus_eml_check_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW); +} + +static void lnl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + + hdac_bus_eml_enable_interrupt(bus, true, AZX_REG_ML_LEPTR_ID_SDW, enable); +} + +static int lnl_dsp_disable_interrupts(struct snd_sof_dev *sdev) +{ + lnl_enable_sdw_irq(sdev, false); + mtl_disable_ipc_interrupts(sdev); + return mtl_enable_interrupts(sdev, false); +} + +const struct sof_intel_dsp_desc lnl_chip_info = { + .cores_num = 5, + .init_core_mask = BIT(0), + .host_managed_cores_mask = BIT(0), + .ipc_req = MTL_DSP_REG_HFIPCXIDR, + .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY, + .ipc_ack = MTL_DSP_REG_HFIPCXIDA, + .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE, + .ipc_ctl = MTL_DSP_REG_HFIPCXCTL, + .rom_status_reg = MTL_DSP_ROM_STS, + .rom_init_timeout = 300, + .ssp_count = MTL_SSP_COUNT, + .d0i3_offset = MTL_HDA_VS_D0I3C, + .read_sdw_lcount = hda_sdw_check_lcount_ext, + .enable_sdw_irq = lnl_enable_sdw_irq, + .check_sdw_irq = lnl_dsp_check_sdw_irq, + .check_ipc_irq = mtl_dsp_check_ipc_irq, + .cl_init = mtl_dsp_cl_init, + .power_down_dsp = mtl_power_down_dsp, + .disable_interrupts = lnl_dsp_disable_interrupts, + .hw_ip_version = SOF_INTEL_ACE_2_0, +}; +EXPORT_SYMBOL_NS(lnl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 30fe77fd87bf..b84ca58da9d5 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -91,7 +91,7 @@ static bool mtl_dsp_check_sdw_irq(struct snd_sof_dev *sdev) return false; } -static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) +int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc4_msg *msg_data = msg->msg_data; @@ -230,7 +230,7 @@ int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) } /* pre fw run operations */ -static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) +int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; u32 dsphfpwrsts; @@ -279,7 +279,7 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev) return ret; } -static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) +int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) { int ret; @@ -301,7 +301,7 @@ static int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev) return 0; } -static void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) +void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; u32 romdbgsts; @@ -495,7 +495,7 @@ err: return ret; } -static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) +irqreturn_t mtl_ipc_irq_thread(int irq, void *context) { struct sof_ipc4_msg notification_data = {{ 0 }}; struct snd_sof_dev *sdev = context; @@ -578,17 +578,17 @@ static irqreturn_t mtl_ipc_irq_thread(int irq, void *context) return IRQ_HANDLED; } -static int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) +int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev) { return MTL_DSP_MBOX_UPLINK_OFFSET; } -static int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) +int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id) { return MTL_SRAM_WINDOW_OFFSET(id); } -static void mtl_ipc_dump(struct snd_sof_dev *sdev) +void mtl_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcidr, hipcidd, hipcida, hipctdr, hipctdd, hipctda, hipcctl; @@ -612,9 +612,9 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev) return mtl_enable_interrupts(sdev, false); } -static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream) +u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct hdac_stream *hstream = substream->runtime->private_data; u32 llp_l, llp_u; @@ -735,6 +735,7 @@ const struct sof_intel_dsp_desc mtl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = mtl_enable_sdw_irq, .check_sdw_irq = mtl_dsp_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = mtl_dsp_check_ipc_irq, .cl_init = mtl_dsp_cl_init, .power_down_dsp = mtl_power_down_dsp, diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 2794fe6e8139..02181490f12a 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -82,10 +82,28 @@ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); +int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); + void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); -bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); -int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + +int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev); +int mtl_dsp_post_fw_run(struct snd_sof_dev *sdev); +void mtl_dsp_dump(struct snd_sof_dev *sdev, u32 flags); + int mtl_power_down_dsp(struct snd_sof_dev *sdev); +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); + +irqreturn_t mtl_ipc_irq_thread(int irq, void *context); + +int mtl_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev); +int mtl_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id); + +void mtl_ipc_dump(struct snd_sof_dev *sdev); + +u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, + struct snd_soc_component *component, + struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c new file mode 100644 index 000000000000..1b12c280edb4 --- /dev/null +++ b/sound/soc/sof/intel/pci-lnl.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2023 Intel Corporation. All rights reserved. +// +// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> +// + +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include <sound/sof.h> +#include "../ops.h" +#include "../sof-pci-dev.h" + +/* platform specific devices */ +#include "hda.h" +#include "mtl.h" + +static const struct sof_dev_desc lnl_desc = { + .use_acpi_target_states = true, + .machines = snd_soc_acpi_intel_lnl_machines, + .alt_machines = snd_soc_acpi_intel_lnl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .chip_info = &lnl_chip_info, + .ipc_supported_mask = BIT(SOF_INTEL_IPC4), + .ipc_default = SOF_INTEL_IPC4, + .dspless_mode_supported = true, + .default_fw_path = { + [SOF_INTEL_IPC4] = "intel/sof-ipc4/lnl", + }, + .default_tplg_path = { + [SOF_INTEL_IPC4] = "intel/sof-ace-tplg", + }, + .default_fw_filename = { + [SOF_INTEL_IPC4] = "sof-lnl.ri", + }, + .nocodec_tplg_filename = "sof-lnl-nocodec.tplg", + .ops = &sof_lnl_ops, + .ops_init = sof_lnl_ops_init, +}; + +/* PCI IDs */ +static const struct pci_device_id sof_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, &lnl_desc) }, /* LNL-P */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, sof_pci_ids); + +/* pci_driver definition */ +static struct pci_driver snd_sof_pci_intel_lnl_driver = { + .name = "sof-audio-pci-intel-lnl", + .id_table = sof_pci_ids, + .probe = hda_pci_intel_probe, + .remove = sof_pci_remove, + .shutdown = sof_pci_shutdown, + .driver = { + .pm = &sof_pci_pm, + }, +}; +module_pci_driver(snd_sof_pci_intel_lnl_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_INTEL_HDA_COMMON); +MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV); diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index 207df48e27cf..9515d753c816 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -189,6 +189,7 @@ struct sof_intel_dsp_desc { int (*read_sdw_lcount)(struct snd_sof_dev *sdev); void (*enable_sdw_irq)(struct snd_sof_dev *sdev, bool enable); bool (*check_sdw_irq)(struct snd_sof_dev *sdev); + bool (*check_sdw_wakeen_irq)(struct snd_sof_dev *sdev); bool (*check_ipc_irq)(struct snd_sof_dev *sdev); int (*power_down_dsp)(struct snd_sof_dev *sdev); int (*disable_interrupts)(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 8e2b07e1612b..bb9f20253c99 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -147,6 +147,7 @@ const struct sof_intel_dsp_desc tgl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -175,6 +176,7 @@ const struct sof_intel_dsp_desc tglh_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -203,6 +205,7 @@ const struct sof_intel_dsp_desc ehl_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, @@ -231,6 +234,7 @@ const struct sof_intel_dsp_desc adls_chip_info = { .read_sdw_lcount = hda_sdw_check_lcount_common, .enable_sdw_irq = hda_common_enable_sdw_irq, .check_sdw_irq = hda_common_check_sdw_irq, + .check_sdw_wakeen_irq = hda_sdw_check_wakeen_irq_common, .check_ipc_irq = hda_dsp_check_ipc_irq, .cl_init = cl_dsp_init, .power_down_dsp = hda_power_down_dsp, diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 35da85a45a9a..bd07f0472efd 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -196,15 +196,9 @@ static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user return -EINVAL; } - string = kmalloc(count + 1, GFP_KERNEL); - if (!string) - return -ENOMEM; - - if (copy_from_user(string, from, count)) { - ret = -EFAULT; - goto error; - } - string[count] = '\0'; + string = memdup_user_nul(from, count); + if (IS_ERR(string)) + return PTR_ERR(string); ret = trace_filter_parse(sdev, string, &num_elems, &elems); if (ret < 0) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 304faf6425ab..cb58ee8c158a 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -309,6 +309,23 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = private->dai_config->afe.channels; channels->max = private->dai_config->afe.channels; + snd_mask_none(fmt); + + switch (private->dai_config->afe.format) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(component->dev, "Not available format!\n"); + return -EINVAL; + } + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", channels->min, channels->max); diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 580960ff273d..fb40378ad084 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -312,7 +312,7 @@ static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) } else { if (sof_debug_check_flag(SOF_DBG_PRINT_IPC_SUCCESS_LOGS)) ipc3_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd); - if (msg->reply_size) + if (reply_data && msg->reply_size) /* copy the data returned from DSP */ memcpy(reply_data, msg->reply_data, msg->reply_size); @@ -567,13 +567,10 @@ int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, /* create read-only cc_version debugfs to store compiler version info */ /* use local copy of the cc_version to prevent data corruption */ if (sdev->first_boot) { - sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, - GFP_KERNEL); - + sdev->cc_version = devm_kmemdup(sdev->dev, cc, cc->ext_hdr.hdr.size, GFP_KERNEL); if (!sdev->cc_version) return -ENOMEM; - memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, cc->ext_hdr.hdr.size, "cc_version", 0444); diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 027416eb2f50..db19cd03ecad 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -23,7 +23,8 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state /* trigger a single pipeline */ if (trigger_list->count == 1) - return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state); + return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0], + state); primary = state; primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); @@ -42,15 +43,15 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size); } -int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state) +int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state) { struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; - dev_dbg(sdev->dev, "ipc4 set pipeline %d state %d", id, state); + dev_dbg(sdev->dev, "ipc4 set pipeline instance %d state %d", instance_id, state); primary = state; - primary |= SOF_IPC4_GLB_PIPE_STATE_ID(id); + primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); @@ -79,19 +80,19 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, * for the first time */ if (spipe->started_count == spipe->paused_count) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; case SOF_IPC4_PIPE_RESET: /* RESET if the pipeline is neither running nor paused */ if (!spipe->started_count && !spipe->paused_count) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; case SOF_IPC4_PIPE_PAUSED: /* Pause the pipeline only when its started_count is 1 more than paused_count */ if (spipe->paused_count == (spipe->started_count - 1)) - trigger_list->pipeline_ids[trigger_list->count++] = + trigger_list->pipeline_instance_ids[trigger_list->count++] = pipe_widget->instance_id; break; default: @@ -113,7 +114,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, /* set state for pipeline if it was just triggered */ for (i = 0; i < trigger_list->count; i++) { - if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) { + if (trigger_list->pipeline_instance_ids[i] == pipe_widget->instance_id) { pipeline->state = state; break; } @@ -314,8 +315,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); /* allocate memory for the pipeline data */ - trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), - GFP_KERNEL); + trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids, + pipeline_list->count), GFP_KERNEL); if (!trigger_list) return -ENOMEM; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 11361e1cd688..f2a30cd31378 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -17,6 +17,19 @@ #include "ipc4-topology.h" #include "ops.h" +/* + * The ignore_cpc flag can be used to ignore the CPC value for all modules by + * using 0 instead. + * The CPC is sent to the firmware along with the SOF_IPC4_MOD_INIT_INSTANCE + * message and it is used for clock scaling. + * 0 as CPC value will instruct the firmware to use maximum frequency, thus + * deactivating the clock scaling. + */ +static bool ignore_cpc; +module_param_named(ipc4_ignore_cpc, ignore_cpc, bool, 0444); +MODULE_PARM_DESC(ipc4_ignore_cpc, + "Ignore CPC values. This option will disable clock scaling in firmware."); + #define SOF_IPC4_GAIN_PARAM_ID 0 #define SOF_IPC4_TPLG_ABI_SIZE 6 #define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2 @@ -970,9 +983,16 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * /* Update base_config->cpc from the module manifest */ sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); - dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", - swidget->widget->name, base_config->ibs, base_config->obs, - base_config->cpc); + if (ignore_cpc) { + dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); + base_config->cpc = 0; + } else { + dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); + } } static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, @@ -1349,23 +1369,29 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s } #endif -static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth) +static bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev, + struct sof_ipc4_pin_format *pin_fmts, + u32 pin_fmts_size) { - switch (bit_depth) { - case 16: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); - break; - case 24: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); - break; - case 32: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - break; - default: - return -EINVAL; + struct sof_ipc4_audio_format *fmt; + u32 valid_bits; + int i; + + fmt = &pin_fmts[0].audio_fmt; + valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + /* check if all output formats in topology are the same */ + for (i = 1; i < pin_fmts_size; i++) { + u32 _valid_bits; + + fmt = &pin_fmts[i].audio_fmt; + _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (_valid_bits != valid_bits) + return false; } - return 0; + return true; } static int @@ -1381,8 +1407,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *ref_params; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_dai *dai; - struct snd_mask *fmt; - int out_sample_valid_bits; u32 gtw_cfg_config_length; u32 dma_config_tlv_size = 0; void **ipc_config_data; @@ -1392,6 +1416,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, u32 out_ref_rate, out_ref_channels; u32 deep_buffer_dma_ms = 0; int output_fmt_index; + bool single_output_format; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1525,6 +1550,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return ret; /* set the reference params for output format selection */ + single_output_format = sof_ipc4_copier_is_single_format(sdev, + available_fmt->output_pin_fmts, + available_fmt->num_output_formats); switch (swidget->id) { case snd_soc_dapm_aif_in: case snd_soc_dapm_dai_out: @@ -1535,17 +1563,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; out_ref_rate = in_fmt->sampling_frequency; out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + if (!single_output_format) + out_ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); break; } case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_in: - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); - if (out_ref_valid_bits < 0) - return out_ref_valid_bits; - out_ref_rate = params_rate(fe_params); out_ref_channels = params_channels(fe_params); + if (!single_output_format) { + out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (out_ref_valid_bits < 0) + return out_ref_valid_bits; + } break; default: /* @@ -1555,6 +1587,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return -EINVAL; } + /* + * if the output format is the same across all available output formats, choose + * that as the reference. + */ + if (single_output_format) { + struct sof_ipc4_audio_format *out_fmt; + + out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; + out_ref_valid_bits = + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg); + } + + dev_dbg(sdev->dev, "copier %s: reference output rate %d, channels %d valid_bits %d\n", + swidget->widget->name, out_ref_rate, out_ref_channels, out_ref_valid_bits); + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config, available_fmt, out_ref_rate, out_ref_channels, out_ref_valid_bits); @@ -1664,11 +1711,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* modify the input params for the next widget */ - fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); - out_sample_valid_bits = - SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg); - snd_mask_none(fmt); - ret = ipc4_set_fmt_mask(fmt, out_sample_valid_bits); + ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format); if (ret) return ret; @@ -1746,6 +1789,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, gtw_cfg_config_length, &ipc4_copier->dma_config_tlv, dma_config_tlv_size); + /* + * Restore gateway config length now that IPC payload is prepared. This avoids + * counting the DMA CONFIG TLV multiple times + */ + copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4; + return 0; } @@ -1832,37 +1881,58 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_src *src = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; - struct sof_ipc4_audio_format *in_fmt; + struct sof_ipc4_audio_format *out_audio_fmt; + struct sof_ipc4_audio_format *in_audio_fmt; u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; - struct snd_interval *rate; - int ret; + int output_format_index, input_format_index; - ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, - pipeline_params, available_fmt); - if (ret < 0) - return ret; + input_format_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, + pipeline_params, available_fmt); + if (input_format_index < 0) + return input_format_index; - in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; - out_ref_rate = in_fmt->sampling_frequency; - out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); - out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + /* + * For playback, the SRC sink rate will be configured based on the requested output + * format, which is restricted to only deal with DAI's with a single format for now. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK && available_fmt->num_output_formats > 1) { + dev_err(sdev->dev, "Invalid number of output formats: %d for SRC %s\n", + available_fmt->num_output_formats, swidget->widget->name); + return -EINVAL; + } - ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt, - out_ref_rate, out_ref_channels, out_ref_valid_bits); - if (ret < 0) { + /* + * SRC does not perform format conversion, so the output channels and valid bit depth must + * be the same as that of the input. + */ + in_audio_fmt = &available_fmt->input_pin_fmts[input_format_index].audio_fmt; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg); + + /* + * For capture, the SRC module should convert the rate to match the rate requested by the + * PCM hw_params. Set the reference params based on the fe_params unconditionally as it + * will be ignored for playback anyway. + */ + out_ref_rate = params_rate(fe_params); + + output_format_index = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_format_index < 0) { dev_err(sdev->dev, "Failed to initialize output format for %s", swidget->widget->name); + return output_format_index; } /* update pipeline memory usage */ sof_ipc4_update_resource_usage(sdev, swidget, &src->base_config); - /* update pipeline_params for sink widgets */ - rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE); - rate->min = src->sink_rate; - rate->max = rate->min; + out_audio_fmt = &available_fmt->output_pin_fmts[output_format_index].audio_fmt; + src->sink_rate = out_audio_fmt->sampling_frequency; - return 0; + /* update pipeline_params for sink widgets */ + return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt); } static int @@ -2319,6 +2389,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline->mem_usage = 0; pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); + swidget->instance_id = -EINVAL; } else { struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 6dcf14886e85..d75f17f4749c 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -144,11 +144,11 @@ struct sof_ipc4_pipeline { /** * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data * @count: Number of pipelines to be triggered - * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered + * @pipeline_instance_ids: Flexible array of IDs of the pipelines to be triggered */ struct ipc4_pipeline_set_state_data { u32 count; - DECLARE_FLEX_ARRAY(u32, pipeline_ids); + DECLARE_FLEX_ARRAY(u32, pipeline_instance_ids); } __packed; /** diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 3e0ea0e109e2..f587edf9e0a7 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -111,6 +111,14 @@ static int platform_parse_resource(struct platform_device *pdev, void *data) dev_dbg(dev, "DMA %pR\n", &res); + adsp->pa_shared_dram = (phys_addr_t)res.start; + adsp->shared_size = resource_size(&res); + if (adsp->pa_shared_dram & DRAM_REMAP_MASK) { + dev_err(dev, "adsp shared dma memory(%#x) is not 4K-aligned\n", + (u32)adsp->pa_shared_dram); + return -EINVAL; + } + ret = of_reserved_mem_device_init(dev); if (ret) { dev_err(dev, "of_reserved_mem_device_init failed\n"); @@ -244,23 +252,18 @@ static int adsp_shared_base_ioremap(struct platform_device *pdev, void *data) { struct device *dev = &pdev->dev; struct mtk_adsp_chip_info *adsp = data; - u32 shared_size; /* remap shared-dram base to be non-cachable */ - shared_size = TOTAL_SIZE_SHARED_DRAM_FROM_TAIL; - adsp->pa_shared_dram = adsp->pa_dram + adsp->dramsize - shared_size; - if (adsp->va_dram) { - adsp->shared_dram = adsp->va_dram + DSP_DRAM_SIZE - shared_size; - } else { - adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, - shared_size); - if (!adsp->shared_dram) { - dev_err(dev, "ioremap failed for shared DRAM\n"); - return -ENOMEM; - } + adsp->shared_dram = devm_ioremap(dev, adsp->pa_shared_dram, + adsp->shared_size); + if (!adsp->shared_dram) { + dev_err(dev, "failed to ioremap base %pa size %#x\n", + adsp->shared_dram, adsp->shared_size); + return -ENOMEM; } - dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", - adsp->shared_dram, &adsp->pa_shared_dram, shared_size); + + dev_dbg(dev, "shared-dram vbase=%p, phy addr :%pa, size=%#x\n", + adsp->shared_dram, &adsp->pa_shared_dram, adsp->shared_size); return 0; } @@ -307,9 +310,12 @@ static int mt8186_dsp_probe(struct snd_sof_dev *sdev) return -ENOMEM; } - sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, - priv->adsp->pa_dram, - priv->adsp->dramsize); + priv->adsp->va_sram = sdev->bar[SOF_FW_BLK_TYPE_IRAM]; + + sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap(sdev->dev, + priv->adsp->pa_dram, + priv->adsp->dramsize); + if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) { dev_err(sdev->dev, "failed to ioremap base %pa size %#x\n", &priv->adsp->pa_dram, priv->adsp->dramsize); diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c index ea21ef176c42..c56a85854d92 100644 --- a/sound/soc/sof/sof-client-probes-ipc4.c +++ b/sound/soc/sof/sof-client-probes-ipc4.c @@ -146,6 +146,9 @@ static int ipc4_probes_deinit(struct sof_client_dev *cdev) struct sof_man4_module *mentry = sof_ipc4_probe_get_module_info(cdev); struct sof_ipc4_msg msg; + if (!mentry) + return -ENODEV; + msg.primary = mentry->id; msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_DELETE_INSTANCE); msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); @@ -197,6 +200,9 @@ static int ipc4_probes_points_add(struct sof_client_dev *cdev, struct sof_ipc4_msg msg; int i, ret; + if (!mentry) + return -ENODEV; + /* The sof_probe_point_desc and sof_ipc4_probe_point structs * are of same size and even the integers are the same in the * same order, and similar meaning, but since there is no @@ -247,6 +253,9 @@ static int ipc4_probes_points_remove(struct sof_client_dev *cdev, u32 *probe_point_ids; int i, ret; + if (!mentry) + return -ENODEV; + probe_point_ids = kcalloc(num_buffer_id, sizeof(*probe_point_ids), GFP_KERNEL); if (!probe_point_ids) diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c index 5530b5d793d0..740b637822db 100644 --- a/sound/soc/sof/sof-client-probes.c +++ b/sound/soc/sof/sof-client-probes.c @@ -354,10 +354,14 @@ static const struct file_operations sof_probes_points_remove_fops = { .owner = THIS_MODULE, }; +static const struct snd_soc_dai_ops sof_probes_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + static struct snd_soc_dai_driver sof_probes_dai_drv[] = { { .name = "Probe Extraction CPU DAI", - .compress_new = snd_soc_new_compress, + .ops = &sof_probes_dai_ops, .cops = &sof_probes_compr_ops, .capture = { .stream_name = "Probe Extraction", diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 698129dccc7d..a3a3af252259 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1117,10 +1117,11 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, { struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; + const char *sname = w->sname; struct snd_soc_dai *cpu_dai; int i, stream; - if (!w->sname) + if (!sname) return; if (w->id == snd_soc_dapm_dai_out) @@ -1133,7 +1134,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || - strcmp(w->sname, rtd->dai_link->stream_name)) + strcmp(sname, rtd->dai_link->stream_name)) continue; for_each_rtd_cpu_dais(rtd, i, cpu_dai) @@ -1366,6 +1367,20 @@ err: return ret; } +static int get_w_no_wname_in_long_name(void *elem, void *object, u32 offset) +{ + struct snd_soc_tplg_vendor_value_elem *velem = elem; + struct snd_soc_dapm_widget *w = object; + + w->no_wname_in_kcontrol_name = !!le32_to_cpu(velem->value); + return 0; +} + +static const struct sof_topology_token dapm_widget_tokens[] = { + {SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME, SND_SOC_TPLG_TUPLE_TYPE_BOOL, + get_w_no_wname_in_long_name, 0} +}; + /* external widget init - used for any driver specific init */ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, @@ -1396,6 +1411,14 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ida_init(&swidget->output_queue_ida); ida_init(&swidget->input_queue_ida); + ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, ARRAY_SIZE(dapm_widget_tokens), + priv->array, le32_to_cpu(priv->size)); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n", + w->name); + goto widget_free; + } + ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens, ARRAY_SIZE(comp_pin_tokens), priv->array, le32_to_cpu(priv->size)); @@ -2157,6 +2180,8 @@ static int sof_complete(struct snd_soc_component *scomp) struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_widget *swidget; + pipe_widget->instance_id = -EINVAL; + /* Update the scheduler widget's IPC structure */ if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c index 705f1420097b..8c117794b028 100644 --- a/sound/soc/starfive/jh7110_tdm.c +++ b/sound/soc/starfive/jh7110_tdm.c @@ -463,13 +463,6 @@ static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, return 0; } -static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { - .startup = jh7110_tdm_startup, - .hw_params = jh7110_tdm_hw_params, - .trigger = jh7110_tdm_trigger, - .set_fmt = jh7110_tdm_set_dai_fmt, -}; - static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) { struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai); @@ -479,6 +472,14 @@ static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = { + .probe = jh7110_tdm_dai_probe, + .startup = jh7110_tdm_startup, + .hw_params = jh7110_tdm_hw_params, + .trigger = jh7110_tdm_trigger, + .set_fmt = jh7110_tdm_set_dai_fmt, +}; + #define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000 #define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -502,7 +503,6 @@ static struct snd_soc_dai_driver jh7110_tdm_dai = { .formats = JH7110_TDM_FORMATS, }, .ops = &jh7110_tdm_dai_ops, - .probe = jh7110_tdm_dai_probe, .symmetric_rate = 1, }; diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c index a4d74d1e3c24..2c21a86421e6 100644 --- a/sound/soc/sti/sti_uniperif.c +++ b/sound/soc/sti/sti_uniperif.c @@ -369,10 +369,14 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai) return sti_uniperiph_dai_create_ctrl(dai); } -static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { +static const struct snd_soc_dai_ops sti_uniperiph_dai_ops = { .probe = sti_uniperiph_dai_probe, }; +static const struct snd_soc_dai_driver sti_uniperiph_dai_template = { + .ops = &sti_uniperiph_dai_ops, +}; + static const struct snd_soc_component_driver sti_uniperiph_dai_component = { .name = "sti_cpu_dai", .suspend = sti_uniperiph_suspend, diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c index 387130701960..06a42130f5e4 100644 --- a/sound/soc/stm/stm32_i2s.c +++ b/sound/soc/stm/stm32_i2s.c @@ -953,6 +953,7 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { }; static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { + .probe = stm32_i2s_dai_probe, .set_sysclk = stm32_i2s_set_sysclk, .set_fmt = stm32_i2s_set_dai_fmt, .startup = stm32_i2s_startup, @@ -1002,7 +1003,6 @@ static int stm32_i2s_dais_init(struct platform_device *pdev, if (!dai_ptr) return -ENOMEM; - dai_ptr->probe = stm32_i2s_dai_probe; dai_ptr->ops = &stm32_i2s_pcm_dai_ops; dai_ptr->id = 1; stm32_i2s_dai_init(&dai_ptr->playback, "playback"); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 39f9b4654fa2..f9b5d5969155 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1222,6 +1222,19 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { + .probe = stm32_sai_dai_probe, + .set_sysclk = stm32_sai_set_sysclk, + .set_fmt = stm32_sai_set_dai_fmt, + .set_tdm_slot = stm32_sai_set_dai_tdm_slot, + .startup = stm32_sai_startup, + .hw_params = stm32_sai_hw_params, + .trigger = stm32_sai_trigger, + .shutdown = stm32_sai_shutdown, + .pcm_new = stm32_sai_pcm_new, +}; + +static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = { + .probe = stm32_sai_dai_probe, .set_sysclk = stm32_sai_set_sysclk, .set_fmt = stm32_sai_set_dai_fmt, .set_tdm_slot = stm32_sai_set_dai_tdm_slot, @@ -1287,8 +1300,6 @@ static const struct snd_pcm_hardware stm32_sai_pcm_hw = { }; static struct snd_soc_dai_driver stm32_sai_playback_dai = { - .probe = stm32_sai_dai_probe, - .pcm_new = stm32_sai_pcm_new, .id = 1, /* avoid call to fmt_single_name() */ .playback = { .channels_min = 1, @@ -1306,7 +1317,6 @@ static struct snd_soc_dai_driver stm32_sai_playback_dai = { }; static struct snd_soc_dai_driver stm32_sai_capture_dai = { - .probe = stm32_sai_dai_probe, .id = 1, /* avoid call to fmt_single_name() */ .capture = { .channels_min = 1, @@ -1320,7 +1330,7 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai = { SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, - .ops = &stm32_sai_pcm_dai_ops, + .ops = &stm32_sai_pcm_dai_ops2, }; static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index a4066f271f2d..a359b528b26b 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -856,6 +856,7 @@ static void stm32_spdifrx_shutdown(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { + .probe = stm32_spdifrx_dai_probe, .startup = stm32_spdifrx_startup, .hw_params = stm32_spdifrx_hw_params, .trigger = stm32_spdifrx_trigger, @@ -864,7 +865,6 @@ static const struct snd_soc_dai_ops stm32_spdifrx_pcm_dai_ops = { static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { { - .probe = stm32_spdifrx_dai_probe, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 55328850aef5..f0a5fd901101 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -1252,9 +1252,12 @@ static int sun4i_codec_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops dummy_dai_ops = { + .probe = sun4i_codec_dai_probe, +}; + static struct snd_soc_dai_driver dummy_cpu_dai = { .name = "sun4i-codec-cpu-dai", - .probe = sun4i_codec_dai_probe, .playback = { .stream_name = "Playback", .channels_min = 1, @@ -1271,6 +1274,7 @@ static struct snd_soc_dai_driver dummy_cpu_dai = { .formats = SUN4I_CODEC_FORMATS, .sig_bits = 24, }, + .ops = &dummy_dai_ops, }; static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev, diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 669d712bbe9f..5124b6c9ceb4 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1081,14 +1081,6 @@ static int sun4i_i2s_set_tdm_slot(struct snd_soc_dai *dai, return 0; } -static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { - .hw_params = sun4i_i2s_hw_params, - .set_fmt = sun4i_i2s_set_fmt, - .set_sysclk = sun4i_i2s_set_sysclk, - .set_tdm_slot = sun4i_i2s_set_tdm_slot, - .trigger = sun4i_i2s_trigger, -}; - static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) { struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -1100,12 +1092,20 @@ static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { + .probe = sun4i_i2s_dai_probe, + .hw_params = sun4i_i2s_hw_params, + .set_fmt = sun4i_i2s_set_fmt, + .set_sysclk = sun4i_i2s_set_sysclk, + .set_tdm_slot = sun4i_i2s_set_tdm_slot, + .trigger = sun4i_i2s_trigger, +}; + #define SUN4I_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_driver sun4i_i2s_dai = { - .probe = sun4i_i2s_dai_probe, .capture = { .stream_name = "Capture", .channels_min = 1, diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c index ff18d4113aac..b849bb7cf58e 100644 --- a/sound/soc/sunxi/sun4i-spdif.c +++ b/sound/soc/sunxi/sun4i-spdif.c @@ -508,6 +508,7 @@ static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { + .probe = sun4i_spdif_soc_dai_probe, .startup = sun4i_spdif_startup, .trigger = sun4i_spdif_trigger, .hw_params = sun4i_spdif_hw_params, @@ -533,7 +534,6 @@ static struct snd_soc_dai_driver sun4i_spdif_dai = { .rates = SUN4I_RATES, .formats = SUN4I_FORMATS, }, - .probe = sun4i_spdif_soc_dai_probe, .ops = &sun4i_spdif_dai_ops, .name = "spdif", }; @@ -718,7 +718,7 @@ static const struct dev_pm_ops sun4i_spdif_pm = { static struct platform_driver sun4i_spdif_driver = { .driver = { .name = "sun4i-spdif", - .of_match_table = of_match_ptr(sun4i_spdif_of_match), + .of_match_table = sun4i_spdif_of_match, .pm = &sun4i_spdif_pm, }, .probe = sun4i_spdif_probe, diff --git a/sound/soc/sunxi/sun50i-dmic.c b/sound/soc/sunxi/sun50i-dmic.c index c10439b9e0a2..2599683a582d 100644 --- a/sound/soc/sunxi/sun50i-dmic.c +++ b/sound/soc/sunxi/sun50i-dmic.c @@ -236,6 +236,7 @@ static int sun50i_dmic_soc_dai_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops sun50i_dmic_dai_ops = { + .probe = sun50i_dmic_soc_dai_probe, .startup = sun50i_dmic_startup, .trigger = sun50i_dmic_trigger, .hw_params = sun50i_dmic_hw_params, @@ -260,7 +261,6 @@ static struct snd_soc_dai_driver sun50i_dmic_dai = { .formats = SUN50I_DMIC_FORMATS, .sig_bits = 21, }, - .probe = sun50i_dmic_soc_dai_probe, .ops = &sun50i_dmic_dai_ops, .name = "dmic", }; diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 60e7df41c64c..e713feca25fa 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -203,10 +203,6 @@ static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { - .trigger = tegra20_ac97_trigger, -}; - static int tegra20_ac97_probe(struct snd_soc_dai *dai) { struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); @@ -217,9 +213,13 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { + .probe = tegra20_ac97_probe, + .trigger = tegra20_ac97_trigger, +}; + static struct snd_soc_dai_driver tegra20_ac97_dai = { .name = "tegra-ac97-pcm", - .probe = tegra20_ac97_probe, .playback = { .stream_name = "PCM Playback", .channels_min = 2, diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index d38b58305c6b..f11618e8f13e 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -310,6 +310,7 @@ static int tegra20_i2s_startup(struct snd_pcm_substream *substream, } static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { + .probe = tegra20_i2s_probe, .set_fmt = tegra20_i2s_set_fmt, .hw_params = tegra20_i2s_hw_params, .trigger = tegra20_i2s_trigger, @@ -317,7 +318,6 @@ static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { }; static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { - .probe = tegra20_i2s_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index d034803695a0..b0670aa4d967 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -241,6 +241,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { + .probe = tegra20_spdif_probe, .hw_params = tegra20_spdif_hw_params, .trigger = tegra20_spdif_trigger, .startup = tegra20_spdif_startup, @@ -248,7 +249,6 @@ static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { static struct snd_soc_dai_driver tegra20_spdif_dai = { .name = "tegra20-spdif", - .probe = tegra20_spdif_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 6868508585a0..9f9334e48049 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -419,11 +419,6 @@ static int tegra_admaif_trigger(struct snd_pcm_substream *substream, int cmd, } } -static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { - .hw_params = tegra_admaif_hw_params, - .trigger = tegra_admaif_trigger, -}; - static int tegra210_admaif_pget_mono_to_stereo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -550,10 +545,15 @@ static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops tegra_admaif_dai_ops = { + .probe = tegra_admaif_dai_probe, + .hw_params = tegra_admaif_hw_params, + .trigger = tegra_admaif_trigger, +}; + #define DAI(dai_name) \ { \ .name = dai_name, \ - .probe = tegra_admaif_dai_probe, \ .playback = { \ .stream_name = dai_name " Playback", \ .channels_min = 1, \ diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 644280603095..81eaece51130 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -304,6 +304,7 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { + .probe = tegra30_i2s_probe, .set_fmt = tegra30_i2s_set_fmt, .hw_params = tegra30_i2s_hw_params, .trigger = tegra30_i2s_trigger, @@ -311,7 +312,6 @@ static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = { }; static const struct snd_soc_dai_driver tegra30_i2s_dai_template = { - .probe = tegra30_i2s_probe, .playback = { .stream_name = "Playback", .channels_min = 2, diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c index 983d69b951b0..544cb3da50eb 100644 --- a/sound/soc/ti/davinci-evm.c +++ b/sound/soc/ti/davinci-evm.c @@ -257,18 +257,7 @@ static struct platform_driver davinci_evm_driver = { }, }; -static int __init evm_init(void) -{ - return platform_driver_register(&davinci_evm_driver); -} - -static void __exit evm_exit(void) -{ - platform_driver_unregister(&davinci_evm_driver); -} - -module_init(evm_init); -module_exit(evm_exit); +module_platform_driver(davinci_evm_driver); MODULE_AUTHOR("Vladimir Barinov"); MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index 97dd1634b6be..07c8b2259208 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -601,16 +601,6 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S32_LE) -static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .shutdown = davinci_i2s_shutdown, - .prepare = davinci_i2s_prepare, - .trigger = davinci_i2s_trigger, - .hw_params = davinci_i2s_hw_params, - .set_fmt = davinci_i2s_set_dai_fmt, - .set_clkdiv = davinci_i2s_dai_set_clkdiv, - -}; - static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); @@ -622,8 +612,18 @@ static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { + .probe = davinci_i2s_dai_probe, + .shutdown = davinci_i2s_shutdown, + .prepare = davinci_i2s_prepare, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params, + .set_fmt = davinci_i2s_set_dai_fmt, + .set_clkdiv = davinci_i2s_dai_set_clkdiv, + +}; + static struct snd_soc_dai_driver davinci_i2s_dai = { - .probe = davinci_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 172fea764a31..7e7d665a5504 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1616,18 +1616,6 @@ static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, } } -static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { - .startup = davinci_mcasp_startup, - .shutdown = davinci_mcasp_shutdown, - .trigger = davinci_mcasp_trigger, - .delay = davinci_mcasp_delay, - .hw_params = davinci_mcasp_hw_params, - .set_fmt = davinci_mcasp_set_dai_fmt, - .set_clkdiv = davinci_mcasp_set_clkdiv, - .set_sysclk = davinci_mcasp_set_sysclk, - .set_tdm_slot = davinci_mcasp_set_tdm_slot, -}; - static int davinci_mcasp_iec958_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1716,6 +1704,19 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .probe = davinci_mcasp_dai_probe, + .startup = davinci_mcasp_startup, + .shutdown = davinci_mcasp_shutdown, + .trigger = davinci_mcasp_trigger, + .delay = davinci_mcasp_delay, + .hw_params = davinci_mcasp_hw_params, + .set_fmt = davinci_mcasp_set_dai_fmt, + .set_clkdiv = davinci_mcasp_set_clkdiv, + .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, +}; + #define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \ @@ -1732,7 +1733,6 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver davinci_mcasp_dai[] = { { .name = "davinci-mcasp.0", - .probe = davinci_mcasp_dai_probe, .playback = { .stream_name = "IIS Playback", .channels_min = 1, @@ -1753,7 +1753,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { }, { .name = "davinci-mcasp.1", - .probe = davinci_mcasp_dai_probe, .playback = { .stream_name = "DIT Playback", .channels_min = 1, diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c index cb60af36dbc3..5b5eccf303ab 100644 --- a/sound/soc/ti/omap-dmic.c +++ b/sound/soc/ti/omap-dmic.c @@ -401,15 +401,6 @@ static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, return -EINVAL; } -static const struct snd_soc_dai_ops omap_dmic_dai_ops = { - .startup = omap_dmic_dai_startup, - .shutdown = omap_dmic_dai_shutdown, - .hw_params = omap_dmic_dai_hw_params, - .prepare = omap_dmic_dai_prepare, - .trigger = omap_dmic_dai_trigger, - .set_sysclk = omap_dmic_set_dai_sysclk, -}; - static int omap_dmic_probe(struct snd_soc_dai *dai) { struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); @@ -438,10 +429,19 @@ static int omap_dmic_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops omap_dmic_dai_ops = { + .probe = omap_dmic_probe, + .remove = omap_dmic_remove, + .startup = omap_dmic_dai_startup, + .shutdown = omap_dmic_dai_shutdown, + .hw_params = omap_dmic_dai_hw_params, + .prepare = omap_dmic_dai_prepare, + .trigger = omap_dmic_dai_trigger, + .set_sysclk = omap_dmic_set_dai_sysclk, +}; + static struct snd_soc_dai_driver omap_dmic_dai = { .name = "omap-dmic", - .probe = omap_dmic_probe, - .remove = omap_dmic_remove, .capture = { .channels_min = 2, .channels_max = 6, diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index f9fe96b61852..fdabed5133e8 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -1254,18 +1254,6 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, return err; } -static const struct snd_soc_dai_ops mcbsp_dai_ops = { - .startup = omap_mcbsp_dai_startup, - .shutdown = omap_mcbsp_dai_shutdown, - .prepare = omap_mcbsp_dai_prepare, - .trigger = omap_mcbsp_dai_trigger, - .delay = omap_mcbsp_dai_delay, - .hw_params = omap_mcbsp_dai_hw_params, - .set_fmt = omap_mcbsp_dai_set_dai_fmt, - .set_clkdiv = omap_mcbsp_dai_set_clkdiv, - .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, -}; - static int omap_mcbsp_probe(struct snd_soc_dai *dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); @@ -1288,9 +1276,21 @@ static int omap_mcbsp_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops mcbsp_dai_ops = { + .probe = omap_mcbsp_probe, + .remove = omap_mcbsp_remove, + .startup = omap_mcbsp_dai_startup, + .shutdown = omap_mcbsp_dai_shutdown, + .prepare = omap_mcbsp_dai_prepare, + .trigger = omap_mcbsp_dai_trigger, + .delay = omap_mcbsp_dai_delay, + .hw_params = omap_mcbsp_dai_hw_params, + .set_fmt = omap_mcbsp_dai_set_dai_fmt, + .set_clkdiv = omap_mcbsp_dai_set_clkdiv, + .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, +}; + static struct snd_soc_dai_driver omap_mcbsp_dai = { - .probe = omap_mcbsp_probe, - .remove = omap_mcbsp_remove, .playback = { .channels_min = 1, .channels_max = 16, diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c index 35deceb73427..d7d9f708f1fd 100644 --- a/sound/soc/ti/omap-mcpdm.c +++ b/sound/soc/ti/omap-mcpdm.c @@ -404,13 +404,6 @@ static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, return 0; } -static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { - .startup = omap_mcpdm_dai_startup, - .shutdown = omap_mcpdm_dai_shutdown, - .hw_params = omap_mcpdm_dai_hw_params, - .prepare = omap_mcpdm_prepare, -}; - static int omap_mcpdm_probe(struct snd_soc_dai *dai) { struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); @@ -457,6 +450,17 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai) return 0; } +static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { + .probe = omap_mcpdm_probe, + .remove = omap_mcpdm_remove, + .startup = omap_mcpdm_dai_startup, + .shutdown = omap_mcpdm_dai_shutdown, + .hw_params = omap_mcpdm_dai_hw_params, + .prepare = omap_mcpdm_prepare, + .probe_order = SND_SOC_COMP_ORDER_LATE, + .remove_order = SND_SOC_COMP_ORDER_EARLY, +}; + #ifdef CONFIG_PM_SLEEP static int omap_mcpdm_suspend(struct snd_soc_component *component) { @@ -502,10 +506,6 @@ static int omap_mcpdm_resume(struct snd_soc_component *component) #define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE static struct snd_soc_dai_driver omap_mcpdm_dai = { - .probe = omap_mcpdm_probe, - .remove = omap_mcpdm_remove, - .probe_order = SND_SOC_COMP_ORDER_LATE, - .remove_order = SND_SOC_COMP_ORDER_EARLY, .playback = { .channels_min = 1, .channels_max = 5, diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c index 4e8d5f7532ba..7c5188477b7c 100644 --- a/sound/soc/uniphier/aio-cpu.c +++ b/sound/soc/uniphier/aio-cpu.c @@ -355,30 +355,7 @@ static int uniphier_aio_prepare(struct snd_pcm_substream *substream, return 0; } -const struct snd_soc_dai_ops uniphier_aio_i2s_ops = { - .set_sysclk = uniphier_aio_set_sysclk, - .set_pll = uniphier_aio_set_pll, - .set_fmt = uniphier_aio_set_fmt, - .startup = uniphier_aio_startup, - .shutdown = uniphier_aio_shutdown, - .hw_params = uniphier_aio_hw_params, - .hw_free = uniphier_aio_hw_free, - .prepare = uniphier_aio_prepare, -}; -EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ops); - -const struct snd_soc_dai_ops uniphier_aio_spdif_ops = { - .set_sysclk = uniphier_aio_set_sysclk, - .set_pll = uniphier_aio_set_pll, - .startup = uniphier_aio_startup, - .shutdown = uniphier_aio_shutdown, - .hw_params = uniphier_aio_hw_params, - .hw_free = uniphier_aio_hw_free, - .prepare = uniphier_aio_prepare, -}; -EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ops); - -int uniphier_aio_dai_probe(struct snd_soc_dai *dai) +static int uniphier_aio_dai_probe(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); int i; @@ -403,9 +380,8 @@ int uniphier_aio_dai_probe(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_probe); -int uniphier_aio_dai_remove(struct snd_soc_dai *dai) +static int uniphier_aio_dai_remove(struct snd_soc_dai *dai) { struct uniphier_aio *aio = uniphier_priv(dai); @@ -413,7 +389,138 @@ int uniphier_aio_dai_remove(struct snd_soc_dai *dai) return 0; } -EXPORT_SYMBOL_GPL(uniphier_aio_dai_remove); + +static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai) +{ + int ret; + + ret = uniphier_aio_dai_probe(dai); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); + if (ret < 0) + return ret; + + return 0; +} + +static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai) +{ + int ret; + + ret = uniphier_aio_dai_probe(dai); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); + if (ret < 0) + return ret; + ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); + if (ret < 0) + return ret; + + return 0; +} + +const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .set_fmt = uniphier_aio_set_fmt, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ld11_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2 = { + .probe = uniphier_aio_ld11_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, + .compress_new = snd_soc_new_compress, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_ld11_ops2); + +const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .set_fmt = uniphier_aio_set_fmt, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_i2s_pxs2_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops); + +const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2 = { + .probe = uniphier_aio_pxs2_probe, + .remove = uniphier_aio_dai_remove, + .set_sysclk = uniphier_aio_set_sysclk, + .set_pll = uniphier_aio_set_pll, + .startup = uniphier_aio_startup, + .shutdown = uniphier_aio_shutdown, + .hw_params = uniphier_aio_hw_params, + .hw_free = uniphier_aio_hw_free, + .prepare = uniphier_aio_prepare, + .compress_new = snd_soc_new_compress, +}; +EXPORT_SYMBOL_GPL(uniphier_aio_spdif_pxs2_ops2); static void uniphier_aio_dai_suspend(struct snd_soc_dai *dai) { diff --git a/sound/soc/uniphier/aio-ld11.c b/sound/soc/uniphier/aio-ld11.c index 7b3cf5d751f6..15dbded63804 100644 --- a/sound/soc/uniphier/aio-ld11.c +++ b/sound/soc/uniphier/aio-ld11.c @@ -188,36 +188,9 @@ static const struct uniphier_aio_pll uniphier_aio_pll_ld11[] = { [AUD_PLL_HSC0] = { .enable = true, }, }; -static int uniphier_aio_ld11_probe(struct snd_soc_dai *dai) -{ - int ret; - - ret = uniphier_aio_dai_probe(dai); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); - if (ret < 0) - return ret; - - return 0; -} - static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { { .name = AUD_GNAME_HDMI, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -234,12 +207,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_PCMIN2, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .capture = { .stream_name = AUD_NAME_PCMIN2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -247,12 +218,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_GNAME_LINE, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -267,12 +236,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HPCMOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -280,12 +247,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 8, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_PCMOUT3, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -293,12 +258,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HIECOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -306,12 +269,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_ld11_ops, }, { .name = AUD_NAME_EPCMOUT2, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_EPCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -321,12 +282,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_EPCMOUT3, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_EPCMOUT3, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -336,19 +295,16 @@ static struct snd_soc_dai_driver uniphier_aio_dai_ld11[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_ld11_ops, }, { .name = AUD_NAME_HIECCOMPOUT1, - .probe = uniphier_aio_ld11_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_ld11_ops2, }, }; diff --git a/sound/soc/uniphier/aio-pxs2.c b/sound/soc/uniphier/aio-pxs2.c index 899904f7ffd6..305cb2a1253d 100644 --- a/sound/soc/uniphier/aio-pxs2.c +++ b/sound/soc/uniphier/aio-pxs2.c @@ -141,36 +141,9 @@ static const struct uniphier_aio_pll uniphier_aio_pll_pxs2[] = { [AUD_PLL_HSC0] = { .enable = true, }, }; -static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai) -{ - int ret; - - ret = uniphier_aio_dai_probe(dai); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A1, 0, 0, 36864000); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F1, 0, 0, 36864000); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_pll(dai, AUD_PLL_A2, 0, 0, 33868800); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(dai, AUD_PLL_F2, 0, 0, 33868800); - if (ret < 0) - return ret; - - return 0; -} - static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { { .name = AUD_GNAME_HDMI, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HPCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -178,12 +151,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_GNAME_LINE, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -198,12 +169,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_GNAME_AUX, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_PCMOUT2, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -218,12 +187,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_i2s_ops, + .ops = &uniphier_aio_i2s_pxs2_ops, }, { .name = AUD_NAME_HIECOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_HIECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -231,12 +198,10 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops, }, { .name = AUD_NAME_IECOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, .playback = { .stream_name = AUD_NAME_IECOUT1, .formats = SNDRV_PCM_FMTBIT_S32_LE, @@ -244,31 +209,25 @@ static struct snd_soc_dai_driver uniphier_aio_dai_pxs2[] = { .channels_min = 2, .channels_max = 2, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops, }, { .name = AUD_NAME_HIECCOMPOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_HIECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops2, }, { .name = AUD_NAME_IECCOMPOUT1, - .probe = uniphier_aio_pxs2_probe, - .remove = uniphier_aio_dai_remove, - .compress_new = snd_soc_new_compress, .playback = { .stream_name = AUD_NAME_IECCOMPOUT1, .channels_min = 1, .channels_max = 1, }, - .ops = &uniphier_aio_spdif_ops, + .ops = &uniphier_aio_spdif_pxs2_ops2, }, }; diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h index 0b03571aa9f0..09ccb47337fd 100644 --- a/sound/soc/uniphier/aio.h +++ b/sound/soc/uniphier/aio.h @@ -306,12 +306,14 @@ static inline struct uniphier_aio *uniphier_priv(struct snd_soc_dai *dai) int uniphier_aiodma_soc_register_platform(struct platform_device *pdev); extern const struct snd_compress_ops uniphier_aio_compress_ops; -int uniphier_aio_dai_probe(struct snd_soc_dai *dai); -int uniphier_aio_dai_remove(struct snd_soc_dai *dai); int uniphier_aio_probe(struct platform_device *pdev); int uniphier_aio_remove(struct platform_device *pdev); -extern const struct snd_soc_dai_ops uniphier_aio_i2s_ops; -extern const struct snd_soc_dai_ops uniphier_aio_spdif_ops; +extern const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops; +extern const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_ld11_ops2; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops; +extern const struct snd_soc_dai_ops uniphier_aio_spdif_pxs2_ops2; u64 aio_rb_cnt(struct uniphier_aio_sub *sub); u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub); diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 44e88dad8584..cde0dd8e2569 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -683,6 +683,7 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { { + .probe = ux500_msp_dai_of_probe, .set_sysclk = ux500_msp_dai_set_dai_sysclk, .set_fmt = ux500_msp_dai_set_dai_fmt, .set_tdm_slot = ux500_msp_dai_set_tdm_slot, @@ -695,7 +696,6 @@ static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = { }; static struct snd_soc_dai_driver ux500_msp_dai_drv = { - .probe = ux500_msp_dai_of_probe, .playback.channels_min = UX500_MSP_MIN_CHANNELS, .playback.channels_max = UX500_MSP_MAX_CHANNELS, .playback.rates = UX500_I2S_RATES, diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index cb8ca46213be..1f6d904c6481 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -14,7 +14,7 @@ * Defines x86 CPU feature bits */ #define NCAPINTS 21 /* N 32-bit words worth of info */ -#define NBUGINTS 1 /* N 32-bit bug flags */ +#define NBUGINTS 2 /* N 32-bit bug flags */ /* * Note: If the comment begins with a quoted string, that string is used diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index 3aedae61af4f..a00a53e15ab7 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -545,6 +545,7 @@ #define MSR_AMD64_DE_CFG 0xc0011029 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT 1 #define MSR_AMD64_DE_CFG_LFENCE_SERIALIZE BIT_ULL(MSR_AMD64_DE_CFG_LFENCE_SERIALIZE_BIT) +#define MSR_AMD64_DE_CFG_ZEN2_FP_BACKUP_FIX_BIT 9 #define MSR_AMD64_BU_CFG2 0xc001102a #define MSR_AMD64_IBSFETCHCTL 0xc0011030 diff --git a/tools/counter/Makefile b/tools/counter/Makefile index a0f4cab71fe5..b2c2946f44c9 100644 --- a/tools/counter/Makefile +++ b/tools/counter/Makefile @@ -40,7 +40,8 @@ $(OUTPUT)counter_example: $(COUNTER_EXAMPLE) clean: rm -f $(ALL_PROGRAMS) rm -rf $(OUTPUT)include/linux/counter.h - rmdir -p $(OUTPUT)include/linux + rm -df $(OUTPUT)include/linux + rm -df $(OUTPUT)include find $(or $(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete install: $(ALL_PROGRAMS) diff --git a/tools/hv/vmbus_testing b/tools/hv/vmbus_testing index e7212903dd1d..4467979d8f69 100755 --- a/tools/hv/vmbus_testing +++ b/tools/hv/vmbus_testing @@ -164,7 +164,7 @@ def recursive_file_lookup(path, file_map): def get_all_devices_test_status(file_map): for device in file_map: - if (get_test_state(locate_state(device, file_map)) is 1): + if (get_test_state(locate_state(device, file_map)) == 1): print("Testing = ON for: {}" .format(device.split("/")[5])) else: @@ -203,7 +203,7 @@ def write_test_files(path, value): def set_test_state(state_path, state_value, quiet): write_test_files(state_path, state_value) - if (get_test_state(state_path) is 1): + if (get_test_state(state_path) == 1): if (not quiet): print("Testing = ON for device: {}" .format(state_path.split("/")[5])) diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 2e1caabecb18..2d51fa8da9e8 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -824,5 +824,8 @@ bool arch_is_retpoline(struct symbol *sym) bool arch_is_rethunk(struct symbol *sym) { - return !strcmp(sym->name, "__x86_return_thunk"); + return !strcmp(sym->name, "__x86_return_thunk") || + !strcmp(sym->name, "srso_untrain_ret") || + !strcmp(sym->name, "srso_safe_ret") || + !strcmp(sym->name, "__ret"); } diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index 561de0cb6b95..512a8f13c4de 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -54,10 +54,11 @@ double perf_pmu__cpu_slots_per_cycle(void) perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, "caps/slots"); /* - * The value of slots is not greater than 32 bits, but sysfs__read_int - * can't read value with 0x prefix, so use sysfs__read_ull instead. + * The value of slots is not greater than 32 bits, but + * filename__read_int can't read value with 0x prefix, + * so use filename__read_ull instead. */ - sysfs__read_ull(path, &slots); + filename__read_ull(path, &slots); } return slots ? (double)slots : NAN; diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c index b7223feec770..5f3edb3004d8 100644 --- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c +++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c @@ -250,6 +250,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain) if (!chain || chain->nr < 3) return skip_slot; + addr_location__init(&al); ip = chain->ips[1]; thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); @@ -259,6 +260,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain) if (!dso) { pr_debug("%" PRIx64 " dso is NULL\n", ip); + addr_location__exit(&al); return skip_slot; } @@ -279,5 +281,7 @@ int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain) */ skip_slot = 3; } + + addr_location__exit(&al); return skip_slot; } diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index b2f82847e4c3..658fb9599d95 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1631,6 +1631,16 @@ static bool test__pmu_cpu_valid(void) return !!perf_pmus__find("cpu"); } +static bool test__pmu_cpu_event_valid(void) +{ + struct perf_pmu *pmu = perf_pmus__find("cpu"); + + if (!pmu) + return false; + + return perf_pmu__has_format(pmu, "event"); +} + static bool test__intel_pt_valid(void) { return !!perf_pmus__find("intel_pt"); @@ -2179,7 +2189,7 @@ static const struct evlist_test test__events_pmu[] = { }, { .name = "cpu/name='COMPLEX_CYCLES_NAME:orig=cycles,desc=chip-clock-ticks',period=0x1,event=0x2/ukp", - .valid = test__pmu_cpu_valid, + .valid = test__pmu_cpu_event_valid, .check = test__checkevent_complex_name, /* 3 */ }, diff --git a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh index 00d2e0e2e0c2..319f36ebb9a4 100755 --- a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh +++ b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh @@ -4,6 +4,12 @@ set -e +# skip if there's no gcc +if ! [ -x "$(command -v gcc)" ]; then + echo "failed: no gcc compiler" + exit 2 +fi + temp_dir=$(mktemp -d /tmp/perf-uprobe-different-cu-sh.XXXXXXXXXX) cleanup() @@ -11,7 +17,7 @@ cleanup() trap - EXIT TERM INT if [[ "${temp_dir}" =~ ^/tmp/perf-uprobe-different-cu-sh.*$ ]]; then echo "--- Cleaning up ---" - perf probe -x ${temp_dir}/testfile -d foo + perf probe -x ${temp_dir}/testfile -d foo || true rm -f "${temp_dir}/"* rmdir "${temp_dir}" fi diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 4e62843d51b7..f4cb41ee23cd 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -45,7 +45,6 @@ static void __machine__remove_thread(struct machine *machine, struct thread_rb_node *nd, struct thread *th, bool lock); -static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip); static struct dso *machine__kernel_dso(struct machine *machine) { @@ -2385,10 +2384,6 @@ static int add_callchain_ip(struct thread *thread, ms.maps = maps__get(al.maps); ms.map = map__get(al.map); ms.sym = al.sym; - - if (!branch && append_inlines(cursor, &ms, ip) == 0) - goto out; - srcline = callchain_srcline(&ms, al.addr); err = callchain_cursor_append(cursor, ip, &ms, branch, flags, nr_loop_iter, diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index acde097e327c..c9ec0cafb69d 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -2100,16 +2100,16 @@ __weak int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs) return lhs->core.idx - rhs->core.idx; } -static int evlist__cmp(void *state, const struct list_head *l, const struct list_head *r) +static int evlist__cmp(void *_fg_idx, const struct list_head *l, const struct list_head *r) { const struct perf_evsel *lhs_core = container_of(l, struct perf_evsel, node); const struct evsel *lhs = container_of(lhs_core, struct evsel, core); const struct perf_evsel *rhs_core = container_of(r, struct perf_evsel, node); const struct evsel *rhs = container_of(rhs_core, struct evsel, core); - int *leader_idx = state; - int lhs_leader_idx = *leader_idx, rhs_leader_idx = *leader_idx, ret; + int *force_grouped_idx = _fg_idx; + int lhs_sort_idx, rhs_sort_idx, ret; const char *lhs_pmu_name, *rhs_pmu_name; - bool lhs_has_group = false, rhs_has_group = false; + bool lhs_has_group, rhs_has_group; /* * First sort by grouping/leader. Read the leader idx only if the evsel @@ -2121,15 +2121,25 @@ static int evlist__cmp(void *state, const struct list_head *l, const struct list */ if (lhs_core->leader != lhs_core || lhs_core->nr_members > 1) { lhs_has_group = true; - lhs_leader_idx = lhs_core->leader->idx; + lhs_sort_idx = lhs_core->leader->idx; + } else { + lhs_has_group = false; + lhs_sort_idx = *force_grouped_idx != -1 && arch_evsel__must_be_in_group(lhs) + ? *force_grouped_idx + : lhs_core->idx; } if (rhs_core->leader != rhs_core || rhs_core->nr_members > 1) { rhs_has_group = true; - rhs_leader_idx = rhs_core->leader->idx; + rhs_sort_idx = rhs_core->leader->idx; + } else { + rhs_has_group = false; + rhs_sort_idx = *force_grouped_idx != -1 && arch_evsel__must_be_in_group(rhs) + ? *force_grouped_idx + : rhs_core->idx; } - if (lhs_leader_idx != rhs_leader_idx) - return lhs_leader_idx - rhs_leader_idx; + if (lhs_sort_idx != rhs_sort_idx) + return lhs_sort_idx - rhs_sort_idx; /* Group by PMU if there is a group. Groups can't span PMUs. */ if (lhs_has_group && rhs_has_group) { @@ -2146,10 +2156,10 @@ static int evlist__cmp(void *state, const struct list_head *l, const struct list static int parse_events__sort_events_and_fix_groups(struct list_head *list) { - int idx = 0, unsorted_idx = -1; + int idx = 0, force_grouped_idx = -1; struct evsel *pos, *cur_leader = NULL; struct perf_evsel *cur_leaders_grp = NULL; - bool idx_changed = false; + bool idx_changed = false, cur_leader_force_grouped = false; int orig_num_leaders = 0, num_leaders = 0; int ret; @@ -2174,12 +2184,14 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) */ pos->core.idx = idx++; - if (unsorted_idx == -1 && pos == pos_leader && pos->core.nr_members < 2) - unsorted_idx = pos->core.idx; + /* Remember an index to sort all forced grouped events together to. */ + if (force_grouped_idx == -1 && pos == pos_leader && pos->core.nr_members < 2 && + arch_evsel__must_be_in_group(pos)) + force_grouped_idx = pos->core.idx; } /* Sort events. */ - list_sort(&unsorted_idx, list, evlist__cmp); + list_sort(&force_grouped_idx, list, evlist__cmp); /* * Recompute groups, splitting for PMUs and adding groups for events @@ -2189,8 +2201,9 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) list_for_each_entry(pos, list, core.node) { const struct evsel *pos_leader = evsel__leader(pos); const char *pos_pmu_name = pos->group_pmu_name; - const char *cur_leader_pmu_name, *pos_leader_pmu_name; - bool force_grouped = arch_evsel__must_be_in_group(pos); + const char *cur_leader_pmu_name; + bool pos_force_grouped = force_grouped_idx != -1 && + arch_evsel__must_be_in_group(pos); /* Reset index and nr_members. */ if (pos->core.idx != idx) @@ -2206,7 +2219,8 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) cur_leader = pos; cur_leader_pmu_name = cur_leader->group_pmu_name; - if ((cur_leaders_grp != pos->core.leader && !force_grouped) || + if ((cur_leaders_grp != pos->core.leader && + (!pos_force_grouped || !cur_leader_force_grouped)) || strcmp(cur_leader_pmu_name, pos_pmu_name)) { /* Event is for a different group/PMU than last. */ cur_leader = pos; @@ -2216,14 +2230,14 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) * group. */ cur_leaders_grp = pos->core.leader; - } - pos_leader_pmu_name = pos_leader->group_pmu_name; - if (strcmp(pos_leader_pmu_name, pos_pmu_name) || force_grouped) { /* - * Event's PMU differs from its leader's. Groups can't - * span PMUs, so update leader from the group/PMU - * tracker. + * Avoid forcing events into groups with events that + * don't need to be in the group. */ + cur_leader_force_grouped = pos_force_grouped; + } + if (pos_leader != cur_leader) { + /* The leader changed so update it. */ evsel__set_leader(pos, cur_leader); } } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7f984a7f16ca..28380e7aa8d0 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1440,6 +1440,17 @@ void perf_pmu__del_formats(struct list_head *formats) } } +bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name) +{ + struct perf_pmu_format *format; + + list_for_each_entry(format, &pmu->format, list) { + if (!strcmp(format->name, name)) + return true; + } + return false; +} + bool is_pmu_core(const char *name) { return !strcmp(name, "cpu") || !strcmp(name, "cpum_cf") || is_sysfs_pmu_core(name); diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 203b92860e3c..6b414cecbad2 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -234,6 +234,7 @@ int perf_pmu__new_format(struct list_head *list, char *name, void perf_pmu__set_format(unsigned long *bits, long from, long to); int perf_pmu__format_parse(int dirfd, struct list_head *head); void perf_pmu__del_formats(struct list_head *formats); +bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name); bool is_pmu_core(const char *name); bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu); diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 3cd9de42139e..c58ba9fb6a36 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -152,16 +152,14 @@ static void pmu_read_sysfs(bool core_only) } closedir(dir); - if (core_only) { - if (!list_empty(&core_pmus)) - read_sysfs_core_pmus = true; - else { - if (perf_pmu__create_placeholder_core_pmu(&core_pmus)) - read_sysfs_core_pmus = true; - } - } else { + if (list_empty(&core_pmus)) { + if (!perf_pmu__create_placeholder_core_pmu(&core_pmus)) + pr_err("Failure to set up any core PMUs\n"); + } + if (!list_empty(&core_pmus)) { read_sysfs_core_pmus = true; - read_sysfs_all_pmus = true; + if (!core_only) + read_sysfs_all_pmus = true; } } diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 7329b3340f88..d45d5dcb0e2b 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -931,6 +931,11 @@ static bool should_skip_zero_counter(struct perf_stat_config *config, */ if (config->aggr_mode == AGGR_THREAD && config->system_wide) return true; + + /* Tool events have the software PMU but are only gathered on 1. */ + if (evsel__is_tool(counter)) + return true; + /* * Skip value 0 when it's an uncore event and the given aggr id * does not belong to the PMU cpumask. diff --git a/tools/testing/radix-tree/regression1.c b/tools/testing/radix-tree/regression1.c index a61c7bcbc72d..63f468bf8245 100644 --- a/tools/testing/radix-tree/regression1.c +++ b/tools/testing/radix-tree/regression1.c @@ -177,7 +177,7 @@ void regression1_test(void) nr_threads = 2; pthread_barrier_init(&worker_barrier, NULL, nr_threads); - threads = malloc(nr_threads * sizeof(pthread_t *)); + threads = malloc(nr_threads * sizeof(*threads)); for (i = 0; i < nr_threads; i++) { arg = i; diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index b4f6f3a50ae5..5674a9d0cacf 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -869,6 +869,77 @@ static void test_msg_redir_to_listening(struct test_sockmap_listen *skel, xbpf_prog_detach2(verdict, sock_map, BPF_SK_MSG_VERDICT); } +static void redir_partial(int family, int sotype, int sock_map, int parser_map) +{ + int s, c0, c1, p0, p1; + int err, n, key, value; + char buf[] = "abc"; + + key = 0; + value = sizeof(buf) - 1; + err = xbpf_map_update_elem(parser_map, &key, &value, 0); + if (err) + return; + + s = socket_loopback(family, sotype | SOCK_NONBLOCK); + if (s < 0) + goto clean_parser_map; + + err = create_socket_pairs(s, family, sotype, &c0, &c1, &p0, &p1); + if (err) + goto close_srv; + + err = add_to_sockmap(sock_map, p0, p1); + if (err) + goto close; + + n = xsend(c1, buf, sizeof(buf), 0); + if (n < sizeof(buf)) + FAIL("incomplete write"); + + n = xrecv_nonblock(c0, buf, sizeof(buf), 0); + if (n != sizeof(buf) - 1) + FAIL("expect %zu, received %d", sizeof(buf) - 1, n); + +close: + xclose(c0); + xclose(p0); + xclose(c1); + xclose(p1); +close_srv: + xclose(s); + +clean_parser_map: + key = 0; + value = 0; + xbpf_map_update_elem(parser_map, &key, &value, 0); +} + +static void test_skb_redir_partial(struct test_sockmap_listen *skel, + struct bpf_map *inner_map, int family, + int sotype) +{ + int verdict = bpf_program__fd(skel->progs.prog_stream_verdict); + int parser = bpf_program__fd(skel->progs.prog_stream_parser); + int parser_map = bpf_map__fd(skel->maps.parser_map); + int sock_map = bpf_map__fd(inner_map); + int err; + + err = xbpf_prog_attach(parser, sock_map, BPF_SK_SKB_STREAM_PARSER, 0); + if (err) + return; + + err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_STREAM_VERDICT, 0); + if (err) + goto detach; + + redir_partial(family, sotype, sock_map, parser_map); + + xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_STREAM_VERDICT); +detach: + xbpf_prog_detach2(parser, sock_map, BPF_SK_SKB_STREAM_PARSER); +} + static void test_reuseport_select_listening(int family, int sotype, int sock_map, int verd_map, int reuseport_prog) @@ -1243,6 +1314,7 @@ static void test_redir(struct test_sockmap_listen *skel, struct bpf_map *map, } tests[] = { TEST(test_skb_redir_to_connected), TEST(test_skb_redir_to_listening), + TEST(test_skb_redir_partial), TEST(test_msg_redir_to_connected), TEST(test_msg_redir_to_listening), }; @@ -1432,7 +1504,7 @@ static void vsock_unix_redir_connectible(int sock_mapfd, int verd_mapfd, if (n < 1) goto out; - n = recv(mode == REDIR_INGRESS ? u0 : u1, &b, sizeof(b), MSG_DONTWAIT); + n = xrecv_nonblock(mode == REDIR_INGRESS ? u0 : u1, &b, sizeof(b), 0); if (n < 0) FAIL("%s: recv() err, errno=%d", log_prefix, errno); if (n == 0) diff --git a/tools/testing/selftests/bpf/progs/test_sockmap_listen.c b/tools/testing/selftests/bpf/progs/test_sockmap_listen.c index 325c9f193432..464d35bd57c7 100644 --- a/tools/testing/selftests/bpf/progs/test_sockmap_listen.c +++ b/tools/testing/selftests/bpf/progs/test_sockmap_listen.c @@ -28,12 +28,26 @@ struct { __type(value, unsigned int); } verdict_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, int); + __type(value, int); +} parser_map SEC(".maps"); + bool test_sockmap = false; /* toggled by user-space */ bool test_ingress = false; /* toggled by user-space */ SEC("sk_skb/stream_parser") int prog_stream_parser(struct __sk_buff *skb) { + int *value; + __u32 key = 0; + + value = bpf_map_lookup_elem(&parser_map, &key); + if (value && *value) + return *value; + return skb->len; } diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c index 258ddc565deb..1b2cec9d18a4 100644 --- a/tools/testing/selftests/cgroup/test_kmem.c +++ b/tools/testing/selftests/cgroup/test_kmem.c @@ -70,6 +70,10 @@ static int test_kmem_basic(const char *root) goto cleanup; cg_write(cg, "memory.high", "1M"); + + /* wait for RCU freeing */ + sleep(1); + slab1 = cg_read_key_long(cg, "memory.stat", "slab "); if (slab1 <= 0) goto cleanup; diff --git a/tools/testing/selftests/mm/ksm_tests.c b/tools/testing/selftests/mm/ksm_tests.c index 435acebdc325..380b691d3eb9 100644 --- a/tools/testing/selftests/mm/ksm_tests.c +++ b/tools/testing/selftests/mm/ksm_tests.c @@ -831,6 +831,7 @@ int main(int argc, char *argv[]) printf("Size must be greater than 0\n"); return KSFT_FAIL; } + break; case 't': { int tmp = atoi(optarg); diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index 0f5e88c8f4ff..df8d90b51867 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -1981,6 +1981,11 @@ basic() run_cmd "$IP link set dev lo up" + # Dump should not loop endlessly when maximum nexthop ID is configured. + run_cmd "$IP nexthop add id $((2**32-1)) blackhole" + run_cmd "timeout 5 $IP nexthop" + log_test $? 0 "Maximum nexthop ID dump" + # # groups # @@ -2201,6 +2206,11 @@ basic_res() run_cmd "$IP nexthop bucket list fdb" log_test $? 255 "Dump all nexthop buckets with invalid 'fdb' keyword" + # Dump should not loop endlessly when maximum nexthop ID is configured. + run_cmd "$IP nexthop add id $((2**32-1)) group 1/2 type resilient buckets 4" + run_cmd "timeout 5 $IP nexthop bucket" + log_test $? 0 "Maximum nexthop ID dump" + # # resilient nexthop buckets get requests # diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb.sh b/tools/testing/selftests/net/forwarding/bridge_mdb.sh index ae3f9462a2b6..d0c6c499d5da 100755 --- a/tools/testing/selftests/net/forwarding/bridge_mdb.sh +++ b/tools/testing/selftests/net/forwarding/bridge_mdb.sh @@ -617,7 +617,7 @@ __cfg_test_port_ip_sg() grep -q "permanent" check_err $? "Entry not added as \"permanent\" when should" bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ - grep -q "0.00" + grep -q " 0.00" check_err $? "\"permanent\" entry has a pending group timer" bridge mdb del dev br0 port $swp1 $grp_key vid 10 @@ -626,7 +626,7 @@ __cfg_test_port_ip_sg() grep -q "temp" check_err $? "Entry not added as \"temp\" when should" bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ - grep -q "0.00" + grep -q " 0.00" check_fail $? "\"temp\" entry has an unpending group timer" bridge mdb del dev br0 port $swp1 $grp_key vid 10 @@ -659,7 +659,7 @@ __cfg_test_port_ip_sg() grep -q "permanent" check_err $? "Entry not marked as \"permanent\" after replace" bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ - grep -q "0.00" + grep -q " 0.00" check_err $? "Entry has a pending group timer after replace" bridge mdb replace dev br0 port $swp1 $grp_key vid 10 temp @@ -667,7 +667,7 @@ __cfg_test_port_ip_sg() grep -q "temp" check_err $? "Entry not marked as \"temp\" after replace" bridge -d -s mdb show dev br0 vid 10 | grep "$grp_key" | \ - grep -q "0.00" + grep -q " 0.00" check_fail $? "Entry has an unpending group timer after replace" bridge mdb del dev br0 port $swp1 $grp_key vid 10 @@ -850,6 +850,7 @@ cfg_test() __fwd_test_host_ip() { local grp=$1; shift + local dmac=$1; shift local src=$1; shift local mode=$1; shift local name @@ -872,27 +873,27 @@ __fwd_test_host_ip() # Packet should only be flooded to multicast router ports when there is # no matching MDB entry. The bridge is not configured as a multicast # router port. - $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $src -B $grp -t udp -q tc_check_packets "dev br0 ingress" 1 0 check_err $? "Packet locally received after flood" # Install a regular port group entry and expect the packet to not be # locally received. bridge mdb add dev br0 port $swp2 grp $grp temp vid 10 - $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $src -B $grp -t udp -q tc_check_packets "dev br0 ingress" 1 0 check_err $? "Packet locally received after installing a regular entry" # Add a host entry and expect the packet to be locally received. bridge mdb add dev br0 port br0 grp $grp temp vid 10 - $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $src -B $grp -t udp -q tc_check_packets "dev br0 ingress" 1 1 check_err $? "Packet not locally received after adding a host entry" # Remove the host entry and expect the packet to not be locally # received. bridge mdb del dev br0 port br0 grp $grp vid 10 - $MZ $mode $h1.10 -c 1 -p 128 -A $src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $src -B $grp -t udp -q tc_check_packets "dev br0 ingress" 1 1 check_err $? "Packet locally received after removing a host entry" @@ -905,8 +906,8 @@ __fwd_test_host_ip() fwd_test_host_ip() { - __fwd_test_host_ip "239.1.1.1" "192.0.2.1" "-4" - __fwd_test_host_ip "ff0e::1" "2001:db8:1::1" "-6" + __fwd_test_host_ip "239.1.1.1" "01:00:5e:01:01:01" "192.0.2.1" "-4" + __fwd_test_host_ip "ff0e::1" "33:33:00:00:00:01" "2001:db8:1::1" "-6" } fwd_test_host_l2() @@ -966,6 +967,7 @@ fwd_test_host() __fwd_test_port_ip() { local grp=$1; shift + local dmac=$1; shift local valid_src=$1; shift local invalid_src=$1; shift local mode=$1; shift @@ -999,43 +1001,43 @@ __fwd_test_port_ip() vlan_ethtype $eth_type vlan_id 10 dst_ip $grp \ src_ip $invalid_src action drop - $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $valid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 1 0 check_err $? "Packet from valid source received on H2 before adding entry" - $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $invalid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 2 0 check_err $? "Packet from invalid source received on H2 before adding entry" bridge mdb add dev br0 port $swp2 grp $grp vid 10 \ filter_mode $filter_mode source_list $src_list - $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $valid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 1 1 check_err $? "Packet from valid source not received on H2 after adding entry" - $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $invalid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 2 0 check_err $? "Packet from invalid source received on H2 after adding entry" bridge mdb replace dev br0 port $swp2 grp $grp vid 10 \ filter_mode exclude - $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $valid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 1 2 check_err $? "Packet from valid source not received on H2 after allowing all sources" - $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $invalid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 2 1 check_err $? "Packet from invalid source not received on H2 after allowing all sources" bridge mdb del dev br0 port $swp2 grp $grp vid 10 - $MZ $mode $h1.10 -c 1 -p 128 -A $valid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $valid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 1 2 check_err $? "Packet from valid source received on H2 after deleting entry" - $MZ $mode $h1.10 -c 1 -p 128 -A $invalid_src -B $grp -t udp -q + $MZ $mode $h1.10 -a own -b $dmac -c 1 -p 128 -A $invalid_src -B $grp -t udp -q tc_check_packets "dev $h2 ingress" 2 1 check_err $? "Packet from invalid source received on H2 after deleting entry" @@ -1047,11 +1049,11 @@ __fwd_test_port_ip() fwd_test_port_ip() { - __fwd_test_port_ip "239.1.1.1" "192.0.2.1" "192.0.2.2" "-4" "exclude" - __fwd_test_port_ip "ff0e::1" "2001:db8:1::1" "2001:db8:1::2" "-6" \ + __fwd_test_port_ip "239.1.1.1" "01:00:5e:01:01:01" "192.0.2.1" "192.0.2.2" "-4" "exclude" + __fwd_test_port_ip "ff0e::1" "33:33:00:00:00:01" "2001:db8:1::1" "2001:db8:1::2" "-6" \ "exclude" - __fwd_test_port_ip "239.1.1.1" "192.0.2.1" "192.0.2.2" "-4" "include" - __fwd_test_port_ip "ff0e::1" "2001:db8:1::1" "2001:db8:1::2" "-6" \ + __fwd_test_port_ip "239.1.1.1" "01:00:5e:01:01:01" "192.0.2.1" "192.0.2.2" "-4" "include" + __fwd_test_port_ip "ff0e::1" "33:33:00:00:00:01" "2001:db8:1::1" "2001:db8:1::2" "-6" \ "include" } @@ -1127,7 +1129,7 @@ ctrl_igmpv3_is_in_test() filter_mode include source_list 192.0.2.1 # IS_IN ( 192.0.2.2 ) - $MZ $h1.10 -c 1 -A 192.0.2.1 -B 239.1.1.1 \ + $MZ $h1.10 -c 1 -a own -b 01:00:5e:01:01:01 -A 192.0.2.1 -B 239.1.1.1 \ -t ip proto=2,p=$(igmpv3_is_in_get 239.1.1.1 192.0.2.2) -q bridge -d mdb show dev br0 vid 10 | grep 239.1.1.1 | grep -q 192.0.2.2 @@ -1140,7 +1142,7 @@ ctrl_igmpv3_is_in_test() filter_mode include source_list 192.0.2.1 # IS_IN ( 192.0.2.2 ) - $MZ $h1.10 -c 1 -A 192.0.2.1 -B 239.1.1.1 \ + $MZ $h1.10 -a own -b 01:00:5e:01:01:01 -c 1 -A 192.0.2.1 -B 239.1.1.1 \ -t ip proto=2,p=$(igmpv3_is_in_get 239.1.1.1 192.0.2.2) -q bridge -d mdb show dev br0 vid 10 | grep 239.1.1.1 | grep -v "src" | \ @@ -1167,7 +1169,7 @@ ctrl_mldv2_is_in_test() # IS_IN ( 2001:db8:1::2 ) local p=$(mldv2_is_in_get fe80::1 ff0e::1 2001:db8:1::2) - $MZ -6 $h1.10 -c 1 -A fe80::1 -B ff0e::1 \ + $MZ -6 $h1.10 -a own -b 33:33:00:00:00:01 -c 1 -A fe80::1 -B ff0e::1 \ -t ip hop=1,next=0,p="$p" -q bridge -d mdb show dev br0 vid 10 | grep ff0e::1 | \ @@ -1181,7 +1183,7 @@ ctrl_mldv2_is_in_test() filter_mode include source_list 2001:db8:1::1 # IS_IN ( 2001:db8:1::2 ) - $MZ -6 $h1.10 -c 1 -A fe80::1 -B ff0e::1 \ + $MZ -6 $h1.10 -a own -b 33:33:00:00:00:01 -c 1 -A fe80::1 -B ff0e::1 \ -t ip hop=1,next=0,p="$p" -q bridge -d mdb show dev br0 vid 10 | grep ff0e::1 | grep -v "src" | \ @@ -1206,6 +1208,11 @@ ctrl_test() ctrl_mldv2_is_in_test } +if ! bridge mdb help 2>&1 | grep -q "replace"; then + echo "SKIP: iproute2 too old, missing bridge mdb replace support" + exit $ksft_skip +fi + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh b/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh index ae255b662ba3..3da9d93ab36f 100755 --- a/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh +++ b/tools/testing/selftests/net/forwarding/bridge_mdb_max.sh @@ -252,7 +252,8 @@ ctl4_entries_add() local IPs=$(seq -f 192.0.2.%g 1 $((n - 1))) local peer=$(locus_dev_peer $locus) local GRP=239.1.1.${grp} - $MZ $peer -c 1 -A 192.0.2.1 -B $GRP \ + local dmac=01:00:5e:01:01:$(printf "%02x" $grp) + $MZ $peer -a own -b $dmac -c 1 -A 192.0.2.1 -B $GRP \ -t ip proto=2,p=$(igmpv3_is_in_get $GRP $IPs) -q sleep 1 @@ -272,7 +273,8 @@ ctl4_entries_del() local peer=$(locus_dev_peer $locus) local GRP=239.1.1.${grp} - $MZ $peer -c 1 -A 192.0.2.1 -B 224.0.0.2 \ + local dmac=01:00:5e:00:00:02 + $MZ $peer -a own -b $dmac -c 1 -A 192.0.2.1 -B 224.0.0.2 \ -t ip proto=2,p=$(igmpv2_leave_get $GRP) -q sleep 1 ! bridge mdb show dev br0 | grep -q $GRP @@ -289,8 +291,10 @@ ctl6_entries_add() local peer=$(locus_dev_peer $locus) local SIP=fe80::1 local GRP=ff0e::${grp} + local dmac=33:33:00:00:00:$(printf "%02x" $grp) local p=$(mldv2_is_in_get $SIP $GRP $IPs) - $MZ -6 $peer -c 1 -A $SIP -B $GRP -t ip hop=1,next=0,p="$p" -q + $MZ -6 $peer -a own -b $dmac -c 1 -A $SIP -B $GRP \ + -t ip hop=1,next=0,p="$p" -q sleep 1 local nn=$(bridge mdb show dev br0 | grep $GRP | wc -l) @@ -310,8 +314,10 @@ ctl6_entries_del() local peer=$(locus_dev_peer $locus) local SIP=fe80::1 local GRP=ff0e::${grp} + local dmac=33:33:00:00:00:$(printf "%02x" $grp) local p=$(mldv1_done_get $SIP $GRP) - $MZ -6 $peer -c 1 -A $SIP -B $GRP -t ip hop=1,next=0,p="$p" -q + $MZ -6 $peer -a own -b $dmac -c 1 -A $SIP -B $GRP \ + -t ip hop=1,next=0,p="$p" -q sleep 1 ! bridge mdb show dev br0 | grep -q $GRP } @@ -1328,6 +1334,11 @@ test_8021qvs() switch_destroy } +if ! bridge link help 2>&1 | grep -q "mcast_max_groups"; then + echo "SKIP: iproute2 too old, missing bridge \"mcast_max_groups\" support" + exit $ksft_skip +fi + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/ethtool.sh b/tools/testing/selftests/net/forwarding/ethtool.sh index dbb9fcf759e0..aa2eafb7b243 100755 --- a/tools/testing/selftests/net/forwarding/ethtool.sh +++ b/tools/testing/selftests/net/forwarding/ethtool.sh @@ -286,6 +286,8 @@ different_speeds_autoneg_on() ethtool -s $h1 autoneg on } +skip_on_veth + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/ethtool_extended_state.sh b/tools/testing/selftests/net/forwarding/ethtool_extended_state.sh index 072faa77f53b..17f89c3b7c02 100755 --- a/tools/testing/selftests/net/forwarding/ethtool_extended_state.sh +++ b/tools/testing/selftests/net/forwarding/ethtool_extended_state.sh @@ -108,6 +108,8 @@ no_cable() ip link set dev $swp3 down } +skip_on_veth + setup_prepare tests_run diff --git a/tools/testing/selftests/net/forwarding/ethtool_mm.sh b/tools/testing/selftests/net/forwarding/ethtool_mm.sh index c580ad623848..39e736f30322 100755 --- a/tools/testing/selftests/net/forwarding/ethtool_mm.sh +++ b/tools/testing/selftests/net/forwarding/ethtool_mm.sh @@ -258,11 +258,6 @@ h2_destroy() setup_prepare() { - check_ethtool_mm_support - check_tc_fp_support - require_command lldptool - bail_on_lldpad "autoconfigure the MAC Merge layer" "configure it manually" - h1=${NETIFS[p1]} h2=${NETIFS[p2]} @@ -278,6 +273,19 @@ cleanup() h1_destroy } +check_ethtool_mm_support +check_tc_fp_support +require_command lldptool +bail_on_lldpad "autoconfigure the MAC Merge layer" "configure it manually" + +for netif in ${NETIFS[@]}; do + ethtool --show-mm $netif 2>&1 &> /dev/null + if [[ $? -ne 0 ]]; then + echo "SKIP: $netif does not support MAC Merge" + exit $ksft_skip + fi +done + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/hw_stats_l3_gre.sh b/tools/testing/selftests/net/forwarding/hw_stats_l3_gre.sh index eb9ec4a68f84..7594bbb49029 100755 --- a/tools/testing/selftests/net/forwarding/hw_stats_l3_gre.sh +++ b/tools/testing/selftests/net/forwarding/hw_stats_l3_gre.sh @@ -99,6 +99,8 @@ test_stats_rx() test_stats g2a rx } +skip_on_veth + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh b/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh index 9f5b3e2e5e95..49fa94b53a1c 100755 --- a/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh +++ b/tools/testing/selftests/net/forwarding/ip6_forward_instats_vrf.sh @@ -14,6 +14,8 @@ ALL_TESTS=" NUM_NETIFS=4 source lib.sh +require_command $TROUTE6 + h1_create() { simple_if_init $h1 2001:1:1::2/64 diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh index 9ddb68dd6a08..f69015bf2dea 100755 --- a/tools/testing/selftests/net/forwarding/lib.sh +++ b/tools/testing/selftests/net/forwarding/lib.sh @@ -30,6 +30,7 @@ REQUIRE_MZ=${REQUIRE_MZ:=yes} REQUIRE_MTOOLS=${REQUIRE_MTOOLS:=no} STABLE_MAC_ADDRS=${STABLE_MAC_ADDRS:=no} TCPDUMP_EXTRA_FLAGS=${TCPDUMP_EXTRA_FLAGS:=} +TROUTE6=${TROUTE6:=traceroute6} relative_path="${BASH_SOURCE%/*}" if [[ "$relative_path" == "${BASH_SOURCE}" ]]; then @@ -163,6 +164,17 @@ check_port_mab_support() fi } +skip_on_veth() +{ + local kind=$(ip -j -d link show dev ${NETIFS[p1]} | + jq -r '.[].linkinfo.info_kind') + + if [[ $kind == veth ]]; then + echo "SKIP: Test cannot be run with veth pairs" + exit $ksft_skip + fi +} + if [[ "$(id -u)" -ne 0 ]]; then echo "SKIP: need root privileges" exit $ksft_skip @@ -225,6 +237,11 @@ create_netif_veth() for ((i = 1; i <= NUM_NETIFS; ++i)); do local j=$((i+1)) + if [ -z ${NETIFS[p$i]} ]; then + echo "SKIP: Cannot create interface. Name not specified" + exit $ksft_skip + fi + ip link show dev ${NETIFS[p$i]} &> /dev/null if [[ $? -ne 0 ]]; then ip link add ${NETIFS[p$i]} type veth \ diff --git a/tools/testing/selftests/net/forwarding/settings b/tools/testing/selftests/net/forwarding/settings new file mode 100644 index 000000000000..e7b9417537fb --- /dev/null +++ b/tools/testing/selftests/net/forwarding/settings @@ -0,0 +1 @@ +timeout=0 diff --git a/tools/testing/selftests/net/forwarding/tc_actions.sh b/tools/testing/selftests/net/forwarding/tc_actions.sh index a96cff8e7219..b0f5e55d2d0b 100755 --- a/tools/testing/selftests/net/forwarding/tc_actions.sh +++ b/tools/testing/selftests/net/forwarding/tc_actions.sh @@ -9,6 +9,8 @@ NUM_NETIFS=4 source tc_common.sh source lib.sh +require_command ncat + tcflags="skip_hw" h1_create() @@ -220,9 +222,9 @@ mirred_egress_to_ingress_tcp_test() ip_proto icmp \ action drop - ip vrf exec v$h1 nc --recv-only -w10 -l -p 12345 -o $mirred_e2i_tf2 & + ip vrf exec v$h1 ncat --recv-only -w10 -l -p 12345 -o $mirred_e2i_tf2 & local rpid=$! - ip vrf exec v$h1 nc -w1 --send-only 192.0.2.2 12345 <$mirred_e2i_tf1 + ip vrf exec v$h1 ncat -w1 --send-only 192.0.2.2 12345 <$mirred_e2i_tf1 wait -n $rpid cmp -s $mirred_e2i_tf1 $mirred_e2i_tf2 check_err $? "server output check failed" diff --git a/tools/testing/selftests/net/forwarding/tc_flower.sh b/tools/testing/selftests/net/forwarding/tc_flower.sh index 683711f41aa9..b1daad19b01e 100755 --- a/tools/testing/selftests/net/forwarding/tc_flower.sh +++ b/tools/testing/selftests/net/forwarding/tc_flower.sh @@ -52,8 +52,8 @@ match_dst_mac_test() tc_check_packets "dev $h2 ingress" 101 1 check_fail $? "Matched on a wrong filter" - tc_check_packets "dev $h2 ingress" 102 1 - check_err $? "Did not match on correct filter" + tc_check_packets "dev $h2 ingress" 102 0 + check_fail $? "Did not match on correct filter" tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower @@ -78,8 +78,8 @@ match_src_mac_test() tc_check_packets "dev $h2 ingress" 101 1 check_fail $? "Matched on a wrong filter" - tc_check_packets "dev $h2 ingress" 102 1 - check_err $? "Did not match on correct filter" + tc_check_packets "dev $h2 ingress" 102 0 + check_fail $? "Did not match on correct filter" tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower tc filter del dev $h2 ingress protocol ip pref 2 handle 102 flower diff --git a/tools/testing/selftests/net/forwarding/tc_flower_l2_miss.sh b/tools/testing/selftests/net/forwarding/tc_flower_l2_miss.sh index e22c2d28b6eb..20a7cb7222b8 100755 --- a/tools/testing/selftests/net/forwarding/tc_flower_l2_miss.sh +++ b/tools/testing/selftests/net/forwarding/tc_flower_l2_miss.sh @@ -127,6 +127,7 @@ test_l2_miss_multicast_common() local proto=$1; shift local sip=$1; shift local dip=$1; shift + local dmac=$1; shift local mode=$1; shift local name=$1; shift @@ -142,7 +143,7 @@ test_l2_miss_multicast_common() action pass # Before adding MDB entry. - $MZ $mode $h1 -t ip -A $sip -B $dip -c 1 -p 100 -q + $MZ $mode $h1 -a own -b $dmac -t ip -A $sip -B $dip -c 1 -p 100 -q tc_check_packets "dev $swp2 egress" 101 1 check_err $? "Unregistered multicast filter was not hit before adding MDB entry" @@ -153,7 +154,7 @@ test_l2_miss_multicast_common() # Adding MDB entry. bridge mdb replace dev br1 port $swp2 grp $dip permanent - $MZ $mode $h1 -t ip -A $sip -B $dip -c 1 -p 100 -q + $MZ $mode $h1 -a own -b $dmac -t ip -A $sip -B $dip -c 1 -p 100 -q tc_check_packets "dev $swp2 egress" 101 1 check_err $? "Unregistered multicast filter was hit after adding MDB entry" @@ -164,7 +165,7 @@ test_l2_miss_multicast_common() # Deleting MDB entry. bridge mdb del dev br1 port $swp2 grp $dip - $MZ $mode $h1 -t ip -A $sip -B $dip -c 1 -p 100 -q + $MZ $mode $h1 -a own -b $dmac -t ip -A $sip -B $dip -c 1 -p 100 -q tc_check_packets "dev $swp2 egress" 101 2 check_err $? "Unregistered multicast filter was not hit after deleting MDB entry" @@ -183,10 +184,11 @@ test_l2_miss_multicast_ipv4() local proto="ipv4" local sip=192.0.2.1 local dip=239.1.1.1 + local dmac=01:00:5e:01:01:01 local mode="-4" local name="IPv4" - test_l2_miss_multicast_common $proto $sip $dip $mode $name + test_l2_miss_multicast_common $proto $sip $dip $dmac $mode $name } test_l2_miss_multicast_ipv6() @@ -194,10 +196,11 @@ test_l2_miss_multicast_ipv6() local proto="ipv6" local sip=2001:db8:1::1 local dip=ff0e::1 + local dmac=33:33:00:00:00:01 local mode="-6" local name="IPv6" - test_l2_miss_multicast_common $proto $sip $dip $mode $name + test_l2_miss_multicast_common $proto $sip $dip $dmac $mode $name } test_l2_miss_multicast() diff --git a/tools/testing/selftests/net/forwarding/tc_tunnel_key.sh b/tools/testing/selftests/net/forwarding/tc_tunnel_key.sh index 5ac184d51809..5a5dd9034819 100755 --- a/tools/testing/selftests/net/forwarding/tc_tunnel_key.sh +++ b/tools/testing/selftests/net/forwarding/tc_tunnel_key.sh @@ -104,11 +104,14 @@ tunnel_key_nofrag_test() local i tc filter add dev $swp1 ingress protocol ip pref 100 handle 100 \ - flower ip_flags nofrag action drop + flower src_ip 192.0.2.1 dst_ip 192.0.2.2 ip_proto udp \ + ip_flags nofrag action drop tc filter add dev $swp1 ingress protocol ip pref 101 handle 101 \ - flower ip_flags firstfrag action drop + flower src_ip 192.0.2.1 dst_ip 192.0.2.2 ip_proto udp \ + ip_flags firstfrag action drop tc filter add dev $swp1 ingress protocol ip pref 102 handle 102 \ - flower ip_flags nofirstfrag action drop + flower src_ip 192.0.2.1 dst_ip 192.0.2.2 ip_proto udp \ + ip_flags nofirstfrag action drop # test 'nofrag' set tc filter add dev h1-et egress protocol all pref 1 handle 1 matchall $tcflags \ diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 3c2096ac97ef..d01b73a8ed0f 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -705,6 +705,7 @@ pm_nl_del_endpoint() local addr=$3 if [ $ip_mptcp -eq 1 ]; then + [ $id -ne 0 ] && addr='' ip -n $ns mptcp endpoint delete id $id $addr else ip netns exec $ns ./pm_nl_ctl del $id $addr @@ -795,10 +796,11 @@ pm_nl_check_endpoint() fi if [ $ip_mptcp -eq 1 ]; then + # get line and trim trailing whitespace line=$(ip -n $ns mptcp endpoint show $id) + line="${line% }" # the dump order is: address id flags port dev - expected_line="$addr" - [ -n "$addr" ] && expected_line="$expected_line $addr" + [ -n "$addr" ] && expected_line="$addr" expected_line="$expected_line $id" [ -n "$_flags" ] && expected_line="$expected_line ${_flags//","/" "}" [ -n "$dev" ] && expected_line="$expected_line $dev" diff --git a/tools/testing/selftests/net/pmtu.sh b/tools/testing/selftests/net/pmtu.sh index dfe3d287f01d..f838dd370f6a 100755 --- a/tools/testing/selftests/net/pmtu.sh +++ b/tools/testing/selftests/net/pmtu.sh @@ -361,6 +361,7 @@ err_buf= tcpdump_pids= nettest_pids= socat_pids= +tmpoutfile= err() { err_buf="${err_buf}${1} @@ -951,6 +952,7 @@ cleanup() { ip link del veth_A-R1 2>/dev/null ovs-vsctl --if-exists del-port vxlan_a 2>/dev/null ovs-vsctl --if-exists del-br ovs_br0 2>/dev/null + rm -f "$tmpoutfile" } mtu() { @@ -1328,6 +1330,39 @@ test_pmtu_ipvX_over_bridged_vxlanY_or_geneveY_exception() { check_pmtu_value ${exp_mtu} "${pmtu}" "exceeding link layer MTU on bridged ${type} interface" pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})" check_pmtu_value ${exp_mtu} "${pmtu}" "exceeding link layer MTU on locally bridged ${type} interface" + + tmpoutfile=$(mktemp) + + # Flush Exceptions, retry with TCP + run_cmd ${ns_a} ip route flush cached ${dst} + run_cmd ${ns_b} ip route flush cached ${dst} + run_cmd ${ns_c} ip route flush cached ${dst} + + for target in "${ns_a}" "${ns_c}" ; do + if [ ${family} -eq 4 ]; then + TCPDST=TCP:${dst}:50000 + else + TCPDST="TCP:[${dst}]:50000" + fi + ${ns_b} socat -T 3 -u -6 TCP-LISTEN:50000 STDOUT > $tmpoutfile & + + sleep 1 + + dd if=/dev/zero of=/dev/stdout status=none bs=1M count=1 | ${target} socat -T 3 -u STDIN $TCPDST,connect-timeout=3 + + size=$(du -sb $tmpoutfile) + size=${size%%/tmp/*} + + [ $size -ne 1048576 ] && err "File size $size mismatches exepcted value in locally bridged vxlan test" && return 1 + done + + rm -f "$tmpoutfile" + + # Check that exceptions were created + pmtu="$(route_get_dst_pmtu_from_exception "${ns_c}" ${dst})" + check_pmtu_value ${exp_mtu} "${pmtu}" "tcp: exceeding link layer MTU on bridged ${type} interface" + pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})" + check_pmtu_value ${exp_mtu} "${pmtu}" "tcp exceeding link layer MTU on locally bridged ${type} interface" } test_pmtu_ipv4_br_vxlan4_exception() { diff --git a/tools/testing/selftests/net/so_incoming_cpu.c b/tools/testing/selftests/net/so_incoming_cpu.c index 0e04f9fef986..a14818164102 100644 --- a/tools/testing/selftests/net/so_incoming_cpu.c +++ b/tools/testing/selftests/net/so_incoming_cpu.c @@ -159,7 +159,7 @@ void create_clients(struct __test_metadata *_metadata, /* Make sure SYN will be processed on the i-th CPU * and finally distributed to the i-th listener. */ - sched_setaffinity(0, sizeof(cpu_set), &cpu_set); + ret = sched_setaffinity(0, sizeof(cpu_set), &cpu_set); ASSERT_EQ(ret, 0); for (j = 0; j < CLIENT_PER_SERVER; j++) { diff --git a/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c b/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c index 5cbc392944a6..2c0d2b1126c1 100644 --- a/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c +++ b/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c @@ -1,6 +1,4 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <sys/prctl.h> - #define THIS_PROGRAM "./vstate_exec_nolibc" int main(int argc, char **argv) diff --git a/tools/testing/selftests/rseq/Makefile b/tools/testing/selftests/rseq/Makefile index b357ba24af06..7a957c7d459a 100644 --- a/tools/testing/selftests/rseq/Makefile +++ b/tools/testing/selftests/rseq/Makefile @@ -4,8 +4,10 @@ ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),) CLANG_FLAGS += -no-integrated-as endif +top_srcdir = ../../../.. + CFLAGS += -O2 -Wall -g -I./ $(KHDR_INCLUDES) -L$(OUTPUT) -Wl,-rpath=./ \ - $(CLANG_FLAGS) + $(CLANG_FLAGS) -I$(top_srcdir)/tools/include LDLIBS += -lpthread -ldl # Own dependencies because we only want to build against 1st prerequisite, but diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c index a723da253244..96e812bdf8a4 100644 --- a/tools/testing/selftests/rseq/rseq.c +++ b/tools/testing/selftests/rseq/rseq.c @@ -31,6 +31,8 @@ #include <sys/auxv.h> #include <linux/auxvec.h> +#include <linux/compiler.h> + #include "../kselftest.h" #include "rseq.h" diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json index a44455372646..08d4861c2e78 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json @@ -131,5 +131,30 @@ "teardown": [ "echo \"1\" > /sys/bus/netdevsim/del_device" ] + }, + { + "id": "3e1e", + "name": "Add taprio Qdisc with an invalid cycle-time", + "category": [ + "qdisc", + "taprio" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "echo \"1 1 8\" > /sys/bus/netdevsim/new_device", + "$TC qdisc add dev $ETH root handle 1: taprio num_tc 3 map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@0 1@0 base-time 1000000000 sched-entry S 01 300000 flags 0x1 clockid CLOCK_TAI cycle-time 4294967296 || /bin/true", + "$IP link set dev $ETH up", + "$IP addr add 10.10.10.10/24 dev $ETH" + ], + "cmdUnderTest": "/bin/true", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $ETH", + "matchPattern": "qdisc taprio 1: root refcnt", + "matchCount": "0", + "teardown": [ + "echo \"1\" > /sys/bus/netdevsim/del_device" + ] } ] diff --git a/tools/testing/vsock/Makefile b/tools/testing/vsock/Makefile index 43a254f0e14d..21a98ba565ab 100644 --- a/tools/testing/vsock/Makefile +++ b/tools/testing/vsock/Makefile @@ -8,5 +8,5 @@ vsock_perf: vsock_perf.o CFLAGS += -g -O2 -Werror -Wall -I. -I../../include -I../../../usr/include -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -D_GNU_SOURCE .PHONY: all test clean clean: - ${RM} *.o *.d vsock_test vsock_diag_test + ${RM} *.o *.d vsock_test vsock_diag_test vsock_perf -include *.d |