diff options
822 files changed, 51426 insertions, 13617 deletions
diff --git a/Documentation/accel/qaic/aic100.rst b/Documentation/accel/qaic/aic100.rst index c80d0f1307db..590dae77ea12 100644 --- a/Documentation/accel/qaic/aic100.rst +++ b/Documentation/accel/qaic/aic100.rst @@ -36,8 +36,9 @@ AIC100 DID (0xa100). AIC100 does not implement FLR (function level reset). -AIC100 implements MSI but does not implement MSI-X. AIC100 requires 17 MSIs to -operate (1 for MHI, 16 for the DMA Bridge). +AIC100 implements MSI but does not implement MSI-X. AIC100 prefers 17 MSIs to +operate (1 for MHI, 16 for the DMA Bridge). Falling back to 1 MSI is possible in +scenarios where reserving 32 MSIs isn't feasible. As a PCIe device, AIC100 utilizes BARs to provide host interfaces to the device hardware. AIC100 provides 3, 64-bit BARs. @@ -220,10 +221,14 @@ of the defined channels, and their uses. +----------------+---------+----------+----------------------------------------+ | QAIC_DEBUG | 18 & 19 | AMSS | Not used. | +----------------+---------+----------+----------------------------------------+ -| QAIC_TIMESYNC | 20 & 21 | SBL/AMSS | Used to synchronize timestamps in the | +| QAIC_TIMESYNC | 20 & 21 | SBL | Used to synchronize timestamps in the | | | | | device side logs with the host time | | | | | source. | +----------------+---------+----------+----------------------------------------+ +| QAIC_TIMESYNC | 22 & 23 | AMSS | Used to periodically synchronize | +| _PERIODIC | | | timestamps in the device side logs with| +| | | | the host time source. | ++----------------+---------+----------+----------------------------------------+ DMA Bridge ========== diff --git a/Documentation/accel/qaic/qaic.rst b/Documentation/accel/qaic/qaic.rst index c88502383136..f81020736ebf 100644 --- a/Documentation/accel/qaic/qaic.rst +++ b/Documentation/accel/qaic/qaic.rst @@ -10,6 +10,9 @@ accelerator products. Interrupts ========== +IRQ Storm Mitigation +-------------------- + While the AIC100 DMA Bridge hardware implements an IRQ storm mitigation mechanism, it is still possible for an IRQ storm to occur. A storm can happen if the workload is particularly quick, and the host is responsive. If the host @@ -35,6 +38,26 @@ generates 100k IRQs per second (per /proc/interrupts) is reduced to roughly 64 IRQs over 5 minutes while keeping the host system stable, and having the same workload throughput performance (within run to run noise variation). +Single MSI Mode +--------------- + +MultiMSI is not well supported on all systems; virtualized ones even less so +(circa 2023). Between hypervisors masking the PCIe MSI capability structure to +large memory requirements for vIOMMUs (required for supporting MultiMSI), it is +useful to be able to fall back to a single MSI when needed. + +To support this fallback, we allow the case where only one MSI is able to be +allocated, and share that one MSI between MHI and the DBCs. The device detects +when only one MSI has been configured and directs the interrupts for the DBCs +to the interrupt normally used for MHI. Unfortunately this means that the +interrupt handlers for every DBC and MHI wake up for every interrupt that +arrives; however, the DBC threaded irq handlers only are started when work to be +done is detected (MHI will always start its threaded handler). + +If the DBC is configured to force MSI interrupts, this can circumvent the +software IRQ storm mitigation mentioned above. Since the MSI is shared it is +never disabled, allowing each new entry to the FIFO to trigger a new interrupt. + Neural Network Control (NNC) Protocol ===================================== @@ -178,3 +201,8 @@ overrides this for that call. Default is 5000 (5 seconds). Sets the polling interval in microseconds (us) when datapath polling is active. Takes effect at the next polling interval. Default is 100 (100 us). + +**timesync_delay_ms (unsigned int)** + +Sets the time interval in milliseconds (ms) between two consecutive timesync +operations. Default is 1000 (1000 ms). diff --git a/Documentation/arch/loongarch/introduction.rst b/Documentation/arch/loongarch/introduction.rst index 8c568cfc2107..5e6db78abeaf 100644 --- a/Documentation/arch/loongarch/introduction.rst +++ b/Documentation/arch/loongarch/introduction.rst @@ -375,9 +375,9 @@ Developer web site of Loongson and LoongArch (Software and Documentation): Documentation of LoongArch ISA: - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.02-CN.pdf (in Chinese) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.10-CN.pdf (in Chinese) - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.02-EN.pdf (in English) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.10-EN.pdf (in English) Documentation of LoongArch ELF psABI: diff --git a/Documentation/arch/x86/boot.rst b/Documentation/arch/x86/boot.rst index f5d2f2414de8..22cc7a040dae 100644 --- a/Documentation/arch/x86/boot.rst +++ b/Documentation/arch/x86/boot.rst @@ -77,7 +77,7 @@ Protocol 2.14 BURNT BY INCORRECT COMMIT Protocol 2.15 (Kernel 5.5) Added the kernel_info and kernel_info.setup_type_max. ============= ============================================================ -.. note:: + .. note:: The protocol version number should be changed only if the setup header is changed. There is no need to update the version number if boot_params or kernel_info are changed. Additionally, it is recommended to use diff --git a/Documentation/devicetree/bindings/display/panel/newvision,nv3051d.yaml b/Documentation/devicetree/bindings/display/panel/newvision,nv3051d.yaml index cce775a87f87..7a634fbc465e 100644 --- a/Documentation/devicetree/bindings/display/panel/newvision,nv3051d.yaml +++ b/Documentation/devicetree/bindings/display/panel/newvision,nv3051d.yaml @@ -21,7 +21,7 @@ properties: - enum: - anbernic,rg351v-panel - anbernic,rg353p-panel - - anbernic,rg353v-panel + - powkiddy,rk2023-panel - const: newvision,nv3051d reg: true diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml index dae55b8a267b..dc078ceeca9a 100644 --- a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml +++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - brcm,2711-v3d + - brcm,2712-v3d - brcm,7268-v3d - brcm,7278-v3d diff --git a/Documentation/devicetree/bindings/gpu/img,powervr.yaml b/Documentation/devicetree/bindings/gpu/img,powervr.yaml new file mode 100644 index 000000000000..a13298f1a182 --- /dev/null +++ b/Documentation/devicetree/bindings/gpu/img,powervr.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2023 Imagination Technologies Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpu/img,powervr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Imagination Technologies PowerVR and IMG GPU + +maintainers: + - Frank Binns <frank.binns@imgtec.com> + +properties: + compatible: + items: + - enum: + - ti,am62-gpu + - const: img,img-axe # IMG AXE GPU model/revision is fully discoverable + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 3 + + clock-names: + items: + - const: core + - const: mem + - const: sys + minItems: 1 + + interrupts: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + const: ti,am62-gpu + then: + properties: + clocks: + maxItems: 1 + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/soc/ti,sci_pm_domain.h> + + gpu@fd00000 { + compatible = "ti,am62-gpu", "img,img-axe"; + reg = <0x0fd00000 0x20000>; + clocks = <&k3_clks 187 0>; + clock-names = "core"; + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&k3_pds 187 TI_SCI_PD_EXCLUSIVE>; + }; diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 9f6a5ccbcefe..d14d123ad7a0 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -275,12 +275,12 @@ allOf: properties: rx-internal-delay-ps: description: - RGMII Receive Clock Delay defined in pico seconds.This is used for + RGMII Receive Clock Delay defined in pico seconds. This is used for controllers that have configurable RX internal delays. If this property is present then the MAC applies the RX delay. tx-internal-delay-ps: description: - RGMII Transmit Clock Delay defined in pico seconds.This is used for + RGMII Transmit Clock Delay defined in pico seconds. This is used for controllers that have configurable TX internal delays. If this property is present then the MAC applies the TX delay. diff --git a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml index 462ead5a1cec..2cf3d016db42 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml @@ -36,6 +36,7 @@ properties: - qcom,sm8350-ufshc - qcom,sm8450-ufshc - qcom,sm8550-ufshc + - qcom,sm8650-ufshc - const: qcom,ufshc - const: jedec,ufs-2.0 @@ -122,6 +123,7 @@ allOf: - qcom,sm8350-ufshc - qcom,sm8450-ufshc - qcom,sm8550-ufshc + - qcom,sm8650-ufshc then: properties: clocks: diff --git a/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml b/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml index ff3a1707ef57..6d4cfd943f58 100644 --- a/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml +++ b/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml @@ -36,7 +36,11 @@ properties: vdd-supply: description: - VDD power supply to the hub + 3V3 power supply to the hub + + vdd2-supply: + description: + 1V2 power supply to the hub peer-hub: $ref: /schemas/types.yaml#/definitions/phandle @@ -62,6 +66,7 @@ allOf: properties: reset-gpios: false vdd-supply: false + vdd2-supply: false peer-hub: false i2c-bus: false else: diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index e889158ca205..915c8205623b 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -521,8 +521,8 @@ examples: interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 486 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 488 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 489 IRQ_TYPE_LEVEL_HIGH>; + <GIC_SPI 488 IRQ_TYPE_EDGE_BOTH>, + <GIC_SPI 489 IRQ_TYPE_EDGE_BOTH>; interrupt-names = "hs_phy_irq", "ss_phy_irq", "dm_hs_phy_irq", "dp_hs_phy_irq"; diff --git a/Documentation/devicetree/bindings/usb/usb-hcd.yaml b/Documentation/devicetree/bindings/usb/usb-hcd.yaml index 692dd60e3f73..45a19d4928af 100644 --- a/Documentation/devicetree/bindings/usb/usb-hcd.yaml +++ b/Documentation/devicetree/bindings/usb/usb-hcd.yaml @@ -41,7 +41,7 @@ examples: - | usb { phys = <&usb2_phy1>, <&usb3_phy1>; - phy-names = "usb"; + phy-names = "usb2", "usb3"; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst index 57c6ae23b3fc..cc4626d6ee4f 100644 --- a/Documentation/filesystems/erofs.rst +++ b/Documentation/filesystems/erofs.rst @@ -91,6 +91,10 @@ compatibility checking tool (fsck.erofs), and a debugging tool (dump.erofs): - git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git +For more information, please also refer to the documentation site: + +- https://erofs.docs.kernel.org + Bugs and patches are welcome, please kindly help us and send to the following linux-erofs mailing list: diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst index 45a12e552091..cc6535f5f28c 100644 --- a/Documentation/gpu/drivers.rst +++ b/Documentation/gpu/drivers.rst @@ -3,9 +3,11 @@ GPU Driver Documentation ======================== .. toctree:: + :maxdepth: 3 amdgpu/index i915 + imagination/index mcde meson pl111 diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index b748b8ae70b2..59cfe8a7a8ba 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -363,6 +363,12 @@ EDID Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_edid.c :export: +.. kernel-doc:: include/drm/drm_eld.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_eld.c + :export: + SCDC Helper Functions Reference =============================== diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 602010cb6894..acc5901ac840 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -552,6 +552,12 @@ Overview .. kernel-doc:: drivers/gpu/drm/scheduler/sched_main.c :doc: Overview +Flow Control +------------ + +.. kernel-doc:: drivers/gpu/drm/scheduler/sched_main.c + :doc: Flow Control + Scheduler Function References ----------------------------- diff --git a/Documentation/gpu/imagination/index.rst b/Documentation/gpu/imagination/index.rst new file mode 100644 index 000000000000..dc9579e758c3 --- /dev/null +++ b/Documentation/gpu/imagination/index.rst @@ -0,0 +1,13 @@ +======================================= +drm/imagination PowerVR Graphics Driver +======================================= + +.. kernel-doc:: drivers/gpu/drm/imagination/pvr_drv.c + :doc: PowerVR Graphics Driver + +Contents +======== +.. toctree:: + :maxdepth: 2 + + uapi diff --git a/Documentation/gpu/imagination/uapi.rst b/Documentation/gpu/imagination/uapi.rst new file mode 100644 index 000000000000..2227ea7e6222 --- /dev/null +++ b/Documentation/gpu/imagination/uapi.rst @@ -0,0 +1,174 @@ +==== +UAPI +==== +The sources associated with this section can be found in ``pvr_drm.h``. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR UAPI + +OBJECT ARRAYS +============= +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_obj_array + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: DRM_PVR_OBJ_ARRAY + +IOCTLS +====== +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL interface + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: PVR_IOCTL + +DEV_QUERY +--------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL DEV_QUERY interface + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_dev_query + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_dev_query_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_dev_query_gpu_info + drm_pvr_dev_query_runtime_info + drm_pvr_dev_query_hwrt_info + drm_pvr_dev_query_quirks + drm_pvr_dev_query_enhancements + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_heap_id + drm_pvr_heap + drm_pvr_dev_query_heap_info + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_static_data_area_usage + drm_pvr_static_data_area + drm_pvr_dev_query_static_data_areas + +CREATE_BO +--------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL CREATE_BO interface + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_create_bo_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for CREATE_BO + +GET_BO_MMAP_OFFSET +------------------ +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL GET_BO_MMAP_OFFSET interface + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_get_bo_mmap_offset_args + +CREATE_VM_CONTEXT and DESTROY_VM_CONTEXT +---------------------------------------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL CREATE_VM_CONTEXT and DESTROY_VM_CONTEXT interfaces + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_create_vm_context_args + drm_pvr_ioctl_destroy_vm_context_args + +VM_MAP and VM_UNMAP +------------------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL VM_MAP and VM_UNMAP interfaces + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_vm_map_args + drm_pvr_ioctl_vm_unmap_args + +CREATE_CONTEXT and DESTROY_CONTEXT +---------------------------------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL CREATE_CONTEXT and DESTROY_CONTEXT interfaces + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_create_context_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ctx_priority + drm_pvr_ctx_type + drm_pvr_static_render_context_state + drm_pvr_static_render_context_state_format + drm_pvr_reset_framework + drm_pvr_reset_framework_format + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_destroy_context_args + +CREATE_FREE_LIST and DESTROY_FREE_LIST +-------------------------------------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL CREATE_FREE_LIST and DESTROY_FREE_LIST interfaces + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_create_free_list_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_destroy_free_list_args + +CREATE_HWRT_DATASET and DESTROY_HWRT_DATASET +-------------------------------------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL CREATE_HWRT_DATASET and DESTROY_HWRT_DATASET interfaces + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_create_hwrt_dataset_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_create_hwrt_geom_data_args + drm_pvr_create_hwrt_rt_data_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_destroy_hwrt_dataset_args + +SUBMIT_JOBS +----------- +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: PowerVR IOCTL SUBMIT_JOBS interface + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for the drm_pvr_sync_op object. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_ioctl_submit_jobs_args + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for SUBMIT_JOB ioctl geometry command. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for SUBMIT_JOB ioctl fragment command. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for SUBMIT_JOB ioctl compute command. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :doc: Flags for SUBMIT_JOB ioctl transfer command. + +.. kernel-doc:: include/uapi/drm/pvr_drm.h + :identifiers: drm_pvr_sync_op + drm_pvr_job_type + drm_pvr_hwrt_data_ref + drm_pvr_job + +Internal notes +============== +.. kernel-doc:: drivers/gpu/drm/imagination/pvr_device.h + :doc: IOCTL validation helpers + +.. kernel-doc:: drivers/gpu/drm/imagination/pvr_device.h + :identifiers: PVR_STATIC_ASSERT_64BIT_ALIGNED PVR_IOCTL_UNION_PADDING_CHECK + pvr_ioctl_union_padding_check diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 03fe5d1247be..b62c7fa0c2bc 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -621,6 +621,23 @@ Contact: Javier Martinez Canillas <javierm@redhat.com> Level: Intermediate +Clean up and document former selftests suites +--------------------------------------------- + +Some KUnit test suites (drm_buddy, drm_cmdline_parser, drm_damage_helper, +drm_format, drm_framebuffer, drm_dp_mst_helper, drm_mm, drm_plane_helper and +drm_rect) are former selftests suites that have been converted over when KUnit +was first introduced. + +These suites were fairly undocumented, and with different goals than what unit +tests can be. Trying to identify what each test in these suites actually test +for, whether that makes sense for a unit test, and either remove it if it +doesn't or document it if it does would be of great help. + +Contact: Maxime Ripard <mripard@kernel.org> + +Level: Intermediate + Enable trinity for DRM ---------------------- diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst index 7feacc20835e..84ee60fceef2 100644 --- a/Documentation/process/maintainer-netdev.rst +++ b/Documentation/process/maintainer-netdev.rst @@ -193,9 +193,23 @@ Review timelines Generally speaking, the patches get triaged quickly (in less than 48h). But be patient, if your patch is active in patchwork (i.e. it's listed on the project's patch list) the chances it was missed are close to zero. -Asking the maintainer for status updates on your -patch is a good way to ensure your patch is ignored or pushed to the -bottom of the priority list. + +The high volume of development on netdev makes reviewers move on +from discussions relatively quickly. New comments and replies +are very unlikely to arrive after a week of silence. If a patch +is no longer active in patchwork and the thread went idle for more +than a week - clarify the next steps and/or post the next version. + +For RFC postings specifically, if nobody responded in a week - reviewers +either missed the posting or have no strong opinions. If the code is ready, +repost as a PATCH. + +Emails saying just "ping" or "bump" are considered rude. If you can't figure +out the status of the patch from patchwork or where the discussion has +landed - describe your best guess and ask if it's correct. For example:: + + I don't understand what the next steps are. Person X seems to be unhappy + with A, should I do B and repost the patches? .. _Changes requested: diff --git a/Documentation/translations/zh_CN/arch/loongarch/introduction.rst b/Documentation/translations/zh_CN/arch/loongarch/introduction.rst index 59d6bf33050c..bf463c5a4c51 100644 --- a/Documentation/translations/zh_CN/arch/loongarch/introduction.rst +++ b/Documentation/translations/zh_CN/arch/loongarch/introduction.rst @@ -338,9 +338,9 @@ Loongson与LoongArch的开发者网站(软件与文档资源): LoongArch指令集架构的文档: - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.02-CN.pdf (中文版) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.10-CN.pdf (中文版) - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.02-EN.pdf (英文版) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.10-EN.pdf (英文版) LoongArch的ELF psABI文档: diff --git a/MAINTAINERS b/MAINTAINERS index 97f51d5ec1cf..0ba904b46efe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6503,8 +6503,7 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/sun4i/sun8i* DRM DRIVER FOR ARM PL111 CLCD -M: Emma Anholt <emma@anholt.net> -S: Supported +S: Orphan T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/pl111/ @@ -6619,8 +6618,7 @@ F: Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml F: drivers/gpu/drm/panel/panel-himax-hx8394.c DRM DRIVER FOR HX8357D PANELS -M: Emma Anholt <emma@anholt.net> -S: Maintained +S: Orphan T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/display/himax,hx8357d.txt F: drivers/gpu/drm/tiny/hx8357d.c @@ -7213,8 +7211,8 @@ F: Documentation/devicetree/bindings/display/ti/ F: drivers/gpu/drm/omapdrm/ DRM DRIVERS FOR V3D -M: Emma Anholt <emma@anholt.net> M: Melissa Wen <mwen@igalia.com> +M: Maíra Canal <mcanal@igalia.com> S: Supported T: git git://anongit.freedesktop.org/drm/drm-misc F: Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml @@ -7222,7 +7220,6 @@ F: drivers/gpu/drm/v3d/ F: include/uapi/drm/v3d_drm.h DRM DRIVERS FOR VC4 -M: Emma Anholt <emma@anholt.net> M: Maxime Ripard <mripard@kernel.org> S: Supported T: git git://github.com/anholt/linux @@ -7855,6 +7852,7 @@ R: Yue Hu <huyue2@coolpad.com> R: Jeffle Xu <jefflexu@linux.alibaba.com> L: linux-erofs@lists.ozlabs.org S: Maintained +W: https://erofs.docs.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git F: Documentation/ABI/testing/sysfs-fs-erofs F: Documentation/filesystems/erofs.rst @@ -8950,7 +8948,6 @@ S: Maintained F: scripts/get_maintainer.pl GFS2 FILE SYSTEM -M: Bob Peterson <rpeterso@redhat.com> M: Andreas Gruenbacher <agruenba@redhat.com> L: gfs2@lists.linux.dev S: Supported @@ -10395,6 +10392,16 @@ IMGTEC IR DECODER DRIVER S: Orphan F: drivers/media/rc/img-ir/ +IMGTEC POWERVR DRM DRIVER +M: Frank Binns <frank.binns@imgtec.com> +M: Donald Robson <donald.robson@imgtec.com> +M: Matt Coster <matt.coster@imgtec.com> +S: Supported +F: Documentation/devicetree/bindings/gpu/img,powervr.yaml +F: Documentation/gpu/imagination/ +F: drivers/gpu/drm/imagination/ +F: include/uapi/drm/pvr_drm.h + IMON SOUNDGRAPH USB IR RECEIVER M: Sean Young <sean@mess.org> L: linux-media@vger.kernel.org @@ -10639,9 +10646,9 @@ M: Rodrigo Vivi <rodrigo.vivi@intel.com> M: Tvrtko Ursulin <tvrtko.ursulin@linux.intel.com> L: intel-gfx@lists.freedesktop.org S: Supported -W: https://01.org/linuxgraphics/ +W: https://drm.pages.freedesktop.org/intel-docs/ Q: http://patchwork.freedesktop.org/project/intel-gfx/ -B: https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs +B: https://drm.pages.freedesktop.org/intel-docs/how-to-file-i915-bugs.html C: irc://irc.oftc.net/intel-gfx T: git git://anongit.freedesktop.org/drm-intel F: Documentation/ABI/testing/sysfs-driver-intel-i915-hwmon @@ -11025,7 +11032,6 @@ F: drivers/net/wireless/intel/iwlwifi/ INTEL WMI SLIM BOOTLOADER (SBL) FIRMWARE UPDATE DRIVER M: Jithu Joseph <jithu.joseph@intel.com> -R: Maurice Ma <maurice.ma@intel.com> S: Maintained W: https://slimbootloader.github.io/security/firmware-update.html F: drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -13779,7 +13785,6 @@ F: drivers/net/ethernet/mellanox/mlxfw/ MELLANOX HARDWARE PLATFORM SUPPORT M: Hans de Goede <hdegoede@redhat.com> M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> -M: Mark Gross <markgross@kernel.org> M: Vadim Pasternak <vadimp@nvidia.com> L: platform-driver-x86@vger.kernel.org S: Supported @@ -14388,7 +14393,6 @@ F: drivers/platform/surface/surface_gpe.c MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT M: Hans de Goede <hdegoede@redhat.com> M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> -M: Mark Gross <markgross@kernel.org> M: Maximilian Luz <luzmaximilian@gmail.com> L: platform-driver-x86@vger.kernel.org S: Maintained @@ -14995,6 +14999,7 @@ M: Jakub Kicinski <kuba@kernel.org> M: Paolo Abeni <pabeni@redhat.com> L: netdev@vger.kernel.org S: Maintained +P: Documentation/process/maintainer-netdev.rst Q: https://patchwork.kernel.org/project/netdevbpf/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git @@ -15046,6 +15051,7 @@ M: Jakub Kicinski <kuba@kernel.org> M: Paolo Abeni <pabeni@redhat.com> L: netdev@vger.kernel.org S: Maintained +P: Documentation/process/maintainer-netdev.rst Q: https://patchwork.kernel.org/project/netdevbpf/list/ B: mailto:netdev@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git @@ -15056,6 +15062,7 @@ F: Documentation/networking/ F: Documentation/process/maintainer-netdev.rst F: Documentation/userspace-api/netlink/ F: include/linux/in.h +F: include/linux/indirect_call_wrapper.h F: include/linux/net.h F: include/linux/netdevice.h F: include/net/ @@ -21769,7 +21776,9 @@ F: Documentation/devicetree/bindings/counter/ti-eqep.yaml F: drivers/counter/ti-eqep.c TI ETHERNET SWITCH DRIVER (CPSW) -R: Grygorii Strashko <grygorii.strashko@ti.com> +R: Siddharth Vadapalli <s-vadapalli@ti.com> +R: Ravi Gunasekaran <r-gunasekaran@ti.com> +R: Roger Quadros <rogerq@kernel.org> L: linux-omap@vger.kernel.org L: netdev@vger.kernel.org S: Maintained @@ -21793,6 +21802,15 @@ F: Documentation/devicetree/bindings/media/i2c/ti,ds90* F: drivers/media/i2c/ds90* F: include/media/i2c/ds90* +TI ICSSG ETHERNET DRIVER (ICSSG) +R: MD Danish Anwar <danishanwar@ti.com> +R: Roger Quadros <rogerq@kernel.org> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: netdev@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/ti,icss*.yaml +F: drivers/net/ethernet/ti/icssg/* + TI J721E CSI2RX DRIVER M: Jai Luthra <j-luthra@ti.com> L: linux-media@vger.kernel.org @@ -22068,6 +22086,7 @@ F: drivers/watchdog/tqmx86_wdt.c TRACING M: Steven Rostedt <rostedt@goodmis.org> M: Masami Hiramatsu <mhiramat@kernel.org> +R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> L: linux-kernel@vger.kernel.org L: linux-trace-kernel@vger.kernel.org S: Maintained @@ -23654,7 +23673,6 @@ F: drivers/platform/x86/x86-android-tablets/ X86 PLATFORM DRIVERS M: Hans de Goede <hdegoede@redhat.com> M: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> -M: Mark Gross <markgross@kernel.org> L: platform-driver-x86@vger.kernel.org S: Maintained Q: https://patchwork.kernel.org/project/platform-driver-x86/list/ @@ -23692,6 +23710,20 @@ F: arch/x86/kernel/dumpstack.c F: arch/x86/kernel/stacktrace.c F: arch/x86/kernel/unwind_*.c +X86 TRUST DOMAIN EXTENSIONS (TDX) +M: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> +R: Dave Hansen <dave.hansen@linux.intel.com> +L: x86@kernel.org +L: linux-coco@lists.linux.dev +S: Supported +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/tdx +F: arch/x86/boot/compressed/tdx* +F: arch/x86/coco/tdx/ +F: arch/x86/include/asm/shared/tdx.h +F: arch/x86/include/asm/tdx.h +F: arch/x86/virt/vmx/tdx/ +F: drivers/virt/coco/tdx-guest + X86 VDSO M: Andy Lutomirski <luto@kernel.org> L: linux-kernel@vger.kernel.org @@ -23872,8 +23904,7 @@ T: git git://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git P: Documentation/filesystems/xfs-maintainer-entry-profile.rst F: Documentation/ABI/testing/sysfs-fs-xfs F: Documentation/admin-guide/xfs.rst -F: Documentation/filesystems/xfs-delayed-logging-design.rst -F: Documentation/filesystems/xfs-self-describing-metadata.rst +F: Documentation/filesystems/xfs-* F: fs/xfs/ F: include/uapi/linux/dqblk_xfs.h F: include/uapi/linux/fsmap.h @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 7 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc3 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 9afdc4c4a5dc..a395b6c0aae2 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -484,7 +484,8 @@ static int __init xen_guest_init(void) * for secondary CPUs as they are brought up. * For uniformity we use VCPUOP_register_vcpu_info even on cpu0. */ - xen_vcpu_info = alloc_percpu(struct vcpu_info); + xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info), + 1 << fls(sizeof(struct vcpu_info) - 1)); if (xen_vcpu_info == NULL) return -ENOMEM; diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 4bd85cc0d32b..9a2d3723cd0f 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -158,7 +158,7 @@ endif all: $(notdir $(KBUILD_IMAGE)) - +vmlinuz.efi: Image Image vmlinuz.efi: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ diff --git a/arch/arm64/include/asm/setup.h b/arch/arm64/include/asm/setup.h index f4af547ef54c..2e4d7da74fb8 100644 --- a/arch/arm64/include/asm/setup.h +++ b/arch/arm64/include/asm/setup.h @@ -21,9 +21,22 @@ static inline bool arch_parse_debug_rodata(char *arg) extern bool rodata_enabled; extern bool rodata_full; - if (arg && !strcmp(arg, "full")) { + if (!arg) + return false; + + if (!strcmp(arg, "full")) { + rodata_enabled = rodata_full = true; + return true; + } + + if (!strcmp(arg, "off")) { + rodata_enabled = rodata_full = false; + return true; + } + + if (!strcmp(arg, "on")) { rodata_enabled = true; - rodata_full = true; + rodata_full = false; return true; } diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c index 8e2017ba5f1b..924843f1f661 100644 --- a/arch/arm64/mm/pageattr.c +++ b/arch/arm64/mm/pageattr.c @@ -29,8 +29,8 @@ bool can_set_direct_map(void) * * KFENCE pool requires page-granular mapping if initialized late. */ - return (rodata_enabled && rodata_full) || debug_pagealloc_enabled() || - arm64_kfence_can_set_direct_map(); + return rodata_full || debug_pagealloc_enabled() || + arm64_kfence_can_set_direct_map(); } static int change_page_range(pte_t *ptep, unsigned long addr, void *data) @@ -105,8 +105,7 @@ static int change_memory_common(unsigned long addr, int numpages, * If we are manipulating read-only permissions, apply the same * change to the linear mapping of the pages that back this VM area. */ - if (rodata_enabled && - rodata_full && (pgprot_val(set_mask) == PTE_RDONLY || + if (rodata_full && (pgprot_val(set_mask) == PTE_RDONLY || pgprot_val(clear_mask) == PTE_RDONLY)) { for (i = 0; i < area->nr_pages; i++) { __change_memory_common((u64)page_address(area->pages[i]), diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 9eeb0c05f3f4..204b94b2e6aa 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -68,6 +68,7 @@ LDFLAGS_vmlinux += -static -n -nostdlib ifdef CONFIG_AS_HAS_EXPLICIT_RELOCS cflags-y += $(call cc-option,-mexplicit-relocs) KBUILD_CFLAGS_KERNEL += $(call cc-option,-mdirect-extern-access) +KBUILD_CFLAGS_KERNEL += $(call cc-option,-fdirect-access-external-data) KBUILD_AFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) KBUILD_CFLAGS_MODULE += $(call cc-option,-fno-direct-access-external-data) KBUILD_AFLAGS_MODULE += $(call cc-option,-mno-relax) $(call cc-option,-Wa$(comma)-mno-relax) @@ -142,6 +143,8 @@ vdso-install-y += arch/loongarch/vdso/vdso.so.dbg all: $(notdir $(KBUILD_IMAGE)) +vmlinuz.efi: vmlinux.efi + vmlinux.elf vmlinux.efi vmlinuz.efi: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $(boot)/$@ diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index c9544f358c33..655db7d7a427 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -609,8 +609,7 @@ lu32i.d \reg, 0 lu52i.d \reg, \reg, 0 .pushsection ".la_abs", "aw", %progbits - 768: - .dword 768b-766b + .dword 766b .dword \sym .popsection #endif diff --git a/arch/loongarch/include/asm/percpu.h b/arch/loongarch/include/asm/percpu.h index ed5da02b1cf6..9b36ac003f89 100644 --- a/arch/loongarch/include/asm/percpu.h +++ b/arch/loongarch/include/asm/percpu.h @@ -40,13 +40,13 @@ static __always_inline unsigned long __percpu_##op(void *ptr, \ switch (size) { \ case 4: \ __asm__ __volatile__( \ - "am"#asm_op".w" " %[ret], %[val], %[ptr] \n" \ + "am"#asm_op".w" " %[ret], %[val], %[ptr] \n" \ : [ret] "=&r" (ret), [ptr] "+ZB"(*(u32 *)ptr) \ : [val] "r" (val)); \ break; \ case 8: \ __asm__ __volatile__( \ - "am"#asm_op".d" " %[ret], %[val], %[ptr] \n" \ + "am"#asm_op".d" " %[ret], %[val], %[ptr] \n" \ : [ret] "=&r" (ret), [ptr] "+ZB"(*(u64 *)ptr) \ : [val] "r" (val)); \ break; \ @@ -63,7 +63,7 @@ PERCPU_OP(and, and, &) PERCPU_OP(or, or, |) #undef PERCPU_OP -static __always_inline unsigned long __percpu_read(void *ptr, int size) +static __always_inline unsigned long __percpu_read(void __percpu *ptr, int size) { unsigned long ret; @@ -100,7 +100,7 @@ static __always_inline unsigned long __percpu_read(void *ptr, int size) return ret; } -static __always_inline void __percpu_write(void *ptr, unsigned long val, int size) +static __always_inline void __percpu_write(void __percpu *ptr, unsigned long val, int size) { switch (size) { case 1: @@ -132,8 +132,7 @@ static __always_inline void __percpu_write(void *ptr, unsigned long val, int siz } } -static __always_inline unsigned long __percpu_xchg(void *ptr, unsigned long val, - int size) +static __always_inline unsigned long __percpu_xchg(void *ptr, unsigned long val, int size) { switch (size) { case 1: diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h index a0bc159ce8bd..ee52fb1e9963 100644 --- a/arch/loongarch/include/asm/setup.h +++ b/arch/loongarch/include/asm/setup.h @@ -25,7 +25,7 @@ extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len #ifdef CONFIG_RELOCATABLE struct rela_la_abs { - long offset; + long pc; long symvalue; }; diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c index 6c3eff9af9fb..1acfa704c8d0 100644 --- a/arch/loongarch/kernel/relocate.c +++ b/arch/loongarch/kernel/relocate.c @@ -52,7 +52,7 @@ static inline void __init relocate_absolute(long random_offset) for (p = begin; (void *)p < end; p++) { long v = p->symvalue; uint32_t lu12iw, ori, lu32id, lu52id; - union loongarch_instruction *insn = (void *)p - p->offset; + union loongarch_instruction *insn = (void *)p->pc; lu12iw = (v >> 12) & 0xfffff; ori = v & 0xfff; @@ -102,6 +102,14 @@ static inline __init unsigned long get_random_boot(void) return hash; } +static int __init nokaslr(char *p) +{ + pr_info("KASLR is disabled.\n"); + + return 0; /* Print a notice and silence the boot warning */ +} +early_param("nokaslr", nokaslr); + static inline __init bool kaslr_disabled(void) { char *str; diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index 3064af94db9c..e7015f7b70e3 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -58,14 +58,16 @@ static int constant_set_state_oneshot(struct clock_event_device *evt) return 0; } -static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) +static int constant_set_state_periodic(struct clock_event_device *evt) { + unsigned long period; unsigned long timer_config; raw_spin_lock(&state_lock); - timer_config = csr_read64(LOONGARCH_CSR_TCFG); - timer_config &= ~CSR_TCFG_EN; + period = const_clock_freq / HZ; + timer_config = period & CSR_TCFG_VAL; + timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN); csr_write64(timer_config, LOONGARCH_CSR_TCFG); raw_spin_unlock(&state_lock); @@ -73,16 +75,14 @@ static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) return 0; } -static int constant_set_state_periodic(struct clock_event_device *evt) +static int constant_set_state_shutdown(struct clock_event_device *evt) { - unsigned long period; unsigned long timer_config; raw_spin_lock(&state_lock); - period = const_clock_freq / HZ; - timer_config = period & CSR_TCFG_VAL; - timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN); + timer_config = csr_read64(LOONGARCH_CSR_TCFG); + timer_config &= ~CSR_TCFG_EN; csr_write64(timer_config, LOONGARCH_CSR_TCFG); raw_spin_unlock(&state_lock); @@ -90,11 +90,6 @@ static int constant_set_state_periodic(struct clock_event_device *evt) return 0; } -static int constant_set_state_shutdown(struct clock_event_device *evt) -{ - return 0; -} - static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt) { unsigned long timer_config; @@ -161,7 +156,7 @@ int constant_clockevent_init(void) cd->rating = 320; cd->cpumask = cpumask_of(cpu); cd->set_state_oneshot = constant_set_state_oneshot; - cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; + cd->set_state_oneshot_stopped = constant_set_state_shutdown; cd->set_state_periodic = constant_set_state_periodic; cd->set_state_shutdown = constant_set_state_shutdown; cd->set_next_event = constant_timer_next_event; diff --git a/arch/loongarch/mm/pgtable.c b/arch/loongarch/mm/pgtable.c index 71d0539e2d0b..2aae72e63871 100644 --- a/arch/loongarch/mm/pgtable.c +++ b/arch/loongarch/mm/pgtable.c @@ -13,13 +13,13 @@ struct page *dmw_virt_to_page(unsigned long kaddr) { return pfn_to_page(virt_to_pfn(kaddr)); } -EXPORT_SYMBOL_GPL(dmw_virt_to_page); +EXPORT_SYMBOL(dmw_virt_to_page); struct page *tlb_virt_to_page(unsigned long kaddr) { return pfn_to_page(pte_pfn(*virt_to_kpte(kaddr))); } -EXPORT_SYMBOL_GPL(tlb_virt_to_page); +EXPORT_SYMBOL(tlb_virt_to_page); pgd_t *pgd_alloc(struct mm_struct *mm) { diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index fd69dfa0cdab..d14ccc948a29 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -115,9 +115,12 @@ config ARCH_HAS_ILOG2_U64 default n config GENERIC_BUG - bool - default y + def_bool y depends on BUG + select GENERIC_BUG_RELATIVE_POINTERS if 64BIT + +config GENERIC_BUG_RELATIVE_POINTERS + bool config GENERIC_HWEIGHT bool @@ -140,11 +143,11 @@ config ARCH_MMAP_RND_COMPAT_BITS_MIN default 8 config ARCH_MMAP_RND_BITS_MAX - default 24 if 64BIT - default 17 + default 18 if 64BIT + default 13 config ARCH_MMAP_RND_COMPAT_BITS_MAX - default 17 + default 13 # unless you want to implement ACPI on PA-RISC ... ;-) config PM diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h index 1ed45fd085d3..1eb488f25b83 100644 --- a/arch/parisc/include/asm/alternative.h +++ b/arch/parisc/include/asm/alternative.h @@ -34,7 +34,8 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end, /* Alternative SMP implementation. */ #define ALTERNATIVE(cond, replacement) "!0:" \ - ".section .altinstructions, \"aw\" !" \ + ".section .altinstructions, \"a\" !" \ + ".align 4 !" \ ".word (0b-4-.) !" \ ".hword 1, " __stringify(cond) " !" \ ".word " __stringify(replacement) " !" \ @@ -44,7 +45,8 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end, /* to replace one single instructions by a new instruction */ #define ALTERNATIVE(from, to, cond, replacement)\ - .section .altinstructions, "aw" ! \ + .section .altinstructions, "a" ! \ + .align 4 ! \ .word (from - .) ! \ .hword (to - from)/4, cond ! \ .word replacement ! \ @@ -52,7 +54,8 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end, /* to replace multiple instructions by new code */ #define ALTERNATIVE_CODE(from, num_instructions, cond, new_instr_ptr)\ - .section .altinstructions, "aw" ! \ + .section .altinstructions, "a" ! \ + .align 4 ! \ .word (from - .) ! \ .hword -num_instructions, cond ! \ .word (new_instr_ptr - .) ! \ diff --git a/arch/parisc/include/asm/assembly.h b/arch/parisc/include/asm/assembly.h index 75677b526b2b..74d17d7e759d 100644 --- a/arch/parisc/include/asm/assembly.h +++ b/arch/parisc/include/asm/assembly.h @@ -574,6 +574,7 @@ */ #define ASM_EXCEPTIONTABLE_ENTRY(fault_addr, except_addr) \ .section __ex_table,"aw" ! \ + .align 4 ! \ .word (fault_addr - .), (except_addr - .) ! \ .previous diff --git a/arch/parisc/include/asm/bug.h b/arch/parisc/include/asm/bug.h index 4b6d60b94124..1641ff9a8b83 100644 --- a/arch/parisc/include/asm/bug.h +++ b/arch/parisc/include/asm/bug.h @@ -17,24 +17,27 @@ #define PARISC_BUG_BREAK_ASM "break 0x1f, 0x1fff" #define PARISC_BUG_BREAK_INSN 0x03ffe01f /* PARISC_BUG_BREAK_ASM */ -#if defined(CONFIG_64BIT) -#define ASM_WORD_INSN ".dword\t" +#ifdef CONFIG_GENERIC_BUG_RELATIVE_POINTERS +# define __BUG_REL(val) ".word " __stringify(val) " - ." #else -#define ASM_WORD_INSN ".word\t" +# define __BUG_REL(val) ".word " __stringify(val) #endif + #ifdef CONFIG_DEBUG_BUGVERBOSE #define BUG() \ do { \ asm volatile("\n" \ "1:\t" PARISC_BUG_BREAK_ASM "\n" \ - "\t.pushsection __bug_table,\"aw\"\n" \ - "2:\t" ASM_WORD_INSN "1b, %c0\n" \ - "\t.short %c1, %c2\n" \ - "\t.org 2b+%c3\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align 4\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t" __BUG_REL(%c0) "\n" \ + "\t.short %1, %2\n" \ + "\t.blockz %3-2*4-2*2\n" \ "\t.popsection" \ : : "i" (__FILE__), "i" (__LINE__), \ - "i" (0), "i" (sizeof(struct bug_entry)) ); \ + "i" (0), "i" (sizeof(struct bug_entry)) ); \ unreachable(); \ } while(0) @@ -51,10 +54,12 @@ do { \ asm volatile("\n" \ "1:\t" PARISC_BUG_BREAK_ASM "\n" \ - "\t.pushsection __bug_table,\"aw\"\n" \ - "2:\t" ASM_WORD_INSN "1b, %c0\n" \ - "\t.short %c1, %c2\n" \ - "\t.org 2b+%c3\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align 4\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t" __BUG_REL(%c0) "\n" \ + "\t.short %1, %2\n" \ + "\t.blockz %3-2*4-2*2\n" \ "\t.popsection" \ : : "i" (__FILE__), "i" (__LINE__), \ "i" (BUGFLAG_WARNING|(flags)), \ @@ -65,10 +70,11 @@ do { \ asm volatile("\n" \ "1:\t" PARISC_BUG_BREAK_ASM "\n" \ - "\t.pushsection __bug_table,\"aw\"\n" \ - "2:\t" ASM_WORD_INSN "1b\n" \ - "\t.short %c0\n" \ - "\t.org 2b+%c1\n" \ + "\t.pushsection __bug_table,\"a\"\n" \ + "\t.align %2\n" \ + "2:\t" __BUG_REL(1b) "\n" \ + "\t.short %0\n" \ + "\t.blockz %1-4-2\n" \ "\t.popsection" \ : : "i" (BUGFLAG_WARNING|(flags)), \ "i" (sizeof(struct bug_entry)) ); \ diff --git a/arch/parisc/include/asm/elf.h b/arch/parisc/include/asm/elf.h index 140eaa97bf21..2d73d3c3cd37 100644 --- a/arch/parisc/include/asm/elf.h +++ b/arch/parisc/include/asm/elf.h @@ -349,15 +349,7 @@ struct pt_regs; /* forward declaration... */ #define ELF_HWCAP 0 -/* Masks for stack and mmap randomization */ -#define BRK_RND_MASK (is_32bit_task() ? 0x07ffUL : 0x3ffffUL) -#define MMAP_RND_MASK (is_32bit_task() ? 0x1fffUL : 0x3ffffUL) -#define STACK_RND_MASK MMAP_RND_MASK - -struct mm_struct; -extern unsigned long arch_randomize_brk(struct mm_struct *); -#define arch_randomize_brk arch_randomize_brk - +#define STACK_RND_MASK 0x7ff /* 8MB of VA */ #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 struct linux_binprm; diff --git a/arch/parisc/include/asm/jump_label.h b/arch/parisc/include/asm/jump_label.h index af2a598bc0f8..94428798b6aa 100644 --- a/arch/parisc/include/asm/jump_label.h +++ b/arch/parisc/include/asm/jump_label.h @@ -15,10 +15,12 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran asm_volatile_goto("1:\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" + ".align %1\n\t" ".word 1b - ., %l[l_yes] - .\n\t" __stringify(ASM_ULONG_INSN) " %c0 - .\n\t" ".popsection\n\t" - : : "i" (&((char *)key)[branch]) : : l_yes); + : : "i" (&((char *)key)[branch]), "i" (sizeof(long)) + : : l_yes); return false; l_yes: @@ -30,10 +32,12 @@ static __always_inline bool arch_static_branch_jump(struct static_key *key, bool asm_volatile_goto("1:\n\t" "b,n %l[l_yes]\n\t" ".pushsection __jump_table, \"aw\"\n\t" + ".align %1\n\t" ".word 1b - ., %l[l_yes] - .\n\t" __stringify(ASM_ULONG_INSN) " %c0 - .\n\t" ".popsection\n\t" - : : "i" (&((char *)key)[branch]) : : l_yes); + : : "i" (&((char *)key)[branch]), "i" (sizeof(long)) + : : l_yes); return false; l_yes: diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h index ee9e071859b2..47ebc4c91eaf 100644 --- a/arch/parisc/include/asm/ldcw.h +++ b/arch/parisc/include/asm/ldcw.h @@ -55,7 +55,7 @@ }) #ifdef CONFIG_SMP -# define __lock_aligned __section(".data..lock_aligned") +# define __lock_aligned __section(".data..lock_aligned") __aligned(16) #endif #endif /* __PARISC_LDCW_H */ diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index c05d121cf5d0..982aca20f56f 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -47,6 +47,8 @@ #ifndef __ASSEMBLY__ +struct rlimit; +unsigned long mmap_upper_limit(struct rlimit *rlim_stack); unsigned long calc_max_stack_size(unsigned long stack_max); /* diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 2bf660eabe42..4165079898d9 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -41,6 +41,7 @@ struct exception_table_entry { #define ASM_EXCEPTIONTABLE_ENTRY( fault_addr, except_addr )\ ".section __ex_table,\"aw\"\n" \ + ".align 4\n" \ ".word (" #fault_addr " - .), (" #except_addr " - .)\n\t" \ ".previous\n" diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h index 87245c584784..8d94739d75c6 100644 --- a/arch/parisc/include/uapi/asm/errno.h +++ b/arch/parisc/include/uapi/asm/errno.h @@ -75,7 +75,6 @@ /* We now return you to your regularly scheduled HPUX. */ -#define ENOSYM 215 /* symbol does not exist in executable */ #define ENOTSOCK 216 /* Socket operation on non-socket */ #define EDESTADDRREQ 217 /* Destination address required */ #define EMSGSIZE 218 /* Message too long */ @@ -101,7 +100,6 @@ #define ETIMEDOUT 238 /* Connection timed out */ #define ECONNREFUSED 239 /* Connection refused */ #define EREFUSED ECONNREFUSED /* for HP's NFS apparently */ -#define EREMOTERELEASE 240 /* Remote peer released connection */ #define EHOSTDOWN 241 /* Host is down */ #define EHOSTUNREACH 242 /* No route to host */ diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c index 29e2750f86a4..e95a977ba5f3 100644 --- a/arch/parisc/kernel/processor.c +++ b/arch/parisc/kernel/processor.c @@ -383,7 +383,7 @@ show_cpuinfo (struct seq_file *m, void *v) char cpu_name[60], *p; /* strip PA path from CPU name to not confuse lscpu */ - strlcpy(cpu_name, per_cpu(cpu_data, 0).dev->name, sizeof(cpu_name)); + strscpy(cpu_name, per_cpu(cpu_data, 0).dev->name, sizeof(cpu_name)); p = strrchr(cpu_name, '['); if (p) *(--p) = 0; diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index ab896eff7a1d..98af719d5f85 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -77,7 +77,7 @@ unsigned long calc_max_stack_size(unsigned long stack_max) * indicating that "current" should be used instead of a passed-in * value from the exec bprm as done with arch_pick_mmap_layout(). */ -static unsigned long mmap_upper_limit(struct rlimit *rlim_stack) +unsigned long mmap_upper_limit(struct rlimit *rlim_stack) { unsigned long stack_base; diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index 58694d1989c2..548051b0b4af 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -130,6 +130,7 @@ SECTIONS RO_DATA(8) /* unwind info */ + . = ALIGN(4); .PARISC.unwind : { __start___unwind = .; *(.PARISC.unwind) diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index dc17896a001a..c15eadbb9983 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -228,7 +228,6 @@ typedef struct thread_struct thread_struct; execve_tail(); \ } while (0) -/* Forward declaration, a strange C thing */ struct task_struct; struct mm_struct; struct seq_file; diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index cc364fce6aa9..ba75f6bee774 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -666,6 +666,7 @@ static int __init ipl_init(void) &ipl_ccw_attr_group_lpar); break; case IPL_TYPE_ECKD: + case IPL_TYPE_ECKD_DUMP: rc = sysfs_create_group(&ipl_kset->kobj, &ipl_eckd_attr_group); break; case IPL_TYPE_FCP: diff --git a/arch/s390/kernel/perf_pai_crypto.c b/arch/s390/kernel/perf_pai_crypto.c index 77fd24e6cbb6..39a91b00438a 100644 --- a/arch/s390/kernel/perf_pai_crypto.c +++ b/arch/s390/kernel/perf_pai_crypto.c @@ -279,12 +279,6 @@ static int paicrypt_event_init(struct perf_event *event) if (IS_ERR(cpump)) return PTR_ERR(cpump); - /* Event initialization sets last_tag to 0. When later on the events - * are deleted and re-added, do not reset the event count value to zero. - * Events are added, deleted and re-added when 2 or more events - * are active at the same time. - */ - event->hw.last_tag = 0; event->destroy = paicrypt_event_destroy; if (a->sample_period) { @@ -318,6 +312,11 @@ static void paicrypt_start(struct perf_event *event, int flags) { u64 sum; + /* Event initialization sets last_tag to 0. When later on the events + * are deleted and re-added, do not reset the event count value to zero. + * Events are added, deleted and re-added when 2 or more events + * are active at the same time. + */ if (!event->hw.last_tag) { event->hw.last_tag = 1; sum = paicrypt_getall(event); /* Get current value */ diff --git a/arch/s390/kernel/perf_pai_ext.c b/arch/s390/kernel/perf_pai_ext.c index 8ba0f1a3a39d..e7013a2e8960 100644 --- a/arch/s390/kernel/perf_pai_ext.c +++ b/arch/s390/kernel/perf_pai_ext.c @@ -260,7 +260,6 @@ static int paiext_event_init(struct perf_event *event) rc = paiext_alloc(a, event); if (rc) return rc; - event->hw.last_tag = 0; event->destroy = paiext_event_destroy; if (a->sample_period) { diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index a08f794a0e79..ce1c777227b4 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -4660,7 +4660,7 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) if (pmu->intel_cap.pebs_output_pt_available) pmu->pmu.capabilities |= PERF_PMU_CAP_AUX_OUTPUT; else - pmu->pmu.capabilities |= ~PERF_PMU_CAP_AUX_OUTPUT; + pmu->pmu.capabilities &= ~PERF_PMU_CAP_AUX_OUTPUT; intel_pmu_check_event_constraints(pmu->event_constraints, pmu->num_counters, diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 21556ad87f4b..8f3a4d16bb79 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <asm/apic.h> #include <asm/desc.h> +#include <asm/e820/api.h> #include <asm/sev.h> #include <asm/ibt.h> #include <asm/hypervisor.h> @@ -286,15 +287,31 @@ static int hv_cpu_die(unsigned int cpu) static int __init hv_pci_init(void) { - int gen2vm = efi_enabled(EFI_BOOT); + bool gen2vm = efi_enabled(EFI_BOOT); /* - * For Generation-2 VM, we exit from pci_arch_init() by returning 0. - * The purpose is to suppress the harmless warning: + * A Generation-2 VM doesn't support legacy PCI/PCIe, so both + * raw_pci_ops and raw_pci_ext_ops are NULL, and pci_subsys_init() -> + * pcibios_init() doesn't call pcibios_resource_survey() -> + * e820__reserve_resources_late(); as a result, any emulated persistent + * memory of E820_TYPE_PRAM (12) via the kernel parameter + * memmap=nn[KMG]!ss is not added into iomem_resource and hence can't be + * detected by register_e820_pmem(). Fix this by directly calling + * e820__reserve_resources_late() here: e820__reserve_resources_late() + * depends on e820__reserve_resources(), which has been called earlier + * from setup_arch(). Note: e820__reserve_resources_late() also adds + * any memory of E820_TYPE_PMEM (7) into iomem_resource, and + * acpi_nfit_register_region() -> acpi_nfit_insert_resource() -> + * region_intersects() returns REGION_INTERSECTS, so the memory of + * E820_TYPE_PMEM won't get added twice. + * + * We return 0 here so that pci_arch_init() won't print the warning: * "PCI: Fatal: No config space access function found" */ - if (gen2vm) + if (gen2vm) { + e820__reserve_resources_late(); return 0; + } /* For Generation-1 VM, we'll proceed in pci_arch_init(). */ return 1; diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index c8a7fc23f63c..f896eed4516c 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -16,6 +16,9 @@ #include <asm/x86_init.h> #include <asm/cpufeature.h> #include <asm/irq_vectors.h> +#include <asm/xen/hypervisor.h> + +#include <xen/xen.h> #ifdef CONFIG_ACPI_APEI # include <asm/pgtable_types.h> @@ -127,6 +130,17 @@ static inline void arch_acpi_set_proc_cap_bits(u32 *cap) if (!cpu_has(c, X86_FEATURE_MWAIT) || boot_option_idle_override == IDLE_NOMWAIT) *cap &= ~(ACPI_PROC_CAP_C_C1_FFH | ACPI_PROC_CAP_C_C2C3_FFH); + + if (xen_initial_domain()) { + /* + * When Linux is running as Xen dom0, the hypervisor is the + * entity in charge of the processor power management, and so + * Xen needs to check the OS capabilities reported in the + * processor capabilities buffer matches what the hypervisor + * driver supports. + */ + xen_sanitize_proc_cap_bits(cap); + } } static inline bool acpi_has_cpu_in_madt(void) diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h index 7048dfacc04b..a9088250770f 100644 --- a/arch/x86/include/asm/xen/hypervisor.h +++ b/arch/x86/include/asm/xen/hypervisor.h @@ -100,4 +100,13 @@ static inline void leave_lazy(enum xen_lazy_mode mode) enum xen_lazy_mode xen_get_lazy_mode(void); +#if defined(CONFIG_XEN_DOM0) && defined(CONFIG_ACPI) +void xen_sanitize_proc_cap_bits(uint32_t *buf); +#else +static inline void xen_sanitize_proc_cap_bits(uint32_t *buf) +{ + BUG(); +} +#endif + #endif /* _ASM_X86_XEN_HYPERVISOR_H */ diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index d0918a75cb00..1a0dd80d81ac 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -63,6 +63,7 @@ int acpi_fix_pin2_polarity __initdata; #ifdef CONFIG_X86_LOCAL_APIC static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; +static bool has_lapic_cpus __initdata; static bool acpi_support_online_capable; #endif @@ -233,6 +234,14 @@ acpi_parse_x2apic(union acpi_subtable_headers *header, const unsigned long end) return 0; /* + * According to https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#processor-local-x2apic-structure + * when MADT provides both valid LAPIC and x2APIC entries, the APIC ID + * in x2APIC must be equal or greater than 0xff. + */ + if (has_lapic_cpus && apic_id < 0xff) + return 0; + + /* * We need to register disabled CPU as well to permit * counting disabled CPUs. This allows us to size * cpus_possible_map more accurately, to permit @@ -1114,10 +1123,7 @@ static int __init early_acpi_parse_madt_lapic_addr_ovr(void) static int __init acpi_parse_madt_lapic_entries(void) { - int count; - int x2count = 0; - int ret; - struct acpi_subtable_proc madt_proc[2]; + int count, x2count = 0; if (!boot_cpu_has(X86_FEATURE_APIC)) return -ENODEV; @@ -1126,21 +1132,11 @@ static int __init acpi_parse_madt_lapic_entries(void) acpi_parse_sapic, MAX_LOCAL_APIC); if (!count) { - memset(madt_proc, 0, sizeof(madt_proc)); - madt_proc[0].id = ACPI_MADT_TYPE_LOCAL_APIC; - madt_proc[0].handler = acpi_parse_lapic; - madt_proc[1].id = ACPI_MADT_TYPE_LOCAL_X2APIC; - madt_proc[1].handler = acpi_parse_x2apic; - ret = acpi_table_parse_entries_array(ACPI_SIG_MADT, - sizeof(struct acpi_table_madt), - madt_proc, ARRAY_SIZE(madt_proc), MAX_LOCAL_APIC); - if (ret < 0) { - pr_err("Error parsing LAPIC/X2APIC entries\n"); - return ret; - } - - count = madt_proc[0].count; - x2count = madt_proc[1].count; + count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_APIC, + acpi_parse_lapic, MAX_LOCAL_APIC); + has_lapic_cpus = count > 0; + x2count = acpi_table_parse_madt(ACPI_MADT_TYPE_LOCAL_X2APIC, + acpi_parse_x2apic, MAX_LOCAL_APIC); } if (!count && !x2count) { pr_err("No LAPIC entries present\n"); diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c index 9373ec01c5ae..13b45b9c806d 100644 --- a/arch/x86/kernel/cpu/microcode/amd.c +++ b/arch/x86/kernel/cpu/microcode/amd.c @@ -104,8 +104,6 @@ struct cont_desc { size_t size; }; -static u32 ucode_new_rev; - /* * Microcode patch container file is prepended to the initrd in cpio * format. See Documentation/arch/x86/microcode.rst @@ -442,12 +440,11 @@ static int __apply_microcode_amd(struct microcode_amd *mc) * * Returns true if container found (sets @desc), false otherwise. */ -static bool early_apply_microcode(u32 cpuid_1_eax, void *ucode, size_t size) +static bool early_apply_microcode(u32 cpuid_1_eax, u32 old_rev, void *ucode, size_t size) { struct cont_desc desc = { 0 }; struct microcode_amd *mc; bool ret = false; - u32 rev, dummy; desc.cpuid_1_eax = cpuid_1_eax; @@ -457,22 +454,15 @@ static bool early_apply_microcode(u32 cpuid_1_eax, void *ucode, size_t size) if (!mc) return ret; - native_rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); - /* * Allow application of the same revision to pick up SMT-specific * changes even if the revision of the other SMT thread is already * up-to-date. */ - if (rev > mc->hdr.patch_id) + if (old_rev > mc->hdr.patch_id) return ret; - if (!__apply_microcode_amd(mc)) { - ucode_new_rev = mc->hdr.patch_id; - ret = true; - } - - return ret; + return !__apply_microcode_amd(mc); } static bool get_builtin_microcode(struct cpio_data *cp, unsigned int family) @@ -506,9 +496,12 @@ static void __init find_blobs_in_containers(unsigned int cpuid_1_eax, struct cpi *ret = cp; } -void __init load_ucode_amd_bsp(unsigned int cpuid_1_eax) +void __init load_ucode_amd_bsp(struct early_load_data *ed, unsigned int cpuid_1_eax) { struct cpio_data cp = { }; + u32 dummy; + + native_rdmsr(MSR_AMD64_PATCH_LEVEL, ed->old_rev, dummy); /* Needed in load_microcode_amd() */ ucode_cpu_info[0].cpu_sig.sig = cpuid_1_eax; @@ -517,7 +510,8 @@ void __init load_ucode_amd_bsp(unsigned int cpuid_1_eax) if (!(cp.data && cp.size)) return; - early_apply_microcode(cpuid_1_eax, cp.data, cp.size); + if (early_apply_microcode(cpuid_1_eax, ed->old_rev, cp.data, cp.size)) + native_rdmsr(MSR_AMD64_PATCH_LEVEL, ed->new_rev, dummy); } static enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size); @@ -625,10 +619,8 @@ void reload_ucode_amd(unsigned int cpu) rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy); if (rev < mc->hdr.patch_id) { - if (!__apply_microcode_amd(mc)) { - ucode_new_rev = mc->hdr.patch_id; - pr_info("reload patch_level=0x%08x\n", ucode_new_rev); - } + if (!__apply_microcode_amd(mc)) + pr_info_once("reload revision: 0x%08x\n", mc->hdr.patch_id); } } @@ -649,8 +641,6 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) if (p && (p->patch_id == csig->rev)) uci->mc = p->data; - pr_info("CPU%d: patch_level=0x%08x\n", cpu, csig->rev); - return 0; } @@ -691,8 +681,6 @@ static enum ucode_state apply_microcode_amd(int cpu) rev = mc_amd->hdr.patch_id; ret = UCODE_UPDATED; - pr_info("CPU%d: new patch_level=0x%08x\n", cpu, rev); - out: uci->cpu_sig.rev = rev; c->microcode = rev; @@ -935,11 +923,6 @@ struct microcode_ops * __init init_amd_microcode(void) pr_warn("AMD CPU family 0x%x not supported\n", c->x86); return NULL; } - - if (ucode_new_rev) - pr_info_once("microcode updated early to new patch_level=0x%08x\n", - ucode_new_rev); - return µcode_amd_ops; } diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 666d25bbc5ad..232026a239a6 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -41,8 +41,6 @@ #include "internal.h" -#define DRIVER_VERSION "2.2" - static struct microcode_ops *microcode_ops; bool dis_ucode_ldr = true; @@ -77,6 +75,8 @@ static u32 final_levels[] = { 0, /* T-101 terminator */ }; +struct early_load_data early_data; + /* * Check the current patch level on this CPU. * @@ -155,9 +155,9 @@ void __init load_ucode_bsp(void) return; if (intel) - load_ucode_intel_bsp(); + load_ucode_intel_bsp(&early_data); else - load_ucode_amd_bsp(cpuid_1_eax); + load_ucode_amd_bsp(&early_data, cpuid_1_eax); } void load_ucode_ap(void) @@ -828,6 +828,11 @@ static int __init microcode_init(void) if (!microcode_ops) return -ENODEV; + pr_info_once("Current revision: 0x%08x\n", (early_data.new_rev ?: early_data.old_rev)); + + if (early_data.new_rev) + pr_info_once("Updated early from: 0x%08x\n", early_data.old_rev); + microcode_pdev = platform_device_register_simple("microcode", -1, NULL, 0); if (IS_ERR(microcode_pdev)) return PTR_ERR(microcode_pdev); @@ -846,8 +851,6 @@ static int __init microcode_init(void) cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/microcode:online", mc_cpu_online, mc_cpu_down_prep); - pr_info("Microcode Update Driver: v%s.", DRIVER_VERSION); - return 0; out_pdev: diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 6024feb98d29..070426b9895f 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c @@ -339,16 +339,9 @@ static enum ucode_state __apply_microcode(struct ucode_cpu_info *uci, static enum ucode_state apply_microcode_early(struct ucode_cpu_info *uci) { struct microcode_intel *mc = uci->mc; - enum ucode_state ret; - u32 cur_rev, date; + u32 cur_rev; - ret = __apply_microcode(uci, mc, &cur_rev); - if (ret == UCODE_UPDATED) { - date = mc->hdr.date; - pr_info_once("updated early: 0x%x -> 0x%x, date = %04x-%02x-%02x\n", - cur_rev, mc->hdr.rev, date & 0xffff, date >> 24, (date >> 16) & 0xff); - } - return ret; + return __apply_microcode(uci, mc, &cur_rev); } static __init bool load_builtin_intel_microcode(struct cpio_data *cp) @@ -413,13 +406,17 @@ static int __init save_builtin_microcode(void) early_initcall(save_builtin_microcode); /* Load microcode on BSP from initrd or builtin blobs */ -void __init load_ucode_intel_bsp(void) +void __init load_ucode_intel_bsp(struct early_load_data *ed) { struct ucode_cpu_info uci; + ed->old_rev = intel_get_microcode_revision(); + uci.mc = get_microcode_blob(&uci, false); if (uci.mc && apply_microcode_early(&uci) == UCODE_UPDATED) ucode_patch_va = UCODE_BSP_LOADED; + + ed->new_rev = uci.cpu_sig.rev; } void load_ucode_intel_ap(void) diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h index f8047b12329a..21776c529fa9 100644 --- a/arch/x86/kernel/cpu/microcode/internal.h +++ b/arch/x86/kernel/cpu/microcode/internal.h @@ -37,6 +37,12 @@ struct microcode_ops { use_nmi : 1; }; +struct early_load_data { + u32 old_rev; + u32 new_rev; +}; + +extern struct early_load_data early_data; extern struct ucode_cpu_info ucode_cpu_info[]; struct cpio_data find_microcode_in_initrd(const char *path); @@ -92,14 +98,14 @@ extern bool dis_ucode_ldr; extern bool force_minrev; #ifdef CONFIG_CPU_SUP_AMD -void load_ucode_amd_bsp(unsigned int family); +void load_ucode_amd_bsp(struct early_load_data *ed, unsigned int family); void load_ucode_amd_ap(unsigned int family); int save_microcode_in_initrd_amd(unsigned int family); void reload_ucode_amd(unsigned int cpu); struct microcode_ops *init_amd_microcode(void); void exit_amd_microcode(void); #else /* CONFIG_CPU_SUP_AMD */ -static inline void load_ucode_amd_bsp(unsigned int family) { } +static inline void load_ucode_amd_bsp(struct early_load_data *ed, unsigned int family) { } static inline void load_ucode_amd_ap(unsigned int family) { } static inline int save_microcode_in_initrd_amd(unsigned int family) { return -EINVAL; } static inline void reload_ucode_amd(unsigned int cpu) { } @@ -108,12 +114,12 @@ static inline void exit_amd_microcode(void) { } #endif /* !CONFIG_CPU_SUP_AMD */ #ifdef CONFIG_CPU_SUP_INTEL -void load_ucode_intel_bsp(void); +void load_ucode_intel_bsp(struct early_load_data *ed); void load_ucode_intel_ap(void); void reload_ucode_intel(void); struct microcode_ops *init_intel_microcode(void); #else /* CONFIG_CPU_SUP_INTEL */ -static inline void load_ucode_intel_bsp(void) { } +static inline void load_ucode_intel_bsp(struct early_load_data *ed) { } static inline void load_ucode_intel_ap(void) { } static inline void reload_ucode_intel(void) { } static inline struct microcode_ops *init_intel_microcode(void) { return NULL; } diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index e6bba12c759c..01fa06dd06b6 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -262,11 +262,14 @@ static uint32_t __init ms_hyperv_platform(void) static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs) { static atomic_t nmi_cpu = ATOMIC_INIT(-1); + unsigned int old_cpu, this_cpu; if (!unknown_nmi_panic) return NMI_DONE; - if (atomic_cmpxchg(&nmi_cpu, -1, raw_smp_processor_id()) != -1) + old_cpu = -1; + this_cpu = raw_smp_processor_id(); + if (!atomic_try_cmpxchg(&nmi_cpu, &old_cpu, this_cpu)) return NMI_HANDLED; return NMI_DONE; diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index cacf2ede6217..23d8aaf8d9fd 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c @@ -175,9 +175,6 @@ int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) frame = get_sigframe(ksig, regs, sizeof(struct rt_sigframe), &fp); uc_flags = frame_uc_flags(regs); - if (setup_signal_shadow_stack(ksig)) - return -EFAULT; - if (!user_access_begin(frame, sizeof(*frame))) return -EFAULT; @@ -198,6 +195,9 @@ int x64_setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) return -EFAULT; } + if (setup_signal_shadow_stack(ksig)) + return -EFAULT; + /* Set up registers for signal handler */ regs->di = ksig->sig; /* In case the signal handler was declared without prototypes */ diff --git a/block/bdev.c b/block/bdev.c index e4cfb7adb645..750aec178b6a 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -425,6 +425,8 @@ void bdev_set_nr_sectors(struct block_device *bdev, sector_t sectors) void bdev_add(struct block_device *bdev, dev_t dev) { + if (bdev_stable_writes(bdev)) + mapping_set_stable_writes(bdev->bd_inode->i_mapping); bdev->bd_dev = dev; bdev->bd_inode->i_rdev = dev; bdev->bd_inode->i_ino = dev; diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 4a42ea2972ad..4b48c2c44098 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -577,6 +577,7 @@ static void blkg_destroy_all(struct gendisk *disk) struct request_queue *q = disk->queue; struct blkcg_gq *blkg, *n; int count = BLKG_DESTROY_BATCH_SIZE; + int i; restart: spin_lock_irq(&q->queue_lock); @@ -602,6 +603,18 @@ restart: } } + /* + * Mark policy deactivated since policy offline has been done, and + * the free is scheduled, so future blkcg_deactivate_policy() can + * be bypassed + */ + for (i = 0; i < BLKCG_MAX_POLS; i++) { + struct blkcg_policy *pol = blkcg_policy[i]; + + if (pol) + __clear_bit(pol->plid, q->blkcg_pols); + } + q->root_blkg = NULL; spin_unlock_irq(&q->queue_lock); } diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 624c03c8fe64..fd482439afbc 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -249,8 +249,6 @@ static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, { struct blkcg_gq *blkg; - WARN_ON_ONCE(!rcu_read_lock_held()); - if (blkcg == &blkcg_root) return q->root_blkg; diff --git a/block/blk-mq.c b/block/blk-mq.c index e2d11183f62e..900c1be1fee1 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2858,11 +2858,8 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, }; struct request *rq; - if (unlikely(bio_queue_enter(bio))) - return NULL; - if (blk_mq_attempt_bio_merge(q, bio, nsegs)) - goto queue_exit; + return NULL; rq_qos_throttle(q, bio); @@ -2878,35 +2875,23 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q, rq_qos_cleanup(q, bio); if (bio->bi_opf & REQ_NOWAIT) bio_wouldblock_error(bio); -queue_exit: - blk_queue_exit(q); return NULL; } -static inline struct request *blk_mq_get_cached_request(struct request_queue *q, - struct blk_plug *plug, struct bio **bio, unsigned int nsegs) +/* return true if this @rq can be used for @bio */ +static bool blk_mq_can_use_cached_rq(struct request *rq, struct blk_plug *plug, + struct bio *bio) { - struct request *rq; - enum hctx_type type, hctx_type; + enum hctx_type type = blk_mq_get_hctx_type(bio->bi_opf); + enum hctx_type hctx_type = rq->mq_hctx->type; - if (!plug) - return NULL; - rq = rq_list_peek(&plug->cached_rq); - if (!rq || rq->q != q) - return NULL; + WARN_ON_ONCE(rq_list_peek(&plug->cached_rq) != rq); - if (blk_mq_attempt_bio_merge(q, *bio, nsegs)) { - *bio = NULL; - return NULL; - } - - type = blk_mq_get_hctx_type((*bio)->bi_opf); - hctx_type = rq->mq_hctx->type; if (type != hctx_type && !(type == HCTX_TYPE_READ && hctx_type == HCTX_TYPE_DEFAULT)) - return NULL; - if (op_is_flush(rq->cmd_flags) != op_is_flush((*bio)->bi_opf)) - return NULL; + return false; + if (op_is_flush(rq->cmd_flags) != op_is_flush(bio->bi_opf)) + return false; /* * If any qos ->throttle() end up blocking, we will have flushed the @@ -2914,12 +2899,12 @@ static inline struct request *blk_mq_get_cached_request(struct request_queue *q, * before we throttle. */ plug->cached_rq = rq_list_next(rq); - rq_qos_throttle(q, *bio); + rq_qos_throttle(rq->q, bio); blk_mq_rq_time_init(rq, 0); - rq->cmd_flags = (*bio)->bi_opf; + rq->cmd_flags = bio->bi_opf; INIT_LIST_HEAD(&rq->queuelist); - return rq; + return true; } static void bio_set_ioprio(struct bio *bio) @@ -2949,7 +2934,7 @@ void blk_mq_submit_bio(struct bio *bio) struct blk_plug *plug = blk_mq_plug(bio); const int is_sync = op_is_sync(bio->bi_opf); struct blk_mq_hw_ctx *hctx; - struct request *rq; + struct request *rq = NULL; unsigned int nr_segs = 1; blk_status_t ret; @@ -2960,20 +2945,36 @@ void blk_mq_submit_bio(struct bio *bio) return; } - if (!bio_integrity_prep(bio)) - return; - bio_set_ioprio(bio); - rq = blk_mq_get_cached_request(q, plug, &bio, nr_segs); - if (!rq) { - if (!bio) + if (plug) { + rq = rq_list_peek(&plug->cached_rq); + if (rq && rq->q != q) + rq = NULL; + } + if (rq) { + if (!bio_integrity_prep(bio)) return; - rq = blk_mq_get_new_requests(q, plug, bio, nr_segs); - if (unlikely(!rq)) + if (blk_mq_attempt_bio_merge(q, bio, nr_segs)) return; + if (blk_mq_can_use_cached_rq(rq, plug, bio)) + goto done; + percpu_ref_get(&q->q_usage_counter); + } else { + if (unlikely(bio_queue_enter(bio))) + return; + if (!bio_integrity_prep(bio)) + goto fail; + } + + rq = blk_mq_get_new_requests(q, plug, bio, nr_segs); + if (unlikely(!rq)) { +fail: + blk_queue_exit(q); + return; } +done: trace_block_getrq(bio); rq_qos_track(q, rq, bio); diff --git a/block/blk-pm.c b/block/blk-pm.c index 6b72b2e03fc8..42e842074715 100644 --- a/block/blk-pm.c +++ b/block/blk-pm.c @@ -163,39 +163,16 @@ EXPORT_SYMBOL(blk_pre_runtime_resume); * @q: the queue of the device * * Description: - * For historical reasons, this routine merely calls blk_set_runtime_active() - * to do the real work of restarting the queue. It does this regardless of - * whether the device's runtime-resume succeeded; even if it failed the + * Restart the queue of a runtime suspended device. It does this regardless + * of whether the device's runtime-resume succeeded; even if it failed the * driver or error handler will need to communicate with the device. * * This function should be called near the end of the device's - * runtime_resume callback. + * runtime_resume callback to correct queue runtime PM status and re-enable + * peeking requests from the queue. */ void blk_post_runtime_resume(struct request_queue *q) { - blk_set_runtime_active(q); -} -EXPORT_SYMBOL(blk_post_runtime_resume); - -/** - * blk_set_runtime_active - Force runtime status of the queue to be active - * @q: the queue of the device - * - * If the device is left runtime suspended during system suspend the resume - * hook typically resumes the device and corrects runtime status - * accordingly. However, that does not affect the queue runtime PM status - * which is still "suspended". This prevents processing requests from the - * queue. - * - * This function can be used in driver's resume hook to correct queue - * runtime PM status and re-enable peeking requests from the queue. It - * should be called before first request is added to the queue. - * - * This function is also called by blk_post_runtime_resume() for - * runtime resumes. It does everything necessary to restart the queue. - */ -void blk_set_runtime_active(struct request_queue *q) -{ int old_status; if (!q->dev) @@ -211,4 +188,4 @@ void blk_set_runtime_active(struct request_queue *q) if (old_status != RPM_ACTIVE) blk_clear_pm_only(q); } -EXPORT_SYMBOL(blk_set_runtime_active); +EXPORT_SYMBOL(blk_post_runtime_resume); diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 13e4377a8b28..16f5766620a4 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1320,6 +1320,7 @@ static void tg_conf_updated(struct throtl_grp *tg, bool global) tg_bps_limit(tg, READ), tg_bps_limit(tg, WRITE), tg_iops_limit(tg, READ), tg_iops_limit(tg, WRITE)); + rcu_read_lock(); /* * Update has_rules[] flags for the updated tg's subtree. A tg is * considered to have rules if either the tg itself or any of its @@ -1347,6 +1348,7 @@ static void tg_conf_updated(struct throtl_grp *tg, bool global) this_tg->latency_target = max(this_tg->latency_target, parent_tg->latency_target); } + rcu_read_unlock(); /* * We're already holding queue_lock and know @tg is valid. Let's diff --git a/drivers/accel/ivpu/Kconfig b/drivers/accel/ivpu/Kconfig index 1a4c4ed9d113..682c53245286 100644 --- a/drivers/accel/ivpu/Kconfig +++ b/drivers/accel/ivpu/Kconfig @@ -1,16 +1,17 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_ACCEL_IVPU - tristate "Intel VPU for Meteor Lake and newer" + tristate "Intel NPU (Neural Processing Unit)" depends on DRM_ACCEL depends on X86_64 && !UML depends on PCI && PCI_MSI select FW_LOADER - select SHMEM + select DRM_GEM_SHMEM_HELPER select GENERIC_ALLOCATOR help - Choose this option if you have a system that has an 14th generation Intel CPU - or newer. VPU stands for Versatile Processing Unit and it's a CPU-integrated - inference accelerator for Computer Vision and Deep Learning applications. + Choose this option if you have a system with an 14th generation + Intel CPU (Meteor Lake) or newer. Intel NPU (formerly called Intel VPU) + is a CPU-integrated inference accelerator for Computer Vision + and Deep Learning applications. If "M" is selected, the module will be called intel_vpu. diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c index ea453b985b49..19035230563d 100644 --- a/drivers/accel/ivpu/ivpu_debugfs.c +++ b/drivers/accel/ivpu/ivpu_debugfs.c @@ -14,6 +14,7 @@ #include "ivpu_fw.h" #include "ivpu_fw_log.h" #include "ivpu_gem.h" +#include "ivpu_hw.h" #include "ivpu_jsm_msg.h" #include "ivpu_pm.h" @@ -115,6 +116,31 @@ static const struct drm_debugfs_info vdev_debugfs_list[] = { {"reset_pending", reset_pending_show, 0}, }; +static ssize_t +dvfs_mode_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + struct ivpu_fw_info *fw = vdev->fw; + u32 dvfs_mode; + int ret; + + ret = kstrtou32_from_user(user_buf, size, 0, &dvfs_mode); + if (ret < 0) + return ret; + + fw->dvfs_mode = dvfs_mode; + + ivpu_pm_schedule_recovery(vdev); + + return size; +} + +static const struct file_operations dvfs_mode_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = dvfs_mode_fops_write, +}; + static int fw_log_show(struct seq_file *s, void *v) { struct ivpu_device *vdev = s->private; @@ -152,6 +178,30 @@ static const struct file_operations fw_log_fops = { }; static ssize_t +fw_profiling_freq_fops_write(struct file *file, const char __user *user_buf, + size_t size, loff_t *pos) +{ + struct ivpu_device *vdev = file->private_data; + bool enable; + int ret; + + ret = kstrtobool_from_user(user_buf, size, &enable); + if (ret < 0) + return ret; + + ivpu_hw_profiling_freq_drive(vdev, enable); + ivpu_pm_schedule_recovery(vdev); + + return size; +} + +static const struct file_operations fw_profiling_freq_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = fw_profiling_freq_fops_write, +}; + +static ssize_t fw_trace_destination_mask_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) { @@ -280,6 +330,9 @@ void ivpu_debugfs_init(struct ivpu_device *vdev) debugfs_create_file("force_recovery", 0200, debugfs_root, vdev, &ivpu_force_recovery_fops); + debugfs_create_file("dvfs_mode", 0200, debugfs_root, vdev, + &dvfs_mode_fops); + debugfs_create_file("fw_log", 0644, debugfs_root, vdev, &fw_log_fops); debugfs_create_file("fw_trace_destination_mask", 0200, debugfs_root, vdev, @@ -291,4 +344,8 @@ void ivpu_debugfs_init(struct ivpu_device *vdev) debugfs_create_file("reset_engine", 0200, debugfs_root, vdev, &ivpu_reset_engine_fops); + + if (ivpu_hw_gen(vdev) >= IVPU_HW_40XX) + debugfs_create_file("fw_profiling_freq_drive", 0200, + debugfs_root, vdev, &fw_profiling_freq_fops); } diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 790603017653..64927682161b 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -31,8 +31,6 @@ __stringify(DRM_IVPU_DRIVER_MINOR) "." #endif -static const struct drm_driver driver; - static struct lock_class_key submitted_jobs_xa_lock_class_key; int ivpu_dbg_mask; @@ -41,7 +39,7 @@ MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros."); int ivpu_test_mode; module_param_named_unsafe(test_mode, ivpu_test_mode, int, 0644); -MODULE_PARM_DESC(test_mode, "Test mode: 0 - normal operation, 1 - fw unit test, 2 - null hw"); +MODULE_PARM_DESC(test_mode, "Test mode mask. See IVPU_TEST_MODE_* macros."); u8 ivpu_pll_min_ratio; module_param_named(pll_min_ratio, ivpu_pll_min_ratio, byte, 0644); @@ -93,8 +91,8 @@ static void file_priv_release(struct kref *ref) ivpu_dbg(vdev, FILE, "file_priv release: ctx %u\n", file_priv->ctx.id); ivpu_cmdq_release_all(file_priv); - ivpu_bo_remove_all_bos_from_context(&file_priv->ctx); ivpu_jsm_context_release(vdev, file_priv->ctx.id); + ivpu_bo_remove_all_bos_from_context(vdev, &file_priv->ctx); ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); drm_WARN_ON(&vdev->drm, xa_erase_irq(&vdev->context_xa, file_priv->ctx.id) != file_priv); mutex_destroy(&file_priv->lock); @@ -317,16 +315,14 @@ static int ivpu_wait_for_ready(struct ivpu_device *vdev) unsigned long timeout; int ret; - if (ivpu_test_mode == IVPU_TEST_MODE_FW_TEST) + if (ivpu_test_mode & IVPU_TEST_MODE_FW_TEST) return 0; - ivpu_ipc_consumer_add(vdev, &cons, IVPU_IPC_CHAN_BOOT_MSG); + ivpu_ipc_consumer_add(vdev, &cons, IVPU_IPC_CHAN_BOOT_MSG, NULL); timeout = jiffies + msecs_to_jiffies(vdev->timeout.boot); while (1) { - ret = ivpu_ipc_irq_handler(vdev); - if (ret) - break; + ivpu_ipc_irq_handler(vdev, NULL); ret = ivpu_ipc_receive(vdev, &cons, &ipc_hdr, NULL, 0); if (ret != -ETIMEDOUT || time_after_eq(jiffies, timeout)) break; @@ -362,7 +358,7 @@ int ivpu_boot(struct ivpu_device *vdev) int ret; /* Update boot params located at first 4KB of FW memory */ - ivpu_fw_boot_params_setup(vdev, vdev->fw->mem->kvaddr); + ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem)); ret = ivpu_hw_boot_fw(vdev); if (ret) { @@ -414,7 +410,9 @@ static const struct drm_driver driver = { .open = ivpu_open, .postclose = ivpu_postclose, - .gem_prime_import = ivpu_gem_prime_import, + + .gem_create_object = ivpu_gem_create_object, + .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, .ioctls = ivpu_drm_ioctls, .num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls), @@ -427,6 +425,13 @@ static const struct drm_driver driver = { .minor = DRM_IVPU_DRIVER_MINOR, }; +static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg) +{ + struct ivpu_device *vdev = arg; + + return ivpu_ipc_irq_thread_handler(vdev); +} + static int ivpu_irq_init(struct ivpu_device *vdev) { struct pci_dev *pdev = to_pci_dev(vdev->drm.dev); @@ -440,8 +445,8 @@ static int ivpu_irq_init(struct ivpu_device *vdev) vdev->irq = pci_irq_vector(pdev, 0); - ret = devm_request_irq(vdev->drm.dev, vdev->irq, vdev->hw->ops->irq_handler, - IRQF_NO_AUTOEN, DRIVER_NAME, vdev); + ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, vdev->hw->ops->irq_handler, + ivpu_irq_thread_handler, IRQF_NO_AUTOEN, DRIVER_NAME, vdev); if (ret) ivpu_err(vdev, "Failed to request an IRQ %d\n", ret); @@ -533,6 +538,11 @@ static int ivpu_dev_init(struct ivpu_device *vdev) xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC); xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1); lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key); + INIT_LIST_HEAD(&vdev->bo_list); + + ret = drmm_mutex_init(&vdev->drm, &vdev->bo_list_lock); + if (ret) + goto err_xa_destroy; ret = ivpu_pci_init(vdev); if (ret) @@ -550,7 +560,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev) /* Power up early so the rest of init code can access VPU registers */ ret = ivpu_hw_power_up(vdev); if (ret) - goto err_xa_destroy; + goto err_power_down; ret = ivpu_mmu_global_context_init(vdev); if (ret) @@ -574,20 +584,15 @@ static int ivpu_dev_init(struct ivpu_device *vdev) ivpu_pm_init(vdev); - ret = ivpu_job_done_thread_init(vdev); - if (ret) - goto err_ipc_fini; - ret = ivpu_boot(vdev); if (ret) - goto err_job_done_thread_fini; + goto err_ipc_fini; + ivpu_job_done_consumer_init(vdev); ivpu_pm_enable(vdev); return 0; -err_job_done_thread_fini: - ivpu_job_done_thread_fini(vdev); err_ipc_fini: ivpu_ipc_fini(vdev); err_fw_fini: @@ -612,7 +617,7 @@ static void ivpu_dev_fini(struct ivpu_device *vdev) ivpu_shutdown(vdev); if (IVPU_WA(d3hot_after_power_off)) pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot); - ivpu_job_done_thread_fini(vdev); + ivpu_job_done_consumer_fini(vdev); ivpu_pm_cancel_recovery(vdev); ivpu_ipc_fini(vdev); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 417ddeca8517..ebc4b84f27b2 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -17,9 +17,10 @@ #include <uapi/drm/ivpu_accel.h> #include "ivpu_mmu_context.h" +#include "ivpu_ipc.h" #define DRIVER_NAME "intel_vpu" -#define DRIVER_DESC "Driver for Intel Versatile Processing Unit (VPU)" +#define DRIVER_DESC "Driver for Intel NPU (Neural Processing Unit)" #define DRIVER_DATE "20230117" #define PCI_DEVICE_ID_MTL 0x7d1d @@ -88,6 +89,7 @@ struct ivpu_wa_table { bool d3hot_after_power_off; bool interrupt_clear_with_0; bool disable_clock_relinquish; + bool disable_d0i3_msg; }; struct ivpu_hw_info; @@ -115,8 +117,11 @@ struct ivpu_device { struct xarray context_xa; struct xa_limit context_xa_limit; + struct mutex bo_list_lock; /* Protects bo_list */ + struct list_head bo_list; + struct xarray submitted_jobs_xa; - struct task_struct *job_done_thread; + struct ivpu_ipc_consumer job_done_consumer; atomic64_t unique_id_counter; @@ -126,6 +131,7 @@ struct ivpu_device { int tdr; int reschedule_suspend; int autosuspend; + int d0i3_entry_msg; } timeout; }; @@ -148,9 +154,11 @@ extern u8 ivpu_pll_min_ratio; extern u8 ivpu_pll_max_ratio; extern bool ivpu_disable_mmu_cont_pages; -#define IVPU_TEST_MODE_DISABLED 0 -#define IVPU_TEST_MODE_FW_TEST 1 -#define IVPU_TEST_MODE_NULL_HW 2 +#define IVPU_TEST_MODE_FW_TEST BIT(0) +#define IVPU_TEST_MODE_NULL_HW BIT(1) +#define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2) +#define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4) +#define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5) extern int ivpu_test_mode; struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv); diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 691da521dde5..6576232f3e67 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -33,12 +33,17 @@ #define ADDR_TO_L2_CACHE_CFG(addr) ((addr) >> 31) -#define IVPU_FW_CHECK_API(vdev, fw_hdr, name, min_major) \ +/* Check if FW API is compatible with the driver */ +#define IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, name, min_major) \ ivpu_fw_check_api(vdev, fw_hdr, #name, \ VPU_##name##_API_VER_INDEX, \ VPU_##name##_API_VER_MAJOR, \ VPU_##name##_API_VER_MINOR, min_major) +/* Check if API version is lower that the given version */ +#define IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, name, major, minor) \ + ivpu_fw_check_api_ver_lt(vdev, fw_hdr, #name, VPU_##name##_API_VER_INDEX, major, minor) + static char *ivpu_firmware; module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644); MODULE_PARM_DESC(firmware, "VPU firmware binary in /lib/firmware/.."); @@ -105,6 +110,19 @@ ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw return 0; } +static bool +ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr, + const char *str, int index, u16 major, u16 minor) +{ + u16 fw_major = (u16)(fw_hdr->api_version[index] >> 16); + u16 fw_minor = (u16)(fw_hdr->api_version[index]); + + if (fw_major < major || (fw_major == major && fw_minor < minor)) + return true; + + return false; +} + static int ivpu_fw_parse(struct ivpu_device *vdev) { struct ivpu_fw_info *fw = vdev->fw; @@ -164,9 +182,9 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) ivpu_info(vdev, "Firmware: %s, version: %s", fw->name, (const char *)fw_hdr + VPU_FW_HEADER_SIZE); - if (IVPU_FW_CHECK_API(vdev, fw_hdr, BOOT, 3)) + if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, BOOT, 3)) return -EINVAL; - if (IVPU_FW_CHECK_API(vdev, fw_hdr, JSM, 3)) + if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3)) return -EINVAL; fw->runtime_addr = runtime_addr; @@ -182,6 +200,8 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) fw->trace_destination_mask = VPU_TRACE_DESTINATION_VERBOSE_TRACING; fw->trace_hw_component_mask = -1; + fw->dvfs_mode = 0; + ivpu_dbg(vdev, FW_BOOT, "Size: file %lu image %u runtime %u shavenn %u\n", fw->file->size, fw->image_size, fw->runtime_size, fw->shave_nn_size); ivpu_dbg(vdev, FW_BOOT, "Address: runtime 0x%llx, load 0x%llx, entry point 0x%llx\n", @@ -195,6 +215,24 @@ static void ivpu_fw_release(struct ivpu_device *vdev) release_firmware(vdev->fw->file); } +/* Initialize workarounds that depend on FW version */ +static void +ivpu_fw_init_wa(struct ivpu_device *vdev) +{ + const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data; + + if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) || + (ivpu_hw_gen(vdev) > IVPU_HW_37XX) || + (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE)) + vdev->wa.disable_d0i3_msg = true; + + /* Force enable the feature for testing purposes */ + if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE) + vdev->wa.disable_d0i3_msg = false; + + IVPU_PRINT_WA(disable_d0i3_msg); +} + static int ivpu_fw_update_global_range(struct ivpu_device *vdev) { struct ivpu_fw_info *fw = vdev->fw; @@ -248,7 +286,7 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev) if (fw->shave_nn_size) { fw->mem_shave_nn = ivpu_bo_alloc_internal(vdev, vdev->hw->ranges.shave.start, - fw->shave_nn_size, DRM_IVPU_BO_UNCACHED); + fw->shave_nn_size, DRM_IVPU_BO_WC); if (!fw->mem_shave_nn) { ivpu_err(vdev, "Failed to allocate shavenn buffer\n"); ret = -ENOMEM; @@ -297,6 +335,8 @@ int ivpu_fw_init(struct ivpu_device *vdev) if (ret) goto err_fw_release; + ivpu_fw_init_wa(vdev); + ret = ivpu_fw_mem_init(vdev); if (ret) goto err_fw_release; @@ -422,14 +462,31 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_ boot_params->punit_telemetry_sram_size); ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_telemetry_enable = 0x%x\n", boot_params->vpu_telemetry_enable); + ivpu_dbg(vdev, FW_BOOT, "boot_params.dvfs_mode = %u\n", + boot_params->dvfs_mode); + ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_delayed_entry = %d\n", + boot_params->d0i3_delayed_entry); + ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n", + boot_params->d0i3_residency_time_us); + ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n", + boot_params->d0i3_entry_vpu_ts); } void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params) { struct ivpu_bo *ipc_mem_rx = vdev->ipc->mem_rx; - /* In case of warm boot we only have to reset the entrypoint addr */ + /* In case of warm boot only update variable params */ if (!ivpu_fw_is_cold_boot(vdev)) { + boot_params->d0i3_residency_time_us = + ktime_us_delta(ktime_get_boottime(), vdev->hw->d0i3_entry_host_ts); + boot_params->d0i3_entry_vpu_ts = vdev->hw->d0i3_entry_vpu_ts; + + ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_residency_time_us = %lld\n", + boot_params->d0i3_residency_time_us); + ivpu_dbg(vdev, FW_BOOT, "boot_params.d0i3_entry_vpu_ts = %llu\n", + boot_params->d0i3_entry_vpu_ts); + boot_params->save_restore_ret_address = 0; vdev->pm->is_warmboot = true; wmb(); /* Flush WC buffers after writing save_restore_ret_address */ @@ -443,6 +500,13 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params boot_params->frequency = ivpu_hw_reg_pll_freq_get(vdev); /* + * This param is a debug firmware feature. It switches default clock + * to higher resolution one for fine-grained and more accurate firmware + * task profiling. + */ + boot_params->perf_clk_frequency = ivpu_hw_profiling_freq_get(vdev); + + /* * Uncached region of VPU address space, covers IPC buffers, job queues * and log buffers, programmable to L2$ Uncached by VPU MTRR */ @@ -493,6 +557,11 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params boot_params->punit_telemetry_sram_base = ivpu_hw_reg_telemetry_offset_get(vdev); boot_params->punit_telemetry_sram_size = ivpu_hw_reg_telemetry_size_get(vdev); boot_params->vpu_telemetry_enable = ivpu_hw_reg_telemetry_enable_get(vdev); + boot_params->dvfs_mode = vdev->fw->dvfs_mode; + if (!IVPU_WA(disable_d0i3_msg)) + boot_params->d0i3_delayed_entry = 1; + boot_params->d0i3_residency_time_us = 0; + boot_params->d0i3_entry_vpu_ts = 0; wmb(); /* Flush WC buffers after writing bootparams */ diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h index 10ae2847f0ef..66b60fa161b5 100644 --- a/drivers/accel/ivpu/ivpu_fw.h +++ b/drivers/accel/ivpu/ivpu_fw.h @@ -27,6 +27,7 @@ struct ivpu_fw_info { u32 trace_level; u32 trace_destination_mask; u64 trace_hw_component_mask; + u32 dvfs_mode; }; int ivpu_fw_init(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c index c91852f2edc8..1dda4f38ea25 100644 --- a/drivers/accel/ivpu/ivpu_gem.c +++ b/drivers/accel/ivpu/ivpu_gem.c @@ -20,215 +20,18 @@ #include "ivpu_mmu.h" #include "ivpu_mmu_context.h" -MODULE_IMPORT_NS(DMA_BUF); - static const struct drm_gem_object_funcs ivpu_gem_funcs; -static struct lock_class_key prime_bo_lock_class_key; - -static int __must_check prime_alloc_pages_locked(struct ivpu_bo *bo) -{ - /* Pages are managed by the underlying dma-buf */ - return 0; -} - -static void prime_free_pages_locked(struct ivpu_bo *bo) -{ - /* Pages are managed by the underlying dma-buf */ -} - -static int prime_map_pages_locked(struct ivpu_bo *bo) -{ - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - struct sg_table *sgt; - - sgt = dma_buf_map_attachment_unlocked(bo->base.import_attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sgt)) { - ivpu_err(vdev, "Failed to map attachment: %ld\n", PTR_ERR(sgt)); - return PTR_ERR(sgt); - } - - bo->sgt = sgt; - return 0; -} - -static void prime_unmap_pages_locked(struct ivpu_bo *bo) -{ - dma_buf_unmap_attachment_unlocked(bo->base.import_attach, bo->sgt, DMA_BIDIRECTIONAL); - bo->sgt = NULL; -} - -static const struct ivpu_bo_ops prime_ops = { - .type = IVPU_BO_TYPE_PRIME, - .name = "prime", - .alloc_pages = prime_alloc_pages_locked, - .free_pages = prime_free_pages_locked, - .map_pages = prime_map_pages_locked, - .unmap_pages = prime_unmap_pages_locked, -}; - -static int __must_check shmem_alloc_pages_locked(struct ivpu_bo *bo) -{ - int npages = ivpu_bo_size(bo) >> PAGE_SHIFT; - struct page **pages; - - pages = drm_gem_get_pages(&bo->base); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - if (bo->flags & DRM_IVPU_BO_WC) - set_pages_array_wc(pages, npages); - else if (bo->flags & DRM_IVPU_BO_UNCACHED) - set_pages_array_uc(pages, npages); - - bo->pages = pages; - return 0; -} - -static void shmem_free_pages_locked(struct ivpu_bo *bo) -{ - if (ivpu_bo_cache_mode(bo) != DRM_IVPU_BO_CACHED) - set_pages_array_wb(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT); - - drm_gem_put_pages(&bo->base, bo->pages, true, false); - bo->pages = NULL; -} - -static int ivpu_bo_map_pages_locked(struct ivpu_bo *bo) -{ - int npages = ivpu_bo_size(bo) >> PAGE_SHIFT; - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - struct sg_table *sgt; - int ret; - - sgt = drm_prime_pages_to_sg(&vdev->drm, bo->pages, npages); - if (IS_ERR(sgt)) { - ivpu_err(vdev, "Failed to allocate sgtable\n"); - return PTR_ERR(sgt); - } - - ret = dma_map_sgtable(vdev->drm.dev, sgt, DMA_BIDIRECTIONAL, 0); - if (ret) { - ivpu_err(vdev, "Failed to map BO in IOMMU: %d\n", ret); - goto err_free_sgt; - } - - bo->sgt = sgt; - return 0; - -err_free_sgt: - kfree(sgt); - return ret; -} - -static void ivpu_bo_unmap_pages_locked(struct ivpu_bo *bo) -{ - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - - dma_unmap_sgtable(vdev->drm.dev, bo->sgt, DMA_BIDIRECTIONAL, 0); - sg_free_table(bo->sgt); - kfree(bo->sgt); - bo->sgt = NULL; -} - -static const struct ivpu_bo_ops shmem_ops = { - .type = IVPU_BO_TYPE_SHMEM, - .name = "shmem", - .alloc_pages = shmem_alloc_pages_locked, - .free_pages = shmem_free_pages_locked, - .map_pages = ivpu_bo_map_pages_locked, - .unmap_pages = ivpu_bo_unmap_pages_locked, -}; - -static int __must_check internal_alloc_pages_locked(struct ivpu_bo *bo) -{ - unsigned int i, npages = ivpu_bo_size(bo) >> PAGE_SHIFT; - struct page **pages; - int ret; - - pages = kvmalloc_array(npages, sizeof(*bo->pages), GFP_KERNEL); - if (!pages) - return -ENOMEM; - - for (i = 0; i < npages; i++) { - pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); - if (!pages[i]) { - ret = -ENOMEM; - goto err_free_pages; - } - cond_resched(); - } - - bo->pages = pages; - return 0; - -err_free_pages: - while (i--) - put_page(pages[i]); - kvfree(pages); - return ret; -} - -static void internal_free_pages_locked(struct ivpu_bo *bo) -{ - unsigned int i, npages = ivpu_bo_size(bo) >> PAGE_SHIFT; - - if (ivpu_bo_cache_mode(bo) != DRM_IVPU_BO_CACHED) - set_pages_array_wb(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT); - - for (i = 0; i < npages; i++) - put_page(bo->pages[i]); - - kvfree(bo->pages); - bo->pages = NULL; -} - -static const struct ivpu_bo_ops internal_ops = { - .type = IVPU_BO_TYPE_INTERNAL, - .name = "internal", - .alloc_pages = internal_alloc_pages_locked, - .free_pages = internal_free_pages_locked, - .map_pages = ivpu_bo_map_pages_locked, - .unmap_pages = ivpu_bo_unmap_pages_locked, -}; - -static int __must_check ivpu_bo_alloc_and_map_pages_locked(struct ivpu_bo *bo) -{ - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - int ret; - - lockdep_assert_held(&bo->lock); - drm_WARN_ON(&vdev->drm, bo->sgt); - - ret = bo->ops->alloc_pages(bo); - if (ret) { - ivpu_err(vdev, "Failed to allocate pages for BO: %d", ret); - return ret; - } - - ret = bo->ops->map_pages(bo); - if (ret) { - ivpu_err(vdev, "Failed to map pages for BO: %d", ret); - goto err_free_pages; - } - return ret; - -err_free_pages: - bo->ops->free_pages(bo); - return ret; -} - -static void ivpu_bo_unmap_and_free_pages(struct ivpu_bo *bo) +static inline void ivpu_dbg_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, const char *action) { - mutex_lock(&bo->lock); - - WARN_ON(!bo->sgt); - bo->ops->unmap_pages(bo); - WARN_ON(bo->sgt); - bo->ops->free_pages(bo); - WARN_ON(bo->pages); - - mutex_unlock(&bo->lock); + if (bo->ctx) + ivpu_dbg(vdev, BO, "%6s: size %zu has_pages %d dma_mapped %d handle %u ctx %d vpu_addr 0x%llx mmu_mapped %d\n", + action, ivpu_bo_size(bo), (bool)bo->base.pages, (bool)bo->base.sgt, + bo->handle, bo->ctx->id, bo->vpu_addr, bo->mmu_mapped); + else + ivpu_dbg(vdev, BO, "%6s: size %zu has_pages %d dma_mapped %d handle %u (not added to context)\n", + action, ivpu_bo_size(bo), (bool)bo->base.pages, (bool)bo->base.sgt, + bo->handle); } /* @@ -245,21 +48,24 @@ int __must_check ivpu_bo_pin(struct ivpu_bo *bo) mutex_lock(&bo->lock); - if (!bo->vpu_addr) { - ivpu_err(vdev, "vpu_addr not set for BO ctx_id: %d handle: %d\n", - bo->ctx->id, bo->handle); + ivpu_dbg_bo(vdev, bo, "pin"); + + if (!bo->ctx) { + ivpu_err(vdev, "vpu_addr not allocated for BO %d\n", bo->handle); ret = -EINVAL; goto unlock; } - if (!bo->sgt) { - ret = ivpu_bo_alloc_and_map_pages_locked(bo); - if (ret) + if (!bo->mmu_mapped) { + struct sg_table *sgt = drm_gem_shmem_get_pages_sgt(&bo->base); + + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + ivpu_err(vdev, "Failed to map BO in IOMMU: %d\n", ret); goto unlock; - } + } - if (!bo->mmu_mapped) { - ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, bo->sgt, + ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt, ivpu_bo_is_snooped(bo)); if (ret) { ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret); @@ -281,248 +87,213 @@ ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx, struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); int ret; - if (!range) { - if (bo->flags & DRM_IVPU_BO_SHAVE_MEM) - range = &vdev->hw->ranges.shave; - else if (bo->flags & DRM_IVPU_BO_DMA_MEM) - range = &vdev->hw->ranges.dma; - else - range = &vdev->hw->ranges.user; - } + mutex_lock(&bo->lock); - mutex_lock(&ctx->lock); - ret = ivpu_mmu_context_insert_node_locked(ctx, range, ivpu_bo_size(bo), &bo->mm_node); + ret = ivpu_mmu_context_insert_node(ctx, range, ivpu_bo_size(bo), &bo->mm_node); if (!ret) { bo->ctx = ctx; bo->vpu_addr = bo->mm_node.start; - list_add_tail(&bo->ctx_node, &ctx->bo_list); + } else { + ivpu_err(vdev, "Failed to add BO to context %u: %d\n", ctx->id, ret); } - mutex_unlock(&ctx->lock); + + ivpu_dbg_bo(vdev, bo, "alloc"); + + mutex_unlock(&bo->lock); return ret; } -static void ivpu_bo_free_vpu_addr(struct ivpu_bo *bo) +static void ivpu_bo_unbind_locked(struct ivpu_bo *bo) { struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - struct ivpu_mmu_context *ctx = bo->ctx; - ivpu_dbg(vdev, BO, "remove from ctx: ctx %d vpu_addr 0x%llx allocated %d mmu_mapped %d\n", - ctx->id, bo->vpu_addr, (bool)bo->sgt, bo->mmu_mapped); + lockdep_assert_held(&bo->lock); - mutex_lock(&bo->lock); + ivpu_dbg_bo(vdev, bo, "unbind"); + + /* TODO: dma_unmap */ if (bo->mmu_mapped) { - drm_WARN_ON(&vdev->drm, !bo->sgt); - ivpu_mmu_context_unmap_sgt(vdev, ctx, bo->vpu_addr, bo->sgt); + drm_WARN_ON(&vdev->drm, !bo->ctx); + drm_WARN_ON(&vdev->drm, !bo->vpu_addr); + drm_WARN_ON(&vdev->drm, !bo->base.sgt); + ivpu_mmu_context_unmap_sgt(vdev, bo->ctx, bo->vpu_addr, bo->base.sgt); bo->mmu_mapped = false; } - mutex_lock(&ctx->lock); - list_del(&bo->ctx_node); - bo->vpu_addr = 0; - bo->ctx = NULL; - ivpu_mmu_context_remove_node_locked(ctx, &bo->mm_node); - mutex_unlock(&ctx->lock); + if (bo->ctx) { + ivpu_mmu_context_remove_node(bo->ctx, &bo->mm_node); + bo->vpu_addr = 0; + bo->ctx = NULL; + } +} +static void ivpu_bo_unbind(struct ivpu_bo *bo) +{ + mutex_lock(&bo->lock); + ivpu_bo_unbind_locked(bo); mutex_unlock(&bo->lock); } -void ivpu_bo_remove_all_bos_from_context(struct ivpu_mmu_context *ctx) +void ivpu_bo_remove_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) { - struct ivpu_bo *bo, *tmp; + struct ivpu_bo *bo; + + if (drm_WARN_ON(&vdev->drm, !ctx)) + return; - list_for_each_entry_safe(bo, tmp, &ctx->bo_list, ctx_node) - ivpu_bo_free_vpu_addr(bo); + mutex_lock(&vdev->bo_list_lock); + list_for_each_entry(bo, &vdev->bo_list, bo_list_node) { + mutex_lock(&bo->lock); + if (bo->ctx == ctx) + ivpu_bo_unbind_locked(bo); + mutex_unlock(&bo->lock); + } + mutex_unlock(&vdev->bo_list_lock); } -static struct ivpu_bo * -ivpu_bo_alloc(struct ivpu_device *vdev, struct ivpu_mmu_context *mmu_context, - u64 size, u32 flags, const struct ivpu_bo_ops *ops, - const struct ivpu_addr_range *range, u64 user_ptr) +struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size) { struct ivpu_bo *bo; - int ret = 0; - - if (drm_WARN_ON(&vdev->drm, size == 0 || !PAGE_ALIGNED(size))) - return ERR_PTR(-EINVAL); - switch (flags & DRM_IVPU_BO_CACHE_MASK) { - case DRM_IVPU_BO_CACHED: - case DRM_IVPU_BO_UNCACHED: - case DRM_IVPU_BO_WC: - break; - default: + if (size == 0 || !PAGE_ALIGNED(size)) return ERR_PTR(-EINVAL); - } bo = kzalloc(sizeof(*bo), GFP_KERNEL); if (!bo) return ERR_PTR(-ENOMEM); - mutex_init(&bo->lock); - bo->base.funcs = &ivpu_gem_funcs; - bo->flags = flags; - bo->ops = ops; - bo->user_ptr = user_ptr; - - if (ops->type == IVPU_BO_TYPE_SHMEM) - ret = drm_gem_object_init(&vdev->drm, &bo->base, size); - else - drm_gem_private_object_init(&vdev->drm, &bo->base, size); - - if (ret) { - ivpu_err(vdev, "Failed to initialize drm object\n"); - goto err_free; - } - - if (flags & DRM_IVPU_BO_MAPPABLE) { - ret = drm_gem_create_mmap_offset(&bo->base); - if (ret) { - ivpu_err(vdev, "Failed to allocate mmap offset\n"); - goto err_release; - } - } - - if (mmu_context) { - ret = ivpu_bo_alloc_vpu_addr(bo, mmu_context, range); - if (ret) { - ivpu_err(vdev, "Failed to add BO to context: %d\n", ret); - goto err_release; - } - } + bo->base.base.funcs = &ivpu_gem_funcs; + bo->base.pages_mark_dirty_on_put = true; /* VPU can dirty a BO anytime */ - return bo; + INIT_LIST_HEAD(&bo->bo_list_node); + mutex_init(&bo->lock); -err_release: - drm_gem_object_release(&bo->base); -err_free: - kfree(bo); - return ERR_PTR(ret); + return &bo->base.base; } -static void ivpu_bo_free(struct drm_gem_object *obj) +static struct ivpu_bo * +ivpu_bo_create(struct ivpu_device *vdev, u64 size, u32 flags) { - struct ivpu_bo *bo = to_ivpu_bo(obj); - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - - if (bo->ctx) - ivpu_dbg(vdev, BO, "free: ctx %d vpu_addr 0x%llx allocated %d mmu_mapped %d\n", - bo->ctx->id, bo->vpu_addr, (bool)bo->sgt, bo->mmu_mapped); - else - ivpu_dbg(vdev, BO, "free: ctx (released) allocated %d mmu_mapped %d\n", - (bool)bo->sgt, bo->mmu_mapped); - - drm_WARN_ON(&vdev->drm, !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ)); + struct drm_gem_shmem_object *shmem; + struct ivpu_bo *bo; - vunmap(bo->kvaddr); + switch (flags & DRM_IVPU_BO_CACHE_MASK) { + case DRM_IVPU_BO_CACHED: + case DRM_IVPU_BO_WC: + break; + default: + return ERR_PTR(-EINVAL); + } - if (bo->ctx) - ivpu_bo_free_vpu_addr(bo); + shmem = drm_gem_shmem_create(&vdev->drm, size); + if (IS_ERR(shmem)) + return ERR_CAST(shmem); - if (bo->sgt) - ivpu_bo_unmap_and_free_pages(bo); + bo = to_ivpu_bo(&shmem->base); + bo->base.map_wc = flags & DRM_IVPU_BO_WC; + bo->flags = flags; - if (bo->base.import_attach) - drm_prime_gem_destroy(&bo->base, bo->sgt); + mutex_lock(&vdev->bo_list_lock); + list_add_tail(&bo->bo_list_node, &vdev->bo_list); + mutex_unlock(&vdev->bo_list_lock); - drm_gem_object_release(&bo->base); + ivpu_dbg(vdev, BO, "create: vpu_addr 0x%llx size %zu flags 0x%x\n", + bo->vpu_addr, bo->base.base.size, flags); - mutex_destroy(&bo->lock); - kfree(bo); + return bo; } -static int ivpu_bo_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +static int ivpu_bo_open(struct drm_gem_object *obj, struct drm_file *file) { + struct ivpu_file_priv *file_priv = file->driver_priv; + struct ivpu_device *vdev = file_priv->vdev; struct ivpu_bo *bo = to_ivpu_bo(obj); - struct ivpu_device *vdev = ivpu_bo_to_vdev(bo); - - ivpu_dbg(vdev, BO, "mmap: ctx %u handle %u vpu_addr 0x%llx size %zu type %s", - bo->ctx->id, bo->handle, bo->vpu_addr, ivpu_bo_size(bo), bo->ops->name); + struct ivpu_addr_range *range; - if (obj->import_attach) { - /* Drop the reference drm_gem_mmap_obj() acquired.*/ - drm_gem_object_put(obj); - vma->vm_private_data = NULL; - return dma_buf_mmap(obj->dma_buf, vma, 0); - } - - vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND); - vma->vm_page_prot = ivpu_bo_pgprot(bo, vm_get_page_prot(vma->vm_flags)); + if (bo->flags & DRM_IVPU_BO_SHAVE_MEM) + range = &vdev->hw->ranges.shave; + else if (bo->flags & DRM_IVPU_BO_DMA_MEM) + range = &vdev->hw->ranges.dma; + else + range = &vdev->hw->ranges.user; - return 0; + return ivpu_bo_alloc_vpu_addr(bo, &file_priv->ctx, range); } -static struct sg_table *ivpu_bo_get_sg_table(struct drm_gem_object *obj) +static void ivpu_bo_free(struct drm_gem_object *obj) { + struct ivpu_device *vdev = to_ivpu_device(obj->dev); struct ivpu_bo *bo = to_ivpu_bo(obj); - loff_t npages = obj->size >> PAGE_SHIFT; - int ret = 0; - mutex_lock(&bo->lock); + mutex_lock(&vdev->bo_list_lock); + list_del(&bo->bo_list_node); + mutex_unlock(&vdev->bo_list_lock); - if (!bo->sgt) - ret = ivpu_bo_alloc_and_map_pages_locked(bo); + drm_WARN_ON(&vdev->drm, !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ)); - mutex_unlock(&bo->lock); + ivpu_dbg_bo(vdev, bo, "free"); - if (ret) - return ERR_PTR(ret); + ivpu_bo_unbind(bo); + mutex_destroy(&bo->lock); - return drm_prime_pages_to_sg(obj->dev, bo->pages, npages); + drm_WARN_ON(obj->dev, bo->base.pages_use_count > 1); + drm_gem_shmem_free(&bo->base); } -static vm_fault_t ivpu_vm_fault(struct vm_fault *vmf) -{ - struct vm_area_struct *vma = vmf->vma; - struct drm_gem_object *obj = vma->vm_private_data; - struct ivpu_bo *bo = to_ivpu_bo(obj); - loff_t npages = obj->size >> PAGE_SHIFT; - pgoff_t page_offset; - struct page *page; - vm_fault_t ret; - int err; - - mutex_lock(&bo->lock); - - if (!bo->sgt) { - err = ivpu_bo_alloc_and_map_pages_locked(bo); - if (err) { - ret = vmf_error(err); - goto unlock; - } - } - - /* We don't use vmf->pgoff since that has the fake offset */ - page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; - if (page_offset >= npages) { - ret = VM_FAULT_SIGBUS; - } else { - page = bo->pages[page_offset]; - ret = vmf_insert_pfn(vma, vmf->address, page_to_pfn(page)); - } - -unlock: - mutex_unlock(&bo->lock); +static const struct dma_buf_ops ivpu_bo_dmabuf_ops = { + .cache_sgt_mapping = true, + .attach = drm_gem_map_attach, + .detach = drm_gem_map_detach, + .map_dma_buf = drm_gem_map_dma_buf, + .unmap_dma_buf = drm_gem_unmap_dma_buf, + .release = drm_gem_dmabuf_release, + .mmap = drm_gem_dmabuf_mmap, + .vmap = drm_gem_dmabuf_vmap, + .vunmap = drm_gem_dmabuf_vunmap, +}; - return ret; +static struct dma_buf *ivpu_bo_export(struct drm_gem_object *obj, int flags) +{ + struct drm_device *dev = obj->dev; + struct dma_buf_export_info exp_info = { + .exp_name = KBUILD_MODNAME, + .owner = dev->driver->fops->owner, + .ops = &ivpu_bo_dmabuf_ops, + .size = obj->size, + .flags = flags, + .priv = obj, + .resv = obj->resv, + }; + void *sgt; + + /* + * Make sure that pages are allocated and dma-mapped before exporting the bo. + * DMA-mapping is required if the bo will be imported to the same device. + */ + sgt = drm_gem_shmem_get_pages_sgt(to_drm_gem_shmem_obj(obj)); + if (IS_ERR(sgt)) + return sgt; + + return drm_gem_dmabuf_export(dev, &exp_info); } -static const struct vm_operations_struct ivpu_vm_ops = { - .fault = ivpu_vm_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - static const struct drm_gem_object_funcs ivpu_gem_funcs = { .free = ivpu_bo_free, - .mmap = ivpu_bo_mmap, - .vm_ops = &ivpu_vm_ops, - .get_sg_table = ivpu_bo_get_sg_table, + .open = ivpu_bo_open, + .export = ivpu_bo_export, + .print_info = drm_gem_shmem_object_print_info, + .pin = drm_gem_shmem_object_pin, + .unpin = drm_gem_shmem_object_unpin, + .get_sg_table = drm_gem_shmem_object_get_sg_table, + .vmap = drm_gem_shmem_object_vmap, + .vunmap = drm_gem_shmem_object_vunmap, + .mmap = drm_gem_shmem_object_mmap, + .vm_ops = &drm_gem_shmem_vm_ops, }; -int -ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) +int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct ivpu_file_priv *file_priv = file->driver_priv; struct ivpu_device *vdev = file_priv->vdev; @@ -537,23 +308,20 @@ ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) if (size == 0) return -EINVAL; - bo = ivpu_bo_alloc(vdev, &file_priv->ctx, size, args->flags, &shmem_ops, NULL, 0); + bo = ivpu_bo_create(vdev, size, args->flags); if (IS_ERR(bo)) { ivpu_err(vdev, "Failed to create BO: %pe (ctx %u size %llu flags 0x%x)", bo, file_priv->ctx.id, args->size, args->flags); return PTR_ERR(bo); } - ret = drm_gem_handle_create(file, &bo->base, &bo->handle); + ret = drm_gem_handle_create(file, &bo->base.base, &bo->handle); if (!ret) { args->vpu_addr = bo->vpu_addr; args->handle = bo->handle; } - drm_gem_object_put(&bo->base); - - ivpu_dbg(vdev, BO, "alloc shmem: ctx %u vpu_addr 0x%llx size %zu flags 0x%x\n", - file_priv->ctx.id, bo->vpu_addr, ivpu_bo_size(bo), bo->flags); + drm_gem_object_put(&bo->base.base); return ret; } @@ -563,8 +331,8 @@ ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 fla { const struct ivpu_addr_range *range; struct ivpu_addr_range fixed_range; + struct iosys_map map; struct ivpu_bo *bo; - pgprot_t prot; int ret; drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(vpu_addr)); @@ -578,81 +346,42 @@ ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 fla range = &vdev->hw->ranges.global; } - bo = ivpu_bo_alloc(vdev, &vdev->gctx, size, flags, &internal_ops, range, 0); + bo = ivpu_bo_create(vdev, size, flags); if (IS_ERR(bo)) { ivpu_err(vdev, "Failed to create BO: %pe (vpu_addr 0x%llx size %llu flags 0x%x)", bo, vpu_addr, size, flags); return NULL; } - ret = ivpu_bo_pin(bo); + ret = ivpu_bo_alloc_vpu_addr(bo, &vdev->gctx, range); if (ret) goto err_put; - if (ivpu_bo_cache_mode(bo) != DRM_IVPU_BO_CACHED) - drm_clflush_pages(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT); - - if (bo->flags & DRM_IVPU_BO_WC) - set_pages_array_wc(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT); - else if (bo->flags & DRM_IVPU_BO_UNCACHED) - set_pages_array_uc(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT); - - prot = ivpu_bo_pgprot(bo, PAGE_KERNEL); - bo->kvaddr = vmap(bo->pages, ivpu_bo_size(bo) >> PAGE_SHIFT, VM_MAP, prot); - if (!bo->kvaddr) { - ivpu_err(vdev, "Failed to map BO into kernel virtual memory\n"); + ret = ivpu_bo_pin(bo); + if (ret) goto err_put; - } - ivpu_dbg(vdev, BO, "alloc internal: ctx 0 vpu_addr 0x%llx size %zu flags 0x%x\n", - bo->vpu_addr, ivpu_bo_size(bo), flags); + ret = drm_gem_shmem_vmap(&bo->base, &map); + if (ret) + goto err_put; return bo; err_put: - drm_gem_object_put(&bo->base); + drm_gem_object_put(&bo->base.base); return NULL; } void ivpu_bo_free_internal(struct ivpu_bo *bo) { - drm_gem_object_put(&bo->base); -} - -struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *buf) -{ - struct ivpu_device *vdev = to_ivpu_device(dev); - struct dma_buf_attachment *attach; - struct ivpu_bo *bo; - - attach = dma_buf_attach(buf, dev->dev); - if (IS_ERR(attach)) - return ERR_CAST(attach); - - get_dma_buf(buf); + struct iosys_map map = IOSYS_MAP_INIT_VADDR(bo->base.vaddr); - bo = ivpu_bo_alloc(vdev, NULL, buf->size, DRM_IVPU_BO_MAPPABLE, &prime_ops, NULL, 0); - if (IS_ERR(bo)) { - ivpu_err(vdev, "Failed to import BO: %pe (size %lu)", bo, buf->size); - goto err_detach; - } - - lockdep_set_class(&bo->lock, &prime_bo_lock_class_key); - - bo->base.import_attach = attach; - - return &bo->base; - -err_detach: - dma_buf_detach(buf, attach); - dma_buf_put(buf); - return ERR_CAST(bo); + drm_gem_shmem_vunmap(&bo->base, &map); + drm_gem_object_put(&bo->base.base); } int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { - struct ivpu_file_priv *file_priv = file->driver_priv; - struct ivpu_device *vdev = to_ivpu_device(dev); struct drm_ivpu_bo_info *args = data; struct drm_gem_object *obj; struct ivpu_bo *bo; @@ -665,21 +394,12 @@ int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file bo = to_ivpu_bo(obj); mutex_lock(&bo->lock); - - if (!bo->ctx) { - ret = ivpu_bo_alloc_vpu_addr(bo, &file_priv->ctx, NULL); - if (ret) { - ivpu_err(vdev, "Failed to allocate vpu_addr: %d\n", ret); - goto unlock; - } - } - args->flags = bo->flags; args->mmap_offset = drm_vma_node_offset_addr(&obj->vma_node); args->vpu_addr = bo->vpu_addr; args->size = obj->size; -unlock: mutex_unlock(&bo->lock); + drm_gem_object_put(obj); return ret; } @@ -714,41 +434,41 @@ static void ivpu_bo_print_info(struct ivpu_bo *bo, struct drm_printer *p) { unsigned long dma_refcount = 0; - if (bo->base.dma_buf && bo->base.dma_buf->file) - dma_refcount = atomic_long_read(&bo->base.dma_buf->file->f_count); + mutex_lock(&bo->lock); + + if (bo->base.base.dma_buf && bo->base.base.dma_buf->file) + dma_refcount = atomic_long_read(&bo->base.base.dma_buf->file->f_count); + + drm_printf(p, "%-3u %-6d 0x%-12llx %-10lu 0x%-8x %-4u %-8lu", + bo->ctx->id, bo->handle, bo->vpu_addr, bo->base.base.size, + bo->flags, kref_read(&bo->base.base.refcount), dma_refcount); + + if (bo->base.base.import_attach) + drm_printf(p, " imported"); + + if (bo->base.pages) + drm_printf(p, " has_pages"); - drm_printf(p, "%5u %6d %16llx %10lu %10u %12lu %14s\n", - bo->ctx->id, bo->handle, bo->vpu_addr, ivpu_bo_size(bo), - kref_read(&bo->base.refcount), dma_refcount, bo->ops->name); + if (bo->mmu_mapped) + drm_printf(p, " mmu_mapped"); + + drm_printf(p, "\n"); + + mutex_unlock(&bo->lock); } void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p) { struct ivpu_device *vdev = to_ivpu_device(dev); - struct ivpu_file_priv *file_priv; - unsigned long ctx_id; struct ivpu_bo *bo; - drm_printf(p, "%5s %6s %16s %10s %10s %12s %14s\n", - "ctx", "handle", "vpu_addr", "size", "refcount", "dma_refcount", "type"); + drm_printf(p, "%-3s %-6s %-14s %-10s %-10s %-4s %-8s %s\n", + "ctx", "handle", "vpu_addr", "size", "flags", "refs", "dma_refs", "attribs"); - mutex_lock(&vdev->gctx.lock); - list_for_each_entry(bo, &vdev->gctx.bo_list, ctx_node) + mutex_lock(&vdev->bo_list_lock); + list_for_each_entry(bo, &vdev->bo_list, bo_list_node) ivpu_bo_print_info(bo, p); - mutex_unlock(&vdev->gctx.lock); - - xa_for_each(&vdev->context_xa, ctx_id, file_priv) { - file_priv = ivpu_file_priv_get_by_ctx_id(vdev, ctx_id); - if (!file_priv) - continue; - - mutex_lock(&file_priv->ctx.lock); - list_for_each_entry(bo, &file_priv->ctx.bo_list, ctx_node) - ivpu_bo_print_info(bo, p); - mutex_unlock(&file_priv->ctx.lock); - - ivpu_file_priv_put(&file_priv); - } + mutex_unlock(&vdev->bo_list_lock); } void ivpu_bo_list_print(struct drm_device *dev) diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h index a0b4d4a32b3b..d75cad0d3c74 100644 --- a/drivers/accel/ivpu/ivpu_gem.h +++ b/drivers/accel/ivpu/ivpu_gem.h @@ -6,84 +6,52 @@ #define __IVPU_GEM_H__ #include <drm/drm_gem.h> +#include <drm/drm_gem_shmem_helper.h> #include <drm/drm_mm.h> -struct dma_buf; -struct ivpu_bo_ops; struct ivpu_file_priv; struct ivpu_bo { - struct drm_gem_object base; - const struct ivpu_bo_ops *ops; - + struct drm_gem_shmem_object base; struct ivpu_mmu_context *ctx; - struct list_head ctx_node; + struct list_head bo_list_node; struct drm_mm_node mm_node; - struct mutex lock; /* Protects: pages, sgt, mmu_mapped */ - struct sg_table *sgt; - struct page **pages; - bool mmu_mapped; - - void *kvaddr; + struct mutex lock; /* Protects: ctx, mmu_mapped, vpu_addr */ u64 vpu_addr; u32 handle; u32 flags; - uintptr_t user_ptr; - u32 job_status; -}; - -enum ivpu_bo_type { - IVPU_BO_TYPE_SHMEM = 1, - IVPU_BO_TYPE_INTERNAL, - IVPU_BO_TYPE_PRIME, -}; - -struct ivpu_bo_ops { - enum ivpu_bo_type type; - const char *name; - int (*alloc_pages)(struct ivpu_bo *bo); - void (*free_pages)(struct ivpu_bo *bo); - int (*map_pages)(struct ivpu_bo *bo); - void (*unmap_pages)(struct ivpu_bo *bo); + u32 job_status; /* Valid only for command buffer */ + bool mmu_mapped; }; int ivpu_bo_pin(struct ivpu_bo *bo); -void ivpu_bo_remove_all_bos_from_context(struct ivpu_mmu_context *ctx); -void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p); -void ivpu_bo_list_print(struct drm_device *dev); +void ivpu_bo_remove_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); -struct ivpu_bo * -ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags); +struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size); +struct ivpu_bo *ivpu_bo_alloc_internal(struct ivpu_device *vdev, u64 vpu_addr, u64 size, u32 flags); void ivpu_bo_free_internal(struct ivpu_bo *bo); -struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); -void ivpu_bo_unmap_sgt_and_remove_from_context(struct ivpu_bo *bo); int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file); +void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p); +void ivpu_bo_list_print(struct drm_device *dev); + static inline struct ivpu_bo *to_ivpu_bo(struct drm_gem_object *obj) { - return container_of(obj, struct ivpu_bo, base); + return container_of(obj, struct ivpu_bo, base.base); } static inline void *ivpu_bo_vaddr(struct ivpu_bo *bo) { - return bo->kvaddr; + return bo->base.vaddr; } static inline size_t ivpu_bo_size(struct ivpu_bo *bo) { - return bo->base.size; -} - -static inline struct page *ivpu_bo_get_page(struct ivpu_bo *bo, u64 offset) -{ - if (offset > ivpu_bo_size(bo) || !bo->pages) - return NULL; - - return bo->pages[offset / PAGE_SIZE]; + return bo->base.base.size; } static inline u32 ivpu_bo_cache_mode(struct ivpu_bo *bo) @@ -96,20 +64,9 @@ static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo) return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED; } -static inline pgprot_t ivpu_bo_pgprot(struct ivpu_bo *bo, pgprot_t prot) -{ - if (bo->flags & DRM_IVPU_BO_WC) - return pgprot_writecombine(prot); - - if (bo->flags & DRM_IVPU_BO_UNCACHED) - return pgprot_noncached(prot); - - return prot; -} - static inline struct ivpu_device *ivpu_bo_to_vdev(struct ivpu_bo *bo) { - return to_ivpu_device(bo->base.dev); + return to_ivpu_device(bo->base.base.dev); } static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr) diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h index 1079e06255ba..b2909168a0a6 100644 --- a/drivers/accel/ivpu/ivpu_hw.h +++ b/drivers/accel/ivpu/ivpu_hw.h @@ -15,8 +15,11 @@ struct ivpu_hw_ops { int (*power_down)(struct ivpu_device *vdev); int (*reset)(struct ivpu_device *vdev); bool (*is_idle)(struct ivpu_device *vdev); + int (*wait_for_idle)(struct ivpu_device *vdev); void (*wdt_disable)(struct ivpu_device *vdev); void (*diagnose_failure)(struct ivpu_device *vdev); + u32 (*profiling_freq_get)(struct ivpu_device *vdev); + void (*profiling_freq_drive)(struct ivpu_device *vdev, bool enable); u32 (*reg_pll_freq_get)(struct ivpu_device *vdev); u32 (*reg_telemetry_offset_get)(struct ivpu_device *vdev); u32 (*reg_telemetry_size_get)(struct ivpu_device *vdev); @@ -58,6 +61,8 @@ struct ivpu_hw_info { u32 sku; u16 config; int dma_bits; + ktime_t d0i3_entry_host_ts; + u64 d0i3_entry_vpu_ts; }; extern const struct ivpu_hw_ops ivpu_hw_37xx_ops; @@ -85,6 +90,11 @@ static inline bool ivpu_hw_is_idle(struct ivpu_device *vdev) return vdev->hw->ops->is_idle(vdev); }; +static inline int ivpu_hw_wait_for_idle(struct ivpu_device *vdev) +{ + return vdev->hw->ops->wait_for_idle(vdev); +}; + static inline int ivpu_hw_power_down(struct ivpu_device *vdev) { ivpu_dbg(vdev, PM, "HW power down\n"); @@ -104,6 +114,16 @@ static inline void ivpu_hw_wdt_disable(struct ivpu_device *vdev) vdev->hw->ops->wdt_disable(vdev); }; +static inline u32 ivpu_hw_profiling_freq_get(struct ivpu_device *vdev) +{ + return vdev->hw->ops->profiling_freq_get(vdev); +}; + +static inline void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable) +{ + return vdev->hw->ops->profiling_freq_drive(vdev, enable); +}; + /* Register indirect accesses */ static inline u32 ivpu_hw_reg_pll_freq_get(struct ivpu_device *vdev) { diff --git a/drivers/accel/ivpu/ivpu_hw_37xx.c b/drivers/accel/ivpu/ivpu_hw_37xx.c index 5c0246b9e522..e33dfe3089af 100644 --- a/drivers/accel/ivpu/ivpu_hw_37xx.c +++ b/drivers/accel/ivpu/ivpu_hw_37xx.c @@ -29,6 +29,7 @@ #define PLL_REF_CLK_FREQ (50 * 1000000) #define PLL_SIMULATION_FREQ (10 * 1000000) +#define PLL_PROF_CLK_FREQ (38400 * 1000) #define PLL_DEFAULT_EPP_VALUE 0x80 #define TIM_SAFE_ENABLE 0xf1d0dead @@ -37,7 +38,7 @@ #define TIMEOUT_US (150 * USEC_PER_MSEC) #define PWR_ISLAND_STATUS_TIMEOUT_US (5 * USEC_PER_MSEC) #define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC) -#define IDLE_TIMEOUT_US (500 * USEC_PER_MSEC) +#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC) #define ICB_0_IRQ_MASK ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \ (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \ @@ -90,6 +91,7 @@ static void ivpu_hw_timeouts_init(struct ivpu_device *vdev) vdev->timeout.tdr = 2000; vdev->timeout.reschedule_suspend = 10; vdev->timeout.autosuspend = 10; + vdev->timeout.d0i3_entry_msg = 5; } static int ivpu_pll_wait_for_cmd_send(struct ivpu_device *vdev) @@ -502,6 +504,16 @@ static int ivpu_boot_pwr_domain_enable(struct ivpu_device *vdev) return ret; } +static int ivpu_boot_pwr_domain_disable(struct ivpu_device *vdev) +{ + ivpu_boot_dpu_active_drive(vdev, false); + ivpu_boot_pwr_island_isolation_drive(vdev, true); + ivpu_boot_pwr_island_trickle_drive(vdev, false); + ivpu_boot_pwr_island_drive(vdev, false); + + return ivpu_boot_wait_for_pwr_island_status(vdev, 0x0); +} + static void ivpu_boot_no_snoop_enable(struct ivpu_device *vdev) { u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES); @@ -600,25 +612,17 @@ static int ivpu_hw_37xx_info_init(struct ivpu_device *vdev) static int ivpu_hw_37xx_reset(struct ivpu_device *vdev) { - int ret; - u32 val; - - if (IVPU_WA(punit_disabled)) - return 0; + int ret = 0; - ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); - if (ret) { - ivpu_err(vdev, "Timed out waiting for TRIGGER bit\n"); - return ret; + if (ivpu_boot_pwr_domain_disable(vdev)) { + ivpu_err(vdev, "Failed to disable power domain\n"); + ret = -EIO; } - val = REGB_RD32(VPU_37XX_BUTTRESS_VPU_IP_RESET); - val = REG_SET_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, val); - REGB_WR32(VPU_37XX_BUTTRESS_VPU_IP_RESET, val); - - ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); - if (ret) - ivpu_err(vdev, "Timed out waiting for RESET completion\n"); + if (ivpu_pll_disable(vdev)) { + ivpu_err(vdev, "Failed to disable PLL\n"); + ret = -EIO; + } return ret; } @@ -651,10 +655,6 @@ static int ivpu_hw_37xx_power_up(struct ivpu_device *vdev) { int ret; - ret = ivpu_hw_37xx_reset(vdev); - if (ret) - ivpu_warn(vdev, "Failed to reset HW: %d\n", ret); - ret = ivpu_hw_37xx_d0i3_disable(vdev); if (ret) ivpu_warn(vdev, "Failed to disable D0I3: %d\n", ret); @@ -718,15 +718,28 @@ static bool ivpu_hw_37xx_is_idle(struct ivpu_device *vdev) REG_TEST_FLD(VPU_37XX_BUTTRESS_VPU_STATUS, IDLE, val); } +static int ivpu_hw_37xx_wait_for_idle(struct ivpu_device *vdev) +{ + return REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US); +} + +static void ivpu_hw_37xx_save_d0i3_entry_timestamp(struct ivpu_device *vdev) +{ + vdev->hw->d0i3_entry_host_ts = ktime_get_boottime(); + vdev->hw->d0i3_entry_vpu_ts = REGV_RD64(VPU_37XX_CPU_SS_TIM_PERF_FREE_CNT); +} + static int ivpu_hw_37xx_power_down(struct ivpu_device *vdev) { int ret = 0; - if (!ivpu_hw_37xx_is_idle(vdev) && ivpu_hw_37xx_reset(vdev)) - ivpu_err(vdev, "Failed to reset the VPU\n"); + ivpu_hw_37xx_save_d0i3_entry_timestamp(vdev); - if (ivpu_pll_disable(vdev)) { - ivpu_err(vdev, "Failed to disable PLL\n"); + if (!ivpu_hw_37xx_is_idle(vdev)) + ivpu_warn(vdev, "VPU not idle during power down\n"); + + if (ivpu_hw_37xx_reset(vdev)) { + ivpu_err(vdev, "Failed to reset VPU\n"); ret = -EIO; } @@ -756,6 +769,16 @@ static void ivpu_hw_37xx_wdt_disable(struct ivpu_device *vdev) REGV_WR32(VPU_37XX_CPU_SS_TIM_GEN_CONFIG, val); } +static u32 ivpu_hw_37xx_profiling_freq_get(struct ivpu_device *vdev) +{ + return PLL_PROF_CLK_FREQ; +} + +static void ivpu_hw_37xx_profiling_freq_drive(struct ivpu_device *vdev, bool enable) +{ + /* Profiling freq - is a debug feature. Unavailable on VPU 37XX. */ +} + static u32 ivpu_hw_37xx_pll_to_freq(u32 ratio, u32 config) { u32 pll_clock = PLL_REF_CLK_FREQ * ratio; @@ -867,17 +890,20 @@ static void ivpu_hw_37xx_irq_noc_firewall_handler(struct ivpu_device *vdev) } /* Handler for IRQs from VPU core (irqV) */ -static u32 ivpu_hw_37xx_irqv_handler(struct ivpu_device *vdev, int irq) +static bool ivpu_hw_37xx_irqv_handler(struct ivpu_device *vdev, int irq, bool *wake_thread) { u32 status = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK; + if (!status) + return false; + REGV_WR32(VPU_37XX_HOST_SS_ICB_CLEAR_0, status); if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT, status)) ivpu_mmu_irq_evtq_handler(vdev); if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT, status)) - ivpu_ipc_irq_handler(vdev); + ivpu_ipc_irq_handler(vdev, wake_thread); if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT, status)) ivpu_dbg(vdev, IRQ, "MMU sync complete\n"); @@ -894,17 +920,17 @@ static u32 ivpu_hw_37xx_irqv_handler(struct ivpu_device *vdev, int irq) if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, status)) ivpu_hw_37xx_irq_noc_firewall_handler(vdev); - return status; + return true; } /* Handler for IRQs from Buttress core (irqB) */ -static u32 ivpu_hw_37xx_irqb_handler(struct ivpu_device *vdev, int irq) +static bool ivpu_hw_37xx_irqb_handler(struct ivpu_device *vdev, int irq) { u32 status = REGB_RD32(VPU_37XX_BUTTRESS_INTERRUPT_STAT) & BUTTRESS_IRQ_MASK; bool schedule_recovery = false; - if (status == 0) - return 0; + if (!status) + return false; if (REG_TEST_FLD(VPU_37XX_BUTTRESS_INTERRUPT_STAT, FREQ_CHANGE, status)) ivpu_dbg(vdev, IRQ, "FREQ_CHANGE irq: %08x", @@ -940,23 +966,27 @@ static u32 ivpu_hw_37xx_irqb_handler(struct ivpu_device *vdev, int irq) if (schedule_recovery) ivpu_pm_schedule_recovery(vdev); - return status; + return true; } static irqreturn_t ivpu_hw_37xx_irq_handler(int irq, void *ptr) { struct ivpu_device *vdev = ptr; - u32 ret_irqv, ret_irqb; + bool irqv_handled, irqb_handled, wake_thread = false; REGB_WR32(VPU_37XX_BUTTRESS_GLOBAL_INT_MASK, 0x1); - ret_irqv = ivpu_hw_37xx_irqv_handler(vdev, irq); - ret_irqb = ivpu_hw_37xx_irqb_handler(vdev, irq); + irqv_handled = ivpu_hw_37xx_irqv_handler(vdev, irq, &wake_thread); + irqb_handled = ivpu_hw_37xx_irqb_handler(vdev, irq); /* Re-enable global interrupts to re-trigger MSI for pending interrupts */ REGB_WR32(VPU_37XX_BUTTRESS_GLOBAL_INT_MASK, 0x0); - return IRQ_RETVAL(ret_irqb | ret_irqv); + if (wake_thread) + return IRQ_WAKE_THREAD; + if (irqv_handled || irqb_handled) + return IRQ_HANDLED; + return IRQ_NONE; } static void ivpu_hw_37xx_diagnose_failure(struct ivpu_device *vdev) @@ -993,11 +1023,14 @@ const struct ivpu_hw_ops ivpu_hw_37xx_ops = { .info_init = ivpu_hw_37xx_info_init, .power_up = ivpu_hw_37xx_power_up, .is_idle = ivpu_hw_37xx_is_idle, + .wait_for_idle = ivpu_hw_37xx_wait_for_idle, .power_down = ivpu_hw_37xx_power_down, .reset = ivpu_hw_37xx_reset, .boot_fw = ivpu_hw_37xx_boot_fw, .wdt_disable = ivpu_hw_37xx_wdt_disable, .diagnose_failure = ivpu_hw_37xx_diagnose_failure, + .profiling_freq_get = ivpu_hw_37xx_profiling_freq_get, + .profiling_freq_drive = ivpu_hw_37xx_profiling_freq_drive, .reg_pll_freq_get = ivpu_hw_37xx_reg_pll_freq_get, .reg_telemetry_offset_get = ivpu_hw_37xx_reg_telemetry_offset_get, .reg_telemetry_size_get = ivpu_hw_37xx_reg_telemetry_size_get, diff --git a/drivers/accel/ivpu/ivpu_hw_37xx_reg.h b/drivers/accel/ivpu/ivpu_hw_37xx_reg.h index 4083beb5e9db..f6fec1919202 100644 --- a/drivers/accel/ivpu/ivpu_hw_37xx_reg.h +++ b/drivers/accel/ivpu/ivpu_hw_37xx_reg.h @@ -240,6 +240,8 @@ #define VPU_37XX_CPU_SS_TIM_GEN_CONFIG 0x06021008u #define VPU_37XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9) +#define VPU_37XX_CPU_SS_TIM_PERF_FREE_CNT 0x06029000u + #define VPU_37XX_CPU_SS_DOORBELL_0 0x06300000u #define VPU_37XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0) diff --git a/drivers/accel/ivpu/ivpu_hw_40xx.c b/drivers/accel/ivpu/ivpu_hw_40xx.c index e691c49c9841..eba2fdef2ace 100644 --- a/drivers/accel/ivpu/ivpu_hw_40xx.c +++ b/drivers/accel/ivpu/ivpu_hw_40xx.c @@ -39,6 +39,7 @@ #define TIMEOUT_US (150 * USEC_PER_MSEC) #define PWR_ISLAND_STATUS_TIMEOUT_US (5 * USEC_PER_MSEC) #define PLL_TIMEOUT_US (1500 * USEC_PER_MSEC) +#define IDLE_TIMEOUT_US (5 * USEC_PER_MSEC) #define WEIGHTS_DEFAULT 0xf711f711u #define WEIGHTS_ATS_DEFAULT 0x0000f711u @@ -139,18 +140,21 @@ static void ivpu_hw_timeouts_init(struct ivpu_device *vdev) vdev->timeout.tdr = 2000000; vdev->timeout.reschedule_suspend = 1000; vdev->timeout.autosuspend = -1; + vdev->timeout.d0i3_entry_msg = 500; } else if (ivpu_is_simics(vdev)) { vdev->timeout.boot = 50; vdev->timeout.jsm = 500; vdev->timeout.tdr = 10000; vdev->timeout.reschedule_suspend = 10; vdev->timeout.autosuspend = -1; + vdev->timeout.d0i3_entry_msg = 100; } else { vdev->timeout.boot = 1000; vdev->timeout.jsm = 500; vdev->timeout.tdr = 2000; vdev->timeout.reschedule_suspend = 10; vdev->timeout.autosuspend = 10; + vdev->timeout.d0i3_entry_msg = 5; } } @@ -824,12 +828,6 @@ static int ivpu_hw_40xx_power_up(struct ivpu_device *vdev) { int ret; - ret = ivpu_hw_40xx_reset(vdev); - if (ret) { - ivpu_err(vdev, "Failed to reset HW: %d\n", ret); - return ret; - } - ret = ivpu_hw_40xx_d0i3_disable(vdev); if (ret) ivpu_warn(vdev, "Failed to disable D0I3: %d\n", ret); @@ -898,10 +896,23 @@ static bool ivpu_hw_40xx_is_idle(struct ivpu_device *vdev) REG_TEST_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, IDLE, val); } +static int ivpu_hw_40xx_wait_for_idle(struct ivpu_device *vdev) +{ + return REGB_POLL_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, IDLE, 0x1, IDLE_TIMEOUT_US); +} + +static void ivpu_hw_40xx_save_d0i3_entry_timestamp(struct ivpu_device *vdev) +{ + vdev->hw->d0i3_entry_host_ts = ktime_get_boottime(); + vdev->hw->d0i3_entry_vpu_ts = REGV_RD64(VPU_40XX_CPU_SS_TIM_PERF_EXT_FREE_CNT); +} + static int ivpu_hw_40xx_power_down(struct ivpu_device *vdev) { int ret = 0; + ivpu_hw_40xx_save_d0i3_entry_timestamp(vdev); + if (!ivpu_hw_40xx_is_idle(vdev) && ivpu_hw_40xx_reset(vdev)) ivpu_warn(vdev, "Failed to reset the VPU\n"); @@ -933,6 +944,19 @@ static void ivpu_hw_40xx_wdt_disable(struct ivpu_device *vdev) REGV_WR32(VPU_40XX_CPU_SS_TIM_GEN_CONFIG, val); } +static u32 ivpu_hw_40xx_profiling_freq_get(struct ivpu_device *vdev) +{ + return vdev->hw->pll.profiling_freq; +} + +static void ivpu_hw_40xx_profiling_freq_drive(struct ivpu_device *vdev, bool enable) +{ + if (enable) + vdev->hw->pll.profiling_freq = PLL_PROFILING_FREQ_HIGH; + else + vdev->hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT; +} + /* Register indirect accesses */ static u32 ivpu_hw_40xx_reg_pll_freq_get(struct ivpu_device *vdev) { @@ -1023,13 +1047,12 @@ static void ivpu_hw_40xx_irq_noc_firewall_handler(struct ivpu_device *vdev) } /* Handler for IRQs from VPU core (irqV) */ -static irqreturn_t ivpu_hw_40xx_irqv_handler(struct ivpu_device *vdev, int irq) +static bool ivpu_hw_40xx_irqv_handler(struct ivpu_device *vdev, int irq, bool *wake_thread) { u32 status = REGV_RD32(VPU_40XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK; - irqreturn_t ret = IRQ_NONE; if (!status) - return IRQ_NONE; + return false; REGV_WR32(VPU_40XX_HOST_SS_ICB_CLEAR_0, status); @@ -1037,7 +1060,7 @@ static irqreturn_t ivpu_hw_40xx_irqv_handler(struct ivpu_device *vdev, int irq) ivpu_mmu_irq_evtq_handler(vdev); if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT, status)) - ret |= ivpu_ipc_irq_handler(vdev); + ivpu_ipc_irq_handler(vdev, wake_thread); if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT, status)) ivpu_dbg(vdev, IRQ, "MMU sync complete\n"); @@ -1054,17 +1077,17 @@ static irqreturn_t ivpu_hw_40xx_irqv_handler(struct ivpu_device *vdev, int irq) if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, status)) ivpu_hw_40xx_irq_noc_firewall_handler(vdev); - return ret; + return true; } /* Handler for IRQs from Buttress core (irqB) */ -static irqreturn_t ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) +static bool ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) { bool schedule_recovery = false; u32 status = REGB_RD32(VPU_40XX_BUTTRESS_INTERRUPT_STAT) & BUTTRESS_IRQ_MASK; - if (status == 0) - return IRQ_NONE; + if (!status) + return false; if (REG_TEST_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, FREQ_CHANGE, status)) ivpu_dbg(vdev, IRQ, "FREQ_CHANGE"); @@ -1116,26 +1139,27 @@ static irqreturn_t ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) if (schedule_recovery) ivpu_pm_schedule_recovery(vdev); - return IRQ_HANDLED; + return true; } static irqreturn_t ivpu_hw_40xx_irq_handler(int irq, void *ptr) { + bool irqv_handled, irqb_handled, wake_thread = false; struct ivpu_device *vdev = ptr; - irqreturn_t ret = IRQ_NONE; REGB_WR32(VPU_40XX_BUTTRESS_GLOBAL_INT_MASK, 0x1); - ret |= ivpu_hw_40xx_irqv_handler(vdev, irq); - ret |= ivpu_hw_40xx_irqb_handler(vdev, irq); + irqv_handled = ivpu_hw_40xx_irqv_handler(vdev, irq, &wake_thread); + irqb_handled = ivpu_hw_40xx_irqb_handler(vdev, irq); /* Re-enable global interrupts to re-trigger MSI for pending interrupts */ REGB_WR32(VPU_40XX_BUTTRESS_GLOBAL_INT_MASK, 0x0); - if (ret & IRQ_WAKE_THREAD) + if (wake_thread) return IRQ_WAKE_THREAD; - - return ret; + if (irqv_handled || irqb_handled) + return IRQ_HANDLED; + return IRQ_NONE; } static void ivpu_hw_40xx_diagnose_failure(struct ivpu_device *vdev) @@ -1185,11 +1209,14 @@ const struct ivpu_hw_ops ivpu_hw_40xx_ops = { .info_init = ivpu_hw_40xx_info_init, .power_up = ivpu_hw_40xx_power_up, .is_idle = ivpu_hw_40xx_is_idle, + .wait_for_idle = ivpu_hw_40xx_wait_for_idle, .power_down = ivpu_hw_40xx_power_down, .reset = ivpu_hw_40xx_reset, .boot_fw = ivpu_hw_40xx_boot_fw, .wdt_disable = ivpu_hw_40xx_wdt_disable, .diagnose_failure = ivpu_hw_40xx_diagnose_failure, + .profiling_freq_get = ivpu_hw_40xx_profiling_freq_get, + .profiling_freq_drive = ivpu_hw_40xx_profiling_freq_drive, .reg_pll_freq_get = ivpu_hw_40xx_reg_pll_freq_get, .reg_telemetry_offset_get = ivpu_hw_40xx_reg_telemetry_offset_get, .reg_telemetry_size_get = ivpu_hw_40xx_reg_telemetry_size_get, diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index a4ca40b184d4..e86621f16f85 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -5,7 +5,7 @@ #include <linux/genalloc.h> #include <linux/highmem.h> -#include <linux/kthread.h> +#include <linux/pm_runtime.h> #include <linux/wait.h> #include "ivpu_drv.h" @@ -17,19 +17,12 @@ #include "ivpu_pm.h" #define IPC_MAX_RX_MSG 128 -#define IS_KTHREAD() (get_current()->flags & PF_KTHREAD) struct ivpu_ipc_tx_buf { struct ivpu_ipc_hdr ipc; struct vpu_jsm_msg jsm; }; -struct ivpu_ipc_rx_msg { - struct list_head link; - struct ivpu_ipc_hdr *ipc_hdr; - struct vpu_jsm_msg *jsm_msg; -}; - static void ivpu_ipc_msg_dump(struct ivpu_device *vdev, char *c, struct ivpu_ipc_hdr *ipc_hdr, u32 vpu_addr) { @@ -139,8 +132,49 @@ static void ivpu_ipc_tx(struct ivpu_device *vdev, u32 vpu_addr) ivpu_hw_reg_ipc_tx_set(vdev, vpu_addr); } -void -ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, u32 channel) +static void +ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, + struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg) +{ + struct ivpu_ipc_info *ipc = vdev->ipc; + struct ivpu_ipc_rx_msg *rx_msg; + + lockdep_assert_held(&ipc->cons_lock); + lockdep_assert_irqs_disabled(); + + rx_msg = kzalloc(sizeof(*rx_msg), GFP_ATOMIC); + if (!rx_msg) { + ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); + return; + } + + atomic_inc(&ipc->rx_msg_count); + + rx_msg->ipc_hdr = ipc_hdr; + rx_msg->jsm_msg = jsm_msg; + rx_msg->callback = cons->rx_callback; + + if (rx_msg->callback) { + list_add_tail(&rx_msg->link, &ipc->cb_msg_list); + } else { + spin_lock(&cons->rx_lock); + list_add_tail(&rx_msg->link, &cons->rx_msg_list); + spin_unlock(&cons->rx_lock); + wake_up(&cons->rx_msg_wq); + } +} + +static void +ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg) +{ + list_del(&rx_msg->link); + ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); + atomic_dec(&vdev->ipc->rx_msg_count); + kfree(rx_msg); +} + +void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, + u32 channel, ivpu_ipc_rx_callback_t rx_callback) { struct ivpu_ipc_info *ipc = vdev->ipc; @@ -148,13 +182,15 @@ ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, cons->channel = channel; cons->tx_vpu_addr = 0; cons->request_id = 0; - spin_lock_init(&cons->rx_msg_lock); + cons->aborted = false; + cons->rx_callback = rx_callback; + spin_lock_init(&cons->rx_lock); INIT_LIST_HEAD(&cons->rx_msg_list); init_waitqueue_head(&cons->rx_msg_wq); - spin_lock_irq(&ipc->cons_list_lock); + spin_lock_irq(&ipc->cons_lock); list_add_tail(&cons->link, &ipc->cons_list); - spin_unlock_irq(&ipc->cons_list_lock); + spin_unlock_irq(&ipc->cons_lock); } void ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons) @@ -162,18 +198,14 @@ void ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *c struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_rx_msg *rx_msg, *r; - spin_lock_irq(&ipc->cons_list_lock); + spin_lock_irq(&ipc->cons_lock); list_del(&cons->link); - spin_unlock_irq(&ipc->cons_list_lock); - - spin_lock_irq(&cons->rx_msg_lock); - list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link) { - list_del(&rx_msg->link); - ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); - atomic_dec(&ipc->rx_msg_count); - kfree(rx_msg); - } - spin_unlock_irq(&cons->rx_msg_lock); + spin_unlock_irq(&ipc->cons_lock); + + spin_lock_irq(&cons->rx_lock); + list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link) + ivpu_ipc_rx_msg_del(vdev, rx_msg); + spin_unlock_irq(&cons->rx_lock); ivpu_ipc_tx_release(vdev, cons->tx_vpu_addr); } @@ -202,52 +234,61 @@ unlock: return ret; } +static bool ivpu_ipc_rx_need_wakeup(struct ivpu_ipc_consumer *cons) +{ + bool ret; + + spin_lock_irq(&cons->rx_lock); + ret = !list_empty(&cons->rx_msg_list) || cons->aborted; + spin_unlock_irq(&cons->rx_lock); + + return ret; +} + int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, struct ivpu_ipc_hdr *ipc_buf, - struct vpu_jsm_msg *ipc_payload, unsigned long timeout_ms) + struct vpu_jsm_msg *jsm_msg, unsigned long timeout_ms) { - struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_rx_msg *rx_msg; int wait_ret, ret = 0; + if (drm_WARN_ONCE(&vdev->drm, cons->rx_callback, "Consumer works only in async mode\n")) + return -EINVAL; + wait_ret = wait_event_timeout(cons->rx_msg_wq, - (IS_KTHREAD() && kthread_should_stop()) || - !list_empty(&cons->rx_msg_list), + ivpu_ipc_rx_need_wakeup(cons), msecs_to_jiffies(timeout_ms)); - if (IS_KTHREAD() && kthread_should_stop()) - return -EINTR; - if (wait_ret == 0) return -ETIMEDOUT; - spin_lock_irq(&cons->rx_msg_lock); + spin_lock_irq(&cons->rx_lock); + if (cons->aborted) { + spin_unlock_irq(&cons->rx_lock); + return -ECANCELED; + } rx_msg = list_first_entry_or_null(&cons->rx_msg_list, struct ivpu_ipc_rx_msg, link); if (!rx_msg) { - spin_unlock_irq(&cons->rx_msg_lock); + spin_unlock_irq(&cons->rx_lock); return -EAGAIN; } - list_del(&rx_msg->link); - spin_unlock_irq(&cons->rx_msg_lock); if (ipc_buf) memcpy(ipc_buf, rx_msg->ipc_hdr, sizeof(*ipc_buf)); if (rx_msg->jsm_msg) { - u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*ipc_payload)); + u32 size = min_t(int, rx_msg->ipc_hdr->data_size, sizeof(*jsm_msg)); if (rx_msg->jsm_msg->result != VPU_JSM_STATUS_SUCCESS) { ivpu_dbg(vdev, IPC, "IPC resp result error: %d\n", rx_msg->jsm_msg->result); ret = -EBADMSG; } - if (ipc_payload) - memcpy(ipc_payload, rx_msg->jsm_msg, size); + if (jsm_msg) + memcpy(jsm_msg, rx_msg->jsm_msg, size); } - ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); - atomic_dec(&ipc->rx_msg_count); - kfree(rx_msg); - + ivpu_ipc_rx_msg_del(vdev, rx_msg); + spin_unlock_irq(&cons->rx_lock); return ret; } @@ -260,7 +301,7 @@ ivpu_ipc_send_receive_internal(struct ivpu_device *vdev, struct vpu_jsm_msg *req struct ivpu_ipc_consumer cons; int ret; - ivpu_ipc_consumer_add(vdev, &cons, channel); + ivpu_ipc_consumer_add(vdev, &cons, channel, NULL); ret = ivpu_ipc_send(vdev, &cons, req); if (ret) { @@ -285,23 +326,19 @@ consumer_del: return ret; } -int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req, - enum vpu_ipc_msg_type expected_resp_type, - struct vpu_jsm_msg *resp, u32 channel, - unsigned long timeout_ms) +int ivpu_ipc_send_receive_active(struct ivpu_device *vdev, struct vpu_jsm_msg *req, + enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp, + u32 channel, unsigned long timeout_ms) { struct vpu_jsm_msg hb_req = { .type = VPU_JSM_MSG_QUERY_ENGINE_HB }; struct vpu_jsm_msg hb_resp; int ret, hb_ret; - ret = ivpu_rpm_get(vdev); - if (ret < 0) - return ret; + drm_WARN_ON(&vdev->drm, pm_runtime_status_suspended(vdev->drm.dev)); - ret = ivpu_ipc_send_receive_internal(vdev, req, expected_resp_type, resp, - channel, timeout_ms); + ret = ivpu_ipc_send_receive_internal(vdev, req, expected_resp, resp, channel, timeout_ms); if (ret != -ETIMEDOUT) - goto rpm_put; + return ret; hb_ret = ivpu_ipc_send_receive_internal(vdev, &hb_req, VPU_JSM_MSG_QUERY_ENGINE_HB_DONE, &hb_resp, VPU_IPC_CHAN_ASYNC_CMD, @@ -311,7 +348,21 @@ int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req, ivpu_pm_schedule_recovery(vdev); } -rpm_put: + return ret; +} + +int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req, + enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp, + u32 channel, unsigned long timeout_ms) +{ + int ret; + + ret = ivpu_rpm_get(vdev); + if (ret < 0) + return ret; + + ret = ivpu_ipc_send_receive_active(vdev, req, expected_resp, resp, channel, timeout_ms); + ivpu_rpm_put(vdev); return ret; } @@ -329,35 +380,7 @@ ivpu_ipc_match_consumer(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons return false; } -static void -ivpu_ipc_dispatch(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, - struct ivpu_ipc_hdr *ipc_hdr, struct vpu_jsm_msg *jsm_msg) -{ - struct ivpu_ipc_info *ipc = vdev->ipc; - struct ivpu_ipc_rx_msg *rx_msg; - unsigned long flags; - - lockdep_assert_held(&ipc->cons_list_lock); - - rx_msg = kzalloc(sizeof(*rx_msg), GFP_ATOMIC); - if (!rx_msg) { - ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); - return; - } - - atomic_inc(&ipc->rx_msg_count); - - rx_msg->ipc_hdr = ipc_hdr; - rx_msg->jsm_msg = jsm_msg; - - spin_lock_irqsave(&cons->rx_msg_lock, flags); - list_add_tail(&rx_msg->link, &cons->rx_msg_list); - spin_unlock_irqrestore(&cons->rx_msg_lock, flags); - - wake_up(&cons->rx_msg_wq); -} - -int ivpu_ipc_irq_handler(struct ivpu_device *vdev) +void ivpu_ipc_irq_handler(struct ivpu_device *vdev, bool *wake_thread) { struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_consumer *cons; @@ -375,7 +398,7 @@ int ivpu_ipc_irq_handler(struct ivpu_device *vdev) vpu_addr = ivpu_hw_reg_ipc_rx_addr_get(vdev); if (vpu_addr == REG_IO_ERROR) { ivpu_err_ratelimited(vdev, "Failed to read IPC rx addr register\n"); - return -EIO; + return; } ipc_hdr = ivpu_to_cpu_addr(ipc->mem_rx, vpu_addr); @@ -405,15 +428,15 @@ int ivpu_ipc_irq_handler(struct ivpu_device *vdev) } dispatched = false; - spin_lock_irqsave(&ipc->cons_list_lock, flags); + spin_lock_irqsave(&ipc->cons_lock, flags); list_for_each_entry(cons, &ipc->cons_list, link) { if (ivpu_ipc_match_consumer(vdev, cons, ipc_hdr, jsm_msg)) { - ivpu_ipc_dispatch(vdev, cons, ipc_hdr, jsm_msg); + ivpu_ipc_rx_msg_add(vdev, cons, ipc_hdr, jsm_msg); dispatched = true; break; } } - spin_unlock_irqrestore(&ipc->cons_list_lock, flags); + spin_unlock_irqrestore(&ipc->cons_lock, flags); if (!dispatched) { ivpu_dbg(vdev, IPC, "IPC RX msg 0x%x dropped (no consumer)\n", vpu_addr); @@ -421,7 +444,28 @@ int ivpu_ipc_irq_handler(struct ivpu_device *vdev) } } - return 0; + if (wake_thread) + *wake_thread = !list_empty(&ipc->cb_msg_list); +} + +irqreturn_t ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev) +{ + struct ivpu_ipc_info *ipc = vdev->ipc; + struct ivpu_ipc_rx_msg *rx_msg, *r; + struct list_head cb_msg_list; + + INIT_LIST_HEAD(&cb_msg_list); + + spin_lock_irq(&ipc->cons_lock); + list_splice_tail_init(&ipc->cb_msg_list, &cb_msg_list); + spin_unlock_irq(&ipc->cons_lock); + + list_for_each_entry_safe(rx_msg, r, &cb_msg_list, link) { + rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); + ivpu_ipc_rx_msg_del(vdev, rx_msg); + } + + return IRQ_HANDLED; } int ivpu_ipc_init(struct ivpu_device *vdev) @@ -456,10 +500,10 @@ int ivpu_ipc_init(struct ivpu_device *vdev) goto err_free_rx; } + spin_lock_init(&ipc->cons_lock); INIT_LIST_HEAD(&ipc->cons_list); - spin_lock_init(&ipc->cons_list_lock); + INIT_LIST_HEAD(&ipc->cb_msg_list); drmm_mutex_init(&vdev->drm, &ipc->lock); - ivpu_ipc_reset(vdev); return 0; @@ -472,6 +516,13 @@ err_free_tx: void ivpu_ipc_fini(struct ivpu_device *vdev) { + struct ivpu_ipc_info *ipc = vdev->ipc; + + drm_WARN_ON(&vdev->drm, ipc->on); + drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cons_list)); + drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cb_msg_list)); + drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0); + ivpu_ipc_mem_fini(vdev); } @@ -488,16 +539,27 @@ void ivpu_ipc_disable(struct ivpu_device *vdev) { struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_consumer *cons, *c; - unsigned long flags; + struct ivpu_ipc_rx_msg *rx_msg, *r; + + drm_WARN_ON(&vdev->drm, !list_empty(&ipc->cb_msg_list)); mutex_lock(&ipc->lock); ipc->on = false; mutex_unlock(&ipc->lock); - spin_lock_irqsave(&ipc->cons_list_lock, flags); - list_for_each_entry_safe(cons, c, &ipc->cons_list, link) + spin_lock_irq(&ipc->cons_lock); + list_for_each_entry_safe(cons, c, &ipc->cons_list, link) { + spin_lock(&cons->rx_lock); + if (!cons->rx_callback) + cons->aborted = true; + list_for_each_entry_safe(rx_msg, r, &cons->rx_msg_list, link) + ivpu_ipc_rx_msg_del(vdev, rx_msg); + spin_unlock(&cons->rx_lock); wake_up(&cons->rx_msg_wq); - spin_unlock_irqrestore(&ipc->cons_list_lock, flags); + } + spin_unlock_irq(&ipc->cons_lock); + + drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0); } void ivpu_ipc_reset(struct ivpu_device *vdev) @@ -505,6 +567,7 @@ void ivpu_ipc_reset(struct ivpu_device *vdev) struct ivpu_ipc_info *ipc = vdev->ipc; mutex_lock(&ipc->lock); + drm_WARN_ON(&vdev->drm, ipc->on); memset(ivpu_bo_vaddr(ipc->mem_tx), 0, ivpu_bo_size(ipc->mem_tx)); memset(ivpu_bo_vaddr(ipc->mem_rx), 0, ivpu_bo_size(ipc->mem_rx)); diff --git a/drivers/accel/ivpu/ivpu_ipc.h b/drivers/accel/ivpu/ivpu_ipc.h index 68f5b6668e00..40ca3cc4e61f 100644 --- a/drivers/accel/ivpu/ivpu_ipc.h +++ b/drivers/accel/ivpu/ivpu_ipc.h @@ -42,13 +42,26 @@ struct ivpu_ipc_hdr { u8 status; } __packed __aligned(IVPU_IPC_ALIGNMENT); +typedef void (*ivpu_ipc_rx_callback_t)(struct ivpu_device *vdev, + struct ivpu_ipc_hdr *ipc_hdr, + struct vpu_jsm_msg *jsm_msg); + +struct ivpu_ipc_rx_msg { + struct list_head link; + struct ivpu_ipc_hdr *ipc_hdr; + struct vpu_jsm_msg *jsm_msg; + ivpu_ipc_rx_callback_t callback; +}; + struct ivpu_ipc_consumer { struct list_head link; u32 channel; u32 tx_vpu_addr; u32 request_id; + bool aborted; + ivpu_ipc_rx_callback_t rx_callback; - spinlock_t rx_msg_lock; /* Protects rx_msg_list */ + spinlock_t rx_lock; /* Protects rx_msg_list and aborted */ struct list_head rx_msg_list; wait_queue_head_t rx_msg_wq; }; @@ -60,8 +73,9 @@ struct ivpu_ipc_info { atomic_t rx_msg_count; - spinlock_t cons_list_lock; /* Protects cons_list */ + spinlock_t cons_lock; /* Protects cons_list and cb_msg_list */ struct list_head cons_list; + struct list_head cb_msg_list; atomic_t request_id; struct mutex lock; /* Lock on status */ @@ -75,19 +89,22 @@ void ivpu_ipc_enable(struct ivpu_device *vdev); void ivpu_ipc_disable(struct ivpu_device *vdev); void ivpu_ipc_reset(struct ivpu_device *vdev); -int ivpu_ipc_irq_handler(struct ivpu_device *vdev); +void ivpu_ipc_irq_handler(struct ivpu_device *vdev, bool *wake_thread); +irqreturn_t ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev); void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, - u32 channel); + u32 channel, ivpu_ipc_rx_callback_t callback); void ivpu_ipc_consumer_del(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons); int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, - struct ivpu_ipc_hdr *ipc_buf, struct vpu_jsm_msg *ipc_payload, + struct ivpu_ipc_hdr *ipc_buf, struct vpu_jsm_msg *jsm_msg, unsigned long timeout_ms); +int ivpu_ipc_send_receive_active(struct ivpu_device *vdev, struct vpu_jsm_msg *req, + enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp, + u32 channel, unsigned long timeout_ms); int ivpu_ipc_send_receive(struct ivpu_device *vdev, struct vpu_jsm_msg *req, - enum vpu_ipc_msg_type expected_resp_type, - struct vpu_jsm_msg *resp, u32 channel, - unsigned long timeout_ms); + enum vpu_ipc_msg_type expected_resp, struct vpu_jsm_msg *resp, + u32 channel, unsigned long timeout_ms); #endif /* __IVPU_IPC_H__ */ diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 8983e3a4fdf9..7206cf9cdb4a 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -7,7 +7,6 @@ #include <linux/bitfield.h> #include <linux/highmem.h> -#include <linux/kthread.h> #include <linux/pci.h> #include <linux/module.h> #include <uapi/drm/ivpu_accel.h> @@ -24,10 +23,6 @@ #define JOB_ID_CONTEXT_MASK GENMASK(31, 8) #define JOB_MAX_BUFFER_COUNT 65535 -static unsigned int ivpu_tdr_timeout_ms; -module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, uint, 0644); -MODULE_PARM_DESC(tdr_timeout_ms, "Timeout for device hang detection, in milliseconds, 0 - default"); - static void ivpu_cmdq_ring_db(struct ivpu_device *vdev, struct ivpu_cmdq *cmdq) { ivpu_hw_reg_db_set(vdev, cmdq->db_id); @@ -196,6 +191,8 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job) entry->batch_buf_addr = job->cmd_buf_vpu_addr; entry->job_id = job->job_id; entry->flags = 0; + if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION)) + entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK; wmb(); /* Ensure that tail is updated after filling entry */ header->tail = next_entry; wmb(); /* Flush WC buffer for jobq header */ @@ -264,7 +261,7 @@ static void job_release(struct kref *ref) for (i = 0; i < job->bo_count; i++) if (job->bos[i]) - drm_gem_object_put(&job->bos[i]->base); + drm_gem_object_put(&job->bos[i]->base.base); dma_fence_put(job->done_fence); ivpu_file_priv_put(&job->file_priv); @@ -340,23 +337,12 @@ static int ivpu_job_done(struct ivpu_device *vdev, u32 job_id, u32 job_status) ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d engine %d status 0x%x\n", job->job_id, job->file_priv->ctx.id, job->engine_idx, job_status); + ivpu_stop_job_timeout_detection(vdev); + job_put(job); return 0; } -static void ivpu_job_done_message(struct ivpu_device *vdev, void *msg) -{ - struct vpu_ipc_msg_payload_job_done *payload; - struct vpu_jsm_msg *job_ret_msg = msg; - int ret; - - payload = (struct vpu_ipc_msg_payload_job_done *)&job_ret_msg->payload; - - ret = ivpu_job_done(vdev, payload->job_id, payload->job_status); - if (ret) - ivpu_err(vdev, "Failed to finish job %d: %d\n", payload->job_id, ret); -} - void ivpu_jobs_abort_all(struct ivpu_device *vdev) { struct ivpu_job *job; @@ -398,11 +384,13 @@ static int ivpu_direct_job_submission(struct ivpu_job *job) if (ret) goto err_xa_erase; + ivpu_start_job_timeout_detection(vdev); + ivpu_dbg(vdev, JOB, "Job submitted: id %3u addr 0x%llx ctx %2d engine %d next %d\n", job->job_id, job->cmd_buf_vpu_addr, file_priv->ctx.id, job->engine_idx, cmdq->jobq->header.tail); - if (ivpu_test_mode == IVPU_TEST_MODE_NULL_HW) { + if (ivpu_test_mode & IVPU_TEST_MODE_NULL_HW) { ivpu_job_done(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS); cmdq->jobq->header.head = cmdq->jobq->header.tail; wmb(); /* Flush WC buffer for jobq header */ @@ -448,7 +436,7 @@ ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 } bo = job->bos[CMD_BUF_IDX]; - if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_READ)) { + if (!dma_resv_test_signaled(bo->base.base.resv, DMA_RESV_USAGE_READ)) { ivpu_warn(vdev, "Buffer is already in use\n"); return -EBUSY; } @@ -468,7 +456,7 @@ ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 } for (i = 0; i < buf_count; i++) { - ret = dma_resv_reserve_fences(job->bos[i]->base.resv, 1); + ret = dma_resv_reserve_fences(job->bos[i]->base.base.resv, 1); if (ret) { ivpu_warn(vdev, "Failed to reserve fences: %d\n", ret); goto unlock_reservations; @@ -477,7 +465,7 @@ ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 for (i = 0; i < buf_count; i++) { usage = (i == CMD_BUF_IDX) ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_BOOKKEEP; - dma_resv_add_fence(job->bos[i]->base.resv, job->done_fence, usage); + dma_resv_add_fence(job->bos[i]->base.base.resv, job->done_fence, usage); } unlock_reservations: @@ -562,61 +550,36 @@ free_handles: return ret; } -static int ivpu_job_done_thread(void *arg) +static void +ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, + struct vpu_jsm_msg *jsm_msg) { - struct ivpu_device *vdev = (struct ivpu_device *)arg; - struct ivpu_ipc_consumer cons; - struct vpu_jsm_msg jsm_msg; - bool jobs_submitted; - unsigned int timeout; + struct vpu_ipc_msg_payload_job_done *payload; int ret; - ivpu_dbg(vdev, JOB, "Started %s\n", __func__); - - ivpu_ipc_consumer_add(vdev, &cons, VPU_IPC_CHAN_JOB_RET); - - while (!kthread_should_stop()) { - timeout = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr; - jobs_submitted = !xa_empty(&vdev->submitted_jobs_xa); - ret = ivpu_ipc_receive(vdev, &cons, NULL, &jsm_msg, timeout); - if (!ret) { - ivpu_job_done_message(vdev, &jsm_msg); - } else if (ret == -ETIMEDOUT) { - if (jobs_submitted && !xa_empty(&vdev->submitted_jobs_xa)) { - ivpu_err(vdev, "TDR detected, timeout %d ms", timeout); - ivpu_hw_diagnose_failure(vdev); - ivpu_pm_schedule_recovery(vdev); - } - } + if (!jsm_msg) { + ivpu_err(vdev, "IPC message has no JSM payload\n"); + return; } - ivpu_ipc_consumer_del(vdev, &cons); - - ivpu_jobs_abort_all(vdev); + if (jsm_msg->result != VPU_JSM_STATUS_SUCCESS) { + ivpu_err(vdev, "Invalid JSM message result: %d\n", jsm_msg->result); + return; + } - ivpu_dbg(vdev, JOB, "Stopped %s\n", __func__); - return 0; + payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload; + ret = ivpu_job_done(vdev, payload->job_id, payload->job_status); + if (!ret && !xa_empty(&vdev->submitted_jobs_xa)) + ivpu_start_job_timeout_detection(vdev); } -int ivpu_job_done_thread_init(struct ivpu_device *vdev) +void ivpu_job_done_consumer_init(struct ivpu_device *vdev) { - struct task_struct *thread; - - thread = kthread_run(&ivpu_job_done_thread, (void *)vdev, "ivpu_job_done_thread"); - if (IS_ERR(thread)) { - ivpu_err(vdev, "Failed to start job completion thread\n"); - return -EIO; - } - - get_task_struct(thread); - wake_up_process(thread); - - vdev->job_done_thread = thread; - - return 0; + ivpu_ipc_consumer_add(vdev, &vdev->job_done_consumer, + VPU_IPC_CHAN_JOB_RET, ivpu_job_done_callback); } -void ivpu_job_done_thread_fini(struct ivpu_device *vdev) +void ivpu_job_done_consumer_fini(struct ivpu_device *vdev) { - kthread_stop_put(vdev->job_done_thread); + ivpu_ipc_consumer_del(vdev, &vdev->job_done_consumer); } diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index 5514c2d8a609..45a2f2ec82e5 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -59,8 +59,8 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file) void ivpu_cmdq_release_all(struct ivpu_file_priv *file_priv); void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev); -int ivpu_job_done_thread_init(struct ivpu_device *vdev); -void ivpu_job_done_thread_fini(struct ivpu_device *vdev); +void ivpu_job_done_consumer_init(struct ivpu_device *vdev); +void ivpu_job_done_consumer_fini(struct ivpu_device *vdev); void ivpu_jobs_abort_all(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c index 0c2fe7142024..8cea0dd731b9 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.c +++ b/drivers/accel/ivpu/ivpu_jsm_msg.c @@ -4,6 +4,7 @@ */ #include "ivpu_drv.h" +#include "ivpu_hw.h" #include "ivpu_ipc.h" #include "ivpu_jsm_msg.h" @@ -36,6 +37,17 @@ const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type) IVPU_CASE_TO_STR(VPU_JSM_MSG_DESTROY_CMD_QUEUE); IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES); IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_REGISTER_DB); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_CMDQ); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SUSPEND_CMDQ); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_ENGINE_RESUME); + IVPU_CASE_TO_STR(VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_STATE_DUMP); + IVPU_CASE_TO_STR(VPU_JSM_MSG_STATE_DUMP_RSP); IVPU_CASE_TO_STR(VPU_JSM_MSG_BLOB_DEINIT); IVPU_CASE_TO_STR(VPU_JSM_MSG_DYNDBG_CONTROL); IVPU_CASE_TO_STR(VPU_JSM_MSG_JOB_DONE); @@ -65,6 +77,12 @@ const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type) IVPU_CASE_TO_STR(VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP); IVPU_CASE_TO_STR(VPU_JSM_MSG_BLOB_DEINIT_DONE); IVPU_CASE_TO_STR(VPU_JSM_MSG_DYNDBG_CONTROL_RSP); + IVPU_CASE_TO_STR(VPU_JSM_MSG_PWR_D0I3_ENTER); + IVPU_CASE_TO_STR(VPU_JSM_MSG_PWR_D0I3_ENTER_DONE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_ENABLE_DONE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE); + IVPU_CASE_TO_STR(VPU_JSM_MSG_DCT_DISABLE_DONE); } #undef IVPU_CASE_TO_STR @@ -243,3 +261,23 @@ int ivpu_jsm_context_release(struct ivpu_device *vdev, u32 host_ssid) return ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_SSID_RELEASE_DONE, &resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); } + +int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev) +{ + struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_PWR_D0I3_ENTER }; + struct vpu_jsm_msg resp; + int ret; + + if (IVPU_WA(disable_d0i3_msg)) + return 0; + + req.payload.pwr_d0i3_enter.send_response = 1; + + ret = ivpu_ipc_send_receive_active(vdev, &req, VPU_JSM_MSG_PWR_D0I3_ENTER_DONE, + &resp, VPU_IPC_CHAN_GEN_CMD, + vdev->timeout.d0i3_entry_msg); + if (ret) + return ret; + + return ivpu_hw_wait_for_idle(vdev); +} diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.h b/drivers/accel/ivpu/ivpu_jsm_msg.h index 66979a948c7c..ae75e5dbcc41 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.h +++ b/drivers/accel/ivpu/ivpu_jsm_msg.h @@ -22,4 +22,5 @@ int ivpu_jsm_trace_get_capability(struct ivpu_device *vdev, u32 *trace_destinati int ivpu_jsm_trace_set_config(struct ivpu_device *vdev, u32 trace_level, u32 trace_destination_mask, u64 trace_hw_component_mask); int ivpu_jsm_context_release(struct ivpu_device *vdev, u32 host_ssid); +int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev); #endif diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c index 2538c78fbebe..2228c44b115f 100644 --- a/drivers/accel/ivpu/ivpu_mmu.c +++ b/drivers/accel/ivpu/ivpu_mmu.c @@ -230,7 +230,12 @@ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_PRIQ_ABT)) | \ (REG_FLD(IVPU_MMU_REG_GERROR, MSI_ABT))) -static char *ivpu_mmu_event_to_str(u32 cmd) +#define IVPU_MMU_CERROR_NONE 0x0 +#define IVPU_MMU_CERROR_ILL 0x1 +#define IVPU_MMU_CERROR_ABT 0x2 +#define IVPU_MMU_CERROR_ATC_INV_SYNC 0x3 + +static const char *ivpu_mmu_event_to_str(u32 cmd) { switch (cmd) { case IVPU_MMU_EVT_F_UUT: @@ -276,6 +281,22 @@ static char *ivpu_mmu_event_to_str(u32 cmd) } } +static const char *ivpu_mmu_cmdq_err_to_str(u32 err) +{ + switch (err) { + case IVPU_MMU_CERROR_NONE: + return "No CMDQ Error"; + case IVPU_MMU_CERROR_ILL: + return "Illegal command"; + case IVPU_MMU_CERROR_ABT: + return "External abort on CMDQ read"; + case IVPU_MMU_CERROR_ATC_INV_SYNC: + return "Sync failed to complete ATS invalidation"; + default: + return "Unknown CMDQ Error"; + } +} + static void ivpu_mmu_config_check(struct ivpu_device *vdev) { u32 val_ref; @@ -479,10 +500,7 @@ static int ivpu_mmu_cmdq_sync(struct ivpu_device *vdev) u64 val; int ret; - val = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_SYNC) | - FIELD_PREP(IVPU_MMU_CMD_SYNC_0_CS, 0x2) | - FIELD_PREP(IVPU_MMU_CMD_SYNC_0_MSH, 0x3) | - FIELD_PREP(IVPU_MMU_CMD_SYNC_0_MSI_ATTR, 0xf); + val = FIELD_PREP(IVPU_MMU_CMD_OPCODE, CMD_SYNC); ret = ivpu_mmu_cmdq_cmd_write(vdev, "SYNC", val, 0); if (ret) @@ -492,8 +510,15 @@ static int ivpu_mmu_cmdq_sync(struct ivpu_device *vdev) REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, q->prod); ret = ivpu_mmu_cmdq_wait_for_cons(vdev); - if (ret) - ivpu_err(vdev, "Timed out waiting for consumer: %d\n", ret); + if (ret) { + u32 err; + + val = REGV_RD32(IVPU_MMU_REG_CMDQ_CONS); + err = REG_GET_FLD(IVPU_MMU_REG_CMDQ_CONS, ERR, val); + + ivpu_err(vdev, "Timed out waiting for MMU consumer: %d, error: %s\n", ret, + ivpu_mmu_cmdq_err_to_str(err)); + } return ret; } @@ -750,9 +775,12 @@ int ivpu_mmu_init(struct ivpu_device *vdev) ivpu_dbg(vdev, MMU, "Init..\n"); - drmm_mutex_init(&vdev->drm, &mmu->lock); ivpu_mmu_config_check(vdev); + ret = drmm_mutex_init(&vdev->drm, &mmu->lock); + if (ret) + return ret; + ret = ivpu_mmu_structs_alloc(vdev); if (ret) return ret; diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index c1050a2df954..12a8c09d4547 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -5,6 +5,9 @@ #include <linux/bitfield.h> #include <linux/highmem.h> +#include <linux/set_memory.h> + +#include <drm/drm_cache.h> #include "ivpu_drv.h" #include "ivpu_hw.h" @@ -39,12 +42,57 @@ #define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \ IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID) +static void *ivpu_pgtable_alloc_page(struct ivpu_device *vdev, dma_addr_t *dma) +{ + dma_addr_t dma_addr; + struct page *page; + void *cpu; + + page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); + if (!page) + return NULL; + + set_pages_array_wc(&page, 1); + + dma_addr = dma_map_page(vdev->drm.dev, page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + if (dma_mapping_error(vdev->drm.dev, dma_addr)) + goto err_free_page; + + cpu = vmap(&page, 1, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + if (!cpu) + goto err_dma_unmap_page; + + + *dma = dma_addr; + return cpu; + +err_dma_unmap_page: + dma_unmap_page(vdev->drm.dev, dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + +err_free_page: + put_page(page); + return NULL; +} + +static void ivpu_pgtable_free_page(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) +{ + struct page *page; + + if (cpu_addr) { + page = vmalloc_to_page(cpu_addr); + vunmap(cpu_addr); + dma_unmap_page(vdev->drm.dev, dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK, PAGE_SIZE, + DMA_BIDIRECTIONAL); + set_pages_array_wb(&page, 1); + put_page(page); + } +} + static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) { dma_addr_t pgd_dma; - pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, - GFP_KERNEL); + pgtable->pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma); if (!pgtable->pgd_dma_ptr) return -ENOMEM; @@ -53,13 +101,6 @@ static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtab return 0; } -static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) -{ - if (cpu_addr) - dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr, - dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK); -} - static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) { int pgd_idx, pud_idx, pmd_idx; @@ -84,19 +125,19 @@ static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgt pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx]; - ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma); + ivpu_pgtable_free_page(vdev, pte_dma_ptr, pte_dma); } kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]); - ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); + ivpu_pgtable_free_page(vdev, pmd_dma_ptr, pmd_dma); } kfree(pgtable->pmd_ptrs[pgd_idx]); kfree(pgtable->pte_ptrs[pgd_idx]); - ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); + ivpu_pgtable_free_page(vdev, pud_dma_ptr, pud_dma); } - ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); + ivpu_pgtable_free_page(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); } static u64* @@ -108,7 +149,7 @@ ivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, if (pud_dma_ptr) return pud_dma_ptr; - pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL); + pud_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pud_dma); if (!pud_dma_ptr) return NULL; @@ -131,7 +172,7 @@ err_free_pmd_ptrs: kfree(pgtable->pmd_ptrs[pgd_idx]); err_free_pud_dma_ptr: - ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); + ivpu_pgtable_free_page(vdev, pud_dma_ptr, pud_dma); return NULL; } @@ -145,7 +186,7 @@ ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, if (pmd_dma_ptr) return pmd_dma_ptr; - pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); + pmd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pmd_dma); if (!pmd_dma_ptr) return NULL; @@ -160,7 +201,7 @@ ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, return pmd_dma_ptr; err_free_pmd_dma_ptr: - ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); + ivpu_pgtable_free_page(vdev, pmd_dma_ptr, pmd_dma); return NULL; } @@ -174,7 +215,7 @@ ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, if (pte_dma_ptr) return pte_dma_ptr; - pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); + pte_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pte_dma); if (!pte_dma_ptr) return NULL; @@ -249,38 +290,6 @@ static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_ad ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID; } -static void -ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) -{ - struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable; - u64 end_addr = vpu_addr + size; - - /* Align to PMD entry (2 MB) */ - vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); - - while (vpu_addr < end_addr) { - int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); - u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE; - - while (vpu_addr < end_addr && vpu_addr < pud_end) { - int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); - u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; - - while (vpu_addr < end_addr && vpu_addr < pmd_end) { - int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); - - clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx], - IVPU_MMU_PGTABLE_SIZE); - vpu_addr += IVPU_MMU_PTE_MAP_SIZE; - } - clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx], - IVPU_MMU_PGTABLE_SIZE); - } - clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE); - } - clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE); -} - static int ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot) @@ -327,6 +336,9 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 prot; u64 i; + if (drm_WARN_ON(&vdev->drm, !ctx)) + return -EINVAL; + if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) return -EINVAL; @@ -349,10 +361,11 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, mutex_unlock(&ctx->lock); return ret; } - ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); vpu_addr += size; } + /* Ensure page table modifications are flushed from wc buffers to memory */ + wmb(); mutex_unlock(&ctx->lock); ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); @@ -369,8 +382,8 @@ ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ct int ret; u64 i; - if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) - ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr); + if (drm_WARN_ON(&vdev->drm, !ctx)) + return; mutex_lock(&ctx->lock); @@ -378,10 +391,11 @@ ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ct size_t size = sg_dma_len(sg) + sg->offset; ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); - ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); vpu_addr += size; } + /* Ensure page table modifications are flushed from wc buffers to memory */ + wmb(); mutex_unlock(&ctx->lock); ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); @@ -390,28 +404,34 @@ ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ct } int -ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, - const struct ivpu_addr_range *range, - u64 size, struct drm_mm_node *node) +ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range, + u64 size, struct drm_mm_node *node) { - lockdep_assert_held(&ctx->lock); + int ret; + + WARN_ON(!range); + mutex_lock(&ctx->lock); if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) { - if (!drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0, - range->start, range->end, DRM_MM_INSERT_BEST)) - return 0; + ret = drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0, + range->start, range->end, DRM_MM_INSERT_BEST); + if (!ret) + goto unlock; } - return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0, - range->start, range->end, DRM_MM_INSERT_BEST); + ret = drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0, + range->start, range->end, DRM_MM_INSERT_BEST); +unlock: + mutex_unlock(&ctx->lock); + return ret; } void -ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) +ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) { - lockdep_assert_held(&ctx->lock); - + mutex_lock(&ctx->lock); drm_mm_remove_node(node); + mutex_unlock(&ctx->lock); } static int @@ -421,7 +441,6 @@ ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u3 int ret; mutex_init(&ctx->lock); - INIT_LIST_HEAD(&ctx->bo_list); ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); if (ret) { diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h index f15d8c630d8a..535db3a1fc74 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.h +++ b/drivers/accel/ivpu/ivpu_mmu_context.h @@ -23,10 +23,9 @@ struct ivpu_mmu_pgtable { }; struct ivpu_mmu_context { - struct mutex lock; /* protects: mm, pgtable, bo_list */ + struct mutex lock; /* Protects: mm, pgtable */ struct drm_mm mm; struct ivpu_mmu_pgtable pgtable; - struct list_head bo_list; u32 id; }; @@ -39,11 +38,9 @@ int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid); -int ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, - const struct ivpu_addr_range *range, - u64 size, struct drm_mm_node *node); -void ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, - struct drm_mm_node *node); +int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range, + u64 size, struct drm_mm_node *node); +void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node); int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, struct sg_table *sgt, bool llc_coherent); diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index 0ace218783c8..0af8864cb3b5 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -15,6 +15,7 @@ #include "ivpu_fw.h" #include "ivpu_ipc.h" #include "ivpu_job.h" +#include "ivpu_jsm_msg.h" #include "ivpu_mmu.h" #include "ivpu_pm.h" @@ -22,6 +23,10 @@ static bool ivpu_disable_recovery; module_param_named_unsafe(disable_recovery, ivpu_disable_recovery, bool, 0644); MODULE_PARM_DESC(disable_recovery, "Disables recovery when VPU hang is detected"); +static unsigned long ivpu_tdr_timeout_ms; +module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644); +MODULE_PARM_DESC(tdr_timeout_ms, "Timeout for device hang detection, in milliseconds, 0 - default"); + #define PM_RESCHEDULE_LIMIT 5 static void ivpu_pm_prepare_cold_boot(struct ivpu_device *vdev) @@ -69,27 +74,31 @@ retry: ret = ivpu_hw_power_up(vdev); if (ret) { ivpu_err(vdev, "Failed to power up HW: %d\n", ret); - return ret; + goto err_power_down; } ret = ivpu_mmu_enable(vdev); if (ret) { ivpu_err(vdev, "Failed to resume MMU: %d\n", ret); - ivpu_hw_power_down(vdev); - return ret; + goto err_power_down; } ret = ivpu_boot(vdev); - if (ret) { - ivpu_mmu_disable(vdev); - ivpu_hw_power_down(vdev); - if (!ivpu_fw_is_cold_boot(vdev)) { - ivpu_warn(vdev, "Failed to resume the FW: %d. Retrying cold boot..\n", ret); - ivpu_pm_prepare_cold_boot(vdev); - goto retry; - } else { - ivpu_err(vdev, "Failed to resume the FW: %d\n", ret); - } + if (ret) + goto err_mmu_disable; + + return 0; + +err_mmu_disable: + ivpu_mmu_disable(vdev); +err_power_down: + ivpu_hw_power_down(vdev); + + if (!ivpu_fw_is_cold_boot(vdev)) { + ivpu_pm_prepare_cold_boot(vdev); + goto retry; + } else { + ivpu_err(vdev, "Failed to resume the FW: %d\n", ret); } return ret; @@ -136,6 +145,31 @@ void ivpu_pm_schedule_recovery(struct ivpu_device *vdev) } } +static void ivpu_job_timeout_work(struct work_struct *work) +{ + struct ivpu_pm_info *pm = container_of(work, struct ivpu_pm_info, job_timeout_work.work); + struct ivpu_device *vdev = pm->vdev; + unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr; + + ivpu_err(vdev, "TDR detected, timeout %lu ms", timeout_ms); + ivpu_hw_diagnose_failure(vdev); + + ivpu_pm_schedule_recovery(vdev); +} + +void ivpu_start_job_timeout_detection(struct ivpu_device *vdev) +{ + unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr; + + /* No-op if already queued */ + queue_delayed_work(system_wq, &vdev->pm->job_timeout_work, msecs_to_jiffies(timeout_ms)); +} + +void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev) +{ + cancel_delayed_work_sync(&vdev->pm->job_timeout_work); +} + int ivpu_pm_suspend_cb(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); @@ -153,6 +187,8 @@ int ivpu_pm_suspend_cb(struct device *dev) } } + ivpu_jsm_pwr_d0i3_enter(vdev); + ivpu_suspend(vdev); ivpu_pm_prepare_warm_boot(vdev); @@ -188,6 +224,7 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); struct ivpu_device *vdev = to_ivpu_device(drm); + bool hw_is_idle = true; int ret; ivpu_dbg(vdev, PM, "Runtime suspend..\n"); @@ -200,11 +237,16 @@ int ivpu_pm_runtime_suspend_cb(struct device *dev) return -EAGAIN; } + if (!vdev->pm->suspend_reschedule_counter) + hw_is_idle = false; + else if (ivpu_jsm_pwr_d0i3_enter(vdev)) + hw_is_idle = false; + ret = ivpu_suspend(vdev); if (ret) ivpu_err(vdev, "Failed to set suspend VPU: %d\n", ret); - if (!vdev->pm->suspend_reschedule_counter) { + if (!hw_is_idle) { ivpu_warn(vdev, "VPU failed to enter idle, force suspended.\n"); ivpu_pm_prepare_cold_boot(vdev); } else { @@ -250,9 +292,6 @@ int ivpu_rpm_get_if_active(struct ivpu_device *vdev) { int ret; - ivpu_dbg(vdev, RPM, "rpm_get_if_active count %d\n", - atomic_read(&vdev->drm.dev->power.usage_count)); - ret = pm_runtime_get_if_active(vdev->drm.dev, false); drm_WARN_ON(&vdev->drm, ret < 0); @@ -307,6 +346,7 @@ void ivpu_pm_init(struct ivpu_device *vdev) atomic_set(&pm->in_reset, 0); INIT_WORK(&pm->recovery_work, ivpu_pm_recovery_work); + INIT_DELAYED_WORK(&pm->job_timeout_work, ivpu_job_timeout_work); if (ivpu_disable_recovery) delay = -1; @@ -321,6 +361,7 @@ void ivpu_pm_init(struct ivpu_device *vdev) void ivpu_pm_cancel_recovery(struct ivpu_device *vdev) { + drm_WARN_ON(&vdev->drm, delayed_work_pending(&vdev->pm->job_timeout_work)); cancel_work_sync(&vdev->pm->recovery_work); } diff --git a/drivers/accel/ivpu/ivpu_pm.h b/drivers/accel/ivpu/ivpu_pm.h index 044db150be07..97c6e0b0aa42 100644 --- a/drivers/accel/ivpu/ivpu_pm.h +++ b/drivers/accel/ivpu/ivpu_pm.h @@ -12,6 +12,7 @@ struct ivpu_device; struct ivpu_pm_info { struct ivpu_device *vdev; + struct delayed_work job_timeout_work; struct work_struct recovery_work; atomic_t in_reset; atomic_t reset_counter; @@ -37,5 +38,7 @@ int __must_check ivpu_rpm_get_if_active(struct ivpu_device *vdev); void ivpu_rpm_put(struct ivpu_device *vdev); void ivpu_pm_schedule_recovery(struct ivpu_device *vdev); +void ivpu_start_job_timeout_detection(struct ivpu_device *vdev); +void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev); #endif /* __IVPU_PM_H__ */ diff --git a/drivers/accel/ivpu/vpu_boot_api.h b/drivers/accel/ivpu/vpu_boot_api.h index 6b71be92ba65..04c954258563 100644 --- a/drivers/accel/ivpu/vpu_boot_api.h +++ b/drivers/accel/ivpu/vpu_boot_api.h @@ -11,7 +11,10 @@ * The bellow values will be used to construct the version info this way: * fw_bin_header->api_version[VPU_BOOT_API_VER_ID] = (VPU_BOOT_API_VER_MAJOR << 16) | * VPU_BOOT_API_VER_MINOR; - * VPU_BOOT_API_VER_PATCH will be ignored. KMD and compatibility is not affected if this changes. + * VPU_BOOT_API_VER_PATCH will be ignored. KMD and compatibility is not affected if this changes + * This information is collected by using vpuip_2/application/vpuFirmware/make_std_fw_image.py + * If a header is missing this info we ignore the header, if a header is missing or contains + * partial info a build error will be generated. */ /* @@ -24,12 +27,12 @@ * Minor version changes when API backward compatibility is preserved. * Resets to 0 if Major version is incremented. */ -#define VPU_BOOT_API_VER_MINOR 12 +#define VPU_BOOT_API_VER_MINOR 20 /* * API header changed (field names, documentation, formatting) but API itself has not been changed */ -#define VPU_BOOT_API_VER_PATCH 2 +#define VPU_BOOT_API_VER_PATCH 4 /* * Index in the API version table @@ -63,6 +66,12 @@ struct vpu_firmware_header { /* Size of memory require for firmware execution */ u32 runtime_size; u32 shave_nn_fw_size; + /* Size of primary preemption buffer. */ + u32 preemption_buffer_1_size; + /* Size of secondary preemption buffer. */ + u32 preemption_buffer_2_size; + /* Space reserved for future preemption-related fields. */ + u32 preemption_reserved[6]; }; /* @@ -89,6 +98,14 @@ enum VPU_BOOT_L2_CACHE_CFG_TYPE { VPU_BOOT_L2_CACHE_CFG_NUM = 2 }; +/** VPU MCA ECC signalling mode. By default, no signalling is used */ +enum VPU_BOOT_MCA_ECC_SIGNAL_TYPE { + VPU_BOOT_MCA_ECC_NONE = 0, + VPU_BOOT_MCA_ECC_CORR = 1, + VPU_BOOT_MCA_ECC_FATAL = 2, + VPU_BOOT_MCA_ECC_BOTH = 3 +}; + /** * Logging destinations. * @@ -131,9 +148,11 @@ enum vpu_trace_destination { #define VPU_TRACE_PROC_BIT_ACT_SHV_3 22 #define VPU_TRACE_PROC_NO_OF_HW_DEVS 23 -/* KMB HW component IDs are sequential, so define first and last IDs. */ -#define VPU_TRACE_PROC_BIT_KMB_FIRST VPU_TRACE_PROC_BIT_LRT -#define VPU_TRACE_PROC_BIT_KMB_LAST VPU_TRACE_PROC_BIT_SHV_15 +/* VPU 30xx HW component IDs are sequential, so define first and last IDs. */ +#define VPU_TRACE_PROC_BIT_30XX_FIRST VPU_TRACE_PROC_BIT_LRT +#define VPU_TRACE_PROC_BIT_30XX_LAST VPU_TRACE_PROC_BIT_SHV_15 +#define VPU_TRACE_PROC_BIT_KMB_FIRST VPU_TRACE_PROC_BIT_30XX_FIRST +#define VPU_TRACE_PROC_BIT_KMB_LAST VPU_TRACE_PROC_BIT_30XX_LAST struct vpu_boot_l2_cache_config { u8 use; @@ -148,6 +167,25 @@ struct vpu_warm_boot_section { u32 is_clear_op; }; +/* + * When HW scheduling mode is enabled, a present period is defined. + * It will be used by VPU to swap between normal and focus priorities + * to prevent starving of normal priority band (when implemented). + * Host must provide a valid value at boot time in + * `vpu_focus_present_timer_ms`. If the value provided by the host is not within the + * defined range a default value will be used. Here we define the min. and max. + * allowed values and the and default value of the present period. Units are milliseconds. + */ +#define VPU_PRESENT_CALL_PERIOD_MS_DEFAULT 50 +#define VPU_PRESENT_CALL_PERIOD_MS_MIN 16 +#define VPU_PRESENT_CALL_PERIOD_MS_MAX 10000 + +/** + * Macros to enable various operation modes within the VPU. + * To be defined as part of 32 bit mask. + */ +#define VPU_OP_MODE_SURVIVABILITY 0x1 + struct vpu_boot_params { u32 magic; u32 vpu_id; @@ -218,6 +256,7 @@ struct vpu_boot_params { * the threshold will not be logged); applies to every enabled logging * destination and loggable HW component. See 'mvLog_t' enum for acceptable * values. + * TODO: EISW-33556: Move log level definition (mvLog_t) to this file. */ u32 default_trace_level; u32 boot_type; @@ -249,7 +288,36 @@ struct vpu_boot_params { u32 temp_sensor_period_ms; /** PLL ratio for efficient clock frequency */ u32 pn_freq_pll_ratio; - u32 pad4[28]; + /** DVFS Mode: Default: 0, Max Performance: 1, On Demand: 2, Power Save: 3 */ + u32 dvfs_mode; + /** + * Depending on DVFS Mode: + * On-demand: Default if 0. + * Bit 0-7 - uint8_t: Highest residency percent + * Bit 8-15 - uint8_t: High residency percent + * Bit 16-23 - uint8_t: Low residency percent + * Bit 24-31 - uint8_t: Lowest residency percent + * Bit 32-35 - unsigned 4b: PLL Ratio increase amount on highest residency + * Bit 36-39 - unsigned 4b: PLL Ratio increase amount on high residency + * Bit 40-43 - unsigned 4b: PLL Ratio decrease amount on low residency + * Bit 44-47 - unsigned 4b: PLL Ratio decrease amount on lowest frequency + * Bit 48-55 - uint8_t: Period (ms) for residency decisions + * Bit 56-63 - uint8_t: Averaging windows (as multiples of period. Max: 30 decimal) + * Power Save/Max Performance: Unused + */ + u64 dvfs_param; + /** + * D0i3 delayed entry + * Bit0: Disable CPU state save on D0i2 entry flow. + * 0: Every D0i2 entry saves state. Save state IPC message ignored. + * 1: IPC message required to save state on D0i3 entry flow. + */ + u32 d0i3_delayed_entry; + /* Time spent by VPU in D0i3 state */ + u64 d0i3_residency_time_us; + /* Value of VPU perf counter at the time of entering D0i3 state . */ + u64 d0i3_entry_vpu_ts; + u32 pad4[20]; /* Warm boot information: 0x400 - 0x43F */ u32 warm_boot_sections_count; u32 warm_boot_start_address_reference; @@ -274,8 +342,12 @@ struct vpu_boot_params { u32 vpu_scheduling_mode; /* Present call period in milliseconds. */ u32 vpu_focus_present_timer_ms; - /* Unused/reserved: 0x478 - 0xFFF */ - u32 pad6[738]; + /* VPU ECC Signaling */ + u32 vpu_uses_ecc_mca_signal; + /* Values defined by VPU_OP_MODE* macros */ + u32 vpu_operation_mode; + /* Unused/reserved: 0x480 - 0xFFF */ + u32 pad6[736]; }; /* diff --git a/drivers/accel/ivpu/vpu_jsm_api.h b/drivers/accel/ivpu/vpu_jsm_api.h index 2949ec8365bd..7da7622742be 100644 --- a/drivers/accel/ivpu/vpu_jsm_api.h +++ b/drivers/accel/ivpu/vpu_jsm_api.h @@ -22,12 +22,12 @@ /* * Minor version changes when API backward compatibility is preserved. */ -#define VPU_JSM_API_VER_MINOR 0 +#define VPU_JSM_API_VER_MINOR 15 /* * API header changed (field names, documentation, formatting) but API itself has not been changed */ -#define VPU_JSM_API_VER_PATCH 1 +#define VPU_JSM_API_VER_PATCH 0 /* * Index in the API version table @@ -84,11 +84,13 @@ * Job flags bit masks. */ #define VPU_JOB_FLAGS_NULL_SUBMISSION_MASK 0x00000001 +#define VPU_JOB_FLAGS_PRIVATE_DATA_MASK 0xFF000000 /* * Sizes of the reserved areas in jobs, in bytes. */ -#define VPU_JOB_RESERVED_BYTES 16 +#define VPU_JOB_RESERVED_BYTES 8 + /* * Sizes of the reserved areas in job queues, in bytes. */ @@ -109,6 +111,20 @@ #define VPU_DYNDBG_CMD_MAX_LEN 96 /* + * For HWS command queue scheduling, we can prioritise command queues inside the + * same process with a relative in-process priority. Valid values for relative + * priority are given below - max and min. + */ +#define VPU_HWS_COMMAND_QUEUE_MAX_IN_PROCESS_PRIORITY 7 +#define VPU_HWS_COMMAND_QUEUE_MIN_IN_PROCESS_PRIORITY -7 + +/* + * For HWS priority scheduling, we can have multiple realtime priority bands. + * They are numbered 0 to a MAX. + */ +#define VPU_HWS_MAX_REALTIME_PRIORITY_LEVEL 31U + +/* * Job format. */ struct vpu_job_queue_entry { @@ -117,8 +133,14 @@ struct vpu_job_queue_entry { u32 flags; /**< Flags bit field, see VPU_JOB_FLAGS_* above */ u64 root_page_table_addr; /**< Address of root page table to use for this job */ u64 root_page_table_update_counter; /**< Page tables update events counter */ - u64 preemption_buffer_address; /**< Address of the preemption buffer to use for this job */ - u64 preemption_buffer_size; /**< Size of the preemption buffer to use for this job */ + u64 primary_preempt_buf_addr; + /**< Address of the primary preemption buffer to use for this job */ + u32 primary_preempt_buf_size; + /**< Size of the primary preemption buffer to use for this job */ + u32 secondary_preempt_buf_size; + /**< Size of secondary preemption buffer to use for this job */ + u64 secondary_preempt_buf_addr; + /**< Address of secondary preemption buffer to use for this job */ u8 reserved_0[VPU_JOB_RESERVED_BYTES]; }; @@ -153,6 +175,46 @@ enum vpu_trace_entity_type { }; /* + * HWS specific log buffer header details. + * Total size is 32 bytes. + */ +struct vpu_hws_log_buffer_header { + /* Written by VPU after adding a log entry. Initialised by host to 0. */ + u32 first_free_entry_index; + /* Incremented by VPU every time the VPU overwrites the 0th entry; + * initialised by host to 0. + */ + u32 wraparound_count; + /* + * This is the number of buffers that can be stored in the log buffer provided by the host. + * It is written by host before passing buffer to VPU. VPU should consider it read-only. + */ + u64 num_of_entries; + u64 reserved[2]; +}; + +/* + * HWS specific log buffer entry details. + * Total size is 32 bytes. + */ +struct vpu_hws_log_buffer_entry { + /* VPU timestamp must be an invariant timer tick (not impacted by DVFS) */ + u64 vpu_timestamp; + /* + * Operation type: + * 0 - context state change + * 1 - queue new work + * 2 - queue unwait sync object + * 3 - queue no more work + * 4 - queue wait sync object + */ + u32 operation_type; + u32 reserved; + /* Operation data depends on operation type */ + u64 operation_data[2]; +}; + +/* * Host <-> VPU IPC messages types. */ enum vpu_ipc_msg_type { @@ -228,6 +290,23 @@ enum vpu_ipc_msg_type { * deallocated or reassigned to another context. */ VPU_JSM_MSG_HWS_REGISTER_DB = 0x1117, + /** Control command: Log buffer setting */ + VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG = 0x1118, + /* Control command: Suspend command queue. */ + VPU_JSM_MSG_HWS_SUSPEND_CMDQ = 0x1119, + /* Control command: Resume command queue */ + VPU_JSM_MSG_HWS_RESUME_CMDQ = 0x111a, + /* Control command: Resume engine after reset */ + VPU_JSM_MSG_HWS_ENGINE_RESUME = 0x111b, + /* Control command: Enable survivability/DCT mode */ + VPU_JSM_MSG_DCT_ENABLE = 0x111c, + /* Control command: Disable survivability/DCT mode */ + VPU_JSM_MSG_DCT_DISABLE = 0x111d, + /** + * Dump VPU state. To be used for debug purposes only. + * NOTE: Please introduce new ASYNC commands before this one. * + */ + VPU_JSM_MSG_STATE_DUMP = 0x11FF, /* IPC Host -> Device, General commands */ VPU_JSM_MSG_GENERAL_CMD = 0x1200, VPU_JSM_MSG_BLOB_DEINIT = VPU_JSM_MSG_GENERAL_CMD, @@ -236,6 +315,10 @@ enum vpu_ipc_msg_type { * Linux command: `echo '<dyndbg_cmd>' > <debugfs>/dynamic_debug/control`. */ VPU_JSM_MSG_DYNDBG_CONTROL = 0x1201, + /** + * Perform the save procedure for the D0i3 entry + */ + VPU_JSM_MSG_PWR_D0I3_ENTER = 0x1202, /* IPC Device -> Host, Job completion */ VPU_JSM_MSG_JOB_DONE = 0x2100, /* IPC Device -> Host, Async command completion */ @@ -304,11 +387,35 @@ enum vpu_ipc_msg_type { VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP = 0x2216, /** Response to control command: Set context scheduling properties */ VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP = 0x2217, + /** Response to control command: Log buffer setting */ + VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP = 0x2218, + /* IPC Device -> Host, HWS notify index entry of log buffer written */ + VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION = 0x2219, + /* IPC Device -> Host, HWS completion of a context suspend request */ + VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE = 0x221a, + /* Response to control command: Resume command queue */ + VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP = 0x221b, + /* Response to control command: Resume engine command response */ + VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE = 0x221c, + /* Response to control command: Enable survivability/DCT mode */ + VPU_JSM_MSG_DCT_ENABLE_DONE = 0x221d, + /* Response to control command: Disable survivability/DCT mode */ + VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e, + /** + * Response to state dump control command. + * NOTE: Please introduce new ASYNC responses before this one. * + */ + VPU_JSM_MSG_STATE_DUMP_RSP = 0x22FF, /* IPC Device -> Host, General command completion */ VPU_JSM_MSG_GENERAL_CMD_DONE = 0x2300, VPU_JSM_MSG_BLOB_DEINIT_DONE = VPU_JSM_MSG_GENERAL_CMD_DONE, /** Response to VPU_JSM_MSG_DYNDBG_CONTROL. */ VPU_JSM_MSG_DYNDBG_CONTROL_RSP = 0x2301, + /** + * Acknowledgment of completion of the save procedure initiated by + * VPU_JSM_MSG_PWR_D0I3_ENTER + */ + VPU_JSM_MSG_PWR_D0I3_ENTER_DONE = 0x2302, }; enum vpu_ipc_msg_status { VPU_JSM_MSG_FREE, VPU_JSM_MSG_ALLOCATED }; @@ -593,12 +700,12 @@ struct vpu_ipc_msg_payload_hws_priority_band_setup { * Default quantum in 100ns units for scheduling across processes * within a priority band */ - u64 process_quantum[VPU_HWS_NUM_PRIORITY_BANDS]; + u32 process_quantum[VPU_HWS_NUM_PRIORITY_BANDS]; /* * Default grace period in 100ns units for processes that preempt each * other within a priority band */ - u64 process_grace_period[VPU_HWS_NUM_PRIORITY_BANDS]; + u32 process_grace_period[VPU_HWS_NUM_PRIORITY_BANDS]; /* * For normal priority band, specifies the target VPU percentage * in situations when it's starved by the focus band. @@ -608,32 +715,51 @@ struct vpu_ipc_msg_payload_hws_priority_band_setup { u32 reserved_0; }; -/* HWS create command queue request */ +/* + * @brief HWS create command queue request. + * Host will create a command queue via this command. + * Note: Cmdq group is a handle of an object which + * may contain one or more command queues. + * @see VPU_JSM_MSG_CREATE_CMD_QUEUE + * @see VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP + */ struct vpu_ipc_msg_payload_hws_create_cmdq { /* Process id */ u64 process_id; /* Host SSID */ u32 host_ssid; - /* Zero Padding */ - u32 reserved; + /* Engine for which queue is being created */ + u32 engine_idx; + /* + * Cmdq group may be set to 0 or equal to + * cmdq_id while each priority band contains + * only single engine instances. + */ + u64 cmdq_group; /* Command queue id */ u64 cmdq_id; /* Command queue base */ u64 cmdq_base; /* Command queue size */ u32 cmdq_size; - /* Reserved */ + /* Zero padding */ u32 reserved_0; }; -/* HWS create command queue response */ +/* + * @brief HWS create command queue response. + * @see VPU_JSM_MSG_CREATE_CMD_QUEUE + * @see VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP + */ struct vpu_ipc_msg_payload_hws_create_cmdq_rsp { /* Process id */ u64 process_id; /* Host SSID */ u32 host_ssid; - /* Zero Padding */ - u32 reserved; + /* Engine for which queue is being created */ + u32 engine_idx; + /* Command queue group */ + u64 cmdq_group; /* Command queue id */ u64 cmdq_id; }; @@ -661,7 +787,7 @@ struct vpu_ipc_msg_payload_hws_set_context_sched_properties { /* Inside realtime band assigns a further priority */ u32 realtime_priority_level; /* Priority relative to other contexts in the same process */ - u32 in_process_priority; + s32 in_process_priority; /* Zero padding / Reserved */ u32 reserved_1; /* Context quantum relative to other contexts of same priority in the same process */ @@ -694,6 +820,123 @@ struct vpu_jsm_hws_register_db { u64 cmdq_size; }; +/* + * @brief Structure to set another buffer to be used for scheduling-related logging. + * The size of the logging buffer and the number of entries is defined as part of the + * buffer itself as described next. + * The log buffer received from the host is made up of; + * - header: 32 bytes in size, as shown in 'struct vpu_hws_log_buffer_header'. + * The header contains the number of log entries in the buffer. + * - log entry: 0 to n-1, each log entry is 32 bytes in size, as shown in + * 'struct vpu_hws_log_buffer_entry'. + * The entry contains the VPU timestamp, operation type and data. + * The host should provide the notify index value of log buffer to VPU. This is a + * value defined within the log buffer and when written to will generate the + * scheduling log notification. + * The host should set engine_idx and vpu_log_buffer_va to 0 to disable logging + * for a particular engine. + * VPU will handle one log buffer for each of supported engines. + * VPU should allow the logging to consume one host_ssid. + * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG + * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP + * @see VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION + */ +struct vpu_ipc_msg_payload_hws_set_scheduling_log { + /* Engine ordinal */ + u32 engine_idx; + /* Host SSID */ + u32 host_ssid; + /* + * VPU log buffer virtual address. + * Set to 0 to disable logging for this engine. + */ + u64 vpu_log_buffer_va; + /* + * Notify index of log buffer. VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION + * is generated when an event log is written to this index. + */ + u64 notify_index; +}; + +/* + * @brief The scheduling log notification is generated by VPU when it writes + * an event into the log buffer at the notify_index. VPU notifies host with + * VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION. This is an asynchronous + * message from VPU to host. + * @see VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION + * @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG + */ +struct vpu_ipc_msg_payload_hws_scheduling_log_notification { + /* Engine ordinal */ + u32 engine_idx; + /* Zero Padding */ + u32 reserved_0; +}; + +/* + * @brief HWS suspend command queue request and done structure. + * Host will request the suspend of contexts and VPU will; + * - Suspend all work on this context + * - Preempt any running work + * - Asynchronously perform the above and return success immediately once + * all items above are started successfully + * - Notify the host of completion of these operations via + * VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE + * - Reject any other context operations on a context with an in-flight + * suspend request running + * Same structure used when VPU notifies host of completion of a context suspend + * request. The ids and suspend fence value reported in this command will match + * the one in the request from the host to suspend the context. Once suspend is + * complete, VPU will not access any data relating to this command queue until + * it is resumed. + * @see VPU_JSM_MSG_HWS_SUSPEND_CMDQ + * @see VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE + */ +struct vpu_ipc_msg_payload_hws_suspend_cmdq { + /* Host SSID */ + u32 host_ssid; + /* Zero Padding */ + u32 reserved_0; + /* Command queue id */ + u64 cmdq_id; + /* + * Suspend fence value - reported by the VPU suspend context + * completed once suspend is complete. + */ + u64 suspend_fence_value; +}; + +/* + * @brief HWS Resume command queue request / response structure. + * Host will request the resume of a context; + * - VPU will resume all work on this context + * - Scheduler will allow this context to be scheduled + * @see VPU_JSM_MSG_HWS_RESUME_CMDQ + * @see VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP + */ +struct vpu_ipc_msg_payload_hws_resume_cmdq { + /* Host SSID */ + u32 host_ssid; + /* Zero Padding */ + u32 reserved_0; + /* Command queue id */ + u64 cmdq_id; +}; + +/* + * @brief HWS Resume engine request / response structure. + * After a HWS engine reset, all scheduling is stopped on VPU until a engine resume. + * Host shall send this command to resume scheduling of any valid queue. + * @see VPU_JSM_MSG_HWS_RESUME_ENGINE + * @see VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE + */ +struct vpu_ipc_msg_payload_hws_resume_engine { + /* Engine to be resumed */ + u32 engine_idx; + /* Reserved */ + u32 reserved_0; +}; + /** * Payload for VPU_JSM_MSG_TRACE_SET_CONFIG[_RSP] and * VPU_JSM_MSG_TRACE_GET_CONFIG_RSP messages. @@ -938,6 +1181,35 @@ struct vpu_ipc_msg_payload_dyndbg_control { char dyndbg_cmd[VPU_DYNDBG_CMD_MAX_LEN]; }; +/** + * Payload for VPU_JSM_MSG_PWR_D0I3_ENTER + * + * This is a bi-directional payload. + */ +struct vpu_ipc_msg_payload_pwr_d0i3_enter { + /** + * 0: VPU_JSM_MSG_PWR_D0I3_ENTER_DONE is not sent to the host driver + * The driver will poll for D0i2 Idle state transitions. + * 1: VPU_JSM_MSG_PWR_D0I3_ENTER_DONE is sent after VPU state save is complete + */ + u32 send_response; + u32 reserved_0; +}; + +/** + * Payload for VPU_JSM_MSG_DCT_ENABLE message. + * + * Default values for DCT active/inactive times are 5.3ms and 30ms respectively, + * corresponding to a 85% duty cycle. This payload allows the host to tune these + * values according to application requirements. + */ +struct vpu_ipc_msg_payload_pwr_dct_control { + /** Duty cycle active time in microseconds */ + u32 dct_active_us; + /** Duty cycle inactive time in microseconds */ + u32 dct_inactive_us; +}; + /* * Payloads union, used to define complete message format. */ @@ -974,6 +1246,13 @@ union vpu_ipc_msg_payload { struct vpu_ipc_msg_payload_hws_destroy_cmdq hws_destroy_cmdq; struct vpu_ipc_msg_payload_hws_set_context_sched_properties hws_set_context_sched_properties; + struct vpu_ipc_msg_payload_hws_set_scheduling_log hws_set_scheduling_log; + struct vpu_ipc_msg_payload_hws_scheduling_log_notification hws_scheduling_log_notification; + struct vpu_ipc_msg_payload_hws_suspend_cmdq hws_suspend_cmdq; + struct vpu_ipc_msg_payload_hws_resume_cmdq hws_resume_cmdq; + struct vpu_ipc_msg_payload_hws_resume_engine hws_resume_engine; + struct vpu_ipc_msg_payload_pwr_d0i3_enter pwr_d0i3_enter; + struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control; }; /* diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile index 2418418f7a50..3f7f6dfde7f2 100644 --- a/drivers/accel/qaic/Makefile +++ b/drivers/accel/qaic/Makefile @@ -9,4 +9,5 @@ qaic-y := \ mhi_controller.o \ qaic_control.o \ qaic_data.o \ - qaic_drv.o + qaic_drv.o \ + qaic_timesync.o diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c index 5036e58e7235..5d3cc30009cc 100644 --- a/drivers/accel/qaic/mhi_controller.c +++ b/drivers/accel/qaic/mhi_controller.c @@ -348,7 +348,7 @@ static struct mhi_channel_config aic100_channels[] = { .local_elements = 0, .event_ring = 0, .dir = DMA_TO_DEVICE, - .ee_mask = MHI_CH_EE_SBL | MHI_CH_EE_AMSS, + .ee_mask = MHI_CH_EE_SBL, .pollcfg = 0, .doorbell = MHI_DB_BRST_DISABLE, .lpm_notify = false, @@ -364,7 +364,39 @@ static struct mhi_channel_config aic100_channels[] = { .local_elements = 0, .event_ring = 0, .dir = DMA_FROM_DEVICE, - .ee_mask = MHI_CH_EE_SBL | MHI_CH_EE_AMSS, + .ee_mask = MHI_CH_EE_SBL, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = false, + .wake_capable = false, + }, + { + .name = "QAIC_TIMESYNC_PERIODIC", + .num = 22, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = false, + .wake_capable = false, + }, + { + .num = 23, + .name = "QAIC_TIMESYNC_PERIODIC", + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, .pollcfg = 0, .doorbell = MHI_DB_BRST_DISABLE, .lpm_notify = false, @@ -468,7 +500,7 @@ static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntrl) } struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar, - int mhi_irq) + int mhi_irq, bool shared_msi) { struct mhi_controller *mhi_cntrl; int ret; @@ -500,6 +532,10 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi return ERR_PTR(-ENOMEM); mhi_cntrl->irq[0] = mhi_irq; + + if (shared_msi) /* MSI shared with data path, no IRQF_NO_SUSPEND */ + mhi_cntrl->irq_flags = IRQF_SHARED; + mhi_cntrl->fw_image = "qcom/aic100/sbl.bin"; /* use latest configured timeout */ diff --git a/drivers/accel/qaic/mhi_controller.h b/drivers/accel/qaic/mhi_controller.h index 2ae45d768e24..500e7f4af2af 100644 --- a/drivers/accel/qaic/mhi_controller.h +++ b/drivers/accel/qaic/mhi_controller.h @@ -8,7 +8,7 @@ #define MHICONTROLLERQAIC_H_ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar, - int mhi_irq); + int mhi_irq, bool shared_msi); void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up); void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl); void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl); diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h index e3f4c30f3ffd..bc40d52dc010 100644 --- a/drivers/accel/qaic/qaic.h +++ b/drivers/accel/qaic/qaic.h @@ -123,6 +123,8 @@ struct qaic_device { struct srcu_struct dev_lock; /* true: Device under reset; false: Device not under reset */ bool in_reset; + /* true: single MSI is used to operate device */ + bool single_msi; /* * true: A tx MHI transaction has failed and a rx buffer is still queued * in control device. Such a buffer is considered lost rx buffer @@ -137,6 +139,10 @@ struct qaic_device { u32 (*gen_crc)(void *msg); /* Validate the CRC of a control message */ bool (*valid_crc)(void *msg); + /* MHI "QAIC_TIMESYNC" channel device */ + struct mhi_device *qts_ch; + /* Work queue for tasks related to MHI "QAIC_TIMESYNC" channel */ + struct workqueue_struct *qts_wq; }; struct qaic_drm_device { diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index 388abd40024b..84915824be54 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -1138,7 +1138,7 @@ static int abort_dma_cont(struct qaic_device *qdev, struct wrapper_list *wrapper if (!list_is_first(&wrapper->list, &wrappers->list)) kref_put(&wrapper->ref_count, free_wrapper); - wrapper = add_wrapper(wrappers, offsetof(struct wrapper_msg, trans) + sizeof(*out_trans)); + wrapper = add_wrapper(wrappers, sizeof(*wrapper)); if (!wrapper) return -ENOMEM; diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 4a8e43a7a6a4..8998c28e566e 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -51,6 +51,7 @@ }) #define NUM_EVENTS 128 #define NUM_DELAYS 10 +#define fifo_at(base, offset) ((base) + (offset) * get_dbc_req_elem_size()) static unsigned int wait_exec_default_timeout_ms = 5000; /* 5 sec default */ module_param(wait_exec_default_timeout_ms, uint, 0600); @@ -451,7 +452,7 @@ static int create_sgt(struct qaic_device *qdev, struct sg_table **sgt_out, u64 s * later */ buf_extra = (PAGE_SIZE - size % PAGE_SIZE) % PAGE_SIZE; - max_order = min(MAX_ORDER - 1, get_order(size)); + max_order = min(MAX_ORDER, get_order(size)); } else { /* allocate a single page for book keeping */ nr_pages = 1; @@ -1058,6 +1059,16 @@ unlock_usr_srcu: return ret; } +static inline u32 fifo_space_avail(u32 head, u32 tail, u32 q_size) +{ + u32 avail = head - tail - 1; + + if (head <= tail) + avail += q_size; + + return avail; +} + static inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, u32 dbc_id, u32 head, u32 *ptail) { @@ -1066,27 +1077,20 @@ static inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slic u32 tail = *ptail; u32 avail; - avail = head - tail; - if (head <= tail) - avail += dbc->nelem; - - --avail; - + avail = fifo_space_avail(head, tail, dbc->nelem); if (avail < slice->nents) return -EAGAIN; if (tail + slice->nents > dbc->nelem) { avail = dbc->nelem - tail; avail = min_t(u32, avail, slice->nents); - memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, - sizeof(*reqs) * avail); + memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * avail); reqs += avail; avail = slice->nents - avail; if (avail) memcpy(dbc->req_q_base, reqs, sizeof(*reqs) * avail); } else { - memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, - sizeof(*reqs) * slice->nents); + memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * slice->nents); } *ptail = (tail + slice->nents) % dbc->nelem; @@ -1094,46 +1098,31 @@ static inline int copy_exec_reqs(struct qaic_device *qdev, struct bo_slice *slic return 0; } -/* - * Based on the value of resize we may only need to transmit first_n - * entries and the last entry, with last_bytes to send from the last entry. - * Note that first_n could be 0. - */ static inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_slice *slice, - u64 resize, u32 dbc_id, u32 head, u32 *ptail) + u64 resize, struct dma_bridge_chan *dbc, u32 head, + u32 *ptail) { - struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id]; struct dbc_req *reqs = slice->reqs; struct dbc_req *last_req; u32 tail = *ptail; - u64 total_bytes; u64 last_bytes; u32 first_n; u32 avail; - int ret; - int i; - - avail = head - tail; - if (head <= tail) - avail += dbc->nelem; - --avail; + avail = fifo_space_avail(head, tail, dbc->nelem); - total_bytes = 0; - for (i = 0; i < slice->nents; i++) { - total_bytes += le32_to_cpu(reqs[i].len); - if (total_bytes >= resize) + /* + * After this for loop is complete, first_n represents the index + * of the last DMA request of this slice that needs to be + * transferred after resizing and last_bytes represents DMA size + * of that request. + */ + last_bytes = resize; + for (first_n = 0; first_n < slice->nents; first_n++) + if (last_bytes > le32_to_cpu(reqs[first_n].len)) + last_bytes -= le32_to_cpu(reqs[first_n].len); + else break; - } - - if (total_bytes < resize) { - /* User space should have used the full buffer path. */ - ret = -EINVAL; - return ret; - } - - first_n = i; - last_bytes = i ? resize + le32_to_cpu(reqs[i].len) - total_bytes : resize; if (avail < (first_n + 1)) return -EAGAIN; @@ -1142,22 +1131,21 @@ static inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_sli if (tail + first_n > dbc->nelem) { avail = dbc->nelem - tail; avail = min_t(u32, avail, first_n); - memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, - sizeof(*reqs) * avail); + memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * avail); last_req = reqs + avail; avail = first_n - avail; if (avail) memcpy(dbc->req_q_base, last_req, sizeof(*reqs) * avail); } else { - memcpy(dbc->req_q_base + tail * get_dbc_req_elem_size(), reqs, - sizeof(*reqs) * first_n); + memcpy(fifo_at(dbc->req_q_base, tail), reqs, sizeof(*reqs) * first_n); } } - /* Copy over the last entry. Here we need to adjust len to the left over + /* + * Copy over the last entry. Here we need to adjust len to the left over * size, and set src and dst to the entry it is copied to. */ - last_req = dbc->req_q_base + (tail + first_n) % dbc->nelem * get_dbc_req_elem_size(); + last_req = fifo_at(dbc->req_q_base, (tail + first_n) % dbc->nelem); memcpy(last_req, reqs + slice->nents - 1, sizeof(*reqs)); /* @@ -1168,6 +1156,9 @@ static inline int copy_partial_exec_reqs(struct qaic_device *qdev, struct bo_sli last_req->len = cpu_to_le32((u32)last_bytes); last_req->src_addr = reqs[first_n].src_addr; last_req->dest_addr = reqs[first_n].dest_addr; + if (!last_bytes) + /* Disable DMA transfer */ + last_req->cmd = GENMASK(7, 2) & reqs[first_n].cmd; *ptail = (tail + first_n + 1) % dbc->nelem; @@ -1227,26 +1218,17 @@ static int send_bo_list_to_device(struct qaic_device *qdev, struct drm_file *fil bo->req_id = dbc->next_req_id++; list_for_each_entry(slice, &bo->slices, slice) { - /* - * If this slice does not fall under the given - * resize then skip this slice and continue the loop - */ - if (is_partial && pexec[i].resize && pexec[i].resize <= slice->offset) - continue; - for (j = 0; j < slice->nents; j++) slice->reqs[j].req_id = cpu_to_le16(bo->req_id); - /* - * If it is a partial execute ioctl call then check if - * resize has cut this slice short then do a partial copy - * else do complete copy - */ - if (is_partial && pexec[i].resize && - pexec[i].resize < slice->offset + slice->size) + if (is_partial && (!pexec[i].resize || pexec[i].resize <= slice->offset)) + /* Configure the slice for no DMA transfer */ + ret = copy_partial_exec_reqs(qdev, slice, 0, dbc, head, tail); + else if (is_partial && pexec[i].resize < slice->offset + slice->size) + /* Configure the slice to be partially DMA transferred */ ret = copy_partial_exec_reqs(qdev, slice, - pexec[i].resize - slice->offset, - dbc->id, head, tail); + pexec[i].resize - slice->offset, dbc, + head, tail); else ret = copy_exec_reqs(qdev, slice, dbc->id, head, tail); if (ret) { @@ -1466,6 +1448,16 @@ irqreturn_t dbc_irq_handler(int irq, void *data) rcu_id = srcu_read_lock(&dbc->ch_lock); + if (datapath_polling) { + srcu_read_unlock(&dbc->ch_lock, rcu_id); + /* + * Normally datapath_polling will not have irqs enabled, but + * when running with only one MSI the interrupt is shared with + * MHI so it cannot be disabled. Return ASAP instead. + */ + return IRQ_HANDLED; + } + if (!dbc->usr) { srcu_read_unlock(&dbc->ch_lock, rcu_id); return IRQ_HANDLED; @@ -1488,7 +1480,8 @@ irqreturn_t dbc_irq_handler(int irq, void *data) return IRQ_NONE; } - disable_irq_nosync(irq); + if (!dbc->qdev->single_msi) + disable_irq_nosync(irq); srcu_read_unlock(&dbc->ch_lock, rcu_id); return IRQ_WAKE_THREAD; } @@ -1559,12 +1552,12 @@ irqreturn_t dbc_irq_threaded_fn(int irq, void *data) u32 tail; rcu_id = srcu_read_lock(&dbc->ch_lock); + qdev = dbc->qdev; head = readl(dbc->dbc_base + RSPHP_OFF); if (head == U32_MAX) /* PCI link error */ goto error_out; - qdev = dbc->qdev; read_fifo: if (!event_count) { @@ -1645,14 +1638,14 @@ read_fifo: goto read_fifo; normal_out: - if (likely(!datapath_polling)) + if (!qdev->single_msi && likely(!datapath_polling)) enable_irq(irq); - else + else if (unlikely(datapath_polling)) schedule_work(&dbc->poll_work); /* checking the fifo and enabling irqs is a race, missed event check */ tail = readl(dbc->dbc_base + RSPTP_OFF); if (tail != U32_MAX && head != tail) { - if (likely(!datapath_polling)) + if (!qdev->single_msi && likely(!datapath_polling)) disable_irq_nosync(irq); goto read_fifo; } @@ -1661,9 +1654,9 @@ normal_out: error_out: srcu_read_unlock(&dbc->ch_lock, rcu_id); - if (likely(!datapath_polling)) + if (!qdev->single_msi && likely(!datapath_polling)) enable_irq(irq); - else + else if (unlikely(datapath_polling)) schedule_work(&dbc->poll_work); return IRQ_HANDLED; diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index 6f58095767df..b12226385003 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -27,6 +27,7 @@ #include "mhi_controller.h" #include "qaic.h" +#include "qaic_timesync.h" MODULE_IMPORT_NS(DMA_BUF); @@ -324,6 +325,7 @@ static void cleanup_qdev(struct qaic_device *qdev) cleanup_srcu_struct(&qdev->dev_lock); pci_set_drvdata(qdev->pdev, NULL); destroy_workqueue(qdev->cntl_wq); + destroy_workqueue(qdev->qts_wq); } static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_device_id *id) @@ -347,6 +349,12 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_de if (!qdev->cntl_wq) return NULL; + qdev->qts_wq = alloc_workqueue("qaic_ts", WQ_UNBOUND, 0); + if (!qdev->qts_wq) { + destroy_workqueue(qdev->cntl_wq); + return NULL; + } + pci_set_drvdata(pdev, qdev); qdev->pdev = pdev; @@ -424,14 +432,24 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev) int i; /* Managed release since we use pcim_enable_device */ - ret = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); - if (ret < 0) - return ret; + ret = pci_alloc_irq_vectors(pdev, 32, 32, PCI_IRQ_MSI); + if (ret == -ENOSPC) { + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (ret < 0) + return ret; - if (ret < 32) { - pci_err(pdev, "%s: Requested 32 MSIs. Obtained %d MSIs which is less than the 32 required.\n", - __func__, ret); - return -ENODEV; + /* + * Operate in one MSI mode. All interrupts will be directed to + * MSI0; every interrupt will wake up all the interrupt handlers + * (MHI and DBC[0-15]). Since the interrupt is now shared, it is + * not disabled during DBC threaded handler, but only one thread + * will be allowed to run per DBC, so while it can be + * interrupted, it shouldn't race with itself. + */ + qdev->single_msi = true; + pci_info(pdev, "Allocating 32 MSIs failed, operating in 1 MSI mode. Performance may be impacted.\n"); + } else if (ret < 0) { + return ret; } mhi_irq = pci_irq_vector(pdev, 0); @@ -439,15 +457,17 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev) return mhi_irq; for (i = 0; i < qdev->num_dbc; ++i) { - ret = devm_request_threaded_irq(&pdev->dev, pci_irq_vector(pdev, i + 1), + ret = devm_request_threaded_irq(&pdev->dev, + pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1), dbc_irq_handler, dbc_irq_threaded_fn, IRQF_SHARED, "qaic_dbc", &qdev->dbc[i]); if (ret) return ret; if (datapath_polling) { - qdev->dbc[i].irq = pci_irq_vector(pdev, i + 1); - disable_irq_nosync(qdev->dbc[i].irq); + qdev->dbc[i].irq = pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1); + if (!qdev->single_msi) + disable_irq_nosync(qdev->dbc[i].irq); INIT_WORK(&qdev->dbc[i].poll_work, irq_polling_work); } } @@ -479,7 +499,8 @@ static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto cleanup_qdev; } - qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_0, mhi_irq); + qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_0, mhi_irq, + qdev->single_msi); if (IS_ERR(qdev->mhi_cntrl)) { ret = PTR_ERR(qdev->mhi_cntrl); goto cleanup_qdev; @@ -586,6 +607,10 @@ static int __init qaic_init(void) goto free_pci; } + ret = qaic_timesync_init(); + if (ret) + pr_debug("qaic: qaic_timesync_init failed %d\n", ret); + return 0; free_pci: @@ -611,6 +636,7 @@ static void __exit qaic_exit(void) * reinitializing the link_up state after the cleanup is done. */ link_up = true; + qaic_timesync_deinit(); mhi_driver_unregister(&qaic_mhi_driver); pci_unregister_driver(&qaic_pci_driver); } diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c new file mode 100644 index 000000000000..301f4462d51b --- /dev/null +++ b/drivers/accel/qaic/qaic_timesync.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/mhi.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/time64.h> +#include <linux/timer.h> + +#include "qaic.h" +#include "qaic_timesync.h" + +#define QTIMER_REG_OFFSET 0xa28 +#define QAIC_TIMESYNC_SIGNATURE 0x55aa +#define QAIC_CONV_QTIMER_TO_US(qtimer) (mul_u64_u32_div(qtimer, 10, 192)) + +static unsigned int timesync_delay_ms = 1000; /* 1 sec default */ +module_param(timesync_delay_ms, uint, 0600); +MODULE_PARM_DESC(timesync_delay_ms, "Delay in ms between two consecutive timesync operations"); + +enum qts_msg_type { + QAIC_TS_CMD_TO_HOST, + QAIC_TS_SYNC_REQ, + QAIC_TS_ACK_TO_HOST, + QAIC_TS_MSG_TYPE_MAX +}; + +/** + * struct qts_hdr - Timesync message header structure. + * @signature: Unique signature to identify the timesync message. + * @reserved_1: Reserved for future use. + * @reserved_2: Reserved for future use. + * @msg_type: sub-type of the timesync message. + * @reserved_3: Reserved for future use. + */ +struct qts_hdr { + __le16 signature; + __le16 reserved_1; + u8 reserved_2; + u8 msg_type; + __le16 reserved_3; +} __packed; + +/** + * struct qts_timeval - Structure to carry time information. + * @tv_sec: Seconds part of the time. + * @tv_usec: uS (microseconds) part of the time. + */ +struct qts_timeval { + __le64 tv_sec; + __le64 tv_usec; +} __packed; + +/** + * struct qts_host_time_sync_msg_data - Structure to denote the timesync message. + * @header: Header of the timesync message. + * @data: Time information. + */ +struct qts_host_time_sync_msg_data { + struct qts_hdr header; + struct qts_timeval data; +} __packed; + +/** + * struct mqts_dev - MHI QAIC Timesync Control device. + * @qdev: Pointer to the root device struct driven by QAIC driver. + * @mhi_dev: Pointer to associated MHI device. + * @timer: Timer handle used for timesync. + * @qtimer_addr: Device QTimer register pointer. + * @buff_in_use: atomic variable to track if the sync_msg buffer is in use. + * @dev: Device pointer to qdev->pdev->dev stored for easy access. + * @sync_msg: Buffer used to send timesync message over MHI. + */ +struct mqts_dev { + struct qaic_device *qdev; + struct mhi_device *mhi_dev; + struct timer_list timer; + void __iomem *qtimer_addr; + atomic_t buff_in_use; + struct device *dev; + struct qts_host_time_sync_msg_data *sync_msg; +}; + +struct qts_resp_msg { + struct qts_hdr hdr; +} __packed; + +struct qts_resp { + struct qts_resp_msg data; + struct work_struct work; + struct qaic_device *qdev; +}; + +#ifdef readq +static u64 read_qtimer(const volatile void __iomem *addr) +{ + return readq(addr); +} +#else +static u64 read_qtimer(const volatile void __iomem *addr) +{ + u64 low, high; + + low = readl(addr); + high = readl(addr + sizeof(u32)); + return low | (high << 32); +} +#endif + +static void qaic_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) +{ + struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev); + + dev_dbg(mqtsdev->dev, "%s status: %d xfer_len: %zu\n", __func__, + mhi_result->transaction_status, mhi_result->bytes_xferd); + + atomic_set(&mqtsdev->buff_in_use, 0); +} + +static void qaic_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) +{ + struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev); + + dev_err(mqtsdev->dev, "%s no data expected on dl channel\n", __func__); +} + +static void qaic_timesync_timer(struct timer_list *t) +{ + struct mqts_dev *mqtsdev = from_timer(mqtsdev, t, timer); + struct qts_host_time_sync_msg_data *sync_msg; + u64 device_qtimer_us; + u64 device_qtimer; + u64 host_time_us; + u64 offset_us; + u64 host_sec; + int ret; + + if (atomic_read(&mqtsdev->buff_in_use)) { + dev_dbg(mqtsdev->dev, "%s buffer not free, schedule next cycle\n", __func__); + goto mod_timer; + } + atomic_set(&mqtsdev->buff_in_use, 1); + + sync_msg = mqtsdev->sync_msg; + sync_msg->header.signature = cpu_to_le16(QAIC_TIMESYNC_SIGNATURE); + sync_msg->header.msg_type = QAIC_TS_SYNC_REQ; + /* Read host UTC time and convert to uS*/ + host_time_us = div_u64(ktime_get_real_ns(), NSEC_PER_USEC); + device_qtimer = read_qtimer(mqtsdev->qtimer_addr); + device_qtimer_us = QAIC_CONV_QTIMER_TO_US(device_qtimer); + /* Offset between host UTC and device time */ + offset_us = host_time_us - device_qtimer_us; + + host_sec = div_u64(offset_us, USEC_PER_SEC); + sync_msg->data.tv_usec = cpu_to_le64(offset_us - host_sec * USEC_PER_SEC); + sync_msg->data.tv_sec = cpu_to_le64(host_sec); + ret = mhi_queue_buf(mqtsdev->mhi_dev, DMA_TO_DEVICE, sync_msg, sizeof(*sync_msg), MHI_EOT); + if (ret && (ret != -EAGAIN)) { + dev_err(mqtsdev->dev, "%s unable to queue to mhi:%d\n", __func__, ret); + return; + } else if (ret == -EAGAIN) { + atomic_set(&mqtsdev->buff_in_use, 0); + } + +mod_timer: + ret = mod_timer(t, jiffies + msecs_to_jiffies(timesync_delay_ms)); + if (ret) + dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret); +} + +static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev)); + struct mqts_dev *mqtsdev; + struct timer_list *timer; + int ret; + + mqtsdev = kzalloc(sizeof(*mqtsdev), GFP_KERNEL); + if (!mqtsdev) { + ret = -ENOMEM; + goto out; + } + + timer = &mqtsdev->timer; + mqtsdev->mhi_dev = mhi_dev; + mqtsdev->qdev = qdev; + mqtsdev->dev = &qdev->pdev->dev; + + mqtsdev->sync_msg = kzalloc(sizeof(*mqtsdev->sync_msg), GFP_KERNEL); + if (!mqtsdev->sync_msg) { + ret = -ENOMEM; + goto free_mqts_dev; + } + atomic_set(&mqtsdev->buff_in_use, 0); + + ret = mhi_prepare_for_transfer(mhi_dev); + if (ret) + goto free_sync_msg; + + /* Qtimer register pointer */ + mqtsdev->qtimer_addr = qdev->bar_0 + QTIMER_REG_OFFSET; + timer_setup(timer, qaic_timesync_timer, 0); + timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms); + add_timer(timer); + dev_set_drvdata(&mhi_dev->dev, mqtsdev); + + return 0; + +free_sync_msg: + kfree(mqtsdev->sync_msg); +free_mqts_dev: + kfree(mqtsdev); +out: + return ret; +}; + +static void qaic_timesync_remove(struct mhi_device *mhi_dev) +{ + struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev); + + del_timer_sync(&mqtsdev->timer); + mhi_unprepare_from_transfer(mqtsdev->mhi_dev); + kfree(mqtsdev->sync_msg); + kfree(mqtsdev); +} + +static const struct mhi_device_id qaic_timesync_match_table[] = { + { .chan = "QAIC_TIMESYNC_PERIODIC"}, + {}, +}; + +MODULE_DEVICE_TABLE(mhi, qaic_timesync_match_table); + +static struct mhi_driver qaic_timesync_driver = { + .id_table = qaic_timesync_match_table, + .remove = qaic_timesync_remove, + .probe = qaic_timesync_probe, + .ul_xfer_cb = qaic_timesync_ul_xfer_cb, + .dl_xfer_cb = qaic_timesync_dl_xfer_cb, + .driver = { + .name = "qaic_timesync_periodic", + }, +}; + +static void qaic_boot_timesync_worker(struct work_struct *work) +{ + struct qts_resp *resp = container_of(work, struct qts_resp, work); + struct qts_host_time_sync_msg_data *req; + struct qts_resp_msg data = resp->data; + struct qaic_device *qdev = resp->qdev; + struct mhi_device *mhi_dev; + struct timespec64 ts; + int ret; + + mhi_dev = qdev->qts_ch; + /* Queue the response message beforehand to avoid race conditions */ + ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT); + if (ret) { + kfree(resp); + dev_warn(&mhi_dev->dev, "Failed to re-queue response buffer %d\n", ret); + return; + } + + switch (data.hdr.msg_type) { + case QAIC_TS_CMD_TO_HOST: + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + break; + + req->header = data.hdr; + req->header.msg_type = QAIC_TS_SYNC_REQ; + ktime_get_real_ts64(&ts); + req->data.tv_sec = cpu_to_le64(ts.tv_sec); + req->data.tv_usec = cpu_to_le64(div_u64(ts.tv_nsec, NSEC_PER_USEC)); + + ret = mhi_queue_buf(mhi_dev, DMA_TO_DEVICE, req, sizeof(*req), MHI_EOT); + if (ret) { + kfree(req); + dev_dbg(&mhi_dev->dev, "Failed to send request message. Error %d\n", ret); + } + break; + case QAIC_TS_ACK_TO_HOST: + dev_dbg(&mhi_dev->dev, "ACK received from device\n"); + break; + default: + dev_err(&mhi_dev->dev, "Invalid message type %u.\n", data.hdr.msg_type); + } +} + +static int qaic_boot_timesync_queue_resp(struct mhi_device *mhi_dev, struct qaic_device *qdev) +{ + struct qts_resp *resp; + int ret; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + return -ENOMEM; + + resp->qdev = qdev; + INIT_WORK(&resp->work, qaic_boot_timesync_worker); + + ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, &resp->data, sizeof(resp->data), MHI_EOT); + if (ret) { + kfree(resp); + dev_warn(&mhi_dev->dev, "Failed to queue response buffer %d\n", ret); + return ret; + } + + return 0; +} + +static void qaic_boot_timesync_remove(struct mhi_device *mhi_dev) +{ + struct qaic_device *qdev; + + qdev = dev_get_drvdata(&mhi_dev->dev); + mhi_unprepare_from_transfer(qdev->qts_ch); + qdev->qts_ch = NULL; +} + +static int qaic_boot_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) +{ + struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev)); + int ret; + + ret = mhi_prepare_for_transfer(mhi_dev); + if (ret) + return ret; + + qdev->qts_ch = mhi_dev; + dev_set_drvdata(&mhi_dev->dev, qdev); + + ret = qaic_boot_timesync_queue_resp(mhi_dev, qdev); + if (ret) { + dev_set_drvdata(&mhi_dev->dev, NULL); + qdev->qts_ch = NULL; + mhi_unprepare_from_transfer(mhi_dev); + } + + return ret; +} + +static void qaic_boot_timesync_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) +{ + kfree(mhi_result->buf_addr); +} + +static void qaic_boot_timesync_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) +{ + struct qts_resp *resp = container_of(mhi_result->buf_addr, struct qts_resp, data); + + if (mhi_result->transaction_status || mhi_result->bytes_xferd != sizeof(resp->data)) { + kfree(resp); + return; + } + + queue_work(resp->qdev->qts_wq, &resp->work); +} + +static const struct mhi_device_id qaic_boot_timesync_match_table[] = { + { .chan = "QAIC_TIMESYNC"}, + {}, +}; + +static struct mhi_driver qaic_boot_timesync_driver = { + .id_table = qaic_boot_timesync_match_table, + .remove = qaic_boot_timesync_remove, + .probe = qaic_boot_timesync_probe, + .ul_xfer_cb = qaic_boot_timesync_ul_xfer_cb, + .dl_xfer_cb = qaic_boot_timesync_dl_xfer_cb, + .driver = { + .name = "qaic_timesync", + }, +}; + +int qaic_timesync_init(void) +{ + int ret; + + ret = mhi_driver_register(&qaic_timesync_driver); + if (ret) + return ret; + ret = mhi_driver_register(&qaic_boot_timesync_driver); + + return ret; +} + +void qaic_timesync_deinit(void) +{ + mhi_driver_unregister(&qaic_boot_timesync_driver); + mhi_driver_unregister(&qaic_timesync_driver); +} diff --git a/drivers/accel/qaic/qaic_timesync.h b/drivers/accel/qaic/qaic_timesync.h new file mode 100644 index 000000000000..851b7acd43bb --- /dev/null +++ b/drivers/accel/qaic/qaic_timesync.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __QAIC_TIMESYNC_H__ +#define __QAIC_TIMESYNC_H__ + +int qaic_timesync_init(void); +void qaic_timesync_deinit(void); +#endif /* __QAIC_TIMESYNC_H__ */ diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 0b7a01f38b65..d321ca7160d9 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -2031,7 +2031,7 @@ static int acpi_video_bus_add(struct acpi_device *device) * HP ZBook Fury 16 G10 requires ACPI video's child devices have _PS0 * evaluated to have functional panel brightness control. */ - acpi_device_fix_up_power_extended(device); + acpi_device_fix_up_power_children(device); pr_info("%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index f007116a8427..3b4d048c4941 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -397,6 +397,19 @@ void acpi_device_fix_up_power_extended(struct acpi_device *adev) } EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_extended); +/** + * acpi_device_fix_up_power_children - Force a device's children into D0. + * @adev: Parent device object whose children's power state is to be fixed up. + * + * Call acpi_device_fix_up_power() for @adev's children so long as they + * are reported as present and enabled. + */ +void acpi_device_fix_up_power_children(struct acpi_device *adev) +{ + acpi_dev_for_each_child(adev, fix_up_power_if_applicable, NULL); +} +EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_children); + int acpi_device_update_power(struct acpi_device *device, int *state_p) { int state; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3a34a8c425fe..55437f5e0c3a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -592,7 +592,7 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) while (1) { if (cx->entry_method == ACPI_CSTATE_HALT) - safe_halt(); + raw_safe_halt(); else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { io_idle(cx->address); } else diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 15a3bdbd0755..9bd9f79cd409 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -448,6 +448,13 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = { }, }, { + /* Asus ExpertBook B1402CVA */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_BOARD_NAME, "B1402CVA"), + }, + }, + { /* Asus ExpertBook B1502CBA */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index 25a63d043c8e..0f77e0424066 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -82,6 +82,9 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev if (pnp_port_valid(idev, 1)) { ctl_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 1), 1); + if (!ctl_addr) + return -ENOMEM; + ap->ioaddr.altstatus_addr = ctl_addr; ap->ioaddr.ctl_addr = ctl_addr; ap->ops = &isapnp_port_ops; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 855fdf5c3b4e..b6414e1e645b 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -67,6 +67,7 @@ struct nbd_sock { struct recv_thread_args { struct work_struct work; struct nbd_device *nbd; + struct nbd_sock *nsock; int index; }; @@ -395,6 +396,22 @@ static u32 req_to_nbd_cmd_type(struct request *req) } } +static struct nbd_config *nbd_get_config_unlocked(struct nbd_device *nbd) +{ + if (refcount_inc_not_zero(&nbd->config_refs)) { + /* + * Add smp_mb__after_atomic to ensure that reading nbd->config_refs + * and reading nbd->config is ordered. The pair is the barrier in + * nbd_alloc_and_init_config(), avoid nbd->config_refs is set + * before nbd->config. + */ + smp_mb__after_atomic(); + return nbd->config; + } + + return NULL; +} + static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req) { struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req); @@ -409,13 +426,13 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req) return BLK_EH_DONE; } - if (!refcount_inc_not_zero(&nbd->config_refs)) { + config = nbd_get_config_unlocked(nbd); + if (!config) { cmd->status = BLK_STS_TIMEOUT; __clear_bit(NBD_CMD_INFLIGHT, &cmd->flags); mutex_unlock(&cmd->lock); goto done; } - config = nbd->config; if (config->num_connections > 1 || (config->num_connections == 1 && nbd->tag_set.timeout)) { @@ -489,15 +506,9 @@ done: return BLK_EH_DONE; } -/* - * Send or receive packet. Return a positive value on success and - * negtive value on failue, and never return 0. - */ -static int sock_xmit(struct nbd_device *nbd, int index, int send, - struct iov_iter *iter, int msg_flags, int *sent) +static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send, + struct iov_iter *iter, int msg_flags, int *sent) { - struct nbd_config *config = nbd->config; - struct socket *sock = config->socks[index]->sock; int result; struct msghdr msg; unsigned int noreclaim_flag; @@ -541,6 +552,19 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send, } /* + * Send or receive packet. Return a positive value on success and + * negtive value on failure, and never return 0. + */ +static int sock_xmit(struct nbd_device *nbd, int index, int send, + struct iov_iter *iter, int msg_flags, int *sent) +{ + struct nbd_config *config = nbd->config; + struct socket *sock = config->socks[index]->sock; + + return __sock_xmit(nbd, sock, send, iter, msg_flags, sent); +} + +/* * Different settings for sk->sk_sndtimeo can result in different return values * if there is a signal pending when we enter sendmsg, because reasons? */ @@ -696,7 +720,7 @@ out: return 0; } -static int nbd_read_reply(struct nbd_device *nbd, int index, +static int nbd_read_reply(struct nbd_device *nbd, struct socket *sock, struct nbd_reply *reply) { struct kvec iov = {.iov_base = reply, .iov_len = sizeof(*reply)}; @@ -705,7 +729,7 @@ static int nbd_read_reply(struct nbd_device *nbd, int index, reply->magic = 0; iov_iter_kvec(&to, ITER_DEST, &iov, 1, sizeof(*reply)); - result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL); + result = __sock_xmit(nbd, sock, 0, &to, MSG_WAITALL, NULL); if (result < 0) { if (!nbd_disconnected(nbd->config)) dev_err(disk_to_dev(nbd->disk), @@ -829,14 +853,14 @@ static void recv_work(struct work_struct *work) struct nbd_device *nbd = args->nbd; struct nbd_config *config = nbd->config; struct request_queue *q = nbd->disk->queue; - struct nbd_sock *nsock; + struct nbd_sock *nsock = args->nsock; struct nbd_cmd *cmd; struct request *rq; while (1) { struct nbd_reply reply; - if (nbd_read_reply(nbd, args->index, &reply)) + if (nbd_read_reply(nbd, nsock->sock, &reply)) break; /* @@ -871,7 +895,6 @@ static void recv_work(struct work_struct *work) percpu_ref_put(&q->q_usage_counter); } - nsock = config->socks[args->index]; mutex_lock(&nsock->tx_lock); nbd_mark_nsock_dead(nbd, nsock, 1); mutex_unlock(&nsock->tx_lock); @@ -977,12 +1000,12 @@ static int nbd_handle_cmd(struct nbd_cmd *cmd, int index) struct nbd_sock *nsock; int ret; - if (!refcount_inc_not_zero(&nbd->config_refs)) { + config = nbd_get_config_unlocked(nbd); + if (!config) { dev_err_ratelimited(disk_to_dev(nbd->disk), "Socks array is empty\n"); return -EINVAL; } - config = nbd->config; if (index >= config->num_connections) { dev_err_ratelimited(disk_to_dev(nbd->disk), @@ -1215,6 +1238,7 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg) INIT_WORK(&args->work, recv_work); args->index = i; args->nbd = nbd; + args->nsock = nsock; nsock->cookie++; mutex_unlock(&nsock->tx_lock); sockfd_put(old); @@ -1397,6 +1421,7 @@ static int nbd_start_device(struct nbd_device *nbd) refcount_inc(&nbd->config_refs); INIT_WORK(&args->work, recv_work); args->nbd = nbd; + args->nsock = config->socks[i]; args->index = i; queue_work(nbd->recv_workq, &args->work); } @@ -1530,17 +1555,20 @@ static int nbd_ioctl(struct block_device *bdev, blk_mode_t mode, return error; } -static struct nbd_config *nbd_alloc_config(void) +static int nbd_alloc_and_init_config(struct nbd_device *nbd) { struct nbd_config *config; + if (WARN_ON(nbd->config)) + return -EINVAL; + if (!try_module_get(THIS_MODULE)) - return ERR_PTR(-ENODEV); + return -ENODEV; config = kzalloc(sizeof(struct nbd_config), GFP_NOFS); if (!config) { module_put(THIS_MODULE); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } atomic_set(&config->recv_threads, 0); @@ -1548,12 +1576,24 @@ static struct nbd_config *nbd_alloc_config(void) init_waitqueue_head(&config->conn_wait); config->blksize_bits = NBD_DEF_BLKSIZE_BITS; atomic_set(&config->live_connections, 0); - return config; + + nbd->config = config; + /* + * Order refcount_set(&nbd->config_refs, 1) and nbd->config assignment, + * its pair is the barrier in nbd_get_config_unlocked(). + * So nbd_get_config_unlocked() won't see nbd->config as null after + * refcount_inc_not_zero() succeed. + */ + smp_mb__before_atomic(); + refcount_set(&nbd->config_refs, 1); + + return 0; } static int nbd_open(struct gendisk *disk, blk_mode_t mode) { struct nbd_device *nbd; + struct nbd_config *config; int ret = 0; mutex_lock(&nbd_index_mutex); @@ -1566,27 +1606,25 @@ static int nbd_open(struct gendisk *disk, blk_mode_t mode) ret = -ENXIO; goto out; } - if (!refcount_inc_not_zero(&nbd->config_refs)) { - struct nbd_config *config; + config = nbd_get_config_unlocked(nbd); + if (!config) { mutex_lock(&nbd->config_lock); if (refcount_inc_not_zero(&nbd->config_refs)) { mutex_unlock(&nbd->config_lock); goto out; } - config = nbd_alloc_config(); - if (IS_ERR(config)) { - ret = PTR_ERR(config); + ret = nbd_alloc_and_init_config(nbd); + if (ret) { mutex_unlock(&nbd->config_lock); goto out; } - nbd->config = config; - refcount_set(&nbd->config_refs, 1); + refcount_inc(&nbd->refs); mutex_unlock(&nbd->config_lock); if (max_part) set_bit(GD_NEED_PART_SCAN, &disk->state); - } else if (nbd_disconnected(nbd->config)) { + } else if (nbd_disconnected(config)) { if (max_part) set_bit(GD_NEED_PART_SCAN, &disk->state); } @@ -1990,22 +2028,17 @@ again: pr_err("nbd%d already in use\n", index); return -EBUSY; } - if (WARN_ON(nbd->config)) { - mutex_unlock(&nbd->config_lock); - nbd_put(nbd); - return -EINVAL; - } - config = nbd_alloc_config(); - if (IS_ERR(config)) { + + ret = nbd_alloc_and_init_config(nbd); + if (ret) { mutex_unlock(&nbd->config_lock); nbd_put(nbd); pr_err("couldn't allocate config\n"); - return PTR_ERR(config); + return ret; } - nbd->config = config; - refcount_set(&nbd->config_refs, 1); - set_bit(NBD_RT_BOUND, &config->runtime_flags); + config = nbd->config; + set_bit(NBD_RT_BOUND, &config->runtime_flags); ret = nbd_genl_size_set(info, nbd); if (ret) goto out; @@ -2208,7 +2241,8 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) } mutex_unlock(&nbd_index_mutex); - if (!refcount_inc_not_zero(&nbd->config_refs)) { + config = nbd_get_config_unlocked(nbd); + if (!config) { dev_err(nbd_to_dev(nbd), "not configured, cannot reconfigure\n"); nbd_put(nbd); @@ -2216,7 +2250,6 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info) } mutex_lock(&nbd->config_lock); - config = nbd->config; if (!test_bit(NBD_RT_BOUND, &config->runtime_flags) || !nbd->pid) { dev_err(nbd_to_dev(nbd), diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 22a3cf7f32e2..3021d58ca51c 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1464,19 +1464,13 @@ blk_status_t null_process_cmd(struct nullb_cmd *cmd, enum req_op op, return BLK_STS_OK; } -static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, - sector_t nr_sectors, enum req_op op) +static void null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, + sector_t nr_sectors, enum req_op op) { struct nullb_device *dev = cmd->nq->dev; struct nullb *nullb = dev->nullb; blk_status_t sts; - if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) { - sts = null_handle_throttled(cmd); - if (sts != BLK_STS_OK) - return sts; - } - if (op == REQ_OP_FLUSH) { cmd->error = errno_to_blk_status(null_handle_flush(nullb)); goto out; @@ -1493,7 +1487,6 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, out: nullb_complete_cmd(cmd); - return BLK_STS_OK; } static enum hrtimer_restart nullb_bwtimer_fn(struct hrtimer *timer) @@ -1724,8 +1717,6 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx, cmd->fake_timeout = should_timeout_request(rq) || blk_should_fake_timeout(rq->q); - blk_mq_start_request(rq); - if (should_requeue_request(rq)) { /* * Alternate between hitting the core BUSY path, and the @@ -1738,6 +1729,15 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx, return BLK_STS_OK; } + if (test_bit(NULLB_DEV_FL_THROTTLED, &nq->dev->flags)) { + blk_status_t sts = null_handle_throttled(cmd); + + if (sts != BLK_STS_OK) + return sts; + } + + blk_mq_start_request(rq); + if (is_poll) { spin_lock(&nq->poll_lock); list_add_tail(&rq->queuelist, &nq->poll_list); @@ -1747,7 +1747,8 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx, if (cmd->fake_timeout) return BLK_STS_OK; - return null_handle_cmd(cmd, sector, nr_sectors, req_op(rq)); + null_handle_cmd(cmd, sector, nr_sectors, req_op(rq)); + return BLK_STS_OK; } static void null_queue_rqs(struct request **rqlist) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 21916bba77d5..8fe5aa67b167 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -46,12 +46,12 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen) { struct dma_buf *dmabuf; char name[DMA_BUF_NAME_LEN]; - size_t ret = 0; + ssize_t ret = 0; dmabuf = dentry->d_fsdata; spin_lock(&dmabuf->name_lock); if (dmabuf->name) - ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN); + ret = strscpy(name, dmabuf->name, sizeof(name)); spin_unlock(&dmabuf->name_lock); return dynamic_dname(buffer, buflen, "/%s:%s", diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c index a6dc3997bf5c..442a0ebeb953 100644 --- a/drivers/dpll/dpll_netlink.c +++ b/drivers/dpll/dpll_netlink.c @@ -1093,9 +1093,10 @@ int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0, DPLL_CMD_PIN_ID_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(msg); return -EMSGSIZE; - + } pin = dpll_pin_find_from_nlattr(info); if (!IS_ERR(pin)) { ret = dpll_msg_add_pin_handle(msg, pin); @@ -1123,8 +1124,10 @@ int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0, DPLL_CMD_PIN_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(msg); return -EMSGSIZE; + } ret = dpll_cmd_pin_get_one(msg, pin, info->extack); if (ret) { nlmsg_free(msg); @@ -1256,8 +1259,10 @@ int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0, DPLL_CMD_DEVICE_ID_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(msg); return -EMSGSIZE; + } dpll = dpll_device_find_from_nlattr(info); if (!IS_ERR(dpll)) { @@ -1284,8 +1289,10 @@ int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0, DPLL_CMD_DEVICE_GET); - if (!hdr) + if (!hdr) { + nlmsg_free(msg); return -EMSGSIZE; + } ret = dpll_device_get_one(dpll, msg, info->extack); if (ret) { diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 74d00b0c83fe..4a98a859d44d 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -131,7 +131,7 @@ config RASPBERRYPI_FIRMWARE config FW_CFG_SYSFS tristate "QEMU fw_cfg device support in sysfs" - depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || SPARC || X86) + depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || RISCV || SPARC || X86) depends on HAS_IOPORT_MAP default n help diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a69399a6b7c0..1448f61173b3 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -211,7 +211,7 @@ static void fw_cfg_io_cleanup(void) /* arch-specific ctrl & data register offsets are not available in ACPI, DT */ #if !(defined(FW_CFG_CTRL_OFF) && defined(FW_CFG_DATA_OFF)) -# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64)) +# if (defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_RISCV)) # define FW_CFG_CTRL_OFF 0x08 # define FW_CFG_DATA_OFF 0x00 # define FW_CFG_DMA_OFF 0x10 diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3eee8636f847..740c1c0bd068 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -75,15 +75,15 @@ config DRM_KUNIT_TEST_HELPERS config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS depends on DRM && KUNIT - select PRIME_NUMBERS + select DRM_BUDDY select DRM_DISPLAY_DP_HELPER select DRM_DISPLAY_HELPER - select DRM_LIB_RANDOM - select DRM_KMS_HELPER - select DRM_BUDDY + select DRM_EXEC select DRM_EXPORT_FOR_TESTS if m + select DRM_KMS_HELPER select DRM_KUNIT_TEST_HELPERS - select DRM_EXEC + select DRM_LIB_RANDOM + select PRIME_NUMBERS default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for @@ -394,6 +394,8 @@ source "drivers/gpu/drm/solomon/Kconfig" source "drivers/gpu/drm/sprd/Kconfig" +source "drivers/gpu/drm/imagination/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && MMU && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 8e1bde059170..b4cb0835620a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -22,6 +22,7 @@ drm-y := \ drm_drv.o \ drm_dumb_buffers.o \ drm_edid.o \ + drm_eld.o \ drm_encoder.o \ drm_file.o \ drm_fourcc.o \ @@ -198,3 +199,4 @@ obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ obj-$(CONFIG_DRM_LOONGSON) += loongson/ +obj-$(CONFIG_DRM_POWERVR) += imagination/ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c index 0ba15dcbe4e1..899e31e3a5e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_arcturus.c @@ -290,7 +290,7 @@ static int suspend_resume_compute_scheduler(struct amdgpu_device *adev, bool sus for (i = 0; i < adev->gfx.num_compute_rings; i++) { struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - if (!(ring && ring->sched.thread)) + if (!(ring && drm_sched_wqueue_ready(&ring->sched))) continue; /* stop secheduler and drain ring. */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 0e61ebdb3f3e..c480192e33f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1671,9 +1671,9 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused) for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; - kthread_park(ring->sched.thread); + drm_sched_wqueue_stop(&ring->sched); } seq_puts(m, "run ib test:\n"); @@ -1687,9 +1687,9 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused) for (i = 0; i < AMDGPU_MAX_RINGS; i++) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; - kthread_unpark(ring->sched.thread); + drm_sched_wqueue_start(&ring->sched); } up_write(&adev->reset_domain->sem); @@ -1909,7 +1909,8 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val) ring = adev->rings[val]; - if (!ring || !ring->funcs->preempt_ib || !ring->sched.thread) + if (!ring || !ring->funcs->preempt_ib || + !drm_sched_wqueue_ready(&ring->sched)) return -EINVAL; /* the last preemption failed */ @@ -1927,7 +1928,7 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val) goto pro_end; /* stop the scheduler */ - kthread_park(ring->sched.thread); + drm_sched_wqueue_stop(&ring->sched); /* preempt the IB */ r = amdgpu_ring_preempt_ib(ring); @@ -1961,7 +1962,7 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val) failure: /* restart the scheduler */ - kthread_unpark(ring->sched.thread); + drm_sched_wqueue_start(&ring->sched); up_read(&adev->reset_domain->sem); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index f29d0faf956e..6c0cf64d465a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2636,7 +2636,7 @@ static int amdgpu_device_init_schedulers(struct amdgpu_device *adev) break; } - r = drm_sched_init(&ring->sched, &amdgpu_sched_ops, + r = drm_sched_init(&ring->sched, &amdgpu_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, ring->num_hw_submission, 0, timeout, adev->reset_domain->wq, @@ -5041,7 +5041,7 @@ bool amdgpu_device_has_job_running(struct amdgpu_device *adev) for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; spin_lock(&ring->sched.job_list_lock); @@ -5180,7 +5180,7 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; /* Clear job fence from fence drv to avoid force_completion @@ -5669,7 +5669,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = tmp_adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; drm_sched_stop(&ring->sched, job ? &job->base : NULL); @@ -5745,7 +5745,7 @@ skip_hw_reset: for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = tmp_adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; drm_sched_start(&ring->sched, true); @@ -6101,7 +6101,7 @@ pci_ers_result_t amdgpu_pci_error_detected(struct pci_dev *pdev, pci_channel_sta for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; drm_sched_stop(&ring->sched, NULL); @@ -6229,7 +6229,7 @@ void amdgpu_pci_resume(struct pci_dev *pdev) for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { struct amdgpu_ring *ring = adev->rings[i]; - if (!ring || !ring->sched.thread) + if (!ring || !drm_sched_wqueue_ready(&ring->sched)) continue; drm_sched_start(&ring->sched, true); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index 1f357198533f..62bb7fc7448a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -115,7 +115,7 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (!entity) return 0; - return drm_sched_job_init(&(*job)->base, entity, owner); + return drm_sched_job_init(&(*job)->base, entity, 1, owner); } int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, 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 39a4b47b6804..bc7b8a325353 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -85,8 +85,10 @@ #include <drm/drm_atomic_uapi.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_blend.h> +#include <drm/drm_fixed.h> #include <drm/drm_fourcc.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/drm_vblank.h> #include <drm/drm_audio_component.h> #include <drm/drm_gem_atomic_helper.h> @@ -6892,8 +6894,8 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, if (IS_ERR(mst_state)) return PTR_ERR(mst_state); - if (!mst_state->pbn_div) - mst_state->pbn_div = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link); + if (!mst_state->pbn_div.full) + mst_state->pbn_div.full = dfixed_const(dm_mst_get_pbn_divider(aconnector->mst_root->dc_link)); if (!state->duplicated) { int max_bpc = conn_state->max_requested_bpc; @@ -6905,7 +6907,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, max_bpc); bpp = convert_dc_color_depth_into_bpc(color_depth) * 3; clock = adjusted_mode->clock; - dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp, false); + dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4); } dm_new_connector_state->vcpi_slots = diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index e44ba5c1c48e..b4696ec621c4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -31,6 +31,7 @@ #include <drm/drm_probe_helper.h> #include <drm/amdgpu_drm.h> #include <drm/drm_edid.h> +#include <drm/drm_fixed.h> #include "dm_services.h" #include "amdgpu.h" @@ -210,7 +211,7 @@ static void dm_helpers_construct_old_payload( struct drm_dp_mst_atomic_payload *old_payload) { struct drm_dp_mst_atomic_payload *pos; - int pbn_per_slot = mst_state->pbn_div; + int pbn_per_slot = dfixed_trunc(mst_state->pbn_div); u8 next_payload_vc_start = mgr->next_start_slot; u8 payload_vc_start = new_payload->vc_start_slot; u8 allocated_time_slots; 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 a6995688d7ad..b599efda3b19 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 @@ -27,6 +27,7 @@ #include <drm/display/drm_dp_mst_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_fixed.h> #include "dm_services.h" #include "amdgpu.h" #include "amdgpu_dm.h" @@ -940,10 +941,10 @@ static int increase_dsc_bpp(struct drm_atomic_state *state, link_timeslots_used = 0; for (i = 0; i < count; i++) - link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, mst_state->pbn_div); + link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, dfixed_trunc(mst_state->pbn_div)); fair_pbn_alloc = - (63 - link_timeslots_used) / remaining_to_increase * mst_state->pbn_div; + (63 - link_timeslots_used) / remaining_to_increase * dfixed_trunc(mst_state->pbn_div); if (initial_slack[next_index] > fair_pbn_alloc) { vars[next_index].pbn += fair_pbn_alloc; @@ -1649,7 +1650,7 @@ enum dc_status dm_dp_mst_is_port_support_mode( } else { /* check if mode could be supported within full_pbn */ bpp = convert_dc_color_depth_into_bpc(stream->timing.display_color_depth) * 3; - pbn = drm_dp_calc_pbn_mode(stream->timing.pix_clk_100hz / 10, bpp, false); + pbn = drm_dp_calc_pbn_mode(stream->timing.pix_clk_100hz / 10, bpp << 4); if (pbn > aconnector->mst_output_port->full_pbn) return DC_FAIL_BANDWIDTH_VALIDATE; } diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 15dd667aa2e7..52d2c942d3d2 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1066,10 +1066,9 @@ static int armada_lcd_probe(struct platform_device *pdev) return component_add(&pdev->dev, &armada_lcd_ops); } -static int armada_lcd_remove(struct platform_device *pdev) +static void armada_lcd_remove(struct platform_device *pdev) { component_del(&pdev->dev, &armada_lcd_ops); - return 0; } static const struct of_device_id armada_lcd_of_match[] = { @@ -1095,7 +1094,7 @@ MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); struct platform_driver armada_lcd_platform_driver = { .probe = armada_lcd_probe, - .remove = armada_lcd_remove, + .remove_new = armada_lcd_remove, .driver = { .name = "armada-lcd", .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index fa1c67598706..e51ecc4f7ef4 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -226,10 +226,9 @@ static int armada_drm_probe(struct platform_device *pdev) match); } -static int armada_drm_remove(struct platform_device *pdev) +static void armada_drm_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &armada_master_ops); - return 0; } static void armada_drm_shutdown(struct platform_device *pdev) @@ -249,7 +248,7 @@ MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids); static struct platform_driver armada_drm_platform_driver = { .probe = armada_drm_probe, - .remove = armada_drm_remove, + .remove_new = armada_drm_remove, .shutdown = armada_drm_shutdown, .driver = { .name = "armada-drm", diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 2aee32344f4a..772f3b049c16 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -174,6 +174,17 @@ to_ast_sil164_connector(struct drm_connector *connector) return container_of(connector, struct ast_sil164_connector, base); } +struct ast_bmc_connector { + struct drm_connector base; + struct drm_connector *physical_connector; +}; + +static inline struct ast_bmc_connector * +to_ast_bmc_connector(struct drm_connector *connector) +{ + return container_of(connector, struct ast_bmc_connector, base); +} + /* * Device */ @@ -218,7 +229,7 @@ struct ast_device { } astdp; struct { struct drm_encoder encoder; - struct drm_connector connector; + struct ast_bmc_connector bmc_connector; } bmc; } output; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index cb9614984285..c20534d0ef7c 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -1767,6 +1767,30 @@ static const struct drm_encoder_funcs ast_bmc_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static int ast_bmc_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_bmc_connector *bmc_connector = to_ast_bmc_connector(connector); + struct drm_connector *physical_connector = bmc_connector->physical_connector; + + /* + * Most user-space compositors cannot handle more than one connected + * connector per CRTC. Hence, we only mark the BMC as connected if the + * physical connector is disconnected. If the physical connector's status + * is connected or unknown, the BMC remains disconnected. This has no + * effect on the output of the BMC. + * + * FIXME: Remove this logic once user-space compositors can handle more + * than one connector per CRTC. The BMC should always be connected. + */ + + if (physical_connector && physical_connector->status == connector_status_disconnected) + return connector_status_connected; + + return connector_status_disconnected; +} + static int ast_bmc_connector_helper_get_modes(struct drm_connector *connector) { return drm_add_modes_noedid(connector, 4096, 4096); @@ -1774,6 +1798,7 @@ static int ast_bmc_connector_helper_get_modes(struct drm_connector *connector) static const struct drm_connector_helper_funcs ast_bmc_connector_helper_funcs = { .get_modes = ast_bmc_connector_helper_get_modes, + .detect_ctx = ast_bmc_connector_helper_detect_ctx, }; static const struct drm_connector_funcs ast_bmc_connector_funcs = { @@ -1784,12 +1809,33 @@ static const struct drm_connector_funcs ast_bmc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int ast_bmc_output_init(struct ast_device *ast) +static int ast_bmc_connector_init(struct drm_device *dev, + struct ast_bmc_connector *bmc_connector, + struct drm_connector *physical_connector) +{ + struct drm_connector *connector = &bmc_connector->base; + int ret; + + ret = drm_connector_init(dev, connector, &ast_bmc_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) + return ret; + + drm_connector_helper_add(connector, &ast_bmc_connector_helper_funcs); + + bmc_connector->physical_connector = physical_connector; + + return 0; +} + +static int ast_bmc_output_init(struct ast_device *ast, + struct drm_connector *physical_connector) { struct drm_device *dev = &ast->base; struct drm_crtc *crtc = &ast->crtc; struct drm_encoder *encoder = &ast->output.bmc.encoder; - struct drm_connector *connector = &ast->output.bmc.connector; + struct ast_bmc_connector *bmc_connector = &ast->output.bmc.bmc_connector; + struct drm_connector *connector = &bmc_connector->base; int ret; ret = drm_encoder_init(dev, encoder, @@ -1799,13 +1845,10 @@ static int ast_bmc_output_init(struct ast_device *ast) return ret; encoder->possible_crtcs = drm_crtc_mask(crtc); - ret = drm_connector_init(dev, connector, &ast_bmc_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); + ret = ast_bmc_connector_init(dev, bmc_connector, physical_connector); if (ret) return ret; - drm_connector_helper_add(connector, &ast_bmc_connector_helper_funcs); - ret = drm_connector_attach_encoder(connector, encoder); if (ret) return ret; @@ -1864,6 +1907,7 @@ static const struct drm_mode_config_funcs ast_mode_config_funcs = { int ast_mode_config_init(struct ast_device *ast) { struct drm_device *dev = &ast->base; + struct drm_connector *physical_connector = NULL; int ret; ret = drmm_mode_config_init(dev); @@ -1904,23 +1948,27 @@ int ast_mode_config_init(struct ast_device *ast) ret = ast_vga_output_init(ast); if (ret) return ret; + physical_connector = &ast->output.vga.vga_connector.base; } if (ast->tx_chip_types & AST_TX_SIL164_BIT) { ret = ast_sil164_output_init(ast); if (ret) return ret; + physical_connector = &ast->output.sil164.sil164_connector.base; } if (ast->tx_chip_types & AST_TX_DP501_BIT) { ret = ast_dp501_output_init(ast); if (ret) return ret; + physical_connector = &ast->output.dp501.connector; } if (ast->tx_chip_types & AST_TX_ASTDP_BIT) { ret = ast_astdp_output_init(ast); if (ret) return ret; + physical_connector = &ast->output.astdp.connector; } - ret = ast_bmc_output_init(ast); + ret = ast_bmc_output_init(ast, physical_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 6af565ac307a..7d470527455b 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2596,11 +2596,10 @@ clk_disable: return ret; } -static int cdns_mhdp_remove(struct platform_device *pdev) +static void cdns_mhdp_remove(struct platform_device *pdev) { struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); unsigned long timeout = msecs_to_jiffies(100); - bool stop_fw = false; int ret; drm_bridge_remove(&mhdp->bridge); @@ -2608,18 +2607,19 @@ static int cdns_mhdp_remove(struct platform_device *pdev) ret = wait_event_timeout(mhdp->fw_load_wq, mhdp->hw_state == MHDP_HW_READY, timeout); - if (ret == 0) - dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n", - __func__); - else - stop_fw = true; - spin_lock(&mhdp->start_lock); mhdp->hw_state = MHDP_HW_STOPPED; spin_unlock(&mhdp->start_lock); - if (stop_fw) + if (ret == 0) { + dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n", + __func__); + } else { ret = cdns_mhdp_set_firmware_active(mhdp, false); + if (ret) + dev_err(mhdp->dev, "Failed to stop firmware (%pe)\n", + ERR_PTR(ret)); + } phy_exit(mhdp->phy); @@ -2634,8 +2634,6 @@ static int cdns_mhdp_remove(struct platform_device *pdev) /* Ignoring mhdp->hdcp.check_work and mhdp->hdcp.prop_work here. */ clk_disable_unprepare(mhdp->clk); - - return ret; } static const struct of_device_id mhdp_ids[] = { @@ -2658,7 +2656,7 @@ static struct platform_driver mhdp_driver = { .of_match_table = mhdp_ids, }, .probe = cdns_mhdp_probe, - .remove = cdns_mhdp_remove, + .remove_new = cdns_mhdp_remove, }; module_platform_driver(mhdp_driver); diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c index e0e015243a60..f9fb35683a27 100644 --- a/drivers/gpu/drm/bridge/ti-tpd12s015.c +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -179,13 +179,11 @@ static int tpd12s015_probe(struct platform_device *pdev) return 0; } -static int __exit tpd12s015_remove(struct platform_device *pdev) +static void tpd12s015_remove(struct platform_device *pdev) { struct tpd12s015_device *tpd = platform_get_drvdata(pdev); drm_bridge_remove(&tpd->bridge); - - return 0; } static const struct of_device_id tpd12s015_of_match[] = { @@ -197,7 +195,7 @@ MODULE_DEVICE_TABLE(of, tpd12s015_of_match); static struct platform_driver tpd12s015_driver = { .probe = tpd12s015_probe, - .remove = __exit_p(tpd12s015_remove), + .remove_new = tpd12s015_remove, .driver = { .name = "tpd12s015", .of_match_table = tpd12s015_of_match, diff --git a/drivers/gpu/drm/ci/xfails/requirements.txt b/drivers/gpu/drm/ci/xfails/requirements.txt index d8856d1581fd..e9994c9db799 100644 --- a/drivers/gpu/drm/ci/xfails/requirements.txt +++ b/drivers/gpu/drm/ci/xfails/requirements.txt @@ -5,7 +5,7 @@ termcolor==2.3.0 certifi==2023.7.22 charset-normalizer==3.2.0 idna==3.4 -pip==23.2.1 +pip==23.3 python-gitlab==3.15.0 requests==2.31.0 requests-toolbelt==1.0.0 @@ -13,5 +13,5 @@ ruamel.yaml==0.17.32 ruamel.yaml.clib==0.2.7 setuptools==68.0.0 tenacity==8.2.3 -urllib3==2.0.4 -wheel==0.41.1
\ No newline at end of file +urllib3==2.0.7 +wheel==0.41.1 diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index f3680f4e6970..d72b6f9a352c 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2245,6 +2245,8 @@ static const struct dpcd_quirk dpcd_quirk_list[] = { { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */ { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) }, + /* Synaptics DP1.4 MST hubs require DSC for some modes on which it applies HBLANK expansion. */ + { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) }, /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */ { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) }, }; @@ -2327,6 +2329,33 @@ int drm_dp_read_desc(struct drm_dp_aux *aux, struct drm_dp_desc *desc, EXPORT_SYMBOL(drm_dp_read_desc); /** + * drm_dp_dsc_sink_bpp_incr() - Get bits per pixel increment + * @dsc_dpcd: DSC capabilities from DPCD + * + * Returns the bpp precision supported by the DP sink. + */ +u8 drm_dp_dsc_sink_bpp_incr(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]) +{ + u8 bpp_increment_dpcd = dsc_dpcd[DP_DSC_BITS_PER_PIXEL_INC - DP_DSC_SUPPORT]; + + switch (bpp_increment_dpcd) { + case DP_DSC_BITS_PER_PIXEL_1_16: + return 16; + case DP_DSC_BITS_PER_PIXEL_1_8: + return 8; + case DP_DSC_BITS_PER_PIXEL_1_4: + return 4; + case DP_DSC_BITS_PER_PIXEL_1_2: + return 2; + case DP_DSC_BITS_PER_PIXEL_1_1: + return 1; + } + + return 0; +} +EXPORT_SYMBOL(drm_dp_dsc_sink_bpp_incr); + +/** * drm_dp_dsc_sink_max_slice_count() - Get the max slice count * supported by the DSC sink. * @dsc_dpcd: DSC capabilities from DPCD @@ -3898,3 +3927,135 @@ int drm_panel_dp_aux_backlight(struct drm_panel *panel, struct drm_dp_aux *aux) EXPORT_SYMBOL(drm_panel_dp_aux_backlight); #endif + +/* See DP Standard v2.1 2.6.4.4.1.1, 2.8.4.4, 2.8.7 */ +static int drm_dp_link_symbol_cycles(int lane_count, int pixels, int bpp_x16, + int symbol_size, bool is_mst) +{ + int cycles = DIV_ROUND_UP(pixels * bpp_x16, 16 * symbol_size * lane_count); + int align = is_mst ? 4 / lane_count : 1; + + return ALIGN(cycles, align); +} + +static int drm_dp_link_dsc_symbol_cycles(int lane_count, int pixels, int slice_count, + int bpp_x16, int symbol_size, bool is_mst) +{ + int slice_pixels = DIV_ROUND_UP(pixels, slice_count); + int slice_data_cycles = drm_dp_link_symbol_cycles(lane_count, slice_pixels, + bpp_x16, symbol_size, is_mst); + int slice_eoc_cycles = is_mst ? 4 / lane_count : 1; + + return slice_count * (slice_data_cycles + slice_eoc_cycles); +} + +/** + * drm_dp_bw_overhead - Calculate the BW overhead of a DP link stream + * @lane_count: DP link lane count + * @hactive: pixel count of the active period in one scanline of the stream + * @dsc_slice_count: DSC slice count if @flags/DRM_DP_LINK_BW_OVERHEAD_DSC is set + * @bpp_x16: bits per pixel in .4 binary fixed point + * @flags: DRM_DP_OVERHEAD_x flags + * + * Calculate the BW allocation overhead of a DP link stream, depending + * on the link's + * - @lane_count + * - SST/MST mode (@flags / %DRM_DP_OVERHEAD_MST) + * - symbol size (@flags / %DRM_DP_OVERHEAD_UHBR) + * - FEC mode (@flags / %DRM_DP_OVERHEAD_FEC) + * - SSC/REF_CLK mode (@flags / %DRM_DP_OVERHEAD_SSC_REF_CLK) + * as well as the stream's + * - @hactive timing + * - @bpp_x16 color depth + * - compression mode (@flags / %DRM_DP_OVERHEAD_DSC). + * Note that this overhead doesn't account for the 8b/10b, 128b/132b + * channel coding efficiency, for that see + * @drm_dp_link_bw_channel_coding_efficiency(). + * + * Returns the overhead as 100% + overhead% in 1ppm units. + */ +int drm_dp_bw_overhead(int lane_count, int hactive, + int dsc_slice_count, + int bpp_x16, unsigned long flags) +{ + int symbol_size = flags & DRM_DP_BW_OVERHEAD_UHBR ? 32 : 8; + bool is_mst = flags & DRM_DP_BW_OVERHEAD_MST; + u32 overhead = 1000000; + int symbol_cycles; + + /* + * DP Standard v2.1 2.6.4.1 + * SSC downspread and ref clock variation margin: + * 5300ppm + 300ppm ~ 0.6% + */ + if (flags & DRM_DP_BW_OVERHEAD_SSC_REF_CLK) + overhead += 6000; + + /* + * DP Standard v2.1 2.6.4.1.1, 3.5.1.5.4: + * FEC symbol insertions for 8b/10b channel coding: + * After each 250 data symbols on 2-4 lanes: + * 250 LL + 5 FEC_PARITY_PH + 1 CD_ADJ (256 byte FEC block) + * After each 2 x 250 data symbols on 1 lane: + * 2 * 250 LL + 11 FEC_PARITY_PH + 1 CD_ADJ (512 byte FEC block) + * After 256 (2-4 lanes) or 128 (1 lane) FEC blocks: + * 256 * 256 bytes + 1 FEC_PM + * or + * 128 * 512 bytes + 1 FEC_PM + * (256 * 6 + 1) / (256 * 250) = 2.4015625 % + */ + if (flags & DRM_DP_BW_OVERHEAD_FEC) + overhead += 24016; + + /* + * DP Standard v2.1 2.7.9, 5.9.7 + * The FEC overhead for UHBR is accounted for in its 96.71% channel + * coding efficiency. + */ + WARN_ON((flags & DRM_DP_BW_OVERHEAD_UHBR) && + (flags & DRM_DP_BW_OVERHEAD_FEC)); + + if (flags & DRM_DP_BW_OVERHEAD_DSC) + symbol_cycles = drm_dp_link_dsc_symbol_cycles(lane_count, hactive, + dsc_slice_count, + bpp_x16, symbol_size, + is_mst); + else + symbol_cycles = drm_dp_link_symbol_cycles(lane_count, hactive, + bpp_x16, symbol_size, + is_mst); + + return DIV_ROUND_UP_ULL(mul_u32_u32(symbol_cycles * symbol_size * lane_count, + overhead * 16), + hactive * bpp_x16); +} +EXPORT_SYMBOL(drm_dp_bw_overhead); + +/** + * drm_dp_bw_channel_coding_efficiency - Get a DP link's channel coding efficiency + * @is_uhbr: Whether the link has a 128b/132b channel coding + * + * Return the channel coding efficiency of the given DP link type, which is + * either 8b/10b or 128b/132b (aka UHBR). The corresponding overhead includes + * the 8b -> 10b, 128b -> 132b pixel data to link symbol conversion overhead + * and for 128b/132b any link or PHY level control symbol insertion overhead + * (LLCP, FEC, PHY sync, see DP Standard v2.1 3.5.2.18). For 8b/10b the + * corresponding FEC overhead is BW allocation specific, included in the value + * returned by drm_dp_bw_overhead(). + * + * Returns the efficiency in the 100%/coding-overhead% ratio in + * 1ppm units. + */ +int drm_dp_bw_channel_coding_efficiency(bool is_uhbr) +{ + if (is_uhbr) + return 967100; + else + /* + * Note that on 8b/10b MST the efficiency is only + * 78.75% due to the 1 out of 64 MTPH packet overhead, + * not accounted for here. + */ + return 800000; +} +EXPORT_SYMBOL(drm_dp_bw_channel_coding_efficiency); diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 0e0d0e76de06..8ca01a6bf645 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -43,6 +43,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> +#include <drm/drm_fixed.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -3578,16 +3579,26 @@ static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr, * value is in units of PBNs/(timeslots/1 MTP). This value can be used to * convert the number of PBNs required for a given stream to the number of * timeslots this stream requires in each MTP. + * + * Returns the BW / timeslot value in 20.12 fixed point format. */ -int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr, - int link_rate, int link_lane_count) +fixed20_12 drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr, + int link_rate, int link_lane_count) { + int ch_coding_efficiency = + drm_dp_bw_channel_coding_efficiency(drm_dp_is_uhbr_rate(link_rate)); + fixed20_12 ret; + if (link_rate == 0 || link_lane_count == 0) drm_dbg_kms(mgr->dev, "invalid link rate/lane count: (%d / %d)\n", link_rate, link_lane_count); - /* See DP v2.0 2.6.4.2, VCPayload_Bandwidth_for_OneTimeSlotPer_MTP_Allocation */ - return link_rate * link_lane_count / 54000; + /* See DP v2.0 2.6.4.2, 2.7.6.3 VCPayload_Bandwidth_for_OneTimeSlotPer_MTP_Allocation */ + ret.full = DIV_ROUND_DOWN_ULL(mul_u32_u32(link_rate * link_lane_count, + ch_coding_efficiency), + (1000000ULL * 8 * 5400) >> 12); + + return ret; } EXPORT_SYMBOL(drm_dp_get_vc_payload_bw); @@ -4335,7 +4346,7 @@ int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, } } - req_slots = DIV_ROUND_UP(pbn, topology_state->pbn_div); + req_slots = DIV_ROUND_UP(dfixed_const(pbn), topology_state->pbn_div.full); drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] TU %d -> %d\n", port->connector->base.id, port->connector->name, @@ -4718,35 +4729,36 @@ EXPORT_SYMBOL(drm_dp_check_act_status); /** * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. - * @clock: dot clock for the mode - * @bpp: bpp for the mode. - * @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel + * @clock: dot clock + * @bpp: bpp as .4 binary fixed point * * This uses the formula in the spec to calculate the PBN value for a mode. */ -int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc) +int drm_dp_calc_pbn_mode(int clock, int bpp) { /* - * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on * common multiplier to render an integer PBN for all link rate/lane * counts combinations * calculate - * peak_kbps *= (1006/1000) - * peak_kbps *= (64/54) - * peak_kbps *= 8 convert to bytes - * - * If the bpp is in units of 1/16, further divide by 16. Put this - * factor in the numerator rather than the denominator to avoid - * integer overflow + * peak_kbps = clock * bpp / 16 + * peak_kbps *= SSC overhead / 1000000 + * peak_kbps /= 8 convert to Kbytes + * peak_kBps *= (64/54) / 1000 convert to PBN */ + /* + * TODO: Use the actual link and mode parameters to calculate + * the overhead. For now it's assumed that these are + * 4 link lanes, 4096 hactive pixels, which don't add any + * significant data padding overhead and that there is no DSC + * or FEC overhead. + */ + int overhead = drm_dp_bw_overhead(4, 4096, 0, bpp, + DRM_DP_BW_OVERHEAD_MST | + DRM_DP_BW_OVERHEAD_SSC_REF_CLK); - if (dsc) - return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006), - 8 * 54 * 1000 * 1000); - - return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006), - 8 * 54 * 1000 * 1000); + return DIV64_U64_ROUND_UP(mul_u32_u32(clock * bpp, 64 * overhead >> 4), + 1000000ULL * 8 * 54 * 1000); } EXPORT_SYMBOL(drm_dp_calc_pbn_mode); @@ -4871,7 +4883,8 @@ void drm_dp_mst_dump_topology(struct seq_file *m, state = to_drm_dp_mst_topology_state(mgr->base.state); seq_printf(m, "\n*** Atomic state info ***\n"); seq_printf(m, "payload_mask: %x, max_payloads: %d, start_slot: %u, pbn_div: %d\n", - state->payload_mask, mgr->max_payloads, state->start_slot, state->pbn_div); + state->payload_mask, mgr->max_payloads, state->start_slot, + dfixed_trunc(state->pbn_div)); seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | status | sink name |\n"); for (i = 0; i < mgr->max_payloads; i++) { @@ -5136,13 +5149,67 @@ static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port, return false; } +static bool +drm_dp_mst_port_downstream_of_parent_locked(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + struct drm_dp_mst_port *parent) +{ + if (!mgr->mst_primary) + return false; + + port = drm_dp_mst_topology_get_port_validated_locked(mgr->mst_primary, + port); + if (!port) + return false; + + if (!parent) + return true; + + parent = drm_dp_mst_topology_get_port_validated_locked(mgr->mst_primary, + parent); + if (!parent) + return false; + + if (!parent->mstb) + return false; + + return drm_dp_mst_port_downstream_of_branch(port, parent->mstb); +} + +/** + * drm_dp_mst_port_downstream_of_parent - check if a port is downstream of a parent port + * @mgr: MST topology manager + * @port: the port being looked up + * @parent: the parent port + * + * The function returns %true if @port is downstream of @parent. If @parent is + * %NULL - denoting the root port - the function returns %true if @port is in + * @mgr's topology. + */ +bool +drm_dp_mst_port_downstream_of_parent(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + struct drm_dp_mst_port *parent) +{ + bool ret; + + mutex_lock(&mgr->lock); + ret = drm_dp_mst_port_downstream_of_parent_locked(mgr, port, parent); + mutex_unlock(&mgr->lock); + + return ret; +} +EXPORT_SYMBOL(drm_dp_mst_port_downstream_of_parent); + static int drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, - struct drm_dp_mst_topology_state *state); + struct drm_dp_mst_topology_state *state, + struct drm_dp_mst_port **failing_port); static int drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb, - struct drm_dp_mst_topology_state *state) + struct drm_dp_mst_topology_state *state, + struct drm_dp_mst_port **failing_port) { struct drm_dp_mst_atomic_payload *payload; struct drm_dp_mst_port *port; @@ -5171,7 +5238,7 @@ drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb, drm_dbg_atomic(mstb->mgr->dev, "[MSTB:%p] Checking bandwidth limits\n", mstb); list_for_each_entry(port, &mstb->ports, next) { - ret = drm_dp_mst_atomic_check_port_bw_limit(port, state); + ret = drm_dp_mst_atomic_check_port_bw_limit(port, state, failing_port); if (ret < 0) return ret; @@ -5183,7 +5250,8 @@ drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb, static int drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, - struct drm_dp_mst_topology_state *state) + struct drm_dp_mst_topology_state *state, + struct drm_dp_mst_port **failing_port) { struct drm_dp_mst_atomic_payload *payload; int pbn_used = 0; @@ -5204,13 +5272,15 @@ drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, drm_dbg_atomic(port->mgr->dev, "[MSTB:%p] [MST PORT:%p] no BW available for the port\n", port->parent, port); + *failing_port = port; return -EINVAL; } pbn_used = payload->pbn; } else { pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb, - state); + state, + failing_port); if (pbn_used <= 0) return pbn_used; } @@ -5219,6 +5289,7 @@ drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port, drm_dbg_atomic(port->mgr->dev, "[MSTB:%p] [MST PORT:%p] required PBN of %d exceeds port limit of %d\n", port->parent, port, pbn_used, port->full_pbn); + *failing_port = port; return -ENOSPC; } @@ -5271,10 +5342,10 @@ drm_dp_mst_atomic_check_payload_alloc_limits(struct drm_dp_mst_topology_mgr *mgr } if (!payload_count) - mst_state->pbn_div = 0; + mst_state->pbn_div.full = dfixed_const(0); drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p TU pbn_div=%d avail=%d used=%d\n", - mgr, mst_state, mst_state->pbn_div, avail_slots, + mgr, mst_state, dfixed_trunc(mst_state->pbn_div), avail_slots, mst_state->total_avail_slots - avail_slots); return 0; @@ -5397,19 +5468,81 @@ int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc); /** + * drm_dp_mst_atomic_check_mgr - Check the atomic state of an MST topology manager + * @state: The global atomic state + * @mgr: Manager to check + * @mst_state: The MST atomic state for @mgr + * @failing_port: Returns the port with a BW limitation + * + * Checks the given MST manager's topology state for an atomic update to ensure + * that it's valid. This includes checking whether there's enough bandwidth to + * support the new timeslot allocations in the atomic update. + * + * Any atomic drivers supporting DP MST must make sure to call this or + * the drm_dp_mst_atomic_check() function after checking the rest of their state + * in their &drm_mode_config_funcs.atomic_check() callback. + * + * See also: + * drm_dp_mst_atomic_check() + * drm_dp_atomic_find_time_slots() + * drm_dp_atomic_release_time_slots() + * + * Returns: + * - 0 if the new state is valid + * - %-ENOSPC, if the new state is invalid, because of BW limitation + * @failing_port is set to: + * - The non-root port where a BW limit check failed + * with all the ports downstream of @failing_port passing + * the BW limit check. + * The returned port pointer is valid until at least + * one payload downstream of it exists. + * - %NULL if the BW limit check failed at the root port + * with all the ports downstream of the root port passing + * the BW limit check. + * - %-EINVAL, if the new state is invalid, because the root port has + * too many payloads. + */ +int drm_dp_mst_atomic_check_mgr(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_port **failing_port) +{ + int ret; + + *failing_port = NULL; + + if (!mgr->mst_state) + return 0; + + mutex_lock(&mgr->lock); + ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary, + mst_state, + failing_port); + mutex_unlock(&mgr->lock); + + if (ret < 0) + return ret; + + return drm_dp_mst_atomic_check_payload_alloc_limits(mgr, mst_state); +} +EXPORT_SYMBOL(drm_dp_mst_atomic_check_mgr); + +/** * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an * atomic update is valid * @state: Pointer to the new &struct drm_dp_mst_topology_state * * Checks the given topology state for an atomic update to ensure that it's - * valid. This includes checking whether there's enough bandwidth to support - * the new timeslot allocations in the atomic update. + * valid, calling drm_dp_mst_atomic_check_mgr() for all MST manager in the + * atomic state. This includes checking whether there's enough bandwidth to + * support the new timeslot allocations in the atomic update. * * Any atomic drivers supporting DP MST must make sure to call this after * checking the rest of their state in their * &drm_mode_config_funcs.atomic_check() callback. * * See also: + * drm_dp_mst_atomic_check_mgr() * drm_dp_atomic_find_time_slots() * drm_dp_atomic_release_time_slots() * @@ -5424,21 +5557,11 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) int i, ret = 0; for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { - if (!mgr->mst_state) - continue; + struct drm_dp_mst_port *tmp_port; - ret = drm_dp_mst_atomic_check_payload_alloc_limits(mgr, mst_state); + ret = drm_dp_mst_atomic_check_mgr(state, mgr, mst_state, &tmp_port); if (ret) break; - - mutex_lock(&mgr->lock); - ret = drm_dp_mst_atomic_check_mstb_bw_limit(mgr->mst_primary, - mst_state); - mutex_unlock(&mgr->lock); - if (ret < 0) - break; - else - ret = 0; } return ret; @@ -5894,6 +6017,7 @@ static bool drm_dp_mst_is_virtual_dpcd(struct drm_dp_mst_port *port) struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port) { struct drm_dp_mst_port *immediate_upstream_port; + struct drm_dp_aux *immediate_upstream_aux; struct drm_dp_mst_port *fec_port; struct drm_dp_desc desc = {}; u8 endpoint_fec; @@ -5958,21 +6082,25 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port) * - Port is on primary branch device * - Not a VGA adapter (DP_DWN_STRM_PORT_TYPE_ANALOG) */ - if (drm_dp_read_desc(port->mgr->aux, &desc, true)) + if (immediate_upstream_port) + immediate_upstream_aux = &immediate_upstream_port->aux; + else + immediate_upstream_aux = port->mgr->aux; + + if (drm_dp_read_desc(immediate_upstream_aux, &desc, true)) return NULL; - if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) && - port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 && - port->parent == port->mgr->mst_primary) { + if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD)) { u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; - if (drm_dp_read_dpcd_caps(port->mgr->aux, dpcd_ext) < 0) + if (drm_dp_read_dpcd_caps(immediate_upstream_aux, dpcd_ext) < 0) return NULL; - if ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT) && + if (dpcd_ext[DP_DPCD_REV] >= DP_DPCD_REV_14 && + ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT) && ((dpcd_ext[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) - != DP_DWN_STRM_PORT_TYPE_ANALOG)) - return port->mgr->aux; + != DP_DWN_STRM_PORT_TYPE_ANALOG))) + return immediate_upstream_aux; } /* diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2444fc33dd7c..c3f677130def 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2382,10 +2382,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_helper_setup_commit); /** - * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits + * drm_atomic_helper_wait_for_dependencies - wait for required preceding commits * @old_state: atomic state object with old state structures * - * This function waits for all preceeding commits that touch the same CRTC as + * This function waits for all preceding commits that touch the same CRTC as * @old_state to both be committed to the hardware (as signalled by * drm_atomic_helper_commit_hw_done()) and executed by the hardware (as signalled * by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event). diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index c3027115d055..9403b3f576f7 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -5,7 +5,6 @@ #include <linux/iosys-map.h> #include <linux/list.h> -#include <linux/module.h> #include <linux/mutex.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -84,16 +83,13 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create) return -EOPNOTSUPP; - if (funcs && !try_module_get(funcs->owner)) - return -ENODEV; - client->dev = dev; client->name = name; client->funcs = funcs; ret = drm_client_modeset_create(client); if (ret) - goto err_put_module; + return ret; ret = drm_client_open(client); if (ret) @@ -105,10 +101,6 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, err_free: drm_client_modeset_free(client); -err_put_module: - if (funcs) - module_put(funcs->owner); - return ret; } EXPORT_SYMBOL(drm_client_init); @@ -177,8 +169,6 @@ void drm_client_release(struct drm_client_dev *client) drm_client_modeset_free(client); drm_client_close(client); drm_dev_put(dev); - if (client->funcs) - module_put(client->funcs->owner); } EXPORT_SYMBOL(drm_client_release); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index c3725086f413..b0516505f7ae 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1198,6 +1198,12 @@ static const u32 dp_colorspaces = * drm_connector_set_path_property(), in the case of DP MST with the * path property the MST manager created. Userspace cannot change this * property. + * + * In the case of DP MST, the property has the format + * ``mst:<parent>-<ports>`` where ``<parent>`` is the KMS object ID of the + * parent connector and ``<ports>`` is a hyphen-separated list of DP MST + * port numbers. Note, KMS object IDs are not guaranteed to be stable + * across reboots. * TILE: * Connector tile group property to indicate how a set of DRM connector * compose together into one logical screen. This is used by both high-res diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 8556c3b3ff88..6b646e0783be 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -222,6 +222,8 @@ int drm_mode_addfb2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_rmfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_mode_closefb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mode_getfb2_ioctl(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 39db08f803ea..cb4031d5dcbb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -41,10 +41,12 @@ #include <drm/drm_displayid.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/drm_encoder.h> #include <drm/drm_print.h> #include "drm_crtc_internal.h" +#include "drm_internal.h" static int oui(u8 first, u8 second, u8 third) { @@ -5510,6 +5512,27 @@ static void clear_eld(struct drm_connector *connector) } /* + * Get 3-byte SAD buffer from struct cea_sad. + */ +void drm_edid_cta_sad_get(const struct cea_sad *cta_sad, u8 *sad) +{ + sad[0] = cta_sad->format << 3 | cta_sad->channels; + sad[1] = cta_sad->freq; + sad[2] = cta_sad->byte2; +} + +/* + * Set struct cea_sad from 3-byte SAD buffer. + */ +void drm_edid_cta_sad_set(struct cea_sad *cta_sad, const u8 *sad) +{ + cta_sad->format = (sad[0] & 0x78) >> 3; + cta_sad->channels = sad[0] & 0x07; + cta_sad->freq = sad[1] & 0x7f; + cta_sad->byte2 = sad[2]; +} + +/* * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink * @drm_edid: EDID to parse @@ -5593,7 +5616,7 @@ static void drm_edid_to_eld(struct drm_connector *connector, } static int _drm_edid_to_sad(const struct drm_edid *drm_edid, - struct cea_sad **sads) + struct cea_sad **psads) { const struct cea_db *db; struct cea_db_iter iter; @@ -5602,20 +5625,16 @@ static int _drm_edid_to_sad(const struct drm_edid *drm_edid, cea_db_iter_edid_begin(drm_edid, &iter); cea_db_iter_for_each(db, &iter) { if (cea_db_tag(db) == CTA_DB_AUDIO) { - int j; + struct cea_sad *sads; + int i; count = cea_db_payload_len(db) / 3; /* SAD is 3B */ - *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); - if (!*sads) + sads = kcalloc(count, sizeof(*sads), GFP_KERNEL); + *psads = sads; + if (!sads) return -ENOMEM; - for (j = 0; j < count; j++) { - const u8 *sad = &db->data[j * 3]; - - (*sads)[j].format = (sad[0] & 0x78) >> 3; - (*sads)[j].channels = sad[0] & 0x7; - (*sads)[j].freq = sad[1] & 0x7F; - (*sads)[j].byte2 = sad[2]; - } + for (i = 0; i < count; i++) + drm_edid_cta_sad_set(&sads[i], &db->data[i * 3]); break; } } diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 5d9ef267ebb3..60fcb80bce61 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -23,22 +23,6 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " "from built-in data or /lib/firmware instead. "); -/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ -int __drm_set_edid_firmware_path(const char *path) -{ - scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path); - - return 0; -} -EXPORT_SYMBOL(__drm_set_edid_firmware_path); - -/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ -int __drm_get_edid_firmware_path(char *buf, size_t bufsize) -{ - return scnprintf(buf, bufsize, "%s", edid_firmware); -} -EXPORT_SYMBOL(__drm_get_edid_firmware_path); - #define GENERIC_EDIDS 6 static const char * const generic_edid_name[GENERIC_EDIDS] = { "edid/800x600.bin", diff --git a/drivers/gpu/drm/drm_eld.c b/drivers/gpu/drm/drm_eld.c new file mode 100644 index 000000000000..5177991aa272 --- /dev/null +++ b/drivers/gpu/drm/drm_eld.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include <drm/drm_edid.h> +#include <drm/drm_eld.h> + +#include "drm_internal.h" + +/** + * drm_eld_sad_get - get SAD from ELD to struct cea_sad + * @eld: ELD buffer + * @sad_index: SAD index + * @cta_sad: destination struct cea_sad + * + * @return: 0 on success, or negative on errors + */ +int drm_eld_sad_get(const u8 *eld, int sad_index, struct cea_sad *cta_sad) +{ + const u8 *sad; + + if (sad_index >= drm_eld_sad_count(eld)) + return -EINVAL; + + sad = eld + DRM_ELD_CEA_SAD(drm_eld_mnl(eld), sad_index); + + drm_edid_cta_sad_set(cta_sad, sad); + + return 0; +} +EXPORT_SYMBOL(drm_eld_sad_get); + +/** + * drm_eld_sad_set - set SAD to ELD from struct cea_sad + * @eld: ELD buffer + * @sad_index: SAD index + * @cta_sad: source struct cea_sad + * + * @return: 0 on success, or negative on errors + */ +int drm_eld_sad_set(u8 *eld, int sad_index, const struct cea_sad *cta_sad) +{ + u8 *sad; + + if (sad_index >= drm_eld_sad_count(eld)) + return -EINVAL; + + sad = eld + DRM_ELD_CEA_SAD(drm_eld_mnl(eld), sad_index); + + drm_edid_cta_sad_get(cta_sad, sad); + + return 0; +} +EXPORT_SYMBOL(drm_eld_sad_set); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 446458aca8e9..5ddaffd32586 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -913,7 +913,7 @@ static void print_size(struct drm_printer *p, const char *stat, unsigned u; for (u = 0; u < ARRAY_SIZE(units) - 1; u++) { - if (sz < SZ_1K) + if (sz == 0 || !IS_ALIGNED(sz, SZ_1K)) break; sz = div_u64(sz, SZ_1K); } diff --git a/drivers/gpu/drm/drm_flip_work.c b/drivers/gpu/drm/drm_flip_work.c index 060b753881a2..8c6090a90d56 100644 --- a/drivers/gpu/drm/drm_flip_work.c +++ b/drivers/gpu/drm/drm_flip_work.c @@ -27,14 +27,12 @@ #include <drm/drm_print.h> #include <drm/drm_util.h> -/** - * drm_flip_work_allocate_task - allocate a flip-work task - * @data: data associated to the task - * @flags: allocator flags - * - * Allocate a drm_flip_task object and attach private data to it. - */ -struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags) +struct drm_flip_task { + struct list_head node; + void *data; +}; + +static struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags) { struct drm_flip_task *task; @@ -44,18 +42,8 @@ struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags) return task; } -EXPORT_SYMBOL(drm_flip_work_allocate_task); -/** - * drm_flip_work_queue_task - queue a specific task - * @work: the flip-work - * @task: the task to handle - * - * Queues task, that will later be run (passed back to drm_flip_func_t - * func) on a work queue after drm_flip_work_commit() is called. - */ -void drm_flip_work_queue_task(struct drm_flip_work *work, - struct drm_flip_task *task) +static void drm_flip_work_queue_task(struct drm_flip_work *work, struct drm_flip_task *task) { unsigned long flags; @@ -63,7 +51,6 @@ void drm_flip_work_queue_task(struct drm_flip_work *work, list_add_tail(&task->node, &work->queued); spin_unlock_irqrestore(&work->lock, flags); } -EXPORT_SYMBOL(drm_flip_work_queue_task); /** * drm_flip_work_queue - queue work diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index f93a4efcee90..b1be458ed4dd 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -20,6 +20,97 @@ #include <drm/drm_print.h> #include <drm/drm_rect.h> +/** + * drm_format_conv_state_init - Initialize format-conversion state + * @state: The state to initialize + * + * Clears all fields in struct drm_format_conv_state. The state will + * be empty with no preallocated resources. + */ +void drm_format_conv_state_init(struct drm_format_conv_state *state) +{ + state->tmp.mem = NULL; + state->tmp.size = 0; + state->tmp.preallocated = false; +} +EXPORT_SYMBOL(drm_format_conv_state_init); + +/** + * drm_format_conv_state_copy - Copy format-conversion state + * @state: Destination state + * @old_state: Source state + * + * Copies format-conversion state from @old_state to @state; except for + * temporary storage. + */ +void drm_format_conv_state_copy(struct drm_format_conv_state *state, + const struct drm_format_conv_state *old_state) +{ + /* + * So far, there's only temporary storage here, which we don't + * duplicate. Just clear the fields. + */ + state->tmp.mem = NULL; + state->tmp.size = 0; + state->tmp.preallocated = false; +} +EXPORT_SYMBOL(drm_format_conv_state_copy); + +/** + * drm_format_conv_state_reserve - Allocates storage for format conversion + * @state: The format-conversion state + * @new_size: The minimum allocation size + * @flags: Flags for kmalloc() + * + * Allocates at least @new_size bytes and returns a pointer to the memory + * range. After calling this function, previously returned memory blocks + * are invalid. It's best to collect all memory requirements of a format + * conversion and call this function once to allocate the range. + * + * Returns: + * A pointer to the allocated memory range, or NULL otherwise. + */ +void *drm_format_conv_state_reserve(struct drm_format_conv_state *state, + size_t new_size, gfp_t flags) +{ + void *mem; + + if (new_size <= state->tmp.size) + goto out; + else if (state->tmp.preallocated) + return NULL; + + mem = krealloc(state->tmp.mem, new_size, flags); + if (!mem) + return NULL; + + state->tmp.mem = mem; + state->tmp.size = new_size; + +out: + return state->tmp.mem; +} +EXPORT_SYMBOL(drm_format_conv_state_reserve); + +/** + * drm_format_conv_state_release - Releases an format-conversion storage + * @state: The format-conversion state + * + * Releases the memory range references by the format-conversion state. + * After this call, all pointers to the memory are invalid. Prefer + * drm_format_conv_state_init() for cleaning up and unloading a driver. + */ +void drm_format_conv_state_release(struct drm_format_conv_state *state) +{ + if (state->tmp.preallocated) + return; + + kfree(state->tmp.mem); + state->tmp.mem = NULL; + state->tmp.size = 0; +} +EXPORT_SYMBOL(drm_format_conv_state_release); + static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp) { return clip->y1 * pitch + clip->x1 * cpp; @@ -45,6 +136,7 @@ EXPORT_SYMBOL(drm_fb_clip_offset); static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize, const void *vaddr, const struct drm_framebuffer *fb, const struct drm_rect *clip, bool vaddr_cached_hint, + struct drm_format_conv_state *state, void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) { unsigned long linepixels = drm_rect_width(clip); @@ -60,7 +152,7 @@ static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_p * one line at a time. */ if (!vaddr_cached_hint) { - stmp = kmalloc(sbuf_len, GFP_KERNEL); + stmp = drm_format_conv_state_reserve(state, sbuf_len, GFP_KERNEL); if (!stmp) return -ENOMEM; } @@ -79,8 +171,6 @@ static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_p dst += dst_pitch; } - kfree(stmp); - return 0; } @@ -88,6 +178,7 @@ static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_p static int __drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize, const void *vaddr, const struct drm_framebuffer *fb, const struct drm_rect *clip, bool vaddr_cached_hint, + struct drm_format_conv_state *state, void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) { unsigned long linepixels = drm_rect_width(clip); @@ -101,9 +192,9 @@ static int __drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsign void *dbuf; if (vaddr_cached_hint) { - dbuf = kmalloc(dbuf_len, GFP_KERNEL); + dbuf = drm_format_conv_state_reserve(state, dbuf_len, GFP_KERNEL); } else { - dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL); + dbuf = drm_format_conv_state_reserve(state, stmp_off + sbuf_len, GFP_KERNEL); stmp = dbuf + stmp_off; } if (!dbuf) @@ -124,8 +215,6 @@ static int __drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsign dst += dst_pitch; } - kfree(dbuf); - return 0; } @@ -134,6 +223,7 @@ static int drm_fb_xfrm(struct iosys_map *dst, const unsigned int *dst_pitch, const u8 *dst_pixsize, const struct iosys_map *src, const struct drm_framebuffer *fb, const struct drm_rect *clip, bool vaddr_cached_hint, + struct drm_format_conv_state *state, void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels)) { static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = { @@ -146,10 +236,12 @@ static int drm_fb_xfrm(struct iosys_map *dst, /* TODO: handle src in I/O memory here */ if (dst[0].is_iomem) return __drm_fb_xfrm_toio(dst[0].vaddr_iomem, dst_pitch[0], dst_pixsize[0], - src[0].vaddr, fb, clip, vaddr_cached_hint, xfrm_line); + src[0].vaddr, fb, clip, vaddr_cached_hint, state, + xfrm_line); else return __drm_fb_xfrm(dst[0].vaddr, dst_pitch[0], dst_pixsize[0], - src[0].vaddr, fb, clip, vaddr_cached_hint, xfrm_line); + src[0].vaddr, fb, clip, vaddr_cached_hint, state, + xfrm_line); } /** @@ -235,6 +327,7 @@ static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels * @fb: DRM framebuffer * @clip: Clip rectangle area to copy * @cached: Source buffer is mapped cached (eg. not write-combined) + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and swaps per-pixel * bytes during the process. Destination and framebuffer formats must match. The @@ -249,7 +342,8 @@ static void drm_fb_swab32_line(void *dbuf, const void *sbuf, unsigned int pixels */ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip, bool cached) + const struct drm_rect *clip, bool cached, + struct drm_format_conv_state *state) { const struct drm_format_info *format = fb->format; u8 cpp = DIV_ROUND_UP(drm_format_info_bpp(format, 0), 8); @@ -268,7 +362,7 @@ void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch, return; } - drm_fb_xfrm(dst, dst_pitch, &cpp, src, fb, clip, cached, swab_line); + drm_fb_xfrm(dst, dst_pitch, &cpp, src, fb, clip, cached, state, swab_line); } EXPORT_SYMBOL(drm_fb_swab); @@ -295,6 +389,7 @@ static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigne * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. Destination and framebuffer formats must match. The @@ -309,13 +404,13 @@ static void drm_fb_xrgb8888_to_rgb332_line(void *dbuf, const void *sbuf, unsigne */ void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 1, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_rgb332_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); @@ -364,6 +459,7 @@ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, * @src: Array of XRGB8888 source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * @swab: Swap bytes * * This function copies parts of a framebuffer to display memory and converts the @@ -379,7 +475,8 @@ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, */ void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip, bool swab) + const struct drm_rect *clip, struct drm_format_conv_state *state, + bool swab) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 2, @@ -392,7 +489,7 @@ void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pi else xfrm_line = drm_fb_xrgb8888_to_rgb565_line; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, xfrm_line); + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, xfrm_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); @@ -421,6 +518,7 @@ static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsig * @src: Array of XRGB8888 source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts * the color format during the process. The parameters @dst, @dst_pitch and @@ -436,13 +534,13 @@ static void drm_fb_xrgb8888_to_xrgb1555_line(void *dbuf, const void *sbuf, unsig */ void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 2, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_xrgb1555_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb1555); @@ -473,6 +571,7 @@ static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsig * @src: Array of XRGB8888 source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts * the color format during the process. The parameters @dst, @dst_pitch and @@ -488,13 +587,13 @@ static void drm_fb_xrgb8888_to_argb1555_line(void *dbuf, const void *sbuf, unsig */ void drm_fb_xrgb8888_to_argb1555(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 2, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_argb1555_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb1555); @@ -525,6 +624,7 @@ static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsig * @src: Array of XRGB8888 source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts * the color format during the process. The parameters @dst, @dst_pitch and @@ -540,13 +640,13 @@ static void drm_fb_xrgb8888_to_rgba5551_line(void *dbuf, const void *sbuf, unsig */ void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 2, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_rgba5551_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgba5551); @@ -575,6 +675,7 @@ static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigne * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. Destination and framebuffer formats must match. The @@ -590,13 +691,13 @@ static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigne */ void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 3, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_rgb888_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888); @@ -623,6 +724,7 @@ static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsig * @src: Array of XRGB8888 source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. The parameters @dst, @dst_pitch and @src refer @@ -638,13 +740,13 @@ static void drm_fb_xrgb8888_to_argb8888_line(void *dbuf, const void *sbuf, unsig */ void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 4, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_argb8888_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb8888); @@ -669,13 +771,14 @@ static void drm_fb_xrgb8888_to_abgr8888_line(void *dbuf, const void *sbuf, unsig static void drm_fb_xrgb8888_to_abgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, + struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 4, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_abgr8888_line); } @@ -699,13 +802,14 @@ static void drm_fb_xrgb8888_to_xbgr8888_line(void *dbuf, const void *sbuf, unsig static void drm_fb_xrgb8888_to_xbgr8888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, + struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 4, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_xbgr8888_line); } @@ -735,6 +839,7 @@ static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, un * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. Destination and framebuffer formats must match. The @@ -750,13 +855,14 @@ static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, un */ void drm_fb_xrgb8888_to_xrgb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, + struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 4, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_xrgb2101010_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010); @@ -788,6 +894,7 @@ static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, un * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts * the color format during the process. The parameters @dst, @dst_pitch and @@ -803,13 +910,14 @@ static void drm_fb_xrgb8888_to_argb2101010_line(void *dbuf, const void *sbuf, un */ void drm_fb_xrgb8888_to_argb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, + struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 4, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_argb2101010_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_argb2101010); @@ -839,6 +947,7 @@ static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. Destination and framebuffer formats must match. The @@ -858,13 +967,13 @@ static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned */ void drm_fb_xrgb8888_to_gray8(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const u8 dst_pixsize[DRM_FORMAT_MAX_PLANES] = { 1, }; - drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, + drm_fb_xfrm(dst, dst_pitch, dst_pixsize, src, fb, clip, false, state, drm_fb_xrgb8888_to_gray8_line); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); @@ -878,6 +987,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); * @src: The framebuffer memory to copy from * @fb: The framebuffer to copy from * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory. If the * formats of the display and the framebuffer mismatch, the blit function @@ -896,7 +1006,7 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); */ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { uint32_t fb_format = fb->format->format; @@ -904,44 +1014,44 @@ int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t d drm_fb_memcpy(dst, dst_pitch, src, fb, clip); return 0; } else if (fb_format == (dst_format | DRM_FORMAT_BIG_ENDIAN)) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false); + drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); return 0; } else if (fb_format == (dst_format & ~DRM_FORMAT_BIG_ENDIAN)) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false); + drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); return 0; } else if (fb_format == DRM_FORMAT_XRGB8888) { if (dst_format == DRM_FORMAT_RGB565) { - drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, false); + drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, state, false); return 0; } else if (dst_format == DRM_FORMAT_XRGB1555) { - drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_ARGB1555) { - drm_fb_xrgb8888_to_argb1555(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_argb1555(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_RGBA5551) { - drm_fb_xrgb8888_to_rgba5551(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_rgba5551(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_RGB888) { - drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_ARGB8888) { - drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_XBGR8888) { - drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_ABGR8888) { - drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_XRGB2101010) { - drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_ARGB2101010) { - drm_fb_xrgb8888_to_argb2101010(dst, dst_pitch, src, fb, clip); + drm_fb_xrgb8888_to_argb2101010(dst, dst_pitch, src, fb, clip, state); return 0; } else if (dst_format == DRM_FORMAT_BGRX8888) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false); + drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); return 0; } } @@ -978,6 +1088,7 @@ static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int * @src: Array of XRGB8888 source buffers * @fb: DRM framebuffer * @clip: Clip rectangle area to copy + * @state: Transform and conversion state * * This function copies parts of a framebuffer to display memory and converts the * color format during the process. Destination and framebuffer formats must match. The @@ -1002,7 +1113,7 @@ static void drm_fb_gray8_to_mono_line(void *dbuf, const void *sbuf, unsigned int */ void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip) + const struct drm_rect *clip, struct drm_format_conv_state *state) { static const unsigned int default_dst_pitch[DRM_FORMAT_MAX_PLANES] = { 0, 0, 0, 0 @@ -1042,7 +1153,7 @@ void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitc * Allocate a buffer to be used for both copying from the cma * memory and to store the intermediate grayscale line pixels. */ - src32 = kmalloc(len_src32 + linepixels, GFP_KERNEL); + src32 = drm_format_conv_state_reserve(state, len_src32 + linepixels, GFP_KERNEL); if (!src32) return; @@ -1056,8 +1167,6 @@ void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitc vaddr += fb->pitches[0]; mono += dst_pitch_0; } - - kfree(src32); } EXPORT_SYMBOL(drm_fb_xrgb8888_to_mono); diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index d3ba0698b84b..09e289fca5c3 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -394,6 +394,31 @@ static void drm_mode_rmfb_work_fn(struct work_struct *w) } } +static int drm_mode_closefb(struct drm_framebuffer *fb, + struct drm_file *file_priv) +{ + struct drm_framebuffer *fbl; + bool found = false; + + mutex_lock(&file_priv->fbs_lock); + list_for_each_entry(fbl, &file_priv->fbs, filp_head) + if (fb == fbl) + found = true; + + if (!found) { + mutex_unlock(&file_priv->fbs_lock); + return -ENOENT; + } + + list_del_init(&fb->filp_head); + mutex_unlock(&file_priv->fbs_lock); + + /* Drop the reference that was stored in the fbs list */ + drm_framebuffer_put(fb); + + return 0; +} + /** * drm_mode_rmfb - remove an FB from the configuration * @dev: drm device @@ -410,9 +435,8 @@ static void drm_mode_rmfb_work_fn(struct work_struct *w) int drm_mode_rmfb(struct drm_device *dev, u32 fb_id, struct drm_file *file_priv) { - struct drm_framebuffer *fb = NULL; - struct drm_framebuffer *fbl = NULL; - int found = 0; + struct drm_framebuffer *fb; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; @@ -421,24 +445,13 @@ int drm_mode_rmfb(struct drm_device *dev, u32 fb_id, if (!fb) return -ENOENT; - mutex_lock(&file_priv->fbs_lock); - list_for_each_entry(fbl, &file_priv->fbs, filp_head) - if (fb == fbl) - found = 1; - if (!found) { - mutex_unlock(&file_priv->fbs_lock); - goto fail_unref; + ret = drm_mode_closefb(fb, file_priv); + if (ret != 0) { + drm_framebuffer_put(fb); + return ret; } - list_del_init(&fb->filp_head); - mutex_unlock(&file_priv->fbs_lock); - - /* drop the reference we picked up in framebuffer lookup */ - drm_framebuffer_put(fb); - /* - * we now own the reference that was stored in the fbs list - * * drm_framebuffer_remove may fail with -EINTR on pending signals, * so run this in a separate stack as there's no way to correctly * handle this after the fb is already removed from the lookup table. @@ -457,10 +470,6 @@ int drm_mode_rmfb(struct drm_device *dev, u32 fb_id, drm_framebuffer_put(fb); return 0; - -fail_unref: - drm_framebuffer_put(fb); - return -ENOENT; } int drm_mode_rmfb_ioctl(struct drm_device *dev, @@ -471,6 +480,28 @@ int drm_mode_rmfb_ioctl(struct drm_device *dev, return drm_mode_rmfb(dev, *fb_id, file_priv); } +int drm_mode_closefb_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_closefb *r = data; + struct drm_framebuffer *fb; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EOPNOTSUPP; + + if (r->pad) + return -EINVAL; + + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); + if (!fb) + return -ENOENT; + + ret = drm_mode_closefb(fb, file_priv); + drm_framebuffer_put(fb); + return ret; +} + /** * drm_mode_getfb - get FB info * @dev: drm device for the ioctl diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index 5d4b9cd077f7..e440f458b663 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -218,7 +218,14 @@ void __drm_gem_duplicate_shadow_plane_state(struct drm_plane *plane, struct drm_shadow_plane_state *new_shadow_plane_state) { + struct drm_plane_state *plane_state = plane->state; + struct drm_shadow_plane_state *shadow_plane_state = + to_drm_shadow_plane_state(plane_state); + __drm_atomic_helper_plane_duplicate_state(plane, &new_shadow_plane_state->base); + + drm_format_conv_state_copy(&shadow_plane_state->fmtcnv_state, + &new_shadow_plane_state->fmtcnv_state); } EXPORT_SYMBOL(__drm_gem_duplicate_shadow_plane_state); @@ -266,6 +273,7 @@ EXPORT_SYMBOL(drm_gem_duplicate_shadow_plane_state); */ void __drm_gem_destroy_shadow_plane_state(struct drm_shadow_plane_state *shadow_plane_state) { + drm_format_conv_state_release(&shadow_plane_state->fmtcnv_state); __drm_atomic_helper_plane_destroy_state(&shadow_plane_state->base); } EXPORT_SYMBOL(__drm_gem_destroy_shadow_plane_state); @@ -302,6 +310,7 @@ void __drm_gem_reset_shadow_plane(struct drm_plane *plane, struct drm_shadow_plane_state *shadow_plane_state) { __drm_atomic_helper_plane_reset(plane, &shadow_plane_state->base); + drm_format_conv_state_init(&shadow_plane_state->fmtcnv_state); } EXPORT_SYMBOL(__drm_gem_reset_shadow_plane); diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index 08c088319652..54f5e8851de5 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -61,6 +61,42 @@ * contained within struct drm_gpuva already. Hence, for inserting &drm_gpuva * entries from within dma-fence signalling critical sections it is enough to * pre-allocate the &drm_gpuva structures. + * + * &drm_gem_objects which are private to a single VM can share a common + * &dma_resv in order to improve locking efficiency (e.g. with &drm_exec). + * For this purpose drivers must pass a &drm_gem_object to drm_gpuvm_init(), in + * the following called 'resv object', which serves as the container of the + * GPUVM's shared &dma_resv. This resv object can be a driver specific + * &drm_gem_object, such as the &drm_gem_object containing the root page table, + * but it can also be a 'dummy' object, which can be allocated with + * drm_gpuvm_resv_object_alloc(). + * + * In order to connect a struct drm_gpuva its backing &drm_gem_object each + * &drm_gem_object maintains a list of &drm_gpuvm_bo structures, and each + * &drm_gpuvm_bo contains a list of &drm_gpuva structures. + * + * A &drm_gpuvm_bo is an abstraction that represents a combination of a + * &drm_gpuvm and a &drm_gem_object. Every such combination should be unique. + * This is ensured by the API through drm_gpuvm_bo_obtain() and + * drm_gpuvm_bo_obtain_prealloc() which first look into the corresponding + * &drm_gem_object list of &drm_gpuvm_bos for an existing instance of this + * particular combination. If not existent a new instance is created and linked + * to the &drm_gem_object. + * + * &drm_gpuvm_bo structures, since unique for a given &drm_gpuvm, are also used + * as entry for the &drm_gpuvm's lists of external and evicted objects. Those + * lists are maintained in order to accelerate locking of dma-resv locks and + * validation of evicted objects bound in a &drm_gpuvm. For instance, all + * &drm_gem_object's &dma_resv of a given &drm_gpuvm can be locked by calling + * drm_gpuvm_exec_lock(). Once locked drivers can call drm_gpuvm_validate() in + * order to validate all evicted &drm_gem_objects. It is also possible to lock + * additional &drm_gem_objects by providing the corresponding parameters to + * drm_gpuvm_exec_lock() as well as open code the &drm_exec loop while making + * use of helper functions such as drm_gpuvm_prepare_range() or + * drm_gpuvm_prepare_objects(). + * + * Every bound &drm_gem_object is treated as external object when its &dma_resv + * structure is different than the &drm_gpuvm's common &dma_resv structure. */ /** @@ -386,21 +422,42 @@ /** * DOC: Locking * - * Generally, the GPU VA manager does not take care of locking itself, it is - * the drivers responsibility to take care about locking. Drivers might want to - * protect the following operations: inserting, removing and iterating - * &drm_gpuva objects as well as generating all kinds of operations, such as - * split / merge or prefetch. - * - * The GPU VA manager also does not take care of the locking of the backing - * &drm_gem_object buffers GPU VA lists by itself; drivers are responsible to - * enforce mutual exclusion using either the GEMs dma_resv lock or alternatively - * a driver specific external lock. For the latter see also - * drm_gem_gpuva_set_lock(). - * - * However, the GPU VA manager contains lockdep checks to ensure callers of its - * API hold the corresponding lock whenever the &drm_gem_objects GPU VA list is - * accessed by functions such as drm_gpuva_link() or drm_gpuva_unlink(). + * In terms of managing &drm_gpuva entries DRM GPUVM does not take care of + * locking itself, it is the drivers responsibility to take care about locking. + * Drivers might want to protect the following operations: inserting, removing + * and iterating &drm_gpuva objects as well as generating all kinds of + * operations, such as split / merge or prefetch. + * + * DRM GPUVM also does not take care of the locking of the backing + * &drm_gem_object buffers GPU VA lists and &drm_gpuvm_bo abstractions by + * itself; drivers are responsible to enforce mutual exclusion using either the + * GEMs dma_resv lock or alternatively a driver specific external lock. For the + * latter see also drm_gem_gpuva_set_lock(). + * + * However, DRM GPUVM contains lockdep checks to ensure callers of its API hold + * the corresponding lock whenever the &drm_gem_objects GPU VA list is accessed + * by functions such as drm_gpuva_link() or drm_gpuva_unlink(), but also + * drm_gpuvm_bo_obtain() and drm_gpuvm_bo_put(). + * + * The latter is required since on creation and destruction of a &drm_gpuvm_bo + * the &drm_gpuvm_bo is attached / removed from the &drm_gem_objects gpuva list. + * Subsequent calls to drm_gpuvm_bo_obtain() for the same &drm_gpuvm and + * &drm_gem_object must be able to observe previous creations and destructions + * of &drm_gpuvm_bos in order to keep instances unique. + * + * The &drm_gpuvm's lists for keeping track of external and evicted objects are + * protected against concurrent insertion / removal and iteration internally. + * + * However, drivers still need ensure to protect concurrent calls to functions + * iterating those lists, namely drm_gpuvm_prepare_objects() and + * drm_gpuvm_validate(). + * + * Alternatively, drivers can set the &DRM_GPUVM_RESV_PROTECTED flag to indicate + * that the corresponding &dma_resv locks are held in order to protect the + * lists. If &DRM_GPUVM_RESV_PROTECTED is set, internal locking is disabled and + * the corresponding lockdep checks are enabled. This is an optimization for + * drivers which are capable of taking the corresponding &dma_resv locks and + * hence do not require internal locking. */ /** @@ -430,6 +487,7 @@ * { * struct drm_gpuva_ops *ops; * struct drm_gpuva_op *op + * struct drm_gpuvm_bo *vm_bo; * * driver_lock_va_space(); * ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr, range, @@ -437,6 +495,10 @@ * if (IS_ERR(ops)) * return PTR_ERR(ops); * + * vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj); + * if (IS_ERR(vm_bo)) + * return PTR_ERR(vm_bo); + * * drm_gpuva_for_each_op(op, ops) { * struct drm_gpuva *va; * @@ -449,7 +511,7 @@ * * driver_vm_map(); * drm_gpuva_map(gpuvm, va, &op->map); - * drm_gpuva_link(va); + * drm_gpuva_link(va, vm_bo); * * break; * case DRM_GPUVA_OP_REMAP: { @@ -476,11 +538,11 @@ * driver_vm_remap(); * drm_gpuva_remap(prev, next, &op->remap); * - * drm_gpuva_unlink(va); * if (prev) - * drm_gpuva_link(prev); + * drm_gpuva_link(prev, va->vm_bo); * if (next) - * drm_gpuva_link(next); + * drm_gpuva_link(next, va->vm_bo); + * drm_gpuva_unlink(va); * * break; * } @@ -496,6 +558,7 @@ * break; * } * } + * drm_gpuvm_bo_put(vm_bo); * driver_unlock_va_space(); * * return 0; @@ -505,6 +568,7 @@ * * struct driver_context { * struct drm_gpuvm *gpuvm; + * struct drm_gpuvm_bo *vm_bo; * struct drm_gpuva *new_va; * struct drm_gpuva *prev_va; * struct drm_gpuva *next_va; @@ -525,6 +589,7 @@ * struct drm_gem_object *obj, u64 offset) * { * struct driver_context ctx; + * struct drm_gpuvm_bo *vm_bo; * struct drm_gpuva_ops *ops; * struct drm_gpuva_op *op; * int ret = 0; @@ -534,16 +599,23 @@ * ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL); * ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL); * ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL); - * if (!ctx.new_va || !ctx.prev_va || !ctx.next_va) { + * ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj); + * if (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) { * ret = -ENOMEM; * goto out; * } * + * // Typically protected with a driver specific GEM gpuva lock + * // used in the fence signaling path for drm_gpuva_link() and + * // drm_gpuva_unlink(), hence pre-allocate. + * ctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo); + * * driver_lock_va_space(); * ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range, obj, offset); * driver_unlock_va_space(); * * out: + * drm_gpuvm_bo_put(ctx.vm_bo); * kfree(ctx.new_va); * kfree(ctx.prev_va); * kfree(ctx.next_va); @@ -556,7 +628,7 @@ * * drm_gpuva_map(ctx->vm, ctx->new_va, &op->map); * - * drm_gpuva_link(ctx->new_va); + * drm_gpuva_link(ctx->new_va, ctx->vm_bo); * * // prevent the new GPUVA from being freed in * // driver_mapping_create() @@ -568,22 +640,23 @@ * int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx) * { * struct driver_context *ctx = __ctx; + * struct drm_gpuva *va = op->remap.unmap->va; * * drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op->remap); * - * drm_gpuva_unlink(op->remap.unmap->va); - * kfree(op->remap.unmap->va); - * * if (op->remap.prev) { - * drm_gpuva_link(ctx->prev_va); + * drm_gpuva_link(ctx->prev_va, va->vm_bo); * ctx->prev_va = NULL; * } * * if (op->remap.next) { - * drm_gpuva_link(ctx->next_va); + * drm_gpuva_link(ctx->next_va, va->vm_bo); * ctx->next_va = NULL; * } * + * drm_gpuva_unlink(va); + * kfree(va); + * * return 0; * } * @@ -597,6 +670,201 @@ * } */ +/** + * get_next_vm_bo_from_list() - get the next vm_bo element + * @__gpuvm: the &drm_gpuvm + * @__list_name: the name of the list we're iterating on + * @__local_list: a pointer to the local list used to store already iterated items + * @__prev_vm_bo: the previous element we got from get_next_vm_bo_from_list() + * + * This helper is here to provide lockless list iteration. Lockless as in, the + * iterator releases the lock immediately after picking the first element from + * the list, so list insertion deletion can happen concurrently. + * + * Elements popped from the original list are kept in a local list, so removal + * and is_empty checks can still happen while we're iterating the list. + */ +#define get_next_vm_bo_from_list(__gpuvm, __list_name, __local_list, __prev_vm_bo) \ + ({ \ + struct drm_gpuvm_bo *__vm_bo = NULL; \ + \ + drm_gpuvm_bo_put(__prev_vm_bo); \ + \ + spin_lock(&(__gpuvm)->__list_name.lock); \ + if (!(__gpuvm)->__list_name.local_list) \ + (__gpuvm)->__list_name.local_list = __local_list; \ + else \ + drm_WARN_ON((__gpuvm)->drm, \ + (__gpuvm)->__list_name.local_list != __local_list); \ + \ + while (!list_empty(&(__gpuvm)->__list_name.list)) { \ + __vm_bo = list_first_entry(&(__gpuvm)->__list_name.list, \ + struct drm_gpuvm_bo, \ + list.entry.__list_name); \ + if (kref_get_unless_zero(&__vm_bo->kref)) { \ + list_move_tail(&(__vm_bo)->list.entry.__list_name, \ + __local_list); \ + break; \ + } else { \ + list_del_init(&(__vm_bo)->list.entry.__list_name); \ + __vm_bo = NULL; \ + } \ + } \ + spin_unlock(&(__gpuvm)->__list_name.lock); \ + \ + __vm_bo; \ + }) + +/** + * for_each_vm_bo_in_list() - internal vm_bo list iterator + * @__gpuvm: the &drm_gpuvm + * @__list_name: the name of the list we're iterating on + * @__local_list: a pointer to the local list used to store already iterated items + * @__vm_bo: the struct drm_gpuvm_bo to assign in each iteration step + * + * This helper is here to provide lockless list iteration. Lockless as in, the + * iterator releases the lock immediately after picking the first element from the + * list, hence list insertion and deletion can happen concurrently. + * + * It is not allowed to re-assign the vm_bo pointer from inside this loop. + * + * Typical use: + * + * struct drm_gpuvm_bo *vm_bo; + * LIST_HEAD(my_local_list); + * + * ret = 0; + * for_each_vm_bo_in_list(gpuvm, <list_name>, &my_local_list, vm_bo) { + * ret = do_something_with_vm_bo(..., vm_bo); + * if (ret) + * break; + * } + * // Drop ref in case we break out of the loop. + * drm_gpuvm_bo_put(vm_bo); + * restore_vm_bo_list(gpuvm, <list_name>, &my_local_list); + * + * + * Only used for internal list iterations, not meant to be exposed to the outside + * world. + */ +#define for_each_vm_bo_in_list(__gpuvm, __list_name, __local_list, __vm_bo) \ + for (__vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name, \ + __local_list, NULL); \ + __vm_bo; \ + __vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name, \ + __local_list, __vm_bo)) + +static void +__restore_vm_bo_list(struct drm_gpuvm *gpuvm, spinlock_t *lock, + struct list_head *list, struct list_head **local_list) +{ + /* Merge back the two lists, moving local list elements to the + * head to preserve previous ordering, in case it matters. + */ + spin_lock(lock); + if (*local_list) { + list_splice(*local_list, list); + *local_list = NULL; + } + spin_unlock(lock); +} + +/** + * restore_vm_bo_list() - move vm_bo elements back to their original list + * @__gpuvm: the &drm_gpuvm + * @__list_name: the name of the list we're iterating on + * + * When we're done iterating a vm_bo list, we should call restore_vm_bo_list() + * to restore the original state and let new iterations take place. + */ +#define restore_vm_bo_list(__gpuvm, __list_name) \ + __restore_vm_bo_list((__gpuvm), &(__gpuvm)->__list_name.lock, \ + &(__gpuvm)->__list_name.list, \ + &(__gpuvm)->__list_name.local_list) + +static void +cond_spin_lock(spinlock_t *lock, bool cond) +{ + if (cond) + spin_lock(lock); +} + +static void +cond_spin_unlock(spinlock_t *lock, bool cond) +{ + if (cond) + spin_unlock(lock); +} + +static void +__drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock, + struct list_head *entry, struct list_head *list) +{ + cond_spin_lock(lock, !!lock); + if (list_empty(entry)) + list_add_tail(entry, list); + cond_spin_unlock(lock, !!lock); +} + +/** + * drm_gpuvm_bo_list_add() - insert a vm_bo into the given list + * @__vm_bo: the &drm_gpuvm_bo + * @__list_name: the name of the list to insert into + * @__lock: whether to lock with the internal spinlock + * + * Inserts the given @__vm_bo into the list specified by @__list_name. + */ +#define drm_gpuvm_bo_list_add(__vm_bo, __list_name, __lock) \ + __drm_gpuvm_bo_list_add((__vm_bo)->vm, \ + __lock ? &(__vm_bo)->vm->__list_name.lock : \ + NULL, \ + &(__vm_bo)->list.entry.__list_name, \ + &(__vm_bo)->vm->__list_name.list) + +static void +__drm_gpuvm_bo_list_del(struct drm_gpuvm *gpuvm, spinlock_t *lock, + struct list_head *entry, bool init) +{ + cond_spin_lock(lock, !!lock); + if (init) { + if (!list_empty(entry)) + list_del_init(entry); + } else { + list_del(entry); + } + cond_spin_unlock(lock, !!lock); +} + +/** + * drm_gpuvm_bo_list_del_init() - remove a vm_bo from the given list + * @__vm_bo: the &drm_gpuvm_bo + * @__list_name: the name of the list to insert into + * @__lock: whether to lock with the internal spinlock + * + * Removes the given @__vm_bo from the list specified by @__list_name. + */ +#define drm_gpuvm_bo_list_del_init(__vm_bo, __list_name, __lock) \ + __drm_gpuvm_bo_list_del((__vm_bo)->vm, \ + __lock ? &(__vm_bo)->vm->__list_name.lock : \ + NULL, \ + &(__vm_bo)->list.entry.__list_name, \ + true) + +/** + * drm_gpuvm_bo_list_del() - remove a vm_bo from the given list + * @__vm_bo: the &drm_gpuvm_bo + * @__list_name: the name of the list to insert into + * @__lock: whether to lock with the internal spinlock + * + * Removes the given @__vm_bo from the list specified by @__list_name. + */ +#define drm_gpuvm_bo_list_del(__vm_bo, __list_name, __lock) \ + __drm_gpuvm_bo_list_del((__vm_bo)->vm, \ + __lock ? &(__vm_bo)->vm->__list_name.lock : \ + NULL, \ + &(__vm_bo)->list.entry.__list_name, \ + false) + #define to_drm_gpuva(__node) container_of((__node), struct drm_gpuva, rb.node) #define GPUVA_START(node) ((node)->va.addr) @@ -618,8 +886,14 @@ drm_gpuvm_check_overflow(u64 addr, u64 range) { u64 end; - return WARN(check_add_overflow(addr, range, &end), - "GPUVA address limited to %zu bytes.\n", sizeof(end)); + return check_add_overflow(addr, range, &end); +} + +static bool +drm_gpuvm_warn_check_overflow(struct drm_gpuvm *gpuvm, u64 addr, u64 range) +{ + return drm_WARN(gpuvm->drm, drm_gpuvm_check_overflow(addr, range), + "GPUVA address limited to %zu bytes.\n", sizeof(addr)); } static bool @@ -643,7 +917,18 @@ drm_gpuvm_in_kernel_node(struct drm_gpuvm *gpuvm, u64 addr, u64 range) return krange && addr < kend && kstart < end; } -static bool +/** + * drm_gpuvm_range_valid() - checks whether the given range is valid for the + * given &drm_gpuvm + * @gpuvm: the GPUVM to check the range for + * @addr: the base address + * @range: the range starting from the base address + * + * Checks whether the range is within the GPUVM's managed boundaries. + * + * Returns: true for a valid range, false otherwise + */ +bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { @@ -651,11 +936,52 @@ drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, drm_gpuvm_in_mm_range(gpuvm, addr, range) && !drm_gpuvm_in_kernel_node(gpuvm, addr, range); } +EXPORT_SYMBOL_GPL(drm_gpuvm_range_valid); + +static void +drm_gpuvm_gem_object_free(struct drm_gem_object *obj) +{ + drm_gem_object_release(obj); + kfree(obj); +} + +static const struct drm_gem_object_funcs drm_gpuvm_object_funcs = { + .free = drm_gpuvm_gem_object_free, +}; + +/** + * drm_gpuvm_resv_object_alloc() - allocate a dummy &drm_gem_object + * @drm: the drivers &drm_device + * + * Allocates a dummy &drm_gem_object which can be passed to drm_gpuvm_init() in + * order to serve as root GEM object providing the &drm_resv shared across + * &drm_gem_objects local to a single GPUVM. + * + * Returns: the &drm_gem_object on success, NULL on failure + */ +struct drm_gem_object * +drm_gpuvm_resv_object_alloc(struct drm_device *drm) +{ + struct drm_gem_object *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return NULL; + + obj->funcs = &drm_gpuvm_object_funcs; + drm_gem_private_object_init(drm, obj, 0); + + return obj; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_resv_object_alloc); /** * drm_gpuvm_init() - initialize a &drm_gpuvm * @gpuvm: pointer to the &drm_gpuvm to initialize * @name: the name of the GPU VA space + * @flags: the &drm_gpuvm_flags for this GPUVM + * @drm: the &drm_device this VM resides in + * @r_obj: the resv &drm_gem_object providing the GPUVM's common &dma_resv * @start_offset: the start offset of the GPU VA space * @range: the size of the GPU VA space * @reserve_offset: the start of the kernel reserved GPU VA area @@ -668,8 +994,10 @@ drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, * &name is expected to be managed by the surrounding driver structures. */ void -drm_gpuvm_init(struct drm_gpuvm *gpuvm, - const char *name, +drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, + enum drm_gpuvm_flags flags, + struct drm_device *drm, + struct drm_gem_object *r_obj, u64 start_offset, u64 range, u64 reserve_offset, u64 reserve_range, const struct drm_gpuvm_ops *ops) @@ -677,45 +1005,676 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, gpuvm->rb.tree = RB_ROOT_CACHED; INIT_LIST_HEAD(&gpuvm->rb.list); - drm_gpuvm_check_overflow(start_offset, range); - gpuvm->mm_start = start_offset; - gpuvm->mm_range = range; + INIT_LIST_HEAD(&gpuvm->extobj.list); + spin_lock_init(&gpuvm->extobj.lock); + + INIT_LIST_HEAD(&gpuvm->evict.list); + spin_lock_init(&gpuvm->evict.lock); + + kref_init(&gpuvm->kref); gpuvm->name = name ? name : "unknown"; + gpuvm->flags = flags; gpuvm->ops = ops; + gpuvm->drm = drm; + gpuvm->r_obj = r_obj; - memset(&gpuvm->kernel_alloc_node, 0, sizeof(struct drm_gpuva)); + drm_gem_object_get(r_obj); + + drm_gpuvm_warn_check_overflow(gpuvm, start_offset, range); + gpuvm->mm_start = start_offset; + gpuvm->mm_range = range; + memset(&gpuvm->kernel_alloc_node, 0, sizeof(struct drm_gpuva)); if (reserve_range) { gpuvm->kernel_alloc_node.va.addr = reserve_offset; gpuvm->kernel_alloc_node.va.range = reserve_range; - if (likely(!drm_gpuvm_check_overflow(reserve_offset, - reserve_range))) + if (likely(!drm_gpuvm_warn_check_overflow(gpuvm, reserve_offset, + reserve_range))) __drm_gpuva_insert(gpuvm, &gpuvm->kernel_alloc_node); } } EXPORT_SYMBOL_GPL(drm_gpuvm_init); +static void +drm_gpuvm_fini(struct drm_gpuvm *gpuvm) +{ + gpuvm->name = NULL; + + if (gpuvm->kernel_alloc_node.va.range) + __drm_gpuva_remove(&gpuvm->kernel_alloc_node); + + drm_WARN(gpuvm->drm, !RB_EMPTY_ROOT(&gpuvm->rb.tree.rb_root), + "GPUVA tree is not empty, potentially leaking memory.\n"); + + drm_WARN(gpuvm->drm, !list_empty(&gpuvm->extobj.list), + "Extobj list should be empty.\n"); + drm_WARN(gpuvm->drm, !list_empty(&gpuvm->evict.list), + "Evict list should be empty.\n"); + + drm_gem_object_put(gpuvm->r_obj); +} + +static void +drm_gpuvm_free(struct kref *kref) +{ + struct drm_gpuvm *gpuvm = container_of(kref, struct drm_gpuvm, kref); + + drm_gpuvm_fini(gpuvm); + + if (drm_WARN_ON(gpuvm->drm, !gpuvm->ops->vm_free)) + return; + + gpuvm->ops->vm_free(gpuvm); +} + /** - * drm_gpuvm_destroy() - cleanup a &drm_gpuvm - * @gpuvm: pointer to the &drm_gpuvm to clean up + * drm_gpuvm_put() - drop a struct drm_gpuvm reference + * @gpuvm: the &drm_gpuvm to release the reference of + * + * This releases a reference to @gpuvm. * - * Note that it is a bug to call this function on a manager that still - * holds GPU VA mappings. + * This function may be called from atomic context. */ void -drm_gpuvm_destroy(struct drm_gpuvm *gpuvm) +drm_gpuvm_put(struct drm_gpuvm *gpuvm) { - gpuvm->name = NULL; + if (gpuvm) + kref_put(&gpuvm->kref, drm_gpuvm_free); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_put); - if (gpuvm->kernel_alloc_node.va.range) - __drm_gpuva_remove(&gpuvm->kernel_alloc_node); +static int +__drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences) +{ + struct drm_gpuvm_bo *vm_bo; + LIST_HEAD(extobjs); + int ret = 0; + + for_each_vm_bo_in_list(gpuvm, extobj, &extobjs, vm_bo) { + ret = drm_exec_prepare_obj(exec, vm_bo->obj, num_fences); + if (ret) + break; + } + /* Drop ref in case we break out of the loop. */ + drm_gpuvm_bo_put(vm_bo); + restore_vm_bo_list(gpuvm, extobj); + + return ret; +} + +static int +drm_gpuvm_prepare_objects_locked(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences) +{ + struct drm_gpuvm_bo *vm_bo; + int ret = 0; + + drm_gpuvm_resv_assert_held(gpuvm); + list_for_each_entry(vm_bo, &gpuvm->extobj.list, list.entry.extobj) { + ret = drm_exec_prepare_obj(exec, vm_bo->obj, num_fences); + if (ret) + break; + + if (vm_bo->evicted) + drm_gpuvm_bo_list_add(vm_bo, evict, false); + } + + return ret; +} + +/** + * drm_gpuvm_prepare_objects() - prepare all assoiciated BOs + * @gpuvm: the &drm_gpuvm + * @exec: the &drm_exec locking context + * @num_fences: the amount of &dma_fences to reserve + * + * Calls drm_exec_prepare_obj() for all &drm_gem_objects the given + * &drm_gpuvm contains mappings of. + * + * Using this function directly, it is the drivers responsibility to call + * drm_exec_init() and drm_exec_fini() accordingly. + * + * Note: This function is safe against concurrent insertion and removal of + * external objects, however it is not safe against concurrent usage itself. + * + * Drivers need to make sure to protect this case with either an outer VM lock + * or by calling drm_gpuvm_prepare_vm() before this function within the + * drm_exec_until_all_locked() loop, such that the GPUVM's dma-resv lock ensures + * mutual exclusion. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences) +{ + if (drm_gpuvm_resv_protected(gpuvm)) + return drm_gpuvm_prepare_objects_locked(gpuvm, exec, + num_fences); + else + return __drm_gpuvm_prepare_objects(gpuvm, exec, num_fences); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_objects); + +/** + * drm_gpuvm_prepare_range() - prepare all BOs mapped within a given range + * @gpuvm: the &drm_gpuvm + * @exec: the &drm_exec locking context + * @addr: the start address within the VA space + * @range: the range to iterate within the VA space + * @num_fences: the amount of &dma_fences to reserve + * + * Calls drm_exec_prepare_obj() for all &drm_gem_objects mapped between @addr + * and @addr + @range. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec, + u64 addr, u64 range, unsigned int num_fences) +{ + struct drm_gpuva *va; + u64 end = addr + range; + int ret; + + drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) { + struct drm_gem_object *obj = va->gem.obj; + + ret = drm_exec_prepare_obj(exec, obj, num_fences); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_prepare_range); + +/** + * drm_gpuvm_exec_lock() - lock all dma-resv of all assoiciated BOs + * @vm_exec: the &drm_gpuvm_exec wrapper + * + * Acquires all dma-resv locks of all &drm_gem_objects the given + * &drm_gpuvm contains mappings of. + * + * Addionally, when calling this function with struct drm_gpuvm_exec::extra + * being set the driver receives the given @fn callback to lock additional + * dma-resv in the context of the &drm_gpuvm_exec instance. Typically, drivers + * would call drm_exec_prepare_obj() from within this callback. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_exec_lock(struct drm_gpuvm_exec *vm_exec) +{ + struct drm_gpuvm *gpuvm = vm_exec->vm; + struct drm_exec *exec = &vm_exec->exec; + unsigned int num_fences = vm_exec->num_fences; + int ret; + + drm_exec_init(exec, vm_exec->flags); + + drm_exec_until_all_locked(exec) { + ret = drm_gpuvm_prepare_vm(gpuvm, exec, num_fences); + drm_exec_retry_on_contention(exec); + if (ret) + goto err; + + ret = drm_gpuvm_prepare_objects(gpuvm, exec, num_fences); + drm_exec_retry_on_contention(exec); + if (ret) + goto err; + + if (vm_exec->extra.fn) { + ret = vm_exec->extra.fn(vm_exec); + drm_exec_retry_on_contention(exec); + if (ret) + goto err; + } + } + + return 0; + +err: + drm_exec_fini(exec); + return ret; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock); - WARN(!RB_EMPTY_ROOT(&gpuvm->rb.tree.rb_root), - "GPUVA tree is not empty, potentially leaking memory."); +static int +fn_lock_array(struct drm_gpuvm_exec *vm_exec) +{ + struct { + struct drm_gem_object **objs; + unsigned int num_objs; + } *args = vm_exec->extra.priv; + + return drm_exec_prepare_array(&vm_exec->exec, args->objs, + args->num_objs, vm_exec->num_fences); +} + +/** + * drm_gpuvm_exec_lock_array() - lock all dma-resv of all assoiciated BOs + * @vm_exec: the &drm_gpuvm_exec wrapper + * @objs: additional &drm_gem_objects to lock + * @num_objs: the number of additional &drm_gem_objects to lock + * + * Acquires all dma-resv locks of all &drm_gem_objects the given &drm_gpuvm + * contains mappings of, plus the ones given through @objs. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_exec_lock_array(struct drm_gpuvm_exec *vm_exec, + struct drm_gem_object **objs, + unsigned int num_objs) +{ + struct { + struct drm_gem_object **objs; + unsigned int num_objs; + } args; + + args.objs = objs; + args.num_objs = num_objs; + + vm_exec->extra.fn = fn_lock_array; + vm_exec->extra.priv = &args; + + return drm_gpuvm_exec_lock(vm_exec); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock_array); + +/** + * drm_gpuvm_exec_lock_range() - prepare all BOs mapped within a given range + * @vm_exec: the &drm_gpuvm_exec wrapper + * @addr: the start address within the VA space + * @range: the range to iterate within the VA space + * + * Acquires all dma-resv locks of all &drm_gem_objects mapped between @addr and + * @addr + @range. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_exec_lock_range(struct drm_gpuvm_exec *vm_exec, + u64 addr, u64 range) +{ + struct drm_gpuvm *gpuvm = vm_exec->vm; + struct drm_exec *exec = &vm_exec->exec; + int ret; + + drm_exec_init(exec, vm_exec->flags); + + drm_exec_until_all_locked(exec) { + ret = drm_gpuvm_prepare_range(gpuvm, exec, addr, range, + vm_exec->num_fences); + drm_exec_retry_on_contention(exec); + if (ret) + goto err; + } + + return ret; + +err: + drm_exec_fini(exec); + return ret; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_exec_lock_range); + +static int +__drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec) +{ + const struct drm_gpuvm_ops *ops = gpuvm->ops; + struct drm_gpuvm_bo *vm_bo; + LIST_HEAD(evict); + int ret = 0; + + for_each_vm_bo_in_list(gpuvm, evict, &evict, vm_bo) { + ret = ops->vm_bo_validate(vm_bo, exec); + if (ret) + break; + } + /* Drop ref in case we break out of the loop. */ + drm_gpuvm_bo_put(vm_bo); + restore_vm_bo_list(gpuvm, evict); + + return ret; +} + +static int +drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec) +{ + const struct drm_gpuvm_ops *ops = gpuvm->ops; + struct drm_gpuvm_bo *vm_bo, *next; + int ret = 0; + + drm_gpuvm_resv_assert_held(gpuvm); + + list_for_each_entry_safe(vm_bo, next, &gpuvm->evict.list, + list.entry.evict) { + ret = ops->vm_bo_validate(vm_bo, exec); + if (ret) + break; + + dma_resv_assert_held(vm_bo->obj->resv); + if (!vm_bo->evicted) + drm_gpuvm_bo_list_del_init(vm_bo, evict, false); + } + + return ret; +} + +/** + * drm_gpuvm_validate() - validate all BOs marked as evicted + * @gpuvm: the &drm_gpuvm to validate evicted BOs + * @exec: the &drm_exec instance used for locking the GPUVM + * + * Calls the &drm_gpuvm_ops::vm_bo_validate callback for all evicted buffer + * objects being mapped in the given &drm_gpuvm. + * + * Returns: 0 on success, negative error code on failure. + */ +int +drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec) +{ + const struct drm_gpuvm_ops *ops = gpuvm->ops; + + if (unlikely(!ops || !ops->vm_bo_validate)) + return -EOPNOTSUPP; + + if (drm_gpuvm_resv_protected(gpuvm)) + return drm_gpuvm_validate_locked(gpuvm, exec); + else + return __drm_gpuvm_validate(gpuvm, exec); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_validate); + +/** + * drm_gpuvm_resv_add_fence - add fence to private and all extobj + * dma-resv + * @gpuvm: the &drm_gpuvm to add a fence to + * @exec: the &drm_exec locking context + * @fence: fence to add + * @private_usage: private dma-resv usage + * @extobj_usage: extobj dma-resv usage + */ +void +drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + struct dma_fence *fence, + enum dma_resv_usage private_usage, + enum dma_resv_usage extobj_usage) +{ + struct drm_gem_object *obj; + unsigned long index; + + drm_exec_for_each_locked_object(exec, index, obj) { + dma_resv_assert_held(obj->resv); + dma_resv_add_fence(obj->resv, fence, + drm_gpuvm_is_extobj(gpuvm, obj) ? + extobj_usage : private_usage); + } +} +EXPORT_SYMBOL_GPL(drm_gpuvm_resv_add_fence); + +/** + * drm_gpuvm_bo_create() - create a new instance of struct drm_gpuvm_bo + * @gpuvm: The &drm_gpuvm the @obj is mapped in. + * @obj: The &drm_gem_object being mapped in the @gpuvm. + * + * If provided by the driver, this function uses the &drm_gpuvm_ops + * vm_bo_alloc() callback to allocate. + * + * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure + */ +struct drm_gpuvm_bo * +drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + const struct drm_gpuvm_ops *ops = gpuvm->ops; + struct drm_gpuvm_bo *vm_bo; + + if (ops && ops->vm_bo_alloc) + vm_bo = ops->vm_bo_alloc(); + else + vm_bo = kzalloc(sizeof(*vm_bo), GFP_KERNEL); + + if (unlikely(!vm_bo)) + return NULL; + + vm_bo->vm = drm_gpuvm_get(gpuvm); + vm_bo->obj = obj; + drm_gem_object_get(obj); + + kref_init(&vm_bo->kref); + INIT_LIST_HEAD(&vm_bo->list.gpuva); + INIT_LIST_HEAD(&vm_bo->list.entry.gem); + + INIT_LIST_HEAD(&vm_bo->list.entry.extobj); + INIT_LIST_HEAD(&vm_bo->list.entry.evict); + + return vm_bo; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_create); + +static void +drm_gpuvm_bo_destroy(struct kref *kref) +{ + struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo, + kref); + struct drm_gpuvm *gpuvm = vm_bo->vm; + const struct drm_gpuvm_ops *ops = gpuvm->ops; + struct drm_gem_object *obj = vm_bo->obj; + bool lock = !drm_gpuvm_resv_protected(gpuvm); + + if (!lock) + drm_gpuvm_resv_assert_held(gpuvm); + + drm_gpuvm_bo_list_del(vm_bo, extobj, lock); + drm_gpuvm_bo_list_del(vm_bo, evict, lock); + + drm_gem_gpuva_assert_lock_held(obj); + list_del(&vm_bo->list.entry.gem); + + if (ops && ops->vm_bo_free) + ops->vm_bo_free(vm_bo); + else + kfree(vm_bo); + + drm_gpuvm_put(gpuvm); + drm_gem_object_put(obj); +} + +/** + * drm_gpuvm_bo_put() - drop a struct drm_gpuvm_bo reference + * @vm_bo: the &drm_gpuvm_bo to release the reference of + * + * This releases a reference to @vm_bo. + * + * If the reference count drops to zero, the &gpuvm_bo is destroyed, which + * includes removing it from the GEMs gpuva list. Hence, if a call to this + * function can potentially let the reference count drop to zero the caller must + * hold the dma-resv or driver specific GEM gpuva lock. + * + * This function may only be called from non-atomic context. + */ +void +drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo) +{ + might_sleep(); + + if (vm_bo) + kref_put(&vm_bo->kref, drm_gpuvm_bo_destroy); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put); + +static struct drm_gpuvm_bo * +__drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + struct drm_gpuvm_bo *vm_bo; + + drm_gem_gpuva_assert_lock_held(obj); + drm_gem_for_each_gpuvm_bo(vm_bo, obj) + if (vm_bo->vm == gpuvm) + return vm_bo; + + return NULL; +} + +/** + * drm_gpuvm_bo_find() - find the &drm_gpuvm_bo for the given + * &drm_gpuvm and &drm_gem_object + * @gpuvm: The &drm_gpuvm the @obj is mapped in. + * @obj: The &drm_gem_object being mapped in the @gpuvm. + * + * Find the &drm_gpuvm_bo representing the combination of the given + * &drm_gpuvm and &drm_gem_object. If found, increases the reference + * count of the &drm_gpuvm_bo accordingly. + * + * Returns: a pointer to the &drm_gpuvm_bo on success, NULL on failure + */ +struct drm_gpuvm_bo * +drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + struct drm_gpuvm_bo *vm_bo = __drm_gpuvm_bo_find(gpuvm, obj); + + return vm_bo ? drm_gpuvm_bo_get(vm_bo) : NULL; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_find); + +/** + * drm_gpuvm_bo_obtain() - obtains and instance of the &drm_gpuvm_bo for the + * given &drm_gpuvm and &drm_gem_object + * @gpuvm: The &drm_gpuvm the @obj is mapped in. + * @obj: The &drm_gem_object being mapped in the @gpuvm. + * + * Find the &drm_gpuvm_bo representing the combination of the given + * &drm_gpuvm and &drm_gem_object. If found, increases the reference + * count of the &drm_gpuvm_bo accordingly. If not found, allocates a new + * &drm_gpuvm_bo. + * + * A new &drm_gpuvm_bo is added to the GEMs gpuva list. + * + * Returns: a pointer to the &drm_gpuvm_bo on success, an ERR_PTR on failure + */ +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + struct drm_gpuvm_bo *vm_bo; + + vm_bo = drm_gpuvm_bo_find(gpuvm, obj); + if (vm_bo) + return vm_bo; + + vm_bo = drm_gpuvm_bo_create(gpuvm, obj); + if (!vm_bo) + return ERR_PTR(-ENOMEM); + + drm_gem_gpuva_assert_lock_held(obj); + list_add_tail(&vm_bo->list.entry.gem, &obj->gpuva.list); + + return vm_bo; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain); + +/** + * drm_gpuvm_bo_obtain_prealloc() - obtains and instance of the &drm_gpuvm_bo + * for the given &drm_gpuvm and &drm_gem_object + * @__vm_bo: A pre-allocated struct drm_gpuvm_bo. + * + * Find the &drm_gpuvm_bo representing the combination of the given + * &drm_gpuvm and &drm_gem_object. If found, increases the reference + * count of the found &drm_gpuvm_bo accordingly, while the @__vm_bo reference + * count is decreased. If not found @__vm_bo is returned without further + * increase of the reference count. + * + * A new &drm_gpuvm_bo is added to the GEMs gpuva list. + * + * Returns: a pointer to the found &drm_gpuvm_bo or @__vm_bo if no existing + * &drm_gpuvm_bo was found + */ +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *__vm_bo) +{ + struct drm_gpuvm *gpuvm = __vm_bo->vm; + struct drm_gem_object *obj = __vm_bo->obj; + struct drm_gpuvm_bo *vm_bo; + + vm_bo = drm_gpuvm_bo_find(gpuvm, obj); + if (vm_bo) { + drm_gpuvm_bo_put(__vm_bo); + return vm_bo; + } + + drm_gem_gpuva_assert_lock_held(obj); + list_add_tail(&__vm_bo->list.entry.gem, &obj->gpuva.list); + + return __vm_bo; +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_obtain_prealloc); + +/** + * drm_gpuvm_bo_extobj_add() - adds the &drm_gpuvm_bo to its &drm_gpuvm's + * extobj list + * @vm_bo: The &drm_gpuvm_bo to add to its &drm_gpuvm's the extobj list. + * + * Adds the given @vm_bo to its &drm_gpuvm's extobj list if not on the list + * already and if the corresponding &drm_gem_object is an external object, + * actually. + */ +void +drm_gpuvm_bo_extobj_add(struct drm_gpuvm_bo *vm_bo) +{ + struct drm_gpuvm *gpuvm = vm_bo->vm; + bool lock = !drm_gpuvm_resv_protected(gpuvm); + + if (!lock) + drm_gpuvm_resv_assert_held(gpuvm); + + if (drm_gpuvm_is_extobj(gpuvm, vm_bo->obj)) + drm_gpuvm_bo_list_add(vm_bo, extobj, lock); } -EXPORT_SYMBOL_GPL(drm_gpuvm_destroy); +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_extobj_add); + +/** + * drm_gpuvm_bo_evict() - add / remove a &drm_gpuvm_bo to / from the &drm_gpuvms + * evicted list + * @vm_bo: the &drm_gpuvm_bo to add or remove + * @evict: indicates whether the object is evicted + * + * Adds a &drm_gpuvm_bo to or removes it from the &drm_gpuvms evicted list. + */ +void +drm_gpuvm_bo_evict(struct drm_gpuvm_bo *vm_bo, bool evict) +{ + struct drm_gpuvm *gpuvm = vm_bo->vm; + struct drm_gem_object *obj = vm_bo->obj; + bool lock = !drm_gpuvm_resv_protected(gpuvm); + + dma_resv_assert_held(obj->resv); + vm_bo->evicted = evict; + + /* Can't add external objects to the evicted list directly if not using + * internal spinlocks, since in this case the evicted list is protected + * with the VM's common dma-resv lock. + */ + if (drm_gpuvm_is_extobj(gpuvm, obj) && !lock) + return; + + if (evict) + drm_gpuvm_bo_list_add(vm_bo, evict, lock); + else + drm_gpuvm_bo_list_del_init(vm_bo, evict, lock); +} +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_evict); static int __drm_gpuva_insert(struct drm_gpuvm *gpuvm, @@ -764,11 +1723,21 @@ drm_gpuva_insert(struct drm_gpuvm *gpuvm, { u64 addr = va->va.addr; u64 range = va->va.range; + int ret; if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range))) return -EINVAL; - return __drm_gpuva_insert(gpuvm, va); + ret = __drm_gpuva_insert(gpuvm, va); + if (likely(!ret)) + /* Take a reference of the GPUVM for the successfully inserted + * drm_gpuva. We can't take the reference in + * __drm_gpuva_insert() itself, since we don't want to increse + * the reference count for the GPUVM's kernel_alloc_node. + */ + drm_gpuvm_get(gpuvm); + + return ret; } EXPORT_SYMBOL_GPL(drm_gpuva_insert); @@ -795,35 +1764,46 @@ drm_gpuva_remove(struct drm_gpuva *va) struct drm_gpuvm *gpuvm = va->vm; if (unlikely(va == &gpuvm->kernel_alloc_node)) { - WARN(1, "Can't destroy kernel reserved node.\n"); + drm_WARN(gpuvm->drm, 1, + "Can't destroy kernel reserved node.\n"); return; } __drm_gpuva_remove(va); + drm_gpuvm_put(va->vm); } EXPORT_SYMBOL_GPL(drm_gpuva_remove); /** * drm_gpuva_link() - link a &drm_gpuva * @va: the &drm_gpuva to link + * @vm_bo: the &drm_gpuvm_bo to add the &drm_gpuva to * - * This adds the given &va to the GPU VA list of the &drm_gem_object it is - * associated with. + * This adds the given &va to the GPU VA list of the &drm_gpuvm_bo and the + * &drm_gpuvm_bo to the &drm_gem_object it is associated with. + * + * For every &drm_gpuva entry added to the &drm_gpuvm_bo an additional + * reference of the latter is taken. * * This function expects the caller to protect the GEM's GPUVA list against - * concurrent access using the GEMs dma_resv lock. + * concurrent access using either the GEMs dma_resv lock or a driver specific + * lock set through drm_gem_gpuva_set_lock(). */ void -drm_gpuva_link(struct drm_gpuva *va) +drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo) { struct drm_gem_object *obj = va->gem.obj; + struct drm_gpuvm *gpuvm = va->vm; if (unlikely(!obj)) return; - drm_gem_gpuva_assert_lock_held(obj); + drm_WARN_ON(gpuvm->drm, obj != vm_bo->obj); + + va->vm_bo = drm_gpuvm_bo_get(vm_bo); - list_add_tail(&va->gem.entry, &obj->gpuva.list); + drm_gem_gpuva_assert_lock_held(obj); + list_add_tail(&va->gem.entry, &vm_bo->list.gpuva); } EXPORT_SYMBOL_GPL(drm_gpuva_link); @@ -834,20 +1814,31 @@ EXPORT_SYMBOL_GPL(drm_gpuva_link); * This removes the given &va from the GPU VA list of the &drm_gem_object it is * associated with. * + * This removes the given &va from the GPU VA list of the &drm_gpuvm_bo and + * the &drm_gpuvm_bo from the &drm_gem_object it is associated with in case + * this call unlinks the last &drm_gpuva from the &drm_gpuvm_bo. + * + * For every &drm_gpuva entry removed from the &drm_gpuvm_bo a reference of + * the latter is dropped. + * * This function expects the caller to protect the GEM's GPUVA list against - * concurrent access using the GEMs dma_resv lock. + * concurrent access using either the GEMs dma_resv lock or a driver specific + * lock set through drm_gem_gpuva_set_lock(). */ void drm_gpuva_unlink(struct drm_gpuva *va) { struct drm_gem_object *obj = va->gem.obj; + struct drm_gpuvm_bo *vm_bo = va->vm_bo; if (unlikely(!obj)) return; drm_gem_gpuva_assert_lock_held(obj); - list_del_init(&va->gem.entry); + + va->vm_bo = NULL; + drm_gpuvm_bo_put(vm_bo); } EXPORT_SYMBOL_GPL(drm_gpuva_unlink); @@ -992,10 +1983,10 @@ drm_gpuva_remap(struct drm_gpuva *prev, struct drm_gpuva *next, struct drm_gpuva_op_remap *op) { - struct drm_gpuva *curr = op->unmap->va; - struct drm_gpuvm *gpuvm = curr->vm; + struct drm_gpuva *va = op->unmap->va; + struct drm_gpuvm *gpuvm = va->vm; - drm_gpuva_remove(curr); + drm_gpuva_remove(va); if (op->prev) { drm_gpuva_init_from_op(prev, op->prev); @@ -1637,9 +2628,8 @@ err_free_ops: EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create); /** - * drm_gpuvm_gem_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM - * @gpuvm: the &drm_gpuvm representing the GPU VA space - * @obj: the &drm_gem_object to unmap + * drm_gpuvm_bo_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM + * @vm_bo: the &drm_gpuvm_bo abstraction * * This function creates a list of operations to perform unmapping for every * GPUVA attached to a GEM. @@ -1656,15 +2646,14 @@ EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create); * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure */ struct drm_gpuva_ops * -drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, - struct drm_gem_object *obj) +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo) { struct drm_gpuva_ops *ops; struct drm_gpuva_op *op; struct drm_gpuva *va; int ret; - drm_gem_gpuva_assert_lock_held(obj); + drm_gem_gpuva_assert_lock_held(vm_bo->obj); ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) @@ -1672,8 +2661,8 @@ drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, INIT_LIST_HEAD(&ops->list); - drm_gem_for_each_gpuva(va, obj) { - op = gpuva_op_alloc(gpuvm); + drm_gpuvm_bo_for_each_va(va, vm_bo) { + op = gpuva_op_alloc(vm_bo->vm); if (!op) { ret = -ENOMEM; goto err_free_ops; @@ -1687,10 +2676,10 @@ drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, return ops; err_free_ops: - drm_gpuva_ops_free(gpuvm, ops); + drm_gpuva_ops_free(vm_bo->vm, ops); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_gpuvm_gem_unmap_ops_create); +EXPORT_SYMBOL_GPL(drm_gpuvm_bo_unmap_ops_create); /** * drm_gpuva_ops_free() - free the given &drm_gpuva_ops diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 8462b657c375..b12c463bc460 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -22,6 +22,7 @@ */ #include <linux/kthread.h> +#include <linux/types.h> #include <drm/drm_ioctl.h> #include <drm/drm_vblank.h> @@ -31,6 +32,7 @@ #define DRM_IF_VERSION(maj, min) (maj << 16 | min) +struct cea_sad; struct dentry; struct dma_buf; struct iosys_map; @@ -267,3 +269,7 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data, void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, const struct drm_framebuffer *fb); void drm_framebuffer_debugfs_init(struct drm_device *dev); + +/* drm_edid.c */ +void drm_edid_cta_sad_get(const struct cea_sad *cta_sad, u8 *sad); +void drm_edid_cta_sad_set(struct cea_sad *cta_sad, const u8 *sad); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 77590b0f38fa..44fda68c28ae 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -675,6 +675,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb_ioctl, 0), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CLOSEFB, drm_mode_closefb_ioctl, 0), DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0), diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 0bf0fc1abf54..0c7550c0462b 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -27,38 +27,6 @@ #include <linux/module.h> -#include <drm/drm_edid.h> -#include <drm/drm_print.h> - -#include "drm_crtc_helper_internal.h" - MODULE_AUTHOR("David Airlie, Jesse Barnes"); MODULE_DESCRIPTION("DRM KMS helper"); MODULE_LICENSE("GPL and additional rights"); - -#if IS_ENABLED(CONFIG_DRM_LOAD_EDID_FIRMWARE) - -/* Backward compatibility for drm_kms_helper.edid_firmware */ -static int edid_firmware_set(const char *val, const struct kernel_param *kp) -{ - DRM_NOTE("drm_kms_helper.edid_firmware is deprecated, please use drm.edid_firmware instead.\n"); - - return __drm_set_edid_firmware_path(val); -} - -static int edid_firmware_get(char *buffer, const struct kernel_param *kp) -{ - return __drm_get_edid_firmware_path(buffer, PAGE_SIZE); -} - -static const struct kernel_param_ops edid_firmware_ops = { - .set = edid_firmware_set, - .get = edid_firmware_get, -}; - -module_param_cb(edid_firmware, &edid_firmware_ops, NULL, 0644); -__MODULE_PARM_TYPE(edid_firmware, "charp"); -MODULE_PARM_DESC(edid_firmware, - "DEPRECATED. Use drm.edid_firmware module parameter instead."); - -#endif diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index e90f0bf895b3..daac649aabdb 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -197,12 +197,14 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf); * @fb: The source framebuffer * @clip: Clipping rectangle of the area to be copied * @swap: When true, swap MSB/LSB of 16-bit values + * @fmtcnv_state: Format-conversion state * * Returns: * Zero on success, negative error code on failure. */ int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *clip, bool swap) + struct drm_rect *clip, bool swap, + struct drm_format_conv_state *fmtcnv_state) { struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0); struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(dst); @@ -215,12 +217,13 @@ int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer * switch (fb->format->format) { case DRM_FORMAT_RGB565: if (swap) - drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach); + drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach, + fmtcnv_state); else drm_fb_memcpy(&dst_map, NULL, src, fb, clip); break; case DRM_FORMAT_XRGB8888: - drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, swap); + drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, fmtcnv_state, swap); break; default: drm_err_once(fb->dev, "Format is not supported: %p4cc\n", @@ -252,7 +255,7 @@ static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, } static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *rect) + struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); unsigned int height = rect->y2 - rect->y1; @@ -270,7 +273,7 @@ static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, if (!dbi->dc || !full || swap || fb->format->format == DRM_FORMAT_XRGB8888) { tr = dbidev->tx_buf; - ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap); + ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap, fmtcnv_state); if (ret) goto err_msg; } else { @@ -332,7 +335,8 @@ void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, return; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); drm_dev_exit(idx); } @@ -368,7 +372,8 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, if (!drm_dev_enter(&dbidev->drm, &idx)) return; - mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); backlight_enable(dbidev->backlight); drm_dev_exit(idx); diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index d5c15292ae93..3d92f66e550c 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -336,6 +336,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "IdeaPad Duet 3 10IGL5"), }, .driver_data = (void *)&lcd1200x1920_rightside_up, + }, { /* Lenovo Legion Go 8APU1 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"), + }, + .driver_data = (void *)&lcd1600x2560_leftside_up, }, { /* Lenovo Yoga Book X90F / X90L */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index a8d3fa81e4ec..6228ce603248 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -640,16 +640,14 @@ static int etnaviv_pdev_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &etnaviv_master_ops, match); } -static int etnaviv_pdev_remove(struct platform_device *pdev) +static void etnaviv_pdev_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &etnaviv_master_ops); - - return 0; } static struct platform_driver etnaviv_platform_driver = { .probe = etnaviv_pdev_probe, - .remove = etnaviv_pdev_remove, + .remove_new = etnaviv_pdev_remove, .driver = { .name = "etnaviv", }, diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 2416c526f9b0..3d0f8d182506 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -535,7 +535,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, ret = drm_sched_job_init(&submit->sched_job, &ctx->sched_entity[args->pipe], - submit->ctx); + 1, submit->ctx); if (ret) goto err_submit_put; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 9276756e1397..9b8445d2a128 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -1904,11 +1904,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) return 0; } -static int etnaviv_gpu_platform_remove(struct platform_device *pdev) +static void etnaviv_gpu_platform_remove(struct platform_device *pdev) { component_del(&pdev->dev, &gpu_ops); pm_runtime_disable(&pdev->dev); - return 0; } static int etnaviv_gpu_rpm_suspend(struct device *dev) @@ -1917,7 +1916,7 @@ static int etnaviv_gpu_rpm_suspend(struct device *dev) u32 idle, mask; /* If there are any jobs in the HW queue, we're not idle */ - if (atomic_read(&gpu->sched.hw_rq_count)) + if (atomic_read(&gpu->sched.credit_count)) return -EBUSY; /* Check whether the hardware (except FE and MC) is idle */ @@ -1970,6 +1969,6 @@ struct platform_driver etnaviv_gpu_driver = { .of_match_table = etnaviv_gpu_match, }, .probe = etnaviv_gpu_platform_probe, - .remove = etnaviv_gpu_platform_remove, + .remove_new = etnaviv_gpu_platform_remove, .id_table = gpu_ids, }; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c index 9b79f218e21a..c4b04b0dee16 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c @@ -134,7 +134,7 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu) { int ret; - ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops, + ret = drm_sched_init(&gpu->sched, &etnaviv_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, etnaviv_hw_jobs_limit, etnaviv_job_hang_limit, msecs_to_jiffies(500), NULL, NULL, diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index a02f75be81f0..e163649816d5 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -51,7 +51,8 @@ static bool gud_is_big_endian(void) static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format, void *src, struct drm_framebuffer *fb, - struct drm_rect *rect) + struct drm_rect *rect, + struct drm_format_conv_state *fmtcnv_state) { unsigned int block_width = drm_format_info_block_width(format, 0); unsigned int bits_per_pixel = 8 / block_width; @@ -75,7 +76,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format iosys_map_set_vaddr(&dst_map, buf); iosys_map_set_vaddr(&vmap, src); - drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, rect); + drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, rect, fmtcnv_state); pix8 = buf; for (y = 0; y < height; y++) { @@ -152,7 +153,8 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb, const struct iosys_map *src, bool cached_reads, const struct drm_format_info *format, struct drm_rect *rect, - struct gud_set_buffer_req *req) + struct gud_set_buffer_req *req, + struct drm_format_conv_state *fmtcnv_state) { u8 compression = gdrm->compression; struct iosys_map dst; @@ -178,23 +180,23 @@ retry: */ if (format != fb->format) { if (format->format == GUD_DRM_FORMAT_R1) { - len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect); + len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect, fmtcnv_state); if (!len) return -ENOMEM; } else if (format->format == DRM_FORMAT_R8) { - drm_fb_xrgb8888_to_gray8(&dst, NULL, src, fb, rect); + drm_fb_xrgb8888_to_gray8(&dst, NULL, src, fb, rect, fmtcnv_state); } else if (format->format == DRM_FORMAT_RGB332) { - drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect); + drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect, fmtcnv_state); } else if (format->format == DRM_FORMAT_RGB565) { - drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect, + drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect, fmtcnv_state, gud_is_big_endian()); } else if (format->format == DRM_FORMAT_RGB888) { - drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect); + drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect, fmtcnv_state); } else { len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect); } } else if (gud_is_big_endian() && format->cpp[0] > 1) { - drm_fb_swab(&dst, NULL, src, fb, rect, cached_reads); + drm_fb_swab(&dst, NULL, src, fb, rect, cached_reads, fmtcnv_state); } else if (compression && cached_reads && pitch == fb->pitches[0]) { /* can compress directly from the framebuffer */ buf = vaddr + rect->y1 * pitch; @@ -266,7 +268,8 @@ static int gud_usb_bulk(struct gud_device *gdrm, size_t len) static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, const struct iosys_map *src, bool cached_reads, - const struct drm_format_info *format, struct drm_rect *rect) + const struct drm_format_info *format, struct drm_rect *rect, + struct drm_format_conv_state *fmtcnv_state) { struct gud_set_buffer_req req; size_t len, trlen; @@ -274,7 +277,7 @@ static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); - ret = gud_prep_flush(gdrm, fb, src, cached_reads, format, rect, &req); + ret = gud_prep_flush(gdrm, fb, src, cached_reads, format, rect, &req, fmtcnv_state); if (ret) return ret; @@ -318,6 +321,7 @@ static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb const struct iosys_map *src, bool cached_reads, struct drm_rect *damage) { + struct drm_format_conv_state fmtcnv_state = DRM_FORMAT_CONV_STATE_INIT; const struct drm_format_info *format; unsigned int i, lines; size_t pitch; @@ -340,7 +344,7 @@ static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb rect.y1 += i * lines; rect.y2 = min_t(u32, rect.y1 + lines, damage->y2); - ret = gud_flush_rect(gdrm, fb, src, cached_reads, format, &rect); + ret = gud_flush_rect(gdrm, fb, src, cached_reads, format, &rect, &fmtcnv_state); if (ret) { if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN && ret != -EPROTO) @@ -350,6 +354,8 @@ static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb break; } } + + drm_format_conv_state_release(&fmtcnv_state); } void gud_flush_work(struct work_struct *work) diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index ce397a8797f7..b5d6e3352071 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -94,7 +94,7 @@ config DRM_I915_CAPTURE_ERROR This option enables capturing the GPU state when a hang is detected. This information is vital for triaging hangs and assists in debugging. Please report any hang for triaging according to: - https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs + https://drm.pages.freedesktop.org/intel-docs/how-to-file-i915-bugs.html If in doubt, say "Y". diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 88b2bb005014..65e984242089 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -47,33 +47,34 @@ subdir-ccflags-y += -I$(srctree)/$(src) # Please keep these build lists sorted! # core driver code -i915-y += i915_driver.o \ - i915_drm_client.o \ - i915_config.o \ - i915_getparam.o \ - i915_ioctl.o \ - i915_irq.o \ - i915_mitigations.o \ - i915_module.o \ - i915_params.o \ - i915_pci.o \ - i915_scatterlist.o \ - i915_suspend.o \ - i915_switcheroo.o \ - i915_sysfs.o \ - i915_utils.o \ - intel_clock_gating.o \ - intel_device_info.o \ - intel_memory_region.o \ - intel_pcode.o \ - intel_region_ttm.o \ - intel_runtime_pm.o \ - intel_sbi.o \ - intel_step.o \ - intel_uncore.o \ - intel_wakeref.o \ - vlv_sideband.o \ - vlv_suspend.o +i915-y += \ + i915_config.o \ + i915_driver.o \ + i915_drm_client.o \ + i915_getparam.o \ + i915_ioctl.o \ + i915_irq.o \ + i915_mitigations.o \ + i915_module.o \ + i915_params.o \ + i915_pci.o \ + i915_scatterlist.o \ + i915_suspend.o \ + i915_switcheroo.o \ + i915_sysfs.o \ + i915_utils.o \ + intel_clock_gating.o \ + intel_device_info.o \ + intel_memory_region.o \ + intel_pcode.o \ + intel_region_ttm.o \ + intel_runtime_pm.o \ + intel_sbi.o \ + intel_step.o \ + intel_uncore.o \ + intel_wakeref.o \ + vlv_sideband.o \ + vlv_suspend.o # core peripheral code i915-y += \ @@ -90,13 +91,13 @@ i915-y += \ i915_syncmap.o \ i915_user_extensions.o -i915-$(CONFIG_COMPAT) += i915_ioc32.o +i915-$(CONFIG_COMPAT) += \ + i915_ioc32.o i915-$(CONFIG_DEBUG_FS) += \ i915_debugfs.o \ - i915_debugfs_params.o \ - display/intel_display_debugfs.o \ - display/intel_pipe_crc.o -i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o + i915_debugfs_params.o +i915-$(CONFIG_PERF_EVENTS) += \ + i915_pmu.o # "Graphics Technology" (aka we talk to the gpu) gt-y += \ @@ -153,7 +154,8 @@ gt-y += \ gt/sysfs_engines.o # x86 intel-gtt module support -gt-$(CONFIG_X86) += gt/intel_ggtt_gmch.o +gt-$(CONFIG_X86) += \ + gt/intel_ggtt_gmch.o # autogenerated null render state gt-y += \ gt/gen6_renderstate.o \ @@ -172,9 +174,9 @@ gem-y += \ gem/i915_gem_domain.o \ gem/i915_gem_execbuffer.o \ gem/i915_gem_internal.o \ - gem/i915_gem_object.o \ gem/i915_gem_lmem.o \ gem/i915_gem_mman.o \ + gem/i915_gem_object.o \ gem/i915_gem_pages.o \ gem/i915_gem_phys.o \ gem/i915_gem_pm.o \ @@ -191,57 +193,61 @@ gem-y += \ gem/i915_gem_wait.o \ gem/i915_gemfs.o i915-y += \ - $(gem-y) \ - i915_active.o \ - i915_cmd_parser.o \ - i915_deps.o \ - i915_gem_evict.o \ - i915_gem_gtt.o \ - i915_gem_ww.o \ - i915_gem.o \ - i915_query.o \ - i915_request.o \ - i915_scheduler.o \ - i915_trace_points.o \ - i915_ttm_buddy_manager.o \ - i915_vma.o \ - i915_vma_resource.o + $(gem-y) \ + i915_active.o \ + i915_cmd_parser.o \ + i915_deps.o \ + i915_gem.o \ + i915_gem_evict.o \ + i915_gem_gtt.o \ + i915_gem_ww.o \ + i915_query.o \ + i915_request.o \ + i915_scheduler.o \ + i915_trace_points.o \ + i915_ttm_buddy_manager.o \ + i915_vma.o \ + i915_vma_resource.o # general-purpose microcontroller (GuC) support i915-y += \ - gt/uc/intel_gsc_fw.o \ - gt/uc/intel_gsc_proxy.o \ - gt/uc/intel_gsc_uc.o \ - gt/uc/intel_gsc_uc_debugfs.o \ - gt/uc/intel_gsc_uc_heci_cmd_submit.o \ - gt/uc/intel_guc.o \ - gt/uc/intel_guc_ads.o \ - gt/uc/intel_guc_capture.o \ - gt/uc/intel_guc_ct.o \ - gt/uc/intel_guc_debugfs.o \ - gt/uc/intel_guc_fw.o \ - gt/uc/intel_guc_hwconfig.o \ - gt/uc/intel_guc_log.o \ - gt/uc/intel_guc_log_debugfs.o \ - gt/uc/intel_guc_rc.o \ - gt/uc/intel_guc_slpc.o \ - gt/uc/intel_guc_submission.o \ - gt/uc/intel_huc.o \ - gt/uc/intel_huc_debugfs.o \ - gt/uc/intel_huc_fw.o \ - gt/uc/intel_uc.o \ - gt/uc/intel_uc_debugfs.o \ - gt/uc/intel_uc_fw.o + gt/uc/intel_gsc_fw.o \ + gt/uc/intel_gsc_proxy.o \ + gt/uc/intel_gsc_uc.o \ + gt/uc/intel_gsc_uc_debugfs.o \ + gt/uc/intel_gsc_uc_heci_cmd_submit.o\ + gt/uc/intel_guc.o \ + gt/uc/intel_guc_ads.o \ + gt/uc/intel_guc_capture.o \ + gt/uc/intel_guc_ct.o \ + gt/uc/intel_guc_debugfs.o \ + gt/uc/intel_guc_fw.o \ + gt/uc/intel_guc_hwconfig.o \ + gt/uc/intel_guc_log.o \ + gt/uc/intel_guc_log_debugfs.o \ + gt/uc/intel_guc_rc.o \ + gt/uc/intel_guc_slpc.o \ + gt/uc/intel_guc_submission.o \ + gt/uc/intel_huc.o \ + gt/uc/intel_huc_debugfs.o \ + gt/uc/intel_huc_fw.o \ + gt/uc/intel_uc.o \ + gt/uc/intel_uc_debugfs.o \ + gt/uc/intel_uc_fw.o # graphics system controller (GSC) support -i915-y += gt/intel_gsc.o +i915-y += \ + gt/intel_gsc.o # graphics hardware monitoring (HWMON) support -i915-$(CONFIG_HWMON) += i915_hwmon.o +i915-$(CONFIG_HWMON) += \ + i915_hwmon.o # modesetting core code i915-y += \ display/hsw_ips.o \ + display/i9xx_plane.o \ + display/i9xx_wm.o \ display/intel_atomic.o \ display/intel_atomic_plane.o \ display/intel_audio.o \ @@ -257,6 +263,7 @@ i915-y += \ display/intel_display.o \ display/intel_display_driver.o \ display/intel_display_irq.o \ + display/intel_display_params.o \ display/intel_display_power.o \ display/intel_display_power_map.o \ display/intel_display_power_well.o \ @@ -268,8 +275,10 @@ i915-y += \ display/intel_dpll.o \ display/intel_dpll_mgr.o \ display/intel_dpt.o \ + display/intel_dpt_common.o \ display/intel_drrs.o \ display/intel_dsb.o \ + display/intel_dsb_buffer.o \ display/intel_fb.o \ display/intel_fb_pin.o \ display/intel_fbc.o \ @@ -287,8 +296,8 @@ i915-y += \ display/intel_load_detect.o \ display/intel_lpe_audio.o \ display/intel_modeset_lock.o \ - display/intel_modeset_verify.o \ display/intel_modeset_setup.o \ + display/intel_modeset_verify.o \ display/intel_overlay.o \ display/intel_pch_display.o \ display/intel_pch_refclk.o \ @@ -302,8 +311,6 @@ i915-y += \ display/intel_vblank.o \ display/intel_vga.o \ display/intel_wm.o \ - display/i9xx_plane.o \ - display/i9xx_wm.o \ display/skl_scaler.o \ display/skl_universal_plane.o \ display/skl_watermark.o @@ -312,6 +319,10 @@ i915-$(CONFIG_ACPI) += \ display/intel_opregion.o i915-$(CONFIG_DRM_FBDEV_EMULATION) += \ display/intel_fbdev.o +i915-$(CONFIG_DEBUG_FS) += \ + display/intel_display_debugfs.o \ + display/intel_display_debugfs_params.o \ + display/intel_pipe_crc.o # modesetting output/encoder code i915-y += \ @@ -357,13 +368,14 @@ i915-y += \ display/vlv_dsi.o \ display/vlv_dsi_pll.o -i915-y += i915_perf.o +i915-y += \ + i915_perf.o # Protected execution platform (PXP) support. Base support is required for HuC i915-y += \ pxp/intel_pxp.o \ - pxp/intel_pxp_tee.o \ - pxp/intel_pxp_huc.o + pxp/intel_pxp_huc.o \ + pxp/intel_pxp_tee.o i915-$(CONFIG_DRM_I915_PXP) += \ pxp/intel_pxp_cmd.o \ @@ -374,11 +386,11 @@ i915-$(CONFIG_DRM_I915_PXP) += \ pxp/intel_pxp_session.o # Post-mortem debug and GPU hang state capture -i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += i915_gpu_error.o +i915-$(CONFIG_DRM_I915_CAPTURE_ERROR) += \ + i915_gpu_error.o i915-$(CONFIG_DRM_I915_SELFTEST) += \ gem/selftests/i915_gem_client_blt.o \ gem/selftests/igt_gem_utils.o \ - selftests/intel_scheduler_helpers.o \ selftests/i915_random.o \ selftests/i915_selftest.o \ selftests/igt_atomic.o \ @@ -387,10 +399,12 @@ i915-$(CONFIG_DRM_I915_SELFTEST) += \ selftests/igt_mmap.o \ selftests/igt_reset.o \ selftests/igt_spinner.o \ + selftests/intel_scheduler_helpers.o \ selftests/librapl.o # virtual gpu code -i915-y += i915_vgpu.o +i915-y += \ + i915_vgpu.o i915-$(CONFIG_DRM_I915_GVT) += \ intel_gvt.o \ diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index e8ee0a08947e..dfe0b07a122d 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -432,7 +432,7 @@ intel_dp_link_down(struct intel_encoder *encoder, intel_de_write(dev_priv, intel_dp->output_reg, intel_dp->DP); intel_de_posting_read(dev_priv, intel_dp->output_reg); - intel_dp->DP &= ~(DP_PORT_EN | DP_AUDIO_OUTPUT_ENABLE); + intel_dp->DP &= ~DP_PORT_EN; intel_de_write(dev_priv, intel_dp->output_reg, intel_dp->DP); intel_de_posting_read(dev_priv, intel_dp->output_reg); @@ -475,6 +475,40 @@ intel_dp_link_down(struct intel_encoder *encoder, } } +static void g4x_dp_audio_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + if (!crtc_state->has_audio) + return; + + /* Enable audio presence detect */ + intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; + intel_de_write(i915, intel_dp->output_reg, intel_dp->DP); + + intel_audio_codec_enable(encoder, crtc_state, conn_state); +} + +static void g4x_dp_audio_disable(struct intel_encoder *encoder, + const struct intel_crtc_state *old_crtc_state, + const struct drm_connector_state *old_conn_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + if (!old_crtc_state->has_audio) + return; + + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); + + /* Disable audio presence detect */ + intel_dp->DP &= ~DP_AUDIO_OUTPUT_ENABLE; + intel_de_write(i915, intel_dp->output_reg, intel_dp->DP); +} + static void intel_disable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, @@ -484,8 +518,6 @@ static void intel_disable_dp(struct intel_atomic_state *state, intel_dp->link_trained = false; - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - /* * Make sure the panel is off before trying to change the mode. * But also ensure that we have vdd while we switch off the panel. @@ -631,8 +663,6 @@ static void intel_dp_enable_port(struct intel_dp *intel_dp, * fail when the power sequencer is freshly used for this port. */ intel_dp->DP |= DP_PORT_EN; - if (crtc_state->has_audio) - intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; intel_de_write(dev_priv, intel_dp->output_reg, intel_dp->DP); intel_de_posting_read(dev_priv, intel_dp->output_reg); @@ -686,8 +716,8 @@ static void g4x_enable_dp(struct intel_atomic_state *state, const struct drm_connector_state *conn_state) { intel_enable_dp(state, encoder, pipe_config, conn_state); - intel_audio_codec_enable(encoder, pipe_config, conn_state); intel_edp_backlight_on(pipe_config, conn_state); + encoder->audio_enable(encoder, pipe_config, conn_state); } static void vlv_enable_dp(struct intel_atomic_state *state, @@ -695,8 +725,8 @@ static void vlv_enable_dp(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - intel_audio_codec_enable(encoder, pipe_config, conn_state); intel_edp_backlight_on(pipe_config, conn_state); + encoder->audio_enable(encoder, pipe_config, conn_state); } static void g4x_pre_enable_dp(struct intel_atomic_state *state, @@ -1325,6 +1355,8 @@ bool g4x_dp_init(struct drm_i915_private *dev_priv, intel_encoder->disable = g4x_disable_dp; intel_encoder->post_disable = g4x_post_disable_dp; } + intel_encoder->audio_enable = g4x_dp_audio_enable; + intel_encoder->audio_disable = g4x_dp_audio_disable; if ((IS_IVYBRIDGE(dev_priv) && port == PORT_A) || (HAS_PCH_CPT(dev_priv) && port != PORT_A)) diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index 45e044b4a88d..8096492b3fad 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -228,25 +228,51 @@ static void g4x_hdmi_enable_port(struct intel_encoder *encoder, temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); } +static void g4x_hdmi_audio_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + if (!crtc_state->has_audio) + return; + + drm_WARN_ON(&i915->drm, !crtc_state->has_hdmi_sink); + + /* Enable audio presence detect */ + intel_de_rmw(i915, hdmi->hdmi_reg, 0, HDMI_AUDIO_ENABLE); + + intel_audio_codec_enable(encoder, crtc_state, conn_state); +} + +static void g4x_hdmi_audio_disable(struct intel_encoder *encoder, + const struct intel_crtc_state *old_crtc_state, + const struct drm_connector_state *old_conn_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + if (!old_crtc_state->has_audio) + return; + + intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); + + /* Disable audio presence detect */ + intel_de_rmw(i915, hdmi->hdmi_reg, HDMI_AUDIO_ENABLE, 0); +} + static void g4x_enable_hdmi(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - g4x_hdmi_enable_port(encoder, pipe_config); - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void ibx_enable_hdmi(struct intel_atomic_state *state, @@ -262,8 +288,6 @@ static void ibx_enable_hdmi(struct intel_atomic_state *state, temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; /* * HW workaround, need to write this twice for issue @@ -296,10 +320,6 @@ static void ibx_enable_hdmi(struct intel_atomic_state *state, intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); } - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void cpt_enable_hdmi(struct intel_atomic_state *state, @@ -317,8 +337,6 @@ static void cpt_enable_hdmi(struct intel_atomic_state *state, temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); temp |= SDVO_ENABLE; - if (pipe_config->has_audio) - temp |= HDMI_AUDIO_ENABLE; /* * WaEnableHDMI8bpcBefore12bpc:snb,ivb @@ -351,10 +369,6 @@ static void cpt_enable_hdmi(struct intel_atomic_state *state, intel_de_rmw(dev_priv, TRANS_CHICKEN1(pipe), TRANS_CHICKEN1_HDMIUNIT_GC_DISABLE, 0); } - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void vlv_enable_hdmi(struct intel_atomic_state *state, @@ -362,11 +376,6 @@ static void vlv_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { - struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - - drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && - !pipe_config->has_hdmi_sink); - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void intel_disable_hdmi(struct intel_atomic_state *state, @@ -384,7 +393,7 @@ static void intel_disable_hdmi(struct intel_atomic_state *state, temp = intel_de_read(dev_priv, intel_hdmi->hdmi_reg); - temp &= ~(SDVO_ENABLE | HDMI_AUDIO_ENABLE); + temp &= ~SDVO_ENABLE; intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); @@ -433,8 +442,6 @@ static void g4x_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - intel_disable_hdmi(state, encoder, old_crtc_state, old_conn_state); } @@ -443,7 +450,6 @@ static void pch_disable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); } static void pch_post_disable_hdmi(struct intel_atomic_state *state, @@ -750,6 +756,8 @@ void g4x_hdmi_init(struct drm_i915_private *dev_priv, else intel_encoder->enable = g4x_enable_hdmi; } + intel_encoder->audio_enable = g4x_hdmi_audio_enable; + intel_encoder->audio_disable = g4x_hdmi_audio_disable; intel_encoder->shutdown = intel_hdmi_encoder_shutdown; intel_encoder->type = INTEL_OUTPUT_HDMI; diff --git a/drivers/gpu/drm/i915/display/hsw_ips.c b/drivers/gpu/drm/i915/display/hsw_ips.c index 7dc38ac02092..611a7d6ef80c 100644 --- a/drivers/gpu/drm/i915/display/hsw_ips.c +++ b/drivers/gpu/drm/i915/display/hsw_ips.c @@ -193,7 +193,7 @@ bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state) if (!hsw_crtc_supports_ips(crtc)) return false; - if (!i915->params.enable_ips) + if (!i915->display.params.enable_ips) return false; if (crtc_state->pipe_bpp > 24) @@ -329,7 +329,7 @@ static int hsw_ips_debugfs_status_show(struct seq_file *m, void *unused) wakeref = intel_runtime_pm_get(&i915->runtime_pm); seq_printf(m, "Enabled by kernel parameter: %s\n", - str_yes_no(i915->params.enable_ips)); + str_yes_no(i915->display.params.enable_ips)); if (DISPLAY_VER(i915) >= 8) { seq_puts(m, "Currently: unknown\n"); diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c index af0c79a4c9a4..b37c0d02d500 100644 --- a/drivers/gpu/drm/i915/display/i9xx_wm.c +++ b/drivers/gpu/drm/i915/display/i9xx_wm.c @@ -2993,7 +2993,7 @@ static void ilk_wm_merge(struct drm_i915_private *dev_priv, /* ILK: LP2+ must be disabled when FBC WM is disabled but FBC enabled */ if (DISPLAY_VER(dev_priv) == 5 && HAS_FBC(dev_priv) && - dev_priv->params.enable_fbc && !merged->fbc_wm_enabled) { + dev_priv->display.params.enable_fbc && !merged->fbc_wm_enabled) { for (level = 2; level < num_levels; level++) { struct intel_wm_level *wm = &merged->wm[level]; diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index c4585e445198..481fcb650850 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -330,7 +330,7 @@ static int afe_clk(struct intel_encoder *encoder, int bpp; if (crtc_state->dsc.compression_enable) - bpp = crtc_state->dsc.compressed_bpp; + bpp = to_bpp_int(crtc_state->dsc.compressed_bpp_x16); else bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); @@ -860,7 +860,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, * compressed and non-compressed bpp. */ if (crtc_state->dsc.compression_enable) { - mul = crtc_state->dsc.compressed_bpp; + mul = to_bpp_int(crtc_state->dsc.compressed_bpp_x16); div = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); } @@ -884,7 +884,7 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder, int bpp, line_time_us, byte_clk_period_ns; if (crtc_state->dsc.compression_enable) - bpp = crtc_state->dsc.compressed_bpp; + bpp = to_bpp_int(crtc_state->dsc.compressed_bpp_x16); else bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); @@ -1451,8 +1451,8 @@ static void gen11_dsi_get_timings(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; - if (pipe_config->dsc.compressed_bpp) { - int div = pipe_config->dsc.compressed_bpp; + if (pipe_config->dsc.compressed_bpp_x16) { + int div = to_bpp_int(pipe_config->dsc.compressed_bpp_x16); int mul = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); adjusted_mode->crtc_htotal = diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 5d18145da279..ec0d5168b503 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -331,9 +331,6 @@ void intel_atomic_state_free(struct drm_atomic_state *_state) drm_atomic_state_default_release(&state->base); kfree(state->global_objs); - - i915_sw_fence_fini(&state->commit_ready); - kfree(state); } diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c index b1074350616c..06c2455bdd78 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c @@ -31,7 +31,10 @@ * prepare/check/commit/cleanup steps. */ +#include <linux/dma-fence-chain.h> + #include <drm/drm_atomic_helper.h> +#include <drm/drm_gem_atomic_helper.h> #include <drm/drm_blend.h> #include <drm/drm_fourcc.h> @@ -1012,6 +1015,41 @@ int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state) return 0; } +static int add_dma_resv_fences(struct dma_resv *resv, + struct drm_plane_state *new_plane_state) +{ + struct dma_fence *fence = dma_fence_get(new_plane_state->fence); + struct dma_fence *new; + int ret; + + ret = dma_resv_get_singleton(resv, dma_resv_usage_rw(false), &new); + if (ret) + goto error; + + if (new && fence) { + struct dma_fence_chain *chain = dma_fence_chain_alloc(); + + if (!chain) { + ret = -ENOMEM; + goto error; + } + + dma_fence_chain_init(chain, fence, new, 1); + fence = &chain->base; + + } else if (new) { + fence = new; + } + + dma_fence_put(new_plane_state->fence); + new_plane_state->fence = fence; + return 0; + +error: + dma_fence_put(fence); + return ret; +} + /** * intel_prepare_plane_fb - Prepare fb for usage on plane * @_plane: drm plane to prepare for @@ -1035,7 +1073,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane, struct intel_atomic_state *state = to_intel_atomic_state(new_plane_state->uapi.state); struct drm_i915_private *dev_priv = to_i915(plane->base.dev); - const struct intel_plane_state *old_plane_state = + struct intel_plane_state *old_plane_state = intel_atomic_get_old_plane_state(state, plane); struct drm_i915_gem_object *obj = intel_fb_obj(new_plane_state->hw.fb); struct drm_i915_gem_object *old_obj = intel_fb_obj(old_plane_state->hw.fb); @@ -1058,55 +1096,28 @@ intel_prepare_plane_fb(struct drm_plane *_plane, * can safely continue. */ if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) { - ret = i915_sw_fence_await_reservation(&state->commit_ready, - old_obj->base.resv, - false, 0, - GFP_KERNEL); + ret = add_dma_resv_fences(intel_bo_to_drm_bo(old_obj)->resv, + &new_plane_state->uapi); if (ret < 0) return ret; } } - if (new_plane_state->uapi.fence) { /* explicit fencing */ - i915_gem_fence_wait_priority(new_plane_state->uapi.fence, - &attr); - ret = i915_sw_fence_await_dma_fence(&state->commit_ready, - new_plane_state->uapi.fence, - i915_fence_timeout(dev_priv), - GFP_KERNEL); - if (ret < 0) - return ret; - } - if (!obj) return 0; - ret = intel_plane_pin_fb(new_plane_state); if (ret) return ret; - i915_gem_object_wait_priority(obj, 0, &attr); + ret = drm_gem_plane_helper_prepare_fb(&plane->base, &new_plane_state->uapi); + if (ret < 0) + goto unpin_fb; - if (!new_plane_state->uapi.fence) { /* implicit fencing */ - struct dma_resv_iter cursor; - struct dma_fence *fence; - - ret = i915_sw_fence_await_reservation(&state->commit_ready, - obj->base.resv, false, - i915_fence_timeout(dev_priv), - GFP_KERNEL); - if (ret < 0) - goto unpin_fb; + if (new_plane_state->uapi.fence) { + i915_gem_fence_wait_priority(new_plane_state->uapi.fence, + &attr); - dma_resv_iter_begin(&cursor, obj->base.resv, - DMA_RESV_USAGE_WRITE); - dma_resv_for_each_fence_unlocked(&cursor, fence) { - intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc, - fence); - } - dma_resv_iter_end(&cursor); - } else { intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc, new_plane_state->uapi.fence); } diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index 19605264a35c..07e0c73204f3 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -25,6 +25,7 @@ #include <linux/kernel.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/i915_component.h> #include "i915_drv.h" @@ -521,25 +522,25 @@ static unsigned int calc_hblank_early_prog(struct intel_encoder *encoder, unsigned int link_clks_available, link_clks_required; unsigned int tu_data, tu_line, link_clks_active; unsigned int h_active, h_total, hblank_delta, pixel_clk; - unsigned int fec_coeff, cdclk, vdsc_bpp; + unsigned int fec_coeff, cdclk, vdsc_bppx16; unsigned int link_clk, lanes; unsigned int hblank_rise; h_active = crtc_state->hw.adjusted_mode.crtc_hdisplay; h_total = crtc_state->hw.adjusted_mode.crtc_htotal; pixel_clk = crtc_state->hw.adjusted_mode.crtc_clock; - vdsc_bpp = crtc_state->dsc.compressed_bpp; + vdsc_bppx16 = crtc_state->dsc.compressed_bpp_x16; cdclk = i915->display.cdclk.hw.cdclk; /* fec= 0.972261, using rounding multiplier of 1000000 */ fec_coeff = 972261; link_clk = crtc_state->port_clock; lanes = crtc_state->lane_count; - drm_dbg_kms(&i915->drm, "h_active = %u link_clk = %u :" - "lanes = %u vdsc_bpp = %u cdclk = %u\n", - h_active, link_clk, lanes, vdsc_bpp, cdclk); + drm_dbg_kms(&i915->drm, + "h_active = %u link_clk = %u : lanes = %u vdsc_bpp = " BPP_X16_FMT " cdclk = %u\n", + h_active, link_clk, lanes, BPP_X16_ARGS(vdsc_bppx16), cdclk); - if (WARN_ON(!link_clk || !pixel_clk || !lanes || !vdsc_bpp || !cdclk)) + if (WARN_ON(!link_clk || !pixel_clk || !lanes || !vdsc_bppx16 || !cdclk)) return 0; link_clks_available = (h_total - h_active) * link_clk / pixel_clk - 28; @@ -551,8 +552,8 @@ static unsigned int calc_hblank_early_prog(struct intel_encoder *encoder, hblank_delta = DIV64_U64_ROUND_UP(mul_u32_u32(5 * (link_clk + cdclk), pixel_clk), mul_u32_u32(link_clk, cdclk)); - tu_data = div64_u64(mul_u32_u32(pixel_clk * vdsc_bpp * 8, 1000000), - mul_u32_u32(link_clk * lanes, fec_coeff)); + tu_data = div64_u64(mul_u32_u32(pixel_clk * vdsc_bppx16 * 8, 1000000), + mul_u32_u32(link_clk * lanes * 16, fec_coeff)); tu_line = div64_u64(h_active * mul_u32_u32(link_clk, fec_coeff), mul_u32_u32(64 * pixel_clk, 1000000)); link_clks_active = (tu_line - 1) * 64 + tu_data; diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c index 2e8f17c04522..612d4cd9dacb 100644 --- a/drivers/gpu/drm/i915/display/intel_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_backlight.c @@ -88,10 +88,10 @@ u32 intel_backlight_invert_pwm_level(struct intel_connector *connector, u32 val) drm_WARN_ON(&i915->drm, panel->backlight.pwm_level_max == 0); - if (i915->params.invert_brightness < 0) + if (i915->display.params.invert_brightness < 0) return val; - if (i915->params.invert_brightness > 0 || + if (i915->display.params.invert_brightness > 0 || intel_has_quirk(i915, QUIRK_INVERT_BRIGHTNESS)) { return panel->backlight.pwm_level_max - val + panel->backlight.pwm_level_min; } @@ -132,8 +132,9 @@ u32 intel_backlight_level_from_pwm(struct intel_connector *connector, u32 val) drm_WARN_ON_ONCE(&i915->drm, panel->backlight.max == 0 || panel->backlight.pwm_level_max == 0); - if (i915->params.invert_brightness > 0 || - (i915->params.invert_brightness == 0 && intel_has_quirk(i915, QUIRK_INVERT_BRIGHTNESS))) + if (i915->display.params.invert_brightness > 0 || + (i915->display.params.invert_brightness == 0 && + intel_has_quirk(i915, QUIRK_INVERT_BRIGHTNESS))) val = panel->backlight.pwm_level_max - (val - panel->backlight.pwm_level_min); return scale(val, panel->backlight.pwm_level_min, panel->backlight.pwm_level_max, diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 4e8f1e91bb08..2fd72b2fd109 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -1116,7 +1116,7 @@ parse_sdvo_panel_data(struct drm_i915_private *i915, struct drm_display_mode *panel_fixed_mode; int index; - index = i915->params.vbt_sdvo_panel_type; + index = i915->display.params.vbt_sdvo_panel_type; if (index == -2) { drm_dbg_kms(&i915->drm, "Ignore SDVO panel mode from BIOS VBT tables.\n"); @@ -1514,9 +1514,9 @@ parse_edp(struct drm_i915_private *i915, u8 vswing; /* Don't read from VBT if module parameter has valid value*/ - if (i915->params.edp_vswing) { + if (i915->display.params.edp_vswing) { panel->vbt.edp.low_vswing = - i915->params.edp_vswing == 1; + i915->display.params.edp_vswing == 1; } else { vswing = (edp->edp_vswing_preemph >> (panel_type * 4)) & 0xF; panel->vbt.edp.low_vswing = vswing == 0; @@ -2473,6 +2473,27 @@ static void sanitize_device_type(struct intel_bios_encoder_data *devdata, devdata->child.device_type |= DEVICE_TYPE_NOT_HDMI_OUTPUT; } +static void sanitize_hdmi_level_shift(struct intel_bios_encoder_data *devdata, + enum port port) +{ + struct drm_i915_private *i915 = devdata->i915; + + if (!intel_bios_encoder_supports_dvi(devdata)) + return; + + /* + * Some BDW machines (eg. HP Pavilion 15-ab) shipped + * with a HSW VBT where the level shifter value goes + * up to 11, whereas the BDW max is 9. + */ + if (IS_BROADWELL(i915) && devdata->child.hdmi_level_shifter_value > 9) { + drm_dbg_kms(&i915->drm, "Bogus port %c VBT HDMI level shift %d, adjusting to %d\n", + port_name(port), devdata->child.hdmi_level_shifter_value, 9); + + devdata->child.hdmi_level_shifter_value = 9; + } +} + static bool intel_bios_encoder_supports_crt(const struct intel_bios_encoder_data *devdata) { @@ -2652,6 +2673,7 @@ static void parse_ddi_port(struct intel_bios_encoder_data *devdata) } sanitize_device_type(devdata, port); + sanitize_hdmi_level_shift(devdata, port); } static bool has_ddi_port_info(struct drm_i915_private *i915) @@ -3392,8 +3414,8 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, crtc_state->pipe_bpp = bpc * 3; - crtc_state->dsc.compressed_bpp = min(crtc_state->pipe_bpp, - VBT_DSC_MAX_BPP(dsc->max_bpp)); + crtc_state->dsc.compressed_bpp_x16 = to_bpp_x16(min(crtc_state->pipe_bpp, + VBT_DSC_MAX_BPP(dsc->max_bpp))); /* * FIXME: This is ugly, and slice count should take DSC engine diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index c4839c67cb0f..b93d1ad7936d 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2598,8 +2598,9 @@ static int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state) * => CDCLK >= compressed_bpp * Pixel clock / 2 * Bigjoiner Interface bits */ int bigjoiner_interface_bits = DISPLAY_VER(i915) > 13 ? 36 : 24; - int min_cdclk_bj = (crtc_state->dsc.compressed_bpp * pixel_clock) / - (2 * bigjoiner_interface_bits); + int min_cdclk_bj = + (to_bpp_int_roundup(crtc_state->dsc.compressed_bpp_x16) * + pixel_clock) / (2 * bigjoiner_interface_bits); min_cdclk = max(min_cdclk, min_cdclk_bj); } diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 1d26be54ddfc..c5092b7e87d5 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -785,14 +785,12 @@ static void chv_assign_csc(struct intel_crtc_state *crtc_state) /* convert hw value with given bit_precision to lut property val */ static u32 intel_color_lut_pack(u32 val, int bit_precision) { - u32 max = 0xffff >> (16 - bit_precision); - - val = clamp_val(val, 0, max); - - if (bit_precision < 16) - val <<= 16 - bit_precision; - - return val; + if (bit_precision > 16) + return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(val, (1 << 16) - 1), + (1 << bit_precision) - 1); + else + return DIV_ROUND_CLOSEST(val * ((1 << 16) - 1), + (1 << bit_precision) - 1); } static u32 i9xx_lut_8(const struct drm_color_lut *color) @@ -911,7 +909,7 @@ static void i965_lut_10p6_pack(struct drm_color_lut *entry, u32 ldw, u32 udw) static u16 i965_lut_11p6_max_pack(u32 val) { /* PIPEGCMAX is 11.6, clamp to 10.6 */ - return clamp_val(val, 0, 0xffff); + return min(val, 0xffffu); } static u32 ilk_lut_10(const struct drm_color_lut *color) @@ -1528,14 +1526,27 @@ static int glk_degamma_lut_size(struct drm_i915_private *i915) return 35; } -/* - * change_lut_val_precision: helper function to upscale or downscale lut values. - * Parameters 'to' and 'from' needs to be less than 32. This should be sufficient - * as currently there are no lut values exceeding 32 bit. - */ -static u32 change_lut_val_precision(u32 lut_val, int to, int from) +static u32 glk_degamma_lut(const struct drm_color_lut *color) +{ + return color->green; +} + +static void glk_degamma_lut_pack(struct drm_color_lut *entry, u32 val) +{ + /* PRE_CSC_GAMC_DATA is 3.16, clamp to 0.16 */ + entry->red = entry->green = entry->blue = min(val, 0xffffu); +} + +static u32 mtl_degamma_lut(const struct drm_color_lut *color) +{ + return drm_color_lut_extract(color->green, 24); +} + +static void mtl_degamma_lut_pack(struct drm_color_lut *entry, u32 val) { - return mul_u32_u32(lut_val, (1 << to)) / (1 << from); + /* PRE_CSC_GAMC_DATA is 3.24, clamp to 0.16 */ + entry->red = entry->green = entry->blue = + intel_color_lut_pack(min(val, 0xffffffu), 24); } static void glk_load_degamma_lut(const struct intel_crtc_state *crtc_state, @@ -1572,20 +1583,16 @@ static void glk_load_degamma_lut(const struct intel_crtc_state *crtc_state, * ToDo: Extend to max 7.0. Enable 32 bit input value * as compared to just 16 to achieve this. */ - u32 lut_val; - - if (DISPLAY_VER(i915) >= 14) - lut_val = change_lut_val_precision(lut[i].green, 24, 16); - else - lut_val = lut[i].green; - ilk_lut_write(crtc_state, PRE_CSC_GAMC_DATA(pipe), - lut_val); + DISPLAY_VER(i915) >= 14 ? + mtl_degamma_lut(&lut[i]) : glk_degamma_lut(&lut[i])); } /* Clamp values > 1.0. */ while (i++ < glk_degamma_lut_size(i915)) - ilk_lut_write(crtc_state, PRE_CSC_GAMC_DATA(pipe), 1 << 16); + ilk_lut_write(crtc_state, PRE_CSC_GAMC_DATA(pipe), + DISPLAY_VER(i915) >= 14 ? + 1 << 24 : 1 << 16); ilk_lut_write(crtc_state, PRE_CSC_GAMC_INDEX(pipe), 0); } @@ -3572,17 +3579,10 @@ static struct drm_property_blob *glk_read_degamma_lut(struct intel_crtc *crtc) for (i = 0; i < lut_size; i++) { u32 val = intel_de_read_fw(dev_priv, PRE_CSC_GAMC_DATA(pipe)); - /* - * For MTL and beyond, convert back the 24 bit lut values - * read from HW to 16 bit values to maintain parity with - * userspace values - */ if (DISPLAY_VER(dev_priv) >= 14) - val = change_lut_val_precision(val, 16, 24); - - lut[i].red = val; - lut[i].green = val; - lut[i].blue = val; + mtl_degamma_lut_pack(&lut[i], val); + else + glk_degamma_lut_pack(&lut[i], val); } intel_de_write_fw(dev_priv, PRE_CSC_GAMC_INDEX(pipe), diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 913e5d230a4d..0e33a0523a75 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -841,7 +841,7 @@ intel_crt_detect(struct drm_connector *connector, if (!intel_display_device_enabled(dev_priv)) return connector_status_disconnected; - if (dev_priv->params.load_detect_test) { + if (dev_priv->display.params.load_detect_test) { wakeref = intel_display_power_get(dev_priv, intel_encoder->power_domain); goto load_detect; @@ -901,7 +901,7 @@ load_detect: else if (DISPLAY_VER(dev_priv) < 4) status = intel_crt_load_detect(crt, to_intel_crtc(connector->state->crtc)->pipe); - else if (dev_priv->params.load_detect_test) + else if (dev_priv->display.params.load_detect_test) status = connector_status_disconnected; else status = connector_status_unknown; diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c index 66fe880af8f3..2d15e82c0b3d 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c +++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c @@ -4,6 +4,7 @@ */ #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include "i915_drv.h" #include "intel_crtc_state_dump.h" diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index d414f6b7f993..a8fa76580802 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -31,7 +31,7 @@ bool intel_is_c10phy(struct drm_i915_private *i915, enum phy phy) { - if (DISPLAY_VER_FULL(i915) == IP_VER(14, 0) && phy < PHY_C) + if ((IS_LUNARLAKE(i915) || IS_METEORLAKE(i915)) && phy < PHY_C) return true; return false; @@ -206,6 +206,13 @@ static int __intel_cx0_read_once(struct drm_i915_private *i915, enum port port, intel_clear_response_ready_flag(i915, port, lane); + /* + * FIXME: Workaround to let HW to settle + * down and let the message bus to end up + * in a known state + */ + intel_cx0_bus_reset(i915, port, lane); + return REG_FIELD_GET(XELPDP_PORT_P2M_DATA_MASK, val); } @@ -285,6 +292,13 @@ static int __intel_cx0_write_once(struct drm_i915_private *i915, enum port port, intel_clear_response_ready_flag(i915, port, lane); + /* + * FIXME: Workaround to let HW to settle + * down and let the message bus to end up + * in a known state + */ + intel_cx0_bus_reset(i915, port, lane); + return 0; } @@ -1850,8 +1864,8 @@ static int intel_c10pll_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } -void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, - struct intel_c10pll_state *pll_state) +static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_c10pll_state *pll_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); u8 lane = INTEL_CX0_LANE0; @@ -2103,8 +2117,8 @@ static bool intel_c20_use_mplla(u32 clock) return false; } -void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, - struct intel_c20pll_state *pll_state) +static void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_c20pll_state *pll_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); bool cntx; @@ -2378,8 +2392,8 @@ static void intel_c20_pll_program(struct drm_i915_private *i915, BIT(0), cntx ? 0 : 1, MB_WRITE_COMMITTED); } -int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c10pll_state *pll_state) +static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c10pll_state *pll_state) { unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; unsigned int multiplier, tx_clk_div, hdmi_div, refclk = 38400; @@ -2405,8 +2419,8 @@ int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, return tmpclk; } -int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c20pll_state *pll_state) +static int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c20pll_state *pll_state) { unsigned int frac, frac_en, frac_quot, frac_rem, frac_den; unsigned int multiplier, refclk = 38400; @@ -3003,17 +3017,110 @@ intel_mtl_port_pll_type(struct intel_encoder *encoder, return ICL_PORT_DPLL_DEFAULT; } -void intel_c10pll_state_verify(struct intel_atomic_state *state, +static void intel_c10pll_state_verify(const struct intel_crtc_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder, + struct intel_c10pll_state *mpllb_hw_state) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + const struct intel_c10pll_state *mpllb_sw_state = &state->cx0pll_state.c10; + int i; + + for (i = 0; i < ARRAY_SIZE(mpllb_sw_state->pll); i++) { + u8 expected = mpllb_sw_state->pll[i]; + + I915_STATE_WARN(i915, mpllb_hw_state->pll[i] != expected, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register[%d] (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, i, + expected, mpllb_hw_state->pll[i]); + } + + I915_STATE_WARN(i915, mpllb_hw_state->tx != mpllb_sw_state->tx, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register TX0 (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, + mpllb_sw_state->tx, mpllb_hw_state->tx); + + I915_STATE_WARN(i915, mpllb_hw_state->cmn != mpllb_sw_state->cmn, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register CMN0 (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, + mpllb_sw_state->cmn, mpllb_hw_state->cmn); +} + +void intel_cx0pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_cx0pll_state *pll_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + if (intel_is_c10phy(i915, phy)) + intel_c10pll_readout_hw_state(encoder, &pll_state->c10); + else + intel_c20pll_readout_hw_state(encoder, &pll_state->c20); +} + +int intel_cx0pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_cx0pll_state *pll_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + if (intel_is_c10phy(i915, phy)) + return intel_c10pll_calc_port_clock(encoder, &pll_state->c10); + + return intel_c20pll_calc_port_clock(encoder, &pll_state->c20); +} + +static void intel_c20pll_state_verify(const struct intel_crtc_state *state, + struct intel_crtc *crtc, + struct intel_encoder *encoder, + struct intel_c20pll_state *mpll_hw_state) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + const struct intel_c20pll_state *mpll_sw_state = &state->cx0pll_state.c20; + bool use_mplla; + int i; + + use_mplla = intel_c20_use_mplla(mpll_hw_state->clock); + if (use_mplla) { + for (i = 0; i < ARRAY_SIZE(mpll_sw_state->mplla); i++) { + I915_STATE_WARN(i915, mpll_hw_state->mplla[i] != mpll_sw_state->mplla[i], + "[CRTC:%d:%s] mismatch in C20MPLLA: Register[%d] (expected 0x%04x, found 0x%04x)", + crtc->base.base.id, crtc->base.name, i, + mpll_sw_state->mplla[i], mpll_hw_state->mplla[i]); + } + } else { + for (i = 0; i < ARRAY_SIZE(mpll_sw_state->mpllb); i++) { + I915_STATE_WARN(i915, mpll_hw_state->mpllb[i] != mpll_sw_state->mpllb[i], + "[CRTC:%d:%s] mismatch in C20MPLLB: Register[%d] (expected 0x%04x, found 0x%04x)", + crtc->base.base.id, crtc->base.name, i, + mpll_sw_state->mpllb[i], mpll_hw_state->mpllb[i]); + } + } + + for (i = 0; i < ARRAY_SIZE(mpll_sw_state->tx); i++) { + I915_STATE_WARN(i915, mpll_hw_state->tx[i] != mpll_sw_state->tx[i], + "[CRTC:%d:%s] mismatch in C20: Register TX[%i] (expected 0x%04x, found 0x%04x)", + crtc->base.base.id, crtc->base.name, i, + mpll_sw_state->tx[i], mpll_hw_state->tx[i]); + } + + for (i = 0; i < ARRAY_SIZE(mpll_sw_state->cmn); i++) { + I915_STATE_WARN(i915, mpll_hw_state->cmn[i] != mpll_sw_state->cmn[i], + "[CRTC:%d:%s] mismatch in C20: Register CMN[%i] (expected 0x%04x, found 0x%04x)", + crtc->base.base.id, crtc->base.name, i, + mpll_sw_state->cmn[i], mpll_hw_state->cmn[i]); + } +} + +void intel_cx0pll_state_verify(struct intel_atomic_state *state, struct intel_crtc *crtc) { struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_crtc_state *new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc); - struct intel_c10pll_state mpllb_hw_state = {}; - const struct intel_c10pll_state *mpllb_sw_state = &new_crtc_state->cx0pll_state.c10; struct intel_encoder *encoder; + struct intel_cx0pll_state mpll_hw_state = {}; enum phy phy; - int i; if (DISPLAY_VER(i915) < 14) return; @@ -3029,27 +3136,10 @@ void intel_c10pll_state_verify(struct intel_atomic_state *state, encoder = intel_get_crtc_new_encoder(state, new_crtc_state); phy = intel_port_to_phy(i915, encoder->port); - if (!intel_is_c10phy(i915, phy)) - return; - - intel_c10pll_readout_hw_state(encoder, &mpllb_hw_state); + intel_cx0pll_readout_hw_state(encoder, &mpll_hw_state); - for (i = 0; i < ARRAY_SIZE(mpllb_sw_state->pll); i++) { - u8 expected = mpllb_sw_state->pll[i]; - - I915_STATE_WARN(i915, mpllb_hw_state.pll[i] != expected, - "[CRTC:%d:%s] mismatch in C10MPLLB: Register[%d] (expected 0x%02x, found 0x%02x)", - crtc->base.base.id, crtc->base.name, i, - expected, mpllb_hw_state.pll[i]); - } - - I915_STATE_WARN(i915, mpllb_hw_state.tx != mpllb_sw_state->tx, - "[CRTC:%d:%s] mismatch in C10MPLLB: Register TX0 (expected 0x%02x, found 0x%02x)", - crtc->base.base.id, crtc->base.name, - mpllb_sw_state->tx, mpllb_hw_state.tx); - - I915_STATE_WARN(i915, mpllb_hw_state.cmn != mpllb_sw_state->cmn, - "[CRTC:%d:%s] mismatch in C10MPLLB: Register CMN0 (expected 0x%02x, found 0x%02x)", - crtc->base.base.id, crtc->base.name, - mpllb_sw_state->cmn, mpllb_hw_state.cmn); + if (intel_is_c10phy(i915, phy)) + intel_c10pll_state_verify(new_crtc_state, crtc, encoder, &mpll_hw_state.c10); + else + intel_c20pll_state_verify(new_crtc_state, crtc, encoder, &mpll_hw_state.c20); } diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 0e0a38dac8cd..c6682677253a 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -16,6 +16,7 @@ struct drm_i915_private; struct intel_atomic_state; struct intel_c10pll_state; struct intel_c20pll_state; +struct intel_cx0pll_state; struct intel_crtc; struct intel_crtc_state; struct intel_encoder; @@ -28,20 +29,19 @@ void intel_mtl_pll_disable(struct intel_encoder *encoder); enum icl_port_dpll_id intel_mtl_port_pll_type(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); -void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state); + int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); +void intel_cx0pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_cx0pll_state *pll_state); +int intel_cx0pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_cx0pll_state *pll_state); + void intel_c10pll_dump_hw_state(struct drm_i915_private *dev_priv, const struct intel_c10pll_state *hw_state); -int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c10pll_state *pll_state); -void intel_c10pll_state_verify(struct intel_atomic_state *state, +void intel_cx0pll_state_verify(struct intel_atomic_state *state, struct intel_crtc *crtc); -void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, - struct intel_c20pll_state *pll_state); void intel_c20pll_dump_hw_state(struct drm_i915_private *i915, const struct intel_c20pll_state *hw_state); -int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, - const struct intel_c20pll_state *pll_state); void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 9151d5add960..38f28c480b38 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -25,6 +25,7 @@ * */ +#include <linux/iopoll.h> #include <linux/string_helpers.h> #include <drm/display/drm_scdc_helper.h> @@ -2210,16 +2211,87 @@ static void intel_dp_sink_set_msa_timing_par_ignore_state(struct intel_dp *intel } static void intel_dp_sink_set_fec_ready(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) + const struct intel_crtc_state *crtc_state, + bool enable) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); if (!crtc_state->fec_enable) return; - if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_FEC_CONFIGURATION, DP_FEC_READY) <= 0) - drm_dbg_kms(&i915->drm, - "Failed to set FEC_READY in the sink\n"); + if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_FEC_CONFIGURATION, + enable ? DP_FEC_READY : 0) <= 0) + drm_dbg_kms(&i915->drm, "Failed to set FEC_READY to %s in the sink\n", + enable ? "enabled" : "disabled"); + + if (enable && + drm_dp_dpcd_writeb(&intel_dp->aux, DP_FEC_STATUS, + DP_FEC_DECODE_EN_DETECTED | DP_FEC_DECODE_DIS_DETECTED) <= 0) + drm_dbg_kms(&i915->drm, "Failed to clear FEC detected flags\n"); +} + +static int read_fec_detected_status(struct drm_dp_aux *aux) +{ + int ret; + u8 status; + + ret = drm_dp_dpcd_readb(aux, DP_FEC_STATUS, &status); + if (ret < 0) + return ret; + + return status; +} + +static void wait_for_fec_detected(struct drm_dp_aux *aux, bool enabled) +{ + struct drm_i915_private *i915 = to_i915(aux->drm_dev); + int mask = enabled ? DP_FEC_DECODE_EN_DETECTED : DP_FEC_DECODE_DIS_DETECTED; + int status; + int err; + + err = readx_poll_timeout(read_fec_detected_status, aux, status, + status & mask || status < 0, + 10000, 200000); + + if (!err && status >= 0) + return; + + if (err == -ETIMEDOUT) + drm_dbg_kms(&i915->drm, "Timeout waiting for FEC %s to get detected\n", + str_enabled_disabled(enabled)); + else + drm_dbg_kms(&i915->drm, "FEC detected status read error: %d\n", status); +} + +void intel_ddi_wait_for_fec_status(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + bool enabled) +{ + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + int ret; + + if (!crtc_state->fec_enable) + return; + + if (enabled) + ret = intel_de_wait_for_set(i915, dp_tp_status_reg(encoder, crtc_state), + DP_TP_STATUS_FEC_ENABLE_LIVE, 1); + else + ret = intel_de_wait_for_clear(i915, dp_tp_status_reg(encoder, crtc_state), + DP_TP_STATUS_FEC_ENABLE_LIVE, 1); + + if (ret) + drm_err(&i915->drm, + "Timeout waiting for FEC live state to get %s\n", + str_enabled_disabled(enabled)); + + /* + * At least the Synoptics MST hub doesn't set the detected flag for + * FEC decoding disabling so skip waiting for that. + */ + if (enabled) + wait_for_fec_detected(&intel_dp->aux, enabled); } static void intel_ddi_enable_fec(struct intel_encoder *encoder, @@ -2234,8 +2306,8 @@ static void intel_ddi_enable_fec(struct intel_encoder *encoder, 0, DP_TP_CTL_FEC_ENABLE); } -static void intel_ddi_disable_fec_state(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +static void intel_ddi_disable_fec(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); @@ -2466,13 +2538,17 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, intel_dp_set_power(intel_dp, DP_SET_POWER_D0); intel_dp_configure_protocol_converter(intel_dp, crtc_state); - intel_dp_sink_set_decompression_state(intel_dp, crtc_state, true); + if (!is_mst) + intel_dp_sink_enable_decompression(state, + to_intel_connector(conn_state->connector), + crtc_state); + /* * DDI FEC: "anticipates enabling FEC encoding sets the FEC_READY bit * in the FEC_CONFIGURATION register to 1 before initiating link * training */ - intel_dp_sink_set_fec_ready(intel_dp, crtc_state); + intel_dp_sink_set_fec_ready(intel_dp, crtc_state, true); intel_dp_check_frl_training(intel_dp); intel_dp_pcon_dsc_configure(intel_dp, crtc_state); @@ -2505,7 +2581,8 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, /* 6.o Configure and enable FEC if needed */ intel_ddi_enable_fec(encoder, crtc_state); - intel_dsc_dp_pps_write(encoder, crtc_state); + if (!is_mst) + intel_dsc_dp_pps_write(encoder, crtc_state); } static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, @@ -2616,13 +2693,16 @@ static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, intel_dp_set_power(intel_dp, DP_SET_POWER_D0); intel_dp_configure_protocol_converter(intel_dp, crtc_state); - intel_dp_sink_set_decompression_state(intel_dp, crtc_state, true); + if (!is_mst) + intel_dp_sink_enable_decompression(state, + to_intel_connector(conn_state->connector), + crtc_state); /* * DDI FEC: "anticipates enabling FEC encoding sets the FEC_READY bit * in the FEC_CONFIGURATION register to 1 before initiating link * training */ - intel_dp_sink_set_fec_ready(intel_dp, crtc_state); + intel_dp_sink_set_fec_ready(intel_dp, crtc_state, true); intel_dp_check_frl_training(intel_dp); intel_dp_pcon_dsc_configure(intel_dp, crtc_state); @@ -2643,7 +2723,8 @@ static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, /* 7.l Configure and enable FEC if needed */ intel_ddi_enable_fec(encoder, crtc_state); - intel_dsc_dp_pps_write(encoder, crtc_state); + if (!is_mst) + intel_dsc_dp_pps_write(encoder, crtc_state); } static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state, @@ -2695,9 +2776,11 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state, if (!is_mst) intel_dp_set_power(intel_dp, DP_SET_POWER_D0); intel_dp_configure_protocol_converter(intel_dp, crtc_state); - intel_dp_sink_set_decompression_state(intel_dp, crtc_state, - true); - intel_dp_sink_set_fec_ready(intel_dp, crtc_state); + if (!is_mst) + intel_dp_sink_enable_decompression(state, + to_intel_connector(conn_state->connector), + crtc_state); + intel_dp_sink_set_fec_ready(intel_dp, crtc_state, true); intel_dp_start_link_train(intel_dp, crtc_state); if ((port != PORT_A || DISPLAY_VER(dev_priv) >= 9) && !is_trans_port_sync_mode(crtc_state)) @@ -2705,10 +2788,10 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state, intel_ddi_enable_fec(encoder, crtc_state); - if (!is_mst) + if (!is_mst) { intel_ddi_enable_transcoder_clock(encoder, crtc_state); - - intel_dsc_dp_pps_write(encoder, crtc_state); + intel_dsc_dp_pps_write(encoder, crtc_state); + } } static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state, @@ -2717,10 +2800,15 @@ static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state, const struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - if (HAS_DP20(dev_priv)) + if (HAS_DP20(dev_priv)) { intel_dp_128b132b_sdp_crc16(enc_to_intel_dp(encoder), crtc_state); + if (crtc_state->has_panel_replay) + drm_dp_dpcd_writeb(&intel_dp->aux, PANEL_REPLAY_CONFIG, + DP_PANEL_REPLAY_ENABLE); + } if (DISPLAY_VER(dev_priv) >= 14) mtl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); @@ -2866,8 +2954,7 @@ static void disable_ddi_buf(struct intel_encoder *encoder, intel_de_rmw(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), DP_TP_CTL_ENABLE, 0); - /* Disable FEC in DP Sink */ - intel_ddi_disable_fec_state(encoder, crtc_state); + intel_ddi_disable_fec(encoder, crtc_state); if (wait) intel_wait_ddi_buf_idle(dev_priv, port); @@ -2882,10 +2969,12 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder, mtl_disable_ddi_buf(encoder, crtc_state); /* 3.f Disable DP_TP_CTL FEC Enable if it is needed */ - intel_ddi_disable_fec_state(encoder, crtc_state); + intel_ddi_disable_fec(encoder, crtc_state); } else { disable_ddi_buf(encoder, crtc_state); } + + intel_ddi_wait_for_fec_status(encoder, crtc_state, false); } static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, @@ -2925,6 +3014,8 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, intel_disable_ddi_buf(encoder, old_crtc_state); + intel_dp_sink_set_fec_ready(intel_dp, old_crtc_state, false); + /* * From TGL spec: "If single stream or multi-stream master transcoder: * Configure Transcoder Clock select to direct no clock to the @@ -3110,11 +3201,18 @@ static void intel_enable_ddi_dp(struct intel_atomic_state *state, if (!dig_port->lspcon.active || intel_dp_has_hdmi_sink(&dig_port->dp)) intel_dp_set_infoframes(encoder, true, crtc_state, conn_state); - intel_audio_codec_enable(encoder, crtc_state, conn_state); - trans_port_sync_stop_link_train(state, encoder, crtc_state); } +/* FIXME bad home for this function */ +i915_reg_t hsw_chicken_trans_reg(struct drm_i915_private *i915, + enum transcoder cpu_transcoder) +{ + return DISPLAY_VER(i915) >= 14 ? + MTL_CHICKEN_TRANS(cpu_transcoder) : + CHICKEN_TRANS(cpu_transcoder); +} + static i915_reg_t gen9_chicken_trans_reg_by_port(struct drm_i915_private *dev_priv, enum port port) @@ -3233,8 +3331,6 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, intel_de_write(dev_priv, DDI_BUF_CTL(port), buf_ctl); intel_wait_ddi_buf_active(dev_priv, port); - - intel_audio_codec_enable(encoder, crtc_state, conn_state); } static void intel_enable_ddi(struct intel_atomic_state *state, @@ -3252,6 +3348,8 @@ static void intel_enable_ddi(struct intel_atomic_state *state, intel_enable_transcoder(crtc_state); + intel_ddi_wait_for_fec_status(encoder, crtc_state, true); + intel_crtc_vblank_on(crtc_state); if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) @@ -3259,10 +3357,8 @@ static void intel_enable_ddi(struct intel_atomic_state *state, else intel_enable_ddi_dp(state, encoder, crtc_state, conn_state); - /* Enable hdcp if it's desired */ - if (conn_state->content_protection == - DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(state, encoder, crtc_state, conn_state); + intel_hdcp_enable(state, encoder, crtc_state, conn_state); + } static void intel_disable_ddi_dp(struct intel_atomic_state *state, @@ -3271,16 +3367,16 @@ static void intel_disable_ddi_dp(struct intel_atomic_state *state, const struct drm_connector_state *old_conn_state) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_connector *connector = + to_intel_connector(old_conn_state->connector); intel_dp->link_trained = false; - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - intel_psr_disable(intel_dp, old_crtc_state); intel_edp_backlight_off(old_conn_state); /* Disable the decompression in DP Sink */ - intel_dp_sink_set_decompression_state(intel_dp, old_crtc_state, - false); + intel_dp_sink_disable_decompression(state, + connector, old_crtc_state); /* Disable Ignore_MSA bit in DP Sink */ intel_dp_sink_set_msa_timing_par_ignore_state(intel_dp, old_crtc_state, false); @@ -3294,8 +3390,6 @@ static void intel_disable_ddi_hdmi(struct intel_atomic_state *state, struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct drm_connector *connector = old_conn_state->connector; - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); - if (!intel_hdmi_handle_sink_scrambling(encoder, connector, false, false)) drm_dbg_kms(&i915->drm, @@ -3854,18 +3948,13 @@ void intel_ddi_get_clock(struct intel_encoder *encoder, static void mtl_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state) { - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - enum phy phy = intel_port_to_phy(i915, encoder->port); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) { crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); - } else if (intel_is_c10phy(i915, phy)) { - intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); - crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); } else { - intel_c20pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c20); - crtc_state->port_clock = intel_c20pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c20); + intel_cx0pll_readout_hw_state(encoder, &crtc_state->cx0pll_state); + crtc_state->port_clock = intel_cx0pll_calc_port_clock(encoder, &crtc_state->cx0pll_state); } intel_ddi_get_config(encoder, crtc_state); @@ -4844,6 +4933,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, encoder->post_pll_disable = intel_ddi_post_pll_disable; encoder->post_disable = intel_ddi_post_disable; encoder->update_pipe = intel_ddi_update_pipe; + encoder->audio_enable = intel_audio_codec_enable; + encoder->audio_disable = intel_audio_codec_disable; encoder->get_hw_state = intel_ddi_get_hw_state; encoder->sync_state = intel_ddi_sync_state; encoder->initial_fastset_check = intel_ddi_initial_fastset_check; diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index 4999c0ee229b..63853a1f6582 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -27,6 +27,8 @@ i915_reg_t dp_tp_ctl_reg(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); i915_reg_t dp_tp_status_reg(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); +i915_reg_t hsw_chicken_trans_reg(struct drm_i915_private *i915, + enum transcoder cpu_transcoder); void intel_ddi_fdi_post_disable(struct intel_atomic_state *state, struct intel_encoder *intel_encoder, const struct intel_crtc_state *old_crtc_state, @@ -60,6 +62,9 @@ void intel_ddi_disable_transcoder_func(const struct intel_crtc_state *crtc_state void intel_ddi_enable_transcoder_clock(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_ddi_disable_transcoder_clock(const struct intel_crtc_state *crtc_state); +void intel_ddi_wait_for_fec_status(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + bool enabled); void intel_ddi_set_dp_msa(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 28d85e1e858e..5cf162628b95 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -48,6 +48,7 @@ #include "g4x_dp.h" #include "g4x_hdmi.h" #include "hsw_ips.h" +#include "i915_config.h" #include "i915_drv.h" #include "i915_reg.h" #include "i915_utils.h" @@ -72,10 +73,10 @@ #include "intel_dp.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" -#include "intel_dpio_phy.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" #include "intel_dpt.h" +#include "intel_dpt_common.h" #include "intel_drrs.h" #include "intel_dsb.h" #include "intel_dsi.h" @@ -193,12 +194,9 @@ static bool is_hdr_mode(const struct intel_crtc_state *crtc_state) static void skl_wa_827(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) { - if (enable) - intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), - 0, DUPS1_GATING_DIS | DUPS2_GATING_DIS); - else - intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), - DUPS1_GATING_DIS | DUPS2_GATING_DIS, 0); + intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), + DUPS1_GATING_DIS | DUPS2_GATING_DIS, + enable ? DUPS1_GATING_DIS | DUPS2_GATING_DIS : 0); } /* Wa_2006604312:icl,ehl */ @@ -206,10 +204,9 @@ static void icl_wa_scalerclkgating(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) { - if (enable) - intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), 0, DPFR_GATING_DIS); - else - intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), DPFR_GATING_DIS, 0); + intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), + DPFR_GATING_DIS, + enable ? DPFR_GATING_DIS : 0); } /* Wa_1604331009:icl,jsl,ehl */ @@ -217,7 +214,8 @@ static void icl_wa_cursorclkgating(struct drm_i915_private *dev_priv, enum pipe pipe, bool enable) { - intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), CURSOR_GATING_DIS, + intel_de_rmw(dev_priv, CLKGATE_DIS_PSL(pipe), + CURSOR_GATING_DIS, enable ? CURSOR_GATING_DIS : 0); } @@ -397,7 +395,6 @@ void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum transcoder cpu_transcoder = new_crtc_state->cpu_transcoder; enum pipe pipe = crtc->pipe; - i915_reg_t reg; u32 val; drm_dbg_kms(&dev_priv->drm, "enabling pipe %c\n", pipe_name(pipe)); @@ -430,16 +427,16 @@ void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state) intel_de_rmw(dev_priv, PIPE_ARB_CTL(pipe), 0, PIPE_ARB_USE_PROG_SLOTS); - reg = TRANSCONF(cpu_transcoder); - val = intel_de_read(dev_priv, reg); + val = intel_de_read(dev_priv, TRANSCONF(cpu_transcoder)); if (val & TRANSCONF_ENABLE) { /* we keep both pipes enabled on 830 */ drm_WARN_ON(&dev_priv->drm, !IS_I830(dev_priv)); return; } - intel_de_write(dev_priv, reg, val | TRANSCONF_ENABLE); - intel_de_posting_read(dev_priv, reg); + intel_de_write(dev_priv, TRANSCONF(cpu_transcoder), + val | TRANSCONF_ENABLE); + intel_de_posting_read(dev_priv, TRANSCONF(cpu_transcoder)); /* * Until the pipe starts PIPEDSL reads will return a stale value, @@ -458,7 +455,6 @@ void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder; enum pipe pipe = crtc->pipe; - i915_reg_t reg; u32 val; drm_dbg_kms(&dev_priv->drm, "disabling pipe %c\n", pipe_name(pipe)); @@ -469,8 +465,7 @@ void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state) */ assert_planes_disabled(crtc); - reg = TRANSCONF(cpu_transcoder); - val = intel_de_read(dev_priv, reg); + val = intel_de_read(dev_priv, TRANSCONF(cpu_transcoder)); if ((val & TRANSCONF_ENABLE) == 0) return; @@ -485,14 +480,12 @@ void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state) if (!IS_I830(dev_priv)) val &= ~TRANSCONF_ENABLE; - if (DISPLAY_VER(dev_priv) >= 14) - intel_de_rmw(dev_priv, MTL_CHICKEN_TRANS(cpu_transcoder), - FECSTALL_DIS_DPTSTREAM_DPTTG, 0); - else if (DISPLAY_VER(dev_priv) >= 12) - intel_de_rmw(dev_priv, CHICKEN_TRANS(cpu_transcoder), + intel_de_write(dev_priv, TRANSCONF(cpu_transcoder), val); + + if (DISPLAY_VER(dev_priv) >= 12) + intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, cpu_transcoder), FECSTALL_DIS_DPTSTREAM_DPTTG, 0); - intel_de_write(dev_priv, reg, val); if ((val & TRANSCONF_ENABLE) == 0) intel_wait_for_pipe_off(old_crtc_state); } @@ -896,6 +889,48 @@ static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state) (DISPLAY_VER(i915) == 9 || IS_BROADWELL(i915) || IS_HASWELL(i915)); } +static void intel_encoders_audio_enable(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + const struct drm_connector_state *conn_state; + struct drm_connector *conn; + int i; + + for_each_new_connector_in_state(&state->base, conn, conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(conn_state->best_encoder); + + if (conn_state->crtc != &crtc->base) + continue; + + if (encoder->audio_enable) + encoder->audio_enable(encoder, crtc_state, conn_state); + } +} + +static void intel_encoders_audio_disable(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + const struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_old_connector_in_state(&state->base, conn, old_conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(old_conn_state->best_encoder); + + if (old_conn_state->crtc != &crtc->base) + continue; + + if (encoder->audio_disable) + encoder->audio_disable(encoder, old_crtc_state, old_conn_state); + } +} + #define is_enabling(feature, old_crtc_state, new_crtc_state) \ ((!(old_crtc_state)->feature || intel_crtc_needs_modeset(new_crtc_state)) && \ (new_crtc_state)->feature) @@ -906,12 +941,18 @@ static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state) static bool planes_enabling(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { + if (!new_crtc_state->hw.active) + return false; + return is_enabling(active_planes, old_crtc_state, new_crtc_state); } static bool planes_disabling(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { + if (!old_crtc_state->hw.active) + return false; + return is_disabling(active_planes, old_crtc_state, new_crtc_state); } @@ -928,6 +969,9 @@ static bool vrr_params_changed(const struct intel_crtc_state *old_crtc_state, static bool vrr_enabling(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { + if (!new_crtc_state->hw.active) + return false; + return is_enabling(vrr.enable, old_crtc_state, new_crtc_state) || (new_crtc_state->vrr.enable && (new_crtc_state->update_m_n || new_crtc_state->update_lrr || @@ -937,12 +981,37 @@ static bool vrr_enabling(const struct intel_crtc_state *old_crtc_state, static bool vrr_disabling(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state) { + if (!old_crtc_state->hw.active) + return false; + return is_disabling(vrr.enable, old_crtc_state, new_crtc_state) || (old_crtc_state->vrr.enable && (new_crtc_state->update_m_n || new_crtc_state->update_lrr || vrr_params_changed(old_crtc_state, new_crtc_state))); } +static bool audio_enabling(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + if (!new_crtc_state->hw.active) + return false; + + return is_enabling(has_audio, old_crtc_state, new_crtc_state) || + (new_crtc_state->has_audio && + memcmp(old_crtc_state->eld, new_crtc_state->eld, MAX_ELD_BYTES) != 0); +} + +static bool audio_disabling(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + if (!old_crtc_state->hw.active) + return false; + + return is_disabling(has_audio, old_crtc_state, new_crtc_state) || + (old_crtc_state->has_audio && + memcmp(old_crtc_state->eld, new_crtc_state->eld, MAX_ELD_BYTES) != 0); +} + #undef is_disabling #undef is_enabling @@ -983,6 +1052,9 @@ static void intel_post_plane_update(struct intel_atomic_state *state, if (intel_crtc_needs_color_update(new_crtc_state)) intel_color_post_update(new_crtc_state); + + if (audio_enabling(old_crtc_state, new_crtc_state)) + intel_encoders_audio_enable(state, crtc); } static void intel_crtc_enable_flip_done(struct intel_atomic_state *state, @@ -1066,6 +1138,9 @@ static void intel_pre_plane_update(struct intel_atomic_state *state, intel_crtc_update_active_timings(old_crtc_state, false); } + if (audio_disabling(old_crtc_state, new_crtc_state)) + intel_encoders_audio_disable(state, crtc); + intel_drrs_deactivate(old_crtc_state); intel_psr_pre_plane_update(state, crtc); @@ -1501,12 +1576,9 @@ static void hsw_set_linetime_wm(const struct intel_crtc_state *crtc_state) static void hsw_set_frame_start_delay(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - enum transcoder transcoder = crtc_state->cpu_transcoder; - i915_reg_t reg = DISPLAY_VER(dev_priv) >= 14 ? MTL_CHICKEN_TRANS(transcoder) : - CHICKEN_TRANS(transcoder); + struct drm_i915_private *i915 = to_i915(crtc->base.dev); - intel_de_rmw(dev_priv, reg, + intel_de_rmw(i915, hsw_chicken_trans_reg(i915, crtc_state->cpu_transcoder), HSW_FRAME_START_DELAY_MASK, HSW_FRAME_START_DELAY(crtc_state->framestart_delay - 1)); } @@ -1784,31 +1856,31 @@ bool intel_phy_is_combo(struct drm_i915_private *dev_priv, enum phy phy) bool intel_phy_is_tc(struct drm_i915_private *dev_priv, enum phy phy) { + /* + * DG2's "TC1", although TC-capable output, doesn't share the same flow + * as other platforms on the display engine side and rather rely on the + * SNPS PHY, that is programmed separately + */ if (IS_DG2(dev_priv)) - /* DG2's "TC1" output uses a SNPS PHY */ return false; - else if (IS_ALDERLAKE_P(dev_priv) || DISPLAY_VER_FULL(dev_priv) == IP_VER(14, 0)) + + if (DISPLAY_VER(dev_priv) >= 13) return phy >= PHY_F && phy <= PHY_I; else if (IS_TIGERLAKE(dev_priv)) return phy >= PHY_D && phy <= PHY_I; else if (IS_ICELAKE(dev_priv)) return phy >= PHY_C && phy <= PHY_F; - else - return false; + + return false; } bool intel_phy_is_snps(struct drm_i915_private *dev_priv, enum phy phy) { - if (phy == PHY_NONE) - return false; - else if (IS_DG2(dev_priv)) - /* - * All four "combo" ports and the TC1 port (PHY E) use - * Synopsis PHYs. - */ - return phy <= PHY_E; - - return false; + /* + * For DG2, and for DG2 only, all four "combo" ports and the TC1 port + * (PHY E) use Synopsis PHYs. See intel_phy_is_tc(). + */ + return IS_DG2(dev_priv) && phy > PHY_NONE && phy <= PHY_E; } enum phy intel_port_to_phy(struct drm_i915_private *i915, enum port port) @@ -2397,15 +2469,15 @@ static void compute_m_n(u32 *ret_m, u32 *ret_n, } void -intel_link_compute_m_n(u16 bits_per_pixel, int nlanes, +intel_link_compute_m_n(u16 bits_per_pixel_x16, int nlanes, int pixel_clock, int link_clock, - struct intel_link_m_n *m_n, - bool fec_enable) + int bw_overhead, + struct intel_link_m_n *m_n) { - u32 data_clock = bits_per_pixel * pixel_clock; - - if (fec_enable) - data_clock = intel_dp_mode_to_fec_clock(data_clock); + u32 link_symbol_clock = intel_dp_link_symbol_clock(link_clock); + u32 data_m = intel_dp_effective_data_rate(pixel_clock, bits_per_pixel_x16, + bw_overhead); + u32 data_n = intel_dp_max_data_rate(link_clock, nlanes); /* * Windows/BIOS uses fixed M/N values always. Follow suit. @@ -2416,11 +2488,11 @@ intel_link_compute_m_n(u16 bits_per_pixel, int nlanes, */ m_n->tu = 64; compute_m_n(&m_n->data_m, &m_n->data_n, - data_clock, link_clock * nlanes * 8, + data_m, data_n, 0x8000000); compute_m_n(&m_n->link_m, &m_n->link_n, - pixel_clock, link_clock, + pixel_clock, link_symbol_clock, 0x80000); } @@ -2838,67 +2910,6 @@ static void i9xx_get_pfit_config(struct intel_crtc_state *crtc_state) intel_de_read(dev_priv, PFIT_PGM_RATIOS); } -static void vlv_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - struct dpll clock; - u32 mdiv; - int refclk = 100000; - - /* In case of DSI, DPLL will not be used */ - if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0) - return; - - vlv_dpio_get(dev_priv); - mdiv = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW3(pipe)); - vlv_dpio_put(dev_priv); - - clock.m1 = (mdiv >> DPIO_M1DIV_SHIFT) & 7; - clock.m2 = mdiv & DPIO_M2DIV_MASK; - clock.n = (mdiv >> DPIO_N_SHIFT) & 0xf; - clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7; - clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f; - - pipe_config->port_clock = vlv_calc_dpll_params(refclk, &clock); -} - -static void chv_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum pipe pipe = crtc->pipe; - enum dpio_channel port = vlv_pipe_to_channel(pipe); - struct dpll clock; - u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2, pll_dw3; - int refclk = 100000; - - /* In case of DSI, DPLL will not be used */ - if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0) - return; - - vlv_dpio_get(dev_priv); - cmn_dw13 = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW13(port)); - pll_dw0 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW0(port)); - pll_dw1 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW1(port)); - pll_dw2 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW2(port)); - pll_dw3 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW3(port)); - vlv_dpio_put(dev_priv); - - clock.m1 = (pll_dw1 & 0x7) == DPIO_CHV_M1_DIV_BY_2 ? 2 : 0; - clock.m2 = (pll_dw0 & 0xff) << 22; - if (pll_dw3 & DPIO_CHV_FRAC_DIV_EN) - clock.m2 |= pll_dw2 & 0x3fffff; - clock.n = (pll_dw1 >> DPIO_CHV_N_DIV_SHIFT) & 0xf; - clock.p1 = (cmn_dw13 >> DPIO_CHV_P1_DIV_SHIFT) & 0x7; - clock.p2 = (cmn_dw13 >> DPIO_CHV_P2_DIV_SHIFT) & 0x1f; - - pipe_config->port_clock = chv_calc_dpll_params(refclk, &clock); -} - static enum intel_output_format bdw_get_pipe_misc_output_format(struct intel_crtc *crtc) { @@ -3790,9 +3801,7 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc, } if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) { - tmp = intel_de_read(dev_priv, DISPLAY_VER(dev_priv) >= 14 ? - MTL_CHICKEN_TRANS(pipe_config->cpu_transcoder) : - CHICKEN_TRANS(pipe_config->cpu_transcoder)); + tmp = intel_de_read(dev_priv, hsw_chicken_trans_reg(dev_priv, pipe_config->cpu_transcoder)); pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1; } else { @@ -3821,133 +3830,27 @@ bool intel_crtc_get_pipe_config(struct intel_crtc_state *crtc_state) return true; } -static int i9xx_pll_refclk(struct drm_device *dev, - const struct intel_crtc_state *pipe_config) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - u32 dpll = pipe_config->dpll_hw_state.dpll; - - if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) - return dev_priv->display.vbt.lvds_ssc_freq; - else if (HAS_PCH_SPLIT(dev_priv)) - return 120000; - else if (DISPLAY_VER(dev_priv) != 2) - return 96000; - else - return 48000; -} - -/* Returns the clock of the currently programmed mode of the given pipe. */ -void i9xx_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - u32 dpll = pipe_config->dpll_hw_state.dpll; - u32 fp; - struct dpll clock; - int port_clock; - int refclk = i9xx_pll_refclk(dev, pipe_config); - - if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = pipe_config->dpll_hw_state.fp0; - else - fp = pipe_config->dpll_hw_state.fp1; - - clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; - if (IS_PINEVIEW(dev_priv)) { - clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1; - clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT; - } else { - clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; - clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; - } - - if (DISPLAY_VER(dev_priv) != 2) { - if (IS_PINEVIEW(dev_priv)) - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >> - DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW); - else - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - - switch (dpll & DPLL_MODE_MASK) { - case DPLLB_MODE_DAC_SERIAL: - clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? - 5 : 10; - break; - case DPLLB_MODE_LVDS: - clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? - 7 : 14; - break; - default: - drm_dbg_kms(&dev_priv->drm, - "Unknown DPLL mode %08x in programmed " - "mode\n", (int)(dpll & DPLL_MODE_MASK)); - return; - } - - if (IS_PINEVIEW(dev_priv)) - port_clock = pnv_calc_dpll_params(refclk, &clock); - else - port_clock = i9xx_calc_dpll_params(refclk, &clock); - } else { - enum pipe lvds_pipe; - - if (IS_I85X(dev_priv) && - intel_lvds_port_enabled(dev_priv, LVDS, &lvds_pipe) && - lvds_pipe == crtc->pipe) { - u32 lvds = intel_de_read(dev_priv, LVDS); - - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - - if (lvds & LVDS_CLKB_POWER_UP) - clock.p2 = 7; - else - clock.p2 = 14; - } else { - if (dpll & PLL_P1_DIVIDE_BY_TWO) - clock.p1 = 2; - else { - clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> - DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; - } - if (dpll & PLL_P2_DIVIDE_BY_4) - clock.p2 = 4; - else - clock.p2 = 2; - } - - port_clock = i9xx_calc_dpll_params(refclk, &clock); - } - - /* - * This value includes pixel_multiplier. We will use - * port_clock to compute adjusted_mode.crtc_clock in the - * encoder's get_config() function. - */ - pipe_config->port_clock = port_clock; -} - int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n) { /* - * The calculation for the data clock is: + * The calculation for the data clock -> pixel clock is: * pixel_clock = ((m/n)*(link_clock * nr_lanes))/bpp * But we want to avoid losing precison if possible, so: * pixel_clock = ((m * link_clock * nr_lanes)/(n*bpp)) * - * and the link clock is simpler: - * link_clock = (m * link_clock) / n + * and for link freq (10kbs units) -> pixel clock it is: + * link_symbol_clock = link_freq * 10 / link_symbol_size + * pixel_clock = (m * link_symbol_clock) / n + * or for more precision: + * pixel_clock = (m * link_freq * 10) / (n * link_symbol_size) */ if (!m_n->link_n) return 0; - return DIV_ROUND_UP_ULL(mul_u32_u32(m_n->link_m, link_freq), - m_n->link_n); + return DIV_ROUND_UP_ULL(mul_u32_u32(m_n->link_m, link_freq * 10), + m_n->link_n * intel_dp_link_symbol_size(link_freq)); } int intel_crtc_dotclock(const struct intel_crtc_state *pipe_config) @@ -4679,6 +4582,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, if (ret) return ret; + crtc_state->fec_enable = limits->force_fec_pipes & BIT(crtc->pipe); crtc_state->max_link_bpp_x16 = limits->max_bpp_x16[crtc->pipe]; if (crtc_state->pipe_bpp > to_bpp_int(crtc_state->max_link_bpp_x16)) { @@ -5057,23 +4961,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, } \ } while (0) -/* - * Checks state where we only read out the enabling, but not the entire - * state itself (like full infoframes or ELD for audio). These states - * require a full modeset on bootup to fix up. - */ -#define PIPE_CONF_CHECK_BOOL_INCOMPLETE(name) do { \ - if (!fixup_inherited || (!current_config->name && !pipe_config->name)) { \ - PIPE_CONF_CHECK_BOOL(name); \ - } else { \ - pipe_config_mismatch(fastset, crtc, __stringify(name), \ - "unable to verify whether state matches exactly, forcing modeset (expected %s, found %s)", \ - str_yes_no(current_config->name), \ - str_yes_no(pipe_config->name)); \ - ret = false; \ - } \ -} while (0) - #define PIPE_CONF_CHECK_P(name) do { \ if (current_config->name != pipe_config->name) { \ pipe_config_mismatch(fastset, crtc, __stringify(name), \ @@ -5261,8 +5148,10 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_BOOL(enhanced_framing); PIPE_CONF_CHECK_BOOL(fec_enable); - PIPE_CONF_CHECK_BOOL_INCOMPLETE(has_audio); - PIPE_CONF_CHECK_BUFFER(eld, MAX_ELD_BYTES); + if (!fastset) { + PIPE_CONF_CHECK_BOOL(has_audio); + PIPE_CONF_CHECK_BUFFER(eld, MAX_ELD_BYTES); + } PIPE_CONF_CHECK_X(gmch_pfit.control); /* pfit ratios are autocomputed by the hw on gen4+ */ @@ -5414,7 +5303,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(dsc.compression_enable); PIPE_CONF_CHECK_I(dsc.dsc_split); - PIPE_CONF_CHECK_I(dsc.compressed_bpp); + PIPE_CONF_CHECK_I(dsc.compressed_bpp_x16); PIPE_CONF_CHECK_BOOL(splitter.enable); PIPE_CONF_CHECK_I(splitter.link_count); @@ -5432,7 +5321,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, #undef PIPE_CONF_CHECK_X #undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_BOOL -#undef PIPE_CONF_CHECK_BOOL_INCOMPLETE #undef PIPE_CONF_CHECK_P #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_CHECK_COLOR_LUT @@ -5523,6 +5411,16 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state, return 0; } +static void +intel_crtc_flag_modeset(struct intel_crtc_state *crtc_state) +{ + crtc_state->uapi.mode_changed = true; + + crtc_state->update_pipe = false; + crtc_state->update_m_n = false; + crtc_state->update_lrr = false; +} + /** * intel_modeset_all_pipes_late - force a full modeset on all pipes * @state: intel atomic state @@ -5556,9 +5454,8 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state, if (ret) return ret; - crtc_state->update_pipe = false; - crtc_state->update_m_n = false; - crtc_state->update_lrr = false; + intel_crtc_flag_modeset(crtc_state); + crtc_state->update_planes |= crtc_state->active_planes; crtc_state->async_flip_planes = 0; crtc_state->do_async_flip = false; @@ -5671,17 +5568,17 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta else new_crtc_state->uapi.mode_changed = false; - if (intel_crtc_needs_modeset(new_crtc_state) || - intel_compare_link_m_n(&old_crtc_state->dp_m_n, + if (intel_compare_link_m_n(&old_crtc_state->dp_m_n, &new_crtc_state->dp_m_n)) new_crtc_state->update_m_n = false; - if (intel_crtc_needs_modeset(new_crtc_state) || - (old_crtc_state->hw.adjusted_mode.crtc_vtotal == new_crtc_state->hw.adjusted_mode.crtc_vtotal && + if ((old_crtc_state->hw.adjusted_mode.crtc_vtotal == new_crtc_state->hw.adjusted_mode.crtc_vtotal && old_crtc_state->hw.adjusted_mode.crtc_vblank_end == new_crtc_state->hw.adjusted_mode.crtc_vblank_end)) new_crtc_state->update_lrr = false; - if (!intel_crtc_needs_modeset(new_crtc_state)) + if (intel_crtc_needs_modeset(new_crtc_state)) + intel_crtc_flag_modeset(new_crtc_state); + else new_crtc_state->update_pipe = true; } @@ -6453,15 +6350,14 @@ int intel_atomic_check(struct drm_device *dev, if (!new_crtc_state->hw.enable || intel_crtc_needs_modeset(new_crtc_state)) continue; + if (intel_dp_mst_crtc_needs_modeset(state, crtc)) + intel_crtc_flag_modeset(new_crtc_state); + if (intel_dp_mst_is_slave_trans(new_crtc_state)) { enum transcoder master = new_crtc_state->mst_master_transcoder; - if (intel_cpu_transcoders_need_modeset(state, BIT(master))) { - new_crtc_state->uapi.mode_changed = true; - new_crtc_state->update_pipe = false; - new_crtc_state->update_m_n = false; - new_crtc_state->update_lrr = false; - } + if (intel_cpu_transcoders_need_modeset(state, BIT(master))) + intel_crtc_flag_modeset(new_crtc_state); } if (is_trans_port_sync_mode(new_crtc_state)) { @@ -6470,21 +6366,13 @@ int intel_atomic_check(struct drm_device *dev, if (new_crtc_state->master_transcoder != INVALID_TRANSCODER) trans |= BIT(new_crtc_state->master_transcoder); - if (intel_cpu_transcoders_need_modeset(state, trans)) { - new_crtc_state->uapi.mode_changed = true; - new_crtc_state->update_pipe = false; - new_crtc_state->update_m_n = false; - new_crtc_state->update_lrr = false; - } + if (intel_cpu_transcoders_need_modeset(state, trans)) + intel_crtc_flag_modeset(new_crtc_state); } if (new_crtc_state->bigjoiner_pipes) { - if (intel_pipes_need_modeset(state, new_crtc_state->bigjoiner_pipes)) { - new_crtc_state->uapi.mode_changed = true; - new_crtc_state->update_pipe = false; - new_crtc_state->update_m_n = false; - new_crtc_state->update_lrr = false; - } + if (intel_pipes_need_modeset(state, new_crtc_state->bigjoiner_pipes)) + intel_crtc_flag_modeset(new_crtc_state); } } @@ -6505,10 +6393,6 @@ int intel_atomic_check(struct drm_device *dev, goto fail; } - ret = drm_dp_mst_atomic_check(&state->base); - if (ret) - goto fail; - ret = intel_atomic_check_planes(state); if (ret) goto fail; @@ -6744,8 +6628,8 @@ static void intel_enable_crtc(struct intel_atomic_state *state, intel_crtc_enable_pipe_crc(crtc); } -static void intel_update_crtc(struct intel_atomic_state *state, - struct intel_crtc *crtc) +static void intel_pre_update_crtc(struct intel_atomic_state *state, + struct intel_crtc *crtc) { struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_crtc_state *old_crtc_state = @@ -6787,6 +6671,15 @@ static void intel_update_crtc(struct intel_atomic_state *state, intel_color_commit_noarm(new_crtc_state); intel_crtc_planes_update_noarm(state, crtc); +} + +static void intel_update_crtc(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + struct intel_crtc_state *new_crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); /* Perform vblank evasion around commit operation */ intel_pipe_update_start(state, crtc); @@ -6815,7 +6708,7 @@ static void intel_update_crtc(struct intel_atomic_state *state, * valid pipe configuration from the BIOS we need to take care * of enabling them on the CRTC's first fastset. */ - if (intel_crtc_needs_fastset(new_crtc_state) && !modeset && + if (intel_crtc_needs_fastset(new_crtc_state) && old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); } @@ -6853,10 +6746,11 @@ static void intel_commit_modeset_disables(struct intel_atomic_state *state) if (!intel_crtc_needs_modeset(new_crtc_state)) continue; + intel_pre_plane_update(state, crtc); + if (!old_crtc_state->hw.active) continue; - intel_pre_plane_update(state, crtc); intel_crtc_disable_planes(state, crtc); } @@ -6910,6 +6804,13 @@ static void intel_commit_modeset_enables(struct intel_atomic_state *state) continue; intel_enable_crtc(state, crtc); + intel_pre_update_crtc(state, crtc); + } + + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { + if (!new_crtc_state->hw.active) + continue; + intel_update_crtc(state, crtc); } } @@ -6947,6 +6848,15 @@ static void skl_commit_modeset_enables(struct intel_atomic_state *state) * So first lets enable all pipes that do not need a fullmodeset as * those don't have any external dependency. */ + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { + enum pipe pipe = crtc->pipe; + + if ((update_pipes & BIT(pipe)) == 0) + continue; + + intel_pre_update_crtc(state, crtc); + } + while (update_pipes) { for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { @@ -7023,6 +6933,15 @@ static void skl_commit_modeset_enables(struct intel_atomic_state *state) if ((update_pipes & BIT(pipe)) == 0) continue; + intel_pre_update_crtc(state, crtc); + } + + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { + enum pipe pipe = crtc->pipe; + + if ((update_pipes & BIT(pipe)) == 0) + continue; + drm_WARN_ON(&dev_priv->drm, skl_ddb_allocation_overlaps(&new_crtc_state->wm.skl.ddb, entries, I915_MAX_PIPES, pipe)); @@ -7056,29 +6975,22 @@ void intel_atomic_helper_free_state_worker(struct work_struct *work) static void intel_atomic_commit_fence_wait(struct intel_atomic_state *intel_state) { - struct wait_queue_entry wait_fence, wait_reset; - struct drm_i915_private *dev_priv = to_i915(intel_state->base.dev); - - init_wait_entry(&wait_fence, 0); - init_wait_entry(&wait_reset, 0); - for (;;) { - prepare_to_wait(&intel_state->commit_ready.wait, - &wait_fence, TASK_UNINTERRUPTIBLE); - prepare_to_wait(bit_waitqueue(&to_gt(dev_priv)->reset.flags, - I915_RESET_MODESET), - &wait_reset, TASK_UNINTERRUPTIBLE); - + struct drm_i915_private *i915 = to_i915(intel_state->base.dev); + struct drm_plane *plane; + struct drm_plane_state *new_plane_state; + int ret, i; - if (i915_sw_fence_done(&intel_state->commit_ready) || - test_bit(I915_RESET_MODESET, &to_gt(dev_priv)->reset.flags)) - break; + for_each_new_plane_in_state(&intel_state->base, plane, new_plane_state, i) { + if (new_plane_state->fence) { + ret = dma_fence_wait_timeout(new_plane_state->fence, false, + i915_fence_timeout(i915)); + if (ret <= 0) + break; - schedule(); + dma_fence_put(new_plane_state->fence); + new_plane_state->fence = NULL; + } } - finish_wait(&intel_state->commit_ready.wait, &wait_fence); - finish_wait(bit_waitqueue(&to_gt(dev_priv)->reset.flags, - I915_RESET_MODESET), - &wait_reset); } static void intel_atomic_cleanup_work(struct work_struct *work) @@ -7370,32 +7282,6 @@ static void intel_atomic_commit_work(struct work_struct *work) intel_atomic_commit_tail(state); } -static int -intel_atomic_commit_ready(struct i915_sw_fence *fence, - enum i915_sw_fence_notify notify) -{ - struct intel_atomic_state *state = - container_of(fence, struct intel_atomic_state, commit_ready); - - switch (notify) { - case FENCE_COMPLETE: - /* we do blocking waits in the worker, nothing to do here */ - break; - case FENCE_FREE: - { - struct drm_i915_private *i915 = to_i915(state->base.dev); - struct intel_atomic_helper *helper = - &i915->display.atomic_helper; - - if (llist_add(&state->freed, &helper->free_list)) - queue_work(i915->unordered_wq, &helper->free_work); - break; - } - } - - return NOTIFY_DONE; -} - static void intel_atomic_track_fbs(struct intel_atomic_state *state) { struct intel_plane_state *old_plane_state, *new_plane_state; @@ -7418,10 +7304,6 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state, state->wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); - drm_atomic_state_get(&state->base); - i915_sw_fence_init(&state->commit_ready, - intel_atomic_commit_ready); - /* * The intel_legacy_cursor_update() fast path takes care * of avoiding the vblank waits for simple cursor @@ -7454,7 +7336,6 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state, if (ret) { drm_dbg_atomic(&dev_priv->drm, "Preparing state failed with %i\n", ret); - i915_sw_fence_commit(&state->commit_ready); intel_runtime_pm_put(&dev_priv->runtime_pm, state->wakeref); return ret; } @@ -7470,8 +7351,6 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state, struct intel_crtc *crtc; int i; - i915_sw_fence_commit(&state->commit_ready); - for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) intel_color_cleanup_commit(new_crtc_state); @@ -7485,7 +7364,6 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state, drm_atomic_state_get(&state->base); INIT_WORK(&state->base.commit_work, intel_atomic_commit_work); - i915_sw_fence_commit(&state->commit_ready); if (nonblock && state->modeset) { queue_work(dev_priv->display.wq.modeset, &state->base.commit_work); } else if (nonblock) { diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 0e5dffe8f018..8548f49e3972 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -105,7 +105,6 @@ enum i9xx_plane_id { }; #define plane_name(p) ((p) + 'A') -#define sprite_name(p, s) ((p) * DISPLAY_RUNTIME_INFO(dev_priv)->num_sprites[(p)] + (s) + 'A') #define for_each_plane_id_on_crtc(__crtc, __p) \ for ((__p) = PLANE_PRIMARY; (__p) < I915_MAX_PLANES; (__p)++) \ @@ -395,8 +394,8 @@ u8 intel_calc_active_pipes(struct intel_atomic_state *state, u8 active_pipes); void intel_link_compute_m_n(u16 bpp, int nlanes, int pixel_clock, int link_clock, - struct intel_link_m_n *m_n, - bool fec_enable); + int bw_overhead, + struct intel_link_m_n *m_n); u32 intel_plane_fb_max_stride(struct drm_i915_private *dev_priv, u32 pixel_format, u64 modifier); enum drm_mode_status @@ -482,8 +481,6 @@ void intel_cpu_transcoder_get_m1_n1(struct intel_crtc *crtc, void intel_cpu_transcoder_get_m2_n2(struct intel_crtc *crtc, enum transcoder cpu_transcoder, struct intel_link_m_n *m_n); -void i9xx_crtc_clock_get(struct intel_crtc *crtc, - struct intel_crtc_state *pipe_config); int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n); int intel_crtc_dotclock(const struct intel_crtc_state *pipe_config); enum intel_display_power_domain intel_port_to_power_domain(struct intel_digital_port *dig_port); @@ -552,7 +549,7 @@ bool assert_port_valid(struct drm_i915_private *i915, enum port port); struct drm_device *drm = &(__i915)->drm; \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ - if (!drm_WARN(drm, i915_modparams.verbose_state_checks, format)) \ + if (!drm_WARN(drm, __i915->display.params.verbose_state_checks, format)) \ drm_err(drm, format); \ unlikely(__ret_warn_on); \ }) diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index ccfe27630fb6..7e82b87e9cde 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -19,6 +19,7 @@ #include "intel_cdclk.h" #include "intel_display_device.h" #include "intel_display_limits.h" +#include "intel_display_params.h" #include "intel_display_power.h" #include "intel_dpll_mgr.h" #include "intel_fbc.h" @@ -348,15 +349,6 @@ struct intel_display { } dbuf; struct { - wait_queue_head_t waitqueue; - - /* mutex to protect pmdemand programming sequence */ - struct mutex lock; - - struct intel_global_obj obj; - } pmdemand; - - struct { /* * dkl.phy_lock protects against concurrent access of the * Dekel TypeC PHYs. @@ -444,6 +436,15 @@ struct intel_display { } ips; struct { + wait_queue_head_t waitqueue; + + /* mutex to protect pmdemand programming sequence */ + struct mutex lock; + + struct intel_global_obj obj; + } pmdemand; + + struct { struct i915_power_domains domains; /* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */ @@ -520,6 +521,7 @@ struct intel_display { struct intel_hotplug hotplug; struct intel_opregion opregion; struct intel_overlay *overlay; + struct intel_display_params params; struct intel_vbt_data vbt; struct intel_wm wm; }; diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 2836826f8c05..915420d0cef8 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -17,6 +17,7 @@ #include "intel_de.h" #include "intel_crtc_state_dump.h" #include "intel_display_debugfs.h" +#include "intel_display_debugfs_params.h" #include "intel_display_power.h" #include "intel_display_power_well.h" #include "intel_display_types.h" @@ -641,6 +642,17 @@ static int i915_display_info(struct seq_file *m, void *unused) return 0; } +static int i915_display_capabilities(struct seq_file *m, void *unused) +{ + struct drm_i915_private *i915 = node_to_i915(m->private); + struct drm_printer p = drm_seq_file_printer(m); + + intel_display_device_info_print(DISPLAY_INFO(i915), + DISPLAY_RUNTIME_INFO(i915), &p); + + return 0; +} + static int i915_shared_dplls_info(struct seq_file *m, void *unused) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -1059,6 +1071,7 @@ static const struct drm_info_list intel_display_debugfs_list[] = { {"i915_gem_framebuffer", i915_gem_framebuffer_info, 0}, {"i915_power_domain_info", i915_power_domain_info, 0}, {"i915_display_info", i915_display_info, 0}, + {"i915_display_capabilities", i915_display_capabilities, 0}, {"i915_shared_dplls_info", i915_shared_dplls_info, 0}, {"i915_dp_mst_info", i915_dp_mst_info, 0}, {"i915_ddb_info", i915_ddb_info, 0}, @@ -1098,6 +1111,7 @@ void intel_display_debugfs_register(struct drm_i915_private *i915) intel_hpd_debugfs_register(i915); intel_psr_debugfs_register(i915); intel_wm_debugfs_register(i915); + intel_display_debugfs_params(i915); } static int i915_panel_show(struct seq_file *m, void *data) @@ -1242,6 +1256,8 @@ static int i915_dsc_fec_support_show(struct seq_file *m, void *data) DP_DSC_YCbCr420_Native)), str_yes_no(drm_dp_dsc_sink_supports_format(connector->dp.dsc_dpcd, DP_DSC_YCbCr444))); + seq_printf(m, "DSC_Sink_BPP_Precision: %d\n", + drm_dp_dsc_sink_bpp_incr(connector->dp.dsc_dpcd)); seq_printf(m, "Force_DSC_Enable: %s\n", str_yes_no(intel_dp->force_dsc_en)); if (!intel_dp_is_edp(intel_dp)) @@ -1434,6 +1450,85 @@ static const struct file_operations i915_dsc_output_format_fops = { .write = i915_dsc_output_format_write }; +static int i915_dsc_fractional_bpp_show(struct seq_file *m, void *data) +{ + struct drm_connector *connector = m->private; + struct drm_device *dev = connector->dev; + struct drm_crtc *crtc; + struct intel_dp *intel_dp; + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_encoder *encoder = intel_attached_encoder(intel_connector); + int ret; + + if (!encoder) + return -ENODEV; + + ret = drm_modeset_lock_single_interruptible(&dev->mode_config.connection_mutex); + if (ret) + return ret; + + crtc = connector->state->crtc; + if (connector->status != connector_status_connected || !crtc) { + ret = -ENODEV; + goto out; + } + + intel_dp = intel_attached_dp(intel_connector); + seq_printf(m, "Force_DSC_Fractional_BPP_Enable: %s\n", + str_yes_no(intel_dp->force_dsc_fractional_bpp_en)); + +out: + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + return ret; +} + +static ssize_t i915_dsc_fractional_bpp_write(struct file *file, + const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct drm_connector *connector = + ((struct seq_file *)file->private_data)->private; + struct intel_encoder *encoder = intel_attached_encoder(to_intel_connector(connector)); + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + bool dsc_fractional_bpp_enable = false; + int ret; + + if (len == 0) + return 0; + + drm_dbg(&i915->drm, + "Copied %zu bytes from user to force fractional bpp for DSC\n", len); + + ret = kstrtobool_from_user(ubuf, len, &dsc_fractional_bpp_enable); + if (ret < 0) + return ret; + + drm_dbg(&i915->drm, "Got %s for DSC Fractional BPP Enable\n", + (dsc_fractional_bpp_enable) ? "true" : "false"); + intel_dp->force_dsc_fractional_bpp_en = dsc_fractional_bpp_enable; + + *offp += len; + + return len; +} + +static int i915_dsc_fractional_bpp_open(struct inode *inode, + struct file *file) +{ + return single_open(file, i915_dsc_fractional_bpp_show, inode->i_private); +} + +static const struct file_operations i915_dsc_fractional_bpp_fops = { + .owner = THIS_MODULE, + .open = i915_dsc_fractional_bpp_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = i915_dsc_fractional_bpp_write +}; + /* * Returns the Current CRTC's bpc. * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/i915_current_bpc @@ -1511,6 +1606,9 @@ void intel_connector_debugfs_add(struct intel_connector *intel_connector) debugfs_create_file("i915_dsc_output_format", 0644, root, connector, &i915_dsc_output_format_fops); + + debugfs_create_file("i915_dsc_fractional_bpp", 0644, root, + connector, &i915_dsc_fractional_bpp_fops); } if (connector->connector_type == DRM_MODE_CONNECTOR_DSI || diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs_params.c b/drivers/gpu/drm/i915/display/intel_display_debugfs_params.c new file mode 100644 index 000000000000..b7e68eb62452 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs_params.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include <linux/kernel.h> + +#include <drm/drm_drv.h> + +#include "intel_display_debugfs_params.h" +#include "i915_drv.h" +#include "intel_display_params.h" + +/* int param */ +static int intel_display_param_int_show(struct seq_file *m, void *data) +{ + int *value = m->private; + + seq_printf(m, "%d\n", *value); + + return 0; +} + +static int intel_display_param_int_open(struct inode *inode, struct file *file) +{ + return single_open(file, intel_display_param_int_show, inode->i_private); +} + +static ssize_t intel_display_param_int_write(struct file *file, + const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + int *value = m->private; + int ret; + + ret = kstrtoint_from_user(ubuf, len, 0, value); + if (ret) { + /* support boolean values too */ + bool b; + + ret = kstrtobool_from_user(ubuf, len, &b); + if (!ret) + *value = b; + } + + return ret ?: len; +} + +static const struct file_operations intel_display_param_int_fops = { + .owner = THIS_MODULE, + .open = intel_display_param_int_open, + .read = seq_read, + .write = intel_display_param_int_write, + .llseek = default_llseek, + .release = single_release, +}; + +static const struct file_operations intel_display_param_int_fops_ro = { + .owner = THIS_MODULE, + .open = intel_display_param_int_open, + .read = seq_read, + .llseek = default_llseek, + .release = single_release, +}; + +/* unsigned int param */ +static int intel_display_param_uint_show(struct seq_file *m, void *data) +{ + unsigned int *value = m->private; + + seq_printf(m, "%u\n", *value); + + return 0; +} + +static int intel_display_param_uint_open(struct inode *inode, struct file *file) +{ + return single_open(file, intel_display_param_uint_show, inode->i_private); +} + +static ssize_t intel_display_param_uint_write(struct file *file, + const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + unsigned int *value = m->private; + int ret; + + ret = kstrtouint_from_user(ubuf, len, 0, value); + if (ret) { + /* support boolean values too */ + bool b; + + ret = kstrtobool_from_user(ubuf, len, &b); + if (!ret) + *value = b; + } + + return ret ?: len; +} + +static const struct file_operations intel_display_param_uint_fops = { + .owner = THIS_MODULE, + .open = intel_display_param_uint_open, + .read = seq_read, + .write = intel_display_param_uint_write, + .llseek = default_llseek, + .release = single_release, +}; + +static const struct file_operations intel_display_param_uint_fops_ro = { + .owner = THIS_MODULE, + .open = intel_display_param_uint_open, + .read = seq_read, + .llseek = default_llseek, + .release = single_release, +}; + +#define RO(mode) (((mode) & 0222) == 0) + +__maybe_unused static struct dentry * +intel_display_debugfs_create_int(const char *name, umode_t mode, + struct dentry *parent, int *value) +{ + return debugfs_create_file_unsafe(name, mode, parent, value, + RO(mode) ? &intel_display_param_int_fops_ro : + &intel_display_param_int_fops); +} + +__maybe_unused static struct dentry * +intel_display_debugfs_create_uint(const char *name, umode_t mode, + struct dentry *parent, unsigned int *value) +{ + return debugfs_create_file_unsafe(name, mode, parent, value, + RO(mode) ? &intel_display_param_uint_fops_ro : + &intel_display_param_uint_fops); +} + +#define _intel_display_param_create_file(parent, name, mode, valp) \ + do { \ + if (mode) \ + _Generic(valp, \ + bool * : debugfs_create_bool, \ + int * : intel_display_debugfs_create_int, \ + unsigned int * : intel_display_debugfs_create_uint, \ + unsigned long * : debugfs_create_ulong, \ + char ** : debugfs_create_str) \ + (name, mode, parent, valp); \ + } while (0) + +/* add a subdirectory with files for each intel display param */ +void intel_display_debugfs_params(struct drm_i915_private *i915) +{ + struct drm_minor *minor = i915->drm.primary; + struct dentry *dir; + char dirname[16]; + + snprintf(dirname, sizeof(dirname), "%s_params", i915->drm.driver->name); + dir = debugfs_lookup(dirname, minor->debugfs_root); + if (!dir) + dir = debugfs_create_dir(dirname, minor->debugfs_root); + if (IS_ERR(dir)) + return; + + /* + * Note: We could create files for params needing special handling + * here. Set mode in params to 0 to skip the generic create file, or + * just let the generic create file fail silently with -EEXIST. + */ + +#define REGISTER(T, x, unused, mode, ...) _intel_display_param_create_file( \ + dir, #x, mode, &i915->display.params.x); + INTEL_DISPLAY_PARAMS_FOR_EACH(REGISTER); +#undef REGISTER +} diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs_params.h b/drivers/gpu/drm/i915/display/intel_display_debugfs_params.h new file mode 100644 index 000000000000..1e9945a4044c --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs_params.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __INTEL_DISPLAY_DEBUGFS_PARAMS__ +#define __INTEL_DISPLAY_DEBUGFS_PARAMS__ + +struct drm_i915_private; + +void intel_display_debugfs_params(struct drm_i915_private *i915); + +#endif /* __INTEL_DISPLAY_DEBUGFS_PARAMS__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 2b1ec23ba9c3..0b522c6a8d6f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -12,6 +12,7 @@ #include "intel_de.h" #include "intel_display.h" #include "intel_display_device.h" +#include "intel_display_params.h" #include "intel_display_power.h" #include "intel_display_reg_defs.h" #include "intel_fbc.h" @@ -937,6 +938,13 @@ void intel_display_device_probe(struct drm_i915_private *i915) DISPLAY_RUNTIME_INFO(i915)->ip.rel = rel; DISPLAY_RUNTIME_INFO(i915)->ip.step = step; } + + intel_display_params_copy(&i915->display.params); +} + +void intel_display_device_remove(struct drm_i915_private *i915) +{ + intel_display_params_free(&i915->display.params); } static void __intel_display_device_info_runtime_init(struct drm_i915_private *i915) @@ -1105,7 +1113,7 @@ void intel_display_device_info_runtime_init(struct drm_i915_private *i915) } /* Disable nuclear pageflip by default on pre-g4x */ - if (!i915->params.nuclear_pageflip && + if (!i915->display.params.nuclear_pageflip && DISPLAY_VER(i915) < 5 && !IS_G4X(i915)) i915->drm.driver_features &= ~DRIVER_ATOMIC; } @@ -1145,5 +1153,6 @@ bool intel_display_device_enabled(struct drm_i915_private *i915) /* Only valid when HAS_DISPLAY() is true */ drm_WARN_ON(&i915->drm, !HAS_DISPLAY(i915)); - return !i915->params.disable_display && !intel_opregion_headless_sku(i915); + return !i915->display.params.disable_display && + !intel_opregion_headless_sku(i915); } diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index 5b5c0e53307f..4299cc452e05 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -161,6 +161,7 @@ struct intel_display_device_info { bool intel_display_device_enabled(struct drm_i915_private *i915); void intel_display_device_probe(struct drm_i915_private *i915); +void intel_display_device_remove(struct drm_i915_private *i915); void intel_display_device_info_runtime_init(struct drm_i915_private *i915); void intel_display_device_info_print(const struct intel_display_device_info *info, diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 44b59ac301e6..62f7b10484be 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -181,6 +181,13 @@ void intel_display_driver_early_probe(struct drm_i915_private *i915) if (!HAS_DISPLAY(i915)) return; + spin_lock_init(&i915->display.fb_tracking.lock); + mutex_init(&i915->display.backlight.lock); + mutex_init(&i915->display.audio.mutex); + mutex_init(&i915->display.wm.wm_mutex); + mutex_init(&i915->display.pps.mutex); + mutex_init(&i915->display.hdcp.hdcp_mutex); + intel_display_irq_init(i915); intel_dkl_phy_init(i915); intel_color_init_hooks(i915); diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c new file mode 100644 index 000000000000..11e03cfb774d --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_params.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include "intel_display_params.h" +#include "i915_drv.h" + +#define intel_display_param_named(name, T, perm, desc) \ + module_param_named(name, intel_display_modparams.name, T, perm); \ + MODULE_PARM_DESC(name, desc) +#define intel_display_param_named_unsafe(name, T, perm, desc) \ + module_param_named_unsafe(name, intel_display_modparams.name, T, perm); \ + MODULE_PARM_DESC(name, desc) + +static struct intel_display_params intel_display_modparams __read_mostly = { +#define MEMBER(T, member, value, ...) .member = (value), + INTEL_DISPLAY_PARAMS_FOR_EACH(MEMBER) +#undef MEMBER +}; +/* + * Note: As a rule, keep module parameter sysfs permissions read-only + * 0400. Runtime changes are only supported through i915 debugfs. + * + * For any exceptions requiring write access and runtime changes through module + * parameter sysfs, prevent debugfs file creation by setting the parameter's + * debugfs mode to 0. + */ + +intel_display_param_named_unsafe(vbt_firmware, charp, 0400, + "Load VBT from specified file under /lib/firmware"); + +intel_display_param_named_unsafe(lvds_channel_mode, int, 0400, + "Specify LVDS channel mode " + "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)"); + +intel_display_param_named_unsafe(panel_use_ssc, int, 0400, + "Use Spread Spectrum Clock with panels [LVDS/eDP] " + "(default: auto from VBT)"); + +intel_display_param_named_unsafe(vbt_sdvo_panel_type, int, 0400, + "Override/Ignore selection of SDVO panel mode in the VBT " + "(-2=ignore, -1=auto [default], index in VBT BIOS table)"); + +intel_display_param_named_unsafe(enable_dc, int, 0400, + "Enable power-saving display C-states. " + "(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6; " + "3=up to DC5 with DC3CO; 4=up to DC6 with DC3CO)"); + +intel_display_param_named_unsafe(enable_dpt, bool, 0400, + "Enable display page table (DPT) (default: true)"); + +intel_display_param_named_unsafe(enable_sagv, bool, 0400, + "Enable system agent voltage/frequency scaling (SAGV) (default: true)"); + +intel_display_param_named_unsafe(disable_power_well, int, 0400, + "Disable display power wells when possible " + "(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)"); + +intel_display_param_named_unsafe(enable_ips, bool, 0400, "Enable IPS (default: true)"); + +intel_display_param_named_unsafe(invert_brightness, int, 0400, + "Invert backlight brightness " + "(-1 force normal, 0 machine defaults, 1 force inversion), please " + "report PCI device ID, subsystem vendor and subsystem device ID " + "to dri-devel@lists.freedesktop.org, if your machine needs it. " + "It will then be included in an upcoming module version."); + +/* WA to get away with the default setting in VBT for early platforms.Will be removed */ +intel_display_param_named_unsafe(edp_vswing, int, 0400, + "Ignore/Override vswing pre-emph table selection from VBT " + "(0=use value from vbt [default], 1=low power swing(200mV)," + "2=default swing(400mV))"); + +intel_display_param_named(enable_dpcd_backlight, int, 0400, + "Enable support for DPCD backlight control" + "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enable, 2=force VESA interface, 3=force Intel interface)"); + +intel_display_param_named_unsafe(load_detect_test, bool, 0400, + "Force-enable the VGA load detect code for testing (default:false). " + "For developers only."); + +intel_display_param_named_unsafe(force_reset_modeset_test, bool, 0400, + "Force a modeset during gpu reset for testing (default:false). " + "For developers only."); + +intel_display_param_named(disable_display, bool, 0400, + "Disable display (default: false)"); + +intel_display_param_named(verbose_state_checks, bool, 0400, + "Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions."); + +intel_display_param_named_unsafe(nuclear_pageflip, bool, 0400, + "Force enable atomic functionality on platforms that don't have full support yet."); + +intel_display_param_named_unsafe(enable_dp_mst, bool, 0400, + "Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)"); + +intel_display_param_named_unsafe(enable_fbc, int, 0400, + "Enable frame buffer compression for power savings " + "(default: -1 (use per-chip default))"); + +intel_display_param_named_unsafe(enable_psr, int, 0400, + "Enable PSR " + "(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " + "Default: -1 (use per-chip default)"); + +intel_display_param_named(psr_safest_params, bool, 0400, + "Replace PSR VBT parameters by the safest and not optimal ones. This " + "is helpful to detect if PSR issues are related to bad values set in " + " VBT. (0=use VBT parameters, 1=use safest parameters)" + "Default: 0"); + +intel_display_param_named_unsafe(enable_psr2_sel_fetch, bool, 0400, + "Enable PSR2 selective fetch " + "(0=disabled, 1=enabled) " + "Default: 1"); + +__maybe_unused +static void _param_print_bool(struct drm_printer *p, const char *driver_name, + const char *name, bool val) +{ + drm_printf(p, "%s.%s=%s\n", driver_name, name, str_yes_no(val)); +} + +__maybe_unused +static void _param_print_int(struct drm_printer *p, const char *driver_name, + const char *name, int val) +{ + drm_printf(p, "%s.%s=%d\n", driver_name, name, val); +} + +__maybe_unused +static void _param_print_uint(struct drm_printer *p, const char *driver_name, + const char *name, unsigned int val) +{ + drm_printf(p, "%s.%s=%u\n", driver_name, name, val); +} + +__maybe_unused +static void _param_print_ulong(struct drm_printer *p, const char *driver_name, + const char *name, unsigned long val) +{ + drm_printf(p, "%s.%s=%lu\n", driver_name, name, val); +} + +__maybe_unused +static void _param_print_charp(struct drm_printer *p, const char *driver_name, + const char *name, const char *val) +{ + drm_printf(p, "%s.%s=%s\n", driver_name, name, val); +} + +#define _param_print(p, driver_name, name, val) \ + _Generic(val, \ + bool : _param_print_bool, \ + int : _param_print_int, \ + unsigned int : _param_print_uint, \ + unsigned long : _param_print_ulong, \ + char * : _param_print_charp)(p, driver_name, name, val) + +/** + * intel_display_params_dump - dump intel display modparams + * @i915: i915 device + * @p: the &drm_printer + * + * Pretty printer for i915 modparams. + */ +void intel_display_params_dump(struct drm_i915_private *i915, struct drm_printer *p) +{ +#define PRINT(T, x, ...) _param_print(p, i915->drm.driver->name, #x, i915->display.params.x); + INTEL_DISPLAY_PARAMS_FOR_EACH(PRINT); +#undef PRINT +} + +__maybe_unused static void _param_dup_charp(char **valp) +{ + *valp = kstrdup(*valp ? *valp : "", GFP_ATOMIC); +} + +__maybe_unused static void _param_nop(void *valp) +{ +} + +#define _param_dup(valp) \ + _Generic(valp, \ + char ** : _param_dup_charp, \ + default : _param_nop) \ + (valp) + +void intel_display_params_copy(struct intel_display_params *dest) +{ + *dest = intel_display_modparams; +#define DUP(T, x, ...) _param_dup(&dest->x); + INTEL_DISPLAY_PARAMS_FOR_EACH(DUP); +#undef DUP +} + +__maybe_unused static void _param_free_charp(char **valp) +{ + kfree(*valp); + *valp = NULL; +} + +#define _param_free(valp) \ + _Generic(valp, \ + char ** : _param_free_charp, \ + default : _param_nop) \ + (valp) + +/* free the allocated members, *not* the passed in params itself */ +void intel_display_params_free(struct intel_display_params *params) +{ +#define FREE(T, x, ...) _param_free(¶ms->x); + INTEL_DISPLAY_PARAMS_FOR_EACH(FREE); +#undef FREE +} diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h new file mode 100644 index 000000000000..6206cc51df04 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_display_params.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef _INTEL_DISPLAY_PARAMS_H_ +#define _INTEL_DISPLAY_PARAMS_H_ + +#include <linux/types.h> + +struct drm_printer; +struct drm_i915_private; + +/* + * Invoke param, a function-like macro, for each intel display param, with + * arguments: + * + * param(type, name, value, mode) + * + * type: parameter type, one of {bool, int, unsigned int, unsigned long, char *} + * name: name of the parameter + * value: initial/default value of the parameter + * mode: debugfs file permissions, one of {0400, 0600, 0}, use 0 to not create + * debugfs file + */ +#define INTEL_DISPLAY_PARAMS_FOR_EACH(param) \ + param(char *, vbt_firmware, NULL, 0400) \ + param(int, lvds_channel_mode, 0, 0400) \ + param(int, panel_use_ssc, -1, 0600) \ + param(int, vbt_sdvo_panel_type, -1, 0400) \ + param(int, enable_dc, -1, 0400) \ + param(bool, enable_dpt, true, 0400) \ + param(bool, enable_sagv, true, 0600) \ + param(int, disable_power_well, -1, 0400) \ + param(bool, enable_ips, true, 0600) \ + param(int, invert_brightness, 0, 0600) \ + param(int, edp_vswing, 0, 0400) \ + param(int, enable_dpcd_backlight, -1, 0600) \ + param(bool, load_detect_test, false, 0600) \ + param(bool, force_reset_modeset_test, false, 0600) \ + param(bool, disable_display, false, 0400) \ + param(bool, verbose_state_checks, true, 0400) \ + param(bool, nuclear_pageflip, false, 0400) \ + param(bool, enable_dp_mst, true, 0600) \ + param(int, enable_fbc, -1, 0600) \ + param(int, enable_psr, -1, 0600) \ + param(bool, psr_safest_params, false, 0400) \ + param(bool, enable_psr2_sel_fetch, true, 0400) \ + +#define MEMBER(T, member, ...) T member; +struct intel_display_params { + INTEL_DISPLAY_PARAMS_FOR_EACH(MEMBER); +}; +#undef MEMBER + +void intel_display_params_dump(struct drm_i915_private *i915, + struct drm_printer *p); +void intel_display_params_copy(struct intel_display_params *dest); +void intel_display_params_free(struct intel_display_params *params); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index e25785ae1c20..e390595d7341 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -967,7 +967,7 @@ static u32 get_allowed_dc_mask(const struct drm_i915_private *dev_priv, DISPLAY_VER(dev_priv) >= 11 ? DC_STATE_EN_DC9 : 0; - if (!dev_priv->params.disable_power_well) + if (!dev_priv->display.params.disable_power_well) max_dc = 0; if (enable_dc >= 0 && enable_dc <= max_dc) { @@ -1016,11 +1016,11 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) { struct i915_power_domains *power_domains = &dev_priv->display.power.domains; - dev_priv->params.disable_power_well = + dev_priv->display.params.disable_power_well = sanitize_disable_power_well_option(dev_priv, - dev_priv->params.disable_power_well); + dev_priv->display.params.disable_power_well); power_domains->allowed_dc_mask = - get_allowed_dc_mask(dev_priv, dev_priv->params.enable_dc); + get_allowed_dc_mask(dev_priv, dev_priv->display.params.enable_dc); power_domains->target_dc_state = sanitize_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); @@ -1950,7 +1950,7 @@ void intel_power_domains_init_hw(struct drm_i915_private *i915, bool resume) intel_display_power_get(i915, POWER_DOMAIN_INIT); /* Disable power support if the user asked so. */ - if (!i915->params.disable_power_well) { + if (!i915->display.params.disable_power_well) { drm_WARN_ON(&i915->drm, power_domains->disable_wakeref); i915->display.power.domains.disable_wakeref = intel_display_power_get(i915, POWER_DOMAIN_INIT); @@ -1977,7 +1977,7 @@ void intel_power_domains_driver_remove(struct drm_i915_private *i915) fetch_and_zero(&i915->display.power.domains.init_wakeref); /* Remove the refcount we took to keep power well support disabled. */ - if (!i915->params.disable_power_well) + if (!i915->display.params.disable_power_well) intel_display_power_put(i915, POWER_DOMAIN_INIT, fetch_and_zero(&i915->display.power.domains.disable_wakeref)); @@ -2096,7 +2096,7 @@ void intel_power_domains_suspend(struct drm_i915_private *i915, bool s2idle) * Even if power well support was disabled we still want to disable * power wells if power domains must be deinitialized for suspend. */ - if (!i915->params.disable_power_well) + if (!i915->display.params.disable_power_well) intel_display_power_put(i915, POWER_DOMAIN_INIT, fetch_and_zero(&i915->display.power.domains.disable_wakeref)); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 07d650050099..47cd6bb04366 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -1400,20 +1400,16 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, { enum i915_power_well_id id = i915_power_well_instance(power_well)->id; enum dpio_phy phy; - enum pipe pipe; u32 tmp; drm_WARN_ON_ONCE(&dev_priv->drm, id != VLV_DISP_PW_DPIO_CMN_BC && id != CHV_DISP_PW_DPIO_CMN_D); - if (id == VLV_DISP_PW_DPIO_CMN_BC) { - pipe = PIPE_A; + if (id == VLV_DISP_PW_DPIO_CMN_BC) phy = DPIO_PHY0; - } else { - pipe = PIPE_C; + else phy = DPIO_PHY1; - } /* since ref/cri clock was enabled */ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */ @@ -1428,24 +1424,24 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv, vlv_dpio_get(dev_priv); /* Enable dynamic power down */ - tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); + tmp = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW28); tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN | DPIO_SUS_CLK_CONFIG_GATE_CLKREQ; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW28, tmp); if (id == VLV_DISP_PW_DPIO_CMN_BC) { - tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); + tmp = vlv_dpio_read(dev_priv, phy, _CHV_CMN_DW6_CH1); tmp |= DPIO_DYNPWRDOWNEN_CH1; - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); + vlv_dpio_write(dev_priv, phy, _CHV_CMN_DW6_CH1, tmp); } else { /* * Force the non-existing CL2 off. BXT does this * too, so maybe it saves some power even though * CL2 doesn't exist? */ - tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); + tmp = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW30); tmp |= DPIO_CL2_LDOFUSE_PWRENB; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, tmp); + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW30, tmp); } vlv_dpio_put(dev_priv); @@ -1499,7 +1495,6 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, enum dpio_channel ch, bool override, unsigned int mask) { - enum pipe pipe = phy == DPIO_PHY0 ? PIPE_A : PIPE_C; u32 reg, val, expected, actual; /* @@ -1518,7 +1513,7 @@ static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpi reg = _CHV_CMN_DW6_CH1; vlv_dpio_get(dev_priv); - val = vlv_dpio_read(dev_priv, pipe, reg); + val = vlv_dpio_read(dev_priv, phy, reg); vlv_dpio_put(dev_priv); /* diff --git a/drivers/gpu/drm/i915/display/intel_display_reset.c b/drivers/gpu/drm/i915/display/intel_display_reset.c index 17178d5d7788..c2c347b22448 100644 --- a/drivers/gpu/drm/i915/display/intel_display_reset.c +++ b/drivers/gpu/drm/i915/display/intel_display_reset.c @@ -29,7 +29,7 @@ void intel_display_reset_prepare(struct drm_i915_private *dev_priv) return; /* reset doesn't touch the display */ - if (!dev_priv->params.force_reset_modeset_test && + if (!dev_priv->display.params.force_reset_modeset_test && !gpu_reset_clobbers_display(dev_priv)) return; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 65ea37fe8cff..b3e942f2eeb0 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -198,6 +198,12 @@ struct intel_encoder { struct intel_encoder *, const struct intel_crtc_state *, const struct drm_connector_state *); + void (*audio_enable)(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state); + void (*audio_disable)(struct intel_encoder *encoder, + const struct intel_crtc_state *old_crtc_state, + const struct drm_connector_state *old_conn_state); /* Read out the current hw state of this connector, returning true if * the encoder is active. If the encoder is enabled it also set the pipe * it is connected to in the pipe parameter. */ @@ -624,6 +630,9 @@ struct intel_connector { struct drm_dp_aux *dsc_decompression_aux; u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]; u8 fec_capability; + + u8 dsc_hblank_expansion_quirk:1; + u8 dsc_decompression_enabled:1; } dp; /* Work struct to schedule a uevent on link train failure */ @@ -676,8 +685,6 @@ struct intel_atomic_state { bool rps_interactive; - struct i915_sw_fence commit_ready; - struct llist_node freed; }; @@ -1210,6 +1217,7 @@ struct intel_crtc_state { bool has_psr2; bool enable_psr2_sel_fetch; bool req_psr2_sdp_prior_scanline; + bool has_panel_replay; bool wm_level_disabled; u32 dc3co_exitline; u16 su_y_granularity; @@ -1361,7 +1369,8 @@ struct intel_crtc_state { struct { bool compression_enable; bool dsc_split; - u16 compressed_bpp; + /* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */ + u16 compressed_bpp_x16; u8 slice_count; struct drm_dsc_config config; } dsc; @@ -1707,9 +1716,13 @@ struct intel_psr { bool irq_aux_error; u16 su_w_granularity; u16 su_y_granularity; + bool source_panel_replay_support; + bool sink_panel_replay_support; + bool panel_replay_enabled; u32 dc3co_exitline; u32 dc3co_exit_delay; struct delayed_work dc3co_work; + u8 entry_setup_frames; }; struct intel_dp { @@ -1808,6 +1821,7 @@ struct intel_dp { /* Display stream compression testing */ bool force_dsc_en; int force_dsc_output_format; + bool force_dsc_fractional_bpp_en; int force_dsc_bpc; bool hobl_failed; @@ -1992,17 +2006,6 @@ dp_to_lspcon(struct intel_dp *intel_dp) #define dp_to_i915(__intel_dp) to_i915(dp_to_dig_port(__intel_dp)->base.base.dev) -#define CAN_PSR(intel_dp) ((intel_dp)->psr.sink_support && \ - (intel_dp)->psr.source_support) - -static inline bool intel_encoder_can_psr(struct intel_encoder *encoder) -{ - if (!intel_encoder_is_dp(encoder)) - return false; - - return CAN_PSR(enc_to_intel_dp(encoder)); -} - static inline struct intel_digital_port * hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) { diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 2c1034578984..1422c2370269 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -85,8 +85,8 @@ #define DP_DSC_MAX_ENC_THROUGHPUT_0 340000 #define DP_DSC_MAX_ENC_THROUGHPUT_1 400000 -/* DP DSC FEC Overhead factor = 1/(0.972261) */ -#define DP_DSC_FEC_OVERHEAD_FACTOR 972261 +/* DP DSC FEC Overhead factor in ppm = 1/(0.972261) = 1.028530 */ +#define DP_DSC_FEC_OVERHEAD_FACTOR 1028530 /* Compliance test status bits */ #define INTEL_DP_RESOLUTION_SHIFT_MASK 0 @@ -124,7 +124,31 @@ static void intel_dp_unset_edid(struct intel_dp *intel_dp); /* Is link rate UHBR and thus 128b/132b? */ bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state) { - return crtc_state->port_clock >= 1000000; + return drm_dp_is_uhbr_rate(crtc_state->port_clock); +} + +/** + * intel_dp_link_symbol_size - get the link symbol size for a given link rate + * @rate: link rate in 10kbit/s units + * + * Returns the link symbol size in bits/symbol units depending on the link + * rate -> channel coding. + */ +int intel_dp_link_symbol_size(int rate) +{ + return drm_dp_is_uhbr_rate(rate) ? 32 : 10; +} + +/** + * intel_dp_link_symbol_clock - convert link rate to link symbol clock + * @rate: link rate in 10kbit/s units + * + * Returns the link symbol clock frequency in kHz units depending on the + * link rate and channel coding. + */ +int intel_dp_link_symbol_clock(int rate) +{ + return DIV_ROUND_CLOSEST(rate * 10, intel_dp_link_symbol_size(rate)); } static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp) @@ -331,6 +355,9 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp) /* * The required data bandwidth for a mode with given pixel clock and bpp. This * is the required net bandwidth independent of the data bandwidth efficiency. + * + * TODO: check if callers of this functions should use + * intel_dp_effective_data_rate() instead. */ int intel_dp_link_required(int pixel_clock, int bpp) @@ -339,6 +366,22 @@ intel_dp_link_required(int pixel_clock, int bpp) return DIV_ROUND_UP(pixel_clock * bpp, 8); } +/** + * intel_dp_effective_data_rate - Return the pixel data rate accounting for BW allocation overhead + * @pixel_clock: pixel clock in kHz + * @bpp_x16: bits per pixel .4 fixed point format + * @bw_overhead: BW allocation overhead in 1ppm units + * + * Return the effective pixel data rate in kB/sec units taking into account + * the provided SSC, FEC, DSC BW allocation overhead. + */ +int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, + int bw_overhead) +{ + return DIV_ROUND_UP_ULL(mul_u32_u32(pixel_clock * bpp_x16, bw_overhead), + 1000000 * 16 * 8); +} + /* * Given a link rate and lanes, get the data bandwidth. * @@ -362,29 +405,27 @@ intel_dp_link_required(int pixel_clock, int bpp) int intel_dp_max_data_rate(int max_link_rate, int max_lanes) { - if (max_link_rate >= 1000000) { - /* - * UHBR rates always use 128b/132b channel encoding, and have - * 97.71% data bandwidth efficiency. Consider max_link_rate the - * link bit rate in units of 10000 bps. - */ - int max_link_rate_kbps = max_link_rate * 10; - - max_link_rate_kbps = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(max_link_rate_kbps, 9671), 10000); - max_link_rate = max_link_rate_kbps / 8; - } + int ch_coding_efficiency = + drm_dp_bw_channel_coding_efficiency(drm_dp_is_uhbr_rate(max_link_rate)); + int max_link_rate_kbps = max_link_rate * 10; /* + * UHBR rates always use 128b/132b channel encoding, and have + * 97.71% data bandwidth efficiency. Consider max_link_rate the + * link bit rate in units of 10000 bps. + */ + /* * Lower than UHBR rates always use 8b/10b channel encoding, and have * 80% data bandwidth efficiency for SST non-FEC. However, this turns - * out to be a nop by coincidence, and can be skipped: + * out to be a nop by coincidence: * * int max_link_rate_kbps = max_link_rate * 10; - * max_link_rate_kbps = DIV_ROUND_CLOSEST_ULL(max_link_rate_kbps * 8, 10); + * max_link_rate_kbps = DIV_ROUND_DOWN_ULL(max_link_rate_kbps * 8, 10); * max_link_rate = max_link_rate_kbps / 8; */ - - return max_link_rate * max_lanes; + return DIV_ROUND_DOWN_ULL(mul_u32_u32(max_link_rate_kbps * max_lanes, + ch_coding_efficiency), + 1000000 * 8); } bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp) @@ -680,8 +721,22 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, u32 intel_dp_mode_to_fec_clock(u32 mode_clock) { - return div_u64(mul_u32_u32(mode_clock, 1000000U), - DP_DSC_FEC_OVERHEAD_FACTOR); + return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR), + 1000000U); +} + +int intel_dp_bw_fec_overhead(bool fec_enabled) +{ + /* + * TODO: Calculate the actual overhead for a given mode. + * The hard-coded 1/0.972261=2.853% overhead factor + * corresponds (for instance) to the 8b/10b DP FEC 2.4% + + * 0.453% DSC overhead. This is enough for a 3840 width mode, + * which has a DSC overhead of up to ~0.2%, but may not be + * enough for a 1024 width mode where this is ~0.8% (on a 4 + * lane DP link, with 2 DSC slices and 8 bpp color depth). + */ + return fec_enabled ? DP_DSC_FEC_OVERHEAD_FACTOR : 1000000; } static int @@ -1369,9 +1424,9 @@ static bool intel_dp_source_supports_fec(struct intel_dp *intel_dp, return false; } -static bool intel_dp_supports_fec(struct intel_dp *intel_dp, - const struct intel_connector *connector, - const struct intel_crtc_state *pipe_config) +bool intel_dp_supports_fec(struct intel_dp *intel_dp, + const struct intel_connector *connector, + const struct intel_crtc_state *pipe_config) { return intel_dp_source_supports_fec(intel_dp, pipe_config) && drm_dp_sink_supports_fec(connector->dp.fec_capability); @@ -1384,6 +1439,7 @@ static bool intel_dp_supports_dsc(const struct intel_connector *connector, return false; return intel_dsc_source_support(crtc_state) && + connector->dp.dsc_decompression_aux && drm_dp_sink_supports_dsc(connector->dp.dsc_dpcd); } @@ -1717,15 +1773,15 @@ static bool intel_dp_dsc_supports_format(const struct intel_connector *connector return drm_dp_dsc_sink_supports_format(connector->dp.dsc_dpcd, sink_dsc_format); } -static bool is_bw_sufficient_for_dsc_config(u16 compressed_bpp, u32 link_clock, +static bool is_bw_sufficient_for_dsc_config(u16 compressed_bppx16, u32 link_clock, u32 lane_count, u32 mode_clock, enum intel_output_format output_format, int timeslots) { u32 available_bw, required_bw; - available_bw = (link_clock * lane_count * timeslots) / 8; - required_bw = compressed_bpp * (intel_dp_mode_to_fec_clock(mode_clock)); + available_bw = (link_clock * lane_count * timeslots * 16) / 8; + required_bw = compressed_bppx16 * (intel_dp_mode_to_fec_clock(mode_clock)); return available_bw > required_bw; } @@ -1733,7 +1789,7 @@ static bool is_bw_sufficient_for_dsc_config(u16 compressed_bpp, u32 link_clock, static int dsc_compute_link_config(struct intel_dp *intel_dp, struct intel_crtc_state *pipe_config, struct link_config_limits *limits, - u16 compressed_bpp, + u16 compressed_bppx16, int timeslots) { const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; @@ -1748,8 +1804,8 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, for (lane_count = limits->min_lane_count; lane_count <= limits->max_lane_count; lane_count <<= 1) { - if (!is_bw_sufficient_for_dsc_config(compressed_bpp, link_rate, lane_count, - adjusted_mode->clock, + if (!is_bw_sufficient_for_dsc_config(compressed_bppx16, link_rate, + lane_count, adjusted_mode->clock, pipe_config->output_format, timeslots)) continue; @@ -1791,7 +1847,7 @@ u16 intel_dp_dsc_max_sink_compressed_bppx16(const struct intel_connector *connec return 0; } -static int dsc_sink_min_compressed_bpp(struct intel_crtc_state *pipe_config) +int intel_dp_dsc_sink_min_compressed_bpp(struct intel_crtc_state *pipe_config) { /* From Mandatory bit rate range Support Table 2-157 (DP v2.0) */ switch (pipe_config->output_format) { @@ -1808,9 +1864,9 @@ static int dsc_sink_min_compressed_bpp(struct intel_crtc_state *pipe_config) return 0; } -static int dsc_sink_max_compressed_bpp(const struct intel_connector *connector, - struct intel_crtc_state *pipe_config, - int bpc) +int intel_dp_dsc_sink_max_compressed_bpp(const struct intel_connector *connector, + struct intel_crtc_state *pipe_config, + int bpc) { return intel_dp_dsc_max_sink_compressed_bppx16(connector, pipe_config, bpc) >> 4; @@ -1862,10 +1918,11 @@ icl_dsc_compute_link_config(struct intel_dp *intel_dp, ret = dsc_compute_link_config(intel_dp, pipe_config, limits, - valid_dsc_bpp[i], + valid_dsc_bpp[i] << 4, timeslots); if (ret == 0) { - pipe_config->dsc.compressed_bpp = valid_dsc_bpp[i]; + pipe_config->dsc.compressed_bpp_x16 = + to_bpp_x16(valid_dsc_bpp[i]); return 0; } } @@ -1881,6 +1938,7 @@ icl_dsc_compute_link_config(struct intel_dp *intel_dp, */ static int xelpd_dsc_compute_link_config(struct intel_dp *intel_dp, + const struct intel_connector *connector, struct intel_crtc_state *pipe_config, struct link_config_limits *limits, int dsc_max_bpp, @@ -1888,22 +1946,38 @@ xelpd_dsc_compute_link_config(struct intel_dp *intel_dp, int pipe_bpp, int timeslots) { - u16 compressed_bpp; + u8 bppx16_incr = drm_dp_dsc_sink_bpp_incr(connector->dp.dsc_dpcd); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + u16 compressed_bppx16; + u8 bppx16_step; int ret; - /* Compressed BPP should be less than the Input DSC bpp */ - dsc_max_bpp = min(dsc_max_bpp, pipe_bpp - 1); + if (DISPLAY_VER(i915) < 14 || bppx16_incr <= 1) + bppx16_step = 16; + else + bppx16_step = 16 / bppx16_incr; - for (compressed_bpp = dsc_max_bpp; - compressed_bpp >= dsc_min_bpp; - compressed_bpp--) { + /* Compressed BPP should be less than the Input DSC bpp */ + dsc_max_bpp = min(dsc_max_bpp << 4, (pipe_bpp << 4) - bppx16_step); + dsc_min_bpp = dsc_min_bpp << 4; + + for (compressed_bppx16 = dsc_max_bpp; + compressed_bppx16 >= dsc_min_bpp; + compressed_bppx16 -= bppx16_step) { + if (intel_dp->force_dsc_fractional_bpp_en && + !to_bpp_frac(compressed_bppx16)) + continue; ret = dsc_compute_link_config(intel_dp, pipe_config, limits, - compressed_bpp, + compressed_bppx16, timeslots); if (ret == 0) { - pipe_config->dsc.compressed_bpp = compressed_bpp; + pipe_config->dsc.compressed_bpp_x16 = compressed_bppx16; + if (intel_dp->force_dsc_fractional_bpp_en && + to_bpp_frac(compressed_bppx16)) + drm_dbg_kms(&i915->drm, "Forcing DSC fractional bpp\n"); + return 0; } } @@ -1924,12 +1998,14 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, int dsc_joiner_max_bpp; dsc_src_min_bpp = dsc_src_min_compressed_bpp(); - dsc_sink_min_bpp = dsc_sink_min_compressed_bpp(pipe_config); + dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(pipe_config); dsc_min_bpp = max(dsc_src_min_bpp, dsc_sink_min_bpp); dsc_min_bpp = max(dsc_min_bpp, to_bpp_int_roundup(limits->link.min_bpp_x16)); dsc_src_max_bpp = dsc_src_max_compressed_bpp(intel_dp); - dsc_sink_max_bpp = dsc_sink_max_compressed_bpp(connector, pipe_config, pipe_bpp / 3); + dsc_sink_max_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector, + pipe_config, + pipe_bpp / 3); dsc_max_bpp = dsc_sink_max_bpp ? min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp; dsc_joiner_max_bpp = get_max_compressed_bpp_with_joiner(i915, adjusted_mode->clock, @@ -1939,7 +2015,7 @@ static int dsc_compute_compressed_bpp(struct intel_dp *intel_dp, dsc_max_bpp = min(dsc_max_bpp, to_bpp_int(limits->link.max_bpp_x16)); if (DISPLAY_VER(i915) >= 13) - return xelpd_dsc_compute_link_config(intel_dp, pipe_config, limits, + return xelpd_dsc_compute_link_config(intel_dp, connector, pipe_config, limits, dsc_max_bpp, dsc_min_bpp, pipe_bpp, timeslots); return icl_dsc_compute_link_config(intel_dp, pipe_config, limits, dsc_max_bpp, dsc_min_bpp, pipe_bpp, timeslots); @@ -2084,19 +2160,22 @@ static int intel_edp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp, pipe_config->lane_count = limits->max_lane_count; dsc_src_min_bpp = dsc_src_min_compressed_bpp(); - dsc_sink_min_bpp = dsc_sink_min_compressed_bpp(pipe_config); + dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(pipe_config); dsc_min_bpp = max(dsc_src_min_bpp, dsc_sink_min_bpp); dsc_min_bpp = max(dsc_min_bpp, to_bpp_int_roundup(limits->link.min_bpp_x16)); dsc_src_max_bpp = dsc_src_max_compressed_bpp(intel_dp); - dsc_sink_max_bpp = dsc_sink_max_compressed_bpp(connector, pipe_config, pipe_bpp / 3); + dsc_sink_max_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector, + pipe_config, + pipe_bpp / 3); dsc_max_bpp = dsc_sink_max_bpp ? min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp; dsc_max_bpp = min(dsc_max_bpp, to_bpp_int(limits->link.max_bpp_x16)); /* Compressed BPP should be less than the Input DSC bpp */ dsc_max_bpp = min(dsc_max_bpp, pipe_bpp - 1); - pipe_config->dsc.compressed_bpp = max(dsc_min_bpp, dsc_max_bpp); + pipe_config->dsc.compressed_bpp_x16 = + to_bpp_x16(max(dsc_min_bpp, dsc_max_bpp)); pipe_config->pipe_bpp = pipe_bpp; @@ -2118,8 +2197,9 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, &pipe_config->hw.adjusted_mode; int ret; - pipe_config->fec_enable = !intel_dp_is_edp(intel_dp) && - intel_dp_supports_fec(intel_dp, connector, pipe_config); + pipe_config->fec_enable = pipe_config->fec_enable || + (!intel_dp_is_edp(intel_dp) && + intel_dp_supports_fec(intel_dp, connector, pipe_config)); if (!intel_dp_supports_dsc(connector, pipe_config)) return -EINVAL; @@ -2184,18 +2264,18 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, ret = intel_dp_dsc_compute_params(connector, pipe_config); if (ret < 0) { drm_dbg_kms(&dev_priv->drm, - "Cannot compute valid DSC parameters for Input Bpp = %d " - "Compressed BPP = %d\n", + "Cannot compute valid DSC parameters for Input Bpp = %d" + "Compressed BPP = " BPP_X16_FMT "\n", pipe_config->pipe_bpp, - pipe_config->dsc.compressed_bpp); + BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16)); return ret; } pipe_config->dsc.compression_enable = true; drm_dbg_kms(&dev_priv->drm, "DP DSC computed with Input Bpp = %d " - "Compressed Bpp = %d Slice Count = %d\n", + "Compressed Bpp = " BPP_X16_FMT " Slice Count = %d\n", pipe_config->pipe_bpp, - pipe_config->dsc.compressed_bpp, + BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16), pipe_config->dsc.slice_count); return 0; @@ -2307,6 +2387,8 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, { struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); + const struct intel_connector *connector = + to_intel_connector(conn_state->connector); const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); @@ -2315,6 +2397,10 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, bool dsc_needed; int ret = 0; + if (pipe_config->fec_enable && + !intel_dp_supports_fec(intel_dp, connector, pipe_config)) + return -EINVAL; + if (intel_dp_need_bigjoiner(intel_dp, adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_clock)) pipe_config->bigjoiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe); @@ -2362,15 +2448,15 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, if (pipe_config->dsc.compression_enable) { drm_dbg_kms(&i915->drm, - "DP lane count %d clock %d Input bpp %d Compressed bpp %d\n", + "DP lane count %d clock %d Input bpp %d Compressed bpp " BPP_X16_FMT "\n", pipe_config->lane_count, pipe_config->port_clock, pipe_config->pipe_bpp, - pipe_config->dsc.compressed_bpp); + BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16)); drm_dbg_kms(&i915->drm, "DP link rate required %i available %i\n", intel_dp_link_required(adjusted_mode->crtc_clock, - pipe_config->dsc.compressed_bpp), + to_bpp_int_roundup(pipe_config->dsc.compressed_bpp_x16)), intel_dp_max_data_rate(pipe_config->port_clock, pipe_config->lane_count)); } else { @@ -2439,12 +2525,22 @@ static void intel_dp_compute_vsc_colorimetry(const struct intel_crtc_state *crtc struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - /* - * Prepare VSC Header for SU as per DP 1.4 spec, Table 2-118 - * VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/ - * Colorimetry Format indication. - */ - vsc->revision = 0x5; + if (crtc_state->has_panel_replay) { + /* + * Prepare VSC Header for SU as per DP 2.0 spec, Table 2-223 + * VSC SDP supporting 3D stereo, Panel Replay, and Pixel + * Encoding/Colorimetry Format indication. + */ + vsc->revision = 0x7; + } else { + /* + * Prepare VSC Header for SU as per DP 1.4 spec, Table 2-118 + * VSC SDP supporting 3D stereo, PSR2, and Pixel Encoding/ + * Colorimetry Format indication. + */ + vsc->revision = 0x5; + } + vsc->length = 0x13; /* DP 1.4a spec, Table 2-120 */ @@ -2553,6 +2649,21 @@ void intel_dp_compute_psr_vsc_sdp(struct intel_dp *intel_dp, vsc->revision = 0x4; vsc->length = 0xe; } + } else if (crtc_state->has_panel_replay) { + if (intel_dp->psr.colorimetry_support && + intel_dp_needs_vsc_sdp(crtc_state, conn_state)) { + /* [Panel Replay with colorimetry info] */ + intel_dp_compute_vsc_colorimetry(crtc_state, conn_state, + vsc); + } else { + /* + * [Panel Replay without colorimetry info] + * Prepare VSC Header for SU as per DP 2.0 spec, Table 2-223 + * VSC SDP supporting 3D stereo + Panel Replay. + */ + vsc->revision = 0x6; + vsc->length = 0x10; + } } else { /* * [PSR1] @@ -2629,7 +2740,7 @@ static bool can_enable_drrs(struct intel_connector *connector, static void intel_dp_drrs_compute_config(struct intel_connector *connector, struct intel_crtc_state *pipe_config, - int link_bpp) + int link_bpp_x16) { struct drm_i915_private *i915 = to_i915(connector->base.dev); const struct drm_display_mode *downclock_mode = @@ -2654,9 +2765,10 @@ intel_dp_drrs_compute_config(struct intel_connector *connector, if (pipe_config->splitter.enable) pixel_clock /= pipe_config->splitter.link_count; - intel_link_compute_m_n(link_bpp, pipe_config->lane_count, pixel_clock, - pipe_config->port_clock, &pipe_config->dp_m2_n2, - pipe_config->fec_enable); + intel_link_compute_m_n(link_bpp_x16, pipe_config->lane_count, pixel_clock, + pipe_config->port_clock, + intel_dp_bw_fec_overhead(pipe_config->fec_enable), + &pipe_config->dp_m2_n2); /* FIXME: abstract this better */ if (pipe_config->splitter.enable) @@ -2757,7 +2869,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct intel_dp *intel_dp = enc_to_intel_dp(encoder); const struct drm_display_mode *fixed_mode; struct intel_connector *connector = intel_dp->attached_connector; - int ret = 0, link_bpp; + int ret = 0, link_bpp_x16; if (HAS_PCH_SPLIT(dev_priv) && !HAS_DDI(dev_priv) && encoder->port != PORT_A) pipe_config->has_pch_encoder = true; @@ -2806,10 +2918,10 @@ intel_dp_compute_config(struct intel_encoder *encoder, drm_dp_enhanced_frame_cap(intel_dp->dpcd); if (pipe_config->dsc.compression_enable) - link_bpp = pipe_config->dsc.compressed_bpp; + link_bpp_x16 = pipe_config->dsc.compressed_bpp_x16; else - link_bpp = intel_dp_output_bpp(pipe_config->output_format, - pipe_config->pipe_bpp); + link_bpp_x16 = to_bpp_x16(intel_dp_output_bpp(pipe_config->output_format, + pipe_config->pipe_bpp)); if (intel_dp->mso_link_count) { int n = intel_dp->mso_link_count; @@ -2833,12 +2945,12 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_dp_audio_compute_config(encoder, pipe_config, conn_state); - intel_link_compute_m_n(link_bpp, + intel_link_compute_m_n(link_bpp_x16, pipe_config->lane_count, adjusted_mode->crtc_clock, pipe_config->port_clock, - &pipe_config->dp_m_n, - pipe_config->fec_enable); + intel_dp_bw_fec_overhead(pipe_config->fec_enable), + &pipe_config->dp_m_n); /* FIXME: abstract this better */ if (pipe_config->splitter.enable) @@ -2849,7 +2961,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_vrr_compute_config(pipe_config, conn_state); intel_psr_compute_config(intel_dp, pipe_config, conn_state); - intel_dp_drrs_compute_config(connector, pipe_config, link_bpp); + intel_dp_drrs_compute_config(connector, pipe_config, link_bpp_x16); intel_dp_compute_vsc_sdp(intel_dp, pipe_config, conn_state); intel_dp_compute_hdr_metadata_infoframe_sdp(intel_dp, pipe_config, conn_state); @@ -2917,25 +3029,180 @@ static bool downstream_hpd_needs_d0(struct intel_dp *intel_dp) intel_dp->downstream_ports[0] & DP_DS_PORT_HPD; } -void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - bool enable) +static int +write_dsc_decompression_flag(struct drm_dp_aux *aux, u8 flag, bool set) { - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - int ret; + int err; + u8 val; - if (!crtc_state->dsc.compression_enable) - return; + err = drm_dp_dpcd_readb(aux, DP_DSC_ENABLE, &val); + if (err < 0) + return err; - ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_DSC_ENABLE, - enable ? DP_DECOMPRESSION_EN : 0); - if (ret < 0) + if (set) + val |= flag; + else + val &= ~flag; + + return drm_dp_dpcd_writeb(aux, DP_DSC_ENABLE, val); +} + +static void +intel_dp_sink_set_dsc_decompression(struct intel_connector *connector, + bool enable) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + + if (write_dsc_decompression_flag(connector->dp.dsc_decompression_aux, + DP_DECOMPRESSION_EN, enable) < 0) drm_dbg_kms(&i915->drm, "Failed to %s sink decompression state\n", str_enable_disable(enable)); } static void +intel_dp_sink_set_dsc_passthrough(const struct intel_connector *connector, + bool enable) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct drm_dp_aux *aux = connector->port ? + connector->port->passthrough_aux : NULL; + + if (!aux) + return; + + if (write_dsc_decompression_flag(aux, + DP_DSC_PASSTHROUGH_EN, enable) < 0) + drm_dbg_kms(&i915->drm, + "Failed to %s sink compression passthrough state\n", + str_enable_disable(enable)); +} + +static int intel_dp_dsc_aux_ref_count(struct intel_atomic_state *state, + const struct intel_connector *connector, + bool for_get_ref) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct drm_connector *_connector_iter; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + int ref_count = 0; + int i; + + /* + * On SST the decompression AUX device won't be shared, each connector + * uses for this its own AUX targeting the sink device. + */ + if (!connector->mst_port) + return connector->dp.dsc_decompression_enabled ? 1 : 0; + + for_each_oldnew_connector_in_state(&state->base, _connector_iter, + old_conn_state, new_conn_state, i) { + const struct intel_connector * + connector_iter = to_intel_connector(_connector_iter); + + if (connector_iter->mst_port != connector->mst_port) + continue; + + if (!connector_iter->dp.dsc_decompression_enabled) + continue; + + drm_WARN_ON(&i915->drm, + (for_get_ref && !new_conn_state->crtc) || + (!for_get_ref && !old_conn_state->crtc)); + + if (connector_iter->dp.dsc_decompression_aux == + connector->dp.dsc_decompression_aux) + ref_count++; + } + + return ref_count; +} + +static bool intel_dp_dsc_aux_get_ref(struct intel_atomic_state *state, + struct intel_connector *connector) +{ + bool ret = intel_dp_dsc_aux_ref_count(state, connector, true) == 0; + + connector->dp.dsc_decompression_enabled = true; + + return ret; +} + +static bool intel_dp_dsc_aux_put_ref(struct intel_atomic_state *state, + struct intel_connector *connector) +{ + connector->dp.dsc_decompression_enabled = false; + + return intel_dp_dsc_aux_ref_count(state, connector, false) == 0; +} + +/** + * intel_dp_sink_enable_decompression - Enable DSC decompression in sink/last branch device + * @state: atomic state + * @connector: connector to enable the decompression for + * @new_crtc_state: new state for the CRTC driving @connector + * + * Enable the DSC decompression if required in the %DP_DSC_ENABLE DPCD + * register of the appropriate sink/branch device. On SST this is always the + * sink device, whereas on MST based on each device's DSC capabilities it's + * either the last branch device (enabling decompression in it) or both the + * last branch device (enabling passthrough in it) and the sink device + * (enabling decompression in it). + */ +void intel_dp_sink_enable_decompression(struct intel_atomic_state *state, + struct intel_connector *connector, + const struct intel_crtc_state *new_crtc_state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + + if (!new_crtc_state->dsc.compression_enable) + return; + + if (drm_WARN_ON(&i915->drm, + !connector->dp.dsc_decompression_aux || + connector->dp.dsc_decompression_enabled)) + return; + + if (!intel_dp_dsc_aux_get_ref(state, connector)) + return; + + intel_dp_sink_set_dsc_passthrough(connector, true); + intel_dp_sink_set_dsc_decompression(connector, true); +} + +/** + * intel_dp_sink_disable_decompression - Disable DSC decompression in sink/last branch device + * @state: atomic state + * @connector: connector to disable the decompression for + * @old_crtc_state: old state for the CRTC driving @connector + * + * Disable the DSC decompression if required in the %DP_DSC_ENABLE DPCD + * register of the appropriate sink/branch device, corresponding to the + * sequence in intel_dp_sink_enable_decompression(). + */ +void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, + struct intel_connector *connector, + const struct intel_crtc_state *old_crtc_state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + + if (!old_crtc_state->dsc.compression_enable) + return; + + if (drm_WARN_ON(&i915->drm, + !connector->dp.dsc_decompression_aux || + !connector->dp.dsc_decompression_enabled)) + return; + + if (!intel_dp_dsc_aux_put_ref(state, connector)) + return; + + intel_dp_sink_set_dsc_decompression(connector, false); + intel_dp_sink_set_dsc_passthrough(connector, false); +} + +static void intel_edp_init_source_oui(struct intel_dp *intel_dp, bool careful) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); @@ -3771,7 +4038,7 @@ intel_dp_can_mst(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); - return i915->params.enable_dp_mst && + return i915->display.params.enable_dp_mst && intel_dp_mst_source_support(intel_dp) && drm_dp_read_mst_cap(&intel_dp->aux, intel_dp->dpcd); } @@ -3789,13 +4056,13 @@ intel_dp_configure_mst(struct intel_dp *intel_dp) encoder->base.base.id, encoder->base.name, str_yes_no(intel_dp_mst_source_support(intel_dp)), str_yes_no(sink_can_mst), - str_yes_no(i915->params.enable_dp_mst)); + str_yes_no(i915->display.params.enable_dp_mst)); if (!intel_dp_mst_source_support(intel_dp)) return; intel_dp->is_mst = sink_can_mst && - i915->params.enable_dp_mst; + i915->display.params.enable_dp_mst; drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst); @@ -3865,11 +4132,16 @@ static ssize_t intel_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, sdp->sdp_header.HB2 = vsc->revision; /* Revision Number */ sdp->sdp_header.HB3 = vsc->length; /* Number of Valid Data Bytes */ + if (vsc->revision == 0x6) { + sdp->db[0] = 1; + sdp->db[3] = 1; + } + /* - * Only revision 0x5 supports Pixel Encoding/Colorimetry Format as - * per DP 1.4a spec. + * Revision 0x5 and revision 0x7 supports Pixel Encoding/Colorimetry + * Format as per DP 1.4a spec and DP 2.0 respectively. */ - if (vsc->revision != 0x5) + if (!(vsc->revision == 0x5 || vsc->revision == 0x7)) goto out; /* VSC SDP Payload for DB16 through DB18 */ @@ -4049,7 +4321,10 @@ void intel_dp_set_infoframes(struct intel_encoder *encoder, VIDEO_DIP_ENABLE_SPD_HSW | VIDEO_DIP_ENABLE_DRM_GLK; u32 val = intel_de_read(dev_priv, reg) & ~dip_enable; - /* TODO: Add DSC case (DIP_ENABLE_PPS) */ + /* TODO: Sanitize DSC enabling wrt. intel_dsc_dp_pps_write(). */ + if (!enable && HAS_DSC(dev_priv)) + val &= ~VDIP_ENABLE_PPS; + /* When PSR is enabled, this routine doesn't disable VSC DIP */ if (!crtc_state->has_psr) val &= ~VIDEO_DIP_ENABLE_VSC_HSW; @@ -5409,6 +5684,7 @@ intel_dp_detect(struct drm_connector *connector, if (status == connector_status_disconnected) { memset(&intel_dp->compliance, 0, sizeof(intel_dp->compliance)); memset(intel_connector->dp.dsc_dpcd, 0, sizeof(intel_connector->dp.dsc_dpcd)); + intel_dp->psr.sink_panel_replay_support = false; if (intel_dp->is_mst) { drm_dbg_kms(&dev_priv->drm, @@ -6037,8 +6313,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, * (eg. Acer Chromebook C710), so we'll check it only if multiple * ports are attempting to use the same AUX CH, according to VBT. */ - if (intel_bios_dp_has_shared_aux_ch(encoder->devdata) && - !intel_digital_port_connected(encoder)) { + if (intel_bios_dp_has_shared_aux_ch(encoder->devdata)) { /* * If this fails, presume the DPCD answer came * from some other port using the same AUX CH. @@ -6046,10 +6321,27 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, * FIXME maybe cleaner to check this before the * DPCD read? Would need sort out the VDD handling... */ - drm_info(&dev_priv->drm, - "[ENCODER:%d:%s] HPD is down, disabling eDP\n", - encoder->base.base.id, encoder->base.name); - goto out_vdd_off; + if (!intel_digital_port_connected(encoder)) { + drm_info(&dev_priv->drm, + "[ENCODER:%d:%s] HPD is down, disabling eDP\n", + encoder->base.base.id, encoder->base.name); + goto out_vdd_off; + } + + /* + * Unfortunately even the HPD based detection fails on + * eg. Asus B360M-A (CFL+CNP), so as a last resort fall + * back to checking for a VGA branch device. Only do this + * on known affected platforms to minimize false positives. + */ + if (DISPLAY_VER(dev_priv) == 9 && drm_dp_is_branch(intel_dp->dpcd) && + (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) == + DP_DWN_STRM_PORT_TYPE_ANALOG) { + drm_info(&dev_priv->drm, + "[ENCODER:%d:%s] VGA converter detected, disabling eDP\n", + encoder->base.base.id, encoder->base.name); + goto out_vdd_off; + } } mutex_lock(&dev_priv->drm.mode_config.mutex); @@ -6238,16 +6530,6 @@ intel_dp_init_connector(struct intel_digital_port *dig_port, "HDCP init failed, skipping.\n"); } - /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written - * 0xd. Failure to do so will result in spurious interrupts being - * generated on the port when a cable is not attached. - */ - if (IS_G45(dev_priv)) { - u32 temp = intel_de_read(dev_priv, PEG_BAND_GAP_DATA); - intel_de_write(dev_priv, PEG_BAND_GAP_DATA, - (temp & ~0xf) | 0xd); - } - intel_dp->frl.is_trained = false; intel_dp->frl.trained_rate_gbps = 0; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 484aea215a25..05db46b111f2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -57,9 +57,12 @@ int intel_dp_retrain_link(struct intel_encoder *encoder, void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); -void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state, - bool enable); +void intel_dp_sink_enable_decompression(struct intel_atomic_state *state, + struct intel_connector *connector, + const struct intel_crtc_state *new_crtc_state); +void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, + struct intel_connector *connector, + const struct intel_crtc_state *old_crtc_state); void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder); void intel_dp_encoder_flush_work(struct drm_encoder *encoder); @@ -78,6 +81,8 @@ void intel_dp_audio_compute_config(struct intel_encoder *encoder, bool intel_dp_has_hdmi_sink(struct intel_dp *intel_dp); bool intel_dp_is_edp(struct intel_dp *intel_dp); bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state); +int intel_dp_link_symbol_size(int rate); +int intel_dp_link_symbol_clock(int rate); bool intel_dp_is_port_edp(struct drm_i915_private *dev_priv, enum port port); enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd); @@ -98,6 +103,8 @@ bool intel_dp_source_supports_tps4(struct drm_i915_private *i915); bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp); int intel_dp_link_required(int pixel_clock, int bpp); +int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, + int bw_overhead); int intel_dp_max_data_rate(int max_link_rate, int max_lanes); bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp); bool intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, @@ -125,6 +132,10 @@ u16 intel_dp_dsc_get_max_compressed_bpp(struct drm_i915_private *i915, enum intel_output_format output_format, u32 pipe_bpp, u32 timeslots); +int intel_dp_dsc_sink_min_compressed_bpp(struct intel_crtc_state *pipe_config); +int intel_dp_dsc_sink_max_compressed_bpp(const struct intel_connector *connector, + struct intel_crtc_state *pipe_config, + int bpc); u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector, int mode_clock, int mode_hdisplay, bool bigjoiner); @@ -136,7 +147,16 @@ static inline unsigned int intel_dp_unused_lane_mask(int lane_count) return ~((1 << lane_count) - 1) & 0xf; } +bool intel_dp_supports_fec(struct intel_dp *intel_dp, + const struct intel_connector *connector, + const struct intel_crtc_state *pipe_config); u32 intel_dp_mode_to_fec_clock(u32 mode_clock); +int intel_dp_bw_fec_overhead(bool fec_enabled); + +bool intel_dp_supports_fec(struct intel_dp *intel_dp, + const struct intel_connector *connector, + const struct intel_crtc_state *pipe_config); + u32 intel_dp_dsc_nearest_valid_bpp(struct drm_i915_private *i915, u32 bpp, u32 pipe_bpp); void intel_ddi_update_pipe(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c index 4431b6290c4c..2e2af71bcd5a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c @@ -74,7 +74,7 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp) static u32 g4x_get_aux_clock_divider(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); if (index) return 0; @@ -83,12 +83,12 @@ static u32 g4x_get_aux_clock_divider(struct intel_dp *intel_dp, int index) * The clock divider is based off the hrawclk, and would like to run at * 2MHz. So, take the hrawclk value and divide by 2000 and use that */ - return DIV_ROUND_CLOSEST(RUNTIME_INFO(dev_priv)->rawclk_freq, 2000); + return DIV_ROUND_CLOSEST(RUNTIME_INFO(i915)->rawclk_freq, 2000); } static u32 ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); u32 freq; @@ -101,18 +101,18 @@ static u32 ilk_get_aux_clock_divider(struct intel_dp *intel_dp, int index) * divide by 2000 and use that */ if (dig_port->aux_ch == AUX_CH_A) - freq = dev_priv->display.cdclk.hw.cdclk; + freq = i915->display.cdclk.hw.cdclk; else - freq = RUNTIME_INFO(dev_priv)->rawclk_freq; + freq = RUNTIME_INFO(i915)->rawclk_freq; return DIV_ROUND_CLOSEST(freq, 2000); } static u32 hsw_get_aux_clock_divider(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - if (dig_port->aux_ch != AUX_CH_A && HAS_PCH_LPT_H(dev_priv)) { + if (dig_port->aux_ch != AUX_CH_A && HAS_PCH_LPT_H(i915)) { /* Workaround for non-ULT HSW */ switch (index) { case 0: return 63; @@ -165,12 +165,11 @@ static u32 g4x_get_aux_send_ctl(struct intel_dp *intel_dp, u32 aux_clock_divider) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_i915_private *dev_priv = - to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); u32 timeout; /* Max timeout value on G4x-BDW: 1.6ms */ - if (IS_BROADWELL(dev_priv)) + if (IS_BROADWELL(i915)) timeout = DP_AUX_CH_CTL_TIME_OUT_600us; else timeout = DP_AUX_CH_CTL_TIME_OUT_400us; @@ -229,8 +228,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp, u32 aux_send_ctl_flags) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_i915_private *i915 = - to_i915(dig_port->base.base.dev); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); enum phy phy = intel_port_to_phy(i915, dig_port->base.port); bool is_tc_port = intel_phy_is_tc(i915, phy); i915_reg_t ch_ctl, ch_data[5]; @@ -531,9 +529,40 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) return ret; } +static i915_reg_t vlv_aux_ctl_reg(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum aux_ch aux_ch = dig_port->aux_ch; + + switch (aux_ch) { + case AUX_CH_B: + case AUX_CH_C: + case AUX_CH_D: + return VLV_DP_AUX_CH_CTL(aux_ch); + default: + MISSING_CASE(aux_ch); + return VLV_DP_AUX_CH_CTL(AUX_CH_B); + } +} + +static i915_reg_t vlv_aux_data_reg(struct intel_dp *intel_dp, int index) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum aux_ch aux_ch = dig_port->aux_ch; + + switch (aux_ch) { + case AUX_CH_B: + case AUX_CH_C: + case AUX_CH_D: + return VLV_DP_AUX_CH_DATA(aux_ch, index); + default: + MISSING_CASE(aux_ch); + return VLV_DP_AUX_CH_DATA(AUX_CH_B, index); + } +} + static i915_reg_t g4x_aux_ctl_reg(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -550,7 +579,6 @@ static i915_reg_t g4x_aux_ctl_reg(struct intel_dp *intel_dp) static i915_reg_t g4x_aux_data_reg(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -567,7 +595,6 @@ static i915_reg_t g4x_aux_data_reg(struct intel_dp *intel_dp, int index) static i915_reg_t ilk_aux_ctl_reg(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -586,7 +613,6 @@ static i915_reg_t ilk_aux_ctl_reg(struct intel_dp *intel_dp) static i915_reg_t ilk_aux_data_reg(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -605,7 +631,6 @@ static i915_reg_t ilk_aux_data_reg(struct intel_dp *intel_dp, int index) static i915_reg_t skl_aux_ctl_reg(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -625,7 +650,6 @@ static i915_reg_t skl_aux_ctl_reg(struct intel_dp *intel_dp) static i915_reg_t skl_aux_data_reg(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -645,7 +669,6 @@ static i915_reg_t skl_aux_data_reg(struct intel_dp *intel_dp, int index) static i915_reg_t tgl_aux_ctl_reg(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -668,7 +691,6 @@ static i915_reg_t tgl_aux_ctl_reg(struct intel_dp *intel_dp) static i915_reg_t tgl_aux_data_reg(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -691,7 +713,7 @@ static i915_reg_t tgl_aux_data_reg(struct intel_dp *intel_dp, int index) static i915_reg_t xelpdp_aux_ctl_reg(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -702,16 +724,16 @@ static i915_reg_t xelpdp_aux_ctl_reg(struct intel_dp *intel_dp) case AUX_CH_USBC2: case AUX_CH_USBC3: case AUX_CH_USBC4: - return XELPDP_DP_AUX_CH_CTL(dev_priv, aux_ch); + return XELPDP_DP_AUX_CH_CTL(i915, aux_ch); default: MISSING_CASE(aux_ch); - return XELPDP_DP_AUX_CH_CTL(dev_priv, AUX_CH_A); + return XELPDP_DP_AUX_CH_CTL(i915, AUX_CH_A); } } static i915_reg_t xelpdp_aux_data_reg(struct intel_dp *intel_dp, int index) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum aux_ch aux_ch = dig_port->aux_ch; @@ -722,10 +744,10 @@ static i915_reg_t xelpdp_aux_data_reg(struct intel_dp *intel_dp, int index) case AUX_CH_USBC2: case AUX_CH_USBC3: case AUX_CH_USBC4: - return XELPDP_DP_AUX_CH_DATA(dev_priv, aux_ch, index); + return XELPDP_DP_AUX_CH_DATA(i915, aux_ch, index); default: MISSING_CASE(aux_ch); - return XELPDP_DP_AUX_CH_DATA(dev_priv, AUX_CH_A, index); + return XELPDP_DP_AUX_CH_DATA(i915, AUX_CH_A, index); } } @@ -739,49 +761,52 @@ void intel_dp_aux_fini(struct intel_dp *intel_dp) void intel_dp_aux_init(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &dig_port->base; enum aux_ch aux_ch = dig_port->aux_ch; char buf[AUX_CH_NAME_BUFSIZE]; - if (DISPLAY_VER(dev_priv) >= 14) { + if (DISPLAY_VER(i915) >= 14) { intel_dp->aux_ch_ctl_reg = xelpdp_aux_ctl_reg; intel_dp->aux_ch_data_reg = xelpdp_aux_data_reg; - } else if (DISPLAY_VER(dev_priv) >= 12) { + } else if (DISPLAY_VER(i915) >= 12) { intel_dp->aux_ch_ctl_reg = tgl_aux_ctl_reg; intel_dp->aux_ch_data_reg = tgl_aux_data_reg; - } else if (DISPLAY_VER(dev_priv) >= 9) { + } else if (DISPLAY_VER(i915) >= 9) { intel_dp->aux_ch_ctl_reg = skl_aux_ctl_reg; intel_dp->aux_ch_data_reg = skl_aux_data_reg; - } else if (HAS_PCH_SPLIT(dev_priv)) { + } else if (HAS_PCH_SPLIT(i915)) { intel_dp->aux_ch_ctl_reg = ilk_aux_ctl_reg; intel_dp->aux_ch_data_reg = ilk_aux_data_reg; + } else if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) { + intel_dp->aux_ch_ctl_reg = vlv_aux_ctl_reg; + intel_dp->aux_ch_data_reg = vlv_aux_data_reg; } else { intel_dp->aux_ch_ctl_reg = g4x_aux_ctl_reg; intel_dp->aux_ch_data_reg = g4x_aux_data_reg; } - if (DISPLAY_VER(dev_priv) >= 9) + if (DISPLAY_VER(i915) >= 9) intel_dp->get_aux_clock_divider = skl_get_aux_clock_divider; - else if (IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) + else if (IS_BROADWELL(i915) || IS_HASWELL(i915)) intel_dp->get_aux_clock_divider = hsw_get_aux_clock_divider; - else if (HAS_PCH_SPLIT(dev_priv)) + else if (HAS_PCH_SPLIT(i915)) intel_dp->get_aux_clock_divider = ilk_get_aux_clock_divider; else intel_dp->get_aux_clock_divider = g4x_get_aux_clock_divider; - if (DISPLAY_VER(dev_priv) >= 9) + if (DISPLAY_VER(i915) >= 9) intel_dp->get_aux_send_ctl = skl_get_aux_send_ctl; else intel_dp->get_aux_send_ctl = g4x_get_aux_send_ctl; - intel_dp->aux.drm_dev = &dev_priv->drm; + intel_dp->aux.drm_dev = &i915->drm; drm_dp_aux_init(&intel_dp->aux); /* Failure to allocate our preferred name is not critical */ intel_dp->aux.name = kasprintf(GFP_KERNEL, "AUX %s/%s", - aux_ch_name(dev_priv, buf, sizeof(buf), aux_ch), + aux_ch_name(i915, buf, sizeof(buf), aux_ch), encoder->base.name); intel_dp->aux.transfer = intel_dp_aux_transfer; diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c index 26ea7e9f1b89..4f58efdc688a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c @@ -146,7 +146,7 @@ intel_dp_aux_supports_hdr_backlight(struct intel_connector *connector) * HDR static metadata we need to start maintaining table of * ranges for such panels. */ - if (i915->params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL && + if (i915->display.params.enable_dpcd_backlight != INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL && !(connector->base.hdr_sink_metadata.hdmi_type1.metadata_type & BIT(HDMI_STATIC_METADATA_TYPE1))) { drm_info(&i915->drm, @@ -489,7 +489,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) /* Check the VBT and user's module parameters to figure out which * interfaces to probe */ - switch (i915->params.enable_dpcd_backlight) { + switch (i915->display.params.enable_dpcd_backlight) { case INTEL_DP_AUX_BACKLIGHT_OFF: return -ENODEV; case INTEL_DP_AUX_BACKLIGHT_AUTO: diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h b/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h index 34f6e0a48ed2..e642445364d2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h +++ b/drivers/gpu/drm/i915/display/intel_dp_aux_regs.h @@ -21,13 +21,14 @@ #define __xe2lpd_aux_ch_idx(aux_ch) \ (aux_ch >= AUX_CH_USBC1 ? aux_ch : AUX_CH_USBC4 + 1 + (aux_ch) - AUX_CH_A) -/* TODO: Remove implicit dev_priv */ -#define _DPA_AUX_CH_CTL (DISPLAY_MMIO_BASE(dev_priv) + 0x64010) -#define _DPB_AUX_CH_CTL (DISPLAY_MMIO_BASE(dev_priv) + 0x64110) +#define _DPA_AUX_CH_CTL 0x64010 +#define _DPB_AUX_CH_CTL 0x64110 #define _XELPDP_USBC1_AUX_CH_CTL 0x16f210 #define _XELPDP_USBC2_AUX_CH_CTL 0x16f410 #define DP_AUX_CH_CTL(aux_ch) _MMIO_PORT(aux_ch, _DPA_AUX_CH_CTL, \ _DPB_AUX_CH_CTL) +#define VLV_DP_AUX_CH_CTL(aux_ch) _MMIO(VLV_DISPLAY_BASE + \ + _PORT(aux_ch, _DPA_AUX_CH_CTL, _DPB_AUX_CH_CTL)) #define _XELPDP_DP_AUX_CH_CTL(aux_ch) \ _MMIO(_PICK_EVEN_2RANGES(aux_ch, AUX_CH_USBC1, \ _DPA_AUX_CH_CTL, _DPB_AUX_CH_CTL, \ @@ -69,13 +70,14 @@ #define DP_AUX_CH_CTL_SYNC_PULSE_SKL_MASK REG_GENMASK(4, 0) /* skl+ */ #define DP_AUX_CH_CTL_SYNC_PULSE_SKL(c) REG_FIELD_PREP(DP_AUX_CH_CTL_SYNC_PULSE_SKL_MASK, (c) - 1) -/* TODO: Remove implicit dev_priv */ -#define _DPA_AUX_CH_DATA1 (DISPLAY_MMIO_BASE(dev_priv) + 0x64014) -#define _DPB_AUX_CH_DATA1 (DISPLAY_MMIO_BASE(dev_priv) + 0x64114) +#define _DPA_AUX_CH_DATA1 0x64014 +#define _DPB_AUX_CH_DATA1 0x64114 #define _XELPDP_USBC1_AUX_CH_DATA1 0x16f214 #define _XELPDP_USBC2_AUX_CH_DATA1 0x16f414 #define DP_AUX_CH_DATA(aux_ch, i) _MMIO(_PORT(aux_ch, _DPA_AUX_CH_DATA1, \ _DPB_AUX_CH_DATA1) + (i) * 4) /* 5 registers */ +#define VLV_DP_AUX_CH_DATA(aux_ch, i) _MMIO(VLV_DISPLAY_BASE + _PORT(aux_ch, _DPA_AUX_CH_DATA1, \ + _DPB_AUX_CH_DATA1) + (i) * 4) /* 5 registers */ #define _XELPDP_DP_AUX_CH_DATA(aux_ch, i) \ _MMIO(_PICK_EVEN_2RANGES(aux_ch, AUX_CH_USBC1, \ _DPA_AUX_CH_DATA1, _DPB_AUX_CH_DATA1, \ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 7b4628f4f124..63364c9602ef 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -26,6 +26,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_fixed.h> #include <drm/drm_probe_helper.h> #include "i915_drv.h" @@ -43,6 +44,9 @@ #include "intel_dpio_phy.h" #include "intel_hdcp.h" #include "intel_hotplug.h" +#include "intel_link_bw.h" +#include "intel_psr.h" +#include "intel_vdsc.h" #include "skl_scaler.h" static int intel_dp_mst_check_constraints(struct drm_i915_private *i915, int bpp, @@ -66,6 +70,73 @@ static int intel_dp_mst_check_constraints(struct drm_i915_private *i915, int bpp return 0; } +static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state, + const struct intel_connector *connector, + bool ssc, bool dsc, int bpp_x16) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->hw.adjusted_mode; + unsigned long flags = DRM_DP_BW_OVERHEAD_MST; + int dsc_slice_count = 0; + int overhead; + + flags |= intel_dp_is_uhbr(crtc_state) ? DRM_DP_BW_OVERHEAD_UHBR : 0; + flags |= ssc ? DRM_DP_BW_OVERHEAD_SSC_REF_CLK : 0; + flags |= crtc_state->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0; + + if (dsc) { + flags |= DRM_DP_BW_OVERHEAD_DSC; + /* TODO: add support for bigjoiner */ + dsc_slice_count = intel_dp_dsc_get_slice_count(connector, + adjusted_mode->clock, + adjusted_mode->hdisplay, + false); + } + + overhead = drm_dp_bw_overhead(crtc_state->lane_count, + adjusted_mode->hdisplay, + dsc_slice_count, + bpp_x16, + flags); + + /* + * TODO: clarify whether a minimum required by the fixed FEC overhead + * in the bspec audio programming sequence is required here. + */ + return max(overhead, intel_dp_bw_fec_overhead(crtc_state->fec_enable)); +} + +static void intel_dp_mst_compute_m_n(const struct intel_crtc_state *crtc_state, + const struct intel_connector *connector, + int overhead, + int bpp_x16, + struct intel_link_m_n *m_n) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->hw.adjusted_mode; + + /* TODO: Check WA 14013163432 to set data M/N for full BW utilization. */ + intel_link_compute_m_n(bpp_x16, crtc_state->lane_count, + adjusted_mode->crtc_clock, + crtc_state->port_clock, + overhead, + m_n); + + m_n->tu = DIV_ROUND_UP_ULL(mul_u32_u32(m_n->data_m, 64), m_n->data_n); +} + +static int intel_dp_mst_calc_pbn(int pixel_clock, int bpp_x16, int bw_overhead) +{ + int effective_data_rate = + intel_dp_effective_data_rate(pixel_clock, bpp_x16, bw_overhead); + + /* + * TODO: Use drm_dp_calc_pbn_mode() instead, once it's converted + * to calculate PBN with the BW overhead passed to it. + */ + return DIV_ROUND_UP(effective_data_rate * 64, 54 * 1000); +} + static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, int max_bpp, @@ -94,20 +165,67 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, crtc_state->lane_count = limits->max_lane_count; crtc_state->port_clock = limits->max_rate; + if (dsc) { + if (!intel_dp_supports_fec(intel_dp, connector, crtc_state)) + return -EINVAL; + + crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state); + } + mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr, crtc_state->port_clock, crtc_state->lane_count); + drm_dbg_kms(&i915->drm, "Looking for slots in range min bpp %d max bpp %d\n", + min_bpp, max_bpp); + for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) { + int local_bw_overhead; + int remote_bw_overhead; + int link_bpp_x16; + int remote_tu; + drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp); ret = intel_dp_mst_check_constraints(i915, bpp, adjusted_mode, crtc_state, dsc); if (ret) continue; - crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, - dsc ? bpp << 4 : bpp, - dsc); + link_bpp_x16 = to_bpp_x16(dsc ? bpp : + intel_dp_output_bpp(crtc_state->output_format, bpp)); + + local_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector, + false, dsc, link_bpp_x16); + remote_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector, + true, dsc, link_bpp_x16); + + intel_dp_mst_compute_m_n(crtc_state, connector, + local_bw_overhead, + link_bpp_x16, + &crtc_state->dp_m_n); + + /* + * The TU size programmed to the HW determines which slots in + * an MTP frame are used for this stream, which needs to match + * the payload size programmed to the first downstream branch + * device's payload table. + * + * Note that atm the payload's PBN value DRM core sends via + * the ALLOCATE_PAYLOAD side-band message matches the payload + * size (which it calculates from the PBN value) it programs + * to the first branch device's payload table. The allocation + * in the payload table could be reduced though (to + * crtc_state->dp_m_n.tu), provided that the driver doesn't + * enable SSC on the corresponding link. + */ + crtc_state->pbn = intel_dp_mst_calc_pbn(adjusted_mode->crtc_clock, + link_bpp_x16, + remote_bw_overhead); + + remote_tu = DIV_ROUND_UP(dfixed_const(crtc_state->pbn), mst_state->pbn_div.full); + + drm_WARN_ON(&i915->drm, remote_tu < crtc_state->dp_m_n.tu); + crtc_state->dp_m_n.tu = remote_tu; slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr, connector->port, @@ -116,13 +234,9 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, return slots; if (slots >= 0) { - ret = drm_dp_mst_atomic_check(state); - /* - * If we got slots >= 0 and we can fit those based on check - * then we can exit the loop. Otherwise keep trying. - */ - if (!ret) - break; + drm_WARN_ON(&i915->drm, slots != crtc_state->dp_m_n.tu); + + break; } } @@ -137,7 +251,7 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder, if (!dsc) crtc_state->pipe_bpp = bpp; else - crtc_state->dsc.compressed_bpp = bpp; + crtc_state->dsc.compressed_bpp_x16 = to_bpp_x16(bpp); drm_dbg_kms(&i915->drm, "Got %d slots for pipe bpp %d dsc %d\n", slots, bpp, dsc); } @@ -149,10 +263,7 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state, struct link_config_limits *limits) { - const struct drm_display_mode *adjusted_mode = - &crtc_state->hw.adjusted_mode; int slots = -EINVAL; - int link_bpp; /* * FIXME: allocate the BW according to link_bpp, which in the case of @@ -167,16 +278,6 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder, if (slots < 0) return slots; - link_bpp = intel_dp_output_bpp(crtc_state->output_format, crtc_state->pipe_bpp); - - intel_link_compute_m_n(link_bpp, - crtc_state->lane_count, - adjusted_mode->crtc_clock, - crtc_state->port_clock, - &crtc_state->dp_m_n, - crtc_state->fec_enable); - crtc_state->dp_m_n.tu = slots; - return 0; } @@ -188,15 +289,12 @@ static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder, struct intel_connector *connector = to_intel_connector(conn_state->connector); struct drm_i915_private *i915 = to_i915(connector->base.dev); - const struct drm_display_mode *adjusted_mode = - &crtc_state->hw.adjusted_mode; int slots = -EINVAL; int i, num_bpc; u8 dsc_bpc[3] = {}; int min_bpp, max_bpp, sink_min_bpp, sink_max_bpp; u8 dsc_max_bpc; - bool need_timeslot_recalc = false; - u32 last_compressed_bpp; + int min_compressed_bpp, max_compressed_bpp; /* Max DSC Input BPC for ICL is 10 and for TGL+ is 12 */ if (DISPLAY_VER(i915) >= 12) @@ -232,45 +330,31 @@ static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder, if (max_bpp > sink_max_bpp) max_bpp = sink_max_bpp; - min_bpp = max(min_bpp, to_bpp_int_roundup(limits->link.min_bpp_x16)); - max_bpp = min(max_bpp, to_bpp_int(limits->link.max_bpp_x16)); - - slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, max_bpp, - min_bpp, limits, - conn_state, 2 * 3, true); + max_compressed_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector, + crtc_state, + max_bpp / 3); + max_compressed_bpp = min(max_compressed_bpp, + to_bpp_int(limits->link.max_bpp_x16)); - if (slots < 0) - return slots; + min_compressed_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state); + min_compressed_bpp = max(min_compressed_bpp, + to_bpp_int_roundup(limits->link.min_bpp_x16)); - last_compressed_bpp = crtc_state->dsc.compressed_bpp; + drm_dbg_kms(&i915->drm, "DSC Sink supported compressed min bpp %d compressed max bpp %d\n", + min_compressed_bpp, max_compressed_bpp); - crtc_state->dsc.compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, - last_compressed_bpp, - crtc_state->pipe_bpp); + /* Align compressed bpps according to our own constraints */ + max_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, max_compressed_bpp, + crtc_state->pipe_bpp); + min_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, min_compressed_bpp, + crtc_state->pipe_bpp); - if (crtc_state->dsc.compressed_bpp != last_compressed_bpp) - need_timeslot_recalc = true; + slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, max_compressed_bpp, + min_compressed_bpp, limits, + conn_state, 1, true); - /* - * Apparently some MST hubs dislike if vcpi slots are not matching precisely - * the actual compressed bpp we use. - */ - if (need_timeslot_recalc) { - slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, - crtc_state->dsc.compressed_bpp, - crtc_state->dsc.compressed_bpp, - limits, conn_state, 2 * 3, true); - if (slots < 0) - return slots; - } - - intel_link_compute_m_n(crtc_state->dsc.compressed_bpp, - crtc_state->lane_count, - adjusted_mode->crtc_clock, - crtc_state->port_clock, - &crtc_state->dp_m_n, - crtc_state->fec_enable); - crtc_state->dp_m_n.tu = slots; + if (slots < 0) + return slots; return 0; } @@ -298,7 +382,102 @@ static int intel_dp_mst_update_slots(struct intel_encoder *encoder, } static bool +intel_dp_mst_dsc_source_support(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); + + /* + * FIXME: Enabling DSC on ICL results in blank screen and FIFO pipe / + * transcoder underruns, re-enable DSC after fixing this issue. + */ + return DISPLAY_VER(i915) >= 12 && intel_dsc_source_support(crtc_state); +} + +static int mode_hblank_period_ns(const struct drm_display_mode *mode) +{ + return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(mode->htotal - mode->hdisplay, + NSEC_PER_SEC / 1000), + mode->crtc_clock); +} + +static bool +hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector, + const struct intel_crtc_state *crtc_state) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->hw.adjusted_mode; + + if (!connector->dp.dsc_hblank_expansion_quirk) + return false; + + if (mode_hblank_period_ns(adjusted_mode) > 300) + return false; + + return true; +} + +static bool +adjust_limits_for_dsc_hblank_expansion_quirk(const struct intel_connector *connector, + const struct intel_crtc_state *crtc_state, + struct link_config_limits *limits, + bool dsc) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + int min_bpp_x16 = limits->link.min_bpp_x16; + + if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state)) + return true; + + if (!dsc) { + if (intel_dp_mst_dsc_source_support(crtc_state)) { + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s][CONNECTOR:%d:%s] DSC needed by hblank expansion quirk\n", + crtc->base.base.id, crtc->base.name, + connector->base.base.id, connector->base.name); + return false; + } + + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to 24 due to hblank expansion quirk\n", + crtc->base.base.id, crtc->base.name, + connector->base.base.id, connector->base.name); + + if (limits->link.max_bpp_x16 < to_bpp_x16(24)) + return false; + + limits->link.min_bpp_x16 = to_bpp_x16(24); + + return true; + } + + drm_WARN_ON(&i915->drm, limits->min_rate != limits->max_rate); + + if (limits->max_rate < 540000) + min_bpp_x16 = to_bpp_x16(13); + else if (limits->max_rate < 810000) + min_bpp_x16 = to_bpp_x16(10); + + if (limits->link.min_bpp_x16 >= min_bpp_x16) + return true; + + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to " BPP_X16_FMT " in DSC mode due to hblank expansion quirk\n", + crtc->base.base.id, crtc->base.name, + connector->base.base.id, connector->base.name, + BPP_X16_ARGS(min_bpp_x16)); + + if (limits->link.max_bpp_x16 < min_bpp_x16) + return false; + + limits->link.min_bpp_x16 = min_bpp_x16; + + return true; +} + +static bool intel_dp_mst_compute_config_limits(struct intel_dp *intel_dp, + const struct intel_connector *connector, struct intel_crtc_state *crtc_state, bool dsc, struct link_config_limits *limits) @@ -326,10 +505,16 @@ intel_dp_mst_compute_config_limits(struct intel_dp *intel_dp, intel_dp_adjust_compliance_config(intel_dp, crtc_state, limits); - return intel_dp_compute_config_link_bpp_limits(intel_dp, - crtc_state, - dsc, - limits); + if (!intel_dp_compute_config_link_bpp_limits(intel_dp, + crtc_state, + dsc, + limits)) + return false; + + return adjust_limits_for_dsc_hblank_expansion_quirk(connector, + crtc_state, + limits, + dsc); } static int intel_dp_mst_compute_config(struct intel_encoder *encoder, @@ -339,12 +524,18 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder); struct intel_dp *intel_dp = &intel_mst->primary->dp; + const struct intel_connector *connector = + to_intel_connector(conn_state->connector); const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct link_config_limits limits; bool dsc_needed; int ret = 0; + if (pipe_config->fec_enable && + !intel_dp_supports_fec(intel_dp, connector, pipe_config)) + return -EINVAL; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) return -EINVAL; @@ -354,6 +545,7 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, dsc_needed = intel_dp->force_dsc_en || !intel_dp_mst_compute_config_limits(intel_dp, + connector, pipe_config, false, &limits); @@ -375,7 +567,11 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, str_yes_no(ret), str_yes_no(intel_dp->force_dsc_en)); + if (!intel_dp_mst_dsc_source_support(pipe_config)) + return -EINVAL; + if (!intel_dp_mst_compute_config_limits(intel_dp, + connector, pipe_config, true, &limits)) @@ -420,6 +616,8 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, intel_ddi_compute_min_voltage_level(dev_priv, pipe_config); + intel_psr_compute_config(intel_dp, pipe_config, conn_state); + return 0; } @@ -459,6 +657,130 @@ intel_dp_mst_transcoder_mask(struct intel_atomic_state *state, return transcoders; } +static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state, + struct drm_dp_mst_topology_mgr *mst_mgr, + struct drm_dp_mst_port *parent_port) +{ + const struct intel_digital_connector_state *conn_state; + struct intel_connector *connector; + u8 mask = 0; + int i; + + for_each_new_intel_connector_in_state(state, connector, conn_state, i) { + if (!conn_state->base.crtc) + continue; + + if (&connector->mst_port->mst_mgr != mst_mgr) + continue; + + if (connector->port != parent_port && + !drm_dp_mst_port_downstream_of_parent(mst_mgr, + connector->port, + parent_port)) + continue; + + mask |= BIT(to_intel_crtc(conn_state->base.crtc)->pipe); + } + + return mask; +} + +static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state, + struct drm_dp_mst_topology_mgr *mst_mgr, + struct intel_link_bw_limits *limits) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc *crtc; + u8 mst_pipe_mask; + u8 fec_pipe_mask = 0; + int ret; + + mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL); + + for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, mst_pipe_mask) { + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + + /* Atomic connector check should've added all the MST CRTCs. */ + if (drm_WARN_ON(&i915->drm, !crtc_state)) + return -EINVAL; + + if (crtc_state->fec_enable) + fec_pipe_mask |= BIT(crtc->pipe); + } + + if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask) + return 0; + + limits->force_fec_pipes |= mst_pipe_mask; + + ret = intel_modeset_pipes_in_mask_early(state, "MST FEC", + mst_pipe_mask); + + return ret ? : -EAGAIN; +} + +static int intel_dp_mst_check_bw(struct intel_atomic_state *state, + struct drm_dp_mst_topology_mgr *mst_mgr, + struct drm_dp_mst_topology_state *mst_state, + struct intel_link_bw_limits *limits) +{ + struct drm_dp_mst_port *mst_port; + u8 mst_port_pipes; + int ret; + + ret = drm_dp_mst_atomic_check_mgr(&state->base, mst_mgr, mst_state, &mst_port); + if (ret != -ENOSPC) + return ret; + + mst_port_pipes = get_pipes_downstream_of_mst_port(state, mst_mgr, mst_port); + + ret = intel_link_bw_reduce_bpp(state, limits, + mst_port_pipes, "MST link BW"); + + return ret ? : -EAGAIN; +} + +/** + * intel_dp_mst_atomic_check_link - check all modeset MST link configuration + * @state: intel atomic state + * @limits: link BW limits + * + * Check the link configuration for all modeset MST outputs. If the + * configuration is invalid @limits will be updated if possible to + * reduce the total BW, after which the configuration for all CRTCs in + * @state must be recomputed with the updated @limits. + * + * Returns: + * - 0 if the confugration is valid + * - %-EAGAIN, if the configuration is invalid and @limits got updated + * with fallback values with which the configuration of all CRTCs in + * @state must be recomputed + * - Other negative error, if the configuration is invalid without a + * fallback possibility, or the check failed for another reason + */ +int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits) +{ + struct drm_dp_mst_topology_mgr *mgr; + struct drm_dp_mst_topology_state *mst_state; + int ret; + int i; + + for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) { + ret = intel_dp_mst_check_fec_change(state, mgr, limits); + if (ret) + return ret; + + ret = intel_dp_mst_check_bw(state, mgr, mst_state, + limits); + if (ret) + return ret; + } + + return 0; +} + static int intel_dp_mst_compute_config_late(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) @@ -479,19 +801,23 @@ static int intel_dp_mst_compute_config_late(struct intel_encoder *encoder, * that shares the same MST stream as mode changed, * intel_modeset_pipe_config()+intel_crtc_check_fastset() will take care to do * a fastset when possible. + * + * On TGL+ this is required since each stream go through a master transcoder, + * so if the master transcoder needs modeset, all other streams in the + * topology need a modeset. All platforms need to add the atomic state + * for all streams in the topology, since a modeset on one may require + * changing the MST link BW usage of the others, which in turn needs a + * recomputation of the corresponding CRTC states. */ static int -intel_dp_mst_atomic_master_trans_check(struct intel_connector *connector, - struct intel_atomic_state *state) +intel_dp_mst_atomic_topology_check(struct intel_connector *connector, + struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); struct drm_connector_list_iter connector_list_iter; struct intel_connector *connector_iter; int ret = 0; - if (DISPLAY_VER(dev_priv) < 12) - return 0; - if (!intel_connector_needs_modeset(state, &connector->base)) return 0; @@ -545,7 +871,7 @@ intel_dp_mst_atomic_check(struct drm_connector *connector, if (ret) return ret; - ret = intel_dp_mst_atomic_master_trans_check(intel_connector, state); + ret = intel_dp_mst_atomic_topology_check(intel_connector, state); if (ret) return ret; @@ -587,10 +913,6 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, struct intel_dp *intel_dp = &dig_port->dp; struct intel_connector *connector = to_intel_connector(old_conn_state->connector); - struct drm_dp_mst_topology_state *new_mst_state = - drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); - struct drm_dp_mst_atomic_payload *new_payload = - drm_atomic_get_mst_payload_state(new_mst_state, connector->port); struct drm_i915_private *i915 = to_i915(connector->base.dev); drm_dbg_kms(&i915->drm, "active links %d\n", @@ -598,9 +920,7 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, intel_hdcp_disable(intel_mst->connector); - drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload); - - intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); + intel_dp_sink_disable_decompression(state, connector, old_crtc_state); } static void intel_mst_post_disable_dp(struct intel_atomic_state *state, @@ -634,6 +954,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, intel_disable_transcoder(old_crtc_state); + drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload); + clear_act_sent(encoder, old_crtc_state); intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(old_crtc_state->cpu_transcoder), @@ -646,6 +968,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, intel_ddi_disable_transcoder_func(old_crtc_state); + intel_dsc_disable(old_crtc_state); + if (DISPLAY_VER(dev_priv) >= 9) skl_scaler_disable(old_crtc_state); else @@ -662,9 +986,8 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, * BSpec 4287: disable DIP after the transcoder is disabled and before * the transcoder clock select is set to none. */ - if (last_mst_stream) - intel_dp_set_infoframes(&dig_port->base, false, - old_crtc_state, NULL); + intel_dp_set_infoframes(&dig_port->base, false, + old_crtc_state, NULL); /* * From TGL spec: "If multi-stream slave transcoder: Configure * Transcoder Clock Select to direct no clock to the transcoder" @@ -754,6 +1077,8 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true); + intel_dp_sink_enable_decompression(state, connector, pipe_config); + if (first_mst_stream) dig_port->base.pre_enable(state, &dig_port->base, pipe_config, NULL); @@ -776,6 +1101,7 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, if (DISPLAY_VER(dev_priv) < 12 || !first_mst_stream) intel_ddi_enable_transcoder_clock(encoder, pipe_config); + intel_dsc_dp_pps_write(&dig_port->base, pipe_config); intel_ddi_set_dp_msa(pipe_config, conn_state); } @@ -792,11 +1118,10 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, struct drm_dp_mst_topology_state *mst_state = drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); enum transcoder trans = pipe_config->cpu_transcoder; + bool first_mst_stream = intel_dp->active_mst_links == 1; drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder); - clear_act_sent(encoder, pipe_config); - if (intel_dp_is_uhbr(pipe_config)) { const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; @@ -810,6 +1135,8 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, intel_ddi_enable_transcoder_func(encoder, pipe_config); + clear_act_sent(encoder, pipe_config); + intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(trans), 0, TRANS_DDI_DP_VC_PAYLOAD_ALLOC); @@ -818,15 +1145,16 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, wait_for_act_sent(encoder, pipe_config); + if (first_mst_stream) + intel_ddi_wait_for_fec_status(encoder, pipe_config, true); + drm_dp_add_payload_part2(&intel_dp->mst_mgr, &state->base, drm_atomic_get_mst_payload_state(mst_state, connector->port)); - if (DISPLAY_VER(dev_priv) >= 14 && pipe_config->fec_enable) - intel_de_rmw(dev_priv, MTL_CHICKEN_TRANS(trans), 0, - FECSTALL_DIS_DPTSTREAM_DPTTG); - else if (DISPLAY_VER(dev_priv) >= 12 && pipe_config->fec_enable) - intel_de_rmw(dev_priv, CHICKEN_TRANS(trans), 0, - FECSTALL_DIS_DPTSTREAM_DPTTG); + if (DISPLAY_VER(dev_priv) >= 12) + intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, trans), + FECSTALL_DIS_DPTSTREAM_DPTTG, + pipe_config->fec_enable ? FECSTALL_DIS_DPTSTREAM_DPTTG : 0); intel_audio_sdp_split_update(pipe_config); @@ -834,12 +1162,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, intel_crtc_vblank_on(pipe_config); - intel_audio_codec_enable(encoder, pipe_config, conn_state); - - /* Enable hdcp if it's desired */ - if (conn_state->content_protection == - DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(state, encoder, pipe_config, conn_state); + intel_hdcp_enable(state, encoder, pipe_config, conn_state); } static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder, @@ -974,8 +1297,20 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector, if (ret) return ret; + /* + * TODO: + * - Also check if compression would allow for the mode + * - Calculate the overhead using drm_dp_bw_overhead() / + * drm_dp_bw_channel_coding_efficiency(), similarly to the + * compute config code, as drm_dp_calc_pbn_mode() doesn't + * account with all the overheads. + * - Check here and during compute config the BW reported by + * DFP_Link_Available_Payload_Bandwidth_Number (or the + * corresponding link capabilities of the sink) in case the + * stream is uncompressed for it by the last branch device. + */ if (mode_rate > max_rate || mode->clock > max_dotclk || - drm_dp_calc_pbn_mode(mode->clock, min_bpp, false) > port->full_pbn) { + drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) { *status = MODE_CLOCK_HIGH; return 0; } @@ -1139,6 +1474,36 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp, intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector); } +static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct drm_dp_desc desc; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + + if (!connector->dp.dsc_decompression_aux) + return false; + + if (drm_dp_read_desc(connector->dp.dsc_decompression_aux, + &desc, true) < 0) + return false; + + if (!drm_dp_has_quirk(&desc, + DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC)) + return false; + + if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd) < 0) + return false; + + if (!(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE)) + return false; + + drm_dbg_kms(&i915->drm, + "[CONNECTOR:%d:%s] DSC HBLANK expansion quirk detected\n", + connector->base.base.id, connector->base.name); + + return true; +} + static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *pathprop) @@ -1161,6 +1526,11 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo intel_connector->port = port; drm_dp_mst_get_port_malloc(port); + intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port); + intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector); + intel_connector->dp.dsc_hblank_expansion_quirk = + detect_dsc_hblank_expansion_quirk(intel_connector); + connector = &intel_connector->base; ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); @@ -1172,14 +1542,6 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs); - /* - * TODO: set the AUX for the actual MST port decompressing the stream. - * At the moment the driver only supports enabling this globally in the - * first downstream MST branch, via intel_dp's (root port) AUX. - */ - intel_connector->dp.dsc_decompression_aux = &intel_dp->aux; - intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector); - for_each_pipe(dev_priv, pipe) { struct drm_encoder *enc = &intel_dp->mst_encoders[pipe]->base.base; @@ -1260,6 +1622,8 @@ intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp; intel_encoder->pre_enable = intel_mst_pre_enable_dp; intel_encoder->enable = intel_mst_enable_dp; + intel_encoder->audio_enable = intel_audio_codec_enable; + intel_encoder->audio_disable = intel_audio_codec_disable; intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state; intel_encoder->get_config = intel_dp_mst_enc_get_config; intel_encoder->initial_fastset_check = intel_dp_mst_initial_fastset_check; @@ -1407,3 +1771,91 @@ int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state, return 0; } + +static struct intel_connector * +get_connector_in_state_for_crtc(struct intel_atomic_state *state, + const struct intel_crtc *crtc) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_connector *_connector; + int i; + + for_each_oldnew_connector_in_state(&state->base, _connector, + old_conn_state, new_conn_state, i) { + struct intel_connector *connector = + to_intel_connector(_connector); + + if (old_conn_state->crtc == &crtc->base || + new_conn_state->crtc == &crtc->base) + return connector; + } + + return NULL; +} + +/** + * intel_dp_mst_crtc_needs_modeset - check if changes in topology need to modeset the given CRTC + * @state: atomic state + * @crtc: CRTC for which to check the modeset requirement + * + * Check if any change in a MST topology requires a forced modeset on @crtc in + * this topology. One such change is enabling/disabling the DSC decompression + * state in the first branch device's UFP DPCD as required by one CRTC, while + * the other @crtc in the same topology is still active, requiring a full modeset + * on @crtc. + */ +bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_connector *crtc_connector; + const struct drm_connector_state *conn_state; + const struct drm_connector *_connector; + int i; + + if (!intel_crtc_has_type(intel_atomic_get_new_crtc_state(state, crtc), + INTEL_OUTPUT_DP_MST)) + return false; + + crtc_connector = get_connector_in_state_for_crtc(state, crtc); + + if (!crtc_connector) + /* None of the connectors in the topology needs modeset */ + return false; + + for_each_new_connector_in_state(&state->base, _connector, conn_state, i) { + const struct intel_connector *connector = + to_intel_connector(_connector); + const struct intel_crtc_state *new_crtc_state; + const struct intel_crtc_state *old_crtc_state; + struct intel_crtc *crtc_iter; + + if (connector->mst_port != crtc_connector->mst_port || + !conn_state->crtc) + continue; + + crtc_iter = to_intel_crtc(conn_state->crtc); + + new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc_iter); + old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc_iter); + + if (!intel_crtc_needs_modeset(new_crtc_state)) + continue; + + if (old_crtc_state->dsc.compression_enable == + new_crtc_state->dsc.compression_enable) + continue; + /* + * Toggling the decompression flag because of this stream in + * the first downstream branch device's UFP DPCD may reset the + * whole branch device. To avoid the reset while other streams + * are also active modeset the whole MST topology in this + * case. + */ + if (connector->dp.dsc_decompression_aux == + &connector->mst_port->aux) + return true; + } + + return false; +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.h b/drivers/gpu/drm/i915/display/intel_dp_mst.h index f1815bb72267..8ca1d599091c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.h +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.h @@ -13,6 +13,7 @@ struct intel_crtc; struct intel_crtc_state; struct intel_digital_port; struct intel_dp; +struct intel_link_bw_limits; int intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_id); void intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port); @@ -22,5 +23,9 @@ bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state); bool intel_dp_mst_source_support(struct intel_dp *intel_dp); int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state, struct intel_crtc *crtc); +int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits); +bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state, + struct intel_crtc *crtc); #endif /* __INTEL_DP_MST_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dpio_phy.c b/drivers/gpu/drm/i915/display/intel_dpio_phy.c index 62b93d097e44..4ca910874a4f 100644 --- a/drivers/gpu/drm/i915/display/intel_dpio_phy.c +++ b/drivers/gpu/drm/i915/display/intel_dpio_phy.c @@ -666,6 +666,20 @@ enum dpio_phy vlv_dig_port_to_phy(struct intel_digital_port *dig_port) } } +enum dpio_phy vlv_pipe_to_phy(enum pipe pipe) +{ + switch (pipe) { + default: + MISSING_CASE(pipe); + fallthrough; + case PIPE_A: + case PIPE_B: + return DPIO_PHY0; + case PIPE_C: + return DPIO_PHY1; + } +} + enum dpio_channel vlv_pipe_to_channel(enum pipe pipe) { switch (pipe) { @@ -689,50 +703,50 @@ void chv_set_phy_signal_level(struct intel_encoder *encoder, struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel ch = vlv_dig_port_to_channel(dig_port); - enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); u32 val; int i; vlv_dpio_get(dev_priv); /* Clear calc init */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW10(ch)); val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW10(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW10(ch)); val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3); val &= ~(DPIO_PCS_TX1DEEMP_MASK | DPIO_PCS_TX2DEEMP_MASK); val |= DPIO_PCS_TX1DEEMP_9P5 | DPIO_PCS_TX2DEEMP_9P5; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW10(ch), val); } - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW9(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW9(ch)); val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW9(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW9(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW9(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW9(ch)); val &= ~(DPIO_PCS_TX1MARGIN_MASK | DPIO_PCS_TX2MARGIN_MASK); val |= DPIO_PCS_TX1MARGIN_000 | DPIO_PCS_TX2MARGIN_000; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW9(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW9(ch), val); } /* Program swing deemph */ for (i = 0; i < crtc_state->lane_count; i++) { - val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i)); + val = vlv_dpio_read(dev_priv, phy, CHV_TX_DW4(ch, i)); val &= ~DPIO_SWING_DEEMPH9P5_MASK; val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val); + vlv_dpio_write(dev_priv, phy, CHV_TX_DW4(ch, i), val); } /* Program swing margin */ for (i = 0; i < crtc_state->lane_count; i++) { - val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i)); + val = vlv_dpio_read(dev_priv, phy, CHV_TX_DW2(ch, i)); val &= ~DPIO_SWING_MARGIN000_MASK; val |= margin_reg_value << DPIO_SWING_MARGIN000_SHIFT; @@ -745,7 +759,7 @@ void chv_set_phy_signal_level(struct intel_encoder *encoder, val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT); val |= 0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val); + vlv_dpio_write(dev_priv, phy, CHV_TX_DW2(ch, i), val); } /* @@ -755,23 +769,23 @@ void chv_set_phy_signal_level(struct intel_encoder *encoder, * 27 for ch0 and ch1. */ for (i = 0; i < crtc_state->lane_count; i++) { - val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i)); + val = vlv_dpio_read(dev_priv, phy, CHV_TX_DW3(ch, i)); if (uniq_trans_scale) val |= DPIO_TX_UNIQ_TRANS_SCALE_EN; else val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val); + vlv_dpio_write(dev_priv, phy, CHV_TX_DW3(ch, i), val); } /* Start swing calculation */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW10(ch)); val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW10(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW10(ch)); val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW10(ch), val); } vlv_dpio_put(dev_priv); @@ -782,43 +796,43 @@ void chv_data_lane_soft_reset(struct intel_encoder *encoder, bool reset) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - enum dpio_channel ch = vlv_dig_port_to_channel(enc_to_dig_port(encoder)); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); - enum pipe pipe = crtc->pipe; + enum dpio_channel ch = vlv_dig_port_to_channel(enc_to_dig_port(encoder)); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); u32 val; - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW0(ch)); if (reset) val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); else val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW0(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW0(ch)); if (reset) val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); else val |= DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW0(ch), val); } - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW1(ch)); val |= CHV_PCS_REQ_SOFTRESET_EN; if (reset) val &= ~DPIO_PCS_CLK_SOFT_RESET; else val |= DPIO_PCS_CLK_SOFT_RESET; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW1(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW1(ch)); val |= CHV_PCS_REQ_SOFTRESET_EN; if (reset) val &= ~DPIO_PCS_CLK_SOFT_RESET; else val |= DPIO_PCS_CLK_SOFT_RESET; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW1(ch), val); } } @@ -829,6 +843,7 @@ void chv_phy_pre_pll_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel ch = vlv_dig_port_to_channel(dig_port); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); enum pipe pipe = crtc->pipe; unsigned int lane_mask = intel_dp_unused_lane_mask(crtc_state->lane_count); @@ -851,40 +866,40 @@ void chv_phy_pre_pll_enable(struct intel_encoder *encoder, /* program left/right clock distribution */ if (pipe != PIPE_B) { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val = vlv_dpio_read(dev_priv, phy, _CHV_CMN_DW5_CH0); val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); if (ch == DPIO_CH0) val |= CHV_BUFLEFTENA1_FORCE; if (ch == DPIO_CH1) val |= CHV_BUFRIGHTENA1_FORCE; - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + vlv_dpio_write(dev_priv, phy, _CHV_CMN_DW5_CH0, val); } else { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val = vlv_dpio_read(dev_priv, phy, _CHV_CMN_DW1_CH1); val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); if (ch == DPIO_CH0) val |= CHV_BUFLEFTENA2_FORCE; if (ch == DPIO_CH1) val |= CHV_BUFRIGHTENA2_FORCE; - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + vlv_dpio_write(dev_priv, phy, _CHV_CMN_DW1_CH1, val); } /* program clock channel usage */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW8(ch)); val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; if (pipe != PIPE_B) val &= ~CHV_PCS_USEDCLKCHANNEL; else val |= CHV_PCS_USEDCLKCHANNEL; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW8(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW8(ch)); val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE; if (pipe != PIPE_B) val &= ~CHV_PCS_USEDCLKCHANNEL; else val |= CHV_PCS_USEDCLKCHANNEL; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW8(ch), val); } /* @@ -892,12 +907,12 @@ void chv_phy_pre_pll_enable(struct intel_encoder *encoder, * matches the pipe, but here we need to * pick the CL based on the port. */ - val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch)); + val = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW19(ch)); if (pipe != PIPE_B) val &= ~CHV_CMN_USEDCLKCHANNEL; else val |= CHV_CMN_USEDCLKCHANNEL; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val); + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW19(ch), val); vlv_dpio_put(dev_priv); } @@ -910,21 +925,21 @@ void chv_phy_pre_encoder_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel ch = vlv_dig_port_to_channel(dig_port); - enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); int data, i, stagger; u32 val; vlv_dpio_get(dev_priv); /* allow hardware to manage TX FIFO reset source */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW11(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW11(ch)); val &= ~DPIO_LANEDESKEW_STRAP_OVRD; - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW11(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW11(ch)); val &= ~DPIO_LANEDESKEW_STRAP_OVRD; - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW11(ch), val); } /* Program Tx lane latency optimal setting*/ @@ -934,7 +949,7 @@ void chv_phy_pre_encoder_enable(struct intel_encoder *encoder, data = 0x0; else data = (i == 1) ? 0x0 : 0x1; - vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i), + vlv_dpio_write(dev_priv, phy, CHV_TX_DW14(ch, i), data << DPIO_UPAR_SHIFT); } @@ -950,17 +965,17 @@ void chv_phy_pre_encoder_enable(struct intel_encoder *encoder, else stagger = 0x2; - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW11(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW11(ch)); val |= DPIO_TX2_STAGGER_MASK(0x1f); - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW11(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW11(ch), val); if (crtc_state->lane_count > 2) { - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW11(ch)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS23_DW11(ch)); val |= DPIO_TX2_STAGGER_MASK(0x1f); - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW11(ch), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW11(ch), val); } - vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW12(ch), + vlv_dpio_write(dev_priv, phy, VLV_PCS01_DW12(ch), DPIO_LANESTAGGER_STRAP(stagger) | DPIO_LANESTAGGER_STRAP_OVRD | DPIO_TX1_STAGGER_MASK(0x1f) | @@ -968,7 +983,7 @@ void chv_phy_pre_encoder_enable(struct intel_encoder *encoder, DPIO_TX2_STAGGER_MULT(0)); if (crtc_state->lane_count > 2) { - vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW12(ch), + vlv_dpio_write(dev_priv, phy, VLV_PCS23_DW12(ch), DPIO_LANESTAGGER_STRAP(stagger) | DPIO_LANESTAGGER_STRAP_OVRD | DPIO_TX1_STAGGER_MASK(0x1f) | @@ -998,19 +1013,20 @@ void chv_phy_post_pll_disable(struct intel_encoder *encoder, { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum pipe pipe = to_intel_crtc(old_crtc_state->uapi.crtc)->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(pipe); u32 val; vlv_dpio_get(dev_priv); /* disable left/right clock distribution */ if (pipe != PIPE_B) { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0); + val = vlv_dpio_read(dev_priv, phy, _CHV_CMN_DW5_CH0); val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK); - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val); + vlv_dpio_write(dev_priv, phy, _CHV_CMN_DW5_CH0, val); } else { - val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1); + val = vlv_dpio_read(dev_priv, phy, _CHV_CMN_DW1_CH1); val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK); - vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val); + vlv_dpio_write(dev_priv, phy, _CHV_CMN_DW1_CH1, val); } vlv_dpio_put(dev_priv); @@ -1036,22 +1052,22 @@ void vlv_set_phy_signal_level(struct intel_encoder *encoder, struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel port = vlv_dig_port_to_channel(dig_port); - enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); vlv_dpio_get(dev_priv); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), 0x00000000); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW4(port), demph_reg_value); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(port), + vlv_dpio_write(dev_priv, phy, VLV_TX_DW5(port), 0x00000000); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW4(port), demph_reg_value); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW2(port), uniqtranscale_reg_value); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(port), 0x0C782040); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW3(port), 0x0C782040); if (tx3_demph) - vlv_dpio_write(dev_priv, pipe, VLV_TX3_DW4(port), tx3_demph); + vlv_dpio_write(dev_priv, phy, VLV_TX3_DW4(port), tx3_demph); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW11(port), 0x00030000); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW9(port), preemph_reg_value); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW11(port), 0x00030000); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW9(port), preemph_reg_value); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW5(port), DPIO_TX_OCALINIT_EN); vlv_dpio_put(dev_priv); } @@ -1063,24 +1079,24 @@ void vlv_phy_pre_pll_enable(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel port = vlv_dig_port_to_channel(dig_port); - enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); /* Program Tx lane resets to default */ vlv_dpio_get(dev_priv); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW0(port), DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW1(port), DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | (1<<DPIO_PCS_CLK_DATAWIDTH_SHIFT) | DPIO_PCS_CLK_SOFT_RESET); /* Fix up inter-pair skew failure */ - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW12(port), 0x00750f00); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW11(port), 0x00001500); - vlv_dpio_write(dev_priv, pipe, VLV_TX_DW14(port), 0x40400000); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW12(port), 0x00750f00); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW11(port), 0x00001500); + vlv_dpio_write(dev_priv, phy, VLV_TX_DW14(port), 0x40400000); vlv_dpio_put(dev_priv); } @@ -1094,23 +1110,24 @@ void vlv_phy_pre_encoder_enable(struct intel_encoder *encoder, struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum dpio_channel port = vlv_dig_port_to_channel(dig_port); enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(pipe); u32 val; vlv_dpio_get(dev_priv); /* Enable clock channels for this port */ - val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port)); + val = vlv_dpio_read(dev_priv, phy, VLV_PCS01_DW8(port)); val = 0; if (pipe) val |= (1<<21); else val &= ~(1<<21); val |= 0x001000c4; - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW8(port), val); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW8(port), val); /* Program lane clock */ - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW14(port), 0x00760018); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW14(port), 0x00760018); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW23(port), 0x00400888); vlv_dpio_put(dev_priv); } @@ -1122,10 +1139,10 @@ void vlv_phy_reset_lanes(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); enum dpio_channel port = vlv_dig_port_to_channel(dig_port); - enum pipe pipe = crtc->pipe; + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); vlv_dpio_get(dev_priv); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port), 0x00000000); - vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW1(port), 0x00e00060); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW0(port), 0x00000000); + vlv_dpio_write(dev_priv, phy, VLV_PCS_DW1(port), 0x00e00060); vlv_dpio_put(dev_priv); } diff --git a/drivers/gpu/drm/i915/display/intel_dpio_phy.h b/drivers/gpu/drm/i915/display/intel_dpio_phy.h index 4d43dbbdf81c..9adc4e8c1738 100644 --- a/drivers/gpu/drm/i915/display/intel_dpio_phy.h +++ b/drivers/gpu/drm/i915/display/intel_dpio_phy.h @@ -44,6 +44,7 @@ u8 bxt_ddi_phy_get_lane_lat_optim_mask(struct intel_encoder *encoder); enum dpio_channel vlv_dig_port_to_channel(struct intel_digital_port *dig_port); enum dpio_phy vlv_dig_port_to_phy(struct intel_digital_port *dig_port); +enum dpio_phy vlv_pipe_to_phy(enum pipe pipe); enum dpio_channel vlv_pipe_to_channel(enum pipe pipe); void chv_set_phy_signal_level(struct intel_encoder *encoder, @@ -116,6 +117,10 @@ static inline enum dpio_phy vlv_dig_port_to_phy(struct intel_digital_port *dig_p { return DPIO_PHY0; } +static inline enum dpio_phy vlv_pipe_to_phy(enum pipe pipe) +{ + return DPIO_PHY0; +} static inline enum dpio_channel vlv_pipe_to_channel(enum pipe pipe) { return DPIO_CH0; diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index d41c1dc9f66c..3038655377ea 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -16,6 +16,7 @@ #include "intel_dpio_phy.h" #include "intel_dpll.h" #include "intel_lvds.h" +#include "intel_lvds_regs.h" #include "intel_panel.h" #include "intel_pps.h" #include "intel_snps_phy.h" @@ -311,7 +312,7 @@ static const struct intel_limit intel_limits_bxt = { * divided-down version of it. */ /* m1 is reserved as 0 in Pineview, n is a ring counter */ -int pnv_calc_dpll_params(int refclk, struct dpll *clock) +static int pnv_calc_dpll_params(int refclk, struct dpll *clock) { clock->m = clock->m2 + 2; clock->p = clock->p1 * clock->p2; @@ -342,7 +343,7 @@ int i9xx_calc_dpll_params(int refclk, struct dpll *clock) return clock->dot; } -int vlv_calc_dpll_params(int refclk, struct dpll *clock) +static int vlv_calc_dpll_params(int refclk, struct dpll *clock) { clock->m = clock->m1 * clock->m2; clock->p = clock->p1 * clock->p2 * 5; @@ -368,6 +369,176 @@ int chv_calc_dpll_params(int refclk, struct dpll *clock) return clock->dot; } +static int i9xx_pll_refclk(struct drm_device *dev, + const struct intel_crtc_state *pipe_config) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dpll = pipe_config->dpll_hw_state.dpll; + + if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) + return dev_priv->display.vbt.lvds_ssc_freq; + else if (HAS_PCH_SPLIT(dev_priv)) + return 120000; + else if (DISPLAY_VER(dev_priv) != 2) + return 96000; + else + return 48000; +} + +/* Returns the clock of the currently programmed mode of the given pipe. */ +void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + u32 dpll = pipe_config->dpll_hw_state.dpll; + u32 fp; + struct dpll clock; + int port_clock; + int refclk = i9xx_pll_refclk(dev, pipe_config); + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = pipe_config->dpll_hw_state.fp0; + else + fp = pipe_config->dpll_hw_state.fp1; + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + if (IS_PINEVIEW(dev_priv)) { + clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1; + clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT; + } else { + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + } + + if (DISPLAY_VER(dev_priv) != 2) { + if (IS_PINEVIEW(dev_priv)) + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >> + DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW); + else + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + switch (dpll & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? + 5 : 10; + break; + case DPLLB_MODE_LVDS: + clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? + 7 : 14; + break; + default: + drm_dbg_kms(&dev_priv->drm, + "Unknown DPLL mode %08x in programmed " + "mode\n", (int)(dpll & DPLL_MODE_MASK)); + return; + } + + if (IS_PINEVIEW(dev_priv)) + port_clock = pnv_calc_dpll_params(refclk, &clock); + else + port_clock = i9xx_calc_dpll_params(refclk, &clock); + } else { + enum pipe lvds_pipe; + + if (IS_I85X(dev_priv) && + intel_lvds_port_enabled(dev_priv, LVDS, &lvds_pipe) && + lvds_pipe == crtc->pipe) { + u32 lvds = intel_de_read(dev_priv, LVDS); + + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + if (lvds & LVDS_CLKB_POWER_UP) + clock.p2 = 7; + else + clock.p2 = 14; + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + } + + port_clock = i9xx_calc_dpll_params(refclk, &clock); + } + + /* + * This value includes pixel_multiplier. We will use + * port_clock to compute adjusted_mode.crtc_clock in the + * encoder's get_config() function. + */ + pipe_config->port_clock = port_clock; +} + +void vlv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); + struct dpll clock; + u32 mdiv; + int refclk = 100000; + + /* In case of DSI, DPLL will not be used */ + if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0) + return; + + vlv_dpio_get(dev_priv); + mdiv = vlv_dpio_read(dev_priv, phy, VLV_PLL_DW3(crtc->pipe)); + vlv_dpio_put(dev_priv); + + clock.m1 = (mdiv >> DPIO_M1DIV_SHIFT) & 7; + clock.m2 = mdiv & DPIO_M2DIV_MASK; + clock.n = (mdiv >> DPIO_N_SHIFT) & 0xf; + clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7; + clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f; + + pipe_config->port_clock = vlv_calc_dpll_params(refclk, &clock); +} + +void chv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = to_i915(dev); + enum dpio_channel port = vlv_pipe_to_channel(crtc->pipe); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); + struct dpll clock; + u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2, pll_dw3; + int refclk = 100000; + + /* In case of DSI, DPLL will not be used */ + if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) == 0) + return; + + vlv_dpio_get(dev_priv); + cmn_dw13 = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW13(port)); + pll_dw0 = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW0(port)); + pll_dw1 = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW1(port)); + pll_dw2 = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW2(port)); + pll_dw3 = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW3(port)); + vlv_dpio_put(dev_priv); + + clock.m1 = (pll_dw1 & 0x7) == DPIO_CHV_M1_DIV_BY_2 ? 2 : 0; + clock.m2 = (pll_dw0 & 0xff) << 22; + if (pll_dw3 & DPIO_CHV_FRAC_DIV_EN) + clock.m2 |= pll_dw2 & 0x3fffff; + clock.n = (pll_dw1 >> DPIO_CHV_N_DIV_SHIFT) & 0xf; + clock.p1 = (cmn_dw13 >> DPIO_CHV_P1_DIV_SHIFT) & 0x7; + clock.p2 = (cmn_dw13 >> DPIO_CHV_P2_DIV_SHIFT) & 0x1f; + + pipe_config->port_clock = chv_calc_dpll_params(refclk, &clock); +} + /* * Returns whether the given set of divisors are valid for a given refclk with * the given connectors. @@ -1003,12 +1174,10 @@ static int dg2_crtc_compute_clock(struct intel_atomic_state *state, static int mtl_crtc_compute_clock(struct intel_atomic_state *state, struct intel_crtc *crtc) { - struct drm_i915_private *i915 = to_i915(state->base.dev); struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder = intel_get_crtc_new_encoder(state, crtc_state); - enum phy phy = intel_port_to_phy(i915, encoder->port); int ret; ret = intel_cx0pll_calc_state(crtc_state, encoder); @@ -1016,10 +1185,7 @@ static int mtl_crtc_compute_clock(struct intel_atomic_state *state, return ret; /* TODO: Do the readback via intel_compute_shared_dplls() */ - if (intel_is_c10phy(i915, phy)) - crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); - else - crtc_state->port_clock = intel_c20pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c20); + crtc_state->port_clock = intel_cx0pll_calc_port_clock(encoder, &crtc_state->cx0pll_state); crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); @@ -1645,7 +1811,7 @@ void i9xx_enable_pll(const struct intel_crtc_state *crtc_state) } static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, - enum pipe pipe) + enum dpio_phy phy) { u32 reg_val; @@ -1653,30 +1819,31 @@ static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv, * PLLB opamp always calibrates to max value of 0x3f, force enable it * and set it to a reasonable value instead. */ - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); + reg_val = vlv_dpio_read(dev_priv, phy, VLV_PLL_DW9(1)); reg_val &= 0xffffff00; reg_val |= 0x00000030; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW9(1), reg_val); - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); + reg_val = vlv_dpio_read(dev_priv, phy, VLV_REF_DW13); reg_val &= 0x00ffffff; reg_val |= 0x8c000000; - vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); + vlv_dpio_write(dev_priv, phy, VLV_REF_DW13, reg_val); - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW9(1)); + reg_val = vlv_dpio_read(dev_priv, phy, VLV_PLL_DW9(1)); reg_val &= 0xffffff00; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9(1), reg_val); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW9(1), reg_val); - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_REF_DW13); + reg_val = vlv_dpio_read(dev_priv, phy, VLV_REF_DW13); reg_val &= 0x00ffffff; reg_val |= 0xb0000000; - vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val); + vlv_dpio_write(dev_priv, phy, VLV_REF_DW13, reg_val); } static void vlv_prepare_pll(const struct intel_crtc_state *crtc_state) { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); enum pipe pipe = crtc->pipe; u32 mdiv; u32 bestn, bestm1, bestm2, bestp1, bestp2; @@ -1694,18 +1861,18 @@ static void vlv_prepare_pll(const struct intel_crtc_state *crtc_state) /* PLL B needs special handling */ if (pipe == PIPE_B) - vlv_pllb_recal_opamp(dev_priv, pipe); + vlv_pllb_recal_opamp(dev_priv, phy); /* Set up Tx target for periodic Rcomp update */ - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW9_BCAST, 0x0100000f); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW9_BCAST, 0x0100000f); /* Disable target IRef on PLL */ - reg_val = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW8(pipe)); + reg_val = vlv_dpio_read(dev_priv, phy, VLV_PLL_DW8(pipe)); reg_val &= 0x00ffffff; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW8(pipe), reg_val); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW8(pipe), reg_val); /* Disable fast lock */ - vlv_dpio_write(dev_priv, pipe, VLV_CMN_DW0, 0x610); + vlv_dpio_write(dev_priv, phy, VLV_CMN_DW0, 0x610); /* Set idtafcrecal before PLL is enabled */ mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); @@ -1719,46 +1886,46 @@ static void vlv_prepare_pll(const struct intel_crtc_state *crtc_state) * Note: don't use the DAC post divider as it seems unstable. */ mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW3(pipe), mdiv); mdiv |= DPIO_ENABLE_CALIBRATION; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW3(pipe), mdiv); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW3(pipe), mdiv); /* Set HBR and RBR LPF coefficients */ if (crtc_state->port_clock == 162000 || intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG) || intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW10(pipe), 0x009f0003); else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW10(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW10(pipe), 0x00d0000f); if (intel_crtc_has_dp_encoder(crtc_state)) { /* Use SSC source */ if (pipe == PIPE_A) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW5(pipe), 0x0df40000); else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW5(pipe), 0x0df70000); } else { /* HDMI or VGA */ /* Use bend source */ if (pipe == PIPE_A) - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW5(pipe), 0x0df70000); else - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe), + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW5(pipe), 0x0df40000); } - coreclk = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW7(pipe)); + coreclk = vlv_dpio_read(dev_priv, phy, VLV_PLL_DW7(pipe)); coreclk = (coreclk & 0x0000ff00) | 0x01c00000; if (intel_crtc_has_dp_encoder(crtc_state)) coreclk |= 0x01000000; - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW7(pipe), coreclk); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW7(pipe), coreclk); - vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW11(pipe), 0x87871000); + vlv_dpio_write(dev_priv, phy, VLV_PLL_DW11(pipe), 0x87871000); vlv_dpio_put(dev_priv); } @@ -1809,6 +1976,7 @@ static void chv_prepare_pll(const struct intel_crtc_state *crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum pipe pipe = crtc->pipe; enum dpio_channel port = vlv_pipe_to_channel(pipe); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); u32 loopfilter, tribuf_calcntr; u32 bestm2, bestp1, bestp2, bestm2_frac; u32 dpio_val; @@ -1825,39 +1993,39 @@ static void chv_prepare_pll(const struct intel_crtc_state *crtc_state) vlv_dpio_get(dev_priv); /* p1 and p2 divider */ - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW13(port), + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW13(port), 5 << DPIO_CHV_S1_DIV_SHIFT | bestp1 << DPIO_CHV_P1_DIV_SHIFT | bestp2 << DPIO_CHV_P2_DIV_SHIFT | 1 << DPIO_CHV_K_DIV_SHIFT); /* Feedback post-divider - m2 */ - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW0(port), bestm2); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW0(port), bestm2); /* Feedback refclk divider - n and m1 */ - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW1(port), + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW1(port), DPIO_CHV_M1_DIV_BY_2 | 1 << DPIO_CHV_N_DIV_SHIFT); /* M2 fraction division */ - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW2(port), bestm2_frac); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW2(port), bestm2_frac); /* M2 fraction division enable */ - dpio_val = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW3(port)); + dpio_val = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW3(port)); dpio_val &= ~(DPIO_CHV_FEEDFWD_GAIN_MASK | DPIO_CHV_FRAC_DIV_EN); dpio_val |= (2 << DPIO_CHV_FEEDFWD_GAIN_SHIFT); if (bestm2_frac) dpio_val |= DPIO_CHV_FRAC_DIV_EN; - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW3(port), dpio_val); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW3(port), dpio_val); /* Program digital lock detect threshold */ - dpio_val = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW9(port)); + dpio_val = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW9(port)); dpio_val &= ~(DPIO_CHV_INT_LOCK_THRESHOLD_MASK | DPIO_CHV_INT_LOCK_THRESHOLD_SEL_COARSE); dpio_val |= (0x5 << DPIO_CHV_INT_LOCK_THRESHOLD_SHIFT); if (!bestm2_frac) dpio_val |= DPIO_CHV_INT_LOCK_THRESHOLD_SEL_COARSE; - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW9(port), dpio_val); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW9(port), dpio_val); /* Loop filter */ if (vco == 5400000) { @@ -1882,16 +2050,16 @@ static void chv_prepare_pll(const struct intel_crtc_state *crtc_state) loopfilter |= (0x3 << DPIO_CHV_GAIN_CTRL_SHIFT); tribuf_calcntr = 0; } - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW6(port), loopfilter); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW6(port), loopfilter); - dpio_val = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW8(port)); + dpio_val = vlv_dpio_read(dev_priv, phy, CHV_PLL_DW8(port)); dpio_val &= ~DPIO_CHV_TDC_TARGET_CNT_MASK; dpio_val |= (tribuf_calcntr << DPIO_CHV_TDC_TARGET_CNT_SHIFT); - vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW8(port), dpio_val); + vlv_dpio_write(dev_priv, phy, CHV_PLL_DW8(port), dpio_val); /* AFC Recal */ - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), - vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port)) | + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW14(port), + vlv_dpio_read(dev_priv, phy, CHV_CMN_DW14(port)) | DPIO_AFC_RECAL); vlv_dpio_put(dev_priv); @@ -1903,14 +2071,15 @@ static void _chv_enable_pll(const struct intel_crtc_state *crtc_state) struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); enum pipe pipe = crtc->pipe; enum dpio_channel port = vlv_pipe_to_channel(pipe); + enum dpio_phy phy = vlv_pipe_to_phy(crtc->pipe); u32 tmp; vlv_dpio_get(dev_priv); /* Enable back the 10bit clock to display controller */ - tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port)); + tmp = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW14(port)); tmp |= DPIO_DCLKP_EN; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), tmp); + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW14(port), tmp); vlv_dpio_put(dev_priv); @@ -2031,6 +2200,7 @@ void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { enum dpio_channel port = vlv_pipe_to_channel(pipe); + enum dpio_phy phy = vlv_pipe_to_phy(pipe); u32 val; /* Make sure the pipe isn't still relying on us */ @@ -2047,9 +2217,9 @@ void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) vlv_dpio_get(dev_priv); /* Disable 10bit clock to display controller */ - val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port)); + val = vlv_dpio_read(dev_priv, phy, CHV_CMN_DW14(port)); val &= ~DPIO_DCLKP_EN; - vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val); + vlv_dpio_write(dev_priv, phy, CHV_CMN_DW14(port), val); vlv_dpio_put(dev_priv); } diff --git a/drivers/gpu/drm/i915/display/intel_dpll.h b/drivers/gpu/drm/i915/display/intel_dpll.h index bbc30542f29f..ac01bb19cc6c 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.h +++ b/drivers/gpu/drm/i915/display/intel_dpll.h @@ -20,8 +20,6 @@ int intel_dpll_crtc_compute_clock(struct intel_atomic_state *state, struct intel_crtc *crtc); int intel_dpll_crtc_get_shared_dpll(struct intel_atomic_state *state, struct intel_crtc *crtc); -int vlv_calc_dpll_params(int refclk, struct dpll *clock); -int pnv_calc_dpll_params(int refclk, struct dpll *clock); int i9xx_calc_dpll_params(int refclk, struct dpll *clock); u32 i9xx_dpll_compute_fp(const struct dpll *dpll); void vlv_compute_dpll(struct intel_crtc_state *crtc_state); @@ -41,6 +39,13 @@ bool bxt_find_best_dpll(struct intel_crtc_state *crtc_state, struct dpll *best_clock); int chv_calc_dpll_params(int refclk, struct dpll *pll_clock); +void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config); +void vlv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config); +void chv_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_state *pipe_config); + void assert_pll_enabled(struct drm_i915_private *i915, enum pipe pipe); void assert_pll_disabled(struct drm_i915_private *i915, enum pipe pipe); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 399653a20f98..7958d0bd851e 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -219,6 +219,26 @@ intel_tc_pll_enable_reg(struct drm_i915_private *i915, return MG_PLL_ENABLE(tc_port); } +static void _intel_enable_shared_dpll(struct drm_i915_private *i915, + struct intel_shared_dpll *pll) +{ + if (pll->info->power_domain) + pll->wakeref = intel_display_power_get(i915, pll->info->power_domain); + + pll->info->funcs->enable(i915, pll); + pll->on = true; +} + +static void _intel_disable_shared_dpll(struct drm_i915_private *i915, + struct intel_shared_dpll *pll) +{ + pll->info->funcs->disable(i915, pll); + pll->on = false; + + if (pll->info->power_domain) + intel_display_power_put(i915, pll->info->power_domain, pll->wakeref); +} + /** * intel_enable_shared_dpll - enable a CRTC's shared DPLL * @crtc_state: CRTC, and its state, which has a shared DPLL @@ -258,8 +278,8 @@ void intel_enable_shared_dpll(const struct intel_crtc_state *crtc_state) drm_WARN_ON(&i915->drm, pll->on); drm_dbg_kms(&i915->drm, "enabling %s\n", pll->info->name); - pll->info->funcs->enable(i915, pll); - pll->on = true; + + _intel_enable_shared_dpll(i915, pll); out: mutex_unlock(&i915->display.dpll.lock); @@ -304,8 +324,8 @@ void intel_disable_shared_dpll(const struct intel_crtc_state *crtc_state) goto out; drm_dbg_kms(&i915->drm, "disabling %s\n", pll->info->name); - pll->info->funcs->disable(i915, pll); - pll->on = false; + + _intel_disable_shared_dpll(i915, pll); out: mutex_unlock(&i915->display.dpll.lock); @@ -631,9 +651,9 @@ static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = { }; static const struct dpll_info pch_plls[] = { - { "PCH DPLL A", &ibx_pch_dpll_funcs, DPLL_ID_PCH_PLL_A, 0 }, - { "PCH DPLL B", &ibx_pch_dpll_funcs, DPLL_ID_PCH_PLL_B, 0 }, - { }, + { .name = "PCH DPLL A", .funcs = &ibx_pch_dpll_funcs, .id = DPLL_ID_PCH_PLL_A, }, + { .name = "PCH DPLL B", .funcs = &ibx_pch_dpll_funcs, .id = DPLL_ID_PCH_PLL_B, }, + {} }; static const struct intel_dpll_mgr pch_pll_mgr = { @@ -1239,13 +1259,16 @@ static const struct intel_shared_dpll_funcs hsw_ddi_lcpll_funcs = { }; static const struct dpll_info hsw_plls[] = { - { "WRPLL 1", &hsw_ddi_wrpll_funcs, DPLL_ID_WRPLL1, 0 }, - { "WRPLL 2", &hsw_ddi_wrpll_funcs, DPLL_ID_WRPLL2, 0 }, - { "SPLL", &hsw_ddi_spll_funcs, DPLL_ID_SPLL, 0 }, - { "LCPLL 810", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_810, INTEL_DPLL_ALWAYS_ON }, - { "LCPLL 1350", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_1350, INTEL_DPLL_ALWAYS_ON }, - { "LCPLL 2700", &hsw_ddi_lcpll_funcs, DPLL_ID_LCPLL_2700, INTEL_DPLL_ALWAYS_ON }, - { }, + { .name = "WRPLL 1", .funcs = &hsw_ddi_wrpll_funcs, .id = DPLL_ID_WRPLL1, }, + { .name = "WRPLL 2", .funcs = &hsw_ddi_wrpll_funcs, .id = DPLL_ID_WRPLL2, }, + { .name = "SPLL", .funcs = &hsw_ddi_spll_funcs, .id = DPLL_ID_SPLL, }, + { .name = "LCPLL 810", .funcs = &hsw_ddi_lcpll_funcs, .id = DPLL_ID_LCPLL_810, + .flags = INTEL_DPLL_ALWAYS_ON, }, + { .name = "LCPLL 1350", .funcs = &hsw_ddi_lcpll_funcs, .id = DPLL_ID_LCPLL_1350, + .flags = INTEL_DPLL_ALWAYS_ON, }, + { .name = "LCPLL 2700", .funcs = &hsw_ddi_lcpll_funcs, .id = DPLL_ID_LCPLL_2700, + .flags = INTEL_DPLL_ALWAYS_ON, }, + {} }; static const struct intel_dpll_mgr hsw_pll_mgr = { @@ -1921,11 +1944,12 @@ static const struct intel_shared_dpll_funcs skl_ddi_dpll0_funcs = { }; static const struct dpll_info skl_plls[] = { - { "DPLL 0", &skl_ddi_dpll0_funcs, DPLL_ID_SKL_DPLL0, INTEL_DPLL_ALWAYS_ON }, - { "DPLL 1", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL1, 0 }, - { "DPLL 2", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL2, 0 }, - { "DPLL 3", &skl_ddi_pll_funcs, DPLL_ID_SKL_DPLL3, 0 }, - { }, + { .name = "DPLL 0", .funcs = &skl_ddi_dpll0_funcs, .id = DPLL_ID_SKL_DPLL0, + .flags = INTEL_DPLL_ALWAYS_ON, }, + { .name = "DPLL 1", .funcs = &skl_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL1, }, + { .name = "DPLL 2", .funcs = &skl_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL2, }, + { .name = "DPLL 3", .funcs = &skl_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL3, }, + {} }; static const struct intel_dpll_mgr skl_pll_mgr = { @@ -2376,10 +2400,10 @@ static const struct intel_shared_dpll_funcs bxt_ddi_pll_funcs = { }; static const struct dpll_info bxt_plls[] = { - { "PORT PLL A", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL0, 0 }, - { "PORT PLL B", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL1, 0 }, - { "PORT PLL C", &bxt_ddi_pll_funcs, DPLL_ID_SKL_DPLL2, 0 }, - { }, + { .name = "PORT PLL A", .funcs = &bxt_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL0, }, + { .name = "PORT PLL B", .funcs = &bxt_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL1, }, + { .name = "PORT PLL C", .funcs = &bxt_ddi_pll_funcs, .id = DPLL_ID_SKL_DPLL2, }, + {} }; static const struct intel_dpll_mgr bxt_pll_mgr = { @@ -3834,18 +3858,6 @@ static void combo_pll_enable(struct drm_i915_private *i915, { i915_reg_t enable_reg = intel_combo_pll_enable_reg(i915, pll); - if ((IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) && - pll->info->id == DPLL_ID_EHL_DPLL4) { - - /* - * We need to disable DC states when this DPLL is enabled. - * This can be done by taking a reference on DPLL4 power - * domain. - */ - pll->wakeref = intel_display_power_get(i915, - POWER_DOMAIN_DC_OFF); - } - icl_pll_power_enable(i915, pll, enable_reg); icl_dpll_write(i915, pll); @@ -3941,11 +3953,6 @@ static void combo_pll_disable(struct drm_i915_private *i915, i915_reg_t enable_reg = intel_combo_pll_enable_reg(i915, pll); icl_pll_disable(i915, pll, enable_reg); - - if ((IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) && - pll->info->id == DPLL_ID_EHL_DPLL4) - intel_display_power_put(i915, POWER_DOMAIN_DC_OFF, - pll->wakeref); } static void tbt_pll_disable(struct drm_i915_private *i915, @@ -4014,14 +4021,14 @@ static const struct intel_shared_dpll_funcs mg_pll_funcs = { }; static const struct dpll_info icl_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "TBT PLL", &tbt_pll_funcs, DPLL_ID_ICL_TBTPLL, 0 }, - { "MG PLL 1", &mg_pll_funcs, DPLL_ID_ICL_MGPLL1, 0 }, - { "MG PLL 2", &mg_pll_funcs, DPLL_ID_ICL_MGPLL2, 0 }, - { "MG PLL 3", &mg_pll_funcs, DPLL_ID_ICL_MGPLL3, 0 }, - { "MG PLL 4", &mg_pll_funcs, DPLL_ID_ICL_MGPLL4, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "TBT PLL", .funcs = &tbt_pll_funcs, .id = DPLL_ID_ICL_TBTPLL, }, + { .name = "MG PLL 1", .funcs = &mg_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, + { .name = "MG PLL 2", .funcs = &mg_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, + { .name = "MG PLL 3", .funcs = &mg_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, + { .name = "MG PLL 4", .funcs = &mg_pll_funcs, .id = DPLL_ID_ICL_MGPLL4, }, + {} }; static const struct intel_dpll_mgr icl_pll_mgr = { @@ -4035,10 +4042,11 @@ static const struct intel_dpll_mgr icl_pll_mgr = { }; static const struct dpll_info ehl_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "DPLL 4", &combo_pll_funcs, DPLL_ID_EHL_DPLL4, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "DPLL 4", .funcs = &combo_pll_funcs, .id = DPLL_ID_EHL_DPLL4, + .power_domain = POWER_DOMAIN_DC_OFF, }, + {} }; static const struct intel_dpll_mgr ehl_pll_mgr = { @@ -4058,16 +4066,16 @@ static const struct intel_shared_dpll_funcs dkl_pll_funcs = { }; static const struct dpll_info tgl_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "TBT PLL", &tbt_pll_funcs, DPLL_ID_ICL_TBTPLL, 0 }, - { "TC PLL 1", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL1, 0 }, - { "TC PLL 2", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL2, 0 }, - { "TC PLL 3", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL3, 0 }, - { "TC PLL 4", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL4, 0 }, - { "TC PLL 5", &dkl_pll_funcs, DPLL_ID_TGL_MGPLL5, 0 }, - { "TC PLL 6", &dkl_pll_funcs, DPLL_ID_TGL_MGPLL6, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "TBT PLL", .funcs = &tbt_pll_funcs, .id = DPLL_ID_ICL_TBTPLL, }, + { .name = "TC PLL 1", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, + { .name = "TC PLL 2", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, + { .name = "TC PLL 3", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, + { .name = "TC PLL 4", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL4, }, + { .name = "TC PLL 5", .funcs = &dkl_pll_funcs, .id = DPLL_ID_TGL_MGPLL5, }, + { .name = "TC PLL 6", .funcs = &dkl_pll_funcs, .id = DPLL_ID_TGL_MGPLL6, }, + {} }; static const struct intel_dpll_mgr tgl_pll_mgr = { @@ -4081,10 +4089,10 @@ static const struct intel_dpll_mgr tgl_pll_mgr = { }; static const struct dpll_info rkl_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "DPLL 4", &combo_pll_funcs, DPLL_ID_EHL_DPLL4, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "DPLL 4", .funcs = &combo_pll_funcs, .id = DPLL_ID_EHL_DPLL4, }, + {} }; static const struct intel_dpll_mgr rkl_pll_mgr = { @@ -4097,11 +4105,11 @@ static const struct intel_dpll_mgr rkl_pll_mgr = { }; static const struct dpll_info dg1_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_DG1_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_DG1_DPLL1, 0 }, - { "DPLL 2", &combo_pll_funcs, DPLL_ID_DG1_DPLL2, 0 }, - { "DPLL 3", &combo_pll_funcs, DPLL_ID_DG1_DPLL3, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL1, }, + { .name = "DPLL 2", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL2, }, + { .name = "DPLL 3", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL3, }, + {} }; static const struct intel_dpll_mgr dg1_pll_mgr = { @@ -4114,11 +4122,11 @@ static const struct intel_dpll_mgr dg1_pll_mgr = { }; static const struct dpll_info adls_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "DPLL 2", &combo_pll_funcs, DPLL_ID_DG1_DPLL2, 0 }, - { "DPLL 3", &combo_pll_funcs, DPLL_ID_DG1_DPLL3, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "DPLL 2", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL2, }, + { .name = "DPLL 3", .funcs = &combo_pll_funcs, .id = DPLL_ID_DG1_DPLL3, }, + {} }; static const struct intel_dpll_mgr adls_pll_mgr = { @@ -4131,14 +4139,14 @@ static const struct intel_dpll_mgr adls_pll_mgr = { }; static const struct dpll_info adlp_plls[] = { - { "DPLL 0", &combo_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, - { "DPLL 1", &combo_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, - { "TBT PLL", &tbt_pll_funcs, DPLL_ID_ICL_TBTPLL, 0 }, - { "TC PLL 1", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL1, 0 }, - { "TC PLL 2", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL2, 0 }, - { "TC PLL 3", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL3, 0 }, - { "TC PLL 4", &dkl_pll_funcs, DPLL_ID_ICL_MGPLL4, 0 }, - { }, + { .name = "DPLL 0", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL0, }, + { .name = "DPLL 1", .funcs = &combo_pll_funcs, .id = DPLL_ID_ICL_DPLL1, }, + { .name = "TBT PLL", .funcs = &tbt_pll_funcs, .id = DPLL_ID_ICL_TBTPLL, }, + { .name = "TC PLL 1", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL1, }, + { .name = "TC PLL 2", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL2, }, + { .name = "TC PLL 3", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL3, }, + { .name = "TC PLL 4", .funcs = &dkl_pll_funcs, .id = DPLL_ID_ICL_MGPLL4, }, + {} }; static const struct intel_dpll_mgr adlp_pll_mgr = { @@ -4365,12 +4373,8 @@ static void readout_dpll_hw_state(struct drm_i915_private *i915, pll->on = intel_dpll_get_hw_state(i915, pll, &pll->state.hw_state); - if ((IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) && - pll->on && - pll->info->id == DPLL_ID_EHL_DPLL4) { - pll->wakeref = intel_display_power_get(i915, - POWER_DOMAIN_DC_OFF); - } + if (pll->on && pll->info->power_domain) + pll->wakeref = intel_display_power_get(i915, pll->info->power_domain); pll->state.pipe_mask = 0; for_each_intel_crtc(&i915->drm, crtc) { @@ -4417,8 +4421,7 @@ static void sanitize_dpll_state(struct drm_i915_private *i915, "%s enabled but not in use, disabling\n", pll->info->name); - pll->info->funcs->disable(i915, pll); - pll->on = false; + _intel_disable_shared_dpll(i915, pll); } void intel_dpll_sanitize_state(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index dd4796a61751..2e7ea0d8d3ff 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -27,6 +27,7 @@ #include <linux/types.h> +#include "intel_display_power.h" #include "intel_wakeref.h" #define for_each_shared_dpll(__i915, __pll, __i) \ @@ -270,6 +271,11 @@ struct dpll_info { */ enum intel_dpll_id id; + /** + * @power_domain: extra power domain required by the DPLL + */ + enum intel_display_power_domain power_domain; + #define INTEL_DPLL_ALWAYS_ON (1 << 0) /** * @flags: diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c index 48582b31b7f7..b29bceff73f2 100644 --- a/drivers/gpu/drm/i915/display/intel_dpt.c +++ b/drivers/gpu/drm/i915/display/intel_dpt.c @@ -9,8 +9,6 @@ #include "gt/gen8_ppgtt.h" #include "i915_drv.h" -#include "i915_reg.h" -#include "intel_de.h" #include "intel_display_types.h" #include "intel_dpt.h" #include "intel_fb.h" @@ -318,25 +316,3 @@ void intel_dpt_destroy(struct i915_address_space *vm) i915_vm_put(&dpt->vm); } -void intel_dpt_configure(struct intel_crtc *crtc) -{ - struct drm_i915_private *i915 = to_i915(crtc->base.dev); - - if (DISPLAY_VER(i915) == 14) { - enum pipe pipe = crtc->pipe; - enum plane_id plane_id; - - for_each_plane_id_on_crtc(crtc, plane_id) { - if (plane_id == PLANE_CURSOR) - continue; - - intel_de_rmw(i915, PLANE_CHICKEN(pipe, plane_id), - PLANE_CHICKEN_DISABLE_DPT, - i915->params.enable_dpt ? 0 : PLANE_CHICKEN_DISABLE_DPT); - } - } else if (DISPLAY_VER(i915) == 13) { - intel_de_rmw(i915, CHICKEN_MISC_2, - CHICKEN_MISC_DISABLE_DPT, - i915->params.enable_dpt ? 0 : CHICKEN_MISC_DISABLE_DPT); - } -} diff --git a/drivers/gpu/drm/i915/display/intel_dpt.h b/drivers/gpu/drm/i915/display/intel_dpt.h index d9a166550185..e18a9f767b11 100644 --- a/drivers/gpu/drm/i915/display/intel_dpt.h +++ b/drivers/gpu/drm/i915/display/intel_dpt.h @@ -10,7 +10,6 @@ struct drm_i915_private; struct i915_address_space; struct i915_vma; -struct intel_crtc; struct intel_framebuffer; void intel_dpt_destroy(struct i915_address_space *vm); @@ -20,6 +19,5 @@ void intel_dpt_suspend(struct drm_i915_private *i915); void intel_dpt_resume(struct drm_i915_private *i915); struct i915_address_space * intel_dpt_create(struct intel_framebuffer *fb); -void intel_dpt_configure(struct intel_crtc *crtc); #endif /* __INTEL_DPT_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dpt_common.c b/drivers/gpu/drm/i915/display/intel_dpt_common.c new file mode 100644 index 000000000000..cdba47165c04 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dpt_common.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include "i915_reg.h" +#include "intel_de.h" +#include "intel_display_types.h" +#include "intel_dpt_common.h" + +void intel_dpt_configure(struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + + if (DISPLAY_VER(i915) == 14) { + enum pipe pipe = crtc->pipe; + enum plane_id plane_id; + + for_each_plane_id_on_crtc(crtc, plane_id) { + if (plane_id == PLANE_CURSOR) + continue; + + intel_de_rmw(i915, PLANE_CHICKEN(pipe, plane_id), + PLANE_CHICKEN_DISABLE_DPT, + i915->display.params.enable_dpt ? 0 : + PLANE_CHICKEN_DISABLE_DPT); + } + } else if (DISPLAY_VER(i915) == 13) { + intel_de_rmw(i915, CHICKEN_MISC_2, + CHICKEN_MISC_DISABLE_DPT, + i915->display.params.enable_dpt ? 0 : + CHICKEN_MISC_DISABLE_DPT); + } +} diff --git a/drivers/gpu/drm/i915/display/intel_dpt_common.h b/drivers/gpu/drm/i915/display/intel_dpt_common.h new file mode 100644 index 000000000000..6d7de405126a --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dpt_common.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __INTEL_DPT_COMMON_H__ +#define __INTEL_DPT_COMMON_H__ + +struct intel_crtc; + +void intel_dpt_configure(struct intel_crtc *crtc); + +#endif /* __INTEL_DPT_COMMON_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index 78b6fe24dcd8..9598d50f68f2 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -4,9 +4,6 @@ * */ -#include "gem/i915_gem_internal.h" -#include "gem/i915_gem_lmem.h" - #include "i915_drv.h" #include "i915_irq.h" #include "i915_reg.h" @@ -14,12 +11,13 @@ #include "intel_de.h" #include "intel_display_types.h" #include "intel_dsb.h" +#include "intel_dsb_buffer.h" #include "intel_dsb_regs.h" #include "intel_vblank.h" #include "intel_vrr.h" #include "skl_watermark.h" -struct i915_vma; +#define CACHELINE_BYTES 64 enum dsb_id { INVALID_DSB = -1, @@ -32,8 +30,7 @@ enum dsb_id { struct intel_dsb { enum dsb_id id; - u32 *cmd_buf; - struct i915_vma *vma; + struct intel_dsb_buffer dsb_buf; struct intel_crtc *crtc; /* @@ -109,15 +106,17 @@ static void intel_dsb_dump(struct intel_dsb *dsb) { struct intel_crtc *crtc = dsb->crtc; struct drm_i915_private *i915 = to_i915(crtc->base.dev); - const u32 *buf = dsb->cmd_buf; int i; drm_dbg_kms(&i915->drm, "[CRTC:%d:%s] DSB %d commands {\n", crtc->base.base.id, crtc->base.name, dsb->id); for (i = 0; i < ALIGN(dsb->free_pos, 64 / 4); i += 4) drm_dbg_kms(&i915->drm, - " 0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", - i * 4, buf[i], buf[i+1], buf[i+2], buf[i+3]); + " 0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", i * 4, + intel_dsb_buffer_read(&dsb->dsb_buf, i), + intel_dsb_buffer_read(&dsb->dsb_buf, i + 1), + intel_dsb_buffer_read(&dsb->dsb_buf, i + 2), + intel_dsb_buffer_read(&dsb->dsb_buf, i + 3)); drm_dbg_kms(&i915->drm, "}\n"); } @@ -129,8 +128,6 @@ static bool is_dsb_busy(struct drm_i915_private *i915, enum pipe pipe, static void intel_dsb_emit(struct intel_dsb *dsb, u32 ldw, u32 udw) { - u32 *buf = dsb->cmd_buf; - if (!assert_dsb_has_room(dsb)) return; @@ -139,14 +136,13 @@ static void intel_dsb_emit(struct intel_dsb *dsb, u32 ldw, u32 udw) dsb->ins_start_offset = dsb->free_pos; - buf[dsb->free_pos++] = ldw; - buf[dsb->free_pos++] = udw; + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->free_pos++, ldw); + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->free_pos++, udw); } static bool intel_dsb_prev_ins_is_write(struct intel_dsb *dsb, u32 opcode, i915_reg_t reg) { - const u32 *buf = dsb->cmd_buf; u32 prev_opcode, prev_reg; /* @@ -157,8 +153,10 @@ static bool intel_dsb_prev_ins_is_write(struct intel_dsb *dsb, if (dsb->free_pos == 0) return false; - prev_opcode = buf[dsb->ins_start_offset + 1] & ~DSB_REG_VALUE_MASK; - prev_reg = buf[dsb->ins_start_offset + 1] & DSB_REG_VALUE_MASK; + prev_opcode = intel_dsb_buffer_read(&dsb->dsb_buf, + dsb->ins_start_offset + 1) & ~DSB_REG_VALUE_MASK; + prev_reg = intel_dsb_buffer_read(&dsb->dsb_buf, + dsb->ins_start_offset + 1) & DSB_REG_VALUE_MASK; return prev_opcode == opcode && prev_reg == i915_mmio_reg_offset(reg); } @@ -191,6 +189,8 @@ static bool intel_dsb_prev_ins_is_indexed_write(struct intel_dsb *dsb, i915_reg_ void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val) { + u32 old_val; + /* * For example the buffer will look like below for 3 dwords for auto * increment register: @@ -214,31 +214,32 @@ void intel_dsb_reg_write(struct intel_dsb *dsb, (DSB_BYTE_EN << DSB_BYTE_EN_SHIFT) | i915_mmio_reg_offset(reg)); } else { - u32 *buf = dsb->cmd_buf; - if (!assert_dsb_has_room(dsb)) return; /* convert to indexed write? */ if (intel_dsb_prev_ins_is_mmio_write(dsb, reg)) { - u32 prev_val = buf[dsb->ins_start_offset + 0]; + u32 prev_val = intel_dsb_buffer_read(&dsb->dsb_buf, + dsb->ins_start_offset + 0); - buf[dsb->ins_start_offset + 0] = 1; /* count */ - buf[dsb->ins_start_offset + 1] = - (DSB_OPCODE_INDEXED_WRITE << DSB_OPCODE_SHIFT) | - i915_mmio_reg_offset(reg); - buf[dsb->ins_start_offset + 2] = prev_val; + intel_dsb_buffer_write(&dsb->dsb_buf, + dsb->ins_start_offset + 0, 1); /* count */ + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->ins_start_offset + 1, + (DSB_OPCODE_INDEXED_WRITE << DSB_OPCODE_SHIFT) | + i915_mmio_reg_offset(reg)); + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->ins_start_offset + 2, prev_val); dsb->free_pos++; } - buf[dsb->free_pos++] = val; + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->free_pos++, val); /* Update the count */ - buf[dsb->ins_start_offset]++; + old_val = intel_dsb_buffer_read(&dsb->dsb_buf, dsb->ins_start_offset); + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->ins_start_offset, old_val + 1); /* if number of data words is odd, then the last dword should be 0.*/ if (dsb->free_pos & 0x1) - buf[dsb->free_pos] = 0; + intel_dsb_buffer_write(&dsb->dsb_buf, dsb->free_pos, 0); } } @@ -297,8 +298,8 @@ static void intel_dsb_align_tail(struct intel_dsb *dsb) aligned_tail = ALIGN(tail, CACHELINE_BYTES); if (aligned_tail > tail) - memset(&dsb->cmd_buf[dsb->free_pos], 0, - aligned_tail - tail); + intel_dsb_buffer_memset(&dsb->dsb_buf, dsb->free_pos, 0, + aligned_tail - tail); dsb->free_pos = aligned_tail / 4; } @@ -317,7 +318,7 @@ void intel_dsb_finish(struct intel_dsb *dsb) intel_dsb_align_tail(dsb); - i915_gem_object_flush_map(dsb->vma->obj); + intel_dsb_buffer_flush_map(&dsb->dsb_buf); } static int intel_dsb_dewake_scanline(const struct intel_crtc_state *crtc_state) @@ -361,7 +362,7 @@ static void _intel_dsb_commit(struct intel_dsb *dsb, u32 ctrl, ctrl | DSB_ENABLE); intel_de_write_fw(dev_priv, DSB_HEAD(pipe, dsb->id), - i915_ggtt_offset(dsb->vma)); + intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf)); if (dewake_scanline >= 0) { int diff, hw_dewake_scanline; @@ -383,7 +384,7 @@ static void _intel_dsb_commit(struct intel_dsb *dsb, u32 ctrl, } intel_de_write_fw(dev_priv, DSB_TAIL(pipe, dsb->id), - i915_ggtt_offset(dsb->vma) + tail); + intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf) + tail); } /** @@ -408,7 +409,7 @@ void intel_dsb_wait(struct intel_dsb *dsb) enum pipe pipe = crtc->pipe; if (wait_for(!is_dsb_busy(dev_priv, pipe, dsb->id), 1)) { - u32 offset = i915_ggtt_offset(dsb->vma); + u32 offset = intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf); intel_de_write_fw(dev_priv, DSB_CTRL(pipe, dsb->id), DSB_ENABLE | DSB_HALT); @@ -445,12 +446,9 @@ struct intel_dsb *intel_dsb_prepare(const struct intel_crtc_state *crtc_state, { struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *i915 = to_i915(crtc->base.dev); - struct drm_i915_gem_object *obj; intel_wakeref_t wakeref; struct intel_dsb *dsb; - struct i915_vma *vma; unsigned int size; - u32 *buf; if (!HAS_DSB(i915)) return NULL; @@ -464,37 +462,13 @@ struct intel_dsb *intel_dsb_prepare(const struct intel_crtc_state *crtc_state, /* ~1 qword per instruction, full cachelines */ size = ALIGN(max_cmds * 8, CACHELINE_BYTES); - if (HAS_LMEM(i915)) { - obj = i915_gem_object_create_lmem(i915, PAGE_ALIGN(size), - I915_BO_ALLOC_CONTIGUOUS); - if (IS_ERR(obj)) - goto out_put_rpm; - } else { - obj = i915_gem_object_create_internal(i915, PAGE_ALIGN(size)); - if (IS_ERR(obj)) - goto out_put_rpm; - - i915_gem_object_set_cache_coherency(obj, I915_CACHE_NONE); - } - - vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); - if (IS_ERR(vma)) { - i915_gem_object_put(obj); - goto out_put_rpm; - } - - buf = i915_gem_object_pin_map_unlocked(vma->obj, I915_MAP_WC); - if (IS_ERR(buf)) { - i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP); + if (!intel_dsb_buffer_create(crtc, &dsb->dsb_buf, size)) goto out_put_rpm; - } intel_runtime_pm_put(&i915->runtime_pm, wakeref); dsb->id = DSB1; - dsb->vma = vma; dsb->crtc = crtc; - dsb->cmd_buf = buf; dsb->size = size / 4; /* in dwords */ dsb->free_pos = 0; dsb->ins_start_offset = 0; @@ -522,6 +496,6 @@ out: */ void intel_dsb_cleanup(struct intel_dsb *dsb) { - i915_vma_unpin_and_release(&dsb->vma, I915_VMA_RELEASE_MAP); + intel_dsb_buffer_cleanup(&dsb->dsb_buf); kfree(dsb); } diff --git a/drivers/gpu/drm/i915/display/intel_dsb_buffer.c b/drivers/gpu/drm/i915/display/intel_dsb_buffer.c new file mode 100644 index 000000000000..c77d48bda26a --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dsb_buffer.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2023, Intel Corporation. + */ + +#include "gem/i915_gem_internal.h" +#include "gem/i915_gem_lmem.h" +#include "i915_drv.h" +#include "i915_vma.h" +#include "intel_display_types.h" +#include "intel_dsb_buffer.h" + +u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf) +{ + return i915_ggtt_offset(dsb_buf->vma); +} + +void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val) +{ + dsb_buf->cmd_buf[idx] = val; +} + +u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx) +{ + return dsb_buf->cmd_buf[idx]; +} + +void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size) +{ + WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf)); + + memset(&dsb_buf->cmd_buf[idx], val, size); +} + +bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + struct drm_i915_gem_object *obj; + struct i915_vma *vma; + u32 *buf; + + if (HAS_LMEM(i915)) { + obj = i915_gem_object_create_lmem(i915, PAGE_ALIGN(size), + I915_BO_ALLOC_CONTIGUOUS); + if (IS_ERR(obj)) + return false; + } else { + obj = i915_gem_object_create_internal(i915, PAGE_ALIGN(size)); + if (IS_ERR(obj)) + return false; + + i915_gem_object_set_cache_coherency(obj, I915_CACHE_NONE); + } + + vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); + if (IS_ERR(vma)) { + i915_gem_object_put(obj); + return false; + } + + buf = i915_gem_object_pin_map_unlocked(vma->obj, I915_MAP_WC); + if (IS_ERR(buf)) { + i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP); + return false; + } + + dsb_buf->vma = vma; + dsb_buf->cmd_buf = buf; + dsb_buf->buf_size = size; + + return true; +} + +void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf) +{ + i915_vma_unpin_and_release(&dsb_buf->vma, I915_VMA_RELEASE_MAP); +} + +void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf) +{ + i915_gem_object_flush_map(dsb_buf->vma->obj); +} diff --git a/drivers/gpu/drm/i915/display/intel_dsb_buffer.h b/drivers/gpu/drm/i915/display/intel_dsb_buffer.h new file mode 100644 index 000000000000..425acd393905 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dsb_buffer.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright © 2023 Intel Corporation + */ + +#ifndef _INTEL_DSB_BUFFER_H +#define _INTEL_DSB_BUFFER_H + +#include <linux/types.h> + +struct intel_crtc; +struct i915_vma; + +struct intel_dsb_buffer { + u32 *cmd_buf; + struct i915_vma *vma; + size_t buf_size; +}; + +u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf); +void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val); +u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx); +void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size); +bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, + size_t size); +void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf); +void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf); + +#endif diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c index 24b2cbcfc1ef..275d0218394c 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c @@ -55,43 +55,6 @@ #define MIPI_VIRTUAL_CHANNEL_SHIFT 1 #define MIPI_PORT_SHIFT 3 -/* base offsets for gpio pads */ -#define VLV_GPIO_NC_0_HV_DDI0_HPD 0x4130 -#define VLV_GPIO_NC_1_HV_DDI0_DDC_SDA 0x4120 -#define VLV_GPIO_NC_2_HV_DDI0_DDC_SCL 0x4110 -#define VLV_GPIO_NC_3_PANEL0_VDDEN 0x4140 -#define VLV_GPIO_NC_4_PANEL0_BKLTEN 0x4150 -#define VLV_GPIO_NC_5_PANEL0_BKLTCTL 0x4160 -#define VLV_GPIO_NC_6_HV_DDI1_HPD 0x4180 -#define VLV_GPIO_NC_7_HV_DDI1_DDC_SDA 0x4190 -#define VLV_GPIO_NC_8_HV_DDI1_DDC_SCL 0x4170 -#define VLV_GPIO_NC_9_PANEL1_VDDEN 0x4100 -#define VLV_GPIO_NC_10_PANEL1_BKLTEN 0x40E0 -#define VLV_GPIO_NC_11_PANEL1_BKLTCTL 0x40F0 - -#define VLV_GPIO_PCONF0(base_offset) (base_offset) -#define VLV_GPIO_PAD_VAL(base_offset) ((base_offset) + 8) - -struct gpio_map { - u16 base_offset; - bool init; -}; - -static struct gpio_map vlv_gpio_table[] = { - { VLV_GPIO_NC_0_HV_DDI0_HPD }, - { VLV_GPIO_NC_1_HV_DDI0_DDC_SDA }, - { VLV_GPIO_NC_2_HV_DDI0_DDC_SCL }, - { VLV_GPIO_NC_3_PANEL0_VDDEN }, - { VLV_GPIO_NC_4_PANEL0_BKLTEN }, - { VLV_GPIO_NC_5_PANEL0_BKLTCTL }, - { VLV_GPIO_NC_6_HV_DDI1_HPD }, - { VLV_GPIO_NC_7_HV_DDI1_DDC_SDA }, - { VLV_GPIO_NC_8_HV_DDI1_DDC_SCL }, - { VLV_GPIO_NC_9_PANEL1_VDDEN }, - { VLV_GPIO_NC_10_PANEL1_BKLTEN }, - { VLV_GPIO_NC_11_PANEL1_BKLTCTL }, -}; - struct i2c_adapter_lookup { u16 slave_addr; struct intel_dsi *intel_dsi; @@ -103,19 +66,6 @@ struct i2c_adapter_lookup { #define CHV_GPIO_IDX_START_SW 100 #define CHV_GPIO_IDX_START_SE 198 -#define CHV_VBT_MAX_PINS_PER_FMLY 15 - -#define CHV_GPIO_PAD_CFG0(f, i) (0x4400 + (f) * 0x400 + (i) * 8) -#define CHV_GPIO_GPIOEN (1 << 15) -#define CHV_GPIO_GPIOCFG_GPIO (0 << 8) -#define CHV_GPIO_GPIOCFG_GPO (1 << 8) -#define CHV_GPIO_GPIOCFG_GPI (2 << 8) -#define CHV_GPIO_GPIOCFG_HIZ (3 << 8) -#define CHV_GPIO_GPIOTXSTATE(state) ((!!(state)) << 1) - -#define CHV_GPIO_PAD_CFG1(f, i) (0x4400 + (f) * 0x400 + (i) * 8 + 4) -#define CHV_GPIO_CFGLOCK (1 << 31) - /* ICL DSI Display GPIO Pins */ #define ICL_GPIO_DDSP_HPD_A 0 #define ICL_GPIO_L_VDDEN_1 1 @@ -142,7 +92,7 @@ static enum port intel_dsi_seq_port_to_port(struct intel_dsi *intel_dsi, if (seq_port) { if (intel_dsi->ports & BIT(PORT_B)) return PORT_B; - else if (intel_dsi->ports & BIT(PORT_C)) + if (intel_dsi->ports & BIT(PORT_C)) return PORT_C; } @@ -243,75 +193,93 @@ static const u8 *mipi_exec_delay(struct intel_dsi *intel_dsi, const u8 *data) return data; } -static void vlv_exec_gpio(struct intel_connector *connector, - u8 gpio_source, u8 gpio_index, bool value) +static void soc_gpio_set_value(struct intel_connector *connector, u8 gpio_index, + const char *con_id, u8 idx, bool value) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - struct gpio_map *map; - u16 pconf0, padval; - u32 tmp; - u8 port; - - if (gpio_index >= ARRAY_SIZE(vlv_gpio_table)) { - drm_dbg_kms(&dev_priv->drm, "unknown gpio index %u\n", - gpio_index); - return; + /* XXX: this table is a quick ugly hack. */ + static struct gpio_desc *soc_gpio_table[U8_MAX + 1]; + struct gpio_desc *gpio_desc = soc_gpio_table[gpio_index]; + + if (gpio_desc) { + gpiod_set_value(gpio_desc, value); + } else { + gpio_desc = devm_gpiod_get_index(dev_priv->drm.dev, con_id, idx, + value ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW); + if (IS_ERR(gpio_desc)) { + drm_err(&dev_priv->drm, + "GPIO index %u request failed (%pe)\n", + gpio_index, gpio_desc); + return; + } + + soc_gpio_table[gpio_index] = gpio_desc; } +} - map = &vlv_gpio_table[gpio_index]; +static void soc_opaque_gpio_set_value(struct intel_connector *connector, + u8 gpio_index, const char *chip, + const char *con_id, u8 idx, bool value) +{ + struct gpiod_lookup_table *lookup; - if (connector->panel.vbt.dsi.seq_version >= 3) { - /* XXX: this assumes vlv_gpio_table only has NC GPIOs. */ - port = IOSF_PORT_GPIO_NC; - } else { - if (gpio_source == 0) { - port = IOSF_PORT_GPIO_NC; - } else if (gpio_source == 1) { + lookup = kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); + if (!lookup) + return; + + lookup->dev_id = "0000:00:02.0"; + lookup->table[0] = + GPIO_LOOKUP_IDX(chip, idx, con_id, idx, GPIO_ACTIVE_HIGH); + + gpiod_add_lookup_table(lookup); + + soc_gpio_set_value(connector, gpio_index, con_id, idx, value); + + gpiod_remove_lookup_table(lookup); + kfree(lookup); +} + +static void vlv_gpio_set_value(struct intel_connector *connector, + u8 gpio_source, u8 gpio_index, bool value) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + + /* XXX: this assumes vlv_gpio_table only has NC GPIOs. */ + if (connector->panel.vbt.dsi.seq_version < 3) { + if (gpio_source == 1) { drm_dbg_kms(&dev_priv->drm, "SC gpio not supported\n"); return; - } else { + } + if (gpio_source > 1) { drm_dbg_kms(&dev_priv->drm, "unknown gpio source %u\n", gpio_source); return; } } - pconf0 = VLV_GPIO_PCONF0(map->base_offset); - padval = VLV_GPIO_PAD_VAL(map->base_offset); - - vlv_iosf_sb_get(dev_priv, BIT(VLV_IOSF_SB_GPIO)); - if (!map->init) { - /* FIXME: remove constant below */ - vlv_iosf_sb_write(dev_priv, port, pconf0, 0x2000CC00); - map->init = true; - } - - tmp = 0x4 | value; - vlv_iosf_sb_write(dev_priv, port, padval, tmp); - vlv_iosf_sb_put(dev_priv, BIT(VLV_IOSF_SB_GPIO)); + soc_opaque_gpio_set_value(connector, gpio_index, + "INT33FC:01", "Panel N", gpio_index, value); } -static void chv_exec_gpio(struct intel_connector *connector, - u8 gpio_source, u8 gpio_index, bool value) +static void chv_gpio_set_value(struct intel_connector *connector, + u8 gpio_source, u8 gpio_index, bool value) { struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - u16 cfg0, cfg1; - u16 family_num; - u8 port; if (connector->panel.vbt.dsi.seq_version >= 3) { if (gpio_index >= CHV_GPIO_IDX_START_SE) { /* XXX: it's unclear whether 255->57 is part of SE. */ - gpio_index -= CHV_GPIO_IDX_START_SE; - port = CHV_IOSF_PORT_GPIO_SE; + soc_opaque_gpio_set_value(connector, gpio_index, "INT33FF:03", "Panel SE", + gpio_index - CHV_GPIO_IDX_START_SE, value); } else if (gpio_index >= CHV_GPIO_IDX_START_SW) { - gpio_index -= CHV_GPIO_IDX_START_SW; - port = CHV_IOSF_PORT_GPIO_SW; + soc_opaque_gpio_set_value(connector, gpio_index, "INT33FF:00", "Panel SW", + gpio_index - CHV_GPIO_IDX_START_SW, value); } else if (gpio_index >= CHV_GPIO_IDX_START_E) { - gpio_index -= CHV_GPIO_IDX_START_E; - port = CHV_IOSF_PORT_GPIO_E; + soc_opaque_gpio_set_value(connector, gpio_index, "INT33FF:02", "Panel E", + gpio_index - CHV_GPIO_IDX_START_E, value); } else { - port = CHV_IOSF_PORT_GPIO_N; + soc_opaque_gpio_set_value(connector, gpio_index, "INT33FF:01", "Panel N", + gpio_index - CHV_GPIO_IDX_START_N, value); } } else { /* XXX: The spec is unclear about CHV GPIO on seq v2 */ @@ -328,56 +296,15 @@ static void chv_exec_gpio(struct intel_connector *connector, return; } - port = CHV_IOSF_PORT_GPIO_N; - } - - family_num = gpio_index / CHV_VBT_MAX_PINS_PER_FMLY; - gpio_index = gpio_index % CHV_VBT_MAX_PINS_PER_FMLY; - - cfg0 = CHV_GPIO_PAD_CFG0(family_num, gpio_index); - cfg1 = CHV_GPIO_PAD_CFG1(family_num, gpio_index); - - vlv_iosf_sb_get(dev_priv, BIT(VLV_IOSF_SB_GPIO)); - vlv_iosf_sb_write(dev_priv, port, cfg1, 0); - vlv_iosf_sb_write(dev_priv, port, cfg0, - CHV_GPIO_GPIOEN | CHV_GPIO_GPIOCFG_GPO | - CHV_GPIO_GPIOTXSTATE(value)); - vlv_iosf_sb_put(dev_priv, BIT(VLV_IOSF_SB_GPIO)); -} - -static void bxt_exec_gpio(struct intel_connector *connector, - u8 gpio_source, u8 gpio_index, bool value) -{ - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - /* XXX: this table is a quick ugly hack. */ - static struct gpio_desc *bxt_gpio_table[U8_MAX + 1]; - struct gpio_desc *gpio_desc = bxt_gpio_table[gpio_index]; - - if (!gpio_desc) { - gpio_desc = devm_gpiod_get_index(dev_priv->drm.dev, - NULL, gpio_index, - value ? GPIOD_OUT_LOW : - GPIOD_OUT_HIGH); - - if (IS_ERR_OR_NULL(gpio_desc)) { - drm_err(&dev_priv->drm, - "GPIO index %u request failed (%ld)\n", - gpio_index, PTR_ERR(gpio_desc)); - return; - } - - bxt_gpio_table[gpio_index] = gpio_desc; + soc_opaque_gpio_set_value(connector, gpio_index, "INT33FF:01", "Panel N", + gpio_index - CHV_GPIO_IDX_START_N, value); } - - gpiod_set_value(gpio_desc, value); } -static void icl_exec_gpio(struct intel_connector *connector, - u8 gpio_source, u8 gpio_index, bool value) +static void bxt_gpio_set_value(struct intel_connector *connector, + u8 gpio_index, bool value) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); - - drm_dbg_kms(&dev_priv->drm, "Skipping ICL GPIO element execution\n"); + soc_gpio_set_value(connector, gpio_index, NULL, gpio_index, value); } enum { @@ -462,44 +389,45 @@ static void icl_native_gpio_set_value(struct drm_i915_private *dev_priv, static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data) { struct drm_device *dev = intel_dsi->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_private *i915 = to_i915(dev); struct intel_connector *connector = intel_dsi->attached_connector; - u8 gpio_source, gpio_index = 0, gpio_number; + u8 gpio_source = 0, gpio_index = 0, gpio_number; bool value; - bool native = DISPLAY_VER(dev_priv) >= 11; + int size; + bool native = DISPLAY_VER(i915) >= 11; - if (connector->panel.vbt.dsi.seq_version >= 3) - gpio_index = *data++; + if (connector->panel.vbt.dsi.seq_version >= 3) { + size = 3; - gpio_number = *data++; + gpio_index = data[0]; + gpio_number = data[1]; + value = data[2] & BIT(0); - /* gpio source in sequence v2 only */ - if (connector->panel.vbt.dsi.seq_version == 2) - gpio_source = (*data >> 1) & 3; - else - gpio_source = 0; + if (connector->panel.vbt.dsi.seq_version >= 4 && data[2] & BIT(1)) + native = false; + } else { + size = 2; - if (connector->panel.vbt.dsi.seq_version >= 4 && *data & BIT(1)) - native = false; + gpio_number = data[0]; + value = data[1] & BIT(0); - /* pull up/down */ - value = *data++ & 1; + if (connector->panel.vbt.dsi.seq_version == 2) + gpio_source = (data[1] >> 1) & 3; + } - drm_dbg_kms(&dev_priv->drm, "GPIO index %u, number %u, source %u, native %s, set to %s\n", + drm_dbg_kms(&i915->drm, "GPIO index %u, number %u, source %u, native %s, set to %s\n", gpio_index, gpio_number, gpio_source, str_yes_no(native), str_on_off(value)); if (native) - icl_native_gpio_set_value(dev_priv, gpio_number, value); - else if (DISPLAY_VER(dev_priv) >= 11) - icl_exec_gpio(connector, gpio_source, gpio_index, value); - else if (IS_VALLEYVIEW(dev_priv)) - vlv_exec_gpio(connector, gpio_source, gpio_number, value); - else if (IS_CHERRYVIEW(dev_priv)) - chv_exec_gpio(connector, gpio_source, gpio_number, value); - else - bxt_exec_gpio(connector, gpio_source, gpio_index, value); - - return data; + icl_native_gpio_set_value(i915, gpio_number, value); + else if (DISPLAY_VER(i915) >= 9) + bxt_gpio_set_value(connector, gpio_index, value); + else if (IS_VALLEYVIEW(i915)) + vlv_gpio_set_value(connector, gpio_source, gpio_number, value); + else if (IS_CHERRYVIEW(i915)) + chv_gpio_set_value(connector, gpio_source, gpio_number, value); + + return data + size; } #ifdef CONFIG_ACPI @@ -658,6 +586,7 @@ static const fn_mipi_elem_exec exec_elem[] = { */ static const char * const seq_name[] = { + [MIPI_SEQ_END] = "MIPI_SEQ_END", [MIPI_SEQ_DEASSERT_RESET] = "MIPI_SEQ_DEASSERT_RESET", [MIPI_SEQ_INIT_OTP] = "MIPI_SEQ_INIT_OTP", [MIPI_SEQ_DISPLAY_ON] = "MIPI_SEQ_DISPLAY_ON", @@ -673,10 +602,10 @@ static const char * const seq_name[] = { static const char *sequence_name(enum mipi_seq seq_id) { - if (seq_id < ARRAY_SIZE(seq_name) && seq_name[seq_id]) + if (seq_id < ARRAY_SIZE(seq_name)) return seq_name[seq_id]; - else - return "(unknown)"; + + return "(unknown)"; } static void intel_dsi_vbt_exec(struct intel_dsi *intel_dsi, @@ -707,13 +636,10 @@ static void intel_dsi_vbt_exec(struct intel_dsi *intel_dsi, if (connector->panel.vbt.dsi.seq_version >= 3) data += 4; - while (1) { + while (*data != MIPI_SEQ_ELEM_END) { u8 operation_byte = *data++; u8 operation_size = 0; - if (operation_byte == MIPI_SEQ_ELEM_END) - break; - if (operation_byte < ARRAY_SIZE(exec_elem)) mipi_elem_exec = exec_elem[operation_byte]; else @@ -873,36 +799,34 @@ bool intel_dsi_vbt_init(struct intel_dsi *intel_dsi, u16 panel_id) * multiply by 100 to preserve remainder */ if (intel_dsi->video_mode == BURST_MODE) { - if (mipi_config->target_burst_mode_freq) { - u32 bitrate = intel_dsi_bitrate(intel_dsi); - - /* - * Sometimes the VBT contains a slightly lower clock, - * then the bitrate we have calculated, in this case - * just replace it with the calculated bitrate. - */ - if (mipi_config->target_burst_mode_freq < bitrate && - intel_fuzzy_clock_check( - mipi_config->target_burst_mode_freq, - bitrate)) - mipi_config->target_burst_mode_freq = bitrate; - - if (mipi_config->target_burst_mode_freq < bitrate) { - drm_err(&dev_priv->drm, - "Burst mode freq is less than computed\n"); - return false; - } + u32 bitrate; - burst_mode_ratio = DIV_ROUND_UP( - mipi_config->target_burst_mode_freq * 100, - bitrate); + if (mipi_config->target_burst_mode_freq == 0) { + drm_err(&dev_priv->drm, "Burst mode target is not set\n"); + return false; + } - intel_dsi->pclk = DIV_ROUND_UP(intel_dsi->pclk * burst_mode_ratio, 100); - } else { - drm_err(&dev_priv->drm, - "Burst mode target is not set\n"); + bitrate = intel_dsi_bitrate(intel_dsi); + + /* + * Sometimes the VBT contains a slightly lower clock, then + * the bitrate we have calculated, in this case just replace it + * with the calculated bitrate. + */ + if (mipi_config->target_burst_mode_freq < bitrate && + intel_fuzzy_clock_check(mipi_config->target_burst_mode_freq, + bitrate)) + mipi_config->target_burst_mode_freq = bitrate; + + if (mipi_config->target_burst_mode_freq < bitrate) { + drm_err(&dev_priv->drm, "Burst mode freq is less than computed\n"); return false; } + + burst_mode_ratio = + DIV_ROUND_UP(mipi_config->target_burst_mode_freq * 100, bitrate); + + intel_dsi->pclk = DIV_ROUND_UP(intel_dsi->pclk * burst_mode_ratio, 100); } else burst_mode_ratio = 100; @@ -964,6 +888,7 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on) struct intel_connector *connector = intel_dsi->attached_connector; struct mipi_config *mipi_config = connector->panel.vbt.dsi.config; enum gpiod_flags flags = panel_is_on ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + struct gpiod_lookup_table *gpiod_lookup_table = NULL; bool want_backlight_gpio = false; bool want_panel_gpio = false; struct pinctrl *pinctrl; @@ -971,12 +896,12 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on) if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && mipi_config->pwm_blc == PPS_BLC_PMIC) { - gpiod_add_lookup_table(&pmic_panel_gpio_table); + gpiod_lookup_table = &pmic_panel_gpio_table; want_panel_gpio = true; } if (IS_VALLEYVIEW(dev_priv) && mipi_config->pwm_blc == PPS_BLC_SOC) { - gpiod_add_lookup_table(&soc_panel_gpio_table); + gpiod_lookup_table = &soc_panel_gpio_table; want_panel_gpio = true; want_backlight_gpio = true; @@ -993,6 +918,9 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on) "Failed to set pinmux to PWM\n"); } + if (gpiod_lookup_table) + gpiod_add_lookup_table(gpiod_lookup_table); + if (want_panel_gpio) { intel_dsi->gpio_panel = gpiod_get(dev->dev, "panel", flags); if (IS_ERR(intel_dsi->gpio_panel)) { @@ -1011,15 +939,13 @@ void intel_dsi_vbt_gpio_init(struct intel_dsi *intel_dsi, bool panel_is_on) intel_dsi->gpio_backlight = NULL; } } + + if (gpiod_lookup_table) + gpiod_remove_lookup_table(gpiod_lookup_table); } void intel_dsi_vbt_gpio_cleanup(struct intel_dsi *intel_dsi) { - struct drm_device *dev = intel_dsi->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_connector *connector = intel_dsi->attached_connector; - struct mipi_config *mipi_config = connector->panel.vbt.dsi.config; - if (intel_dsi->gpio_panel) { gpiod_put(intel_dsi->gpio_panel); intel_dsi->gpio_panel = NULL; @@ -1029,13 +955,4 @@ void intel_dsi_vbt_gpio_cleanup(struct intel_dsi *intel_dsi) gpiod_put(intel_dsi->gpio_backlight); intel_dsi->gpio_backlight = NULL; } - - if ((IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && - mipi_config->pwm_blc == PPS_BLC_PMIC) - gpiod_remove_lookup_table(&pmic_panel_gpio_table); - - if (IS_VALLEYVIEW(dev_priv) && mipi_config->pwm_blc == PPS_BLC_SOC) { - pinctrl_unregister_mappings(soc_pwm_pinctrl_map); - gpiod_remove_lookup_table(&soc_panel_gpio_table); - } } diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index 19b35ece31f1..6d48aa3af95a 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -764,7 +764,7 @@ bool intel_fb_modifier_uses_dpt(struct drm_i915_private *i915, u64 modifier) bool intel_fb_uses_dpt(const struct drm_framebuffer *fb) { - return fb && to_i915(fb->dev)->params.enable_dpt && + return fb && to_i915(fb->dev)->display.params.enable_dpt && intel_fb_modifier_uses_dpt(to_i915(fb->dev), fb->modifier); } @@ -1930,10 +1930,10 @@ static int intel_user_framebuffer_dirty(struct drm_framebuffer *fb, if (!atomic_read(&front->bits)) return 0; - if (dma_resv_test_signaled(obj->base.resv, dma_resv_usage_rw(false))) + if (dma_resv_test_signaled(intel_bo_to_drm_bo(obj)->resv, dma_resv_usage_rw(false))) goto flush; - ret = dma_resv_get_singleton(obj->base.resv, dma_resv_usage_rw(false), + ret = dma_resv_get_singleton(intel_bo_to_drm_bo(obj)->resv, dma_resv_usage_rw(false), &fence); if (ret || !fence) goto flush; @@ -2093,7 +2093,7 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb, } } - fb->obj[i] = &obj->base; + fb->obj[i] = intel_bo_to_drm_bo(obj); } ret = intel_fill_fb_info(dev_priv, intel_fb); diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 4820d21cc942..63f389a1707d 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -608,6 +608,7 @@ static u32 ivb_dpfc_ctl(struct intel_fbc *fbc) static void ivb_fbc_activate(struct intel_fbc *fbc) { struct drm_i915_private *i915 = fbc->i915; + u32 dpfc_ctl; if (DISPLAY_VER(i915) >= 10) glk_fbc_program_cfb_stride(fbc); @@ -617,8 +618,13 @@ static void ivb_fbc_activate(struct intel_fbc *fbc) if (intel_gt_support_legacy_fencing(to_gt(i915))) snb_fbc_program_fence(fbc); + /* wa_14019417088 Alternative WA*/ + dpfc_ctl = ivb_dpfc_ctl(fbc); + if (DISPLAY_VER(i915) >= 20) + intel_de_write(i915, ILK_DPFC_CONTROL(fbc->id), dpfc_ctl); + intel_de_write(i915, ILK_DPFC_CONTROL(fbc->id), - DPFC_CTL_EN | ivb_dpfc_ctl(fbc)); + DPFC_CTL_EN | dpfc_ctl); } static bool ivb_fbc_is_compressing(struct intel_fbc *fbc) @@ -1022,10 +1028,13 @@ static bool intel_fbc_hw_tracking_covers_screen(const struct intel_plane_state * struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev); unsigned int effective_w, effective_h, max_w, max_h; - if (DISPLAY_VER(i915) >= 10) { + if (DISPLAY_VER(i915) >= 11) { + max_w = 8192; + max_h = 4096; + } else if (DISPLAY_VER(i915) >= 10) { max_w = 5120; max_h = 4096; - } else if (DISPLAY_VER(i915) >= 8 || IS_HASWELL(i915)) { + } else if (DISPLAY_VER(i915) >= 7) { max_w = 4096; max_h = 4096; } else if (IS_G4X(i915) || DISPLAY_VER(i915) >= 5) { @@ -1044,6 +1053,31 @@ static bool intel_fbc_hw_tracking_covers_screen(const struct intel_plane_state * return effective_w <= max_w && effective_h <= max_h; } +static bool intel_fbc_plane_size_valid(const struct intel_plane_state *plane_state) +{ + struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev); + unsigned int w, h, max_w, max_h; + + if (DISPLAY_VER(i915) >= 10) { + max_w = 5120; + max_h = 4096; + } else if (DISPLAY_VER(i915) >= 8 || IS_HASWELL(i915)) { + max_w = 4096; + max_h = 4096; + } else if (IS_G4X(i915) || DISPLAY_VER(i915) >= 5) { + max_w = 4096; + max_h = 2048; + } else { + max_w = 2048; + max_h = 1536; + } + + w = drm_rect_width(&plane_state->uapi.src) >> 16; + h = drm_rect_height(&plane_state->uapi.src) >> 16; + + return w <= max_w && h <= max_h; +} + static bool i8xx_fbc_tiling_valid(const struct intel_plane_state *plane_state) { const struct drm_framebuffer *fb = plane_state->hw.fb; @@ -1174,7 +1208,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, return 0; } - if (!i915->params.enable_fbc) { + if (!i915->display.params.enable_fbc) { plane_state->no_fbc_reason = "disabled per module param or by default"; return 0; } @@ -1241,11 +1275,16 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, return 0; } - if (!intel_fbc_hw_tracking_covers_screen(plane_state)) { + if (!intel_fbc_plane_size_valid(plane_state)) { plane_state->no_fbc_reason = "plane size too big"; return 0; } + if (!intel_fbc_hw_tracking_covers_screen(plane_state)) { + plane_state->no_fbc_reason = "surface size too big"; + return 0; + } + /* * Work around a problem on GEN9+ HW, where enabling FBC on a plane * having a Y offset that isn't divisible by 4 causes FIFO underrun @@ -1751,8 +1790,8 @@ void intel_fbc_handle_fifo_underrun_irq(struct drm_i915_private *i915) */ static int intel_sanitize_fbc_option(struct drm_i915_private *i915) { - if (i915->params.enable_fbc >= 0) - return !!i915->params.enable_fbc; + if (i915->display.params.enable_fbc >= 0) + return !!i915->display.params.enable_fbc; if (!HAS_FBC(i915)) return 0; @@ -1824,9 +1863,9 @@ void intel_fbc_init(struct drm_i915_private *i915) if (need_fbc_vtd_wa(i915)) DISPLAY_RUNTIME_INFO(i915)->fbc_mask = 0; - i915->params.enable_fbc = intel_sanitize_fbc_option(i915); + i915->display.params.enable_fbc = intel_sanitize_fbc_option(i915); drm_dbg_kms(&i915->drm, "Sanitized enable_fbc value: %d\n", - i915->params.enable_fbc); + i915->display.params.enable_fbc); for_each_fbc_id(i915, fbc_id) i915->display.fbc[fbc_id] = intel_fbc_create(i915, fbc_id); diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c index e6429dfebe15..295a0f24ebbf 100644 --- a/drivers/gpu/drm/i915/display/intel_fdi.c +++ b/drivers/gpu/drm/i915/display/intel_fdi.c @@ -10,6 +10,7 @@ #include "intel_crtc.h" #include "intel_ddi.h" #include "intel_de.h" +#include "intel_dp.h" #include "intel_display_types.h" #include "intel_fdi.h" #include "intel_fdi_regs.h" @@ -338,8 +339,11 @@ int ilk_fdi_compute_config(struct intel_crtc *crtc, pipe_config->fdi_lanes = lane; - intel_link_compute_m_n(pipe_config->pipe_bpp, lane, fdi_dotclock, - link_bw, &pipe_config->fdi_m_n, false); + intel_link_compute_m_n(to_bpp_x16(pipe_config->pipe_bpp), + lane, fdi_dotclock, + link_bw, + intel_dp_bw_fec_overhead(false), + &pipe_config->fdi_m_n); return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index ec46716b2f49..2ea37c0414a9 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -265,8 +265,6 @@ static void frontbuffer_release(struct kref *ref) spin_unlock(&intel_bo_to_i915(obj)->display.fb_tracking.lock); i915_active_fini(&front->write); - - i915_gem_object_put(obj); kfree_rcu(front, rcu); } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index c89da3568ebd..39b3f7c0c77c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -923,7 +923,7 @@ static int _intel_hdcp_disable(struct intel_connector *connector) return 0; } -static int _intel_hdcp_enable(struct intel_connector *connector) +static int intel_hdcp1_enable(struct intel_connector *connector) { struct drm_i915_private *i915 = to_i915(connector->base.dev); struct intel_hdcp *hdcp = &connector->hdcp; @@ -1058,7 +1058,7 @@ static int intel_hdcp_check_link(struct intel_connector *connector) goto out; } - ret = _intel_hdcp_enable(connector); + ret = intel_hdcp1_enable(connector); if (ret) { drm_err(&i915->drm, "Failed to enable hdcp (%d)\n", ret); intel_hdcp_update_value(connector, @@ -2324,10 +2324,10 @@ intel_hdcp_set_streams(struct intel_digital_port *dig_port, return 0; } -int intel_hdcp_enable(struct intel_atomic_state *state, - struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config, - const struct drm_connector_state *conn_state) +static int _intel_hdcp_enable(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config, + const struct drm_connector_state *conn_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct intel_connector *connector = @@ -2388,7 +2388,7 @@ int intel_hdcp_enable(struct intel_atomic_state *state, */ if (ret && intel_hdcp_capable(connector) && hdcp->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) { - ret = _intel_hdcp_enable(connector); + ret = intel_hdcp1_enable(connector); } if (!ret) { @@ -2404,6 +2404,27 @@ int intel_hdcp_enable(struct intel_atomic_state *state, return ret; } +void intel_hdcp_enable(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_connector *connector = + to_intel_connector(conn_state->connector); + struct intel_hdcp *hdcp = &connector->hdcp; + + /* + * Enable hdcp if it's desired or if userspace is enabled and + * driver set its state to undesired + */ + if (conn_state->content_protection == + DRM_MODE_CONTENT_PROTECTION_DESIRED || + (conn_state->content_protection == + DRM_MODE_CONTENT_PROTECTION_ENABLED && hdcp->value == + DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) + _intel_hdcp_enable(state, encoder, crtc_state, conn_state); +} + int intel_hdcp_disable(struct intel_connector *connector) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); @@ -2491,7 +2512,7 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, } if (desired_and_not_enabled || content_protection_type_changed) - intel_hdcp_enable(state, encoder, crtc_state, conn_state); + _intel_hdcp_enable(state, encoder, crtc_state, conn_state); } void intel_hdcp_component_fini(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index 5997c52a0958..a9c784fd9ba5 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -28,10 +28,10 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, int intel_hdcp_init(struct intel_connector *connector, struct intel_digital_port *dig_port, const struct intel_hdcp_shim *hdcp_shim); -int intel_hdcp_enable(struct intel_atomic_state *state, - struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config, - const struct drm_connector_state *conn_state); +void intel_hdcp_enable(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config, + const struct drm_connector_state *conn_state); int intel_hdcp_disable(struct intel_connector *connector); void intel_hdcp_update_pipe(struct intel_atomic_state *state, struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index ac315f8e7820..ab18cfc19c0a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -3030,16 +3030,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *dig_port, "HDCP init failed, skipping.\n"); } - /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written - * 0xd. Failure to do so will result in spurious interrupts being - * generated on the port when a cable is not attached. - */ - if (IS_G45(dev_priv)) { - u32 temp = intel_de_read(dev_priv, PEG_BAND_GAP_DATA); - intel_de_write(dev_priv, PEG_BAND_GAP_DATA, - (temp & ~0xf) | 0xd); - } - cec_fill_conn_info_from_drm(&conn_info, connector); intel_hdmi->cec_notifier = diff --git a/drivers/gpu/drm/i915/display/intel_hotplug_irq.c b/drivers/gpu/drm/i915/display/intel_hotplug_irq.c index f07047e9cb30..04f62f27ad74 100644 --- a/drivers/gpu/drm/i915/display/intel_hotplug_irq.c +++ b/drivers/gpu/drm/i915/display/intel_hotplug_irq.c @@ -1361,11 +1361,24 @@ static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) bxt_hpd_detection_setup(dev_priv); } +static void g45_hpd_peg_band_gap_wa(struct drm_i915_private *i915) +{ + /* + * For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written + * 0xd. Failure to do so will result in spurious interrupts being + * generated on the port when a cable is not attached. + */ + intel_de_rmw(i915, PEG_BAND_GAP_DATA, 0xf, 0xd); +} + static void i915_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); u32 hotplug_en = hpd_mask_i915[encoder->hpd_pin]; + if (IS_G45(i915)) + g45_hpd_peg_band_gap_wa(i915); + /* HPD sense and interrupt enable are one and the same */ i915_hotplug_interrupt_update(i915, hotplug_en, hotplug_en); } @@ -1389,6 +1402,9 @@ static void i915_hpd_irq_setup(struct drm_i915_private *dev_priv) hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; + if (IS_G45(dev_priv)) + g45_hpd_peg_band_gap_wa(dev_priv); + /* Ignore TV since it's buggy */ i915_hotplug_interrupt_update_locked(dev_priv, HOTPLUG_INT_EN_MASK | diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.c b/drivers/gpu/drm/i915/display/intel_link_bw.c index c5eb5f242536..9c6d35a405a1 100644 --- a/drivers/gpu/drm/i915/display/intel_link_bw.c +++ b/drivers/gpu/drm/i915/display/intel_link_bw.c @@ -7,6 +7,7 @@ #include "intel_atomic.h" #include "intel_display_types.h" +#include "intel_dp_mst.h" #include "intel_fdi.h" #include "intel_link_bw.h" @@ -21,6 +22,7 @@ void intel_link_bw_init_limits(struct drm_i915_private *i915, struct intel_link_ { enum pipe pipe; + limits->force_fec_pipes = 0; limits->bpp_limit_reached_pipes = 0; for_each_pipe(i915, pipe) limits->max_bpp_x16[pipe] = INT_MAX; @@ -53,11 +55,11 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, struct drm_i915_private *i915 = to_i915(state->base.dev); enum pipe max_bpp_pipe = INVALID_PIPE; struct intel_crtc *crtc; - int max_bpp = 0; + int max_bpp_x16 = 0; for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, pipe_mask) { struct intel_crtc_state *crtc_state; - int link_bpp; + int link_bpp_x16; if (limits->bpp_limit_reached_pipes & BIT(crtc->pipe)) continue; @@ -68,7 +70,7 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, return PTR_ERR(crtc_state); if (crtc_state->dsc.compression_enable) - link_bpp = crtc_state->dsc.compressed_bpp; + link_bpp_x16 = crtc_state->dsc.compressed_bpp_x16; else /* * TODO: for YUV420 the actual link bpp is only half @@ -76,10 +78,10 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, * is based on the pipe bpp value, set the actual link bpp * limit here once the MST BW allocation is fixed. */ - link_bpp = crtc_state->pipe_bpp; + link_bpp_x16 = to_bpp_x16(crtc_state->pipe_bpp); - if (link_bpp > max_bpp) { - max_bpp = link_bpp; + if (link_bpp_x16 > max_bpp_x16) { + max_bpp_x16 = link_bpp_x16; max_bpp_pipe = crtc->pipe; } } @@ -87,7 +89,7 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, if (max_bpp_pipe == INVALID_PIPE) return -ENOSPC; - limits->max_bpp_x16[max_bpp_pipe] = to_bpp_x16(max_bpp) - 1; + limits->max_bpp_x16[max_bpp_pipe] = max_bpp_x16 - 1; return intel_modeset_pipes_in_mask_early(state, reason, BIT(max_bpp_pipe)); @@ -143,6 +145,10 @@ static int check_all_link_config(struct intel_atomic_state *state, /* TODO: Check additional shared display link configurations like MST */ int ret; + ret = intel_dp_mst_atomic_check_link(state, limits); + if (ret) + return ret; + ret = intel_fdi_atomic_check_link(state, limits); if (ret) return ret; @@ -158,6 +164,12 @@ assert_link_limit_change_valid(struct drm_i915_private *i915, bool bpps_changed = false; enum pipe pipe; + /* FEC can't be forced off after it was forced on. */ + if (drm_WARN_ON(&i915->drm, + (old_limits->force_fec_pipes & new_limits->force_fec_pipes) != + old_limits->force_fec_pipes)) + return false; + for_each_pipe(i915, pipe) { /* The bpp limit can only decrease. */ if (drm_WARN_ON(&i915->drm, @@ -172,7 +184,9 @@ assert_link_limit_change_valid(struct drm_i915_private *i915, /* At least one limit must change. */ if (drm_WARN_ON(&i915->drm, - !bpps_changed)) + !bpps_changed && + new_limits->force_fec_pipes == + old_limits->force_fec_pipes)) return false; return true; diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.h b/drivers/gpu/drm/i915/display/intel_link_bw.h index e07df22a779a..2cf57307cc24 100644 --- a/drivers/gpu/drm/i915/display/intel_link_bw.h +++ b/drivers/gpu/drm/i915/display/intel_link_bw.h @@ -16,6 +16,7 @@ struct intel_atomic_state; struct intel_crtc_state; struct intel_link_bw_limits { + u8 force_fec_pipes; u8 bpp_limit_reached_pipes; /* in 1/16 bpp units */ int max_bpp_x16[I915_MAX_PIPES]; diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index 2a4ca7e65775..4b114fde57b1 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -794,8 +794,8 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) unsigned int val; /* use the module option value if specified */ - if (i915->params.lvds_channel_mode > 0) - return i915->params.lvds_channel_mode == 2; + if (i915->display.params.lvds_channel_mode > 0) + return i915->display.params.lvds_channel_mode == 2; /* single channel LVDS is limited to 112 MHz */ if (fixed_mode->clock > 112999) diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c index b8f43efb0ab5..94eece7f63be 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c @@ -318,6 +318,12 @@ static void intel_modeset_update_connector_atomic_state(struct drm_i915_private const struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); + if (crtc_state->dsc.compression_enable) { + drm_WARN_ON(&i915->drm, !connector->dp.dsc_decompression_aux); + connector->dp.dsc_decompression_enabled = true; + } else { + connector->dp.dsc_decompression_enabled = false; + } conn_state->max_bpc = (crtc_state->pipe_bpp ?: 24) / 3; } } diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 5e1c2c780412..076298a8d405 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -244,7 +244,7 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state, verify_crtc_state(state, crtc); intel_shared_dpll_state_verify(state, crtc); intel_mpllb_state_verify(state, crtc); - intel_c10pll_state_verify(state, crtc); + intel_cx0pll_state_verify(state, crtc); } void intel_modeset_verify_disabled(struct intel_atomic_state *state) diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c index 84078fb82b2f..1ce785db6a5e 100644 --- a/drivers/gpu/drm/i915/display/intel_opregion.c +++ b/drivers/gpu/drm/i915/display/intel_opregion.c @@ -841,7 +841,7 @@ static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv) { struct intel_opregion *opregion = &dev_priv->display.opregion; const struct firmware *fw = NULL; - const char *name = dev_priv->params.vbt_firmware; + const char *name = dev_priv->display.params.vbt_firmware; int ret; if (!name || !*name) diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 483beedac5b8..0d8e5320a4f8 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -46,8 +46,8 @@ bool intel_panel_use_ssc(struct drm_i915_private *i915) { - if (i915->params.panel_use_ssc >= 0) - return i915->params.panel_use_ssc != 0; + if (i915->display.params.panel_use_ssc >= 0) + return i915->display.params.panel_use_ssc != 0; return i915->display.vbt.lvds_use_ssc && !intel_has_quirk(i915, QUIRK_LVDS_SSC_DISABLE); } diff --git a/drivers/gpu/drm/i915/display/intel_pch_display.c b/drivers/gpu/drm/i915/display/intel_pch_display.c index 866786e6b32f..baf679759e00 100644 --- a/drivers/gpu/drm/i915/display/intel_pch_display.c +++ b/drivers/gpu/drm/i915/display/intel_pch_display.c @@ -8,6 +8,7 @@ #include "intel_crt.h" #include "intel_de.h" #include "intel_display_types.h" +#include "intel_dpll.h" #include "intel_fdi.h" #include "intel_fdi_regs.h" #include "intel_lvds.h" diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c index 73f0f1714b37..a8fa3a20990e 100644 --- a/drivers/gpu/drm/i915/display/intel_pps.c +++ b/drivers/gpu/drm/i915/display/intel_pps.c @@ -90,7 +90,7 @@ vlv_power_sequencer_kick(struct intel_dp *intel_dp) struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); enum pipe pipe = intel_dp->pps.pps_pipe; bool pll_enabled, release_cl_override = false; - enum dpio_phy phy = DPIO_PHY(pipe); + enum dpio_phy phy = vlv_pipe_to_phy(pipe); enum dpio_channel ch = vlv_pipe_to_channel(pipe); u32 DP; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 4f1f31fc9529..15c1804dcd59 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -29,6 +29,7 @@ #include "i915_reg.h" #include "intel_atomic.h" #include "intel_crtc.h" +#include "intel_ddi.h" #include "intel_de.h" #include "intel_display_types.h" #include "intel_dp.h" @@ -172,6 +173,15 @@ * irrelevant for normal operation. */ +bool intel_encoder_can_psr(struct intel_encoder *encoder) +{ + if (intel_encoder_is_dp(encoder) || encoder->type == INTEL_OUTPUT_DP_MST) + return CAN_PSR(enc_to_intel_dp(encoder)) || + CAN_PANEL_REPLAY(enc_to_intel_dp(encoder)); + else + return false; +} + static bool psr_global_enabled(struct intel_dp *intel_dp) { struct intel_connector *connector = intel_dp->attached_connector; @@ -179,9 +189,9 @@ static bool psr_global_enabled(struct intel_dp *intel_dp) switch (intel_dp->psr.debug & I915_PSR_DEBUG_MODE_MASK) { case I915_PSR_DEBUG_DEFAULT: - if (i915->params.enable_psr == -1) + if (i915->display.params.enable_psr == -1) return connector->panel.vbt.psr.enable; - return i915->params.enable_psr; + return i915->display.params.enable_psr; case I915_PSR_DEBUG_DISABLE: return false; default: @@ -198,7 +208,7 @@ static bool psr2_global_enabled(struct intel_dp *intel_dp) case I915_PSR_DEBUG_FORCE_PSR1: return false; default: - if (i915->params.enable_psr == 1) + if (i915->display.params.enable_psr == 1) return false; return true; } @@ -474,27 +484,41 @@ exit: intel_dp->psr.su_y_granularity = y; } -void intel_psr_init_dpcd(struct intel_dp *intel_dp) +static void _panel_replay_init_dpcd(struct intel_dp *intel_dp) { - struct drm_i915_private *dev_priv = - to_i915(dp_to_dig_port(intel_dp)->base.base.dev); + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + u8 pr_dpcd = 0; - drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, intel_dp->psr_dpcd, - sizeof(intel_dp->psr_dpcd)); + intel_dp->psr.sink_panel_replay_support = false; + drm_dp_dpcd_readb(&intel_dp->aux, DP_PANEL_REPLAY_CAP, &pr_dpcd); - if (!intel_dp->psr_dpcd[0]) + if (!(pr_dpcd & DP_PANEL_REPLAY_SUPPORT)) { + drm_dbg_kms(&i915->drm, + "Panel replay is not supported by panel\n"); return; - drm_dbg_kms(&dev_priv->drm, "eDP panel supports PSR version %x\n", + } + + drm_dbg_kms(&i915->drm, + "Panel replay is supported by panel\n"); + intel_dp->psr.sink_panel_replay_support = true; +} + +static void _psr_init_dpcd(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = + to_i915(dp_to_dig_port(intel_dp)->base.base.dev); + + drm_dbg_kms(&i915->drm, "eDP panel supports PSR version %x\n", intel_dp->psr_dpcd[0]); if (drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_NO_PSR)) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(&i915->drm, "PSR support not currently available for this panel\n"); return; } if (!(intel_dp->edp_dpcd[1] & DP_EDP_SET_POWER_CAP)) { - drm_dbg_kms(&dev_priv->drm, + drm_dbg_kms(&i915->drm, "Panel lacks power state control, PSR cannot be enabled\n"); return; } @@ -503,8 +527,8 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp) intel_dp->psr.sink_sync_latency = intel_dp_get_sink_sync_latency(intel_dp); - if (DISPLAY_VER(dev_priv) >= 9 && - (intel_dp->psr_dpcd[0] == DP_PSR2_WITH_Y_COORD_IS_SUPPORTED)) { + if (DISPLAY_VER(i915) >= 9 && + intel_dp->psr_dpcd[0] == DP_PSR2_WITH_Y_COORD_IS_SUPPORTED) { bool y_req = intel_dp->psr_dpcd[1] & DP_PSR2_SU_Y_COORDINATE_REQUIRED; bool alpm = intel_dp_get_alpm_status(intel_dp); @@ -521,14 +545,25 @@ void intel_psr_init_dpcd(struct intel_dp *intel_dp) * GTC first. */ intel_dp->psr.sink_psr2_support = y_req && alpm; - drm_dbg_kms(&dev_priv->drm, "PSR2 %ssupported\n", + drm_dbg_kms(&i915->drm, "PSR2 %ssupported\n", intel_dp->psr.sink_psr2_support ? "" : "not "); + } +} - if (intel_dp->psr.sink_psr2_support) { - intel_dp->psr.colorimetry_support = - intel_dp_get_colorimetry_status(intel_dp); - intel_dp_get_su_granularity(intel_dp); - } +void intel_psr_init_dpcd(struct intel_dp *intel_dp) +{ + _panel_replay_init_dpcd(intel_dp); + + drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT, intel_dp->psr_dpcd, + sizeof(intel_dp->psr_dpcd)); + + if (intel_dp->psr_dpcd[0]) + _psr_init_dpcd(intel_dp); + + if (intel_dp->psr.sink_psr2_support) { + intel_dp->psr.colorimetry_support = + intel_dp_get_colorimetry_status(intel_dp); + intel_dp_get_su_granularity(intel_dp); } } @@ -574,8 +609,11 @@ static void intel_psr_enable_sink(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); u8 dpcd_val = DP_PSR_ENABLE; - /* Enable ALPM at sink for psr2 */ + if (intel_dp->psr.panel_replay_enabled) + return; + if (intel_dp->psr.psr2_enabled) { + /* Enable ALPM at sink for psr2 */ drm_dp_dpcd_writeb(&intel_dp->aux, DP_RECEIVER_ALPM_CONFIG, DP_ALPM_ENABLE | DP_ALPM_LOCK_ERROR_IRQ_HPD_ENABLE); @@ -592,6 +630,9 @@ static void intel_psr_enable_sink(struct intel_dp *intel_dp) if (intel_dp->psr.req_psr2_sdp_prior_scanline) dpcd_val |= DP_PSR_SU_REGION_SCANLINE_CAPTURE; + if (intel_dp->psr.entry_setup_frames > 0) + dpcd_val |= DP_PSR_FRAME_CAPTURE; + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, dpcd_val); drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER, DP_SET_POWER_D0); @@ -606,7 +647,7 @@ static u32 intel_psr1_get_tp_time(struct intel_dp *intel_dp) if (DISPLAY_VER(dev_priv) >= 11) val |= EDP_PSR_TP4_TIME_0us; - if (dev_priv->params.psr_safest_params) { + if (dev_priv->display.params.psr_safest_params) { val |= EDP_PSR_TP1_TIME_2500us; val |= EDP_PSR_TP2_TP3_TIME_2500us; goto check_tp3_sel; @@ -690,6 +731,9 @@ static void hsw_activate_psr1(struct intel_dp *intel_dp) if (DISPLAY_VER(dev_priv) >= 8) val |= EDP_PSR_CRC_ENABLE; + if (DISPLAY_VER(dev_priv) >= 20) + val |= LNL_EDP_PSR_ENTRY_SETUP_FRAMES(intel_dp->psr.entry_setup_frames); + intel_de_rmw(dev_priv, psr_ctl_reg(dev_priv, cpu_transcoder), ~EDP_PSR_RESTORE_PSR_ACTIVE_CTX_MASK, val); } @@ -700,7 +744,7 @@ static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); u32 val = 0; - if (dev_priv->params.psr_safest_params) + if (dev_priv->display.params.psr_safest_params) return EDP_PSR2_TP2_TIME_2500us; if (connector->panel.vbt.psr.psr2_tp2_tp3_wakeup_time_us >= 0 && @@ -727,11 +771,38 @@ static int psr2_block_count(struct intel_dp *intel_dp) return psr2_block_count_lines(intel_dp) / 4; } +static u8 frames_before_su_entry(struct intel_dp *intel_dp) +{ + u8 frames_before_su_entry; + + frames_before_su_entry = max_t(u8, + intel_dp->psr.sink_sync_latency + 1, + 2); + + /* Entry setup frames must be at least 1 less than frames before SU entry */ + if (intel_dp->psr.entry_setup_frames >= frames_before_su_entry) + frames_before_su_entry = intel_dp->psr.entry_setup_frames + 1; + + return frames_before_su_entry; +} + +static void dg2_activate_panel_replay(struct intel_dp *intel_dp) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + + intel_de_rmw(dev_priv, PSR2_MAN_TRK_CTL(intel_dp->psr.transcoder), + 0, ADLP_PSR2_MAN_TRK_CTL_SF_CONTINUOS_FULL_FRAME); + + intel_de_rmw(dev_priv, TRANS_DP2_CTL(intel_dp->psr.transcoder), 0, + TRANS_DP2_PANEL_REPLAY_ENABLE); +} + static void hsw_activate_psr2(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); enum transcoder cpu_transcoder = intel_dp->psr.transcoder; u32 val = EDP_PSR2_ENABLE; + u32 psr_val = 0; val |= EDP_PSR2_IDLE_FRAMES(psr_compute_idle_frames(intel_dp)); @@ -741,7 +812,8 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) if (DISPLAY_VER(dev_priv) >= 10 && DISPLAY_VER(dev_priv) <= 12) val |= EDP_Y_COORDINATE_ENABLE; - val |= EDP_PSR2_FRAME_BEFORE_SU(max_t(u8, intel_dp->psr.sink_sync_latency + 1, 2)); + val |= EDP_PSR2_FRAME_BEFORE_SU(frames_before_su_entry(intel_dp)); + val |= intel_psr2_get_tp_time(intel_dp); if (DISPLAY_VER(dev_priv) >= 12) { @@ -785,6 +857,9 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) if (intel_dp->psr.req_psr2_sdp_prior_scanline) val |= EDP_PSR2_SU_SDP_SCANLINE; + if (DISPLAY_VER(dev_priv) >= 20) + psr_val |= LNL_EDP_PSR_ENTRY_SETUP_FRAMES(intel_dp->psr.entry_setup_frames); + if (intel_dp->psr.psr2_sel_fetch_enabled) { u32 tmp; @@ -798,7 +873,7 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) * PSR2 HW is incorrectly using EDP_PSR_TP1_TP3_SEL and BSpec is * recommending keep this bit unset while PSR2 is enabled. */ - intel_de_write(dev_priv, psr_ctl_reg(dev_priv, cpu_transcoder), 0); + intel_de_write(dev_priv, psr_ctl_reg(dev_priv, cpu_transcoder), psr_val); intel_de_write(dev_priv, EDP_PSR2_CTL(cpu_transcoder), val); } @@ -943,7 +1018,7 @@ static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - if (!dev_priv->params.enable_psr2_sel_fetch && + if (!dev_priv->display.params.enable_psr2_sel_fetch && intel_dp->psr.debug != I915_PSR_DEBUG_ENABLE_SEL_FETCH) { drm_dbg_kms(&dev_priv->drm, "PSR2 sel fetch not enabled, disabled by parameter\n"); @@ -1056,7 +1131,7 @@ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, fast_wake_lines > max_wake_lines) return false; - if (i915->params.psr_safest_params) + if (i915->display.params.psr_safest_params) io_wake_lines = fast_wake_lines = max_wake_lines; /* According to Bspec lower limit should be set as 7 lines. */ @@ -1066,6 +1141,39 @@ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, return true; } +static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, + const struct drm_display_mode *adjusted_mode) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + int psr_setup_time = drm_dp_psr_setup_time(intel_dp->psr_dpcd); + int entry_setup_frames = 0; + + if (psr_setup_time < 0) { + drm_dbg_kms(&i915->drm, + "PSR condition failed: Invalid PSR setup time (0x%02x)\n", + intel_dp->psr_dpcd[1]); + return -ETIME; + } + + if (intel_usecs_to_scanlines(adjusted_mode, psr_setup_time) > + adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vdisplay - 1) { + if (DISPLAY_VER(i915) >= 20) { + /* setup entry frames can be up to 3 frames */ + entry_setup_frames = 1; + drm_dbg_kms(&i915->drm, + "PSR setup entry frames %d\n", + entry_setup_frames); + } else { + drm_dbg_kms(&i915->drm, + "PSR condition failed: PSR setup time (%d us) too long\n", + psr_setup_time); + return -ETIME; + } + } + + return entry_setup_frames; +} + static bool intel_psr2_config_valid(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { @@ -1206,24 +1314,42 @@ unsupported: return false; } -void intel_psr_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +static bool _psr_compute_config(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - const struct drm_display_mode *adjusted_mode = - &crtc_state->hw.adjusted_mode; - int psr_setup_time; + const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + int entry_setup_frames; /* * Current PSR panels don't work reliably with VRR enabled * So if VRR is enabled, do not enable PSR. */ if (crtc_state->vrr.enable) - return; + return false; if (!CAN_PSR(intel_dp)) - return; + return false; + + entry_setup_frames = intel_psr_entry_setup_frames(intel_dp, adjusted_mode); + + if (entry_setup_frames >= 0) { + intel_dp->psr.entry_setup_frames = entry_setup_frames; + } else { + drm_dbg_kms(&dev_priv->drm, + "PSR condition failed: PSR setup timing not met\n"); + return false; + } + + return true; +} + +void intel_psr_compute_config(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; if (!psr_global_enabled(intel_dp)) { drm_dbg_kms(&dev_priv->drm, "PSR disabled by flag\n"); @@ -1242,23 +1368,14 @@ void intel_psr_compute_config(struct intel_dp *intel_dp, return; } - psr_setup_time = drm_dp_psr_setup_time(intel_dp->psr_dpcd); - if (psr_setup_time < 0) { - drm_dbg_kms(&dev_priv->drm, - "PSR condition failed: Invalid PSR setup time (0x%02x)\n", - intel_dp->psr_dpcd[1]); - return; - } + if (CAN_PANEL_REPLAY(intel_dp)) + crtc_state->has_panel_replay = true; + else + crtc_state->has_psr = _psr_compute_config(intel_dp, crtc_state); - if (intel_usecs_to_scanlines(adjusted_mode, psr_setup_time) > - adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vdisplay - 1) { - drm_dbg_kms(&dev_priv->drm, - "PSR condition failed: PSR setup time (%d us) too long\n", - psr_setup_time); + if (!(crtc_state->has_panel_replay || crtc_state->has_psr)) return; - } - crtc_state->has_psr = true; crtc_state->has_psr2 = intel_psr2_config_valid(intel_dp, crtc_state); crtc_state->infoframes.enable |= intel_hdmi_infoframe_enable(DP_SDP_VSC); @@ -1279,18 +1396,23 @@ void intel_psr_get_config(struct intel_encoder *encoder, return; intel_dp = &dig_port->dp; - if (!CAN_PSR(intel_dp)) + if (!(CAN_PSR(intel_dp) || CAN_PANEL_REPLAY(intel_dp))) return; mutex_lock(&intel_dp->psr.lock); if (!intel_dp->psr.enabled) goto unlock; - /* - * Not possible to read EDP_PSR/PSR2_CTL registers as it is - * enabled/disabled because of frontbuffer tracking and others. - */ - pipe_config->has_psr = true; + if (intel_dp->psr.panel_replay_enabled) { + pipe_config->has_panel_replay = true; + } else { + /* + * Not possible to read EDP_PSR/PSR2_CTL registers as it is + * enabled/disabled because of frontbuffer tracking and others. + */ + pipe_config->has_psr = true; + } + pipe_config->has_psr2 = intel_dp->psr.psr2_enabled; pipe_config->infoframes.enable |= intel_hdmi_infoframe_enable(DP_SDP_VSC); @@ -1327,8 +1449,10 @@ static void intel_psr_activate(struct intel_dp *intel_dp) lockdep_assert_held(&intel_dp->psr.lock); - /* psr1 and psr2 are mutually exclusive.*/ - if (intel_dp->psr.psr2_enabled) + /* psr1, psr2 and panel-replay are mutually exclusive.*/ + if (intel_dp->psr.panel_replay_enabled) + dg2_activate_panel_replay(intel_dp); + else if (intel_dp->psr.psr2_enabled) hsw_activate_psr2(intel_dp); else hsw_activate_psr1(intel_dp); @@ -1452,12 +1576,10 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, * All supported adlp panels have 1-based X granularity, this may * cause issues if non-supported panels are used. */ - if (IS_DISPLAY_IP_STEP(dev_priv, IP_VER(14, 0), STEP_A0, STEP_B0)) - intel_de_rmw(dev_priv, MTL_CHICKEN_TRANS(cpu_transcoder), 0, - ADLP_1_BASED_X_GRANULARITY); - else if (IS_ALDERLAKE_P(dev_priv)) - intel_de_rmw(dev_priv, CHICKEN_TRANS(cpu_transcoder), 0, - ADLP_1_BASED_X_GRANULARITY); + if (IS_DISPLAY_IP_STEP(dev_priv, IP_VER(14, 0), STEP_A0, STEP_B0) || + IS_ALDERLAKE_P(dev_priv)) + intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, cpu_transcoder), + 0, ADLP_1_BASED_X_GRANULARITY); /* Wa_16012604467:adlp,mtl[a0,b0] */ if (IS_DISPLAY_IP_STEP(dev_priv, IP_VER(14, 0), STEP_A0, STEP_B0)) @@ -1508,6 +1630,7 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, drm_WARN_ON(&dev_priv->drm, intel_dp->psr.enabled); intel_dp->psr.psr2_enabled = crtc_state->has_psr2; + intel_dp->psr.panel_replay_enabled = crtc_state->has_panel_replay; intel_dp->psr.busy_frontbuffer_bits = 0; intel_dp->psr.pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; intel_dp->psr.transcoder = crtc_state->cpu_transcoder; @@ -1523,8 +1646,12 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, if (!psr_interrupt_error_check(intel_dp)) return; - drm_dbg_kms(&dev_priv->drm, "Enabling PSR%s\n", - intel_dp->psr.psr2_enabled ? "2" : "1"); + if (intel_dp->psr.panel_replay_enabled) + drm_dbg_kms(&dev_priv->drm, "Enabling Panel Replay\n"); + else + drm_dbg_kms(&dev_priv->drm, "Enabling PSR%s\n", + intel_dp->psr.psr2_enabled ? "2" : "1"); + intel_write_dp_vsc_sdp(encoder, crtc_state, &crtc_state->psr_vsc); intel_snps_phy_update_psr_power_state(dev_priv, phy, true); intel_psr_enable_sink(intel_dp); @@ -1553,7 +1680,10 @@ static void intel_psr_exit(struct intel_dp *intel_dp) return; } - if (intel_dp->psr.psr2_enabled) { + if (intel_dp->psr.panel_replay_enabled) { + intel_de_rmw(dev_priv, TRANS_DP2_CTL(intel_dp->psr.transcoder), + TRANS_DP2_PANEL_REPLAY_ENABLE, 0); + } else if (intel_dp->psr.psr2_enabled) { tgl_disallow_dc3co_on_psr2_exit(intel_dp); val = intel_de_rmw(dev_priv, EDP_PSR2_CTL(cpu_transcoder), @@ -1602,8 +1732,11 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) if (!intel_dp->psr.enabled) return; - drm_dbg_kms(&dev_priv->drm, "Disabling PSR%s\n", - intel_dp->psr.psr2_enabled ? "2" : "1"); + if (intel_dp->psr.panel_replay_enabled) + drm_dbg_kms(&dev_priv->drm, "Disabling Panel Replay\n"); + else + drm_dbg_kms(&dev_priv->drm, "Disabling PSR%s\n", + intel_dp->psr.psr2_enabled ? "2" : "1"); intel_psr_exit(intel_dp); intel_psr_wait_exit_locked(intel_dp); @@ -1636,6 +1769,7 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) drm_dp_dpcd_writeb(&intel_dp->aux, DP_RECEIVER_ALPM_CONFIG, 0); intel_dp->psr.enabled = false; + intel_dp->psr.panel_replay_enabled = false; intel_dp->psr.psr2_enabled = false; intel_dp->psr.psr2_sel_fetch_enabled = false; intel_dp->psr.psr2_sel_fetch_cff_enabled = false; @@ -2207,7 +2341,7 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder; - if (!crtc_state->has_psr) + if (!(crtc_state->has_psr || crtc_state->has_panel_replay)) return; for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, @@ -2693,9 +2827,12 @@ void intel_psr_init(struct intel_dp *intel_dp) struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); - if (!HAS_PSR(dev_priv)) + if (!(HAS_PSR(dev_priv) || HAS_DP20(dev_priv))) return; + if (!intel_dp_is_edp(intel_dp)) + intel_psr_init_dpcd(intel_dp); + /* * HSW spec explicitly says PSR is tied to port A. * BDW+ platforms have a instance of PSR registers per transcoder but @@ -2711,7 +2848,10 @@ void intel_psr_init(struct intel_dp *intel_dp) return; } - intel_dp->psr.source_support = true; + if (HAS_DP20(dev_priv) && !intel_dp_is_edp(intel_dp)) + intel_dp->psr.source_panel_replay_support = true; + else + intel_dp->psr.source_support = true; /* Set link_standby x link_off defaults */ if (DISPLAY_VER(dev_priv) < 12) @@ -2728,12 +2868,19 @@ static int psr_get_status_and_error_status(struct intel_dp *intel_dp, { struct drm_dp_aux *aux = &intel_dp->aux; int ret; + unsigned int offset; - ret = drm_dp_dpcd_readb(aux, DP_PSR_STATUS, status); + offset = intel_dp->psr.panel_replay_enabled ? + DP_SINK_DEVICE_PR_AND_FRAME_LOCK_STATUS : DP_PSR_STATUS; + + ret = drm_dp_dpcd_readb(aux, offset, status); if (ret != 1) return ret; - ret = drm_dp_dpcd_readb(aux, DP_PSR_ERROR_STATUS, error_status); + offset = intel_dp->psr.panel_replay_enabled ? + DP_PANEL_REPLAY_ERROR_STATUS : DP_PSR_ERROR_STATUS; + + ret = drm_dp_dpcd_readb(aux, offset, error_status); if (ret != 1) return ret; @@ -2954,7 +3101,7 @@ psr_source_status(struct intel_dp *intel_dp, struct seq_file *m) status = live_status[status_val]; } - seq_printf(m, "Source PSR status: %s [0x%08x]\n", status, val); + seq_printf(m, "Source PSR/PanelReplay status: %s [0x%08x]\n", status, val); } static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) @@ -2967,18 +3114,22 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) bool enabled; u32 val; - seq_printf(m, "Sink support: %s", str_yes_no(psr->sink_support)); + seq_printf(m, "Sink support: PSR = %s", + str_yes_no(psr->sink_support)); + if (psr->sink_support) seq_printf(m, " [0x%02x]", intel_dp->psr_dpcd[0]); - seq_puts(m, "\n"); + seq_printf(m, ", Panel Replay = %s\n", str_yes_no(psr->sink_panel_replay_support)); - if (!psr->sink_support) + if (!(psr->sink_support || psr->sink_panel_replay_support)) return 0; wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); mutex_lock(&psr->lock); - if (psr->enabled) + if (psr->panel_replay_enabled) + status = "Panel Replay Enabled"; + else if (psr->enabled) status = psr->psr2_enabled ? "PSR2 enabled" : "PSR1 enabled"; else status = "disabled"; @@ -2991,14 +3142,17 @@ static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp) goto unlock; } - if (psr->psr2_enabled) { + if (psr->panel_replay_enabled) { + val = intel_de_read(dev_priv, TRANS_DP2_CTL(cpu_transcoder)); + enabled = val & TRANS_DP2_PANEL_REPLAY_ENABLE; + } else if (psr->psr2_enabled) { val = intel_de_read(dev_priv, EDP_PSR2_CTL(cpu_transcoder)); enabled = val & EDP_PSR2_ENABLE; } else { val = intel_de_read(dev_priv, psr_ctl_reg(dev_priv, cpu_transcoder)); enabled = val & EDP_PSR_ENABLE; } - seq_printf(m, "Source PSR ctl: %s [0x%08x]\n", + seq_printf(m, "Source PSR/PanelReplay ctl: %s [0x%08x]\n", str_enabled_disabled(enabled), val); psr_source_status(intel_dp, m); seq_printf(m, "Busy frontbuffer bits: 0x%08x\n", @@ -3136,6 +3290,16 @@ void intel_psr_debugfs_register(struct drm_i915_private *i915) i915, &i915_edp_psr_status_fops); } +static const char *psr_mode_str(struct intel_dp *intel_dp) +{ + if (intel_dp->psr.panel_replay_enabled) + return "PANEL-REPLAY"; + else if (intel_dp->psr.enabled) + return "PSR"; + + return "unknown"; +} + static int i915_psr_sink_status_show(struct seq_file *m, void *data) { struct intel_connector *connector = m->private; @@ -3150,12 +3314,19 @@ static int i915_psr_sink_status_show(struct seq_file *m, void *data) "reserved", "sink internal error", }; + static const char * const panel_replay_status[] = { + "Sink device frame is locked to the Source device", + "Sink device is coasting, using the VTotal target", + "Sink device is governing the frame rate (frame rate unlock is granted)", + "Sink device in the process of re-locking with the Source device", + }; const char *str; int ret; u8 status, error_status; + u32 idx; - if (!CAN_PSR(intel_dp)) { - seq_puts(m, "PSR Unsupported\n"); + if (!(CAN_PSR(intel_dp) || CAN_PANEL_REPLAY(intel_dp))) { + seq_puts(m, "PSR/Panel-Replay Unsupported\n"); return -ENODEV; } @@ -3166,15 +3337,20 @@ static int i915_psr_sink_status_show(struct seq_file *m, void *data) if (ret) return ret; - status &= DP_PSR_SINK_STATE_MASK; - if (status < ARRAY_SIZE(sink_status)) - str = sink_status[status]; - else - str = "unknown"; + str = "unknown"; + if (intel_dp->psr.panel_replay_enabled) { + idx = (status & DP_SINK_FRAME_LOCKED_MASK) >> DP_SINK_FRAME_LOCKED_SHIFT; + if (idx < ARRAY_SIZE(panel_replay_status)) + str = panel_replay_status[idx]; + } else if (intel_dp->psr.enabled) { + idx = status & DP_PSR_SINK_STATE_MASK; + if (idx < ARRAY_SIZE(sink_status)) + str = sink_status[idx]; + } - seq_printf(m, "Sink PSR status: 0x%x [%s]\n", status, str); + seq_printf(m, "Sink %s status: 0x%x [%s]\n", psr_mode_str(intel_dp), status, str); - seq_printf(m, "Sink PSR error status: 0x%x", error_status); + seq_printf(m, "Sink %s error status: 0x%x", psr_mode_str(intel_dp), error_status); if (error_status & (DP_PSR_RFB_STORAGE_ERROR | DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR | @@ -3183,11 +3359,11 @@ static int i915_psr_sink_status_show(struct seq_file *m, void *data) else seq_puts(m, "\n"); if (error_status & DP_PSR_RFB_STORAGE_ERROR) - seq_puts(m, "\tPSR RFB storage error\n"); + seq_printf(m, "\t%s RFB storage error\n", psr_mode_str(intel_dp)); if (error_status & DP_PSR_VSC_SDP_UNCORRECTABLE_ERROR) - seq_puts(m, "\tPSR VSC SDP uncorrectable error\n"); + seq_printf(m, "\t%s VSC SDP uncorrectable error\n", psr_mode_str(intel_dp)); if (error_status & DP_PSR_LINK_CRC_ERROR) - seq_puts(m, "\tPSR Link CRC error\n"); + seq_printf(m, "\t%s Link CRC error\n", psr_mode_str(intel_dp)); return ret; } @@ -3207,13 +3383,16 @@ void intel_psr_connector_debugfs_add(struct intel_connector *connector) struct drm_i915_private *i915 = to_i915(connector->base.dev); struct dentry *root = connector->base.debugfs_entry; - if (connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) - return; + if (connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) { + if (!(HAS_DP20(i915) && + connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort)) + return; + } debugfs_create_file("i915_psr_sink_status", 0444, root, connector, &i915_psr_sink_status_fops); - if (HAS_PSR(i915)) + if (HAS_PSR(i915) || HAS_DP20(i915)) debugfs_create_file("i915_psr_status", 0444, root, connector, &i915_psr_status_fops); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index bf35f42df6bc..6a1f4573852b 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -21,6 +21,13 @@ struct intel_encoder; struct intel_plane; struct intel_plane_state; +#define CAN_PSR(intel_dp) ((intel_dp)->psr.sink_support && \ + (intel_dp)->psr.source_support) + +#define CAN_PANEL_REPLAY(intel_dp) ((intel_dp)->psr.sink_panel_replay_support && \ + (intel_dp)->psr.source_panel_replay_support) + +bool intel_encoder_can_psr(struct intel_encoder *encoder); void intel_psr_init_dpcd(struct intel_dp *intel_dp); void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc); diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h index d39951383c92..efe4306b37e0 100644 --- a/drivers/gpu/drm/i915/display/intel_psr_regs.h +++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h @@ -35,6 +35,8 @@ #define EDP_PSR_MIN_LINK_ENTRY_TIME_0_LINES REG_FIELD_PREP(EDP_PSR_MIN_LINK_ENTRY_TIME_MASK, 3) #define EDP_PSR_MAX_SLEEP_TIME_MASK REG_GENMASK(24, 20) #define EDP_PSR_MAX_SLEEP_TIME(x) REG_FIELD_PREP(EDP_PSR_MAX_SLEEP_TIME_MASK, (x)) +#define LNL_EDP_PSR_ENTRY_SETUP_FRAMES_MASK REG_GENMASK(17, 16) +#define LNL_EDP_PSR_ENTRY_SETUP_FRAMES(x) REG_FIELD_PREP(LNL_EDP_PSR_ENTRY_SETUP_FRAMES_MASK, (x)) #define EDP_PSR_SKIP_AUX_EXIT REG_BIT(12) #define EDP_PSR_TP_MASK REG_BIT(11) #define EDP_PSR_TP_TP1_TP2 REG_FIELD_PREP(EDP_PSR_TP_MASK, 0) diff --git a/drivers/gpu/drm/i915/display/intel_qp_tables.c b/drivers/gpu/drm/i915/display/intel_qp_tables.c index 543cdc46aa1d..600c815e37e4 100644 --- a/drivers/gpu/drm/i915/display/intel_qp_tables.c +++ b/drivers/gpu/drm/i915/display/intel_qp_tables.c @@ -34,9 +34,6 @@ * These qp tables are as per the C model * and it has the rows pointing to bpps which increment * in steps of 0.5 - * We do not support fractional bpps as of today, - * hence we would skip the fractional bpps during - * our references for qp calclulations. */ static const u8 rc_range_minqp444_8bpc[DSC_NUM_BUF_RANGES][RC_RANGE_QP444_8BPC_MAX_NUM_BPP] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index a636f42ceae5..bcb4959df70d 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -35,6 +35,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include "i915_drv.h" #include "i915_reg.h" @@ -1787,17 +1788,28 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, intel_sdvo_get_eld(intel_sdvo, pipe_config); } -static void intel_sdvo_disable_audio(struct intel_sdvo *intel_sdvo) +static void intel_sdvo_disable_audio(struct intel_encoder *encoder, + const struct intel_crtc_state *old_crtc_state, + const struct drm_connector_state *old_conn_state) { + struct intel_sdvo *intel_sdvo = to_sdvo(encoder); + + if (!old_crtc_state->has_audio) + return; + intel_sdvo_set_audio_state(intel_sdvo, 0); } -static void intel_sdvo_enable_audio(struct intel_sdvo *intel_sdvo, +static void intel_sdvo_enable_audio(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state) { + struct intel_sdvo *intel_sdvo = to_sdvo(encoder); const u8 *eld = crtc_state->eld; + if (!crtc_state->has_audio) + return; + intel_sdvo_set_audio_state(intel_sdvo, 0); intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_ELD, @@ -1818,8 +1830,7 @@ static void intel_disable_sdvo(struct intel_atomic_state *state, struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); u32 temp; - if (old_crtc_state->has_audio) - intel_sdvo_disable_audio(intel_sdvo); + encoder->audio_disable(encoder, old_crtc_state, conn_state); intel_sdvo_set_active_outputs(intel_sdvo, 0); if (0) @@ -1913,8 +1924,7 @@ static void intel_enable_sdvo(struct intel_atomic_state *state, DRM_MODE_DPMS_ON); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo_connector->output_flag); - if (pipe_config->has_audio) - intel_sdvo_enable_audio(intel_sdvo, pipe_config, conn_state); + encoder->audio_enable(encoder, pipe_config, conn_state); } static enum drm_mode_status @@ -3390,6 +3400,8 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv, } intel_encoder->pre_enable = intel_sdvo_pre_enable; intel_encoder->enable = intel_enable_sdvo; + intel_encoder->audio_enable = intel_sdvo_enable_audio; + intel_encoder->audio_disable = intel_sdvo_disable_audio; intel_encoder->get_hw_state = intel_sdvo_get_hw_state; intel_encoder->get_config = intel_sdvo_get_config; diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index 1fb16510f750..d7b440c8caef 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -48,6 +48,11 @@ #include "intel_frontbuffer.h" #include "intel_sprite.h" +static char sprite_name(struct drm_i915_private *i915, enum pipe pipe, int sprite) +{ + return pipe * DISPLAY_RUNTIME_INFO(i915)->num_sprites[pipe] + sprite + 'A'; +} + static void i9xx_plane_linear_gamma(u16 gamma[8]) { /* The points are not evenly spaced. */ @@ -1636,7 +1641,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, 0, plane_funcs, formats, num_formats, modifiers, DRM_PLANE_TYPE_OVERLAY, - "sprite %c", sprite_name(pipe, sprite)); + "sprite %c", sprite_name(dev_priv, pipe, sprite)); kfree(modifiers); if (ret) diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index 6757dbae9ee5..5f2fb702e367 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -77,8 +77,8 @@ intel_vdsc_set_min_max_qp(struct drm_dsc_config *vdsc_cfg, int buf, static void calculate_rc_params(struct drm_dsc_config *vdsc_cfg) { + int bpp = to_bpp_int(vdsc_cfg->bits_per_pixel); int bpc = vdsc_cfg->bits_per_component; - int bpp = vdsc_cfg->bits_per_pixel >> 4; int qp_bpc_modifier = (bpc - 8) * 2; int uncompressed_bpg_rate; int first_line_bpg_offset; @@ -148,7 +148,13 @@ calculate_rc_params(struct drm_dsc_config *vdsc_cfg) static const s8 ofs_und8[] = { 10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12 }; - + /* + * For 420 format since bits_per_pixel (bpp) is set to target bpp * 2, + * QP table values for target bpp 4.0 to 4.4375 (rounded to 4.0) are + * actually for bpp 8 to 8.875 (rounded to 4.0 * 2 i.e 8). + * Similarly values for target bpp 4.5 to 4.8375 (rounded to 4.5) + * are for bpp 9 to 9.875 (rounded to 4.5 * 2 i.e 9), and so on. + */ bpp_i = bpp - 8; for (buf_i = 0; buf_i < DSC_NUM_BUF_RANGES; buf_i++) { u8 range_bpg_offset; @@ -178,6 +184,9 @@ calculate_rc_params(struct drm_dsc_config *vdsc_cfg) range_bpg_offset & DSC_RANGE_BPG_OFFSET_MASK; } } else { + /* fractional bpp part * 10000 (for precision up to 4 decimal places) */ + int fractional_bits = to_bpp_frac(vdsc_cfg->bits_per_pixel); + static const s8 ofs_und6[] = { 0, -2, -2, -4, -6, -6, -8, -8, -8, -10, -10, -12, -12, -12, -12 }; @@ -191,7 +200,14 @@ calculate_rc_params(struct drm_dsc_config *vdsc_cfg) 10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10, -10, -12, -12, -12 }; - bpp_i = (2 * (bpp - 6)); + /* + * QP table rows have values in increment of 0.5. + * So 6.0 bpp to 6.4375 will have index 0, 6.5 to 6.9375 will have index 1, + * and so on. + * 0.5 fractional part with 4 decimal precision becomes 5000 + */ + bpp_i = ((bpp - 6) + (fractional_bits < 5000 ? 0 : 1)); + for (buf_i = 0; buf_i < DSC_NUM_BUF_RANGES; buf_i++) { u8 range_bpg_offset; @@ -248,7 +264,7 @@ int intel_dsc_compute_params(struct intel_crtc_state *pipe_config) struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); struct drm_dsc_config *vdsc_cfg = &pipe_config->dsc.config; - u16 compressed_bpp = pipe_config->dsc.compressed_bpp; + u16 compressed_bpp = to_bpp_int(pipe_config->dsc.compressed_bpp_x16); int err; int ret; @@ -279,8 +295,7 @@ int intel_dsc_compute_params(struct intel_crtc_state *pipe_config) /* Gen 11 does not support VBR */ vdsc_cfg->vbr_enable = false; - /* Gen 11 only supports integral values of bpp */ - vdsc_cfg->bits_per_pixel = compressed_bpp << 4; + vdsc_cfg->bits_per_pixel = pipe_config->dsc.compressed_bpp_x16; /* * According to DSC 1.2 specs in Section 4.1 if native_420 is set @@ -874,7 +889,7 @@ static void intel_dsc_get_pps_config(struct intel_crtc_state *crtc_state) if (vdsc_cfg->native_420) vdsc_cfg->bits_per_pixel >>= 1; - crtc_state->dsc.compressed_bpp = vdsc_cfg->bits_per_pixel >> 4; + crtc_state->dsc.compressed_bpp_x16 = vdsc_cfg->bits_per_pixel; /* PPS 2 */ pps_temp = intel_dsc_pps_read_and_verify(crtc_state, 2); diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 245a64332cc7..f5c77a018e10 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -21,7 +21,6 @@ #include "skl_scaler.h" #include "skl_universal_plane.h" #include "skl_watermark.h" -#include "gt/intel_gt.h" #include "pxp/intel_pxp.h" static const u32 skl_plane_formats[] = { @@ -1007,7 +1006,8 @@ static u32 skl_surf_address(const struct intel_plane_state *plane_state, * The DPT object contains only one vma, so the VMA's offset * within the DPT is always 0. */ - drm_WARN_ON(&i915->drm, plane_state->dpt_vma->node.start); + drm_WARN_ON(&i915->drm, plane_state->dpt_vma && + plane_state->dpt_vma->node.start); drm_WARN_ON(&i915->drm, offset & 0x1fffff); return offset >> 9; } else { @@ -1855,16 +1855,19 @@ static bool skl_fb_scalable(const struct drm_framebuffer *fb) } } -static bool bo_has_valid_encryption(struct drm_i915_gem_object *obj) +static void check_protection(struct intel_plane_state *plane_state) { - struct drm_i915_private *i915 = to_i915(obj->base.dev); + struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); + struct drm_i915_private *i915 = to_i915(plane->base.dev); + const struct drm_framebuffer *fb = plane_state->hw.fb; + struct drm_i915_gem_object *obj = intel_fb_obj(fb); - return intel_pxp_key_check(i915->pxp, obj, false) == 0; -} + if (DISPLAY_VER(i915) < 11) + return; -static bool pxp_is_borked(struct drm_i915_gem_object *obj) -{ - return i915_gem_object_is_protected(obj) && !bo_has_valid_encryption(obj); + plane_state->decrypt = intel_pxp_key_check(i915->pxp, obj, false) == 0; + plane_state->force_black = i915_gem_object_is_protected(obj) && + !plane_state->decrypt; } static int skl_plane_check(struct intel_crtc_state *crtc_state, @@ -1911,10 +1914,7 @@ static int skl_plane_check(struct intel_crtc_state *crtc_state, if (ret) return ret; - if (DISPLAY_VER(dev_priv) >= 11) { - plane_state->decrypt = bo_has_valid_encryption(intel_fb_obj(fb)); - plane_state->force_black = pxp_is_borked(intel_fb_obj(fb)); - } + check_protection(plane_state); /* HW only has 8 bits pixel precision, disable plane if invisible */ if (!(plane_state->hw.alpha >> 8)) @@ -2489,7 +2489,7 @@ skl_get_initial_plane_config(struct intel_crtc *crtc, goto error; } - if (!dev_priv->params.enable_dpt && + if (!dev_priv->display.params.enable_dpt && intel_fb_modifier_uses_dpt(dev_priv, fb->modifier)) { drm_dbg_kms(&dev_priv->drm, "DPT disabled, skipping initial FB\n"); goto error; diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 99b8ccdc3dfa..56588d6e24ae 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -412,7 +412,7 @@ static bool intel_crtc_can_enable_sagv(const struct intel_crtc_state *crtc_state struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct drm_i915_private *i915 = to_i915(crtc->base.dev); - if (!i915->params.enable_sagv) + if (!i915->display.params.enable_sagv) return false; if (DISPLAY_VER(i915) >= 12) @@ -3702,7 +3702,8 @@ static int intel_sagv_status_show(struct seq_file *m, void *unused) }; seq_printf(m, "SAGV available: %s\n", str_yes_no(intel_has_sagv(i915))); - seq_printf(m, "SAGV modparam: %s\n", str_enabled_disabled(i915->params.enable_sagv)); + seq_printf(m, "SAGV modparam: %s\n", + str_enabled_disabled(i915->display.params.enable_sagv)); seq_printf(m, "SAGV status: %s\n", sagv_status[i915->display.sagv.status]); seq_printf(m, "SAGV block time: %d usec\n", i915->display.sagv.block_time_us); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 55da627a8b8d..bda49734ca33 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -561,6 +561,12 @@ static void glk_dsi_clear_device_ready(struct intel_encoder *encoder) glk_dsi_disable_mipi_io(encoder); } +static i915_reg_t port_ctrl_reg(struct drm_i915_private *i915, enum port port) +{ + return IS_GEMINILAKE(i915) || IS_BROXTON(i915) ? + BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); +} + static void vlv_dsi_clear_device_ready(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); @@ -570,7 +576,7 @@ static void vlv_dsi_clear_device_ready(struct intel_encoder *encoder) drm_dbg_kms(&dev_priv->drm, "\n"); for_each_dsi_port(port, intel_dsi->ports) { /* Common bit for both MIPI Port A & MIPI Port C on VLV/CHV */ - i915_reg_t port_ctrl = IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv) ? + i915_reg_t port_ctrl = IS_BROXTON(dev_priv) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(PORT_A); intel_de_write(dev_priv, MIPI_DEVICE_READY(port), @@ -589,7 +595,7 @@ static void vlv_dsi_clear_device_ready(struct intel_encoder *encoder) * On VLV/CHV, wait till Clock lanes are in LP-00 state for MIPI * Port A only. MIPI Port C has no similar bit for checking. */ - if ((IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv) || port == PORT_A) && + if ((IS_BROXTON(dev_priv) || port == PORT_A) && intel_de_wait_for_clear(dev_priv, port_ctrl, AFE_LATCHOUT, 30)) drm_err(&dev_priv->drm, "DSI LP not going Low\n"); @@ -627,8 +633,7 @@ static void intel_dsi_port_enable(struct intel_encoder *encoder, } for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t port_ctrl = IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv) ? - BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); + i915_reg_t port_ctrl = port_ctrl_reg(dev_priv, port); u32 temp; temp = intel_de_read(dev_priv, port_ctrl); @@ -664,8 +669,7 @@ static void intel_dsi_port_disable(struct intel_encoder *encoder) enum port port; for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t port_ctrl = IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv) ? - BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); + i915_reg_t port_ctrl = port_ctrl_reg(dev_priv, port); /* de-assert ip_tg_enable signal */ intel_de_rmw(dev_priv, port_ctrl, DPI_ENABLE, 0); @@ -955,9 +959,8 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, /* XXX: this only works for one DSI output */ for_each_dsi_port(port, intel_dsi->ports) { - i915_reg_t ctrl_reg = IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv) ? - BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); - bool enabled = intel_de_read(dev_priv, ctrl_reg) & DPI_ENABLE; + i915_reg_t port_ctrl = port_ctrl_reg(dev_priv, port); + bool enabled = intel_de_read(dev_priv, port_ctrl) & DPI_ENABLE; /* * Due to some hardware limitations on VLV/CHV, the DPI enable diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h index e5e870b6f186..9fbf14867a2a 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h @@ -89,6 +89,7 @@ i915_gem_object_set_frontbuffer(struct drm_i915_gem_object *obj, if (!front) { RCU_INIT_POINTER(obj->frontbuffer, NULL); + drm_gem_object_put(intel_bo_to_drm_bo(obj)); } else if (rcu_access_pointer(obj->frontbuffer)) { cur = rcu_dereference_protected(obj->frontbuffer, true); kref_get(&cur->ref); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pm.h b/drivers/gpu/drm/i915/gt/intel_engine_pm.h index d68675925b79..1d97c435a015 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_pm.h +++ b/drivers/gpu/drm/i915/gt/intel_engine_pm.h @@ -10,6 +10,7 @@ #include "i915_request.h" #include "intel_engine_types.h" #include "intel_wakeref.h" +#include "intel_gt.h" #include "intel_gt_pm.h" static inline bool diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c index ed32bf5b1546..ba1186fc524f 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt.c +++ b/drivers/gpu/drm/i915/gt/intel_gt.c @@ -982,8 +982,6 @@ int intel_gt_probe_all(struct drm_i915_private *i915) err: i915_probe_error(i915, "Failed to initialize %s! (%d)\n", gtdef->name, ret); - intel_gt_release_all(i915); - return ret; } @@ -1002,15 +1000,6 @@ int intel_gt_tiles_init(struct drm_i915_private *i915) return 0; } -void intel_gt_release_all(struct drm_i915_private *i915) -{ - struct intel_gt *gt; - unsigned int id; - - for_each_gt(gt, i915, id) - i915->gt[id] = NULL; -} - void intel_gt_info_print(const struct intel_gt_info *info, struct drm_printer *p) { diff --git a/drivers/gpu/drm/i915/gt/intel_gt.h b/drivers/gpu/drm/i915/gt/intel_gt.h index 970bedf6b78a..e1f13735f530 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt.h +++ b/drivers/gpu/drm/i915/gt/intel_gt.h @@ -167,6 +167,20 @@ void intel_gt_release_all(struct drm_i915_private *i915); (id__)++) \ for_each_if(((gt__) = (i915__)->gt[(id__)])) +/* Simple iterator over all initialised engines */ +#define for_each_engine(engine__, gt__, id__) \ + for ((id__) = 0; \ + (id__) < I915_NUM_ENGINES; \ + (id__)++) \ + for_each_if ((engine__) = (gt__)->engine[(id__)]) + +/* Iterator over subset of engines selected by mask */ +#define for_each_engine_masked(engine__, gt__, mask__, tmp__) \ + for ((tmp__) = (mask__) & (gt__)->info.engine_mask; \ + (tmp__) ? \ + ((engine__) = (gt__)->engine[__mask_next_bit(tmp__)]), 1 : \ + 0;) + void intel_gt_info_print(const struct intel_gt_info *info, struct drm_printer *p); diff --git a/drivers/gpu/drm/i915/gt/intel_gt_engines_debugfs.c b/drivers/gpu/drm/i915/gt/intel_gt_engines_debugfs.c index 8f9b874fdc9c..3aa1d014c14d 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_engines_debugfs.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_engines_debugfs.c @@ -6,8 +6,8 @@ #include <drm/drm_print.h> -#include "i915_drv.h" /* for_each_engine! */ #include "intel_engine.h" +#include "intel_gt.h" #include "intel_gt_debugfs.h" #include "intel_gt_engines_debugfs.h" diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 05f9348b7a9d..d4a3f3e093b0 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -3047,7 +3047,7 @@ put_obj: static int combine_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) { - u32 per_ctx_start[CACHELINE_DWORDS] = {0}; + u32 per_ctx_start[CACHELINE_DWORDS] = {}; unsigned char *bb_start_sva; if (!wa_ctx->per_ctx.valid) diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 835c3fde8a20..313efdabee57 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -56,7 +56,7 @@ static const struct pixel_format bdw_pixel_formats[] = { {DRM_FORMAT_XBGR8888, 32, "32-bit RGBX (8:8:8:8 MSB-X:B:G:R)"}, /* non-supported format has bpp default to 0 */ - {0, 0, NULL}, + {} }; static const struct pixel_format skl_pixel_formats[] = { @@ -76,7 +76,7 @@ static const struct pixel_format skl_pixel_formats[] = { {DRM_FORMAT_XRGB2101010, 32, "32-bit BGRX (2:10:10:10 MSB-X:R:G:B)"}, /* non-supported format has bpp default to 0 */ - {0, 0, NULL}, + {} }; static int bdw_format_to_drm(int format) @@ -293,7 +293,7 @@ static const struct cursor_mode_format cursor_pixel_formats[] = { {DRM_FORMAT_ARGB8888, 32, 64, 64, "64x64 32bpp ARGB"}, /* non-supported format has bpp default to 0 */ - {0, 0, 0, 0, NULL}, + {} }; static int cursor_mode_to_drm(int mode) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index a9f7fa9b90bd..90f6c1ece57d 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -538,7 +538,7 @@ static u32 bxt_vgpu_get_dp_bitrate(struct intel_vgpu *vgpu, enum port port) int refclk = vgpu->gvt->gt->i915->display.dpll.ref_clks.nssc; enum dpio_phy phy = DPIO_PHY0; enum dpio_channel ch = DPIO_CH0; - struct dpll clock = {0}; + struct dpll clock = {}; u32 temp; /* Port to PHY mapping is fixed, see bxt_ddi_phy_info{} */ @@ -2576,7 +2576,6 @@ static int init_bdw_mmio_info(struct intel_gvt *gvt) static int init_skl_mmio_info(struct intel_gvt *gvt) { - struct drm_i915_private *dev_priv = gvt->gt->i915; int ret; MMIO_DH(FORCEWAKE_RENDER_GEN9, D_SKL_PLUS, NULL, mul_force_wake_write); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e9b79c2c37d8..bfe92d2402ea 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -32,6 +32,8 @@ #include <drm/drm_debugfs.h> +#include "display/intel_display_params.h" + #include "gem/i915_gem_context.h" #include "gt/intel_gt.h" #include "gt/intel_gt_buffer_pool.h" @@ -67,13 +69,13 @@ static int i915_capabilities(struct seq_file *m, void *data) seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(i915)); intel_device_info_print(INTEL_INFO(i915), RUNTIME_INFO(i915), &p); - intel_display_device_info_print(DISPLAY_INFO(i915), DISPLAY_RUNTIME_INFO(i915), &p); i915_print_iommu_status(i915, &p); intel_gt_info_print(&to_gt(i915)->info, &p); intel_driver_caps_print(&i915->caps, &p); kernel_param_lock(THIS_MODULE); i915_params_dump(&i915->params, &p); + intel_display_params_dump(i915, &p); kernel_param_unlock(THIS_MODULE); return 0; diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 8a0e2c745e1f..2a1faf403965 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -231,16 +231,10 @@ static int i915_driver_early_probe(struct drm_i915_private *dev_priv) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->gpu_error.lock); - mutex_init(&dev_priv->display.backlight.lock); mutex_init(&dev_priv->sb_lock); cpu_latency_qos_add_request(&dev_priv->sb_qos, PM_QOS_DEFAULT_VALUE); - mutex_init(&dev_priv->display.audio.mutex); - mutex_init(&dev_priv->display.wm.wm_mutex); - mutex_init(&dev_priv->display.pps.mutex); - mutex_init(&dev_priv->display.hdcp.hdcp_mutex); - i915_memcpy_init_early(dev_priv); intel_runtime_pm_init_early(&dev_priv->runtime_pm); @@ -782,7 +776,7 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ret = i915_driver_mmio_probe(i915); if (ret < 0) - goto out_tiles_cleanup; + goto out_runtime_pm_put; ret = i915_driver_hw_probe(i915); if (ret < 0) @@ -842,8 +836,6 @@ out_cleanup_hw: i915_ggtt_driver_late_release(i915); out_cleanup_mmio: i915_driver_mmio_release(i915); -out_tiles_cleanup: - intel_gt_release_all(i915); out_runtime_pm_put: enable_rpm_wakeref_asserts(&i915->runtime_pm); i915_driver_late_release(i915); @@ -909,6 +901,8 @@ static void i915_driver_release(struct drm_device *dev) intel_runtime_pm_driver_release(rpm); i915_driver_late_release(dev_priv); + + intel_display_device_remove(dev_priv); } static int i915_driver_open(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dd452c220df7..861567362abd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -396,20 +396,6 @@ static inline struct intel_gt *to_gt(const struct drm_i915_private *i915) return i915->gt[0]; } -/* Simple iterator over all initialised engines */ -#define for_each_engine(engine__, gt__, id__) \ - for ((id__) = 0; \ - (id__) < I915_NUM_ENGINES; \ - (id__)++) \ - for_each_if ((engine__) = (gt__)->engine[(id__)]) - -/* Iterator over subset of engines selected by mask */ -#define for_each_engine_masked(engine__, gt__, mask__, tmp__) \ - for ((tmp__) = (mask__) & (gt__)->info.engine_mask; \ - (tmp__) ? \ - ((engine__) = (gt__)->engine[__mask_next_bit(tmp__)]), 1 : \ - 0;) - #define rb_to_uabi_engine(rb) \ rb_entry_safe(rb, struct intel_engine_cs, uabi_node) @@ -418,11 +404,6 @@ static inline struct intel_gt *to_gt(const struct drm_i915_private *i915) (engine__); \ (engine__) = rb_to_uabi_engine(rb_next(&(engine__)->uabi_node))) -#define for_each_uabi_class_engine(engine__, class__, i915__) \ - for ((engine__) = intel_engine_lookup_user((i915__), (class__), 0); \ - (engine__) && (engine__)->uabi_class == (class__); \ - (engine__) = rb_to_uabi_engine(rb_next(&(engine__)->uabi_node))) - #define INTEL_INFO(i915) ((i915)->__info) #define RUNTIME_INFO(i915) (&(i915)->__runtime) #define DRIVER_CAPS(i915) (&(i915)->caps) @@ -575,6 +556,7 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define IS_DG2(i915) IS_PLATFORM(i915, INTEL_DG2) #define IS_PONTEVECCHIO(i915) IS_PLATFORM(i915, INTEL_PONTEVECCHIO) #define IS_METEORLAKE(i915) IS_PLATFORM(i915, INTEL_METEORLAKE) +#define IS_LUNARLAKE(i915) 0 #define IS_DG2_G10(i915) \ IS_SUBPLATFORM(i915, INTEL_DG2, INTEL_SUBPLATFORM_G10) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c166ad5e187a..92758b6b41f0 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1306,8 +1306,6 @@ void i915_gem_init_early(struct drm_i915_private *dev_priv) { i915_gem_init__mm(dev_priv); i915_gem_init__contexts(dev_priv); - - spin_lock_init(&dev_priv->display.fb_tracking.lock); } void i915_gem_cleanup_early(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index b4e31e59c799..0971f4976324 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -660,6 +660,7 @@ static void err_print_params(struct drm_i915_error_state_buf *m, struct drm_printer p = i915_error_printer(m); i915_params_dump(params, &p); + intel_display_params_dump(m->i915, &p); } static void err_print_pciid(struct drm_i915_error_state_buf *m, @@ -1027,6 +1028,7 @@ static void i915_vma_coredump_free(struct i915_vma_coredump *vma) static void cleanup_params(struct i915_gpu_coredump *error) { i915_params_free(&error->params); + intel_display_params_free(&error->display_params); } static void cleanup_uc(struct intel_uc_coredump *uc) @@ -1988,6 +1990,7 @@ static void capture_gen(struct i915_gpu_coredump *error) error->suspend_count = i915->suspend_count; i915_params_copy(&error->params, &i915->params); + intel_display_params_copy(&error->display_params); memcpy(&error->device_info, INTEL_INFO(i915), sizeof(error->device_info)); @@ -2174,7 +2177,7 @@ void i915_error_state_store(struct i915_gpu_coredump *error) ktime_get_real_seconds() - DRIVER_TIMESTAMP < DAY_AS_SECONDS(180)) { pr_info("GPU hangs can indicate a bug anywhere in the entire gfx stack, including userspace.\n"); pr_info("Please file a _new_ bug report at https://gitlab.freedesktop.org/drm/intel/issues/new.\n"); - pr_info("Please see https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs for details.\n"); + pr_info("Please see https://drm.pages.freedesktop.org/intel-docs/how-to-file-i915-bugs.html for details.\n"); pr_info("drm/i915 developers can then reassign to the right component if it's not a kernel issue.\n"); pr_info("The GPU crash dump is required to analyze GPU hangs, so please always attach it.\n"); pr_info("GPU crash dump saved to /sys/class/drm/card%d/error\n", diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h index 9f5971f5e980..4ce227f7e1e1 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.h +++ b/drivers/gpu/drm/i915/i915_gpu_error.h @@ -15,6 +15,7 @@ #include <drm/drm_mm.h> #include "display/intel_display_device.h" +#include "display/intel_display_params.h" #include "gt/intel_engine.h" #include "gt/intel_gt_types.h" #include "gt/uc/intel_uc_fw.h" @@ -214,6 +215,7 @@ struct i915_gpu_coredump { struct intel_display_runtime_info display_runtime_info; struct intel_driver_caps driver_caps; struct i915_params params; + struct intel_display_params display_params; struct intel_overlay_error_state *overlay; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 036c4c3ed6ed..de43048543e8 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -67,33 +67,9 @@ i915_param_named(modeset, int, 0400, "Use kernel modesetting [KMS] (0=disable, " "1=on, -1=force vga console preference [default])"); -i915_param_named_unsafe(enable_dc, int, 0400, - "Enable power-saving display C-states. " - "(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6; " - "3=up to DC5 with DC3CO; 4=up to DC6 with DC3CO)"); - -i915_param_named_unsafe(enable_fbc, int, 0400, - "Enable frame buffer compression for power savings " - "(default: -1 (use per-chip default))"); - -i915_param_named_unsafe(lvds_channel_mode, int, 0400, - "Specify LVDS channel mode " - "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)"); - -i915_param_named_unsafe(panel_use_ssc, int, 0400, - "Use Spread Spectrum Clock with panels [LVDS/eDP] " - "(default: auto from VBT)"); - -i915_param_named_unsafe(vbt_sdvo_panel_type, int, 0400, - "Override/Ignore selection of SDVO panel mode in the VBT " - "(-2=ignore, -1=auto [default], index in VBT BIOS table)"); - i915_param_named_unsafe(reset, uint, 0400, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])"); -i915_param_named_unsafe(vbt_firmware, charp, 0400, - "Load VBT from specified file under /lib/firmware"); - #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) i915_param_named(error_capture, bool, 0400, "Record the GPU state following a hang. " @@ -106,55 +82,10 @@ i915_param_named_unsafe(enable_hangcheck, bool, 0400, "WARNING: Disabling this can cause system wide hangs. " "(default: true)"); -i915_param_named_unsafe(enable_psr, int, 0400, - "Enable PSR " - "(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) " - "Default: -1 (use per-chip default)"); - -i915_param_named(psr_safest_params, bool, 0400, - "Replace PSR VBT parameters by the safest and not optimal ones. This " - "is helpful to detect if PSR issues are related to bad values set in " - " VBT. (0=use VBT parameters, 1=use safest parameters)"); - -i915_param_named_unsafe(enable_psr2_sel_fetch, bool, 0400, - "Enable PSR2 selective fetch " - "(0=disabled, 1=enabled) " - "Default: 0"); - -i915_param_named_unsafe(enable_sagv, bool, 0600, - "Enable system agent voltage/frequency scaling (SAGV) (default: true)"); - i915_param_named_unsafe(force_probe, charp, 0400, "Force probe options for specified supported devices. " "See CONFIG_DRM_I915_FORCE_PROBE for details."); -i915_param_named_unsafe(disable_power_well, int, 0400, - "Disable display power wells when possible " - "(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)"); - -i915_param_named_unsafe(enable_ips, int, 0400, "Enable IPS (default: true)"); - -i915_param_named_unsafe(enable_dpt, bool, 0400, - "Enable display page table (DPT) (default: true)"); - -i915_param_named_unsafe(load_detect_test, bool, 0400, - "Force-enable the VGA load detect code for testing (default:false). " - "For developers only."); - -i915_param_named_unsafe(force_reset_modeset_test, bool, 0400, - "Force a modeset during gpu reset for testing (default:false). " - "For developers only."); - -i915_param_named_unsafe(invert_brightness, int, 0400, - "Invert backlight brightness " - "(-1 force normal, 0 machine defaults, 1 force inversion), please " - "report PCI device ID, subsystem vendor and subsystem device ID " - "to dri-devel@lists.freedesktop.org, if your machine needs it. " - "It will then be included in an upcoming module version."); - -i915_param_named(disable_display, bool, 0400, - "Disable display (default: false)"); - i915_param_named(memtest, bool, 0400, "Perform a read/write test of all device memory on module load (default: off)"); @@ -162,19 +93,6 @@ i915_param_named(mmio_debug, int, 0400, "Enable the MMIO debug code for the first N failures (default: off). " "This may negatively affect performance."); -/* Special case writable file */ -i915_param_named(verbose_state_checks, bool, 0600, - "Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions."); - -i915_param_named_unsafe(nuclear_pageflip, bool, 0400, - "Force enable atomic functionality on platforms that don't have full support yet."); - -/* WA to get away with the default setting in VBT for early platforms.Will be removed */ -i915_param_named_unsafe(edp_vswing, int, 0400, - "Ignore/Override vswing pre-emph table selection from VBT " - "(0=use value from vbt [default], 1=low power swing(200mV)," - "2=default swing(400mV))"); - i915_param_named_unsafe(enable_guc, int, 0400, "Enable GuC load for GuC submission and/or HuC load. " "Required functionality can be selected using bitmask values. " @@ -196,18 +114,11 @@ i915_param_named_unsafe(dmc_firmware_path, charp, 0400, i915_param_named_unsafe(gsc_firmware_path, charp, 0400, "GSC firmware path to use instead of the default one"); -i915_param_named_unsafe(enable_dp_mst, bool, 0400, - "Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)"); - #if IS_ENABLED(CONFIG_DRM_I915_DEBUG) i915_param_named_unsafe(inject_probe_failure, uint, 0400, "Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)"); #endif -i915_param_named(enable_dpcd_backlight, int, 0400, - "Enable support for DPCD backlight control" - "(-1=use per-VBT LFP backlight type setting [default], 0=disabled, 1=enable, 2=force VESA interface, 3=force Intel interface)"); - #if IS_ENABLED(CONFIG_DRM_I915_GVT) i915_param_named(enable_gvt, bool, 0400, "Enable support for Intel GVT-g graphics virtualization host support(default:false)"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index d5194b039aab..1315d7fac850 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -46,21 +46,7 @@ struct drm_printer; * debugfs file */ #define I915_PARAMS_FOR_EACH(param) \ - param(char *, vbt_firmware, NULL, 0400) \ param(int, modeset, -1, 0400) \ - param(int, lvds_channel_mode, 0, 0400) \ - param(int, panel_use_ssc, -1, 0600) \ - param(int, vbt_sdvo_panel_type, -1, 0400) \ - param(int, enable_dc, -1, 0400) \ - param(int, enable_fbc, -1, 0600) \ - param(int, enable_psr, -1, 0600) \ - param(bool, enable_dpt, true, 0400) \ - param(bool, psr_safest_params, false, 0400) \ - param(bool, enable_psr2_sel_fetch, true, 0400) \ - param(bool, enable_sagv, true, 0600) \ - param(int, disable_power_well, -1, 0400) \ - param(int, enable_ips, 1, 0600) \ - param(int, invert_brightness, 0, 0600) \ param(int, enable_guc, -1, 0400) \ param(int, guc_log_level, -1, 0400) \ param(char *, guc_firmware_path, NULL, 0400) \ @@ -69,23 +55,15 @@ struct drm_printer; param(char *, gsc_firmware_path, NULL, 0400) \ param(bool, memtest, false, 0400) \ param(int, mmio_debug, -IS_ENABLED(CONFIG_DRM_I915_DEBUG_MMIO), 0600) \ - param(int, edp_vswing, 0, 0400) \ param(unsigned int, reset, 3, 0600) \ param(unsigned int, inject_probe_failure, 0, 0) \ - param(int, enable_dpcd_backlight, -1, 0600) \ param(char *, force_probe, CONFIG_DRM_I915_FORCE_PROBE, 0400) \ param(unsigned int, request_timeout_ms, CONFIG_DRM_I915_REQUEST_TIMEOUT, CONFIG_DRM_I915_REQUEST_TIMEOUT ? 0600 : 0) \ param(unsigned int, lmem_size, 0, 0400) \ param(unsigned int, lmem_bar_size, 0, 0400) \ /* leave bools at the end to not create holes */ \ param(bool, enable_hangcheck, true, 0600) \ - param(bool, load_detect_test, false, 0600) \ - param(bool, force_reset_modeset_test, false, 0600) \ param(bool, error_capture, true, IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) ? 0600 : 0) \ - param(bool, disable_display, false, 0400) \ - param(bool, verbose_state_checks, true, 0) \ - param(bool, nuclear_pageflip, false, 0400) \ - param(bool, enable_dp_mst, true, 0600) \ param(bool, enable_gvt, false, IS_ENABLED(CONFIG_DRM_I915_GVT) ? 0400 : 0) #define MEMBER(T, member, ...) T member; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 135e8d8dbdf0..27dc903f0553 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -195,8 +195,6 @@ #define DPIO_SFR_BYPASS (1 << 1) #define DPIO_CMNRST (1 << 0) -#define DPIO_PHY(pipe) ((pipe) >> 1) - /* * Per pipe/PLL DPIO regs */ diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h index c61066498bf2..f98577967b7f 100644 --- a/drivers/gpu/drm/i915/i915_utils.h +++ b/drivers/gpu/drm/i915/i915_utils.h @@ -40,7 +40,7 @@ struct drm_i915_private; struct timer_list; -#define FDO_BUG_URL "https://gitlab.freedesktop.org/drm/intel/-/wikis/How-to-file-i915-bugs" +#define FDO_BUG_URL "https://drm.pages.freedesktop.org/intel-docs/how-to-file-i915-bugs.html" #define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \ __stringify(x), (long)(x)) diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.h b/drivers/gpu/drm/i915/intel_runtime_pm.h index f79cda7a2503..be43614c73fd 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.h +++ b/drivers/gpu/drm/i915/intel_runtime_pm.h @@ -11,8 +11,6 @@ #include "intel_wakeref.h" -#include "i915_utils.h" - struct device; struct drm_i915_private; struct drm_printer; diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c index 03ea75cd84dd..4f98aa8a861e 100644 --- a/drivers/gpu/drm/i915/selftests/intel_uncore.c +++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c @@ -24,6 +24,8 @@ #include "../i915_selftest.h" +#include "gt/intel_gt.h" + static int intel_fw_table_check(const struct intel_forcewake_range *ranges, unsigned int num_ranges, bool is_watertight) diff --git a/drivers/gpu/drm/i915/soc/intel_gmch.c b/drivers/gpu/drm/i915/soc/intel_gmch.c index f32e9f78770a..40874ebfb64c 100644 --- a/drivers/gpu/drm/i915/soc/intel_gmch.c +++ b/drivers/gpu/drm/i915/soc/intel_gmch.c @@ -33,18 +33,22 @@ int intel_gmch_bridge_setup(struct drm_i915_private *i915) i915->gmch.pdev); } +static int mchbar_reg(struct drm_i915_private *i915) +{ + return GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; +} + /* Allocate space for the MCH regs if needed, return nonzero on error */ static int intel_alloc_mchbar_resource(struct drm_i915_private *i915) { - int reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp_lo, temp_hi = 0; u64 mchbar_addr; int ret; if (GRAPHICS_VER(i915) >= 4) - pci_read_config_dword(i915->gmch.pdev, reg + 4, &temp_hi); - pci_read_config_dword(i915->gmch.pdev, reg, &temp_lo); + pci_read_config_dword(i915->gmch.pdev, mchbar_reg(i915) + 4, &temp_hi); + pci_read_config_dword(i915->gmch.pdev, mchbar_reg(i915), &temp_lo); mchbar_addr = ((u64)temp_hi << 32) | temp_lo; /* If ACPI doesn't have it, assume we need to allocate it ourselves */ @@ -68,10 +72,10 @@ intel_alloc_mchbar_resource(struct drm_i915_private *i915) } if (GRAPHICS_VER(i915) >= 4) - pci_write_config_dword(i915->gmch.pdev, reg + 4, + pci_write_config_dword(i915->gmch.pdev, mchbar_reg(i915) + 4, upper_32_bits(i915->gmch.mch_res.start)); - pci_write_config_dword(i915->gmch.pdev, reg, + pci_write_config_dword(i915->gmch.pdev, mchbar_reg(i915), lower_32_bits(i915->gmch.mch_res.start)); return 0; } @@ -79,7 +83,6 @@ intel_alloc_mchbar_resource(struct drm_i915_private *i915) /* Setup MCHBAR if possible, return true if we should disable it again */ void intel_gmch_bar_setup(struct drm_i915_private *i915) { - int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; u32 temp; bool enabled; @@ -92,7 +95,7 @@ void intel_gmch_bar_setup(struct drm_i915_private *i915) pci_read_config_dword(i915->gmch.pdev, DEVEN, &temp); enabled = !!(temp & DEVEN_MCHBAR_EN); } else { - pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp); + pci_read_config_dword(i915->gmch.pdev, mchbar_reg(i915), &temp); enabled = temp & 1; } @@ -110,15 +113,13 @@ void intel_gmch_bar_setup(struct drm_i915_private *i915) pci_write_config_dword(i915->gmch.pdev, DEVEN, temp | DEVEN_MCHBAR_EN); } else { - pci_read_config_dword(i915->gmch.pdev, mchbar_reg, &temp); - pci_write_config_dword(i915->gmch.pdev, mchbar_reg, temp | 1); + pci_read_config_dword(i915->gmch.pdev, mchbar_reg(i915), &temp); + pci_write_config_dword(i915->gmch.pdev, mchbar_reg(i915), temp | 1); } } void intel_gmch_bar_teardown(struct drm_i915_private *i915) { - int mchbar_reg = GRAPHICS_VER(i915) >= 4 ? MCHBAR_I965 : MCHBAR_I915; - if (i915->gmch.mchbar_need_disable) { if (IS_I915G(i915) || IS_I915GM(i915)) { u32 deven_val; @@ -131,10 +132,10 @@ void intel_gmch_bar_teardown(struct drm_i915_private *i915) } else { u32 mchbar_val; - pci_read_config_dword(i915->gmch.pdev, mchbar_reg, + pci_read_config_dword(i915->gmch.pdev, mchbar_reg(i915), &mchbar_val); mchbar_val &= ~1; - pci_write_config_dword(i915->gmch.pdev, mchbar_reg, + pci_write_config_dword(i915->gmch.pdev, mchbar_reg(i915), mchbar_val); } } diff --git a/drivers/gpu/drm/i915/vlv_sideband.c b/drivers/gpu/drm/i915/vlv_sideband.c index b98dec3ad817..ffa195560d0d 100644 --- a/drivers/gpu/drm/i915/vlv_sideband.c +++ b/drivers/gpu/drm/i915/vlv_sideband.c @@ -166,23 +166,6 @@ u32 vlv_nc_read(struct drm_i915_private *i915, u8 addr) return val; } -u32 vlv_iosf_sb_read(struct drm_i915_private *i915, u8 port, u32 reg) -{ - u32 val = 0; - - vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, - SB_CRRDDA_NP, reg, &val); - - return val; -} - -void vlv_iosf_sb_write(struct drm_i915_private *i915, - u8 port, u32 reg, u32 val) -{ - vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port, - SB_CRWRDA_NP, reg, &val); -} - u32 vlv_cck_read(struct drm_i915_private *i915, u32 reg) { u32 val = 0; @@ -227,9 +210,9 @@ static u32 vlv_dpio_phy_iosf_port(struct drm_i915_private *i915, enum dpio_phy p return IOSF_PORT_DPIO; } -u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg) +u32 vlv_dpio_read(struct drm_i915_private *i915, enum dpio_phy phy, int reg) { - u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); + u32 port = vlv_dpio_phy_iosf_port(i915, phy); u32 val = 0; vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MRD_NP, reg, &val); @@ -239,16 +222,16 @@ u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg) * so ideally we should check the register offset instead... */ drm_WARN(&i915->drm, val == 0xffffffff, - "DPIO read pipe %c reg 0x%x == 0x%x\n", - pipe_name(pipe), reg, val); + "DPIO PHY%d read reg 0x%x == 0x%x\n", + phy, reg, val); return val; } void vlv_dpio_write(struct drm_i915_private *i915, - enum pipe pipe, int reg, u32 val) + enum dpio_phy phy, int reg, u32 val) { - u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe)); + u32 port = vlv_dpio_phy_iosf_port(i915, phy); vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MWR_NP, reg, &val); } diff --git a/drivers/gpu/drm/i915/vlv_sideband.h b/drivers/gpu/drm/i915/vlv_sideband.h index 9ce283d96b80..c20cf41b2d39 100644 --- a/drivers/gpu/drm/i915/vlv_sideband.h +++ b/drivers/gpu/drm/i915/vlv_sideband.h @@ -11,7 +11,7 @@ #include "vlv_sideband_reg.h" -enum pipe; +enum dpio_phy; struct drm_i915_private; enum { @@ -26,9 +26,6 @@ enum { }; void vlv_iosf_sb_get(struct drm_i915_private *i915, unsigned long ports); -u32 vlv_iosf_sb_read(struct drm_i915_private *i915, u8 port, u32 reg); -void vlv_iosf_sb_write(struct drm_i915_private *i915, - u8 port, u32 reg, u32 val); void vlv_iosf_sb_put(struct drm_i915_private *i915, unsigned long ports); static inline void vlv_bunit_get(struct drm_i915_private *i915) @@ -75,9 +72,9 @@ static inline void vlv_dpio_get(struct drm_i915_private *i915) vlv_iosf_sb_get(i915, BIT(VLV_IOSF_SB_DPIO)); } -u32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg); +u32 vlv_dpio_read(struct drm_i915_private *i915, enum dpio_phy phy, int reg); void vlv_dpio_write(struct drm_i915_private *i915, - enum pipe pipe, int reg, u32 val); + enum dpio_phy phy, int reg, u32 val); static inline void vlv_dpio_put(struct drm_i915_private *i915) { diff --git a/drivers/gpu/drm/imagination/Kconfig b/drivers/gpu/drm/imagination/Kconfig new file mode 100644 index 000000000000..3bfa2ac212dc --- /dev/null +++ b/drivers/gpu/drm/imagination/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (c) 2023 Imagination Technologies Ltd. + +config DRM_POWERVR + tristate "Imagination Technologies PowerVR (Series 6 and later) & IMG Graphics" + depends on ARM64 + depends on DRM + depends on PM + select DRM_EXEC + select DRM_GEM_SHMEM_HELPER + select DRM_SCHED + select DRM_GPUVM + select FW_LOADER + help + Choose this option if you have a system that has an Imagination + Technologies PowerVR (Series 6 or later) or IMG GPU. + + If "M" is selected, the module will be called powervr. diff --git a/drivers/gpu/drm/imagination/Makefile b/drivers/gpu/drm/imagination/Makefile new file mode 100644 index 000000000000..ec6db8e9b403 --- /dev/null +++ b/drivers/gpu/drm/imagination/Makefile @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only OR MIT +# Copyright (c) 2023 Imagination Technologies Ltd. + +subdir-ccflags-y := -I$(srctree)/$(src) + +powervr-y := \ + pvr_ccb.o \ + pvr_cccb.o \ + pvr_context.o \ + pvr_device.o \ + pvr_device_info.o \ + pvr_drv.o \ + pvr_free_list.o \ + pvr_fw.o \ + pvr_fw_meta.o \ + pvr_fw_mips.o \ + pvr_fw_startstop.o \ + pvr_fw_trace.o \ + pvr_gem.o \ + pvr_hwrt.o \ + pvr_job.o \ + pvr_mmu.o \ + pvr_params.o \ + pvr_power.o \ + pvr_queue.o \ + pvr_stream.o \ + pvr_stream_defs.o \ + pvr_sync.o \ + pvr_vm.o \ + pvr_vm_mips.o + +powervr-$(CONFIG_DEBUG_FS) += \ + pvr_debugfs.o + +obj-$(CONFIG_DRM_POWERVR) += powervr.o diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c new file mode 100644 index 000000000000..4deeac7ed40a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_ccb.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_ccb.h" +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_free_list.h" +#include "pvr_fw.h" +#include "pvr_gem.h" +#include "pvr_power.h" + +#include <drm/drm_managed.h> +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#define RESERVE_SLOT_TIMEOUT (1 * HZ) /* 1s */ +#define RESERVE_SLOT_MIN_RETRIES 10 + +static void +ccb_ctrl_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_ccb_ctl *ctrl = cpu_ptr; + struct pvr_ccb *pvr_ccb = priv; + + ctrl->write_offset = 0; + ctrl->read_offset = 0; + ctrl->wrap_mask = pvr_ccb->num_cmds - 1; + ctrl->cmd_size = pvr_ccb->cmd_size; +} + +/** + * pvr_ccb_init() - Initialise a CCB + * @pvr_dev: Device pointer. + * @pvr_ccb: Pointer to CCB structure to initialise. + * @num_cmds_log2: Log2 of number of commands in this CCB. + * @cmd_size: Command size for this CCB. + * + * Return: + * * Zero on success, or + * * Any error code returned by pvr_fw_object_create_and_map(). + */ +static int +pvr_ccb_init(struct pvr_device *pvr_dev, struct pvr_ccb *pvr_ccb, + u32 num_cmds_log2, size_t cmd_size) +{ + u32 num_cmds = 1 << num_cmds_log2; + u32 ccb_size = num_cmds * cmd_size; + int err; + + pvr_ccb->num_cmds = num_cmds; + pvr_ccb->cmd_size = cmd_size; + + err = drmm_mutex_init(from_pvr_device(pvr_dev), &pvr_ccb->lock); + if (err) + return err; + + /* + * Map CCB and control structure as uncached, so we don't have to flush + * CPU cache repeatedly when polling for space. + */ + pvr_ccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_ccb->ctrl), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + ccb_ctrl_init, pvr_ccb, &pvr_ccb->ctrl_obj); + if (IS_ERR(pvr_ccb->ctrl)) + return PTR_ERR(pvr_ccb->ctrl); + + pvr_ccb->ccb = pvr_fw_object_create_and_map(pvr_dev, ccb_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &pvr_ccb->ccb_obj); + if (IS_ERR(pvr_ccb->ccb)) { + err = PTR_ERR(pvr_ccb->ccb); + goto err_free_ctrl; + } + + pvr_fw_object_get_fw_addr(pvr_ccb->ctrl_obj, &pvr_ccb->ctrl_fw_addr); + pvr_fw_object_get_fw_addr(pvr_ccb->ccb_obj, &pvr_ccb->ccb_fw_addr); + + WRITE_ONCE(pvr_ccb->ctrl->write_offset, 0); + WRITE_ONCE(pvr_ccb->ctrl->read_offset, 0); + WRITE_ONCE(pvr_ccb->ctrl->wrap_mask, num_cmds - 1); + WRITE_ONCE(pvr_ccb->ctrl->cmd_size, cmd_size); + + return 0; + +err_free_ctrl: + pvr_fw_object_unmap_and_destroy(pvr_ccb->ctrl_obj); + + return err; +} + +/** + * pvr_ccb_fini() - Release CCB structure + * @pvr_ccb: CCB to release. + */ +void +pvr_ccb_fini(struct pvr_ccb *pvr_ccb) +{ + pvr_fw_object_unmap_and_destroy(pvr_ccb->ccb_obj); + pvr_fw_object_unmap_and_destroy(pvr_ccb->ctrl_obj); +} + +/** + * pvr_ccb_slot_available_locked() - Test whether any slots are available in CCB + * @pvr_ccb: CCB to test. + * @write_offset: Address to store number of next available slot. May be %NULL. + * + * Caller must hold @pvr_ccb->lock. + * + * Return: + * * %true if a slot is available, or + * * %false if no slot is available. + */ +static __always_inline bool +pvr_ccb_slot_available_locked(struct pvr_ccb *pvr_ccb, u32 *write_offset) +{ + struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl; + u32 next_write_offset = (READ_ONCE(ctrl->write_offset) + 1) & READ_ONCE(ctrl->wrap_mask); + + lockdep_assert_held(&pvr_ccb->lock); + + if (READ_ONCE(ctrl->read_offset) != next_write_offset) { + if (write_offset) + *write_offset = next_write_offset; + return true; + } + + return false; +} + +static void +process_fwccb_command(struct pvr_device *pvr_dev, struct rogue_fwif_fwccb_cmd *cmd) +{ + switch (cmd->cmd_type) { + case ROGUE_FWIF_FWCCB_CMD_REQUEST_GPU_RESTART: + pvr_power_reset(pvr_dev, false); + break; + + case ROGUE_FWIF_FWCCB_CMD_FREELISTS_RECONSTRUCTION: + pvr_free_list_process_reconstruct_req(pvr_dev, + &cmd->cmd_data.cmd_freelists_reconstruction); + break; + + case ROGUE_FWIF_FWCCB_CMD_FREELIST_GROW: + pvr_free_list_process_grow_req(pvr_dev, &cmd->cmd_data.cmd_free_list_gs); + break; + + default: + drm_info(from_pvr_device(pvr_dev), "Received unknown FWCCB command %x\n", + cmd->cmd_type); + break; + } +} + +/** + * pvr_fwccb_process() - Process any pending FWCCB commands + * @pvr_dev: Target PowerVR device + */ +void pvr_fwccb_process(struct pvr_device *pvr_dev) +{ + struct rogue_fwif_fwccb_cmd *fwccb = pvr_dev->fwccb.ccb; + struct rogue_fwif_ccb_ctl *ctrl = pvr_dev->fwccb.ctrl; + u32 read_offset; + + mutex_lock(&pvr_dev->fwccb.lock); + + while ((read_offset = READ_ONCE(ctrl->read_offset)) != READ_ONCE(ctrl->write_offset)) { + struct rogue_fwif_fwccb_cmd cmd = fwccb[read_offset]; + + WRITE_ONCE(ctrl->read_offset, (read_offset + 1) & READ_ONCE(ctrl->wrap_mask)); + + /* Drop FWCCB lock while we process command. */ + mutex_unlock(&pvr_dev->fwccb.lock); + + process_fwccb_command(pvr_dev, &cmd); + + mutex_lock(&pvr_dev->fwccb.lock); + } + + mutex_unlock(&pvr_dev->fwccb.lock); +} + +/** + * pvr_kccb_capacity() - Returns the maximum number of usable KCCB slots. + * @pvr_dev: Target PowerVR device + * + * Return: + * * The maximum number of active slots. + */ +static u32 pvr_kccb_capacity(struct pvr_device *pvr_dev) +{ + /* Capacity is the number of slot minus one to cope with the wrapping + * mechanisms. If we were to use all slots, we might end up with + * read_offset == write_offset, which the FW considers as a KCCB-is-empty + * condition. + */ + return pvr_dev->kccb.slot_count - 1; +} + +/** + * pvr_kccb_used_slot_count_locked() - Get the number of used slots + * @pvr_dev: Device pointer. + * + * KCCB lock must be held. + * + * Return: + * * The number of slots currently used. + */ +static u32 +pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev) +{ + struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb; + struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl; + u32 wr_offset = READ_ONCE(ctrl->write_offset); + u32 rd_offset = READ_ONCE(ctrl->read_offset); + u32 used_count; + + lockdep_assert_held(&pvr_ccb->lock); + + if (wr_offset >= rd_offset) + used_count = wr_offset - rd_offset; + else + used_count = wr_offset + pvr_dev->kccb.slot_count - rd_offset; + + return used_count; +} + +/** + * pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref + * held and a slot pre-reserved + * @pvr_dev: Device pointer. + * @cmd: Command to sent. + * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. + */ +void +pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev, + struct rogue_fwif_kccb_cmd *cmd, + u32 *kccb_slot) +{ + struct pvr_ccb *pvr_ccb = &pvr_dev->kccb.ccb; + struct rogue_fwif_kccb_cmd *kccb = pvr_ccb->ccb; + struct rogue_fwif_ccb_ctl *ctrl = pvr_ccb->ctrl; + u32 old_write_offset; + u32 new_write_offset; + + WARN_ON(pvr_dev->lost); + + mutex_lock(&pvr_ccb->lock); + + if (WARN_ON(!pvr_dev->kccb.reserved_count)) + goto out_unlock; + + old_write_offset = READ_ONCE(ctrl->write_offset); + + /* We reserved the slot, we should have one available. */ + if (WARN_ON(!pvr_ccb_slot_available_locked(pvr_ccb, &new_write_offset))) + goto out_unlock; + + memcpy(&kccb[old_write_offset], cmd, + sizeof(struct rogue_fwif_kccb_cmd)); + if (kccb_slot) { + *kccb_slot = old_write_offset; + /* Clear return status for this slot. */ + WRITE_ONCE(pvr_dev->kccb.rtn[old_write_offset], + ROGUE_FWIF_KCCB_RTN_SLOT_NO_RESPONSE); + } + mb(); /* memory barrier */ + WRITE_ONCE(ctrl->write_offset, new_write_offset); + pvr_dev->kccb.reserved_count--; + + /* Kick MTS */ + pvr_fw_mts_schedule(pvr_dev, + PVR_FWIF_DM_GP & ~ROGUE_CR_MTS_SCHEDULE_DM_CLRMSK); + +out_unlock: + mutex_unlock(&pvr_ccb->lock); +} + +/** + * pvr_kccb_try_reserve_slot() - Try to reserve a KCCB slot + * @pvr_dev: Device pointer. + * + * Return: + * * true if a KCCB slot was reserved, or + * * false otherwise. + */ +static bool pvr_kccb_try_reserve_slot(struct pvr_device *pvr_dev) +{ + bool reserved = false; + u32 used_count; + + mutex_lock(&pvr_dev->kccb.ccb.lock); + + used_count = pvr_kccb_used_slot_count_locked(pvr_dev); + if (pvr_dev->kccb.reserved_count < pvr_kccb_capacity(pvr_dev) - used_count) { + pvr_dev->kccb.reserved_count++; + reserved = true; + } + + mutex_unlock(&pvr_dev->kccb.ccb.lock); + + return reserved; +} + +/** + * pvr_kccb_reserve_slot_sync() - Try to reserve a slot synchronously + * @pvr_dev: Device pointer. + * + * Return: + * * 0 on success, or + * * -EBUSY if no slots were reserved after %RESERVE_SLOT_TIMEOUT, with a minimum of + * %RESERVE_SLOT_MIN_RETRIES retries. + */ +static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev) +{ + unsigned long start_timestamp = jiffies; + bool reserved = false; + u32 retries = 0; + + while ((jiffies - start_timestamp) < (u32)RESERVE_SLOT_TIMEOUT || + retries < RESERVE_SLOT_MIN_RETRIES) { + reserved = pvr_kccb_try_reserve_slot(pvr_dev); + if (reserved) + break; + + usleep_range(1, 50); + + if (retries < U32_MAX) + retries++; + } + + return reserved ? 0 : -EBUSY; +} + +/** + * pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held + * @pvr_dev: Device pointer. + * @cmd: Command to sent. + * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. + * + * Returns: + * * Zero on success, or + * * -EBUSY if timeout while waiting for a free KCCB slot. + */ +int +pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd, + u32 *kccb_slot) +{ + int err; + + err = pvr_kccb_reserve_slot_sync(pvr_dev); + if (err) + return err; + + pvr_kccb_send_cmd_reserved_powered(pvr_dev, cmd, kccb_slot); + return 0; +} + +/** + * pvr_kccb_send_cmd() - Send command to the KCCB + * @pvr_dev: Device pointer. + * @cmd: Command to sent. + * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. + * + * Returns: + * * Zero on success, or + * * -EBUSY if timeout while waiting for a free KCCB slot. + */ +int +pvr_kccb_send_cmd(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *cmd, + u32 *kccb_slot) +{ + int err; + + err = pvr_power_get(pvr_dev); + if (err) + return err; + + err = pvr_kccb_send_cmd_powered(pvr_dev, cmd, kccb_slot); + + pvr_power_put(pvr_dev); + + return err; +} + +/** + * pvr_kccb_wait_for_completion() - Wait for a KCCB command to complete + * @pvr_dev: Device pointer. + * @slot_nr: KCCB slot to wait on. + * @timeout: Timeout length (in jiffies). + * @rtn_out: Location to store KCCB command result. May be %NULL. + * + * Returns: + * * Zero on success, or + * * -ETIMEDOUT on timeout. + */ +int +pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr, + u32 timeout, u32 *rtn_out) +{ + int ret = wait_event_timeout(pvr_dev->kccb.rtn_q, READ_ONCE(pvr_dev->kccb.rtn[slot_nr]) & + ROGUE_FWIF_KCCB_RTN_SLOT_CMD_EXECUTED, timeout); + + if (ret && rtn_out) + *rtn_out = READ_ONCE(pvr_dev->kccb.rtn[slot_nr]); + + return ret ? 0 : -ETIMEDOUT; +} + +/** + * pvr_kccb_is_idle() - Returns whether the device's KCCB is idle + * @pvr_dev: Device pointer + * + * Returns: + * * %true if the KCCB is idle (contains no commands), or + * * %false if the KCCB contains pending commands. + */ +bool +pvr_kccb_is_idle(struct pvr_device *pvr_dev) +{ + struct rogue_fwif_ccb_ctl *ctrl = pvr_dev->kccb.ccb.ctrl; + bool idle; + + mutex_lock(&pvr_dev->kccb.ccb.lock); + + idle = (READ_ONCE(ctrl->write_offset) == READ_ONCE(ctrl->read_offset)); + + mutex_unlock(&pvr_dev->kccb.ccb.lock); + + return idle; +} + +static const char * +pvr_kccb_fence_get_driver_name(struct dma_fence *f) +{ + return PVR_DRIVER_NAME; +} + +static const char * +pvr_kccb_fence_get_timeline_name(struct dma_fence *f) +{ + return "kccb"; +} + +static const struct dma_fence_ops pvr_kccb_fence_ops = { + .get_driver_name = pvr_kccb_fence_get_driver_name, + .get_timeline_name = pvr_kccb_fence_get_timeline_name, +}; + +/** + * struct pvr_kccb_fence - Fence object used to wait for a KCCB slot + */ +struct pvr_kccb_fence { + /** @base: Base dma_fence object. */ + struct dma_fence base; + + /** @node: Node used to insert the fence in the pvr_device::kccb::waiters list. */ + struct list_head node; +}; + +/** + * pvr_kccb_wake_up_waiters() - Check the KCCB waiters + * @pvr_dev: Target PowerVR device + * + * Signal as many KCCB fences as we have slots available. + */ +void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev) +{ + struct pvr_kccb_fence *fence, *tmp_fence; + u32 used_count, available_count; + + /* Wake up those waiting for KCCB slot execution. */ + wake_up_all(&pvr_dev->kccb.rtn_q); + + /* Then iterate over all KCCB fences and signal as many as we can. */ + mutex_lock(&pvr_dev->kccb.ccb.lock); + used_count = pvr_kccb_used_slot_count_locked(pvr_dev); + + if (WARN_ON(used_count + pvr_dev->kccb.reserved_count > pvr_kccb_capacity(pvr_dev))) + goto out_unlock; + + available_count = pvr_kccb_capacity(pvr_dev) - used_count - pvr_dev->kccb.reserved_count; + list_for_each_entry_safe(fence, tmp_fence, &pvr_dev->kccb.waiters, node) { + if (!available_count) + break; + + list_del(&fence->node); + pvr_dev->kccb.reserved_count++; + available_count--; + dma_fence_signal(&fence->base); + dma_fence_put(&fence->base); + } + +out_unlock: + mutex_unlock(&pvr_dev->kccb.ccb.lock); +} + +/** + * pvr_kccb_fini() - Cleanup device KCCB + * @pvr_dev: Target PowerVR device + */ +void pvr_kccb_fini(struct pvr_device *pvr_dev) +{ + pvr_ccb_fini(&pvr_dev->kccb.ccb); + WARN_ON(!list_empty(&pvr_dev->kccb.waiters)); + WARN_ON(pvr_dev->kccb.reserved_count); +} + +/** + * pvr_kccb_init() - Initialise device KCCB + * @pvr_dev: Target PowerVR device + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_ccb_init(). + */ +int +pvr_kccb_init(struct pvr_device *pvr_dev) +{ + pvr_dev->kccb.slot_count = 1 << ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT; + INIT_LIST_HEAD(&pvr_dev->kccb.waiters); + pvr_dev->kccb.fence_ctx.id = dma_fence_context_alloc(1); + spin_lock_init(&pvr_dev->kccb.fence_ctx.lock); + + return pvr_ccb_init(pvr_dev, &pvr_dev->kccb.ccb, + ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT, + sizeof(struct rogue_fwif_kccb_cmd)); +} + +/** + * pvr_kccb_fence_alloc() - Allocate a pvr_kccb_fence object + * + * Return: + * * NULL if the allocation fails, or + * * A valid dma_fence pointer otherwise. + */ +struct dma_fence *pvr_kccb_fence_alloc(void) +{ + struct pvr_kccb_fence *kccb_fence; + + kccb_fence = kzalloc(sizeof(*kccb_fence), GFP_KERNEL); + if (!kccb_fence) + return NULL; + + return &kccb_fence->base; +} + +/** + * pvr_kccb_fence_put() - Drop a KCCB fence reference + * @fence: The fence to drop the reference on. + * + * If the fence hasn't been initialized yet, dma_fence_free() is called. This + * way we have a single function taking care of both cases. + */ +void pvr_kccb_fence_put(struct dma_fence *fence) +{ + if (!fence) + return; + + if (!fence->ops) { + dma_fence_free(fence); + } else { + WARN_ON(fence->ops != &pvr_kccb_fence_ops); + dma_fence_put(fence); + } +} + +/** + * pvr_kccb_reserve_slot() - Reserve a KCCB slot for later use + * @pvr_dev: Target PowerVR device + * @f: KCCB fence object previously allocated with pvr_kccb_fence_alloc() + * + * Try to reserve a KCCB slot, and if there's no slot available, + * initializes the fence object and queue it to the waiters list. + * + * If NULL is returned, that means the slot is reserved. In that case, + * the @f is freed and shouldn't be accessed after that point. + * + * Return: + * * NULL if a slot was available directly, or + * * A valid dma_fence object to wait on if no slot was available. + */ +struct dma_fence * +pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f) +{ + struct pvr_kccb_fence *fence = container_of(f, struct pvr_kccb_fence, base); + struct dma_fence *out_fence = NULL; + u32 used_count; + + mutex_lock(&pvr_dev->kccb.ccb.lock); + + used_count = pvr_kccb_used_slot_count_locked(pvr_dev); + if (pvr_dev->kccb.reserved_count >= pvr_kccb_capacity(pvr_dev) - used_count) { + dma_fence_init(&fence->base, &pvr_kccb_fence_ops, + &pvr_dev->kccb.fence_ctx.lock, + pvr_dev->kccb.fence_ctx.id, + atomic_inc_return(&pvr_dev->kccb.fence_ctx.seqno)); + out_fence = dma_fence_get(&fence->base); + list_add_tail(&fence->node, &pvr_dev->kccb.waiters); + } else { + pvr_kccb_fence_put(f); + pvr_dev->kccb.reserved_count++; + } + + mutex_unlock(&pvr_dev->kccb.ccb.lock); + + return out_fence; +} + +/** + * pvr_kccb_release_slot() - Release a KCCB slot reserved with + * pvr_kccb_reserve_slot() + * @pvr_dev: Target PowerVR device + * + * Should only be called if something failed after the + * pvr_kccb_reserve_slot() call and you know you won't call + * pvr_kccb_send_cmd_reserved(). + */ +void pvr_kccb_release_slot(struct pvr_device *pvr_dev) +{ + mutex_lock(&pvr_dev->kccb.ccb.lock); + if (!WARN_ON(!pvr_dev->kccb.reserved_count)) + pvr_dev->kccb.reserved_count--; + mutex_unlock(&pvr_dev->kccb.ccb.lock); +} + +/** + * pvr_fwccb_init() - Initialise device FWCCB + * @pvr_dev: Target PowerVR device + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_ccb_init(). + */ +int +pvr_fwccb_init(struct pvr_device *pvr_dev) +{ + return pvr_ccb_init(pvr_dev, &pvr_dev->fwccb, + ROGUE_FWIF_FWCCB_NUMCMDS_LOG2, + sizeof(struct rogue_fwif_fwccb_cmd)); +} diff --git a/drivers/gpu/drm/imagination/pvr_ccb.h b/drivers/gpu/drm/imagination/pvr_ccb.h new file mode 100644 index 000000000000..4c8aef31eeb0 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_ccb.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_CCB_H +#define PVR_CCB_H + +#include "pvr_rogue_fwif.h" + +#include <linux/mutex.h> +#include <linux/types.h> + +/* Forward declaration from pvr_device.h. */ +struct pvr_device; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +struct pvr_ccb { + /** @ctrl_obj: FW object representing CCB control structure. */ + struct pvr_fw_object *ctrl_obj; + /** @ccb_obj: FW object representing CCB. */ + struct pvr_fw_object *ccb_obj; + + /** @ctrl_fw_addr: FW virtual address of CCB control structure. */ + u32 ctrl_fw_addr; + /** @ccb_fw_addr: FW virtual address of CCB. */ + u32 ccb_fw_addr; + + /** @num_cmds: Number of commands in this CCB. */ + u32 num_cmds; + + /** @cmd_size: Size of each command in this CCB, in bytes. */ + u32 cmd_size; + + /** @lock: Mutex protecting @ctrl and @ccb. */ + struct mutex lock; + /** + * @ctrl: Kernel mapping of CCB control structure. @lock must be held + * when accessing. + */ + struct rogue_fwif_ccb_ctl *ctrl; + /** @ccb: Kernel mapping of CCB. @lock must be held when accessing. */ + void *ccb; +}; + +int pvr_kccb_init(struct pvr_device *pvr_dev); +void pvr_kccb_fini(struct pvr_device *pvr_dev); +int pvr_fwccb_init(struct pvr_device *pvr_dev); +void pvr_ccb_fini(struct pvr_ccb *ccb); + +void pvr_fwccb_process(struct pvr_device *pvr_dev); + +struct dma_fence *pvr_kccb_fence_alloc(void); +void pvr_kccb_fence_put(struct dma_fence *fence); +struct dma_fence * +pvr_kccb_reserve_slot(struct pvr_device *pvr_dev, struct dma_fence *f); +void pvr_kccb_release_slot(struct pvr_device *pvr_dev); +int pvr_kccb_send_cmd(struct pvr_device *pvr_dev, + struct rogue_fwif_kccb_cmd *cmd, u32 *kccb_slot); +int pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, + struct rogue_fwif_kccb_cmd *cmd, + u32 *kccb_slot); +void pvr_kccb_send_cmd_reserved_powered(struct pvr_device *pvr_dev, + struct rogue_fwif_kccb_cmd *cmd, + u32 *kccb_slot); +int pvr_kccb_wait_for_completion(struct pvr_device *pvr_dev, u32 slot_nr, u32 timeout, + u32 *rtn_out); +bool pvr_kccb_is_idle(struct pvr_device *pvr_dev); +void pvr_kccb_wake_up_waiters(struct pvr_device *pvr_dev); + +#endif /* PVR_CCB_H */ diff --git a/drivers/gpu/drm/imagination/pvr_cccb.c b/drivers/gpu/drm/imagination/pvr_cccb.c new file mode 100644 index 000000000000..4fabab41bea7 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_cccb.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_ccb.h" +#include "pvr_cccb.h" +#include "pvr_device.h" +#include "pvr_gem.h" +#include "pvr_hwrt.h" + +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/types.h> + +static __always_inline u32 +get_ccb_space(u32 w_off, u32 r_off, u32 ccb_size) +{ + return (((r_off) - (w_off)) + ((ccb_size) - 1)) & ((ccb_size) - 1); +} + +static void +cccb_ctrl_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_cccb_ctl *ctrl = cpu_ptr; + struct pvr_cccb *pvr_cccb = priv; + + WRITE_ONCE(ctrl->write_offset, 0); + WRITE_ONCE(ctrl->read_offset, 0); + WRITE_ONCE(ctrl->dep_offset, 0); + WRITE_ONCE(ctrl->wrap_mask, pvr_cccb->wrap_mask); +} + +/** + * pvr_cccb_init() - Initialise a Client CCB + * @pvr_dev: Device pointer. + * @pvr_cccb: Pointer to Client CCB structure to initialise. + * @size_log2: Log2 size of Client CCB in bytes. + * @name: Name of owner of Client CCB. Used for fence context. + * + * Return: + * * Zero on success, or + * * Any error code returned by pvr_fw_object_create_and_map(). + */ +int +pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *pvr_cccb, + u32 size_log2, const char *name) +{ + size_t size = 1 << size_log2; + int err; + + pvr_cccb->size = size; + pvr_cccb->write_offset = 0; + pvr_cccb->wrap_mask = size - 1; + + /* + * Map CCCB and control structure as uncached, so we don't have to flush + * CPU cache repeatedly when polling for space. + */ + pvr_cccb->ctrl = pvr_fw_object_create_and_map(pvr_dev, sizeof(*pvr_cccb->ctrl), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + cccb_ctrl_init, pvr_cccb, + &pvr_cccb->ctrl_obj); + if (IS_ERR(pvr_cccb->ctrl)) + return PTR_ERR(pvr_cccb->ctrl); + + pvr_cccb->cccb = pvr_fw_object_create_and_map(pvr_dev, size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &pvr_cccb->cccb_obj); + if (IS_ERR(pvr_cccb->cccb)) { + err = PTR_ERR(pvr_cccb->cccb); + goto err_free_ctrl; + } + + pvr_fw_object_get_fw_addr(pvr_cccb->ctrl_obj, &pvr_cccb->ctrl_fw_addr); + pvr_fw_object_get_fw_addr(pvr_cccb->cccb_obj, &pvr_cccb->cccb_fw_addr); + + return 0; + +err_free_ctrl: + pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj); + + return err; +} + +/** + * pvr_cccb_fini() - Release Client CCB structure + * @pvr_cccb: Client CCB to release. + */ +void +pvr_cccb_fini(struct pvr_cccb *pvr_cccb) +{ + pvr_fw_object_unmap_and_destroy(pvr_cccb->cccb_obj); + pvr_fw_object_unmap_and_destroy(pvr_cccb->ctrl_obj); +} + +/** + * pvr_cccb_cmdseq_fits() - Check if a command sequence fits in the CCCB + * @pvr_cccb: Target Client CCB. + * @size: Size of the command sequence. + * + * Check if a command sequence fits in the CCCB we have at hand. + * + * Return: + * * true if the command sequence fits in the CCCB, or + * * false otherwise. + */ +bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size) +{ + struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl; + u32 read_offset, remaining; + bool fits = false; + + read_offset = READ_ONCE(ctrl->read_offset); + remaining = pvr_cccb->size - pvr_cccb->write_offset; + + /* Always ensure we have enough room for a padding command at the end of the CCCB. + * If our command sequence does not fit, reserve the remaining space for a padding + * command. + */ + if (size + PADDING_COMMAND_SIZE > remaining) + size += remaining; + + if (get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size) >= size) + fits = true; + + return fits; +} + +/** + * pvr_cccb_write_command_with_header() - Write a command + command header to a + * Client CCB + * @pvr_cccb: Target Client CCB. + * @cmd_type: Client CCB command type. Must be one of %ROGUE_FWIF_CCB_CMD_TYPE_*. + * @cmd_size: Size of command in bytes. + * @cmd_data: Pointer to command to write. + * @ext_job_ref: External job reference. + * @int_job_ref: Internal job reference. + * + * Caller must make sure there's enough space in CCCB to queue this command. This + * can be done by calling pvr_cccb_cmdseq_fits(). + * + * This function is not protected by any lock. The caller must ensure there's + * no concurrent caller, which should be guaranteed by the drm_sched model (job + * submission is serialized in drm_sched_main()). + */ +void +pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb, u32 cmd_type, u32 cmd_size, + void *cmd_data, u32 ext_job_ref, u32 int_job_ref) +{ + u32 sz_with_hdr = pvr_cccb_get_size_of_cmd_with_hdr(cmd_size); + struct rogue_fwif_ccb_cmd_header cmd_header = { + .cmd_type = cmd_type, + .cmd_size = ALIGN(cmd_size, 8), + .ext_job_ref = ext_job_ref, + .int_job_ref = int_job_ref, + }; + struct rogue_fwif_cccb_ctl *ctrl = pvr_cccb->ctrl; + u32 remaining = pvr_cccb->size - pvr_cccb->write_offset; + u32 required_size, cccb_space, read_offset; + + /* + * Always ensure we have enough room for a padding command at the end of + * the CCCB. + */ + if (remaining < sz_with_hdr + PADDING_COMMAND_SIZE) { + /* + * Command would need to wrap, so we need to pad the remainder + * of the CCCB. + */ + required_size = sz_with_hdr + remaining; + } else { + required_size = sz_with_hdr; + } + + read_offset = READ_ONCE(ctrl->read_offset); + cccb_space = get_ccb_space(pvr_cccb->write_offset, read_offset, pvr_cccb->size); + if (WARN_ON(cccb_space < required_size)) + return; + + if (required_size != sz_with_hdr) { + /* Add padding command */ + struct rogue_fwif_ccb_cmd_header pad_cmd = { + .cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_PADDING, + .cmd_size = remaining - sizeof(pad_cmd), + }; + + memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &pad_cmd, sizeof(pad_cmd)); + pvr_cccb->write_offset = 0; + } + + memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset], &cmd_header, sizeof(cmd_header)); + memcpy(&pvr_cccb->cccb[pvr_cccb->write_offset + sizeof(cmd_header)], cmd_data, cmd_size); + pvr_cccb->write_offset += sz_with_hdr; +} + +static void fill_cmd_kick_data(struct pvr_cccb *cccb, u32 ctx_fw_addr, + struct pvr_hwrt_data *hwrt, + struct rogue_fwif_kccb_cmd_kick_data *k) +{ + k->context_fw_addr = ctx_fw_addr; + k->client_woff_update = cccb->write_offset; + k->client_wrap_mask_update = cccb->wrap_mask; + + if (hwrt) { + u32 cleanup_state_offset = offsetof(struct rogue_fwif_hwrtdata, cleanup_state); + + pvr_fw_object_get_fw_addr_offset(hwrt->fw_obj, cleanup_state_offset, + &k->cleanup_ctl_fw_addr[k->num_cleanup_ctl++]); + } +} + +/** + * pvr_cccb_send_kccb_kick: Send KCCB kick to trigger command processing + * @pvr_dev: Device pointer. + * @pvr_cccb: Pointer to CCCB to process. + * @cctx_fw_addr: FW virtual address for context owning this Client CCB. + * @hwrt: HWRT data set associated with this kick. May be %NULL. + * + * You must call pvr_kccb_reserve_slot() and wait for the returned fence to + * signal (if this function didn't return NULL) before calling + * pvr_cccb_send_kccb_kick(). + */ +void +pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev, + struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr, + struct pvr_hwrt_data *hwrt) +{ + struct rogue_fwif_kccb_cmd cmd_kick = { + .cmd_type = ROGUE_FWIF_KCCB_CMD_KICK, + }; + + fill_cmd_kick_data(pvr_cccb, cctx_fw_addr, hwrt, &cmd_kick.cmd_data.cmd_kick_data); + + /* Make sure the writes to the CCCB are flushed before sending the KICK. */ + wmb(); + + pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL); +} + +void +pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev, + struct pvr_cccb *geom_cccb, + struct pvr_cccb *frag_cccb, + u32 geom_ctx_fw_addr, + u32 frag_ctx_fw_addr, + struct pvr_hwrt_data *hwrt, + bool frag_is_pr) +{ + struct rogue_fwif_kccb_cmd cmd_kick = { + .cmd_type = ROGUE_FWIF_KCCB_CMD_COMBINED_GEOM_FRAG_KICK, + }; + + fill_cmd_kick_data(geom_cccb, geom_ctx_fw_addr, hwrt, + &cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.geom_cmd_kick_data); + + /* If this is a partial-render job, we don't attach resources to cleanup-ctl array, + * because the resources are already retained by the geometry job. + */ + fill_cmd_kick_data(frag_cccb, frag_ctx_fw_addr, frag_is_pr ? NULL : hwrt, + &cmd_kick.cmd_data.combined_geom_frag_cmd_kick_data.frag_cmd_kick_data); + + /* Make sure the writes to the CCCB are flushed before sending the KICK. */ + wmb(); + + pvr_kccb_send_cmd_reserved_powered(pvr_dev, &cmd_kick, NULL); +} diff --git a/drivers/gpu/drm/imagination/pvr_cccb.h b/drivers/gpu/drm/imagination/pvr_cccb.h new file mode 100644 index 000000000000..f35b3d4c9575 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_cccb.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_CCCB_H +#define PVR_CCCB_H + +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_fwif_shared.h" + +#include <linux/mutex.h> +#include <linux/types.h> + +#define PADDING_COMMAND_SIZE sizeof(struct rogue_fwif_ccb_cmd_header) + +/* Forward declaration from pvr_device.h. */ +struct pvr_device; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +/* Forward declaration from pvr_hwrt.h. */ +struct pvr_hwrt_data; + +struct pvr_cccb { + /** @ctrl_obj: FW object representing CCCB control structure. */ + struct pvr_fw_object *ctrl_obj; + + /** @ccb_obj: FW object representing CCCB. */ + struct pvr_fw_object *cccb_obj; + + /** + * @ctrl: Kernel mapping of CCCB control structure. @lock must be held + * when accessing. + */ + struct rogue_fwif_cccb_ctl *ctrl; + + /** @cccb: Kernel mapping of CCCB. @lock must be held when accessing.*/ + u8 *cccb; + + /** @ctrl_fw_addr: FW virtual address of CCCB control structure. */ + u32 ctrl_fw_addr; + /** @ccb_fw_addr: FW virtual address of CCCB. */ + u32 cccb_fw_addr; + + /** @size: Size of CCCB in bytes. */ + size_t size; + + /** @write_offset: CCCB write offset. */ + u32 write_offset; + + /** @wrap_mask: CCCB wrap mask. */ + u32 wrap_mask; +}; + +int pvr_cccb_init(struct pvr_device *pvr_dev, struct pvr_cccb *cccb, + u32 size_log2, const char *name); +void pvr_cccb_fini(struct pvr_cccb *cccb); + +void pvr_cccb_write_command_with_header(struct pvr_cccb *pvr_cccb, + u32 cmd_type, u32 cmd_size, void *cmd_data, + u32 ext_job_ref, u32 int_job_ref); +void pvr_cccb_send_kccb_kick(struct pvr_device *pvr_dev, + struct pvr_cccb *pvr_cccb, u32 cctx_fw_addr, + struct pvr_hwrt_data *hwrt); +void pvr_cccb_send_kccb_combined_kick(struct pvr_device *pvr_dev, + struct pvr_cccb *geom_cccb, + struct pvr_cccb *frag_cccb, + u32 geom_ctx_fw_addr, + u32 frag_ctx_fw_addr, + struct pvr_hwrt_data *hwrt, + bool frag_is_pr); +bool pvr_cccb_cmdseq_fits(struct pvr_cccb *pvr_cccb, size_t size); + +/** + * pvr_cccb_get_size_of_cmd_with_hdr() - Get the size of a command and its header. + * @cmd_size: Command size. + * + * Returns the size of the command and its header. + */ +static __always_inline u32 +pvr_cccb_get_size_of_cmd_with_hdr(u32 cmd_size) +{ + WARN_ON(!IS_ALIGNED(cmd_size, 8)); + return sizeof(struct rogue_fwif_ccb_cmd_header) + ALIGN(cmd_size, 8); +} + +/** + * pvr_cccb_cmdseq_can_fit() - Check if a command sequence can fit in the CCCB. + * @size: Command sequence size. + * + * Returns: + * * true it the CCCB is big enough to contain a command sequence, or + * * false otherwise. + */ +static __always_inline bool +pvr_cccb_cmdseq_can_fit(struct pvr_cccb *pvr_cccb, size_t size) +{ + /* We divide the capacity by two to simplify our CCCB fencing logic: + * we want to be sure that, no matter what we had queued before, we + * are able to either queue our command sequence at the end or add a + * padding command and queue the command sequence at the beginning + * of the CCCB. If the command sequence size is bigger than half the + * CCCB capacity, we'd have to queue the padding command and make sure + * the FW is done processing it before queueing our command sequence. + */ + return size + PADDING_COMMAND_SIZE <= pvr_cccb->size / 2; +} + +#endif /* PVR_CCCB_H */ diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c new file mode 100644 index 000000000000..eded5e955cc0 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_cccb.h" +#include "pvr_context.h" +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_gem.h" +#include "pvr_job.h" +#include "pvr_power.h" +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_fwif_common.h" +#include "pvr_rogue_fwif_resetframework.h" +#include "pvr_stream.h" +#include "pvr_stream_defs.h" +#include "pvr_vm.h" + +#include <drm/drm_auth.h> +#include <drm/drm_managed.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/xarray.h> + +static int +remap_priority(struct pvr_file *pvr_file, s32 uapi_priority, + enum pvr_context_priority *priority_out) +{ + switch (uapi_priority) { + case DRM_PVR_CTX_PRIORITY_LOW: + *priority_out = PVR_CTX_PRIORITY_LOW; + break; + case DRM_PVR_CTX_PRIORITY_NORMAL: + *priority_out = PVR_CTX_PRIORITY_MEDIUM; + break; + case DRM_PVR_CTX_PRIORITY_HIGH: + if (!capable(CAP_SYS_NICE) && !drm_is_current_master(from_pvr_file(pvr_file))) + return -EACCES; + *priority_out = PVR_CTX_PRIORITY_HIGH; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int get_fw_obj_size(enum drm_pvr_ctx_type type) +{ + switch (type) { + case DRM_PVR_CTX_TYPE_RENDER: + return sizeof(struct rogue_fwif_fwrendercontext); + case DRM_PVR_CTX_TYPE_COMPUTE: + return sizeof(struct rogue_fwif_fwcomputecontext); + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + return sizeof(struct rogue_fwif_fwtransfercontext); + } + + return -EINVAL; +} + +static int +process_static_context_state(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs, + u64 stream_user_ptr, u32 stream_size, void *dest) +{ + void *stream; + int err; + + stream = kzalloc(stream_size, GFP_KERNEL); + if (!stream) + return -ENOMEM; + + if (copy_from_user(stream, u64_to_user_ptr(stream_user_ptr), stream_size)) { + err = -EFAULT; + goto err_free; + } + + err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, dest); + if (err) + goto err_free; + + kfree(stream); + + return 0; + +err_free: + kfree(stream); + + return err; +} + +static int init_render_fw_objs(struct pvr_context *ctx, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + struct rogue_fwif_static_rendercontext_state *static_rendercontext_state; + struct rogue_fwif_fwrendercontext *fw_render_context = fw_ctx_map; + + if (!args->static_context_state_len) + return -EINVAL; + + static_rendercontext_state = &fw_render_context->static_render_context_state; + + /* Copy static render context state from userspace. */ + return process_static_context_state(ctx->pvr_dev, + &pvr_static_render_context_state_stream, + args->static_context_state, + args->static_context_state_len, + &static_rendercontext_state->ctxswitch_regs[0]); +} + +static int init_compute_fw_objs(struct pvr_context *ctx, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + struct rogue_fwif_fwcomputecontext *fw_compute_context = fw_ctx_map; + struct rogue_fwif_cdm_registers_cswitch *ctxswitch_regs; + + if (!args->static_context_state_len) + return -EINVAL; + + ctxswitch_regs = &fw_compute_context->static_compute_context_state.ctxswitch_regs; + + /* Copy static render context state from userspace. */ + return process_static_context_state(ctx->pvr_dev, + &pvr_static_compute_context_state_stream, + args->static_context_state, + args->static_context_state_len, + ctxswitch_regs); +} + +static int init_transfer_fw_objs(struct pvr_context *ctx, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + if (args->static_context_state_len) + return -EINVAL; + + return 0; +} + +static int init_fw_objs(struct pvr_context *ctx, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + switch (ctx->type) { + case DRM_PVR_CTX_TYPE_RENDER: + return init_render_fw_objs(ctx, args, fw_ctx_map); + case DRM_PVR_CTX_TYPE_COMPUTE: + return init_compute_fw_objs(ctx, args, fw_ctx_map); + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + return init_transfer_fw_objs(ctx, args, fw_ctx_map); + } + + return -EINVAL; +} + +static void +ctx_fw_data_init(void *cpu_ptr, void *priv) +{ + struct pvr_context *ctx = priv; + + memcpy(cpu_ptr, ctx->data, ctx->data_size); +} + +/** + * pvr_context_destroy_queues() - Destroy all queues attached to a context. + * @ctx: Context to destroy queues on. + * + * Should be called when the last reference to a context object is dropped. + * It releases all resources attached to the queues bound to this context. + */ +static void pvr_context_destroy_queues(struct pvr_context *ctx) +{ + switch (ctx->type) { + case DRM_PVR_CTX_TYPE_RENDER: + pvr_queue_destroy(ctx->queues.fragment); + pvr_queue_destroy(ctx->queues.geometry); + break; + case DRM_PVR_CTX_TYPE_COMPUTE: + pvr_queue_destroy(ctx->queues.compute); + break; + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + pvr_queue_destroy(ctx->queues.transfer); + break; + } +} + +/** + * pvr_context_create_queues() - Create all queues attached to a context. + * @ctx: Context to create queues on. + * @args: Context creation arguments passed by userspace. + * @fw_ctx_map: CPU mapping of the FW context object. + * + * Return: + * * 0 on success, or + * * A negative error code otherwise. + */ +static int pvr_context_create_queues(struct pvr_context *ctx, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + int err; + + switch (ctx->type) { + case DRM_PVR_CTX_TYPE_RENDER: + ctx->queues.geometry = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_GEOMETRY, + args, fw_ctx_map); + if (IS_ERR(ctx->queues.geometry)) { + err = PTR_ERR(ctx->queues.geometry); + ctx->queues.geometry = NULL; + goto err_destroy_queues; + } + + ctx->queues.fragment = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_FRAGMENT, + args, fw_ctx_map); + if (IS_ERR(ctx->queues.fragment)) { + err = PTR_ERR(ctx->queues.fragment); + ctx->queues.fragment = NULL; + goto err_destroy_queues; + } + return 0; + + case DRM_PVR_CTX_TYPE_COMPUTE: + ctx->queues.compute = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_COMPUTE, + args, fw_ctx_map); + if (IS_ERR(ctx->queues.compute)) { + err = PTR_ERR(ctx->queues.compute); + ctx->queues.compute = NULL; + goto err_destroy_queues; + } + return 0; + + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + ctx->queues.transfer = pvr_queue_create(ctx, DRM_PVR_JOB_TYPE_TRANSFER_FRAG, + args, fw_ctx_map); + if (IS_ERR(ctx->queues.transfer)) { + err = PTR_ERR(ctx->queues.transfer); + ctx->queues.transfer = NULL; + goto err_destroy_queues; + } + return 0; + } + + return -EINVAL; + +err_destroy_queues: + pvr_context_destroy_queues(ctx); + return err; +} + +/** + * pvr_context_kill_queues() - Kill queues attached to context. + * @ctx: Context to kill queues on. + * + * Killing the queues implies making them unusable for future jobs, while still + * letting the currently submitted jobs a chance to finish. Queue resources will + * stay around until pvr_context_destroy_queues() is called. + */ +static void pvr_context_kill_queues(struct pvr_context *ctx) +{ + switch (ctx->type) { + case DRM_PVR_CTX_TYPE_RENDER: + pvr_queue_kill(ctx->queues.fragment); + pvr_queue_kill(ctx->queues.geometry); + break; + case DRM_PVR_CTX_TYPE_COMPUTE: + pvr_queue_kill(ctx->queues.compute); + break; + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + pvr_queue_kill(ctx->queues.transfer); + break; + } +} + +/** + * pvr_context_create() - Create a context. + * @pvr_file: File to attach the created context to. + * @args: Context creation arguments. + * + * Return: + * * 0 on success, or + * * A negative error code on failure. + */ +int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args) +{ + struct pvr_device *pvr_dev = pvr_file->pvr_dev; + struct pvr_context *ctx; + int ctx_size; + int err; + + /* Context creation flags are currently unused and must be zero. */ + if (args->flags) + return -EINVAL; + + ctx_size = get_fw_obj_size(args->type); + if (ctx_size < 0) + return ctx_size; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->data_size = ctx_size; + ctx->type = args->type; + ctx->flags = args->flags; + ctx->pvr_dev = pvr_dev; + kref_init(&ctx->ref_count); + + err = remap_priority(pvr_file, args->priority, &ctx->priority); + if (err) + goto err_free_ctx; + + ctx->vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); + if (IS_ERR(ctx->vm_ctx)) { + err = PTR_ERR(ctx->vm_ctx); + goto err_free_ctx; + } + + ctx->data = kzalloc(ctx_size, GFP_KERNEL); + if (!ctx->data) { + err = -ENOMEM; + goto err_put_vm; + } + + err = pvr_context_create_queues(ctx, args, ctx->data); + if (err) + goto err_free_ctx_data; + + err = init_fw_objs(ctx, args, ctx->data); + if (err) + goto err_destroy_queues; + + err = pvr_fw_object_create(pvr_dev, ctx_size, PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + ctx_fw_data_init, ctx, &ctx->fw_obj); + if (err) + goto err_free_ctx_data; + + err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL); + if (err) + goto err_destroy_fw_obj; + + err = xa_alloc(&pvr_file->ctx_handles, &args->handle, ctx, xa_limit_32b, GFP_KERNEL); + if (err) { + /* + * It's possible that another thread could have taken a reference on the context at + * this point as it is in the ctx_ids xarray. Therefore instead of directly + * destroying the context, drop a reference instead. + */ + pvr_context_put(ctx); + return err; + } + + return 0; + +err_destroy_fw_obj: + pvr_fw_object_destroy(ctx->fw_obj); + +err_destroy_queues: + pvr_context_destroy_queues(ctx); + +err_free_ctx_data: + kfree(ctx->data); + +err_put_vm: + pvr_vm_context_put(ctx->vm_ctx); + +err_free_ctx: + kfree(ctx); + return err; +} + +static void +pvr_context_release(struct kref *ref_count) +{ + struct pvr_context *ctx = + container_of(ref_count, struct pvr_context, ref_count); + struct pvr_device *pvr_dev = ctx->pvr_dev; + + xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id); + pvr_context_destroy_queues(ctx); + pvr_fw_object_destroy(ctx->fw_obj); + kfree(ctx->data); + pvr_vm_context_put(ctx->vm_ctx); + kfree(ctx); +} + +/** + * pvr_context_put() - Release reference on context + * @ctx: Target context. + */ +void +pvr_context_put(struct pvr_context *ctx) +{ + if (ctx) + kref_put(&ctx->ref_count, pvr_context_release); +} + +/** + * pvr_context_destroy() - Destroy context + * @pvr_file: Pointer to pvr_file structure. + * @handle: Userspace context handle. + * + * Removes context from context list and drops initial reference. Context will + * then be destroyed once all outstanding references are dropped. + * + * Return: + * * 0 on success, or + * * -%EINVAL if context not in context list. + */ +int +pvr_context_destroy(struct pvr_file *pvr_file, u32 handle) +{ + struct pvr_context *ctx = xa_erase(&pvr_file->ctx_handles, handle); + + if (!ctx) + return -EINVAL; + + /* Make sure nothing can be queued to the queues after that point. */ + pvr_context_kill_queues(ctx); + + /* Release the reference held by the handle set. */ + pvr_context_put(ctx); + + return 0; +} + +/** + * pvr_destroy_contexts_for_file: Destroy any contexts associated with the given file + * @pvr_file: Pointer to pvr_file structure. + * + * Removes all contexts associated with @pvr_file from the device context list and drops initial + * references. Contexts will then be destroyed once all outstanding references are dropped. + */ +void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file) +{ + struct pvr_context *ctx; + unsigned long handle; + + xa_for_each(&pvr_file->ctx_handles, handle, ctx) + pvr_context_destroy(pvr_file, handle); +} + +/** + * pvr_context_device_init() - Device level initialization for queue related resources. + * @pvr_dev: The device to initialize. + */ +void pvr_context_device_init(struct pvr_device *pvr_dev) +{ + xa_init_flags(&pvr_dev->ctx_ids, XA_FLAGS_ALLOC1); +} + +/** + * pvr_context_device_fini() - Device level cleanup for queue related resources. + * @pvr_dev: The device to cleanup. + */ +void pvr_context_device_fini(struct pvr_device *pvr_dev) +{ + WARN_ON(!xa_empty(&pvr_dev->ctx_ids)); + xa_destroy(&pvr_dev->ctx_ids); +} diff --git a/drivers/gpu/drm/imagination/pvr_context.h b/drivers/gpu/drm/imagination/pvr_context.h new file mode 100644 index 000000000000..0c7b97dfa6ba --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_context.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_CONTEXT_H +#define PVR_CONTEXT_H + +#include <drm/gpu_scheduler.h> + +#include <linux/compiler_attributes.h> +#include <linux/dma-fence.h> +#include <linux/kref.h> +#include <linux/types.h> +#include <linux/xarray.h> +#include <uapi/drm/pvr_drm.h> + +#include "pvr_cccb.h" +#include "pvr_device.h" +#include "pvr_queue.h" + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +enum pvr_context_priority { + PVR_CTX_PRIORITY_LOW = 0, + PVR_CTX_PRIORITY_MEDIUM, + PVR_CTX_PRIORITY_HIGH, +}; + +/** + * struct pvr_context - Context data + */ +struct pvr_context { + /** @ref_count: Refcount for context. */ + struct kref ref_count; + + /** @pvr_dev: Pointer to owning device. */ + struct pvr_device *pvr_dev; + + /** @vm_ctx: Pointer to associated VM context. */ + struct pvr_vm_context *vm_ctx; + + /** @type: Type of context. */ + enum drm_pvr_ctx_type type; + + /** @flags: Context flags. */ + u32 flags; + + /** @priority: Context priority*/ + enum pvr_context_priority priority; + + /** @fw_obj: FW object representing FW-side context data. */ + struct pvr_fw_object *fw_obj; + + /** @data: Pointer to local copy of FW context data. */ + void *data; + + /** @data_size: Size of FW context data, in bytes. */ + u32 data_size; + + /** @ctx_id: FW context ID. */ + u32 ctx_id; + + /** + * @faulty: Set to 1 when the context queues had unfinished job when + * a GPU reset happened. + * + * In that case, the context is in an inconsistent state and can't be + * used anymore. + */ + atomic_t faulty; + + /** @queues: Union containing all kind of queues. */ + union { + struct { + /** @geometry: Geometry queue. */ + struct pvr_queue *geometry; + + /** @fragment: Fragment queue. */ + struct pvr_queue *fragment; + }; + + /** @compute: Compute queue. */ + struct pvr_queue *compute; + + /** @compute: Transfer queue. */ + struct pvr_queue *transfer; + } queues; +}; + +static __always_inline struct pvr_queue * +pvr_context_get_queue_for_job(struct pvr_context *ctx, enum drm_pvr_job_type type) +{ + switch (type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.geometry : NULL; + case DRM_PVR_JOB_TYPE_FRAGMENT: + return ctx->type == DRM_PVR_CTX_TYPE_RENDER ? ctx->queues.fragment : NULL; + case DRM_PVR_JOB_TYPE_COMPUTE: + return ctx->type == DRM_PVR_CTX_TYPE_COMPUTE ? ctx->queues.compute : NULL; + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return ctx->type == DRM_PVR_CTX_TYPE_TRANSFER_FRAG ? ctx->queues.transfer : NULL; + } + + return NULL; +} + +/** + * pvr_context_get() - Take additional reference on context. + * @ctx: Context pointer. + * + * Call pvr_context_put() to release. + * + * Returns: + * * The requested context on success, or + * * %NULL if no context pointer passed. + */ +static __always_inline struct pvr_context * +pvr_context_get(struct pvr_context *ctx) +{ + if (ctx) + kref_get(&ctx->ref_count); + + return ctx; +} + +/** + * pvr_context_lookup() - Lookup context pointer from handle and file. + * @pvr_file: Pointer to pvr_file structure. + * @handle: Context handle. + * + * Takes reference on context. Call pvr_context_put() to release. + * + * Return: + * * The requested context on success, or + * * %NULL on failure (context does not exist, or does not belong to @pvr_file). + */ +static __always_inline struct pvr_context * +pvr_context_lookup(struct pvr_file *pvr_file, u32 handle) +{ + struct pvr_context *ctx; + + /* Take the array lock to protect against context removal. */ + xa_lock(&pvr_file->ctx_handles); + ctx = pvr_context_get(xa_load(&pvr_file->ctx_handles, handle)); + xa_unlock(&pvr_file->ctx_handles); + + return ctx; +} + +/** + * pvr_context_lookup_id() - Lookup context pointer from ID. + * @pvr_dev: Device pointer. + * @id: FW context ID. + * + * Takes reference on context. Call pvr_context_put() to release. + * + * Return: + * * The requested context on success, or + * * %NULL on failure (context does not exist). + */ +static __always_inline struct pvr_context * +pvr_context_lookup_id(struct pvr_device *pvr_dev, u32 id) +{ + struct pvr_context *ctx; + + /* Take the array lock to protect against context removal. */ + xa_lock(&pvr_dev->ctx_ids); + + /* Contexts are removed from the ctx_ids set in the context release path, + * meaning the ref_count reached zero before they get removed. We need + * to make sure we're not trying to acquire a context that's being + * destroyed. + */ + ctx = xa_load(&pvr_dev->ctx_ids, id); + if (!kref_get_unless_zero(&ctx->ref_count)) + ctx = NULL; + + xa_unlock(&pvr_dev->ctx_ids); + + return ctx; +} + +static __always_inline u32 +pvr_context_get_fw_addr(struct pvr_context *ctx) +{ + u32 ctx_fw_addr = 0; + + pvr_fw_object_get_fw_addr(ctx->fw_obj, &ctx_fw_addr); + + return ctx_fw_addr; +} + +void pvr_context_put(struct pvr_context *ctx); + +int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_context_args *args); + +int pvr_context_destroy(struct pvr_file *pvr_file, u32 handle); + +void pvr_destroy_contexts_for_file(struct pvr_file *pvr_file); + +void pvr_context_device_init(struct pvr_device *pvr_dev); + +void pvr_context_device_fini(struct pvr_device *pvr_dev); + +#endif /* PVR_CONTEXT_H */ diff --git a/drivers/gpu/drm/imagination/pvr_debugfs.c b/drivers/gpu/drm/imagination/pvr_debugfs.c new file mode 100644 index 000000000000..6b77c9b4bde8 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_debugfs.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_debugfs.h" + +#include "pvr_device.h" +#include "pvr_fw_trace.h" +#include "pvr_params.h" + +#include <linux/dcache.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <drm/drm_device.h> +#include <drm/drm_file.h> +#include <drm/drm_print.h> + +static const struct pvr_debugfs_entry pvr_debugfs_entries[] = { + {"pvr_params", pvr_params_debugfs_init}, + {"pvr_fw", pvr_fw_trace_debugfs_init}, +}; + +void +pvr_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *drm_dev = minor->dev; + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct dentry *root = minor->debugfs_root; + size_t i; + + for (i = 0; i < ARRAY_SIZE(pvr_debugfs_entries); ++i) { + const struct pvr_debugfs_entry *entry = &pvr_debugfs_entries[i]; + struct dentry *dir; + + dir = debugfs_create_dir(entry->name, root); + if (IS_ERR(dir)) { + drm_warn(drm_dev, + "failed to create debugfs dir '%s' (err=%d)", + entry->name, (int)PTR_ERR(dir)); + continue; + } + + entry->init(pvr_dev, dir); + } +} + +/* + * Since all entries are created under &drm_minor->debugfs_root, there's no + * need for a pvr_debugfs_fini() as DRM will clean up everything under its root + * automatically. + */ diff --git a/drivers/gpu/drm/imagination/pvr_debugfs.h b/drivers/gpu/drm/imagination/pvr_debugfs.h new file mode 100644 index 000000000000..ebacbd13b84a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_debugfs.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_DEBUGFS_H +#define PVR_DEBUGFS_H + +/* Forward declaration from <drm/drm_drv.h>. */ +struct drm_minor; + +#if defined(CONFIG_DEBUG_FS) +/* Forward declaration from "pvr_device.h". */ +struct pvr_device; + +/* Forward declaration from <linux/dcache.h>. */ +struct dentry; + +struct pvr_debugfs_entry { + const char *name; + void (*init)(struct pvr_device *pvr_dev, struct dentry *dir); +}; + +void pvr_debugfs_init(struct drm_minor *minor); +#else /* defined(CONFIG_DEBUG_FS) */ +#include <linux/compiler_attributes.h> + +static __always_inline void pvr_debugfs_init(struct drm_minor *minor) {} +#endif /* defined(CONFIG_DEBUG_FS) */ + +#endif /* PVR_DEBUGFS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c new file mode 100644 index 000000000000..8499becf4fbb --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_device_info.h" + +#include "pvr_fw.h" +#include "pvr_params.h" +#include "pvr_power.h" +#include "pvr_queue.h" +#include "pvr_rogue_cr_defs.h" +#include "pvr_stream.h" +#include "pvr_vm.h" + +#include <drm/drm_print.h> + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/compiler_attributes.h> +#include <linux/compiler_types.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/gfp.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/stddef.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +/* Major number for the supported version of the firmware. */ +#define PVR_FW_VERSION_MAJOR 1 + +/** + * pvr_device_reg_init() - Initialize kernel access to a PowerVR device's + * control registers. + * @pvr_dev: Target PowerVR device. + * + * Sets struct pvr_device->regs. + * + * This method of mapping the device control registers into memory ensures that + * they are unmapped when the driver is detached (i.e. no explicit cleanup is + * required). + * + * Return: + * * 0 on success, or + * * Any error returned by devm_platform_ioremap_resource(). + */ +static int +pvr_device_reg_init(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct platform_device *plat_dev = to_platform_device(drm_dev->dev); + struct resource *regs_resource; + void __iomem *regs; + + pvr_dev->regs_resource = NULL; + pvr_dev->regs = NULL; + + regs = devm_platform_get_and_ioremap_resource(plat_dev, 0, ®s_resource); + if (IS_ERR(regs)) + return dev_err_probe(drm_dev->dev, PTR_ERR(regs), + "failed to ioremap gpu registers\n"); + + pvr_dev->regs = regs; + pvr_dev->regs_resource = regs_resource; + + return 0; +} + +/** + * pvr_device_clk_init() - Initialize clocks required by a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and + * struct pvr_device->mem_clk. + * + * Three clocks are required by the PowerVR device: core, sys and mem. On + * return, this function guarantees that the clocks are in one of the following + * states: + * + * * All successfully initialized, + * * Core errored, sys and mem uninitialized, + * * Core deinitialized, sys errored, mem uninitialized, or + * * Core and sys deinitialized, mem errored. + * + * Return: + * * 0 on success, + * * Any error returned by devm_clk_get(), or + * * Any error returned by devm_clk_get_optional(). + */ +static int pvr_device_clk_init(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct clk *core_clk; + struct clk *sys_clk; + struct clk *mem_clk; + + core_clk = devm_clk_get(drm_dev->dev, "core"); + if (IS_ERR(core_clk)) + return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk), + "failed to get core clock\n"); + + sys_clk = devm_clk_get_optional(drm_dev->dev, "sys"); + if (IS_ERR(sys_clk)) + return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk), + "failed to get sys clock\n"); + + mem_clk = devm_clk_get_optional(drm_dev->dev, "mem"); + if (IS_ERR(mem_clk)) + return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk), + "failed to get mem clock\n"); + + pvr_dev->core_clk = core_clk; + pvr_dev->sys_clk = sys_clk; + pvr_dev->mem_clk = mem_clk; + + return 0; +} + +/** + * pvr_device_process_active_queues() - Process all queue related events. + * @pvr_dev: PowerVR device to check + * + * This is called any time we receive a FW event. It iterates over all + * active queues and calls pvr_queue_process() on them. + */ +void pvr_device_process_active_queues(struct pvr_device *pvr_dev) +{ + struct pvr_queue *queue, *tmp_queue; + LIST_HEAD(active_queues); + + mutex_lock(&pvr_dev->queues.lock); + + /* Move all active queues to a temporary list. Queues that remain + * active after we're done processing them are re-inserted to + * the queues.active list by pvr_queue_process(). + */ + list_splice_init(&pvr_dev->queues.active, &active_queues); + + list_for_each_entry_safe(queue, tmp_queue, &active_queues, node) + pvr_queue_process(queue); + + mutex_unlock(&pvr_dev->queues.lock); +} + +static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data) +{ + struct pvr_device *pvr_dev = data; + irqreturn_t ret = IRQ_NONE; + + /* We are in the threaded handler, we can keep dequeuing events until we + * don't see any. This should allow us to reduce the number of interrupts + * when the GPU is receiving a massive amount of short jobs. + */ + while (pvr_fw_irq_pending(pvr_dev)) { + pvr_fw_irq_clear(pvr_dev); + + if (pvr_dev->fw_dev.booted) { + pvr_fwccb_process(pvr_dev); + pvr_kccb_wake_up_waiters(pvr_dev); + pvr_device_process_active_queues(pvr_dev); + } + + pm_runtime_mark_last_busy(from_pvr_device(pvr_dev)->dev); + + ret = IRQ_HANDLED; + } + + /* Unmask FW irqs before returning, so new interrupts can be received. */ + pvr_fw_irq_enable(pvr_dev); + return ret; +} + +static irqreturn_t pvr_device_irq_handler(int irq, void *data) +{ + struct pvr_device *pvr_dev = data; + + if (!pvr_fw_irq_pending(pvr_dev)) + return IRQ_NONE; /* Spurious IRQ - ignore. */ + + /* Mask the FW interrupts before waking up the thread. Will be unmasked + * when the thread handler is done processing events. + */ + pvr_fw_irq_disable(pvr_dev); + return IRQ_WAKE_THREAD; +} + +/** + * pvr_device_irq_init() - Initialise IRQ required by a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, + * * Any error returned by platform_get_irq_byname(), or + * * Any error returned by request_irq(). + */ +static int +pvr_device_irq_init(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct platform_device *plat_dev = to_platform_device(drm_dev->dev); + + init_waitqueue_head(&pvr_dev->kccb.rtn_q); + + pvr_dev->irq = platform_get_irq(plat_dev, 0); + if (pvr_dev->irq < 0) + return pvr_dev->irq; + + /* Clear any pending events before requesting the IRQ line. */ + pvr_fw_irq_clear(pvr_dev); + pvr_fw_irq_enable(pvr_dev); + + return request_threaded_irq(pvr_dev->irq, pvr_device_irq_handler, + pvr_device_irq_thread_handler, + IRQF_SHARED, "gpu", pvr_dev); +} + +/** + * pvr_device_irq_fini() - Deinitialise IRQ required by a PowerVR device + * @pvr_dev: Target PowerVR device. + */ +static void +pvr_device_irq_fini(struct pvr_device *pvr_dev) +{ + free_irq(pvr_dev->irq, pvr_dev); +} + +/** + * pvr_build_firmware_filename() - Construct a PowerVR firmware filename + * @pvr_dev: Target PowerVR device. + * @base: First part of the filename. + * @major: Major version number. + * + * A PowerVR firmware filename consists of three parts separated by underscores + * (``'_'``) along with a '.fw' file suffix. The first part is the exact value + * of @base, the second part is the hardware version string derived from @pvr_fw + * and the final part is the firmware version number constructed from @major with + * a 'v' prefix, e.g. powervr/rogue_4.40.2.51_v1.fw. + * + * The returned string will have been slab allocated and must be freed with + * kfree(). + * + * Return: + * * The constructed filename on success, or + * * Any error returned by kasprintf(). + */ +static char * +pvr_build_firmware_filename(struct pvr_device *pvr_dev, const char *base, + u8 major) +{ + struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id; + + return kasprintf(GFP_KERNEL, "%s_%d.%d.%d.%d_v%d.fw", base, gpu_id->b, + gpu_id->v, gpu_id->n, gpu_id->c, major); +} + +static void +pvr_release_firmware(void *data) +{ + struct pvr_device *pvr_dev = data; + + release_firmware(pvr_dev->fw_dev.firmware); +} + +/** + * pvr_request_firmware() - Load firmware for a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * See pvr_build_firmware_filename() for details on firmware file naming. + * + * Return: + * * 0 on success, + * * Any error returned by pvr_build_firmware_filename(), or + * * Any error returned by request_firmware(). + */ +static int +pvr_request_firmware(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = &pvr_dev->base; + char *filename; + const struct firmware *fw; + int err; + + filename = pvr_build_firmware_filename(pvr_dev, "powervr/rogue", + PVR_FW_VERSION_MAJOR); + if (IS_ERR(filename)) + return PTR_ERR(filename); + + /* + * This function takes a copy of &filename, meaning we can free our + * instance before returning. + */ + err = request_firmware(&fw, filename, pvr_dev->base.dev); + if (err) { + drm_err(drm_dev, "failed to load firmware %s (err=%d)\n", + filename, err); + goto err_free_filename; + } + + drm_info(drm_dev, "loaded firmware %s\n", filename); + kfree(filename); + + pvr_dev->fw_dev.firmware = fw; + + return devm_add_action_or_reset(drm_dev->dev, pvr_release_firmware, pvr_dev); + +err_free_filename: + kfree(filename); + + return err; +} + +/** + * pvr_load_gpu_id() - Load a PowerVR device's GPU ID (BVNC) from control registers. + * + * Sets struct pvr_dev.gpu_id. + * + * @pvr_dev: Target PowerVR device. + */ +static void +pvr_load_gpu_id(struct pvr_device *pvr_dev) +{ + struct pvr_gpu_id *gpu_id = &pvr_dev->gpu_id; + u64 bvnc; + + /* + * Try reading the BVNC using the newer (cleaner) method first. If the + * B value is zero, fall back to the older method. + */ + bvnc = pvr_cr_read64(pvr_dev, ROGUE_CR_CORE_ID__PBVNC); + + gpu_id->b = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__BRANCH_ID); + if (gpu_id->b != 0) { + gpu_id->v = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__VERSION_ID); + gpu_id->n = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__NUMBER_OF_SCALABLE_UNITS); + gpu_id->c = PVR_CR_FIELD_GET(bvnc, CORE_ID__PBVNC__CONFIG_ID); + } else { + u32 core_rev = pvr_cr_read32(pvr_dev, ROGUE_CR_CORE_REVISION); + u32 core_id = pvr_cr_read32(pvr_dev, ROGUE_CR_CORE_ID); + u16 core_id_config = PVR_CR_FIELD_GET(core_id, CORE_ID_CONFIG); + + gpu_id->b = PVR_CR_FIELD_GET(core_rev, CORE_REVISION_MAJOR); + gpu_id->v = PVR_CR_FIELD_GET(core_rev, CORE_REVISION_MINOR); + gpu_id->n = FIELD_GET(0xFF00, core_id_config); + gpu_id->c = FIELD_GET(0x00FF, core_id_config); + } +} + +/** + * pvr_set_dma_info() - Set PowerVR device DMA information + * @pvr_dev: Target PowerVR device. + * + * Sets the DMA mask and max segment size for the PowerVR device. + * + * Return: + * * 0 on success, + * * Any error returned by PVR_FEATURE_VALUE(), or + * * Any error returned by dma_set_mask(). + */ + +static int +pvr_set_dma_info(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + u16 phys_bus_width; + int err; + + err = PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width); + if (err) { + drm_err(drm_dev, "Failed to get device physical bus width\n"); + return err; + } + + err = dma_set_mask(drm_dev->dev, DMA_BIT_MASK(phys_bus_width)); + if (err) { + drm_err(drm_dev, "Failed to set DMA mask (err=%d)\n", err); + return err; + } + + dma_set_max_seg_size(drm_dev->dev, UINT_MAX); + + return 0; +} + +/** + * pvr_device_gpu_init() - GPU-specific initialization for a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * The following steps are taken to ensure the device is ready: + * + * 1. Read the hardware version information from control registers, + * 2. Initialise the hardware feature information, + * 3. Setup the device DMA information, + * 4. Setup the device-scoped memory context, and + * 5. Load firmware into the device. + * + * Return: + * * 0 on success, + * * -%ENODEV if the GPU is not supported, + * * Any error returned by pvr_set_dma_info(), + * * Any error returned by pvr_memory_context_init(), or + * * Any error returned by pvr_request_firmware(). + */ +static int +pvr_device_gpu_init(struct pvr_device *pvr_dev) +{ + int err; + + pvr_load_gpu_id(pvr_dev); + + err = pvr_request_firmware(pvr_dev); + if (err) + return err; + + err = pvr_fw_validate_init_device_info(pvr_dev); + if (err) + return err; + + if (PVR_HAS_FEATURE(pvr_dev, meta)) + pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_META; + else if (PVR_HAS_FEATURE(pvr_dev, mips)) + pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_MIPS; + else if (PVR_HAS_FEATURE(pvr_dev, riscv_fw_processor)) + pvr_dev->fw_dev.processor_type = PVR_FW_PROCESSOR_TYPE_RISCV; + else + return -EINVAL; + + pvr_stream_create_musthave_masks(pvr_dev); + + err = pvr_set_dma_info(pvr_dev); + if (err) + return err; + + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { + pvr_dev->kernel_vm_ctx = pvr_vm_create_context(pvr_dev, false); + if (IS_ERR(pvr_dev->kernel_vm_ctx)) + return PTR_ERR(pvr_dev->kernel_vm_ctx); + } + + err = pvr_fw_init(pvr_dev); + if (err) + goto err_vm_ctx_put; + + return 0; + +err_vm_ctx_put: + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { + pvr_vm_context_put(pvr_dev->kernel_vm_ctx); + pvr_dev->kernel_vm_ctx = NULL; + } + + return err; +} + +/** + * pvr_device_gpu_fini() - GPU-specific deinitialization for a PowerVR device + * @pvr_dev: Target PowerVR device. + */ +static void +pvr_device_gpu_fini(struct pvr_device *pvr_dev) +{ + pvr_fw_fini(pvr_dev); + + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { + WARN_ON(!pvr_vm_context_put(pvr_dev->kernel_vm_ctx)); + pvr_dev->kernel_vm_ctx = NULL; + } +} + +/** + * pvr_device_init() - Initialize a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * If this function returns successfully, the device will have been fully + * initialized. Otherwise, any parts of the device initialized before an error + * occurs will be de-initialized before returning. + * + * NOTE: The initialization steps currently taken are the bare minimum required + * to read from the control registers. The device is unlikely to function + * until further initialization steps are added. [This note should be + * removed when that happens.] + * + * Return: + * * 0 on success, + * * Any error returned by pvr_device_reg_init(), + * * Any error returned by pvr_device_clk_init(), or + * * Any error returned by pvr_device_gpu_init(). + */ +int +pvr_device_init(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct device *dev = drm_dev->dev; + int err; + + /* + * Setup device parameters. We do this first in case other steps + * depend on them. + */ + err = pvr_device_params_init(&pvr_dev->params); + if (err) + return err; + + /* Enable and initialize clocks required for the device to operate. */ + err = pvr_device_clk_init(pvr_dev); + if (err) + return err; + + /* Explicitly power the GPU so we can access control registers before the FW is booted. */ + err = pm_runtime_resume_and_get(dev); + if (err) + return err; + + /* Map the control registers into memory. */ + err = pvr_device_reg_init(pvr_dev); + if (err) + goto err_pm_runtime_put; + + /* Perform GPU-specific initialization steps. */ + err = pvr_device_gpu_init(pvr_dev); + if (err) + goto err_pm_runtime_put; + + err = pvr_device_irq_init(pvr_dev); + if (err) + goto err_device_gpu_fini; + + pm_runtime_put(dev); + + return 0; + +err_device_gpu_fini: + pvr_device_gpu_fini(pvr_dev); + +err_pm_runtime_put: + pm_runtime_put_sync_suspend(dev); + + return err; +} + +/** + * pvr_device_fini() - Deinitialize a PowerVR device + * @pvr_dev: Target PowerVR device. + */ +void +pvr_device_fini(struct pvr_device *pvr_dev) +{ + /* + * Deinitialization stages are performed in reverse order compared to + * the initialization stages in pvr_device_init(). + */ + pvr_device_irq_fini(pvr_dev); + pvr_device_gpu_fini(pvr_dev); +} + +bool +pvr_device_has_uapi_quirk(struct pvr_device *pvr_dev, u32 quirk) +{ + switch (quirk) { + case 47217: + return PVR_HAS_QUIRK(pvr_dev, 47217); + case 48545: + return PVR_HAS_QUIRK(pvr_dev, 48545); + case 49927: + return PVR_HAS_QUIRK(pvr_dev, 49927); + case 51764: + return PVR_HAS_QUIRK(pvr_dev, 51764); + case 62269: + return PVR_HAS_QUIRK(pvr_dev, 62269); + default: + return false; + }; +} + +bool +pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement) +{ + switch (enhancement) { + case 35421: + return PVR_HAS_ENHANCEMENT(pvr_dev, 35421); + case 42064: + return PVR_HAS_ENHANCEMENT(pvr_dev, 42064); + default: + return false; + }; +} + +/** + * pvr_device_has_feature() - Look up device feature based on feature definition + * @pvr_dev: Device pointer. + * @feature: Feature to look up. Should be one of %PVR_FEATURE_*. + * + * Returns: + * * %true if feature is present on device, or + * * %false if feature is not present on device. + */ +bool +pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature) +{ + switch (feature) { + case PVR_FEATURE_CLUSTER_GROUPING: + return PVR_HAS_FEATURE(pvr_dev, cluster_grouping); + + case PVR_FEATURE_COMPUTE_MORTON_CAPABLE: + return PVR_HAS_FEATURE(pvr_dev, compute_morton_capable); + + case PVR_FEATURE_FB_CDC_V4: + return PVR_HAS_FEATURE(pvr_dev, fb_cdc_v4); + + case PVR_FEATURE_GPU_MULTICORE_SUPPORT: + return PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support); + + case PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE: + return PVR_HAS_FEATURE(pvr_dev, isp_zls_d24_s8_packing_ogl_mode); + + case PVR_FEATURE_S7_TOP_INFRASTRUCTURE: + return PVR_HAS_FEATURE(pvr_dev, s7_top_infrastructure); + + case PVR_FEATURE_TESSELLATION: + return PVR_HAS_FEATURE(pvr_dev, tessellation); + + case PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS: + return PVR_HAS_FEATURE(pvr_dev, tpu_dm_global_registers); + + case PVR_FEATURE_VDM_DRAWINDIRECT: + return PVR_HAS_FEATURE(pvr_dev, vdm_drawindirect); + + case PVR_FEATURE_VDM_OBJECT_LEVEL_LLS: + return PVR_HAS_FEATURE(pvr_dev, vdm_object_level_lls); + + case PVR_FEATURE_ZLS_SUBTILE: + return PVR_HAS_FEATURE(pvr_dev, zls_subtile); + + /* Derived features. */ + case PVR_FEATURE_CDM_USER_MODE_QUEUE: { + u8 cdm_control_stream_format = 0; + + PVR_FEATURE_VALUE(pvr_dev, cdm_control_stream_format, &cdm_control_stream_format); + return (cdm_control_stream_format >= 2 && cdm_control_stream_format <= 4); + } + + case PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP: + if (PVR_HAS_FEATURE(pvr_dev, fbcdc_algorithm)) { + u8 fbcdc_algorithm = 0; + + PVR_FEATURE_VALUE(pvr_dev, fbcdc_algorithm, &fbcdc_algorithm); + return (fbcdc_algorithm < 3 || PVR_HAS_FEATURE(pvr_dev, fb_cdc_v4)); + } + return false; + + default: + WARN(true, "Looking up undefined feature %u\n", feature); + return false; + } +} diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h new file mode 100644 index 000000000000..e07655fc65e8 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_device.h @@ -0,0 +1,710 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_DEVICE_H +#define PVR_DEVICE_H + +#include "pvr_ccb.h" +#include "pvr_device_info.h" +#include "pvr_fw.h" +#include "pvr_params.h" +#include "pvr_rogue_fwif_stream.h" +#include "pvr_stream.h" + +#include <drm/drm_device.h> +#include <drm/drm_file.h> +#include <drm/drm_mm.h> + +#include <linux/bits.h> +#include <linux/compiler_attributes.h> +#include <linux/compiler_types.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/math.h> +#include <linux/mutex.h> +#include <linux/timer.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <linux/xarray.h> + +/* Forward declaration from <linux/clk.h>. */ +struct clk; + +/* Forward declaration from <linux/firmware.h>. */ +struct firmware; + +/** + * struct pvr_gpu_id - Hardware GPU ID information for a PowerVR device + * @b: Branch ID. + * @v: Version ID. + * @n: Number of scalable units. + * @c: Config ID. + */ +struct pvr_gpu_id { + u16 b, v, n, c; +}; + +/** + * struct pvr_fw_version - Firmware version information + * @major: Major version number. + * @minor: Minor version number. + */ +struct pvr_fw_version { + u16 major, minor; +}; + +/** + * struct pvr_device - powervr-specific wrapper for &struct drm_device + */ +struct pvr_device { + /** + * @base: The underlying &struct drm_device. + * + * Do not access this member directly, instead call + * from_pvr_device(). + */ + struct drm_device base; + + /** @gpu_id: GPU ID detected at runtime. */ + struct pvr_gpu_id gpu_id; + + /** + * @features: Hardware feature information. + * + * Do not access this member directly, instead use PVR_HAS_FEATURE() + * or PVR_FEATURE_VALUE() macros. + */ + struct pvr_device_features features; + + /** + * @quirks: Hardware quirk information. + * + * Do not access this member directly, instead use PVR_HAS_QUIRK(). + */ + struct pvr_device_quirks quirks; + + /** + * @enhancements: Hardware enhancement information. + * + * Do not access this member directly, instead use + * PVR_HAS_ENHANCEMENT(). + */ + struct pvr_device_enhancements enhancements; + + /** @fw_version: Firmware version detected at runtime. */ + struct pvr_fw_version fw_version; + + /** @regs_resource: Resource representing device control registers. */ + struct resource *regs_resource; + + /** + * @regs: Device control registers. + * + * These are mapped into memory when the device is initialized; that + * location is where this pointer points. + */ + void __iomem *regs; + + /** + * @core_clk: General core clock. + * + * This is the primary clock used by the entire GPU core. + */ + struct clk *core_clk; + + /** + * @sys_clk: Optional system bus clock. + * + * This may be used on some platforms to provide an independent clock to the SoC Interface + * (SOCIF). If present, this needs to be enabled/disabled together with @core_clk. + */ + struct clk *sys_clk; + + /** + * @mem_clk: Optional memory clock. + * + * This may be used on some platforms to provide an independent clock to the Memory + * Interface (MEMIF). If present, this needs to be enabled/disabled together with @core_clk. + */ + struct clk *mem_clk; + + /** @irq: IRQ number. */ + int irq; + + /** @fwccb: Firmware CCB. */ + struct pvr_ccb fwccb; + + /** + * @kernel_vm_ctx: Virtual memory context used for kernel mappings. + * + * This is used for mappings in the firmware address region when a META firmware processor + * is in use. + * + * When a MIPS firmware processor is in use, this will be %NULL. + */ + struct pvr_vm_context *kernel_vm_ctx; + + /** @fw_dev: Firmware related data. */ + struct pvr_fw_device fw_dev; + + /** + * @params: Device-specific parameters. + * + * The values of these parameters are initialized from the + * defaults specified as module parameters. They may be + * modified at runtime via debugfs (if enabled). + */ + struct pvr_device_params params; + + /** @stream_musthave_quirks: Bit array of "must-have" quirks for stream commands. */ + u32 stream_musthave_quirks[PVR_STREAM_TYPE_MAX][PVR_STREAM_EXTHDR_TYPE_MAX]; + + /** + * @mmu_flush_cache_flags: Records which MMU caches require flushing + * before submitting the next job. + */ + atomic_t mmu_flush_cache_flags; + + /** + * @ctx_ids: Array of contexts belonging to this device. Array members + * are of type "struct pvr_context *". + * + * This array is used to allocate IDs used by the firmware. + */ + struct xarray ctx_ids; + + /** + * @free_list_ids: Array of free lists belonging to this device. Array members + * are of type "struct pvr_free_list *". + * + * This array is used to allocate IDs used by the firmware. + */ + struct xarray free_list_ids; + + /** + * @job_ids: Array of jobs belonging to this device. Array members + * are of type "struct pvr_job *". + */ + struct xarray job_ids; + + /** + * @queues: Queue-related fields. + */ + struct { + /** @active: Active queue list. */ + struct list_head active; + + /** @idle: Idle queue list. */ + struct list_head idle; + + /** @lock: Lock protecting access to the active/idle lists. */ + struct mutex lock; + } queues; + + struct { + /** @work: Work item for watchdog callback. */ + struct delayed_work work; + + /** @old_kccb_cmds_executed: KCCB command execution count at last watchdog poll. */ + u32 old_kccb_cmds_executed; + + /** @kccb_stall_count: Number of watchdog polls KCCB has been stalled for. */ + u32 kccb_stall_count; + } watchdog; + + struct { + /** @ccb: Kernel CCB. */ + struct pvr_ccb ccb; + + /** @rtn_q: Waitqueue for KCCB command return waiters. */ + wait_queue_head_t rtn_q; + + /** @rtn_obj: Object representing KCCB return slots. */ + struct pvr_fw_object *rtn_obj; + + /** + * @rtn: Pointer to CPU mapping of KCCB return slots. Must be accessed by + * READ_ONCE()/WRITE_ONCE(). + */ + u32 *rtn; + + /** @slot_count: Total number of KCCB slots available. */ + u32 slot_count; + + /** @reserved_count: Number of KCCB slots reserved for future use. */ + u32 reserved_count; + + /** + * @waiters: List of KCCB slot waiters. + */ + struct list_head waiters; + + /** @fence_ctx: KCCB fence context. */ + struct { + /** @id: KCCB fence context ID allocated with dma_fence_context_alloc(). */ + u64 id; + + /** @seqno: Sequence number incremented each time a fence is created. */ + atomic_t seqno; + + /** + * @lock: Lock used to synchronize access to fences allocated by this + * context. + */ + spinlock_t lock; + } fence_ctx; + } kccb; + + /** + * @lost: %true if the device has been lost. + * + * This variable is set if the device has become irretrievably unavailable, e.g. if the + * firmware processor has stopped responding and can not be revived via a hard reset. + */ + bool lost; + + /** + * @reset_sem: Reset semaphore. + * + * GPU reset code will lock this for writing. Any code that submits commands to the firmware + * that isn't in an IRQ handler or on the scheduler workqueue must lock this for reading. + * Once this has been successfully locked, &pvr_dev->lost _must_ be checked, and -%EIO must + * be returned if it is set. + */ + struct rw_semaphore reset_sem; + + /** @sched_wq: Workqueue for schedulers. */ + struct workqueue_struct *sched_wq; +}; + +/** + * struct pvr_file - powervr-specific data to be assigned to &struct + * drm_file.driver_priv + */ +struct pvr_file { + /** + * @file: A reference to the parent &struct drm_file. + * + * Do not access this member directly, instead call from_pvr_file(). + */ + struct drm_file *file; + + /** + * @pvr_dev: A reference to the powervr-specific wrapper for the + * associated device. Saves on repeated calls to + * to_pvr_device(). + */ + struct pvr_device *pvr_dev; + + /** + * @ctx_handles: Array of contexts belonging to this file. Array members + * are of type "struct pvr_context *". + * + * This array is used to allocate handles returned to userspace. + */ + struct xarray ctx_handles; + + /** + * @free_list_handles: Array of free lists belonging to this file. Array + * members are of type "struct pvr_free_list *". + * + * This array is used to allocate handles returned to userspace. + */ + struct xarray free_list_handles; + + /** + * @hwrt_handles: Array of HWRT datasets belonging to this file. Array + * members are of type "struct pvr_hwrt_dataset *". + * + * This array is used to allocate handles returned to userspace. + */ + struct xarray hwrt_handles; + + /** + * @vm_ctx_handles: Array of VM contexts belonging to this file. Array + * members are of type "struct pvr_vm_context *". + * + * This array is used to allocate handles returned to userspace. + */ + struct xarray vm_ctx_handles; +}; + +/** + * PVR_HAS_FEATURE() - Tests whether a PowerVR device has a given feature + * @pvr_dev: [IN] Target PowerVR device. + * @feature: [IN] Hardware feature name. + * + * Feature names are derived from those found in &struct pvr_device_features by + * dropping the 'has_' prefix, which is applied by this macro. + * + * Return: + * * true if the named feature is present in the hardware + * * false if the named feature is not present in the hardware + */ +#define PVR_HAS_FEATURE(pvr_dev, feature) ((pvr_dev)->features.has_##feature) + +/** + * PVR_FEATURE_VALUE() - Gets a PowerVR device feature value + * @pvr_dev: [IN] Target PowerVR device. + * @feature: [IN] Feature name. + * @value_out: [OUT] Feature value. + * + * This macro will get a feature value for those features that have values. + * If the feature is not present, nothing will be stored to @value_out. + * + * Feature names are derived from those found in &struct pvr_device_features by + * dropping the 'has_' prefix. + * + * Return: + * * 0 on success, or + * * -%EINVAL if the named feature is not present in the hardware + */ +#define PVR_FEATURE_VALUE(pvr_dev, feature, value_out) \ + ({ \ + struct pvr_device *_pvr_dev = pvr_dev; \ + int _ret = -EINVAL; \ + if (_pvr_dev->features.has_##feature) { \ + *(value_out) = _pvr_dev->features.feature; \ + _ret = 0; \ + } \ + _ret; \ + }) + +/** + * PVR_HAS_QUIRK() - Tests whether a physical device has a given quirk + * @pvr_dev: [IN] Target PowerVR device. + * @quirk: [IN] Hardware quirk name. + * + * Quirk numbers are derived from those found in #pvr_device_quirks by + * dropping the 'has_brn' prefix, which is applied by this macro. + * + * Returns + * * true if the quirk is present in the hardware, or + * * false if the quirk is not present in the hardware. + */ +#define PVR_HAS_QUIRK(pvr_dev, quirk) ((pvr_dev)->quirks.has_brn##quirk) + +/** + * PVR_HAS_ENHANCEMENT() - Tests whether a physical device has a given + * enhancement + * @pvr_dev: [IN] Target PowerVR device. + * @enhancement: [IN] Hardware enhancement name. + * + * Enhancement numbers are derived from those found in #pvr_device_enhancements + * by dropping the 'has_ern' prefix, which is applied by this macro. + * + * Returns + * * true if the enhancement is present in the hardware, or + * * false if the enhancement is not present in the hardware. + */ +#define PVR_HAS_ENHANCEMENT(pvr_dev, enhancement) ((pvr_dev)->enhancements.has_ern##enhancement) + +#define from_pvr_device(pvr_dev) (&(pvr_dev)->base) + +#define to_pvr_device(drm_dev) container_of_const(drm_dev, struct pvr_device, base) + +#define from_pvr_file(pvr_file) ((pvr_file)->file) + +#define to_pvr_file(file) ((file)->driver_priv) + +/** + * PVR_PACKED_BVNC() - Packs B, V, N and C values into a 64-bit unsigned integer + * @b: Branch ID. + * @v: Version ID. + * @n: Number of scalable units. + * @c: Config ID. + * + * The packed layout is as follows: + * + * +--------+--------+--------+-------+ + * | 63..48 | 47..32 | 31..16 | 15..0 | + * +========+========+========+=======+ + * | B | V | N | C | + * +--------+--------+--------+-------+ + * + * pvr_gpu_id_to_packed_bvnc() should be used instead of this macro when a + * &struct pvr_gpu_id is available in order to ensure proper type checking. + * + * Return: Packed BVNC. + */ +/* clang-format off */ +#define PVR_PACKED_BVNC(b, v, n, c) \ + ((((u64)(b) & GENMASK_ULL(15, 0)) << 48) | \ + (((u64)(v) & GENMASK_ULL(15, 0)) << 32) | \ + (((u64)(n) & GENMASK_ULL(15, 0)) << 16) | \ + (((u64)(c) & GENMASK_ULL(15, 0)) << 0)) +/* clang-format on */ + +/** + * pvr_gpu_id_to_packed_bvnc() - Packs B, V, N and C values into a 64-bit + * unsigned integer + * @gpu_id: GPU ID. + * + * The packed layout is as follows: + * + * +--------+--------+--------+-------+ + * | 63..48 | 47..32 | 31..16 | 15..0 | + * +========+========+========+=======+ + * | B | V | N | C | + * +--------+--------+--------+-------+ + * + * This should be used in preference to PVR_PACKED_BVNC() when a &struct + * pvr_gpu_id is available in order to ensure proper type checking. + * + * Return: Packed BVNC. + */ +static __always_inline u64 +pvr_gpu_id_to_packed_bvnc(struct pvr_gpu_id *gpu_id) +{ + return PVR_PACKED_BVNC(gpu_id->b, gpu_id->v, gpu_id->n, gpu_id->c); +} + +static __always_inline void +packed_bvnc_to_pvr_gpu_id(u64 bvnc, struct pvr_gpu_id *gpu_id) +{ + gpu_id->b = (bvnc & GENMASK_ULL(63, 48)) >> 48; + gpu_id->v = (bvnc & GENMASK_ULL(47, 32)) >> 32; + gpu_id->n = (bvnc & GENMASK_ULL(31, 16)) >> 16; + gpu_id->c = bvnc & GENMASK_ULL(15, 0); +} + +int pvr_device_init(struct pvr_device *pvr_dev); +void pvr_device_fini(struct pvr_device *pvr_dev); +void pvr_device_reset(struct pvr_device *pvr_dev); + +bool +pvr_device_has_uapi_quirk(struct pvr_device *pvr_dev, u32 quirk); +bool +pvr_device_has_uapi_enhancement(struct pvr_device *pvr_dev, u32 enhancement); +bool +pvr_device_has_feature(struct pvr_device *pvr_dev, u32 feature); + +/** + * PVR_CR_FIELD_GET() - Extract a single field from a PowerVR control register + * @val: Value of the target register. + * @field: Field specifier, as defined in "pvr_rogue_cr_defs.h". + * + * Return: The extracted field. + */ +#define PVR_CR_FIELD_GET(val, field) FIELD_GET(~ROGUE_CR_##field##_CLRMSK, val) + +/** + * pvr_cr_read32() - Read a 32-bit register from a PowerVR device + * @pvr_dev: Target PowerVR device. + * @reg: Target register. + * + * Return: The value of the requested register. + */ +static __always_inline u32 +pvr_cr_read32(struct pvr_device *pvr_dev, u32 reg) +{ + return ioread32(pvr_dev->regs + reg); +} + +/** + * pvr_cr_read64() - Read a 64-bit register from a PowerVR device + * @pvr_dev: Target PowerVR device. + * @reg: Target register. + * + * Return: The value of the requested register. + */ +static __always_inline u64 +pvr_cr_read64(struct pvr_device *pvr_dev, u32 reg) +{ + return ioread64(pvr_dev->regs + reg); +} + +/** + * pvr_cr_write32() - Write to a 32-bit register in a PowerVR device + * @pvr_dev: Target PowerVR device. + * @reg: Target register. + * @val: Value to write. + */ +static __always_inline void +pvr_cr_write32(struct pvr_device *pvr_dev, u32 reg, u32 val) +{ + iowrite32(val, pvr_dev->regs + reg); +} + +/** + * pvr_cr_write64() - Write to a 64-bit register in a PowerVR device + * @pvr_dev: Target PowerVR device. + * @reg: Target register. + * @val: Value to write. + */ +static __always_inline void +pvr_cr_write64(struct pvr_device *pvr_dev, u32 reg, u64 val) +{ + iowrite64(val, pvr_dev->regs + reg); +} + +/** + * pvr_cr_poll_reg32() - Wait for a 32-bit register to match a given value by + * polling + * @pvr_dev: Target PowerVR device. + * @reg_addr: Address of register. + * @reg_value: Expected register value (after masking). + * @reg_mask: Mask of bits valid for comparison with @reg_value. + * @timeout_usec: Timeout length, in us. + * + * Returns: + * * 0 on success, or + * * -%ETIMEDOUT on timeout. + */ +static __always_inline int +pvr_cr_poll_reg32(struct pvr_device *pvr_dev, u32 reg_addr, u32 reg_value, + u32 reg_mask, u64 timeout_usec) +{ + u32 value; + + return readl_poll_timeout(pvr_dev->regs + reg_addr, value, + (value & reg_mask) == reg_value, 0, timeout_usec); +} + +/** + * pvr_cr_poll_reg64() - Wait for a 64-bit register to match a given value by + * polling + * @pvr_dev: Target PowerVR device. + * @reg_addr: Address of register. + * @reg_value: Expected register value (after masking). + * @reg_mask: Mask of bits valid for comparison with @reg_value. + * @timeout_usec: Timeout length, in us. + * + * Returns: + * * 0 on success, or + * * -%ETIMEDOUT on timeout. + */ +static __always_inline int +pvr_cr_poll_reg64(struct pvr_device *pvr_dev, u32 reg_addr, u64 reg_value, + u64 reg_mask, u64 timeout_usec) +{ + u64 value; + + return readq_poll_timeout(pvr_dev->regs + reg_addr, value, + (value & reg_mask) == reg_value, 0, timeout_usec); +} + +/** + * pvr_round_up_to_cacheline_size() - Round up a provided size to be cacheline + * aligned + * @pvr_dev: Target PowerVR device. + * @size: Initial size, in bytes. + * + * Returns: + * * Size aligned to cacheline size. + */ +static __always_inline size_t +pvr_round_up_to_cacheline_size(struct pvr_device *pvr_dev, size_t size) +{ + u16 slc_cacheline_size_bits = 0; + u16 slc_cacheline_size_bytes; + + WARN_ON(!PVR_HAS_FEATURE(pvr_dev, slc_cache_line_size_bits)); + PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits, + &slc_cacheline_size_bits); + slc_cacheline_size_bytes = slc_cacheline_size_bits / 8; + + return round_up(size, slc_cacheline_size_bytes); +} + +/** + * DOC: IOCTL validation helpers + * + * To validate the constraints imposed on IOCTL argument structs, a collection + * of macros and helper functions exist in ``pvr_device.h``. + * + * Of the current helpers, it should only be necessary to call + * PVR_IOCTL_UNION_PADDING_CHECK() directly. This macro should be used once in + * every code path which extracts a union member from a struct passed from + * userspace. + */ + +/** + * pvr_ioctl_union_padding_check() - Validate that the implicit padding between + * the end of a union member and the end of the union itself is zeroed. + * @instance: Pointer to the instance of the struct to validate. + * @union_offset: Offset into the type of @instance of the target union. Must + * be 64-bit aligned. + * @union_size: Size of the target union in the type of @instance. Must be + * 64-bit aligned. + * @member_size: Size of the target member in the target union specified by + * @union_offset and @union_size. It is assumed that the offset of the target + * member is zero relative to @union_offset. Must be 64-bit aligned. + * + * You probably want to use PVR_IOCTL_UNION_PADDING_CHECK() instead of calling + * this function directly, since that macro abstracts away much of the setup, + * and also provides some static validation. See its docs for details. + * + * Return: + * * %true if every byte between the end of the used member of the union and + * the end of that union is zeroed, or + * * %false otherwise. + */ +static __always_inline bool +pvr_ioctl_union_padding_check(void *instance, size_t union_offset, + size_t union_size, size_t member_size) +{ + /* + * void pointer arithmetic is technically illegal - cast to a byte + * pointer so this addition works safely. + */ + void *padding_start = ((u8 *)instance) + union_offset + member_size; + size_t padding_size = union_size - member_size; + + return !memchr_inv(padding_start, 0, padding_size); +} + +/** + * PVR_STATIC_ASSERT_64BIT_ALIGNED() - Inline assertion for 64-bit alignment. + * @static_expr_: Target expression to evaluate. + * + * If @static_expr_ does not evaluate to a constant integer which would be a + * 64-bit aligned address (i.e. a multiple of 8), compilation will fail. + * + * Return: + * The value of @static_expr_. + */ +#define PVR_STATIC_ASSERT_64BIT_ALIGNED(static_expr_) \ + ({ \ + static_assert(((static_expr_) & (sizeof(u64) - 1)) == 0); \ + (static_expr_); \ + }) + +/** + * PVR_IOCTL_UNION_PADDING_CHECK() - Validate that the implicit padding between + * the end of a union member and the end of the union itself is zeroed. + * @struct_instance_: An expression which evaluates to a pointer to a UAPI data + * struct. + * @union_: The name of the union member of @struct_instance_ to check. If the + * union member is nested within the type of @struct_instance_, this may + * contain the member access operator ("."). + * @member_: The name of the member of @union_ to assess. + * + * This is a wrapper around pvr_ioctl_union_padding_check() which performs + * alignment checks and simplifies things for the caller. + * + * Return: + * * %true if every byte in @struct_instance_ between the end of @member_ and + * the end of @union_ is zeroed, or + * * %false otherwise. + */ +#define PVR_IOCTL_UNION_PADDING_CHECK(struct_instance_, union_, member_) \ + ({ \ + typeof(struct_instance_) __instance = (struct_instance_); \ + size_t __union_offset = PVR_STATIC_ASSERT_64BIT_ALIGNED( \ + offsetof(typeof(*__instance), union_)); \ + size_t __union_size = PVR_STATIC_ASSERT_64BIT_ALIGNED( \ + sizeof(__instance->union_)); \ + size_t __member_size = PVR_STATIC_ASSERT_64BIT_ALIGNED( \ + sizeof(__instance->union_.member_)); \ + pvr_ioctl_union_padding_check(__instance, __union_offset, \ + __union_size, __member_size); \ + }) + +#define PVR_FW_PROCESSOR_TYPE_META 0 +#define PVR_FW_PROCESSOR_TYPE_MIPS 1 +#define PVR_FW_PROCESSOR_TYPE_RISCV 2 + +#endif /* PVR_DEVICE_H */ diff --git a/drivers/gpu/drm/imagination/pvr_device_info.c b/drivers/gpu/drm/imagination/pvr_device_info.c new file mode 100644 index 000000000000..11e6bef52ecd --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_device_info.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_device_info.h" +#include "pvr_rogue_fwif_dev_info.h" + +#include <drm/drm_print.h> + +#include <linux/bits.h> +#include <linux/minmax.h> +#include <linux/stddef.h> +#include <linux/types.h> + +#define QUIRK_MAPPING(quirk) \ + [PVR_FW_HAS_BRN_##quirk] = offsetof(struct pvr_device, quirks.has_brn##quirk) + +static const uintptr_t quirks_mapping[] = { + QUIRK_MAPPING(44079), + QUIRK_MAPPING(47217), + QUIRK_MAPPING(48492), + QUIRK_MAPPING(48545), + QUIRK_MAPPING(49927), + QUIRK_MAPPING(50767), + QUIRK_MAPPING(51764), + QUIRK_MAPPING(62269), + QUIRK_MAPPING(63142), + QUIRK_MAPPING(63553), + QUIRK_MAPPING(66011), + QUIRK_MAPPING(71242), +}; + +#undef QUIRK_MAPPING + +#define ENHANCEMENT_MAPPING(enhancement) \ + [PVR_FW_HAS_ERN_##enhancement] = offsetof(struct pvr_device, \ + enhancements.has_ern##enhancement) + +static const uintptr_t enhancements_mapping[] = { + ENHANCEMENT_MAPPING(35421), + ENHANCEMENT_MAPPING(38020), + ENHANCEMENT_MAPPING(38748), + ENHANCEMENT_MAPPING(42064), + ENHANCEMENT_MAPPING(42290), + ENHANCEMENT_MAPPING(42606), + ENHANCEMENT_MAPPING(47025), + ENHANCEMENT_MAPPING(57596), +}; + +#undef ENHANCEMENT_MAPPING + +static void pvr_device_info_set_common(struct pvr_device *pvr_dev, const u64 *bitmask, + u32 bitmask_size, const uintptr_t *mapping, u32 mapping_max) +{ + const u32 mapping_max_size = (mapping_max + 63) >> 6; + const u32 nr_bits = min(bitmask_size * 64, mapping_max); + + /* Warn if any unsupported values in the bitmask. */ + if (bitmask_size > mapping_max_size) { + if (mapping == quirks_mapping) + drm_warn(from_pvr_device(pvr_dev), "Unsupported quirks in firmware image"); + else + drm_warn(from_pvr_device(pvr_dev), + "Unsupported enhancements in firmware image"); + } else if (bitmask_size == mapping_max_size && (mapping_max & 63)) { + u64 invalid_mask = ~0ull << (mapping_max & 63); + + if (bitmask[bitmask_size - 1] & invalid_mask) { + if (mapping == quirks_mapping) + drm_warn(from_pvr_device(pvr_dev), + "Unsupported quirks in firmware image"); + else + drm_warn(from_pvr_device(pvr_dev), + "Unsupported enhancements in firmware image"); + } + } + + for (u32 i = 0; i < nr_bits; i++) { + if (bitmask[i >> 6] & BIT_ULL(i & 63)) + *(bool *)((u8 *)pvr_dev + mapping[i]) = true; + } +} + +/** + * pvr_device_info_set_quirks() - Set device quirks from device information in firmware + * @pvr_dev: Device pointer. + * @quirks: Pointer to quirks mask in device information. + * @quirks_size: Size of quirks mask, in u64s. + */ +void pvr_device_info_set_quirks(struct pvr_device *pvr_dev, const u64 *quirks, u32 quirks_size) +{ + BUILD_BUG_ON(ARRAY_SIZE(quirks_mapping) != PVR_FW_HAS_BRN_MAX); + + pvr_device_info_set_common(pvr_dev, quirks, quirks_size, quirks_mapping, + ARRAY_SIZE(quirks_mapping)); +} + +/** + * pvr_device_info_set_enhancements() - Set device enhancements from device information in firmware + * @pvr_dev: Device pointer. + * @enhancements: Pointer to enhancements mask in device information. + * @enhancements_size: Size of enhancements mask, in u64s. + */ +void pvr_device_info_set_enhancements(struct pvr_device *pvr_dev, const u64 *enhancements, + u32 enhancements_size) +{ + BUILD_BUG_ON(ARRAY_SIZE(enhancements_mapping) != PVR_FW_HAS_ERN_MAX); + + pvr_device_info_set_common(pvr_dev, enhancements, enhancements_size, + enhancements_mapping, ARRAY_SIZE(enhancements_mapping)); +} + +#define FEATURE_MAPPING(fw_feature, feature) \ + [PVR_FW_HAS_FEATURE_##fw_feature] = { \ + .flag_offset = offsetof(struct pvr_device, features.has_##feature), \ + .value_offset = 0 \ + } + +#define FEATURE_MAPPING_VALUE(fw_feature, feature) \ + [PVR_FW_HAS_FEATURE_##fw_feature] = { \ + .flag_offset = offsetof(struct pvr_device, features.has_##feature), \ + .value_offset = offsetof(struct pvr_device, features.feature) \ + } + +static const struct { + uintptr_t flag_offset; + uintptr_t value_offset; +} features_mapping[] = { + FEATURE_MAPPING(AXI_ACELITE, axi_acelite), + FEATURE_MAPPING_VALUE(CDM_CONTROL_STREAM_FORMAT, cdm_control_stream_format), + FEATURE_MAPPING(CLUSTER_GROUPING, cluster_grouping), + FEATURE_MAPPING_VALUE(COMMON_STORE_SIZE_IN_DWORDS, common_store_size_in_dwords), + FEATURE_MAPPING(COMPUTE, compute), + FEATURE_MAPPING(COMPUTE_MORTON_CAPABLE, compute_morton_capable), + FEATURE_MAPPING(COMPUTE_OVERLAP, compute_overlap), + FEATURE_MAPPING(COREID_PER_OS, coreid_per_os), + FEATURE_MAPPING(DYNAMIC_DUST_POWER, dynamic_dust_power), + FEATURE_MAPPING_VALUE(ECC_RAMS, ecc_rams), + FEATURE_MAPPING_VALUE(FBCDC, fbcdc), + FEATURE_MAPPING_VALUE(FBCDC_ALGORITHM, fbcdc_algorithm), + FEATURE_MAPPING_VALUE(FBCDC_ARCHITECTURE, fbcdc_architecture), + FEATURE_MAPPING_VALUE(FBC_MAX_DEFAULT_DESCRIPTORS, fbc_max_default_descriptors), + FEATURE_MAPPING_VALUE(FBC_MAX_LARGE_DESCRIPTORS, fbc_max_large_descriptors), + FEATURE_MAPPING(FB_CDC_V4, fb_cdc_v4), + FEATURE_MAPPING(GPU_MULTICORE_SUPPORT, gpu_multicore_support), + FEATURE_MAPPING(GPU_VIRTUALISATION, gpu_virtualisation), + FEATURE_MAPPING(GS_RTA_SUPPORT, gs_rta_support), + FEATURE_MAPPING(IRQ_PER_OS, irq_per_os), + FEATURE_MAPPING_VALUE(ISP_MAX_TILES_IN_FLIGHT, isp_max_tiles_in_flight), + FEATURE_MAPPING_VALUE(ISP_SAMPLES_PER_PIXEL, isp_samples_per_pixel), + FEATURE_MAPPING(ISP_ZLS_D24_S8_PACKING_OGL_MODE, isp_zls_d24_s8_packing_ogl_mode), + FEATURE_MAPPING_VALUE(LAYOUT_MARS, layout_mars), + FEATURE_MAPPING_VALUE(MAX_PARTITIONS, max_partitions), + FEATURE_MAPPING_VALUE(META, meta), + FEATURE_MAPPING_VALUE(META_COREMEM_SIZE, meta_coremem_size), + FEATURE_MAPPING(MIPS, mips), + FEATURE_MAPPING_VALUE(NUM_CLUSTERS, num_clusters), + FEATURE_MAPPING_VALUE(NUM_ISP_IPP_PIPES, num_isp_ipp_pipes), + FEATURE_MAPPING_VALUE(NUM_OSIDS, num_osids), + FEATURE_MAPPING_VALUE(NUM_RASTER_PIPES, num_raster_pipes), + FEATURE_MAPPING(PBE2_IN_XE, pbe2_in_xe), + FEATURE_MAPPING(PBVNC_COREID_REG, pbvnc_coreid_reg), + FEATURE_MAPPING(PERFBUS, perfbus), + FEATURE_MAPPING(PERF_COUNTER_BATCH, perf_counter_batch), + FEATURE_MAPPING_VALUE(PHYS_BUS_WIDTH, phys_bus_width), + FEATURE_MAPPING(RISCV_FW_PROCESSOR, riscv_fw_processor), + FEATURE_MAPPING(ROGUEXE, roguexe), + FEATURE_MAPPING(S7_TOP_INFRASTRUCTURE, s7_top_infrastructure), + FEATURE_MAPPING(SIMPLE_INTERNAL_PARAMETER_FORMAT, simple_internal_parameter_format), + FEATURE_MAPPING(SIMPLE_INTERNAL_PARAMETER_FORMAT_V2, simple_internal_parameter_format_v2), + FEATURE_MAPPING_VALUE(SIMPLE_PARAMETER_FORMAT_VERSION, simple_parameter_format_version), + FEATURE_MAPPING_VALUE(SLC_BANKS, slc_banks), + FEATURE_MAPPING_VALUE(SLC_CACHE_LINE_SIZE_BITS, slc_cache_line_size_bits), + FEATURE_MAPPING(SLC_SIZE_CONFIGURABLE, slc_size_configurable), + FEATURE_MAPPING_VALUE(SLC_SIZE_IN_KILOBYTES, slc_size_in_kilobytes), + FEATURE_MAPPING(SOC_TIMER, soc_timer), + FEATURE_MAPPING(SYS_BUS_SECURE_RESET, sys_bus_secure_reset), + FEATURE_MAPPING(TESSELLATION, tessellation), + FEATURE_MAPPING(TILE_REGION_PROTECTION, tile_region_protection), + FEATURE_MAPPING_VALUE(TILE_SIZE_X, tile_size_x), + FEATURE_MAPPING_VALUE(TILE_SIZE_Y, tile_size_y), + FEATURE_MAPPING(TLA, tla), + FEATURE_MAPPING(TPU_CEM_DATAMASTER_GLOBAL_REGISTERS, tpu_cem_datamaster_global_registers), + FEATURE_MAPPING(TPU_DM_GLOBAL_REGISTERS, tpu_dm_global_registers), + FEATURE_MAPPING(TPU_FILTERING_MODE_CONTROL, tpu_filtering_mode_control), + FEATURE_MAPPING_VALUE(USC_MIN_OUTPUT_REGISTERS_PER_PIX, usc_min_output_registers_per_pix), + FEATURE_MAPPING(VDM_DRAWINDIRECT, vdm_drawindirect), + FEATURE_MAPPING(VDM_OBJECT_LEVEL_LLS, vdm_object_level_lls), + FEATURE_MAPPING_VALUE(VIRTUAL_ADDRESS_SPACE_BITS, virtual_address_space_bits), + FEATURE_MAPPING(WATCHDOG_TIMER, watchdog_timer), + FEATURE_MAPPING(WORKGROUP_PROTECTION, workgroup_protection), + FEATURE_MAPPING_VALUE(XE_ARCHITECTURE, xe_architecture), + FEATURE_MAPPING(XE_MEMORY_HIERARCHY, xe_memory_hierarchy), + FEATURE_MAPPING(XE_TPU2, xe_tpu2), + FEATURE_MAPPING_VALUE(XPU_MAX_REGBANKS_ADDR_WIDTH, xpu_max_regbanks_addr_width), + FEATURE_MAPPING_VALUE(XPU_MAX_SLAVES, xpu_max_slaves), + FEATURE_MAPPING_VALUE(XPU_REGISTER_BROADCAST, xpu_register_broadcast), + FEATURE_MAPPING(XT_TOP_INFRASTRUCTURE, xt_top_infrastructure), + FEATURE_MAPPING(ZLS_SUBTILE, zls_subtile), +}; + +#undef FEATURE_MAPPING_VALUE +#undef FEATURE_MAPPING + +/** + * pvr_device_info_set_features() - Set device features from device information in firmware + * @pvr_dev: Device pointer. + * @features: Pointer to features mask in device information. + * @features_size: Size of features mask, in u64s. + * @feature_param_size: Size of feature parameters, in u64s. + * + * Returns: + * * 0 on success, or + * * -%EINVAL on malformed stream. + */ +int pvr_device_info_set_features(struct pvr_device *pvr_dev, const u64 *features, u32 features_size, + u32 feature_param_size) +{ + const u32 mapping_max = ARRAY_SIZE(features_mapping); + const u32 mapping_max_size = (mapping_max + 63) >> 6; + const u32 nr_bits = min(features_size * 64, mapping_max); + const u64 *feature_params = features + features_size; + u32 param_idx = 0; + + BUILD_BUG_ON(ARRAY_SIZE(features_mapping) != PVR_FW_HAS_FEATURE_MAX); + + /* Verify no unsupported values in the bitmask. */ + if (features_size > mapping_max_size) { + drm_warn(from_pvr_device(pvr_dev), "Unsupported features in firmware image"); + } else if (features_size == mapping_max_size && (mapping_max & 63)) { + u64 invalid_mask = ~0ull << (mapping_max & 63); + + if (features[features_size - 1] & invalid_mask) + drm_warn(from_pvr_device(pvr_dev), + "Unsupported features in firmware image"); + } + + for (u32 i = 0; i < nr_bits; i++) { + if (features[i >> 6] & BIT_ULL(i & 63)) { + *(bool *)((u8 *)pvr_dev + features_mapping[i].flag_offset) = true; + + if (features_mapping[i].value_offset) { + if (param_idx >= feature_param_size) + return -EINVAL; + + *(u64 *)((u8 *)pvr_dev + features_mapping[i].value_offset) = + feature_params[param_idx]; + param_idx++; + } + } + } + + return 0; +} diff --git a/drivers/gpu/drm/imagination/pvr_device_info.h b/drivers/gpu/drm/imagination/pvr_device_info.h new file mode 100644 index 000000000000..f61fb988b553 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_device_info.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_DEVICE_INFO_H +#define PVR_DEVICE_INFO_H + +#include <linux/types.h> + +struct pvr_device; + +/* + * struct pvr_device_features - Hardware feature information + */ +struct pvr_device_features { + bool has_axi_acelite; + bool has_cdm_control_stream_format; + bool has_cluster_grouping; + bool has_common_store_size_in_dwords; + bool has_compute; + bool has_compute_morton_capable; + bool has_compute_overlap; + bool has_coreid_per_os; + bool has_dynamic_dust_power; + bool has_ecc_rams; + bool has_fb_cdc_v4; + bool has_fbc_max_default_descriptors; + bool has_fbc_max_large_descriptors; + bool has_fbcdc; + bool has_fbcdc_algorithm; + bool has_fbcdc_architecture; + bool has_gpu_multicore_support; + bool has_gpu_virtualisation; + bool has_gs_rta_support; + bool has_irq_per_os; + bool has_isp_max_tiles_in_flight; + bool has_isp_samples_per_pixel; + bool has_isp_zls_d24_s8_packing_ogl_mode; + bool has_layout_mars; + bool has_max_partitions; + bool has_meta; + bool has_meta_coremem_size; + bool has_mips; + bool has_num_clusters; + bool has_num_isp_ipp_pipes; + bool has_num_osids; + bool has_num_raster_pipes; + bool has_pbe2_in_xe; + bool has_pbvnc_coreid_reg; + bool has_perfbus; + bool has_perf_counter_batch; + bool has_phys_bus_width; + bool has_riscv_fw_processor; + bool has_roguexe; + bool has_s7_top_infrastructure; + bool has_simple_internal_parameter_format; + bool has_simple_internal_parameter_format_v2; + bool has_simple_parameter_format_version; + bool has_slc_banks; + bool has_slc_cache_line_size_bits; + bool has_slc_size_configurable; + bool has_slc_size_in_kilobytes; + bool has_soc_timer; + bool has_sys_bus_secure_reset; + bool has_tessellation; + bool has_tile_region_protection; + bool has_tile_size_x; + bool has_tile_size_y; + bool has_tla; + bool has_tpu_cem_datamaster_global_registers; + bool has_tpu_dm_global_registers; + bool has_tpu_filtering_mode_control; + bool has_usc_min_output_registers_per_pix; + bool has_vdm_drawindirect; + bool has_vdm_object_level_lls; + bool has_virtual_address_space_bits; + bool has_watchdog_timer; + bool has_workgroup_protection; + bool has_xe_architecture; + bool has_xe_memory_hierarchy; + bool has_xe_tpu2; + bool has_xpu_max_regbanks_addr_width; + bool has_xpu_max_slaves; + bool has_xpu_register_broadcast; + bool has_xt_top_infrastructure; + bool has_zls_subtile; + + u64 cdm_control_stream_format; + u64 common_store_size_in_dwords; + u64 ecc_rams; + u64 fbc_max_default_descriptors; + u64 fbc_max_large_descriptors; + u64 fbcdc; + u64 fbcdc_algorithm; + u64 fbcdc_architecture; + u64 isp_max_tiles_in_flight; + u64 isp_samples_per_pixel; + u64 layout_mars; + u64 max_partitions; + u64 meta; + u64 meta_coremem_size; + u64 num_clusters; + u64 num_isp_ipp_pipes; + u64 num_osids; + u64 num_raster_pipes; + u64 phys_bus_width; + u64 simple_parameter_format_version; + u64 slc_banks; + u64 slc_cache_line_size_bits; + u64 slc_size_in_kilobytes; + u64 tile_size_x; + u64 tile_size_y; + u64 usc_min_output_registers_per_pix; + u64 virtual_address_space_bits; + u64 xe_architecture; + u64 xpu_max_regbanks_addr_width; + u64 xpu_max_slaves; + u64 xpu_register_broadcast; +}; + +/* + * struct pvr_device_quirks - Hardware quirk information + */ +struct pvr_device_quirks { + bool has_brn44079; + bool has_brn47217; + bool has_brn48492; + bool has_brn48545; + bool has_brn49927; + bool has_brn50767; + bool has_brn51764; + bool has_brn62269; + bool has_brn63142; + bool has_brn63553; + bool has_brn66011; + bool has_brn71242; +}; + +/* + * struct pvr_device_enhancements - Hardware enhancement information + */ +struct pvr_device_enhancements { + bool has_ern35421; + bool has_ern38020; + bool has_ern38748; + bool has_ern42064; + bool has_ern42290; + bool has_ern42606; + bool has_ern47025; + bool has_ern57596; +}; + +void pvr_device_info_set_quirks(struct pvr_device *pvr_dev, const u64 *bitmask, + u32 bitmask_len); +void pvr_device_info_set_enhancements(struct pvr_device *pvr_dev, const u64 *bitmask, + u32 bitmask_len); +int pvr_device_info_set_features(struct pvr_device *pvr_dev, const u64 *features, u32 features_size, + u32 feature_param_size); + +/* + * Meta cores + * + * These are the values for the 'meta' feature when the feature is present + * (as per &struct pvr_device_features)/ + */ +#define PVR_META_MTP218 (1) +#define PVR_META_MTP219 (2) +#define PVR_META_LTP218 (3) +#define PVR_META_LTP217 (4) + +enum { + PVR_FEATURE_CDM_USER_MODE_QUEUE, + PVR_FEATURE_CLUSTER_GROUPING, + PVR_FEATURE_COMPUTE_MORTON_CAPABLE, + PVR_FEATURE_FB_CDC_V4, + PVR_FEATURE_GPU_MULTICORE_SUPPORT, + PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE, + PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP, + PVR_FEATURE_S7_TOP_INFRASTRUCTURE, + PVR_FEATURE_TESSELLATION, + PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS, + PVR_FEATURE_VDM_DRAWINDIRECT, + PVR_FEATURE_VDM_OBJECT_LEVEL_LLS, + PVR_FEATURE_ZLS_SUBTILE, +}; + +#endif /* PVR_DEVICE_INFO_H */ diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c new file mode 100644 index 000000000000..5c3b2d58d766 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -0,0 +1,1501 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_context.h" +#include "pvr_debugfs.h" +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_free_list.h" +#include "pvr_gem.h" +#include "pvr_hwrt.h" +#include "pvr_job.h" +#include "pvr_mmu.h" +#include "pvr_power.h" +#include "pvr_rogue_defs.h" +#include "pvr_rogue_fwif_client.h" +#include "pvr_rogue_fwif_shared.h" +#include "pvr_vm.h" + +#include <uapi/drm/pvr_drm.h> + +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_ioctl.h> + +#include <linux/err.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/xarray.h> + +/** + * DOC: PowerVR (Series 6 and later) and IMG Graphics Driver + * + * This driver supports the following PowerVR/IMG graphics cores from Imagination Technologies: + * + * * AXE-1-16M (found in Texas Instruments AM62) + */ + +/** + * pvr_ioctl_create_bo() - IOCTL to create a GEM buffer object. + * @drm_dev: [IN] Target DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_create_bo_args. + * @file: [IN] DRM file-private data. + * + * Called from userspace with %DRM_IOCTL_PVR_CREATE_BO. + * + * Return: + * * 0 on success, + * * -%EINVAL if the value of &drm_pvr_ioctl_create_bo_args.size is zero + * or wider than &typedef size_t, + * * -%EINVAL if any bits in &drm_pvr_ioctl_create_bo_args.flags that are + * reserved or undefined are set, + * * -%EINVAL if any padding fields in &drm_pvr_ioctl_create_bo_args are not + * zero, + * * Any error encountered while creating the object (see + * pvr_gem_object_create()), or + * * Any error encountered while transferring ownership of the object into a + * userspace-accessible handle (see pvr_gem_object_into_handle()). + */ +static int +pvr_ioctl_create_bo(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_create_bo_args *args = raw_args; + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct pvr_file *pvr_file = to_pvr_file(file); + + struct pvr_gem_object *pvr_obj; + size_t sanitized_size; + + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + /* All padding fields must be zeroed. */ + if (args->_padding_c != 0) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + /* + * On 64-bit platforms (our primary target), size_t is a u64. However, + * on other architectures we have to check for overflow when casting + * down to size_t from u64. + * + * We also disallow zero-sized allocations, and reserved (kernel-only) + * flags. + */ + if (args->size > SIZE_MAX || args->size == 0 || args->flags & + ~DRM_PVR_BO_FLAGS_MASK || args->size & (PVR_DEVICE_PAGE_SIZE - 1)) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + sanitized_size = (size_t)args->size; + + /* + * Create a buffer object and transfer ownership to a userspace- + * accessible handle. + */ + pvr_obj = pvr_gem_object_create(pvr_dev, sanitized_size, args->flags); + if (IS_ERR(pvr_obj)) { + err = PTR_ERR(pvr_obj); + goto err_drm_dev_exit; + } + + /* This function will not modify &args->handle unless it succeeds. */ + err = pvr_gem_object_into_handle(pvr_obj, pvr_file, &args->handle); + if (err) + goto err_destroy_obj; + + drm_dev_exit(idx); + + return 0; + +err_destroy_obj: + /* + * GEM objects are refcounted, so there is no explicit destructor + * function. Instead, we release the singular reference we currently + * hold on the object and let GEM take care of the rest. + */ + pvr_gem_object_put(pvr_obj); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * pvr_ioctl_get_bo_mmap_offset() - IOCTL to generate a "fake" offset to be + * used when calling mmap() from userspace to map the given GEM buffer object + * @drm_dev: [IN] DRM device (unused). + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_get_bo_mmap_offset_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_GET_BO_MMAP_OFFSET. + * + * This IOCTL does *not* perform an mmap. See the docs on + * &struct drm_pvr_ioctl_get_bo_mmap_offset_args for details. + * + * Return: + * * 0 on success, + * * -%ENOENT if the handle does not reference a valid GEM buffer object, + * * -%EINVAL if any padding fields in &struct + * drm_pvr_ioctl_get_bo_mmap_offset_args are not zero, or + * * Any error returned by drm_gem_create_mmap_offset(). + */ +static int +pvr_ioctl_get_bo_mmap_offset(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_get_bo_mmap_offset_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_gem_object *pvr_obj; + struct drm_gem_object *gem_obj; + int idx; + int ret; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + /* All padding fields must be zeroed. */ + if (args->_padding_4 != 0) { + ret = -EINVAL; + goto err_drm_dev_exit; + } + + /* + * Obtain a kernel reference to the buffer object. This reference is + * counted and must be manually dropped before returning. If a buffer + * object cannot be found for the specified handle, return -%ENOENT (No + * such file or directory). + */ + pvr_obj = pvr_gem_object_from_handle(pvr_file, args->handle); + if (!pvr_obj) { + ret = -ENOENT; + goto err_drm_dev_exit; + } + + gem_obj = gem_from_pvr_gem(pvr_obj); + + /* + * Allocate a fake offset which can be used in userspace calls to mmap + * on the DRM device file. If this fails, return the error code. This + * operation is idempotent. + */ + ret = drm_gem_create_mmap_offset(gem_obj); + if (ret != 0) { + /* Drop our reference to the buffer object. */ + drm_gem_object_put(gem_obj); + goto err_drm_dev_exit; + } + + /* + * Read out the fake offset allocated by the earlier call to + * drm_gem_create_mmap_offset. + */ + args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); + + /* Drop our reference to the buffer object. */ + pvr_gem_object_put(pvr_obj); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return ret; +} + +static __always_inline u64 +pvr_fw_version_packed(u32 major, u32 minor) +{ + return ((u64)major << 32) | minor; +} + +static u32 +rogue_get_common_store_partition_space_size(struct pvr_device *pvr_dev) +{ + u32 max_partitions = 0; + u32 tile_size_x = 0; + u32 tile_size_y = 0; + + PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &tile_size_x); + PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &tile_size_y); + PVR_FEATURE_VALUE(pvr_dev, max_partitions, &max_partitions); + + if (tile_size_x == 16 && tile_size_y == 16) { + u32 usc_min_output_registers_per_pix = 0; + + PVR_FEATURE_VALUE(pvr_dev, usc_min_output_registers_per_pix, + &usc_min_output_registers_per_pix); + + return tile_size_x * tile_size_y * max_partitions * + usc_min_output_registers_per_pix; + } + + return max_partitions * 1024; +} + +static u32 +rogue_get_common_store_alloc_region_size(struct pvr_device *pvr_dev) +{ + u32 common_store_size_in_dwords = 512 * 4 * 4; + u32 alloc_region_size; + + PVR_FEATURE_VALUE(pvr_dev, common_store_size_in_dwords, &common_store_size_in_dwords); + + alloc_region_size = common_store_size_in_dwords - (256U * 4U) - + rogue_get_common_store_partition_space_size(pvr_dev); + + if (PVR_HAS_QUIRK(pvr_dev, 44079)) { + u32 common_store_split_point = (768U * 4U * 4U); + + return min(common_store_split_point - (256U * 4U), alloc_region_size); + } + + return alloc_region_size; +} + +static inline u32 +rogue_get_num_phantoms(struct pvr_device *pvr_dev) +{ + u32 num_clusters = 1; + + PVR_FEATURE_VALUE(pvr_dev, num_clusters, &num_clusters); + + return ROGUE_REQ_NUM_PHANTOMS(num_clusters); +} + +static inline u32 +rogue_get_max_coeffs(struct pvr_device *pvr_dev) +{ + u32 max_coeff_additional_portion = ROGUE_MAX_VERTEX_SHARED_REGISTERS; + u32 pending_allocation_shared_regs = 2U * 1024U; + u32 pending_allocation_coeff_regs = 0U; + u32 num_phantoms = rogue_get_num_phantoms(pvr_dev); + u32 tiles_in_flight = 0; + u32 max_coeff_pixel_portion; + + PVR_FEATURE_VALUE(pvr_dev, isp_max_tiles_in_flight, &tiles_in_flight); + max_coeff_pixel_portion = DIV_ROUND_UP(tiles_in_flight, num_phantoms); + max_coeff_pixel_portion *= ROGUE_MAX_PIXEL_SHARED_REGISTERS; + + /* + * Compute tasks on cores with BRN48492 and without compute overlap may lock + * up without two additional lines of coeffs. + */ + if (PVR_HAS_QUIRK(pvr_dev, 48492) && !PVR_HAS_FEATURE(pvr_dev, compute_overlap)) + pending_allocation_coeff_regs = 2U * 1024U; + + if (PVR_HAS_ENHANCEMENT(pvr_dev, 38748)) + pending_allocation_shared_regs = 0; + + if (PVR_HAS_ENHANCEMENT(pvr_dev, 38020)) + max_coeff_additional_portion += ROGUE_MAX_COMPUTE_SHARED_REGISTERS; + + return rogue_get_common_store_alloc_region_size(pvr_dev) + pending_allocation_coeff_regs - + (max_coeff_pixel_portion + max_coeff_additional_portion + + pending_allocation_shared_regs); +} + +static inline u32 +rogue_get_cdm_max_local_mem_size_regs(struct pvr_device *pvr_dev) +{ + u32 available_coeffs_in_dwords = rogue_get_max_coeffs(pvr_dev); + + if (PVR_HAS_QUIRK(pvr_dev, 48492) && PVR_HAS_FEATURE(pvr_dev, roguexe) && + !PVR_HAS_FEATURE(pvr_dev, compute_overlap)) { + /* Driver must not use the 2 reserved lines. */ + available_coeffs_in_dwords -= ROGUE_CSRM_LINE_SIZE_IN_DWORDS * 2; + } + + /* + * The maximum amount of local memory available to a kernel is the minimum + * of the total number of coefficient registers available and the max common + * store allocation size which can be made by the CDM. + * + * If any coeff lines are reserved for tessellation or pixel then we need to + * subtract those too. + */ + return min(available_coeffs_in_dwords, (u32)ROGUE_MAX_PER_KERNEL_LOCAL_MEM_SIZE_REGS); +} + +/** + * pvr_dev_query_gpu_info_get() + * @pvr_dev: Device pointer. + * @args: [IN] Device query arguments containing a pointer to a userspace + * struct drm_pvr_dev_query_gpu_info. + * + * If the query object pointer is NULL, the size field is updated with the + * expected size of the query object. + * + * Returns: + * * 0 on success, or if size is requested using a NULL pointer, or + * * -%E2BIG if the indicated length of the allocation is less than is + * required to contain the copied data, or + * * -%EFAULT if local memory could not be copied to userspace. + */ +static int +pvr_dev_query_gpu_info_get(struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + struct drm_pvr_dev_query_gpu_info gpu_info = {0}; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_gpu_info); + return 0; + } + + gpu_info.gpu_id = + pvr_gpu_id_to_packed_bvnc(&pvr_dev->gpu_id); + gpu_info.num_phantoms = rogue_get_num_phantoms(pvr_dev); + + err = PVR_UOBJ_SET(args->pointer, args->size, gpu_info); + if (err < 0) + return err; + + if (args->size > sizeof(gpu_info)) + args->size = sizeof(gpu_info); + return 0; +} + +/** + * pvr_dev_query_runtime_info_get() + * @pvr_dev: Device pointer. + * @args: [IN] Device query arguments containing a pointer to a userspace + * struct drm_pvr_dev_query_runtime_info. + * + * If the query object pointer is NULL, the size field is updated with the + * expected size of the query object. + * + * Returns: + * * 0 on success, or if size is requested using a NULL pointer, or + * * -%E2BIG if the indicated length of the allocation is less than is + * required to contain the copied data, or + * * -%EFAULT if local memory could not be copied to userspace. + */ +static int +pvr_dev_query_runtime_info_get(struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + struct drm_pvr_dev_query_runtime_info runtime_info = {0}; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_runtime_info); + return 0; + } + + runtime_info.free_list_min_pages = + pvr_get_free_list_min_pages(pvr_dev); + runtime_info.free_list_max_pages = + ROGUE_PM_MAX_FREELIST_SIZE / ROGUE_PM_PAGE_SIZE; + runtime_info.common_store_alloc_region_size = + rogue_get_common_store_alloc_region_size(pvr_dev); + runtime_info.common_store_partition_space_size = + rogue_get_common_store_partition_space_size(pvr_dev); + runtime_info.max_coeffs = rogue_get_max_coeffs(pvr_dev); + runtime_info.cdm_max_local_mem_size_regs = + rogue_get_cdm_max_local_mem_size_regs(pvr_dev); + + err = PVR_UOBJ_SET(args->pointer, args->size, runtime_info); + if (err < 0) + return err; + + if (args->size > sizeof(runtime_info)) + args->size = sizeof(runtime_info); + return 0; +} + +/** + * pvr_dev_query_quirks_get() - Unpack array of quirks at the address given + * in a struct drm_pvr_dev_query_quirks, or gets the amount of space required + * for it. + * @pvr_dev: Device pointer. + * @args: [IN] Device query arguments containing a pointer to a userspace + * struct drm_pvr_dev_query_query_quirks. + * + * If the query object pointer is NULL, the size field is updated with the + * expected size of the query object. + * If the userspace pointer in the query object is NULL, or the count is + * short, no data is copied. + * The count field will be updated to that copied, or if either pointer is + * NULL, that which would have been copied. + * The size field in the query object will be updated to the size copied. + * + * Returns: + * * 0 on success, or if size/count is requested using a NULL pointer, or + * * -%EINVAL if args contained non-zero reserved fields, or + * * -%E2BIG if the indicated length of the allocation is less than is + * required to contain the copied data, or + * * -%EFAULT if local memory could not be copied to userspace. + */ +static int +pvr_dev_query_quirks_get(struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + /* + * @FIXME - hardcoding of numbers here is intended as an + * intermediate step so the UAPI can be fixed, but requires a + * a refactor in the future to store them in a more appropriate + * location + */ + static const u32 umd_quirks_musthave[] = { + 47217, + 49927, + 62269, + }; + static const u32 umd_quirks[] = { + 48545, + 51764, + }; + struct drm_pvr_dev_query_quirks query; + u32 out[ARRAY_SIZE(umd_quirks_musthave) + ARRAY_SIZE(umd_quirks)]; + size_t out_musthave_count = 0; + size_t out_count = 0; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_quirks); + return 0; + } + + err = PVR_UOBJ_GET(query, args->size, args->pointer); + + if (err < 0) + return err; + if (query._padding_c) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(umd_quirks_musthave); i++) { + if (pvr_device_has_uapi_quirk(pvr_dev, umd_quirks_musthave[i])) { + out[out_count++] = umd_quirks_musthave[i]; + out_musthave_count++; + } + } + + for (int i = 0; i < ARRAY_SIZE(umd_quirks); i++) { + if (pvr_device_has_uapi_quirk(pvr_dev, umd_quirks[i])) + out[out_count++] = umd_quirks[i]; + } + + if (!query.quirks) + goto copy_out; + if (query.count < out_count) + return -E2BIG; + + if (copy_to_user(u64_to_user_ptr(query.quirks), out, + out_count * sizeof(u32))) { + return -EFAULT; + } + + query.musthave_count = out_musthave_count; + +copy_out: + query.count = out_count; + err = PVR_UOBJ_SET(args->pointer, args->size, query); + if (err < 0) + return err; + + args->size = sizeof(query); + return 0; +} + +/** + * pvr_dev_query_enhancements_get() - Unpack array of enhancements at the + * address given in a struct drm_pvr_dev_query_enhancements, or gets the amount + * of space required for it. + * @pvr_dev: Device pointer. + * @args: [IN] Device query arguments containing a pointer to a userspace + * struct drm_pvr_dev_query_enhancements. + * + * If the query object pointer is NULL, the size field is updated with the + * expected size of the query object. + * If the userspace pointer in the query object is NULL, or the count is + * short, no data is copied. + * The count field will be updated to that copied, or if either pointer is + * NULL, that which would have been copied. + * The size field in the query object will be updated to the size copied. + * + * Returns: + * * 0 on success, or if size/count is requested using a NULL pointer, or + * * -%EINVAL if args contained non-zero reserved fields, or + * * -%E2BIG if the indicated length of the allocation is less than is + * required to contain the copied data, or + * * -%EFAULT if local memory could not be copied to userspace. + */ +static int +pvr_dev_query_enhancements_get(struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + /* + * @FIXME - hardcoding of numbers here is intended as an + * intermediate step so the UAPI can be fixed, but requires a + * a refactor in the future to store them in a more appropriate + * location + */ + const u32 umd_enhancements[] = { + 35421, + 42064, + }; + struct drm_pvr_dev_query_enhancements query; + u32 out[ARRAY_SIZE(umd_enhancements)]; + size_t out_idx = 0; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_enhancements); + return 0; + } + + err = PVR_UOBJ_GET(query, args->size, args->pointer); + + if (err < 0) + return err; + if (query._padding_a) + return -EINVAL; + if (query._padding_c) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(umd_enhancements); i++) { + if (pvr_device_has_uapi_enhancement(pvr_dev, umd_enhancements[i])) + out[out_idx++] = umd_enhancements[i]; + } + + if (!query.enhancements) + goto copy_out; + if (query.count < out_idx) + return -E2BIG; + + if (copy_to_user(u64_to_user_ptr(query.enhancements), out, + out_idx * sizeof(u32))) { + return -EFAULT; + } + +copy_out: + query.count = out_idx; + err = PVR_UOBJ_SET(args->pointer, args->size, query); + if (err < 0) + return err; + + args->size = sizeof(query); + return 0; +} + +/** + * pvr_ioctl_dev_query() - IOCTL to copy information about a device + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_dev_query_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_DEV_QUERY. + * If the given receiving struct pointer is NULL, or the indicated size is too + * small, the expected size of the struct type will be returned in the size + * argument field. + * + * Return: + * * 0 on success or when fetching the size with args->pointer == NULL, or + * * -%E2BIG if the indicated size of the receiving struct is less than is + * required to contain the copied data, or + * * -%EINVAL if the indicated struct type is unknown, or + * * -%ENOMEM if local memory could not be allocated, or + * * -%EFAULT if local memory could not be copied to userspace. + */ +static int +pvr_ioctl_dev_query(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct drm_pvr_ioctl_dev_query_args *args = raw_args; + int idx; + int ret = -EINVAL; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + switch ((enum drm_pvr_dev_query)args->type) { + case DRM_PVR_DEV_QUERY_GPU_INFO_GET: + ret = pvr_dev_query_gpu_info_get(pvr_dev, args); + break; + + case DRM_PVR_DEV_QUERY_RUNTIME_INFO_GET: + ret = pvr_dev_query_runtime_info_get(pvr_dev, args); + break; + + case DRM_PVR_DEV_QUERY_QUIRKS_GET: + ret = pvr_dev_query_quirks_get(pvr_dev, args); + break; + + case DRM_PVR_DEV_QUERY_ENHANCEMENTS_GET: + ret = pvr_dev_query_enhancements_get(pvr_dev, args); + break; + + case DRM_PVR_DEV_QUERY_HEAP_INFO_GET: + ret = pvr_heap_info_get(pvr_dev, args); + break; + + case DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET: + ret = pvr_static_data_areas_get(pvr_dev, args); + break; + } + + drm_dev_exit(idx); + + return ret; +} + +/** + * pvr_ioctl_create_context() - IOCTL to create a context + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_create_context_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_CREATE_CONTEXT. + * + * Return: + * * 0 on success, or + * * -%EINVAL if provided arguments are invalid, or + * * -%EFAULT if arguments can't be copied from userspace, or + * * Any error returned by pvr_create_render_context(). + */ +static int +pvr_ioctl_create_context(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_create_context_args *args = raw_args; + struct pvr_file *pvr_file = file->driver_priv; + int idx; + int ret; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + ret = pvr_context_create(pvr_file, args); + + drm_dev_exit(idx); + + return ret; +} + +/** + * pvr_ioctl_destroy_context() - IOCTL to destroy a context + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_destroy_context_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_DESTROY_CONTEXT. + * + * Return: + * * 0 on success, or + * * -%EINVAL if context not in context list. + */ +static int +pvr_ioctl_destroy_context(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_destroy_context_args *args = raw_args; + struct pvr_file *pvr_file = file->driver_priv; + + if (args->_padding_4) + return -EINVAL; + + return pvr_context_destroy(pvr_file, args->handle); +} + +/** + * pvr_ioctl_create_free_list() - IOCTL to create a free list + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_create_free_list_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_CREATE_FREE_LIST. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_free_list_create(). + */ +static int +pvr_ioctl_create_free_list(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_create_free_list_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_free_list *free_list; + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + free_list = pvr_free_list_create(pvr_file, args); + if (IS_ERR(free_list)) { + err = PTR_ERR(free_list); + goto err_drm_dev_exit; + } + + /* Allocate object handle for userspace. */ + err = xa_alloc(&pvr_file->free_list_handles, + &args->handle, + free_list, + xa_limit_32b, + GFP_KERNEL); + if (err < 0) + goto err_cleanup; + + drm_dev_exit(idx); + + return 0; + +err_cleanup: + pvr_free_list_put(free_list); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * pvr_ioctl_destroy_free_list() - IOCTL to destroy a free list + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_destroy_free_list_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_DESTROY_FREE_LIST. + * + * Return: + * * 0 on success, or + * * -%EINVAL if free list not in object list. + */ +static int +pvr_ioctl_destroy_free_list(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_destroy_free_list_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_free_list *free_list; + + if (args->_padding_4) + return -EINVAL; + + free_list = xa_erase(&pvr_file->free_list_handles, args->handle); + if (!free_list) + return -EINVAL; + + pvr_free_list_put(free_list); + return 0; +} + +/** + * pvr_ioctl_create_hwrt_dataset() - IOCTL to create a HWRT dataset + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_create_hwrt_dataset_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_CREATE_HWRT_DATASET. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_hwrt_dataset_create(). + */ +static int +pvr_ioctl_create_hwrt_dataset(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_create_hwrt_dataset_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_hwrt_dataset *hwrt; + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + hwrt = pvr_hwrt_dataset_create(pvr_file, args); + if (IS_ERR(hwrt)) { + err = PTR_ERR(hwrt); + goto err_drm_dev_exit; + } + + /* Allocate object handle for userspace. */ + err = xa_alloc(&pvr_file->hwrt_handles, + &args->handle, + hwrt, + xa_limit_32b, + GFP_KERNEL); + if (err < 0) + goto err_cleanup; + + drm_dev_exit(idx); + + return 0; + +err_cleanup: + pvr_hwrt_dataset_put(hwrt); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * pvr_ioctl_destroy_hwrt_dataset() - IOCTL to destroy a HWRT dataset + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_destroy_hwrt_dataset_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_DESTROY_HWRT_DATASET. + * + * Return: + * * 0 on success, or + * * -%EINVAL if HWRT dataset not in object list. + */ +static int +pvr_ioctl_destroy_hwrt_dataset(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_destroy_hwrt_dataset_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_hwrt_dataset *hwrt; + + if (args->_padding_4) + return -EINVAL; + + hwrt = xa_erase(&pvr_file->hwrt_handles, args->handle); + if (!hwrt) + return -EINVAL; + + pvr_hwrt_dataset_put(hwrt); + return 0; +} + +/** + * pvr_ioctl_create_vm_context() - IOCTL to create a VM context + * @drm_dev: [IN] DRM device. + * @raw_args: [IN/OUT] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_create_vm_context_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_CREATE_VM_CONTEXT. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_vm_create_context(). + */ +static int +pvr_ioctl_create_vm_context(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_create_vm_context_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_vm_context *vm_ctx; + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + if (args->_padding_4) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + vm_ctx = pvr_vm_create_context(pvr_file->pvr_dev, true); + if (IS_ERR(vm_ctx)) { + err = PTR_ERR(vm_ctx); + goto err_drm_dev_exit; + } + + /* Allocate object handle for userspace. */ + err = xa_alloc(&pvr_file->vm_ctx_handles, + &args->handle, + vm_ctx, + xa_limit_32b, + GFP_KERNEL); + if (err < 0) + goto err_cleanup; + + drm_dev_exit(idx); + + return 0; + +err_cleanup: + pvr_vm_context_put(vm_ctx); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * pvr_ioctl_destroy_vm_context() - IOCTL to destroy a VM context +* @drm_dev: [IN] DRM device. +* @raw_args: [IN] Arguments passed to this IOCTL. This must be of type +* &struct drm_pvr_ioctl_destroy_vm_context_args. +* @file: [IN] DRM file private data. +* +* Called from userspace with %DRM_IOCTL_PVR_DESTROY_VM_CONTEXT. +* +* Return: +* * 0 on success, or +* * -%EINVAL if object not in object list. + */ +static int +pvr_ioctl_destroy_vm_context(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_destroy_vm_context_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_vm_context *vm_ctx; + + if (args->_padding_4) + return -EINVAL; + + vm_ctx = xa_erase(&pvr_file->vm_ctx_handles, args->handle); + if (!vm_ctx) + return -EINVAL; + + pvr_vm_context_put(vm_ctx); + return 0; +} + +/** + * pvr_ioctl_vm_map() - IOCTL to map buffer to GPU address space. + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_vm_map_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_VM_MAP. + * + * Return: + * * 0 on success, + * * -%EINVAL if &drm_pvr_ioctl_vm_op_map_args.flags is not zero, + * * -%EINVAL if the bounds specified by &drm_pvr_ioctl_vm_op_map_args.offset + * and &drm_pvr_ioctl_vm_op_map_args.size are not valid or do not fall + * within the buffer object specified by + * &drm_pvr_ioctl_vm_op_map_args.handle, + * * -%EINVAL if the bounds specified by + * &drm_pvr_ioctl_vm_op_map_args.device_addr and + * &drm_pvr_ioctl_vm_op_map_args.size do not form a valid device-virtual + * address range which falls entirely within a single heap, or + * * -%ENOENT if &drm_pvr_ioctl_vm_op_map_args.handle does not refer to a + * valid PowerVR buffer object. + */ +static int +pvr_ioctl_vm_map(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct drm_pvr_ioctl_vm_map_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_vm_context *vm_ctx; + + struct pvr_gem_object *pvr_obj; + size_t pvr_obj_size; + + u64 offset_plus_size; + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + /* Initial validation of args. */ + if (args->_padding_14) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + if (args->flags != 0 || + check_add_overflow(args->offset, args->size, &offset_plus_size) || + !pvr_find_heap_containing(pvr_dev, args->device_addr, args->size)) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); + if (!vm_ctx) { + err = -EINVAL; + goto err_drm_dev_exit; + } + + pvr_obj = pvr_gem_object_from_handle(pvr_file, args->handle); + if (!pvr_obj) { + err = -ENOENT; + goto err_put_vm_context; + } + + pvr_obj_size = pvr_gem_object_size(pvr_obj); + + /* + * Validate offset and size args. The alignment of these will be + * checked when mapping; for now just check that they're within valid + * bounds + */ + if (args->offset >= pvr_obj_size || offset_plus_size > pvr_obj_size) { + err = -EINVAL; + goto err_put_pvr_object; + } + + err = pvr_vm_map(vm_ctx, pvr_obj, args->offset, + args->device_addr, args->size); + if (err) + goto err_put_pvr_object; + + /* + * In order to set up the mapping, we needed a reference to &pvr_obj. + * However, pvr_vm_map() obtains and stores its own reference, so we + * must release ours before returning. + */ + +err_put_pvr_object: + pvr_gem_object_put(pvr_obj); + +err_put_vm_context: + pvr_vm_context_put(vm_ctx); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * pvr_ioctl_vm_unmap() - IOCTL to unmap buffer from GPU address space. + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_vm_unmap_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_VM_UNMAP. + * + * Return: + * * 0 on success, + * * -%EINVAL if &drm_pvr_ioctl_vm_op_unmap_args.device_addr is not a valid + * device page-aligned device-virtual address, or + * * -%ENOENT if there is currently no PowerVR buffer object mapped at + * &drm_pvr_ioctl_vm_op_unmap_args.device_addr. + */ +static int +pvr_ioctl_vm_unmap(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_vm_unmap_args *args = raw_args; + struct pvr_file *pvr_file = to_pvr_file(file); + struct pvr_vm_context *vm_ctx; + int err; + + /* Initial validation of args. */ + if (args->_padding_4) + return -EINVAL; + + vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); + if (!vm_ctx) + return -EINVAL; + + err = pvr_vm_unmap(vm_ctx, args->device_addr, args->size); + + pvr_vm_context_put(vm_ctx); + + return err; +} + +/* + * pvr_ioctl_submit_job() - IOCTL to submit a job to the GPU + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_submit_job_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_SUBMIT_JOB. + * + * Return: + * * 0 on success, or + * * -%EINVAL if arguments are invalid. + */ +static int +pvr_ioctl_submit_jobs(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_submit_jobs_args *args = raw_args; + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct pvr_file *pvr_file = to_pvr_file(file); + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + err = pvr_submit_jobs(pvr_dev, pvr_file, args); + + drm_dev_exit(idx); + + return err; +} + +int +pvr_get_uobj(u64 usr_ptr, u32 usr_stride, u32 min_stride, u32 obj_size, void *out) +{ + if (usr_stride < min_stride) + return -EINVAL; + + return copy_struct_from_user(out, obj_size, u64_to_user_ptr(usr_ptr), usr_stride); +} + +int +pvr_set_uobj(u64 usr_ptr, u32 usr_stride, u32 min_stride, u32 obj_size, const void *in) +{ + if (usr_stride < min_stride) + return -EINVAL; + + if (copy_to_user(u64_to_user_ptr(usr_ptr), in, min_t(u32, usr_stride, obj_size))) + return -EFAULT; + + if (usr_stride > obj_size && + clear_user(u64_to_user_ptr(usr_ptr + obj_size), usr_stride - obj_size)) { + return -EFAULT; + } + + return 0; +} + +int +pvr_get_uobj_array(const struct drm_pvr_obj_array *in, u32 min_stride, u32 obj_size, void **out) +{ + int ret = 0; + void *out_alloc; + + if (in->stride < min_stride) + return -EINVAL; + + if (!in->count) + return 0; + + out_alloc = kvmalloc_array(in->count, obj_size, GFP_KERNEL); + if (!out_alloc) + return -ENOMEM; + + if (obj_size == in->stride) { + if (copy_from_user(out_alloc, u64_to_user_ptr(in->array), + (unsigned long)obj_size * in->count)) + ret = -EFAULT; + } else { + void __user *in_ptr = u64_to_user_ptr(in->array); + void *out_ptr = out_alloc; + + for (u32 i = 0; i < in->count; i++) { + ret = copy_struct_from_user(out_ptr, obj_size, in_ptr, in->stride); + if (ret) + break; + + out_ptr += obj_size; + in_ptr += in->stride; + } + } + + if (ret) { + kvfree(out_alloc); + return ret; + } + + *out = out_alloc; + return 0; +} + +int +pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_size, + const void *in) +{ + if (out->stride < min_stride) + return -EINVAL; + + if (!out->count) + return 0; + + if (obj_size == out->stride) { + if (copy_to_user(u64_to_user_ptr(out->array), in, + (unsigned long)obj_size * out->count)) + return -EFAULT; + } else { + u32 cpy_elem_size = min_t(u32, out->stride, obj_size); + void __user *out_ptr = u64_to_user_ptr(out->array); + const void *in_ptr = in; + + for (u32 i = 0; i < out->count; i++) { + if (copy_to_user(out_ptr, in_ptr, cpy_elem_size)) + return -EFAULT; + + out_ptr += obj_size; + in_ptr += out->stride; + } + + if (out->stride > obj_size && + clear_user(u64_to_user_ptr(out->array + obj_size), + out->stride - obj_size)) { + return -EFAULT; + } + } + + return 0; +} + +#define DRM_PVR_IOCTL(_name, _func, _flags) \ + DRM_IOCTL_DEF_DRV(PVR_##_name, pvr_ioctl_##_func, _flags) + +/* clang-format off */ + +static const struct drm_ioctl_desc pvr_drm_driver_ioctls[] = { + DRM_PVR_IOCTL(DEV_QUERY, dev_query, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(CREATE_BO, create_bo, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(GET_BO_MMAP_OFFSET, get_bo_mmap_offset, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(CREATE_VM_CONTEXT, create_vm_context, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(DESTROY_VM_CONTEXT, destroy_vm_context, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(VM_MAP, vm_map, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(VM_UNMAP, vm_unmap, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(CREATE_CONTEXT, create_context, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(DESTROY_CONTEXT, destroy_context, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(CREATE_FREE_LIST, create_free_list, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(DESTROY_FREE_LIST, destroy_free_list, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(CREATE_HWRT_DATASET, create_hwrt_dataset, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(DESTROY_HWRT_DATASET, destroy_hwrt_dataset, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(SUBMIT_JOBS, submit_jobs, DRM_RENDER_ALLOW), +}; + +/* clang-format on */ + +#undef DRM_PVR_IOCTL + +/** + * pvr_drm_driver_open() - Driver callback when a new &struct drm_file is opened + * @drm_dev: [IN] DRM device. + * @file: [IN] DRM file private data. + * + * Allocates powervr-specific file private data (&struct pvr_file). + * + * Registered in &pvr_drm_driver. + * + * Return: + * * 0 on success, + * * -%ENOMEM if the allocation of a &struct ipvr_file fails, or + * * Any error returned by pvr_memory_context_init(). + */ +static int +pvr_drm_driver_open(struct drm_device *drm_dev, struct drm_file *file) +{ + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + struct pvr_file *pvr_file; + + pvr_file = kzalloc(sizeof(*pvr_file), GFP_KERNEL); + if (!pvr_file) + return -ENOMEM; + + /* + * Store reference to base DRM file private data for use by + * from_pvr_file. + */ + pvr_file->file = file; + + /* + * Store reference to powervr-specific outer device struct in file + * private data for convenient access. + */ + pvr_file->pvr_dev = pvr_dev; + + xa_init_flags(&pvr_file->ctx_handles, XA_FLAGS_ALLOC1); + xa_init_flags(&pvr_file->free_list_handles, XA_FLAGS_ALLOC1); + xa_init_flags(&pvr_file->hwrt_handles, XA_FLAGS_ALLOC1); + xa_init_flags(&pvr_file->vm_ctx_handles, XA_FLAGS_ALLOC1); + + /* + * Store reference to powervr-specific file private data in DRM file + * private data. + */ + file->driver_priv = pvr_file; + + return 0; +} + +/** + * pvr_drm_driver_postclose() - One of the driver callbacks when a &struct + * drm_file is closed. + * @drm_dev: [IN] DRM device (unused). + * @file: [IN] DRM file private data. + * + * Frees powervr-specific file private data (&struct pvr_file). + * + * Registered in &pvr_drm_driver. + */ +static void +pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev, + struct drm_file *file) +{ + struct pvr_file *pvr_file = to_pvr_file(file); + + /* Kill remaining contexts. */ + pvr_destroy_contexts_for_file(pvr_file); + + /* Drop references on any remaining objects. */ + pvr_destroy_free_lists_for_file(pvr_file); + pvr_destroy_hwrt_datasets_for_file(pvr_file); + pvr_destroy_vm_contexts_for_file(pvr_file); + + kfree(pvr_file); + file->driver_priv = NULL; +} + +DEFINE_DRM_GEM_FOPS(pvr_drm_driver_fops); + +static struct drm_driver pvr_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_GEM_GPUVA | DRIVER_RENDER | + DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, + .open = pvr_drm_driver_open, + .postclose = pvr_drm_driver_postclose, + .ioctls = pvr_drm_driver_ioctls, + .num_ioctls = ARRAY_SIZE(pvr_drm_driver_ioctls), + .fops = &pvr_drm_driver_fops, +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = pvr_debugfs_init, +#endif + + .name = PVR_DRIVER_NAME, + .desc = PVR_DRIVER_DESC, + .date = PVR_DRIVER_DATE, + .major = PVR_DRIVER_MAJOR, + .minor = PVR_DRIVER_MINOR, + .patchlevel = PVR_DRIVER_PATCHLEVEL, + + .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, + .gem_create_object = pvr_gem_create_object, +}; + +static int +pvr_probe(struct platform_device *plat_dev) +{ + struct pvr_device *pvr_dev; + struct drm_device *drm_dev; + int err; + + pvr_dev = devm_drm_dev_alloc(&plat_dev->dev, &pvr_drm_driver, + struct pvr_device, base); + if (IS_ERR(pvr_dev)) + return PTR_ERR(pvr_dev); + + drm_dev = &pvr_dev->base; + + platform_set_drvdata(plat_dev, drm_dev); + + init_rwsem(&pvr_dev->reset_sem); + + pvr_context_device_init(pvr_dev); + + err = pvr_queue_device_init(pvr_dev); + if (err) + goto err_context_fini; + + devm_pm_runtime_enable(&plat_dev->dev); + pm_runtime_mark_last_busy(&plat_dev->dev); + + pm_runtime_set_autosuspend_delay(&plat_dev->dev, 50); + pm_runtime_use_autosuspend(&plat_dev->dev); + pvr_watchdog_init(pvr_dev); + + err = pvr_device_init(pvr_dev); + if (err) + goto err_watchdog_fini; + + err = drm_dev_register(drm_dev, 0); + if (err) + goto err_device_fini; + + xa_init_flags(&pvr_dev->free_list_ids, XA_FLAGS_ALLOC1); + xa_init_flags(&pvr_dev->job_ids, XA_FLAGS_ALLOC1); + + return 0; + +err_device_fini: + pvr_device_fini(pvr_dev); + +err_watchdog_fini: + pvr_watchdog_fini(pvr_dev); + + pvr_queue_device_fini(pvr_dev); + +err_context_fini: + pvr_context_device_fini(pvr_dev); + + return err; +} + +static int +pvr_remove(struct platform_device *plat_dev) +{ + struct drm_device *drm_dev = platform_get_drvdata(plat_dev); + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + + WARN_ON(!xa_empty(&pvr_dev->job_ids)); + WARN_ON(!xa_empty(&pvr_dev->free_list_ids)); + + xa_destroy(&pvr_dev->job_ids); + xa_destroy(&pvr_dev->free_list_ids); + + pm_runtime_suspend(drm_dev->dev); + pvr_device_fini(pvr_dev); + drm_dev_unplug(drm_dev); + pvr_watchdog_fini(pvr_dev); + pvr_queue_device_fini(pvr_dev); + pvr_context_device_fini(pvr_dev); + + return 0; +} + +static const struct of_device_id dt_match[] = { + { .compatible = "img,img-axe", .data = NULL }, + {} +}; +MODULE_DEVICE_TABLE(of, dt_match); + +static const struct dev_pm_ops pvr_pm_ops = { + RUNTIME_PM_OPS(pvr_power_device_suspend, pvr_power_device_resume, pvr_power_device_idle) +}; + +static struct platform_driver pvr_driver = { + .probe = pvr_probe, + .remove = pvr_remove, + .driver = { + .name = PVR_DRIVER_NAME, + .pm = &pvr_pm_ops, + .of_match_table = dt_match, + }, +}; +module_platform_driver(pvr_driver); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION(PVR_DRIVER_DESC); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_IMPORT_NS(DMA_BUF); +MODULE_FIRMWARE("powervr/rogue_33.15.11.3_v1.fw"); diff --git a/drivers/gpu/drm/imagination/pvr_drv.h b/drivers/gpu/drm/imagination/pvr_drv.h new file mode 100644 index 000000000000..378fe477b759 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_drv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_DRV_H +#define PVR_DRV_H + +#include "linux/compiler_attributes.h" +#include <uapi/drm/pvr_drm.h> + +#define PVR_DRIVER_NAME "powervr" +#define PVR_DRIVER_DESC "Imagination PowerVR (Series 6 and later) & IMG Graphics" +#define PVR_DRIVER_DATE "20230904" + +/* + * Driver interface version: + * - 1.0: Initial interface + */ +#define PVR_DRIVER_MAJOR 1 +#define PVR_DRIVER_MINOR 0 +#define PVR_DRIVER_PATCHLEVEL 0 + +int pvr_get_uobj(u64 usr_ptr, u32 usr_size, u32 min_size, u32 obj_size, void *out); +int pvr_set_uobj(u64 usr_ptr, u32 usr_size, u32 min_size, u32 obj_size, const void *in); +int pvr_get_uobj_array(const struct drm_pvr_obj_array *in, u32 min_stride, u32 obj_size, + void **out); +int pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_size, + const void *in); + +#define PVR_UOBJ_MIN_SIZE_INTERNAL(_typename, _last_mandatory_field) \ + (offsetof(_typename, _last_mandatory_field) + \ + sizeof(((_typename *)NULL)->_last_mandatory_field)) + +/* NOLINTBEGIN(bugprone-macro-parentheses) */ +#define PVR_UOBJ_DECL(_typename, _last_mandatory_field) \ + , _typename : PVR_UOBJ_MIN_SIZE_INTERNAL(_typename, _last_mandatory_field) +/* NOLINTEND(bugprone-macro-parentheses) */ + +/** + * DOC: PVR user objects. + * + * Macros used to aid copying structured and array data to and from + * userspace. Objects can differ in size, provided the minimum size + * allowed is specified (using the last mandatory field in the struct). + * All types used with PVR_UOBJ_GET/SET macros must be listed here under + * PVR_UOBJ_MIN_SIZE, with the last mandatory struct field specified. + */ + +/** + * PVR_UOBJ_MIN_SIZE() - Fetch the minimum copy size of a compatible type object. + * @_obj_name: The name of the object. Cannot be a typename - this is deduced. + * + * This cannot fail. Using the macro with an incompatible type will result in a + * compiler error. + * + * To add compatibility for a type, list it within the macro in an orderly + * fashion. The second argument is the name of the last mandatory field of the + * struct type, which is used to calculate the size. See also PVR_UOBJ_DECL(). + * + * Return: The minimum copy size. + */ +#define PVR_UOBJ_MIN_SIZE(_obj_name) _Generic(_obj_name \ + PVR_UOBJ_DECL(struct drm_pvr_job, hwrt) \ + PVR_UOBJ_DECL(struct drm_pvr_sync_op, value) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_gpu_info, num_phantoms) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_runtime_info, cdm_max_local_mem_size_regs) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_quirks, _padding_c) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_enhancements, _padding_c) \ + PVR_UOBJ_DECL(struct drm_pvr_heap, page_size_log2) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_heap_info, heaps) \ + PVR_UOBJ_DECL(struct drm_pvr_static_data_area, offset) \ + PVR_UOBJ_DECL(struct drm_pvr_dev_query_static_data_areas, static_data_areas) \ + ) + +/** + * PVR_UOBJ_GET() - Copies from _src_usr_ptr to &_dest_obj. + * @_dest_obj: The destination container object in kernel space. + * @_usr_size: The size of the source container in user space. + * @_src_usr_ptr: __u64 raw pointer to the source container in user space. + * + * Return: Error code. See pvr_get_uobj(). + */ +#define PVR_UOBJ_GET(_dest_obj, _usr_size, _src_usr_ptr) \ + pvr_get_uobj(_src_usr_ptr, _usr_size, \ + PVR_UOBJ_MIN_SIZE(_dest_obj), \ + sizeof(_dest_obj), &(_dest_obj)) + +/** + * PVR_UOBJ_SET() - Copies from &_src_obj to _dest_usr_ptr. + * @_dest_usr_ptr: __u64 raw pointer to the destination container in user space. + * @_usr_size: The size of the destination container in user space. + * @_src_obj: The source container object in kernel space. + * + * Return: Error code. See pvr_set_uobj(). + */ +#define PVR_UOBJ_SET(_dest_usr_ptr, _usr_size, _src_obj) \ + pvr_set_uobj(_dest_usr_ptr, _usr_size, \ + PVR_UOBJ_MIN_SIZE(_src_obj), \ + sizeof(_src_obj), &(_src_obj)) + +/** + * PVR_UOBJ_GET_ARRAY() - Copies from @_src_drm_pvr_obj_array.array to + * alloced memory and returns a pointer in _dest_array. + * @_dest_array: The destination C array object in kernel space. + * @_src_drm_pvr_obj_array: The &struct drm_pvr_obj_array containing a __u64 raw + * pointer to the source C array in user space and the size of each array + * element in user space (the 'stride'). + * + * Return: Error code. See pvr_get_uobj_array(). + */ +#define PVR_UOBJ_GET_ARRAY(_dest_array, _src_drm_pvr_obj_array) \ + pvr_get_uobj_array(_src_drm_pvr_obj_array, \ + PVR_UOBJ_MIN_SIZE((_dest_array)[0]), \ + sizeof((_dest_array)[0]), (void **)&(_dest_array)) + +/** + * PVR_UOBJ_SET_ARRAY() - Copies from _src_array to @_dest_drm_pvr_obj_array.array. + * @_dest_drm_pvr_obj_array: The &struct drm_pvr_obj_array containing a __u64 raw + * pointer to the destination C array in user space and the size of each array + * element in user space (the 'stride'). + * @_src_array: The source C array object in kernel space. + * + * Return: Error code. See pvr_set_uobj_array(). + */ +#define PVR_UOBJ_SET_ARRAY(_dest_drm_pvr_obj_array, _src_array) \ + pvr_set_uobj_array(_dest_drm_pvr_obj_array, \ + PVR_UOBJ_MIN_SIZE((_src_array)[0]), \ + sizeof((_src_array)[0]), _src_array) + +#endif /* PVR_DRV_H */ diff --git a/drivers/gpu/drm/imagination/pvr_free_list.c b/drivers/gpu/drm/imagination/pvr_free_list.c new file mode 100644 index 000000000000..c61fd417edcb --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_free_list.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_free_list.h" +#include "pvr_gem.h" +#include "pvr_hwrt.h" +#include "pvr_rogue_fwif.h" +#include "pvr_vm.h" + +#include <drm/drm_gem.h> +#include <linux/slab.h> +#include <linux/xarray.h> +#include <uapi/drm/pvr_drm.h> + +#define FREE_LIST_ENTRY_SIZE sizeof(u32) + +#define FREE_LIST_ALIGNMENT \ + ((ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE / FREE_LIST_ENTRY_SIZE) - 1) + +#define FREE_LIST_MIN_PAGES 50 +#define FREE_LIST_MIN_PAGES_BRN66011 40 +#define FREE_LIST_MIN_PAGES_ROGUEXE 25 + +/** + * pvr_get_free_list_min_pages() - Get minimum free list size for this device + * @pvr_dev: Device pointer. + * + * Returns: + * * Minimum free list size, in PM physical pages. + */ +u32 +pvr_get_free_list_min_pages(struct pvr_device *pvr_dev) +{ + u32 value; + + if (PVR_HAS_FEATURE(pvr_dev, roguexe)) { + if (PVR_HAS_QUIRK(pvr_dev, 66011)) + value = FREE_LIST_MIN_PAGES_BRN66011; + else + value = FREE_LIST_MIN_PAGES_ROGUEXE; + } else { + value = FREE_LIST_MIN_PAGES; + } + + return value; +} + +static int +free_list_create_kernel_structure(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_free_list_args *args, + struct pvr_free_list *free_list) +{ + struct pvr_gem_object *free_list_obj; + struct pvr_vm_context *vm_ctx; + u64 free_list_size; + int err; + + if (args->grow_threshold > 100 || + args->initial_num_pages > args->max_num_pages || + args->grow_num_pages > args->max_num_pages || + args->max_num_pages == 0 || + (args->initial_num_pages < args->max_num_pages && !args->grow_num_pages) || + (args->initial_num_pages == args->max_num_pages && args->grow_num_pages)) + return -EINVAL; + + if ((args->initial_num_pages & FREE_LIST_ALIGNMENT) || + (args->max_num_pages & FREE_LIST_ALIGNMENT) || + (args->grow_num_pages & FREE_LIST_ALIGNMENT)) + return -EINVAL; + + vm_ctx = pvr_vm_context_lookup(pvr_file, args->vm_context_handle); + if (!vm_ctx) + return -EINVAL; + + free_list_obj = pvr_vm_find_gem_object(vm_ctx, args->free_list_gpu_addr, + NULL, &free_list_size); + if (!free_list_obj) { + err = -EINVAL; + goto err_put_vm_context; + } + + if ((free_list_obj->flags & DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS) || + !(free_list_obj->flags & DRM_PVR_BO_PM_FW_PROTECT) || + free_list_size < (args->max_num_pages * FREE_LIST_ENTRY_SIZE)) { + err = -EINVAL; + goto err_put_free_list_obj; + } + + free_list->pvr_dev = pvr_file->pvr_dev; + free_list->current_pages = 0; + free_list->max_pages = args->max_num_pages; + free_list->grow_pages = args->grow_num_pages; + free_list->grow_threshold = args->grow_threshold; + free_list->obj = free_list_obj; + free_list->free_list_gpu_addr = args->free_list_gpu_addr; + free_list->initial_num_pages = args->initial_num_pages; + + pvr_vm_context_put(vm_ctx); + + return 0; + +err_put_free_list_obj: + pvr_gem_object_put(free_list_obj); + +err_put_vm_context: + pvr_vm_context_put(vm_ctx); + + return err; +} + +static void +free_list_destroy_kernel_structure(struct pvr_free_list *free_list) +{ + WARN_ON(!list_empty(&free_list->hwrt_list)); + + pvr_gem_object_put(free_list->obj); +} + +/** + * calculate_free_list_ready_pages_locked() - Function to work out the number of free + * list pages to reserve for growing within + * the FW without having to wait for the + * host to progress a grow request + * @free_list: Pointer to free list. + * @pages: Total pages currently in free list. + * + * If the threshold or grow size means less than the alignment size (4 pages on + * Rogue), then the feature is not used. + * + * Caller must hold &free_list->lock. + * + * Return: number of pages to reserve. + */ +static u32 +calculate_free_list_ready_pages_locked(struct pvr_free_list *free_list, u32 pages) +{ + u32 ready_pages; + + lockdep_assert_held(&free_list->lock); + + ready_pages = ((pages * free_list->grow_threshold) / 100); + + /* The number of pages must be less than the grow size. */ + ready_pages = min(ready_pages, free_list->grow_pages); + + /* + * The number of pages must be a multiple of the free list align size. + */ + ready_pages &= ~FREE_LIST_ALIGNMENT; + + return ready_pages; +} + +static u32 +calculate_free_list_ready_pages(struct pvr_free_list *free_list, u32 pages) +{ + u32 ret; + + mutex_lock(&free_list->lock); + + ret = calculate_free_list_ready_pages_locked(free_list, pages); + + mutex_unlock(&free_list->lock); + + return ret; +} + +static void +free_list_fw_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_freelist *fw_data = cpu_ptr; + struct pvr_free_list *free_list = priv; + u32 ready_pages; + + /* Fill out FW structure */ + ready_pages = calculate_free_list_ready_pages(free_list, + free_list->initial_num_pages); + + fw_data->max_pages = free_list->max_pages; + fw_data->current_pages = free_list->initial_num_pages - ready_pages; + fw_data->grow_pages = free_list->grow_pages; + fw_data->ready_pages = ready_pages; + fw_data->freelist_id = free_list->fw_id; + fw_data->grow_pending = false; + fw_data->current_stack_top = fw_data->current_pages - 1; + fw_data->freelist_dev_addr = free_list->free_list_gpu_addr; + fw_data->current_dev_addr = (fw_data->freelist_dev_addr + + ((fw_data->max_pages - fw_data->current_pages) * + FREE_LIST_ENTRY_SIZE)) & + ~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1); +} + +static int +free_list_create_fw_structure(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_free_list_args *args, + struct pvr_free_list *free_list) +{ + struct pvr_device *pvr_dev = pvr_file->pvr_dev; + + /* + * Create and map the FW structure so we can initialise it. This is not + * accessed on the CPU side post-initialisation so the mapping lifetime + * is only for this function. + */ + free_list->fw_data = pvr_fw_object_create_and_map(pvr_dev, sizeof(*free_list->fw_data), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + free_list_fw_init, free_list, + &free_list->fw_obj); + if (IS_ERR(free_list->fw_data)) + return PTR_ERR(free_list->fw_data); + + return 0; +} + +static void +free_list_destroy_fw_structure(struct pvr_free_list *free_list) +{ + pvr_fw_object_unmap_and_destroy(free_list->fw_obj); +} + +static int +pvr_free_list_insert_pages_locked(struct pvr_free_list *free_list, + struct sg_table *sgt, u32 offset, u32 num_pages) +{ + struct sg_dma_page_iter dma_iter; + u32 *page_list; + + lockdep_assert_held(&free_list->lock); + + page_list = pvr_gem_object_vmap(free_list->obj); + if (IS_ERR(page_list)) + return PTR_ERR(page_list); + + offset /= FREE_LIST_ENTRY_SIZE; + /* clang-format off */ + for_each_sgtable_dma_page(sgt, &dma_iter, 0) { + dma_addr_t dma_addr = sg_page_iter_dma_address(&dma_iter); + u64 dma_pfn = dma_addr >> + ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT; + u32 dma_addr_offset; + + BUILD_BUG_ON(ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE > PAGE_SIZE); + + for (dma_addr_offset = 0; dma_addr_offset < PAGE_SIZE; + dma_addr_offset += ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE) { + WARN_ON_ONCE(dma_pfn >> 32); + + page_list[offset++] = (u32)dma_pfn; + dma_pfn++; + + num_pages--; + if (!num_pages) + break; + } + + if (!num_pages) + break; + }; + /* clang-format on */ + + /* Make sure our free_list update is flushed. */ + wmb(); + + pvr_gem_object_vunmap(free_list->obj); + + return 0; +} + +static int +pvr_free_list_insert_node_locked(struct pvr_free_list_node *free_list_node) +{ + struct pvr_free_list *free_list = free_list_node->free_list; + struct sg_table *sgt; + u32 start_page; + u32 offset; + int err; + + lockdep_assert_held(&free_list->lock); + + start_page = free_list->max_pages - free_list->current_pages - + free_list_node->num_pages; + offset = (start_page * FREE_LIST_ENTRY_SIZE) & + ~((u64)ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE - 1); + + sgt = drm_gem_shmem_get_pages_sgt(&free_list_node->mem_obj->base); + if (WARN_ON(IS_ERR(sgt))) + return PTR_ERR(sgt); + + err = pvr_free_list_insert_pages_locked(free_list, sgt, + offset, free_list_node->num_pages); + if (!err) + free_list->current_pages += free_list_node->num_pages; + + return err; +} + +static int +pvr_free_list_grow(struct pvr_free_list *free_list, u32 num_pages) +{ + struct pvr_device *pvr_dev = free_list->pvr_dev; + struct pvr_free_list_node *free_list_node; + int err; + + mutex_lock(&free_list->lock); + + if (num_pages & FREE_LIST_ALIGNMENT) { + err = -EINVAL; + goto err_unlock; + } + + free_list_node = kzalloc(sizeof(*free_list_node), GFP_KERNEL); + if (!free_list_node) { + err = -ENOMEM; + goto err_unlock; + } + + free_list_node->num_pages = num_pages; + free_list_node->free_list = free_list; + + free_list_node->mem_obj = pvr_gem_object_create(pvr_dev, + num_pages << + ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT, + PVR_BO_FW_FLAGS_DEVICE_CACHED); + if (IS_ERR(free_list_node->mem_obj)) { + err = PTR_ERR(free_list_node->mem_obj); + goto err_free; + } + + err = pvr_free_list_insert_node_locked(free_list_node); + if (err) + goto err_destroy_gem_object; + + list_add_tail(&free_list_node->node, &free_list->mem_block_list); + + /* + * Reserve a number ready pages to allow the FW to process OOM quickly + * and asynchronously request a grow. + */ + free_list->ready_pages = + calculate_free_list_ready_pages_locked(free_list, + free_list->current_pages); + free_list->current_pages -= free_list->ready_pages; + + mutex_unlock(&free_list->lock); + + return 0; + +err_destroy_gem_object: + pvr_gem_object_put(free_list_node->mem_obj); + +err_free: + kfree(free_list_node); + +err_unlock: + mutex_unlock(&free_list->lock); + + return err; +} + +void pvr_free_list_process_grow_req(struct pvr_device *pvr_dev, + struct rogue_fwif_fwccb_cmd_freelist_gs_data *req) +{ + struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, req->freelist_id); + struct rogue_fwif_kccb_cmd resp_cmd = { + .cmd_type = ROGUE_FWIF_KCCB_CMD_FREELIST_GROW_UPDATE, + }; + struct rogue_fwif_freelist_gs_data *resp = &resp_cmd.cmd_data.free_list_gs_data; + u32 grow_pages = 0; + + /* If we don't have a freelist registered for this ID, we can't do much. */ + if (WARN_ON(!free_list)) + return; + + /* Since the FW made the request, it has already consumed the ready pages, + * update the host struct. + */ + free_list->current_pages += free_list->ready_pages; + free_list->ready_pages = 0; + + /* If the grow succeeds, update the grow_pages argument. */ + if (!pvr_free_list_grow(free_list, free_list->grow_pages)) + grow_pages = free_list->grow_pages; + + /* Now prepare the response and send it back to the FW. */ + pvr_fw_object_get_fw_addr(free_list->fw_obj, &resp->freelist_fw_addr); + resp->delta_pages = grow_pages; + resp->new_pages = free_list->current_pages + free_list->ready_pages; + resp->ready_pages = free_list->ready_pages; + pvr_free_list_put(free_list); + + WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL)); +} + +static void +pvr_free_list_free_node(struct pvr_free_list_node *free_list_node) +{ + pvr_gem_object_put(free_list_node->mem_obj); + + kfree(free_list_node); +} + +/** + * pvr_free_list_create() - Create a new free list and return an object pointer + * @pvr_file: Pointer to pvr_file structure. + * @args: Creation arguments from userspace. + * + * Return: + * * Pointer to new free_list, or + * * ERR_PTR(-%ENOMEM) on out of memory. + */ +struct pvr_free_list * +pvr_free_list_create(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_free_list_args *args) +{ + struct pvr_free_list *free_list; + int err; + + /* Create and fill out the kernel structure */ + free_list = kzalloc(sizeof(*free_list), GFP_KERNEL); + + if (!free_list) + return ERR_PTR(-ENOMEM); + + kref_init(&free_list->ref_count); + INIT_LIST_HEAD(&free_list->mem_block_list); + INIT_LIST_HEAD(&free_list->hwrt_list); + mutex_init(&free_list->lock); + + err = free_list_create_kernel_structure(pvr_file, args, free_list); + if (err < 0) + goto err_free; + + /* Allocate global object ID for firmware. */ + err = xa_alloc(&pvr_file->pvr_dev->free_list_ids, + &free_list->fw_id, + free_list, + xa_limit_32b, + GFP_KERNEL); + if (err) + goto err_destroy_kernel_structure; + + err = free_list_create_fw_structure(pvr_file, args, free_list); + if (err < 0) + goto err_free_fw_id; + + err = pvr_free_list_grow(free_list, args->initial_num_pages); + if (err < 0) + goto err_fw_struct_cleanup; + + return free_list; + +err_fw_struct_cleanup: + WARN_ON(pvr_fw_structure_cleanup(free_list->pvr_dev, + ROGUE_FWIF_CLEANUP_FREELIST, + free_list->fw_obj, 0)); + +err_free_fw_id: + xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id); + +err_destroy_kernel_structure: + free_list_destroy_kernel_structure(free_list); + +err_free: + mutex_destroy(&free_list->lock); + kfree(free_list); + + return ERR_PTR(err); +} + +static void +pvr_free_list_release(struct kref *ref_count) +{ + struct pvr_free_list *free_list = + container_of(ref_count, struct pvr_free_list, ref_count); + struct list_head *pos, *n; + int err; + + xa_erase(&free_list->pvr_dev->free_list_ids, free_list->fw_id); + + err = pvr_fw_structure_cleanup(free_list->pvr_dev, + ROGUE_FWIF_CLEANUP_FREELIST, + free_list->fw_obj, 0); + if (err == -EBUSY) { + /* Flush the FWCCB to process any HWR or freelist reconstruction + * request that might keep the freelist busy, and try again. + */ + pvr_fwccb_process(free_list->pvr_dev); + err = pvr_fw_structure_cleanup(free_list->pvr_dev, + ROGUE_FWIF_CLEANUP_FREELIST, + free_list->fw_obj, 0); + } + + WARN_ON(err); + + /* clang-format off */ + list_for_each_safe(pos, n, &free_list->mem_block_list) { + struct pvr_free_list_node *free_list_node = + container_of(pos, struct pvr_free_list_node, node); + + list_del(pos); + pvr_free_list_free_node(free_list_node); + } + /* clang-format on */ + + free_list_destroy_kernel_structure(free_list); + free_list_destroy_fw_structure(free_list); + mutex_destroy(&free_list->lock); + kfree(free_list); +} + +/** + * pvr_destroy_free_lists_for_file: Destroy any free lists associated with the + * given file. + * @pvr_file: Pointer to pvr_file structure. + * + * Removes all free lists associated with @pvr_file from the device free_list + * list and drops initial references. Free lists will then be destroyed once + * all outstanding references are dropped. + */ +void pvr_destroy_free_lists_for_file(struct pvr_file *pvr_file) +{ + struct pvr_free_list *free_list; + unsigned long handle; + + xa_for_each(&pvr_file->free_list_handles, handle, free_list) { + (void)free_list; + pvr_free_list_put(xa_erase(&pvr_file->free_list_handles, handle)); + } +} + +/** + * pvr_free_list_put() - Release reference on free list + * @free_list: Pointer to list to release reference on + */ +void +pvr_free_list_put(struct pvr_free_list *free_list) +{ + if (free_list) + kref_put(&free_list->ref_count, pvr_free_list_release); +} + +void pvr_free_list_add_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data) +{ + mutex_lock(&free_list->lock); + + list_add_tail(&hwrt_data->freelist_node, &free_list->hwrt_list); + + mutex_unlock(&free_list->lock); +} + +void pvr_free_list_remove_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data) +{ + mutex_lock(&free_list->lock); + + list_del(&hwrt_data->freelist_node); + + mutex_unlock(&free_list->lock); +} + +static void +pvr_free_list_reconstruct(struct pvr_device *pvr_dev, u32 freelist_id) +{ + struct pvr_free_list *free_list = pvr_free_list_lookup_id(pvr_dev, freelist_id); + struct pvr_free_list_node *free_list_node; + struct rogue_fwif_freelist *fw_data; + struct pvr_hwrt_data *hwrt_data; + + if (!free_list) + return; + + mutex_lock(&free_list->lock); + + /* Rebuild the free list based on the memory block list. */ + free_list->current_pages = 0; + + list_for_each_entry(free_list_node, &free_list->mem_block_list, node) + WARN_ON(pvr_free_list_insert_node_locked(free_list_node)); + + /* + * Remove the ready pages, which are reserved to allow the FW to process OOM quickly and + * asynchronously request a grow. + */ + free_list->current_pages -= free_list->ready_pages; + + fw_data = free_list->fw_data; + fw_data->current_stack_top = fw_data->current_pages - 1; + fw_data->allocated_page_count = 0; + fw_data->allocated_mmu_page_count = 0; + + /* Reset the state of any associated HWRTs. */ + list_for_each_entry(hwrt_data, &free_list->hwrt_list, freelist_node) { + struct rogue_fwif_hwrtdata *hwrt_fw_data = pvr_fw_object_vmap(hwrt_data->fw_obj); + + if (!WARN_ON(IS_ERR(hwrt_fw_data))) { + hwrt_fw_data->state = ROGUE_FWIF_RTDATA_STATE_HWR; + hwrt_fw_data->hwrt_data_flags &= ~HWRTDATA_HAS_LAST_GEOM; + } + + pvr_fw_object_vunmap(hwrt_data->fw_obj); + } + + mutex_unlock(&free_list->lock); + + pvr_free_list_put(free_list); +} + +void +pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev, + struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data *req) +{ + struct rogue_fwif_kccb_cmd resp_cmd = { + .cmd_type = ROGUE_FWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE, + }; + struct rogue_fwif_freelists_reconstruction_data *resp = + &resp_cmd.cmd_data.free_lists_reconstruction_data; + + for (u32 i = 0; i < req->freelist_count; i++) + pvr_free_list_reconstruct(pvr_dev, req->freelist_ids[i]); + + resp->freelist_count = req->freelist_count; + memcpy(resp->freelist_ids, req->freelist_ids, + req->freelist_count * sizeof(resp->freelist_ids[0])); + + WARN_ON(pvr_kccb_send_cmd(pvr_dev, &resp_cmd, NULL)); +} diff --git a/drivers/gpu/drm/imagination/pvr_free_list.h b/drivers/gpu/drm/imagination/pvr_free_list.h new file mode 100644 index 000000000000..bfb4f5fc622c --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_free_list.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FREE_LIST_H +#define PVR_FREE_LIST_H + +#include <linux/compiler_attributes.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/types.h> +#include <linux/xarray.h> +#include <uapi/drm/pvr_drm.h> + +#include "pvr_device.h" + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_gem_object; + +/* Forward declaration from pvr_hwrt.h. */ +struct pvr_hwrt_data; + +/** + * struct pvr_free_list_node - structure representing an allocation in the free + * list + */ +struct pvr_free_list_node { + /** @node: List node for &pvr_free_list.mem_block_list. */ + struct list_head node; + + /** @free_list: Pointer to owning free list. */ + struct pvr_free_list *free_list; + + /** @num_pages: Number of pages in this node. */ + u32 num_pages; + + /** @mem_obj: GEM object representing the pages in this node. */ + struct pvr_gem_object *mem_obj; +}; + +/** + * struct pvr_free_list - structure representing a free list + */ +struct pvr_free_list { + /** @ref_count: Reference count of object. */ + struct kref ref_count; + + /** @pvr_dev: Pointer to device that owns this object. */ + struct pvr_device *pvr_dev; + + /** @obj: GEM object representing the free list. */ + struct pvr_gem_object *obj; + + /** @fw_obj: FW object representing the FW-side structure. */ + struct pvr_fw_object *fw_obj; + + /** @fw_data: Pointer to CPU mapping of the FW-side structure. */ + struct rogue_fwif_freelist *fw_data; + + /** + * @lock: Mutex protecting modification of the free list. Must be held when accessing any + * of the members below. + */ + struct mutex lock; + + /** @fw_id: Firmware ID for this object. */ + u32 fw_id; + + /** @current_pages: Current number of pages in free list. */ + u32 current_pages; + + /** @max_pages: Maximum number of pages in free list. */ + u32 max_pages; + + /** @grow_pages: Pages to grow free list by per request. */ + u32 grow_pages; + + /** + * @grow_threshold: Percentage of FL memory used that should trigger a + * new grow request. + */ + u32 grow_threshold; + + /** + * @ready_pages: Number of pages reserved for FW to use while a grow + * request is being processed. + */ + u32 ready_pages; + + /** @mem_block_list: List of memory blocks in this free list. */ + struct list_head mem_block_list; + + /** @hwrt_list: List of HWRTs using this free list. */ + struct list_head hwrt_list; + + /** @initial_num_pages: Initial number of pages in free list. */ + u32 initial_num_pages; + + /** @free_list_gpu_addr: Address of free list in GPU address space. */ + u64 free_list_gpu_addr; +}; + +struct pvr_free_list * +pvr_free_list_create(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_free_list_args *args); + +void +pvr_destroy_free_lists_for_file(struct pvr_file *pvr_file); + +u32 +pvr_get_free_list_min_pages(struct pvr_device *pvr_dev); + +static __always_inline struct pvr_free_list * +pvr_free_list_get(struct pvr_free_list *free_list) +{ + if (free_list) + kref_get(&free_list->ref_count); + + return free_list; +} + +/** + * pvr_free_list_lookup() - Lookup free list pointer from handle and file + * @pvr_file: Pointer to pvr_file structure. + * @handle: Object handle. + * + * Takes reference on free list object. Call pvr_free_list_put() to release. + * + * Returns: + * * The requested object on success, or + * * %NULL on failure (object does not exist in list, is not a free list, or + * does not belong to @pvr_file) + */ +static __always_inline struct pvr_free_list * +pvr_free_list_lookup(struct pvr_file *pvr_file, u32 handle) +{ + struct pvr_free_list *free_list; + + xa_lock(&pvr_file->free_list_handles); + free_list = pvr_free_list_get(xa_load(&pvr_file->free_list_handles, handle)); + xa_unlock(&pvr_file->free_list_handles); + + return free_list; +} + +/** + * pvr_free_list_lookup_id() - Lookup free list pointer from FW ID + * @pvr_dev: Device pointer. + * @id: FW object ID. + * + * Takes reference on free list object. Call pvr_free_list_put() to release. + * + * Returns: + * * The requested object on success, or + * * %NULL on failure (object does not exist in list, or is not a free list) + */ +static __always_inline struct pvr_free_list * +pvr_free_list_lookup_id(struct pvr_device *pvr_dev, u32 id) +{ + struct pvr_free_list *free_list; + + xa_lock(&pvr_dev->free_list_ids); + + /* Contexts are removed from the ctx_ids set in the context release path, + * meaning the ref_count reached zero before they get removed. We need + * to make sure we're not trying to acquire a context that's being + * destroyed. + */ + free_list = xa_load(&pvr_dev->free_list_ids, id); + if (free_list && !kref_get_unless_zero(&free_list->ref_count)) + free_list = NULL; + xa_unlock(&pvr_dev->free_list_ids); + + return free_list; +} + +void +pvr_free_list_put(struct pvr_free_list *free_list); + +void +pvr_free_list_add_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data); +void +pvr_free_list_remove_hwrt(struct pvr_free_list *free_list, struct pvr_hwrt_data *hwrt_data); + +void pvr_free_list_process_grow_req(struct pvr_device *pvr_dev, + struct rogue_fwif_fwccb_cmd_freelist_gs_data *req); + +void +pvr_free_list_process_reconstruct_req(struct pvr_device *pvr_dev, + struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data *req); + +#endif /* PVR_FREE_LIST_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c new file mode 100644 index 000000000000..3debc9870a82 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw.c @@ -0,0 +1,1489 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_ccb.h" +#include "pvr_device.h" +#include "pvr_device_info.h" +#include "pvr_fw.h" +#include "pvr_fw_info.h" +#include "pvr_fw_startstop.h" +#include "pvr_fw_trace.h" +#include "pvr_gem.h" +#include "pvr_power.h" +#include "pvr_rogue_fwif_dev_info.h" +#include "pvr_rogue_heap_config.h" +#include "pvr_vm.h" + +#include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <drm/drm_mm.h> +#include <linux/clk.h> +#include <linux/firmware.h> +#include <linux/math.h> +#include <linux/minmax.h> +#include <linux/sizes.h> + +#define FW_MAX_SUPPORTED_MAJOR_VERSION 1 + +#define FW_BOOT_TIMEOUT_USEC 5000000 + +/* Config heap occupies top 192k of the firmware heap. */ +#define PVR_ROGUE_FW_CONFIG_HEAP_GRANULARITY SZ_64K +#define PVR_ROGUE_FW_CONFIG_HEAP_SIZE (3 * PVR_ROGUE_FW_CONFIG_HEAP_GRANULARITY) + +/* Main firmware allocations should come from the remainder of the heap. */ +#define PVR_ROGUE_FW_MAIN_HEAP_BASE ROGUE_FW_HEAP_BASE + +/* Offsets from start of configuration area of FW heap. */ +#define PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET 0 +#define PVR_ROGUE_FWIF_OSINIT_OFFSET \ + (PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET + PVR_ROGUE_FW_CONFIG_HEAP_GRANULARITY) +#define PVR_ROGUE_FWIF_SYSINIT_OFFSET \ + (PVR_ROGUE_FWIF_OSINIT_OFFSET + PVR_ROGUE_FW_CONFIG_HEAP_GRANULARITY) + +#define PVR_ROGUE_FAULT_PAGE_SIZE SZ_4K + +#define PVR_SYNC_OBJ_SIZE sizeof(u32) + +const struct pvr_fw_layout_entry * +pvr_fw_find_layout_entry(struct pvr_device *pvr_dev, enum pvr_fw_section_id id) +{ + const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries; + u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num; + u32 entry; + + for (entry = 0; entry < num_layout_entries; entry++) { + if (layout_entries[entry].id == id) + return &layout_entries[entry]; + } + + return NULL; +} + +static const struct pvr_fw_layout_entry * +pvr_fw_find_private_data(struct pvr_device *pvr_dev) +{ + const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries; + u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num; + u32 entry; + + for (entry = 0; entry < num_layout_entries; entry++) { + if (layout_entries[entry].id == META_PRIVATE_DATA || + layout_entries[entry].id == MIPS_PRIVATE_DATA || + layout_entries[entry].id == RISCV_PRIVATE_DATA) + return &layout_entries[entry]; + } + + return NULL; +} + +#define DEV_INFO_MASK_SIZE(x) DIV_ROUND_UP(x, 64) + +/** + * pvr_fw_validate() - Parse firmware header and check compatibility + * @pvr_dev: Device pointer. + * + * Returns: + * * 0 on success, or + * * -EINVAL if firmware is incompatible. + */ +static int +pvr_fw_validate(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + const struct firmware *firmware = pvr_dev->fw_dev.firmware; + const struct pvr_fw_layout_entry *layout_entries; + const struct pvr_fw_info_header *header; + const u8 *fw = firmware->data; + u32 fw_offset = firmware->size - SZ_4K; + u32 layout_table_size; + u32 entry; + + if (firmware->size < SZ_4K || (firmware->size % FW_BLOCK_SIZE)) + return -EINVAL; + + header = (const struct pvr_fw_info_header *)&fw[fw_offset]; + + if (header->info_version != PVR_FW_INFO_VERSION) { + drm_err(drm_dev, "Unsupported fw info version %u\n", + header->info_version); + return -EINVAL; + } + + if (header->header_len != sizeof(struct pvr_fw_info_header) || + header->layout_entry_size != sizeof(struct pvr_fw_layout_entry) || + header->layout_entry_num > PVR_FW_INFO_MAX_NUM_ENTRIES) { + drm_err(drm_dev, "FW info format mismatch\n"); + return -EINVAL; + } + + if (!(header->flags & PVR_FW_FLAGS_OPEN_SOURCE) || + header->fw_version_major > FW_MAX_SUPPORTED_MAJOR_VERSION || + header->fw_version_major == 0) { + drm_err(drm_dev, "Unsupported FW version %u.%u (build: %u%s)\n", + header->fw_version_major, header->fw_version_minor, + header->fw_version_build, + (header->flags & PVR_FW_FLAGS_OPEN_SOURCE) ? " OS" : ""); + return -EINVAL; + } + + if (pvr_gpu_id_to_packed_bvnc(&pvr_dev->gpu_id) != header->bvnc) { + struct pvr_gpu_id fw_gpu_id; + + packed_bvnc_to_pvr_gpu_id(header->bvnc, &fw_gpu_id); + drm_err(drm_dev, "FW built for incorrect GPU ID %i.%i.%i.%i (expected %i.%i.%i.%i)\n", + fw_gpu_id.b, fw_gpu_id.v, fw_gpu_id.n, fw_gpu_id.c, + pvr_dev->gpu_id.b, pvr_dev->gpu_id.v, pvr_dev->gpu_id.n, pvr_dev->gpu_id.c); + return -EINVAL; + } + + fw_offset += header->header_len; + layout_table_size = + header->layout_entry_size * header->layout_entry_num; + if ((fw_offset + layout_table_size) > firmware->size) + return -EINVAL; + + layout_entries = (const struct pvr_fw_layout_entry *)&fw[fw_offset]; + for (entry = 0; entry < header->layout_entry_num; entry++) { + u32 start_addr = layout_entries[entry].base_addr; + u32 end_addr = start_addr + layout_entries[entry].alloc_size; + + if (start_addr >= end_addr) + return -EINVAL; + } + + fw_offset = (firmware->size - SZ_4K) - header->device_info_size; + + drm_info(drm_dev, "FW version v%u.%u (build %u OS)\n", header->fw_version_major, + header->fw_version_minor, header->fw_version_build); + + pvr_dev->fw_version.major = header->fw_version_major; + pvr_dev->fw_version.minor = header->fw_version_minor; + + pvr_dev->fw_dev.header = header; + pvr_dev->fw_dev.layout_entries = layout_entries; + + return 0; +} + +static int +pvr_fw_get_device_info(struct pvr_device *pvr_dev) +{ + const struct firmware *firmware = pvr_dev->fw_dev.firmware; + struct pvr_fw_device_info_header *header; + const u8 *fw = firmware->data; + const u64 *dev_info; + u32 fw_offset; + + fw_offset = (firmware->size - SZ_4K) - pvr_dev->fw_dev.header->device_info_size; + + header = (struct pvr_fw_device_info_header *)&fw[fw_offset]; + dev_info = (u64 *)(header + 1); + + pvr_device_info_set_quirks(pvr_dev, dev_info, header->brn_mask_size); + dev_info += header->brn_mask_size; + + pvr_device_info_set_enhancements(pvr_dev, dev_info, header->ern_mask_size); + dev_info += header->ern_mask_size; + + return pvr_device_info_set_features(pvr_dev, dev_info, header->feature_mask_size, + header->feature_param_size); +} + +static void +layout_get_sizes(struct pvr_device *pvr_dev) +{ + const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries; + u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num; + struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; + + fw_mem->code_alloc_size = 0; + fw_mem->data_alloc_size = 0; + fw_mem->core_code_alloc_size = 0; + fw_mem->core_data_alloc_size = 0; + + /* Extract section sizes from FW layout table. */ + for (u32 entry = 0; entry < num_layout_entries; entry++) { + switch (layout_entries[entry].type) { + case FW_CODE: + fw_mem->code_alloc_size += layout_entries[entry].alloc_size; + break; + case FW_DATA: + fw_mem->data_alloc_size += layout_entries[entry].alloc_size; + break; + case FW_COREMEM_CODE: + fw_mem->core_code_alloc_size += + layout_entries[entry].alloc_size; + break; + case FW_COREMEM_DATA: + fw_mem->core_data_alloc_size += + layout_entries[entry].alloc_size; + break; + case NONE: + break; + } + } +} + +int +pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw_code_ptr, + void *fw_data_ptr, void *fw_core_code_ptr, void *fw_core_data_ptr, + void **host_addr_out) +{ + const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries; + u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num; + u32 end_addr = addr + size; + int entry = 0; + + /* Ensure requested range is not zero, and size is not causing addr to overflow. */ + if (end_addr <= addr) + return -EINVAL; + + for (entry = 0; entry < num_layout_entries; entry++) { + u32 entry_start_addr = layout_entries[entry].base_addr; + u32 entry_end_addr = entry_start_addr + layout_entries[entry].alloc_size; + + if (addr >= entry_start_addr && addr < entry_end_addr && + end_addr > entry_start_addr && end_addr <= entry_end_addr) { + switch (layout_entries[entry].type) { + case FW_CODE: + *host_addr_out = fw_code_ptr; + break; + + case FW_DATA: + *host_addr_out = fw_data_ptr; + break; + + case FW_COREMEM_CODE: + *host_addr_out = fw_core_code_ptr; + break; + + case FW_COREMEM_DATA: + *host_addr_out = fw_core_data_ptr; + break; + + default: + return -EINVAL; + } + /* Direct Mem write to mapped memory */ + addr -= layout_entries[entry].base_addr; + addr += layout_entries[entry].alloc_offset; + + /* + * Add offset to pointer to FW allocation only if that + * allocation is available + */ + *(u8 **)host_addr_out += addr; + return 0; + } + } + + return -EINVAL; +} + +static int +pvr_fw_create_fwif_connection_ctl(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + fw_dev->fwif_connection_ctl = + pvr_fw_object_create_and_map_offset(pvr_dev, + fw_dev->fw_heap_info.config_offset + + PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET, + sizeof(*fw_dev->fwif_connection_ctl), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, + &fw_dev->mem.fwif_connection_ctl_obj); + if (IS_ERR(fw_dev->fwif_connection_ctl)) { + drm_err(drm_dev, + "Unable to allocate FWIF connection control memory\n"); + return PTR_ERR(fw_dev->fwif_connection_ctl); + } + + return 0; +} + +static void +pvr_fw_fini_fwif_connection_ctl(struct pvr_device *pvr_dev) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + pvr_fw_object_unmap_and_destroy(fw_dev->mem.fwif_connection_ctl_obj); +} + +static void +fw_osinit_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_osinit *fwif_osinit = cpu_ptr; + struct pvr_device *pvr_dev = priv; + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mem *fw_mem = &fw_dev->mem; + + fwif_osinit->kernel_ccbctl_fw_addr = pvr_dev->kccb.ccb.ctrl_fw_addr; + fwif_osinit->kernel_ccb_fw_addr = pvr_dev->kccb.ccb.ccb_fw_addr; + pvr_fw_object_get_fw_addr(pvr_dev->kccb.rtn_obj, + &fwif_osinit->kernel_ccb_rtn_slots_fw_addr); + + fwif_osinit->firmware_ccbctl_fw_addr = pvr_dev->fwccb.ctrl_fw_addr; + fwif_osinit->firmware_ccb_fw_addr = pvr_dev->fwccb.ccb_fw_addr; + + fwif_osinit->work_est_firmware_ccbctl_fw_addr = 0; + fwif_osinit->work_est_firmware_ccb_fw_addr = 0; + + pvr_fw_object_get_fw_addr(fw_mem->hwrinfobuf_obj, + &fwif_osinit->rogue_fwif_hwr_info_buf_ctl_fw_addr); + pvr_fw_object_get_fw_addr(fw_mem->osdata_obj, &fwif_osinit->fw_os_data_fw_addr); + + fwif_osinit->hwr_debug_dump_limit = 0; + + rogue_fwif_compchecks_bvnc_init(&fwif_osinit->rogue_comp_checks.hw_bvnc); + rogue_fwif_compchecks_bvnc_init(&fwif_osinit->rogue_comp_checks.fw_bvnc); +} + +static void +fw_osdata_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_osdata *fwif_osdata = cpu_ptr; + struct pvr_device *pvr_dev = priv; + struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; + + pvr_fw_object_get_fw_addr(fw_mem->power_sync_obj, &fwif_osdata->power_sync_fw_addr); +} + +static void +fw_fault_page_init(void *cpu_ptr, void *priv) +{ + u32 *fault_page = cpu_ptr; + + for (int i = 0; i < PVR_ROGUE_FAULT_PAGE_SIZE / sizeof(*fault_page); i++) + fault_page[i] = 0xdeadbee0; +} + +static void +fw_sysinit_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_sysinit *fwif_sysinit = cpu_ptr; + struct pvr_device *pvr_dev = priv; + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mem *fw_mem = &fw_dev->mem; + dma_addr_t fault_dma_addr = 0; + u32 clock_speed_hz = clk_get_rate(pvr_dev->core_clk); + + WARN_ON(!clock_speed_hz); + + WARN_ON(pvr_fw_object_get_dma_addr(fw_mem->fault_page_obj, 0, &fault_dma_addr)); + fwif_sysinit->fault_phys_addr = (u64)fault_dma_addr; + + fwif_sysinit->pds_exec_base = ROGUE_PDSCODEDATA_HEAP_BASE; + fwif_sysinit->usc_exec_base = ROGUE_USCCODE_HEAP_BASE; + + pvr_fw_object_get_fw_addr(fw_mem->runtime_cfg_obj, &fwif_sysinit->runtime_cfg_fw_addr); + pvr_fw_object_get_fw_addr(fw_dev->fw_trace.tracebuf_ctrl_obj, + &fwif_sysinit->trace_buf_ctl_fw_addr); + pvr_fw_object_get_fw_addr(fw_mem->sysdata_obj, &fwif_sysinit->fw_sys_data_fw_addr); + pvr_fw_object_get_fw_addr(fw_mem->gpu_util_fwcb_obj, + &fwif_sysinit->gpu_util_fw_cb_ctl_fw_addr); + if (fw_mem->core_data_obj) { + pvr_fw_object_get_fw_addr(fw_mem->core_data_obj, + &fwif_sysinit->coremem_data_store.fw_addr); + } + + /* Currently unsupported. */ + fwif_sysinit->counter_dump_ctl.buffer_fw_addr = 0; + fwif_sysinit->counter_dump_ctl.size_in_dwords = 0; + + /* Skip alignment checks. */ + fwif_sysinit->align_checks = 0; + + fwif_sysinit->filter_flags = 0; + fwif_sysinit->hw_perf_filter = 0; + fwif_sysinit->firmware_perf = FW_PERF_CONF_NONE; + fwif_sysinit->initial_core_clock_speed = clock_speed_hz; + fwif_sysinit->active_pm_latency_ms = 0; + fwif_sysinit->gpio_validation_mode = ROGUE_FWIF_GPIO_VAL_OFF; + fwif_sysinit->firmware_started = false; + fwif_sysinit->marker_val = 1; + + memset(&fwif_sysinit->bvnc_km_feature_flags, 0, + sizeof(fwif_sysinit->bvnc_km_feature_flags)); +} + +#define ROGUE_FWIF_SLC_MIN_SIZE_FOR_DM_OVERLAP_KB 4 + +static void +fw_sysdata_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_sysdata *fwif_sysdata = cpu_ptr; + struct pvr_device *pvr_dev = priv; + u32 slc_size_in_kilobytes = 0; + u32 config_flags = 0; + + WARN_ON(PVR_FEATURE_VALUE(pvr_dev, slc_size_in_kilobytes, &slc_size_in_kilobytes)); + + if (slc_size_in_kilobytes < ROGUE_FWIF_SLC_MIN_SIZE_FOR_DM_OVERLAP_KB) + config_flags |= ROGUE_FWIF_INICFG_DISABLE_DM_OVERLAP; + + fwif_sysdata->config_flags = config_flags; +} + +static void +fw_runtime_cfg_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_runtime_cfg *runtime_cfg = cpu_ptr; + struct pvr_device *pvr_dev = priv; + u32 clock_speed_hz = clk_get_rate(pvr_dev->core_clk); + + WARN_ON(!clock_speed_hz); + + runtime_cfg->core_clock_speed = clock_speed_hz; + runtime_cfg->active_pm_latency_ms = 0; + runtime_cfg->active_pm_latency_persistant = true; + WARN_ON(PVR_FEATURE_VALUE(pvr_dev, num_clusters, + &runtime_cfg->default_dusts_num_init) != 0); +} + +static void +fw_gpu_util_fwcb_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_gpu_util_fwcb *gpu_util_fwcb = cpu_ptr; + + gpu_util_fwcb->last_word = PVR_FWIF_GPU_UTIL_STATE_IDLE; +} + +static int +pvr_fw_create_structures(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mem *fw_mem = &fw_dev->mem; + int err; + + fw_dev->power_sync = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->power_sync), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->power_sync_obj); + if (IS_ERR(fw_dev->power_sync)) { + drm_err(drm_dev, "Unable to allocate FW power_sync structure\n"); + return PTR_ERR(fw_dev->power_sync); + } + + fw_dev->hwrinfobuf = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->hwrinfobuf), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->hwrinfobuf_obj); + if (IS_ERR(fw_dev->hwrinfobuf)) { + drm_err(drm_dev, + "Unable to allocate FW hwrinfobuf structure\n"); + err = PTR_ERR(fw_dev->hwrinfobuf); + goto err_release_power_sync; + } + + err = pvr_fw_object_create(pvr_dev, PVR_SYNC_OBJ_SIZE, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->mmucache_sync_obj); + if (err) { + drm_err(drm_dev, + "Unable to allocate MMU cache sync object\n"); + goto err_release_hwrinfobuf; + } + + fw_dev->fwif_sysdata = pvr_fw_object_create_and_map(pvr_dev, + sizeof(*fw_dev->fwif_sysdata), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_sysdata_init, pvr_dev, + &fw_mem->sysdata_obj); + if (IS_ERR(fw_dev->fwif_sysdata)) { + drm_err(drm_dev, "Unable to allocate FW SYSDATA structure\n"); + err = PTR_ERR(fw_dev->fwif_sysdata); + goto err_release_mmucache_sync_obj; + } + + err = pvr_fw_object_create(pvr_dev, PVR_ROGUE_FAULT_PAGE_SIZE, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_fault_page_init, NULL, &fw_mem->fault_page_obj); + if (err) { + drm_err(drm_dev, "Unable to allocate FW fault page\n"); + goto err_release_sysdata; + } + + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_gpu_util_fwcb), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_gpu_util_fwcb_init, pvr_dev, &fw_mem->gpu_util_fwcb_obj); + if (err) { + drm_err(drm_dev, "Unable to allocate GPU util FWCB\n"); + goto err_release_fault_page; + } + + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_runtime_cfg), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_runtime_cfg_init, pvr_dev, &fw_mem->runtime_cfg_obj); + if (err) { + drm_err(drm_dev, "Unable to allocate FW runtime config\n"); + goto err_release_gpu_util_fwcb; + } + + err = pvr_fw_trace_init(pvr_dev); + if (err) + goto err_release_runtime_cfg; + + fw_dev->fwif_osdata = pvr_fw_object_create_and_map(pvr_dev, + sizeof(*fw_dev->fwif_osdata), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_osdata_init, pvr_dev, + &fw_mem->osdata_obj); + if (IS_ERR(fw_dev->fwif_osdata)) { + drm_err(drm_dev, "Unable to allocate FW OSDATA structure\n"); + err = PTR_ERR(fw_dev->fwif_osdata); + goto err_fw_trace_fini; + } + + fw_dev->fwif_osinit = + pvr_fw_object_create_and_map_offset(pvr_dev, + fw_dev->fw_heap_info.config_offset + + PVR_ROGUE_FWIF_OSINIT_OFFSET, + sizeof(*fw_dev->fwif_osinit), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_osinit_init, pvr_dev, &fw_mem->osinit_obj); + if (IS_ERR(fw_dev->fwif_osinit)) { + drm_err(drm_dev, "Unable to allocate FW OSINIT structure\n"); + err = PTR_ERR(fw_dev->fwif_osinit); + goto err_release_osdata; + } + + fw_dev->fwif_sysinit = + pvr_fw_object_create_and_map_offset(pvr_dev, + fw_dev->fw_heap_info.config_offset + + PVR_ROGUE_FWIF_SYSINIT_OFFSET, + sizeof(*fw_dev->fwif_sysinit), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_sysinit_init, pvr_dev, &fw_mem->sysinit_obj); + if (IS_ERR(fw_dev->fwif_sysinit)) { + drm_err(drm_dev, "Unable to allocate FW SYSINIT structure\n"); + err = PTR_ERR(fw_dev->fwif_sysinit); + goto err_release_osinit; + } + + return 0; + +err_release_osinit: + pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj); + +err_release_osdata: + pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj); + +err_fw_trace_fini: + pvr_fw_trace_fini(pvr_dev); + +err_release_runtime_cfg: + pvr_fw_object_destroy(fw_mem->runtime_cfg_obj); + +err_release_gpu_util_fwcb: + pvr_fw_object_destroy(fw_mem->gpu_util_fwcb_obj); + +err_release_fault_page: + pvr_fw_object_destroy(fw_mem->fault_page_obj); + +err_release_sysdata: + pvr_fw_object_unmap_and_destroy(fw_mem->sysdata_obj); + +err_release_mmucache_sync_obj: + pvr_fw_object_destroy(fw_mem->mmucache_sync_obj); + +err_release_hwrinfobuf: + pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj); + +err_release_power_sync: + pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj); + + return err; +} + +static void +pvr_fw_destroy_structures(struct pvr_device *pvr_dev) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mem *fw_mem = &fw_dev->mem; + + pvr_fw_trace_fini(pvr_dev); + pvr_fw_object_destroy(fw_mem->runtime_cfg_obj); + pvr_fw_object_destroy(fw_mem->gpu_util_fwcb_obj); + pvr_fw_object_destroy(fw_mem->fault_page_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->sysdata_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->sysinit_obj); + + pvr_fw_object_destroy(fw_mem->mmucache_sync_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj); + pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj); +} + +/** + * pvr_fw_process() - Process firmware image, allocate FW memory and create boot + * arguments + * @pvr_dev: Device pointer. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_fw_object_create_and_map_offset(), or + * * Any error returned by pvr_fw_object_create_and_map(). + */ +static int +pvr_fw_process(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; + const u8 *fw = pvr_dev->fw_dev.firmware->data; + const struct pvr_fw_layout_entry *private_data; + u8 *fw_code_ptr; + u8 *fw_data_ptr; + u8 *fw_core_code_ptr; + u8 *fw_core_data_ptr; + int err; + + layout_get_sizes(pvr_dev); + + private_data = pvr_fw_find_private_data(pvr_dev); + if (!private_data) + return -EINVAL; + + /* Allocate and map memory for firmware sections. */ + + /* + * Code allocation must be at the start of the firmware heap, otherwise + * firmware processor will be unable to boot. + * + * This has the useful side-effect that for every other object in the + * driver, a firmware address of 0 is invalid. + */ + fw_code_ptr = pvr_fw_object_create_and_map_offset(pvr_dev, 0, fw_mem->code_alloc_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->code_obj); + if (IS_ERR(fw_code_ptr)) { + drm_err(drm_dev, "Unable to allocate FW code memory\n"); + return PTR_ERR(fw_code_ptr); + } + + if (pvr_dev->fw_dev.defs->has_fixed_data_addr()) { + u32 base_addr = private_data->base_addr & pvr_dev->fw_dev.fw_heap_info.offset_mask; + + fw_data_ptr = + pvr_fw_object_create_and_map_offset(pvr_dev, base_addr, + fw_mem->data_alloc_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->data_obj); + } else { + fw_data_ptr = pvr_fw_object_create_and_map(pvr_dev, fw_mem->data_alloc_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->data_obj); + } + if (IS_ERR(fw_data_ptr)) { + drm_err(drm_dev, "Unable to allocate FW data memory\n"); + err = PTR_ERR(fw_data_ptr); + goto err_free_fw_code_obj; + } + + /* Core code and data sections are optional. */ + if (fw_mem->core_code_alloc_size) { + fw_core_code_ptr = + pvr_fw_object_create_and_map(pvr_dev, fw_mem->core_code_alloc_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->core_code_obj); + if (IS_ERR(fw_core_code_ptr)) { + drm_err(drm_dev, + "Unable to allocate FW core code memory\n"); + err = PTR_ERR(fw_core_code_ptr); + goto err_free_fw_data_obj; + } + } else { + fw_core_code_ptr = NULL; + } + + if (fw_mem->core_data_alloc_size) { + fw_core_data_ptr = + pvr_fw_object_create_and_map(pvr_dev, fw_mem->core_data_alloc_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->core_data_obj); + if (IS_ERR(fw_core_data_ptr)) { + drm_err(drm_dev, + "Unable to allocate FW core data memory\n"); + err = PTR_ERR(fw_core_data_ptr); + goto err_free_fw_core_code_obj; + } + } else { + fw_core_data_ptr = NULL; + } + + fw_mem->code = kzalloc(fw_mem->code_alloc_size, GFP_KERNEL); + fw_mem->data = kzalloc(fw_mem->data_alloc_size, GFP_KERNEL); + if (fw_mem->core_code_alloc_size) + fw_mem->core_code = kzalloc(fw_mem->core_code_alloc_size, GFP_KERNEL); + if (fw_mem->core_data_alloc_size) + fw_mem->core_data = kzalloc(fw_mem->core_data_alloc_size, GFP_KERNEL); + + if (!fw_mem->code || !fw_mem->data || + (!fw_mem->core_code && fw_mem->core_code_alloc_size) || + (!fw_mem->core_data && fw_mem->core_data_alloc_size)) { + err = -ENOMEM; + goto err_free_kdata; + } + + err = pvr_dev->fw_dev.defs->fw_process(pvr_dev, fw, + fw_mem->code, fw_mem->data, fw_mem->core_code, + fw_mem->core_data, fw_mem->core_code_alloc_size); + + if (err) + goto err_free_fw_core_data_obj; + + memcpy(fw_code_ptr, fw_mem->code, fw_mem->code_alloc_size); + memcpy(fw_data_ptr, fw_mem->data, fw_mem->data_alloc_size); + if (fw_mem->core_code) + memcpy(fw_core_code_ptr, fw_mem->core_code, fw_mem->core_code_alloc_size); + if (fw_mem->core_data) + memcpy(fw_core_data_ptr, fw_mem->core_data, fw_mem->core_data_alloc_size); + + /* We're finished with the firmware section memory on the CPU, unmap. */ + if (fw_core_data_ptr) + pvr_fw_object_vunmap(fw_mem->core_data_obj); + if (fw_core_code_ptr) + pvr_fw_object_vunmap(fw_mem->core_code_obj); + pvr_fw_object_vunmap(fw_mem->data_obj); + fw_data_ptr = NULL; + pvr_fw_object_vunmap(fw_mem->code_obj); + fw_code_ptr = NULL; + + err = pvr_fw_create_fwif_connection_ctl(pvr_dev); + if (err) + goto err_free_fw_core_data_obj; + + return 0; + +err_free_kdata: + kfree(fw_mem->core_data); + kfree(fw_mem->core_code); + kfree(fw_mem->data); + kfree(fw_mem->code); + +err_free_fw_core_data_obj: + if (fw_core_data_ptr) + pvr_fw_object_unmap_and_destroy(fw_mem->core_data_obj); + +err_free_fw_core_code_obj: + if (fw_core_code_ptr) + pvr_fw_object_unmap_and_destroy(fw_mem->core_code_obj); + +err_free_fw_data_obj: + if (fw_data_ptr) + pvr_fw_object_vunmap(fw_mem->data_obj); + pvr_fw_object_destroy(fw_mem->data_obj); + +err_free_fw_code_obj: + if (fw_code_ptr) + pvr_fw_object_vunmap(fw_mem->code_obj); + pvr_fw_object_destroy(fw_mem->code_obj); + + return err; +} + +static int +pvr_copy_to_fw(struct pvr_fw_object *dest_obj, u8 *src_ptr, u32 size) +{ + u8 *dest_ptr = pvr_fw_object_vmap(dest_obj); + + if (IS_ERR(dest_ptr)) + return PTR_ERR(dest_ptr); + + memcpy(dest_ptr, src_ptr, size); + + pvr_fw_object_vunmap(dest_obj); + + return 0; +} + +static int +pvr_fw_reinit_code_data(struct pvr_device *pvr_dev) +{ + struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; + int err; + + err = pvr_copy_to_fw(fw_mem->code_obj, fw_mem->code, fw_mem->code_alloc_size); + if (err) + return err; + + err = pvr_copy_to_fw(fw_mem->data_obj, fw_mem->data, fw_mem->data_alloc_size); + if (err) + return err; + + if (fw_mem->core_code) { + err = pvr_copy_to_fw(fw_mem->core_code_obj, fw_mem->core_code, + fw_mem->core_code_alloc_size); + if (err) + return err; + } + + if (fw_mem->core_data) { + err = pvr_copy_to_fw(fw_mem->core_data_obj, fw_mem->core_data, + fw_mem->core_data_alloc_size); + if (err) + return err; + } + + return 0; +} + +static void +pvr_fw_cleanup(struct pvr_device *pvr_dev) +{ + struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; + + pvr_fw_fini_fwif_connection_ctl(pvr_dev); + if (fw_mem->core_code_obj) + pvr_fw_object_destroy(fw_mem->core_code_obj); + if (fw_mem->core_data_obj) + pvr_fw_object_destroy(fw_mem->core_data_obj); + pvr_fw_object_destroy(fw_mem->code_obj); + pvr_fw_object_destroy(fw_mem->data_obj); +} + +/** + * pvr_wait_for_fw_boot() - Wait for firmware to finish booting + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, or + * * -%ETIMEDOUT if firmware fails to boot within timeout. + */ +int +pvr_wait_for_fw_boot(struct pvr_device *pvr_dev) +{ + ktime_t deadline = ktime_add_us(ktime_get(), FW_BOOT_TIMEOUT_USEC); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + while (ktime_to_ns(ktime_sub(deadline, ktime_get())) > 0) { + if (READ_ONCE(fw_dev->fwif_sysinit->firmware_started)) + return 0; + } + + return -ETIMEDOUT; +} + +/* + * pvr_fw_heap_info_init() - Calculate size and masks for FW heap + * @pvr_dev: Target PowerVR device. + * @log2_size: Log2 of raw heap size. + * @reserved_size: Size of reserved area of heap, in bytes. May be zero. + */ +void +pvr_fw_heap_info_init(struct pvr_device *pvr_dev, u32 log2_size, u32 reserved_size) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + fw_dev->fw_heap_info.gpu_addr = PVR_ROGUE_FW_MAIN_HEAP_BASE; + fw_dev->fw_heap_info.log2_size = log2_size; + fw_dev->fw_heap_info.reserved_size = reserved_size; + fw_dev->fw_heap_info.raw_size = 1 << fw_dev->fw_heap_info.log2_size; + fw_dev->fw_heap_info.offset_mask = fw_dev->fw_heap_info.raw_size - 1; + fw_dev->fw_heap_info.config_offset = fw_dev->fw_heap_info.raw_size - + PVR_ROGUE_FW_CONFIG_HEAP_SIZE; + fw_dev->fw_heap_info.size = fw_dev->fw_heap_info.raw_size - + (PVR_ROGUE_FW_CONFIG_HEAP_SIZE + reserved_size); +} + +/** + * pvr_fw_validate_init_device_info() - Validate firmware and initialise device information + * @pvr_dev: Target PowerVR device. + * + * This function must be called before querying device information. + * + * Returns: + * * 0 on success, or + * * -%EINVAL if firmware validation fails. + */ +int +pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev) +{ + int err; + + err = pvr_fw_validate(pvr_dev); + if (err) + return err; + + return pvr_fw_get_device_info(pvr_dev); +} + +/** + * pvr_fw_init() - Initialise and boot firmware + * @pvr_dev: Target PowerVR device + * + * On successful completion of the function the PowerVR device will be + * initialised and ready to use. + * + * Returns: + * * 0 on success, + * * -%EINVAL on invalid firmware image, + * * -%ENOMEM on out of memory, or + * * -%ETIMEDOUT if firmware processor fails to boot or on register poll timeout. + */ +int +pvr_fw_init(struct pvr_device *pvr_dev) +{ + u32 kccb_size_log2 = ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT; + u32 kccb_rtn_size = (1 << kccb_size_log2) * sizeof(*pvr_dev->kccb.rtn); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + int err; + + if (fw_dev->processor_type == PVR_FW_PROCESSOR_TYPE_META) + fw_dev->defs = &pvr_fw_defs_meta; + else if (fw_dev->processor_type == PVR_FW_PROCESSOR_TYPE_MIPS) + fw_dev->defs = &pvr_fw_defs_mips; + else + return -EINVAL; + + err = fw_dev->defs->init(pvr_dev); + if (err) + return err; + + drm_mm_init(&fw_dev->fw_mm, ROGUE_FW_HEAP_BASE, fw_dev->fw_heap_info.raw_size); + fw_dev->fw_mm_base = ROGUE_FW_HEAP_BASE; + spin_lock_init(&fw_dev->fw_mm_lock); + + INIT_LIST_HEAD(&fw_dev->fw_objs.list); + err = drmm_mutex_init(from_pvr_device(pvr_dev), &fw_dev->fw_objs.lock); + if (err) + goto err_mm_takedown; + + err = pvr_fw_process(pvr_dev); + if (err) + goto err_mm_takedown; + + /* Initialise KCCB and FWCCB. */ + err = pvr_kccb_init(pvr_dev); + if (err) + goto err_fw_cleanup; + + err = pvr_fwccb_init(pvr_dev); + if (err) + goto err_kccb_fini; + + /* Allocate memory for KCCB return slots. */ + pvr_dev->kccb.rtn = pvr_fw_object_create_and_map(pvr_dev, kccb_rtn_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &pvr_dev->kccb.rtn_obj); + if (IS_ERR(pvr_dev->kccb.rtn)) { + err = PTR_ERR(pvr_dev->kccb.rtn); + goto err_fwccb_fini; + } + + err = pvr_fw_create_structures(pvr_dev); + if (err) + goto err_kccb_rtn_release; + + err = pvr_fw_start(pvr_dev); + if (err) + goto err_destroy_structures; + + err = pvr_wait_for_fw_boot(pvr_dev); + if (err) { + drm_err(from_pvr_device(pvr_dev), "Firmware failed to boot\n"); + goto err_fw_stop; + } + + fw_dev->booted = true; + + return 0; + +err_fw_stop: + pvr_fw_stop(pvr_dev); + +err_destroy_structures: + pvr_fw_destroy_structures(pvr_dev); + +err_kccb_rtn_release: + pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj); + +err_fwccb_fini: + pvr_ccb_fini(&pvr_dev->fwccb); + +err_kccb_fini: + pvr_kccb_fini(pvr_dev); + +err_fw_cleanup: + pvr_fw_cleanup(pvr_dev); + +err_mm_takedown: + drm_mm_takedown(&fw_dev->fw_mm); + + if (fw_dev->defs->fini) + fw_dev->defs->fini(pvr_dev); + + return err; +} + +/** + * pvr_fw_fini() - Shutdown firmware processor and free associated memory + * @pvr_dev: Target PowerVR device + */ +void +pvr_fw_fini(struct pvr_device *pvr_dev) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + fw_dev->booted = false; + + pvr_fw_destroy_structures(pvr_dev); + pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj); + + /* + * Ensure FWCCB worker has finished executing before destroying FWCCB. The IRQ handler has + * been unregistered at this point so no new work should be being submitted. + */ + pvr_ccb_fini(&pvr_dev->fwccb); + pvr_kccb_fini(pvr_dev); + pvr_fw_cleanup(pvr_dev); + + mutex_lock(&pvr_dev->fw_dev.fw_objs.lock); + WARN_ON(!list_empty(&pvr_dev->fw_dev.fw_objs.list)); + mutex_unlock(&pvr_dev->fw_dev.fw_objs.lock); + + drm_mm_takedown(&fw_dev->fw_mm); + + if (fw_dev->defs->fini) + fw_dev->defs->fini(pvr_dev); +} + +/** + * pvr_fw_mts_schedule() - Schedule work via an MTS kick + * @pvr_dev: Target PowerVR device + * @val: Kick mask. Should be a combination of %ROGUE_CR_MTS_SCHEDULE_* + */ +void +pvr_fw_mts_schedule(struct pvr_device *pvr_dev, u32 val) +{ + /* Ensure memory is flushed before kicking MTS. */ + wmb(); + + pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_SCHEDULE, val); + + /* Ensure the MTS kick goes through before continuing. */ + mb(); +} + +/** + * pvr_fw_structure_cleanup() - Send FW cleanup request for an object + * @pvr_dev: Target PowerVR device. + * @type: Type of object to cleanup. Must be one of &enum rogue_fwif_cleanup_type. + * @fw_obj: Pointer to FW object containing object to cleanup. + * @offset: Offset within FW object of object to cleanup. + * + * Returns: + * * 0 on success, + * * -EBUSY if object is busy, + * * -ETIMEDOUT on timeout, or + * * -EIO if device is lost. + */ +int +pvr_fw_structure_cleanup(struct pvr_device *pvr_dev, u32 type, struct pvr_fw_object *fw_obj, + u32 offset) +{ + struct rogue_fwif_kccb_cmd cmd; + int slot_nr; + int idx; + int err; + u32 rtn; + + struct rogue_fwif_cleanup_request *cleanup_req = &cmd.cmd_data.cleanup_data; + + down_read(&pvr_dev->reset_sem); + + if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) { + err = -EIO; + goto err_up_read; + } + + cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_CLEANUP; + cmd.kccb_flags = 0; + cleanup_req->cleanup_type = type; + + switch (type) { + case ROGUE_FWIF_CLEANUP_FWCOMMONCONTEXT: + pvr_fw_object_get_fw_addr_offset(fw_obj, offset, + &cleanup_req->cleanup_data.context_fw_addr); + break; + case ROGUE_FWIF_CLEANUP_HWRTDATA: + pvr_fw_object_get_fw_addr_offset(fw_obj, offset, + &cleanup_req->cleanup_data.hwrt_data_fw_addr); + break; + case ROGUE_FWIF_CLEANUP_FREELIST: + pvr_fw_object_get_fw_addr_offset(fw_obj, offset, + &cleanup_req->cleanup_data.freelist_fw_addr); + break; + default: + err = -EINVAL; + goto err_drm_dev_exit; + } + + err = pvr_kccb_send_cmd(pvr_dev, &cmd, &slot_nr); + if (err) + goto err_drm_dev_exit; + + err = pvr_kccb_wait_for_completion(pvr_dev, slot_nr, HZ, &rtn); + if (err) + goto err_drm_dev_exit; + + if (rtn & ROGUE_FWIF_KCCB_RTN_SLOT_CLEANUP_BUSY) + err = -EBUSY; + +err_drm_dev_exit: + drm_dev_exit(idx); + +err_up_read: + up_read(&pvr_dev->reset_sem); + + return err; +} + +/** + * pvr_fw_object_fw_map() - Map a FW object in firmware address space + * @pvr_dev: Device pointer. + * @fw_obj: FW object to map. + * @dev_addr: Desired address in device space, if a specific address is + * required. 0 otherwise. + * + * Returns: + * * 0 on success, or + * * -%EINVAL if @fw_obj is already mapped but has no references, or + * * Any error returned by DRM. + */ +static int +pvr_fw_object_fw_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj, u64 dev_addr) +{ + struct pvr_gem_object *pvr_obj = fw_obj->gem; + struct drm_gem_object *gem_obj = gem_from_pvr_gem(pvr_obj); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + int err; + + spin_lock(&fw_dev->fw_mm_lock); + + if (drm_mm_node_allocated(&fw_obj->fw_mm_node)) { + err = -EINVAL; + goto err_unlock; + } + + if (!dev_addr) { + /* + * Allocate from the main heap only (firmware heap minus + * config space). + */ + err = drm_mm_insert_node_in_range(&fw_dev->fw_mm, &fw_obj->fw_mm_node, + gem_obj->size, 0, 0, + fw_dev->fw_heap_info.gpu_addr, + fw_dev->fw_heap_info.gpu_addr + + fw_dev->fw_heap_info.size, 0); + if (err) + goto err_unlock; + } else { + fw_obj->fw_mm_node.start = dev_addr; + fw_obj->fw_mm_node.size = gem_obj->size; + err = drm_mm_reserve_node(&fw_dev->fw_mm, &fw_obj->fw_mm_node); + if (err) + goto err_unlock; + } + + spin_unlock(&fw_dev->fw_mm_lock); + + /* Map object on GPU. */ + err = fw_dev->defs->vm_map(pvr_dev, fw_obj); + if (err) + goto err_remove_node; + + fw_obj->fw_addr_offset = (u32)(fw_obj->fw_mm_node.start - fw_dev->fw_mm_base); + + return 0; + +err_remove_node: + spin_lock(&fw_dev->fw_mm_lock); + drm_mm_remove_node(&fw_obj->fw_mm_node); + +err_unlock: + spin_unlock(&fw_dev->fw_mm_lock); + + return err; +} + +/** + * pvr_fw_object_fw_unmap() - Unmap a previously mapped FW object + * @fw_obj: FW object to unmap. + * + * Returns: + * * 0 on success, or + * * -%EINVAL if object is not currently mapped. + */ +static int +pvr_fw_object_fw_unmap(struct pvr_fw_object *fw_obj) +{ + struct pvr_gem_object *pvr_obj = fw_obj->gem; + struct drm_gem_object *gem_obj = gem_from_pvr_gem(pvr_obj); + struct pvr_device *pvr_dev = to_pvr_device(gem_obj->dev); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + fw_dev->defs->vm_unmap(pvr_dev, fw_obj); + + spin_lock(&fw_dev->fw_mm_lock); + + if (!drm_mm_node_allocated(&fw_obj->fw_mm_node)) { + spin_unlock(&fw_dev->fw_mm_lock); + return -EINVAL; + } + + drm_mm_remove_node(&fw_obj->fw_mm_node); + + spin_unlock(&fw_dev->fw_mm_lock); + + return 0; +} + +static void * +pvr_fw_object_create_and_map_common(struct pvr_device *pvr_dev, size_t size, + u64 flags, u64 dev_addr, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **fw_obj_out) +{ + struct pvr_fw_object *fw_obj; + void *cpu_ptr; + int err; + + /* %DRM_PVR_BO_PM_FW_PROTECT is implicit for FW objects. */ + flags |= DRM_PVR_BO_PM_FW_PROTECT; + + fw_obj = kzalloc(sizeof(*fw_obj), GFP_KERNEL); + if (!fw_obj) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&fw_obj->node); + fw_obj->init = init; + fw_obj->init_priv = init_priv; + + fw_obj->gem = pvr_gem_object_create(pvr_dev, size, flags); + if (IS_ERR(fw_obj->gem)) { + err = PTR_ERR(fw_obj->gem); + fw_obj->gem = NULL; + goto err_put_object; + } + + err = pvr_fw_object_fw_map(pvr_dev, fw_obj, dev_addr); + if (err) + goto err_put_object; + + cpu_ptr = pvr_fw_object_vmap(fw_obj); + if (IS_ERR(cpu_ptr)) { + err = PTR_ERR(cpu_ptr); + goto err_put_object; + } + + *fw_obj_out = fw_obj; + + if (fw_obj->init) + fw_obj->init(cpu_ptr, fw_obj->init_priv); + + mutex_lock(&pvr_dev->fw_dev.fw_objs.lock); + list_add_tail(&fw_obj->node, &pvr_dev->fw_dev.fw_objs.list); + mutex_unlock(&pvr_dev->fw_dev.fw_objs.lock); + + return cpu_ptr; + +err_put_object: + pvr_fw_object_destroy(fw_obj); + + return ERR_PTR(err); +} + +/** + * pvr_fw_object_create() - Create a FW object and map to firmware + * @pvr_dev: PowerVR device pointer. + * @size: Size of object, in bytes. + * @flags: Options which affect both this operation and future mapping + * operations performed on the returned object. Must be a combination of + * DRM_PVR_BO_* and/or PVR_BO_* flags. + * @init: Initialisation callback. + * @init_priv: Private pointer to pass to initialisation callback. + * @fw_obj_out: Pointer to location to store created object pointer. + * + * %DRM_PVR_BO_DEVICE_PM_FW_PROTECT is implied for all FW objects. Consequently, + * this function will fail if @flags has %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS + * set. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_fw_object_create_common(). + */ +int +pvr_fw_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), void *init_priv, + struct pvr_fw_object **fw_obj_out) +{ + void *cpu_ptr; + + cpu_ptr = pvr_fw_object_create_and_map_common(pvr_dev, size, flags, 0, init, init_priv, + fw_obj_out); + if (IS_ERR(cpu_ptr)) + return PTR_ERR(cpu_ptr); + + pvr_fw_object_vunmap(*fw_obj_out); + + return 0; +} + +/** + * pvr_fw_object_create_and_map() - Create a FW object and map to firmware and CPU + * @pvr_dev: PowerVR device pointer. + * @size: Size of object, in bytes. + * @flags: Options which affect both this operation and future mapping + * operations performed on the returned object. Must be a combination of + * DRM_PVR_BO_* and/or PVR_BO_* flags. + * @init: Initialisation callback. + * @init_priv: Private pointer to pass to initialisation callback. + * @fw_obj_out: Pointer to location to store created object pointer. + * + * %DRM_PVR_BO_DEVICE_PM_FW_PROTECT is implied for all FW objects. Consequently, + * this function will fail if @flags has %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS + * set. + * + * Caller is responsible for calling pvr_fw_object_vunmap() to release the CPU + * mapping. + * + * Returns: + * * Pointer to CPU mapping of newly created object, or + * * Any error returned by pvr_fw_object_create(), or + * * Any error returned by pvr_fw_object_vmap(). + */ +void * +pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **fw_obj_out) +{ + return pvr_fw_object_create_and_map_common(pvr_dev, size, flags, 0, init, init_priv, + fw_obj_out); +} + +/** + * pvr_fw_object_create_and_map_offset() - Create a FW object and map to + * firmware at the provided offset and to the CPU. + * @pvr_dev: PowerVR device pointer. + * @dev_offset: Base address of desired FW mapping, offset from start of FW heap. + * @size: Size of object, in bytes. + * @flags: Options which affect both this operation and future mapping + * operations performed on the returned object. Must be a combination of + * DRM_PVR_BO_* and/or PVR_BO_* flags. + * @init: Initialisation callback. + * @init_priv: Private pointer to pass to initialisation callback. + * @fw_obj_out: Pointer to location to store created object pointer. + * + * %DRM_PVR_BO_DEVICE_PM_FW_PROTECT is implied for all FW objects. Consequently, + * this function will fail if @flags has %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS + * set. + * + * Caller is responsible for calling pvr_fw_object_vunmap() to release the CPU + * mapping. + * + * Returns: + * * Pointer to CPU mapping of newly created object, or + * * Any error returned by pvr_fw_object_create(), or + * * Any error returned by pvr_fw_object_vmap(). + */ +void * +pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, + u32 dev_offset, size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **fw_obj_out) +{ + u64 dev_addr = pvr_dev->fw_dev.fw_mm_base + dev_offset; + + return pvr_fw_object_create_and_map_common(pvr_dev, size, flags, dev_addr, init, init_priv, + fw_obj_out); +} + +/** + * pvr_fw_object_destroy() - Destroy a pvr_fw_object + * @fw_obj: Pointer to object to destroy. + */ +void pvr_fw_object_destroy(struct pvr_fw_object *fw_obj) +{ + struct pvr_gem_object *pvr_obj = fw_obj->gem; + struct drm_gem_object *gem_obj = gem_from_pvr_gem(pvr_obj); + struct pvr_device *pvr_dev = to_pvr_device(gem_obj->dev); + + mutex_lock(&pvr_dev->fw_dev.fw_objs.lock); + list_del(&fw_obj->node); + mutex_unlock(&pvr_dev->fw_dev.fw_objs.lock); + + if (drm_mm_node_allocated(&fw_obj->fw_mm_node)) { + /* If we can't unmap, leak the memory. */ + if (WARN_ON(pvr_fw_object_fw_unmap(fw_obj))) + return; + } + + if (fw_obj->gem) + pvr_gem_object_put(fw_obj->gem); + + kfree(fw_obj); +} + +/** + * pvr_fw_object_get_fw_addr_offset() - Return address of object in firmware address space, with + * given offset. + * @fw_obj: Pointer to object. + * @offset: Desired offset from start of object. + * @fw_addr_out: Location to store address to. + */ +void pvr_fw_object_get_fw_addr_offset(struct pvr_fw_object *fw_obj, u32 offset, u32 *fw_addr_out) +{ + struct pvr_gem_object *pvr_obj = fw_obj->gem; + struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(pvr_obj)->dev); + + *fw_addr_out = pvr_dev->fw_dev.defs->get_fw_addr_with_offset(fw_obj, offset); +} + +/* + * pvr_fw_hard_reset() - Re-initialise the FW code and data segments, and reset all global FW + * structures + * @pvr_dev: Device pointer + * + * If this function returns an error then the caller must regard the device as lost. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_fw_init_dev_structures() or pvr_fw_reset_all(). + */ +int +pvr_fw_hard_reset(struct pvr_device *pvr_dev) +{ + struct list_head *pos; + int err; + + /* Reset all FW objects */ + mutex_lock(&pvr_dev->fw_dev.fw_objs.lock); + + list_for_each(pos, &pvr_dev->fw_dev.fw_objs.list) { + struct pvr_fw_object *fw_obj = container_of(pos, struct pvr_fw_object, node); + void *cpu_ptr = pvr_fw_object_vmap(fw_obj); + + WARN_ON(IS_ERR(cpu_ptr)); + + if (!(fw_obj->gem->flags & PVR_BO_FW_NO_CLEAR_ON_RESET)) { + memset(cpu_ptr, 0, pvr_gem_object_size(fw_obj->gem)); + + if (fw_obj->init) + fw_obj->init(cpu_ptr, fw_obj->init_priv); + } + + pvr_fw_object_vunmap(fw_obj); + } + + mutex_unlock(&pvr_dev->fw_dev.fw_objs.lock); + + err = pvr_fw_reinit_code_data(pvr_dev); + if (err) + return err; + + return 0; +} diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h new file mode 100644 index 000000000000..5cd3ef08d82b --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw.h @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_H +#define PVR_FW_H + +#include "pvr_fw_info.h" +#include "pvr_fw_trace.h" +#include "pvr_gem.h" + +#include <drm/drm_mm.h> + +#include <linux/types.h> + +/* Forward declarations from "pvr_device.h". */ +struct pvr_device; +struct pvr_file; + +/* Forward declaration from "pvr_vm.h". */ +struct pvr_vm_context; + +#define ROGUE_FWIF_FWCCB_NUMCMDS_LOG2 5 + +#define ROGUE_FWIF_KCCB_NUMCMDS_LOG2_DEFAULT 7 + +/** + * struct pvr_fw_object - container for firmware memory allocations + */ +struct pvr_fw_object { + /** @ref_count: FW object reference counter. */ + struct kref ref_count; + + /** @gem: GEM object backing the FW object. */ + struct pvr_gem_object *gem; + + /** + * @fw_mm_node: Node representing mapping in FW address space. @pvr_obj->lock must + * be held when writing. + */ + struct drm_mm_node fw_mm_node; + + /** + * @fw_addr_offset: Virtual address offset of firmware mapping. Only + * valid if @flags has %PVR_GEM_OBJECT_FLAGS_FW_MAPPED + * set. + */ + u32 fw_addr_offset; + + /** + * @init: Initialisation callback. Will be called on object creation and FW hard reset. + * Object will have been zeroed before this is called. + */ + void (*init)(void *cpu_ptr, void *priv); + + /** @init_priv: Private data for initialisation callback. */ + void *init_priv; + + /** @node: Node for firmware object list. */ + struct list_head node; +}; + +/** + * struct pvr_fw_defs - FW processor function table and static definitions + */ +struct pvr_fw_defs { + /** + * @init: + * + * FW processor specific initialisation. + * @pvr_dev: Target PowerVR device. + * + * This function must call pvr_fw_heap_calculate() to initialise the firmware heap for this + * FW processor. + * + * This function is mandatory. + * + * Returns: + * * 0 on success, or + * * Any appropriate error on failure. + */ + int (*init)(struct pvr_device *pvr_dev); + + /** + * @fini: + * + * FW processor specific finalisation. + * @pvr_dev: Target PowerVR device. + * + * This function is optional. + */ + void (*fini)(struct pvr_device *pvr_dev); + + /** + * @fw_process: + * + * Load and process firmware image. + * @pvr_dev: Target PowerVR device. + * @fw: Pointer to firmware image. + * @fw_code_ptr: Pointer to firmware code section. + * @fw_data_ptr: Pointer to firmware data section. + * @fw_core_code_ptr: Pointer to firmware core code section. May be %NULL. + * @fw_core_data_ptr: Pointer to firmware core data section. May be %NULL. + * @core_code_alloc_size: Total allocation size of core code section. + * + * This function is mandatory. + * + * Returns: + * * 0 on success, or + * * Any appropriate error on failure. + */ + int (*fw_process)(struct pvr_device *pvr_dev, const u8 *fw, + u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, + u8 *fw_core_data_ptr, u32 core_code_alloc_size); + + /** + * @vm_map: + * + * Map FW object into FW processor address space. + * @pvr_dev: Target PowerVR device. + * @fw_obj: FW object to map. + * + * This function is mandatory. + * + * Returns: + * * 0 on success, or + * * Any appropriate error on failure. + */ + int (*vm_map)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj); + + /** + * @vm_unmap: + * + * Unmap FW object from FW processor address space. + * @pvr_dev: Target PowerVR device. + * @fw_obj: FW object to map. + * + * This function is mandatory. + */ + void (*vm_unmap)(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj); + + /** + * @get_fw_addr_with_offset: + * + * Called to get address of object in firmware address space, with offset. + * @fw_obj: Pointer to object. + * @offset: Desired offset from start of object. + * + * This function is mandatory. + * + * Returns: + * * Address in firmware address space. + */ + u32 (*get_fw_addr_with_offset)(struct pvr_fw_object *fw_obj, u32 offset); + + /** + * @wrapper_init: + * + * Called to initialise FW wrapper. + * @pvr_dev: Target PowerVR device. + * + * This function is mandatory. + * + * Returns: + * * 0 on success. + * * Any appropriate error on failure. + */ + int (*wrapper_init)(struct pvr_device *pvr_dev); + + /** + * @has_fixed_data_addr: + * + * Called to check if firmware fixed data must be loaded at the address given by the + * firmware layout table. + * + * This function is mandatory. + * + * Returns: + * * %true if firmware fixed data must be loaded at the address given by the firmware + * layout table. + * * %false otherwise. + */ + bool (*has_fixed_data_addr)(void); + + /** + * @irq: FW Interrupt information. + * + * Those are processor dependent, and should be initialized by the + * processor backend in pvr_fw_funcs::init(). + */ + struct { + /** @enable_reg: FW interrupt enable register. */ + u32 enable_reg; + + /** @status_reg: FW interrupt status register. */ + u32 status_reg; + + /** + * @clear_reg: FW interrupt clear register. + * + * If @status_reg == @clear_reg, we clear by write a bit to zero, + * otherwise we clear by writing a bit to one. + */ + u32 clear_reg; + + /** @event_mask: Bitmask of events to listen for. */ + u32 event_mask; + + /** @clear_mask: Value to write to the clear_reg in order to clear FW IRQs. */ + u32 clear_mask; + } irq; +}; + +/** + * struct pvr_fw_mem - FW memory allocations + */ +struct pvr_fw_mem { + /** @code_obj: Object representing firmware code. */ + struct pvr_fw_object *code_obj; + + /** @data_obj: Object representing firmware data. */ + struct pvr_fw_object *data_obj; + + /** + * @core_code_obj: Object representing firmware core code. May be + * %NULL if firmware does not contain this section. + */ + struct pvr_fw_object *core_code_obj; + + /** + * @core_data_obj: Object representing firmware core data. May be + * %NULL if firmware does not contain this section. + */ + struct pvr_fw_object *core_data_obj; + + /** @code: Driver-side copy of firmware code. */ + u8 *code; + + /** @data: Driver-side copy of firmware data. */ + u8 *data; + + /** + * @core_code: Driver-side copy of firmware core code. May be %NULL if firmware does not + * contain this section. + */ + u8 *core_code; + + /** + * @core_data: Driver-side copy of firmware core data. May be %NULL if firmware does not + * contain this section. + */ + u8 *core_data; + + /** @code_alloc_size: Allocation size of firmware code section. */ + u32 code_alloc_size; + + /** @data_alloc_size: Allocation size of firmware data section. */ + u32 data_alloc_size; + + /** @core_code_alloc_size: Allocation size of firmware core code section. */ + u32 core_code_alloc_size; + + /** @core_data_alloc_size: Allocation size of firmware core data section. */ + u32 core_data_alloc_size; + + /** + * @fwif_connection_ctl_obj: Object representing FWIF connection control + * structure. + */ + struct pvr_fw_object *fwif_connection_ctl_obj; + + /** @osinit_obj: Object representing FW OSINIT structure. */ + struct pvr_fw_object *osinit_obj; + + /** @sysinit_obj: Object representing FW SYSINIT structure. */ + struct pvr_fw_object *sysinit_obj; + + /** @osdata_obj: Object representing FW OSDATA structure. */ + struct pvr_fw_object *osdata_obj; + + /** @hwrinfobuf_obj: Object representing FW hwrinfobuf structure. */ + struct pvr_fw_object *hwrinfobuf_obj; + + /** @sysdata_obj: Object representing FW SYSDATA structure. */ + struct pvr_fw_object *sysdata_obj; + + /** @power_sync_obj: Object representing power sync state. */ + struct pvr_fw_object *power_sync_obj; + + /** @fault_page_obj: Object representing FW fault page. */ + struct pvr_fw_object *fault_page_obj; + + /** @gpu_util_fwcb_obj: Object representing FW GPU utilisation control structure. */ + struct pvr_fw_object *gpu_util_fwcb_obj; + + /** @runtime_cfg_obj: Object representing FW runtime config structure. */ + struct pvr_fw_object *runtime_cfg_obj; + + /** @mmucache_sync_obj: Object used as the sync parameter in an MMU cache operation. */ + struct pvr_fw_object *mmucache_sync_obj; +}; + +struct pvr_fw_device { + /** @firmware: Handle to the firmware loaded into the device. */ + const struct firmware *firmware; + + /** @header: Pointer to firmware header. */ + const struct pvr_fw_info_header *header; + + /** @layout_entries: Pointer to firmware layout. */ + const struct pvr_fw_layout_entry *layout_entries; + + /** @mem: Structure containing objects representing firmware memory allocations. */ + struct pvr_fw_mem mem; + + /** @booted: %true if the firmware has been booted, %false otherwise. */ + bool booted; + + /** + * @processor_type: FW processor type for this device. Must be one of + * %PVR_FW_PROCESSOR_TYPE_*. + */ + u16 processor_type; + + /** @funcs: Function table for the FW processor used by this device. */ + const struct pvr_fw_defs *defs; + + /** @processor_data: Pointer to data specific to FW processor. */ + union { + /** @mips_data: Pointer to MIPS-specific data. */ + struct pvr_fw_mips_data *mips_data; + } processor_data; + + /** @fw_heap_info: Firmware heap information. */ + struct { + /** @gpu_addr: Base address of firmware heap in GPU address space. */ + u64 gpu_addr; + + /** @size: Size of main area of heap. */ + u32 size; + + /** @offset_mask: Mask for offsets within FW heap. */ + u32 offset_mask; + + /** @raw_size: Raw size of heap, including reserved areas. */ + u32 raw_size; + + /** @log2_size: Log2 of raw size of heap. */ + u32 log2_size; + + /** @config_offset: Offset of config area within heap. */ + u32 config_offset; + + /** @reserved_size: Size of reserved area in heap. */ + u32 reserved_size; + } fw_heap_info; + + /** @fw_mm: Firmware address space allocator. */ + struct drm_mm fw_mm; + + /** @fw_mm_lock: Lock protecting access to &fw_mm. */ + spinlock_t fw_mm_lock; + + /** @fw_mm_base: Base address of address space managed by @fw_mm. */ + u64 fw_mm_base; + + /** + * @fwif_connection_ctl: Pointer to CPU mapping of FWIF connection + * control structure. + */ + struct rogue_fwif_connection_ctl *fwif_connection_ctl; + + /** @fwif_sysinit: Pointer to CPU mapping of FW SYSINIT structure. */ + struct rogue_fwif_sysinit *fwif_sysinit; + + /** @fwif_sysdata: Pointer to CPU mapping of FW SYSDATA structure. */ + struct rogue_fwif_sysdata *fwif_sysdata; + + /** @fwif_osinit: Pointer to CPU mapping of FW OSINIT structure. */ + struct rogue_fwif_osinit *fwif_osinit; + + /** @fwif_osdata: Pointer to CPU mapping of FW OSDATA structure. */ + struct rogue_fwif_osdata *fwif_osdata; + + /** @power_sync: Pointer to CPU mapping of power sync state. */ + u32 *power_sync; + + /** @hwrinfobuf: Pointer to CPU mapping of FW HWR info buffer. */ + struct rogue_fwif_hwrinfobuf *hwrinfobuf; + + /** @fw_trace: Device firmware trace buffer state. */ + struct pvr_fw_trace fw_trace; + + /** @fw_objs: Structure tracking FW objects. */ + struct { + /** @list: Head of FW object list. */ + struct list_head list; + + /** @lock: Lock protecting access to FW object list. */ + struct mutex lock; + } fw_objs; +}; + +#define pvr_fw_irq_read_reg(pvr_dev, name) \ + pvr_cr_read32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg) + +#define pvr_fw_irq_write_reg(pvr_dev, name, value) \ + pvr_cr_write32((pvr_dev), (pvr_dev)->fw_dev.defs->irq.name ## _reg, value) + +#define pvr_fw_irq_pending(pvr_dev) \ + (pvr_fw_irq_read_reg(pvr_dev, status) & (pvr_dev)->fw_dev.defs->irq.event_mask) + +#define pvr_fw_irq_clear(pvr_dev) \ + pvr_fw_irq_write_reg(pvr_dev, clear, (pvr_dev)->fw_dev.defs->irq.clear_mask) + +#define pvr_fw_irq_enable(pvr_dev) \ + pvr_fw_irq_write_reg(pvr_dev, enable, (pvr_dev)->fw_dev.defs->irq.event_mask) + +#define pvr_fw_irq_disable(pvr_dev) \ + pvr_fw_irq_write_reg(pvr_dev, enable, 0) + +extern const struct pvr_fw_defs pvr_fw_defs_meta; +extern const struct pvr_fw_defs pvr_fw_defs_mips; + +int pvr_fw_validate_init_device_info(struct pvr_device *pvr_dev); +int pvr_fw_init(struct pvr_device *pvr_dev); +void pvr_fw_fini(struct pvr_device *pvr_dev); + +int pvr_wait_for_fw_boot(struct pvr_device *pvr_dev); + +int +pvr_fw_hard_reset(struct pvr_device *pvr_dev); + +void pvr_fw_mts_schedule(struct pvr_device *pvr_dev, u32 val); + +void +pvr_fw_heap_info_init(struct pvr_device *pvr_dev, u32 log2_size, u32 reserved_size); + +const struct pvr_fw_layout_entry * +pvr_fw_find_layout_entry(struct pvr_device *pvr_dev, enum pvr_fw_section_id id); +int +pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw_code_ptr, + void *fw_data_ptr, void *fw_core_code_ptr, void *fw_core_data_ptr, + void **host_addr_out); + +int +pvr_fw_structure_cleanup(struct pvr_device *pvr_dev, u32 type, struct pvr_fw_object *fw_obj, + u32 offset); + +int pvr_fw_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), void *init_priv, + struct pvr_fw_object **pvr_obj_out); + +void *pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **pvr_obj_out); + +void * +pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, u32 dev_offset, size_t size, + u64 flags, void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **pvr_obj_out); + +static __always_inline void * +pvr_fw_object_vmap(struct pvr_fw_object *fw_obj) +{ + return pvr_gem_object_vmap(fw_obj->gem); +} + +static __always_inline void +pvr_fw_object_vunmap(struct pvr_fw_object *fw_obj) +{ + pvr_gem_object_vunmap(fw_obj->gem); +} + +void pvr_fw_object_destroy(struct pvr_fw_object *fw_obj); + +static __always_inline void +pvr_fw_object_unmap_and_destroy(struct pvr_fw_object *fw_obj) +{ + pvr_fw_object_vunmap(fw_obj); + pvr_fw_object_destroy(fw_obj); +} + +/** + * pvr_fw_get_dma_addr() - Get DMA address for given offset in firmware object + * @fw_obj: Pointer to object to lookup address in. + * @offset: Offset within object to lookup address at. + * @dma_addr_out: Pointer to location to store DMA address. + * + * Returns: + * * 0 on success, or + * * -%EINVAL if object is not currently backed, or if @offset is out of valid + * range for this object. + */ +static __always_inline int +pvr_fw_object_get_dma_addr(struct pvr_fw_object *fw_obj, u32 offset, dma_addr_t *dma_addr_out) +{ + return pvr_gem_get_dma_addr(fw_obj->gem, offset, dma_addr_out); +} + +void pvr_fw_object_get_fw_addr_offset(struct pvr_fw_object *fw_obj, u32 offset, u32 *fw_addr_out); + +static __always_inline void +pvr_fw_object_get_fw_addr(struct pvr_fw_object *fw_obj, u32 *fw_addr_out) +{ + pvr_fw_object_get_fw_addr_offset(fw_obj, 0, fw_addr_out); +} + +#endif /* PVR_FW_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw_info.h b/drivers/gpu/drm/imagination/pvr_fw_info.h new file mode 100644 index 000000000000..ad5d44a3067a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_info.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_INFO_H +#define PVR_FW_INFO_H + +#include <linux/bits.h> +#include <linux/sizes.h> +#include <linux/types.h> + +/* + * Firmware binary block unit in bytes. + * Raw data stored in FW binary will be aligned to this size. + */ +#define FW_BLOCK_SIZE SZ_4K + +/* Maximum number of entries in firmware layout table. */ +#define PVR_FW_INFO_MAX_NUM_ENTRIES 8 + +enum pvr_fw_section_id { + META_CODE = 0, + META_PRIVATE_DATA, + META_COREMEM_CODE, + META_COREMEM_DATA, + MIPS_CODE, + MIPS_EXCEPTIONS_CODE, + MIPS_BOOT_CODE, + MIPS_PRIVATE_DATA, + MIPS_BOOT_DATA, + MIPS_STACK, + RISCV_UNCACHED_CODE, + RISCV_CACHED_CODE, + RISCV_PRIVATE_DATA, + RISCV_COREMEM_CODE, + RISCV_COREMEM_DATA, +}; + +enum pvr_fw_section_type { + NONE = 0, + FW_CODE, + FW_DATA, + FW_COREMEM_CODE, + FW_COREMEM_DATA, +}; + +/* + * FW binary format with FW info attached: + * + * Contents Offset + * +-----------------+ + * | | 0 + * | | + * | Original binary | + * | file | + * | (.ldr/.elf) | + * | | + * | | + * +-----------------+ + * | Device info | FILE_SIZE - 4K - device_info_size + * +-----------------+ + * | FW info header | FILE_SIZE - 4K + * +-----------------+ + * | | + * | FW layout table | + * | | + * +-----------------+ + * FILE_SIZE + */ + +#define PVR_FW_INFO_VERSION 3 + +#define PVR_FW_FLAGS_OPEN_SOURCE BIT(0) + +/** struct pvr_fw_info_header - Firmware header */ +struct pvr_fw_info_header { + /** @info_version: FW info header version. */ + u32 info_version; + /** @header_len: Header length. */ + u32 header_len; + /** @layout_entry_num: Number of entries in the layout table. */ + u32 layout_entry_num; + /** @layout_entry_size: Size of an entry in the layout table. */ + u32 layout_entry_size; + /** @bvnc: GPU ID supported by firmware. */ + aligned_u64 bvnc; + /** @fw_page_size: Page size of processor on which firmware executes. */ + u32 fw_page_size; + /** @flags: Compatibility flags. */ + u32 flags; + /** @fw_version_major: Firmware major version number. */ + u16 fw_version_major; + /** @fw_version_minor: Firmware minor version number. */ + u16 fw_version_minor; + /** @fw_version_build: Firmware build number. */ + u32 fw_version_build; + /** @device_info_size: Size of device info structure. */ + u32 device_info_size; + /** @padding: Padding. */ + u32 padding; +}; + +/** + * struct pvr_fw_layout_entry - Entry in firmware layout table, describing a + * section of the firmware image + */ +struct pvr_fw_layout_entry { + /** @id: Section ID. */ + enum pvr_fw_section_id id; + /** @type: Section type. */ + enum pvr_fw_section_type type; + /** @base_addr: Base address of section in FW address space. */ + u32 base_addr; + /** @max_size: Maximum size of section, in bytes. */ + u32 max_size; + /** @alloc_size: Allocation size of section, in bytes. */ + u32 alloc_size; + /** @alloc_offset: Allocation offset of section. */ + u32 alloc_offset; +}; + +/** + * struct pvr_fw_device_info_header - Device information header. + */ +struct pvr_fw_device_info_header { + /* BRN Mask size (in u64s). */ + u64 brn_mask_size; + /* ERN Mask size (in u64s). */ + u64 ern_mask_size; + /* Feature Mask size (in u64s). */ + u64 feature_mask_size; + /* Feature Parameter size (in u64s). */ + u64 feature_param_size; +}; + +#endif /* PVR_FW_INFO_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.c b/drivers/gpu/drm/imagination/pvr_fw_meta.c new file mode 100644 index 000000000000..119934c36184 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_meta.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_fw.h" +#include "pvr_fw_info.h" +#include "pvr_gem.h" +#include "pvr_rogue_cr_defs.h" +#include "pvr_rogue_meta.h" +#include "pvr_vm.h" + +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/ktime.h> +#include <linux/types.h> + +#define ROGUE_FW_HEAP_META_SHIFT 25 /* 32 MB */ + +#define POLL_TIMEOUT_USEC 1000000 + +/** + * pvr_meta_cr_read32() - Read a META register via the Slave Port + * @pvr_dev: Device pointer. + * @reg_addr: Address of register to read. + * @reg_value_out: Pointer to location to store register value. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_cr_poll_reg32(). + */ +int +pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out) +{ + int err; + + /* Wait for Slave Port to be Ready. */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL1, + ROGUE_CR_META_SP_MSLVCTRL1_READY_EN | + ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN, + ROGUE_CR_META_SP_MSLVCTRL1_READY_EN | + ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN, + POLL_TIMEOUT_USEC); + if (err) + return err; + + /* Issue a Read. */ + pvr_cr_write32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL0, + reg_addr | ROGUE_CR_META_SP_MSLVCTRL0_RD_EN); + (void)pvr_cr_read32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL0); /* Fence write. */ + + /* Wait for Slave Port to be Ready. */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_META_SP_MSLVCTRL1, + ROGUE_CR_META_SP_MSLVCTRL1_READY_EN | + ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN, + ROGUE_CR_META_SP_MSLVCTRL1_READY_EN | + ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN, + POLL_TIMEOUT_USEC); + if (err) + return err; + + *reg_value_out = pvr_cr_read32(pvr_dev, ROGUE_CR_META_SP_MSLVDATAX); + + return 0; +} + +static int +pvr_meta_wrapper_init(struct pvr_device *pvr_dev) +{ + u64 garten_config; + + /* Configure META to Master boot. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_META_BOOT, ROGUE_CR_META_BOOT_MODE_EN); + + /* Set Garten IDLE to META idle and Set the Garten Wrapper BIF Fence address. */ + + /* Garten IDLE bit controlled by META. */ + garten_config = ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META; + + /* The fence addr is set during the fw init sequence. */ + + /* Set PC = 0 for fences. */ + garten_config &= + ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_CLRMSK; + garten_config |= + (u64)MMU_CONTEXT_MAPPING_FWPRIV + << ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_SHIFT; + + /* Set SLC DM=META. */ + garten_config |= ((u64)ROGUE_FW_SEGMMU_META_BIFDM_ID) + << ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_DM_SHIFT; + + pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG, garten_config); + + return 0; +} + +static __always_inline void +add_boot_arg(u32 **boot_conf, u32 param, u32 data) +{ + *(*boot_conf)++ = param; + *(*boot_conf)++ = data; +} + +static int +meta_ldr_cmd_loadmem(struct drm_device *drm_dev, const u8 *fw, + struct rogue_meta_ldr_l1_data_blk *l1_data, u32 coremem_size, u8 *fw_code_ptr, + u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, const u32 fw_size) +{ + struct rogue_meta_ldr_l2_data_blk *l2_block = + (struct rogue_meta_ldr_l2_data_blk *)(fw + + l1_data->cmd_data[1]); + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + u32 offset = l1_data->cmd_data[0]; + u32 data_size; + void *write_addr; + int err; + + /* Verify header is within bounds. */ + if (((u8 *)l2_block - fw) >= fw_size || ((u8 *)(l2_block + 1) - fw) >= fw_size) + return -EINVAL; + + data_size = l2_block->length - 6 /* L2 Tag length and checksum */; + + /* Verify data is within bounds. */ + if (((u8 *)l2_block->block_data - fw) >= fw_size || + ((((u8 *)l2_block->block_data) + data_size) - fw) >= fw_size) + return -EINVAL; + + if (!ROGUE_META_IS_COREMEM_CODE(offset, coremem_size) && + !ROGUE_META_IS_COREMEM_DATA(offset, coremem_size)) { + /* Global range is aliased to local range */ + offset &= ~META_MEM_GLOBAL_RANGE_BIT; + } + + err = pvr_fw_find_mmu_segment(pvr_dev, offset, data_size, fw_code_ptr, fw_data_ptr, + fw_core_code_ptr, fw_core_data_ptr, &write_addr); + if (err) { + drm_err(drm_dev, + "Addr 0x%x (size: %d) not found in any firmware segment", + offset, data_size); + return err; + } + + memcpy(write_addr, l2_block->block_data, data_size); + + return 0; +} + +static int +meta_ldr_cmd_zeromem(struct drm_device *drm_dev, + struct rogue_meta_ldr_l1_data_blk *l1_data, u32 coremem_size, + u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr) +{ + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + u32 offset = l1_data->cmd_data[0]; + u32 byte_count = l1_data->cmd_data[1]; + void *write_addr; + int err; + + if (ROGUE_META_IS_COREMEM_DATA(offset, coremem_size)) { + /* cannot zero coremem directly */ + return 0; + } + + /* Global range is aliased to local range */ + offset &= ~META_MEM_GLOBAL_RANGE_BIT; + + err = pvr_fw_find_mmu_segment(pvr_dev, offset, byte_count, fw_code_ptr, fw_data_ptr, + fw_core_code_ptr, fw_core_data_ptr, &write_addr); + if (err) { + drm_err(drm_dev, + "Addr 0x%x (size: %d) not found in any firmware segment", + offset, byte_count); + return err; + } + + memset(write_addr, 0, byte_count); + + return 0; +} + +static int +meta_ldr_cmd_config(struct drm_device *drm_dev, const u8 *fw, + struct rogue_meta_ldr_l1_data_blk *l1_data, + const u32 fw_size, u32 **boot_conf_ptr) +{ + struct rogue_meta_ldr_l2_data_blk *l2_block = + (struct rogue_meta_ldr_l2_data_blk *)(fw + + l1_data->cmd_data[0]); + struct rogue_meta_ldr_cfg_blk *config_command; + u32 l2_block_size; + u32 curr_block_size = 0; + u32 *boot_conf = boot_conf_ptr ? *boot_conf_ptr : NULL; + + /* Verify block header is within bounds. */ + if (((u8 *)l2_block - fw) >= fw_size || ((u8 *)(l2_block + 1) - fw) >= fw_size) + return -EINVAL; + + l2_block_size = l2_block->length - 6 /* L2 Tag length and checksum */; + config_command = (struct rogue_meta_ldr_cfg_blk *)l2_block->block_data; + + if (((u8 *)config_command - fw) >= fw_size || + ((((u8 *)config_command) + l2_block_size) - fw) >= fw_size) + return -EINVAL; + + while (l2_block_size >= 12) { + if (config_command->type != ROGUE_META_LDR_CFG_WRITE) + return -EINVAL; + + /* + * Only write to bootloader if we got a valid pointer to the FW + * code allocation. + */ + if (boot_conf) { + u32 register_offset = config_command->block_data[0]; + u32 register_value = config_command->block_data[1]; + + /* Do register write */ + add_boot_arg(&boot_conf, register_offset, + register_value); + } + + curr_block_size = 12; + l2_block_size -= curr_block_size; + config_command = (struct rogue_meta_ldr_cfg_blk + *)((uintptr_t)config_command + + curr_block_size); + } + + if (boot_conf_ptr) + *boot_conf_ptr = boot_conf; + + return 0; +} + +/** + * process_ldr_command_stream() - Process LDR firmware image and populate + * firmware sections + * @pvr_dev: Device pointer. + * @fw: Pointer to firmware image. + * @fw_code_ptr: Pointer to FW code section. + * @fw_data_ptr: Pointer to FW data section. + * @fw_core_code_ptr: Pointer to FW coremem code section. + * @fw_core_data_ptr: Pointer to FW coremem data section. + * @boot_conf_ptr: Pointer to boot config argument pointer. + * + * Returns : + * * 0 on success, or + * * -EINVAL on any error in LDR command stream. + */ +static int +process_ldr_command_stream(struct pvr_device *pvr_dev, const u8 *fw, u8 *fw_code_ptr, + u8 *fw_data_ptr, u8 *fw_core_code_ptr, + u8 *fw_core_data_ptr, u32 **boot_conf_ptr) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct rogue_meta_ldr_block_hdr *ldr_header = + (struct rogue_meta_ldr_block_hdr *)fw; + struct rogue_meta_ldr_l1_data_blk *l1_data = + (struct rogue_meta_ldr_l1_data_blk *)(fw + ldr_header->sl_data); + const u32 fw_size = pvr_dev->fw_dev.firmware->size; + int err; + + u32 *boot_conf = boot_conf_ptr ? *boot_conf_ptr : NULL; + u32 coremem_size; + + err = PVR_FEATURE_VALUE(pvr_dev, meta_coremem_size, &coremem_size); + if (err) + return err; + + coremem_size *= SZ_1K; + + while (l1_data) { + /* Verify block header is within bounds. */ + if (((u8 *)l1_data - fw) >= fw_size || ((u8 *)(l1_data + 1) - fw) >= fw_size) + return -EINVAL; + + if (ROGUE_META_LDR_BLK_IS_COMMENT(l1_data->cmd)) { + /* Don't process comment blocks */ + goto next_block; + } + + switch (l1_data->cmd & ROGUE_META_LDR_CMD_MASK) + case ROGUE_META_LDR_CMD_LOADMEM: { + err = meta_ldr_cmd_loadmem(drm_dev, fw, l1_data, + coremem_size, + fw_code_ptr, fw_data_ptr, + fw_core_code_ptr, + fw_core_data_ptr, fw_size); + if (err) + return err; + break; + + case ROGUE_META_LDR_CMD_START_THREADS: + /* Don't process this block */ + break; + + case ROGUE_META_LDR_CMD_ZEROMEM: + err = meta_ldr_cmd_zeromem(drm_dev, l1_data, + coremem_size, + fw_code_ptr, fw_data_ptr, + fw_core_code_ptr, + fw_core_data_ptr); + if (err) + return err; + break; + + case ROGUE_META_LDR_CMD_CONFIG: + err = meta_ldr_cmd_config(drm_dev, fw, l1_data, fw_size, + &boot_conf); + if (err) + return err; + break; + + default: + return -EINVAL; + } + +next_block: + if (l1_data->next == 0xFFFFFFFF) + break; + + l1_data = (struct rogue_meta_ldr_l1_data_blk *)(fw + + l1_data->next); + } + + if (boot_conf_ptr) + *boot_conf_ptr = boot_conf; + + return 0; +} + +static void +configure_seg_id(u64 seg_out_addr, u32 seg_base, u32 seg_limit, u32 seg_id, + u32 **boot_conf_ptr) +{ + u32 seg_out_addr0 = seg_out_addr & 0x00000000FFFFFFFFUL; + u32 seg_out_addr1 = (seg_out_addr >> 32) & 0x00000000FFFFFFFFUL; + u32 *boot_conf = *boot_conf_ptr; + + /* META segments have a minimum size. */ + u32 limit_off = max(seg_limit, ROGUE_FW_SEGMMU_ALIGN); + + /* The limit is an offset, therefore off = size - 1. */ + limit_off -= 1; + + seg_base |= ROGUE_FW_SEGMMU_ALLTHRS_WRITEABLE; + + add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_BASE(seg_id), seg_base); + add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_LIMIT(seg_id), limit_off); + add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_OUTA0(seg_id), seg_out_addr0); + add_boot_arg(&boot_conf, META_CR_MMCU_SEGMENT_N_OUTA1(seg_id), seg_out_addr1); + + *boot_conf_ptr = boot_conf; +} + +static u64 get_fw_obj_gpu_addr(struct pvr_fw_object *fw_obj) +{ + struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(fw_obj->gem)->dev); + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + + return fw_obj->fw_addr_offset + fw_dev->fw_heap_info.gpu_addr; +} + +static void +configure_seg_mmu(struct pvr_device *pvr_dev, u32 **boot_conf_ptr) +{ + const struct pvr_fw_layout_entry *layout_entries = pvr_dev->fw_dev.layout_entries; + u32 num_layout_entries = pvr_dev->fw_dev.header->layout_entry_num; + u64 seg_out_addr_top; + u32 i; + + seg_out_addr_top = + ROGUE_FW_SEGMMU_OUTADDR_TOP_SLC(MMU_CONTEXT_MAPPING_FWPRIV, + ROGUE_FW_SEGMMU_META_BIFDM_ID); + + for (i = 0; i < num_layout_entries; i++) { + /* + * FW code is using the bootloader segment which is already + * configured on boot. FW coremem code and data don't use the + * segment MMU. Only the FW data segment needs to be configured. + */ + if (layout_entries[i].type == FW_DATA) { + u32 seg_id = ROGUE_FW_SEGMMU_DATA_ID; + u64 seg_out_addr = get_fw_obj_gpu_addr(pvr_dev->fw_dev.mem.data_obj); + + seg_out_addr += layout_entries[i].alloc_offset; + seg_out_addr |= seg_out_addr_top; + + /* Write the sequence to the bootldr. */ + configure_seg_id(seg_out_addr, + layout_entries[i].base_addr, + layout_entries[i].alloc_size, seg_id, + boot_conf_ptr); + + break; + } + } +} + +static void +configure_meta_caches(u32 **boot_conf_ptr) +{ + u32 *boot_conf = *boot_conf_ptr; + u32 d_cache_t0, i_cache_t0; + u32 d_cache_t1, i_cache_t1; + u32 d_cache_t2, i_cache_t2; + u32 d_cache_t3, i_cache_t3; + + /* Initialise I/Dcache settings */ + d_cache_t0 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; + d_cache_t1 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; + d_cache_t2 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; + d_cache_t3 = META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE; + i_cache_t0 = 0; + i_cache_t1 = 0; + i_cache_t2 = 0; + i_cache_t3 = 0; + + d_cache_t0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; + i_cache_t0 |= META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE; + + /* Local region MMU enhanced bypass: WIN-3 mode for code and data caches */ + add_boot_arg(&boot_conf, META_CR_MMCU_LOCAL_EBCTRL, + META_CR_MMCU_LOCAL_EBCTRL_ICWIN | + META_CR_MMCU_LOCAL_EBCTRL_DCWIN); + + /* Data cache partitioning thread 0 to 3 */ + add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(0), d_cache_t0); + add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(1), d_cache_t1); + add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(2), d_cache_t2); + add_boot_arg(&boot_conf, META_CR_SYSC_DCPART(3), d_cache_t3); + + /* Enable data cache hits */ + add_boot_arg(&boot_conf, META_CR_MMCU_DCACHE_CTRL, + META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN); + + /* Instruction cache partitioning thread 0 to 3 */ + add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(0), i_cache_t0); + add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(1), i_cache_t1); + add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(2), i_cache_t2); + add_boot_arg(&boot_conf, META_CR_SYSC_ICPART(3), i_cache_t3); + + /* Enable instruction cache hits */ + add_boot_arg(&boot_conf, META_CR_MMCU_ICACHE_CTRL, + META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN); + + add_boot_arg(&boot_conf, 0x040000C0, 0); + + *boot_conf_ptr = boot_conf; +} + +static int +pvr_meta_fw_process(struct pvr_device *pvr_dev, const u8 *fw, + u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, + u32 core_code_alloc_size) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + u32 *boot_conf; + int err; + + boot_conf = ((u32 *)fw_code_ptr) + ROGUE_FW_BOOTLDR_CONF_OFFSET; + + /* Slave port and JTAG accesses are privileged. */ + add_boot_arg(&boot_conf, META_CR_SYSC_JTAG_THREAD, + META_CR_SYSC_JTAG_THREAD_PRIV_EN); + + configure_seg_mmu(pvr_dev, &boot_conf); + + /* Populate FW sections from LDR image. */ + err = process_ldr_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, fw_core_code_ptr, + fw_core_data_ptr, &boot_conf); + if (err) + return err; + + configure_meta_caches(&boot_conf); + + /* End argument list. */ + add_boot_arg(&boot_conf, 0, 0); + + if (fw_dev->mem.core_code_obj) { + u32 core_code_fw_addr; + + pvr_fw_object_get_fw_addr(fw_dev->mem.core_code_obj, &core_code_fw_addr); + add_boot_arg(&boot_conf, core_code_fw_addr, core_code_alloc_size); + } else { + add_boot_arg(&boot_conf, 0, 0); + } + /* None of the cores supported by this driver have META DMA. */ + add_boot_arg(&boot_conf, 0, 0); + + return 0; +} + +static int +pvr_meta_init(struct pvr_device *pvr_dev) +{ + pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_META_SHIFT, 0); + + return 0; +} + +static u32 +pvr_meta_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset) +{ + u32 fw_addr = fw_obj->fw_addr_offset + offset + ROGUE_FW_SEGMMU_DATA_BASE_ADDRESS; + + /* META cacheability is determined by address. */ + if (fw_obj->gem->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED) + fw_addr |= ROGUE_FW_SEGMMU_DATA_META_UNCACHED | + ROGUE_FW_SEGMMU_DATA_VIVT_SLC_UNCACHED; + + return fw_addr; +} + +static int +pvr_meta_vm_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) +{ + struct pvr_gem_object *pvr_obj = fw_obj->gem; + + return pvr_vm_map(pvr_dev->kernel_vm_ctx, pvr_obj, 0, fw_obj->fw_mm_node.start, + pvr_gem_object_size(pvr_obj)); +} + +static void +pvr_meta_vm_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) +{ + pvr_vm_unmap(pvr_dev->kernel_vm_ctx, fw_obj->fw_mm_node.start, + fw_obj->fw_mm_node.size); +} + +static bool +pvr_meta_has_fixed_data_addr(void) +{ + return false; +} + +const struct pvr_fw_defs pvr_fw_defs_meta = { + .init = pvr_meta_init, + .fw_process = pvr_meta_fw_process, + .vm_map = pvr_meta_vm_map, + .vm_unmap = pvr_meta_vm_unmap, + .get_fw_addr_with_offset = pvr_meta_get_fw_addr_with_offset, + .wrapper_init = pvr_meta_wrapper_init, + .has_fixed_data_addr = pvr_meta_has_fixed_data_addr, + .irq = { + .enable_reg = ROGUE_CR_META_SP_MSLVIRQENABLE, + .status_reg = ROGUE_CR_META_SP_MSLVIRQSTATUS, + .clear_reg = ROGUE_CR_META_SP_MSLVIRQSTATUS, + .event_mask = ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_EN, + .clear_mask = ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_CLRMSK, + }, +}; diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.h b/drivers/gpu/drm/imagination/pvr_fw_meta.h new file mode 100644 index 000000000000..911ad700cba6 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_meta.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_META_H +#define PVR_FW_META_H + +#include <linux/types.h> + +/* Forward declaration from pvr_device.h */ +struct pvr_device; + +int pvr_meta_cr_read32(struct pvr_device *pvr_dev, u32 reg_addr, u32 *reg_value_out); + +#endif /* PVR_FW_META_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw_mips.c b/drivers/gpu/drm/imagination/pvr_fw_mips.c new file mode 100644 index 000000000000..0bed0257e2ab --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_mips.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_fw.h" +#include "pvr_fw_mips.h" +#include "pvr_gem.h" +#include "pvr_rogue_mips.h" +#include "pvr_vm_mips.h" + +#include <linux/elf.h> +#include <linux/err.h> +#include <linux/types.h> + +#define ROGUE_FW_HEAP_MIPS_BASE 0xC0000000 +#define ROGUE_FW_HEAP_MIPS_SHIFT 24 /* 16 MB */ +#define ROGUE_FW_HEAP_MIPS_RESERVED_SIZE SZ_1M + +/** + * process_elf_command_stream() - Process ELF firmware image and populate + * firmware sections + * @pvr_dev: Device pointer. + * @fw: Pointer to firmware image. + * @fw_code_ptr: Pointer to FW code section. + * @fw_data_ptr: Pointer to FW data section. + * @fw_core_code_ptr: Pointer to FW coremem code section. + * @fw_core_data_ptr: Pointer to FW coremem data section. + * + * Returns : + * * 0 on success, or + * * -EINVAL on any error in ELF command stream. + */ +static int +process_elf_command_stream(struct pvr_device *pvr_dev, const u8 *fw, u8 *fw_code_ptr, + u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr) +{ + struct elf32_hdr *header = (struct elf32_hdr *)fw; + struct elf32_phdr *program_header = (struct elf32_phdr *)(fw + header->e_phoff); + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + u32 entry; + int err; + + for (entry = 0; entry < header->e_phnum; entry++, program_header++) { + void *write_addr; + + /* Only consider loadable entries in the ELF segment table */ + if (program_header->p_type != PT_LOAD) + continue; + + err = pvr_fw_find_mmu_segment(pvr_dev, program_header->p_vaddr, + program_header->p_memsz, fw_code_ptr, fw_data_ptr, + fw_core_code_ptr, fw_core_data_ptr, &write_addr); + if (err) { + drm_err(drm_dev, + "Addr 0x%x (size: %d) not found in any firmware segment", + program_header->p_vaddr, program_header->p_memsz); + return err; + } + + /* Write to FW allocation only if available */ + if (write_addr) { + memcpy(write_addr, fw + program_header->p_offset, + program_header->p_filesz); + + memset((u8 *)write_addr + program_header->p_filesz, 0, + program_header->p_memsz - program_header->p_filesz); + } + } + + return 0; +} + +static int +pvr_mips_init(struct pvr_device *pvr_dev) +{ + pvr_fw_heap_info_init(pvr_dev, ROGUE_FW_HEAP_MIPS_SHIFT, ROGUE_FW_HEAP_MIPS_RESERVED_SIZE); + + return pvr_vm_mips_init(pvr_dev); +} + +static void +pvr_mips_fini(struct pvr_device *pvr_dev) +{ + pvr_vm_mips_fini(pvr_dev); +} + +static int +pvr_mips_fw_process(struct pvr_device *pvr_dev, const u8 *fw, + u8 *fw_code_ptr, u8 *fw_data_ptr, u8 *fw_core_code_ptr, u8 *fw_core_data_ptr, + u32 core_code_alloc_size) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data; + const struct pvr_fw_layout_entry *boot_code_entry; + const struct pvr_fw_layout_entry *boot_data_entry; + const struct pvr_fw_layout_entry *exception_code_entry; + const struct pvr_fw_layout_entry *stack_entry; + struct rogue_mipsfw_boot_data *boot_data; + dma_addr_t dma_addr; + u32 page_nr; + int err; + + err = process_elf_command_stream(pvr_dev, fw, fw_code_ptr, fw_data_ptr, fw_core_code_ptr, + fw_core_data_ptr); + if (err) + return err; + + boot_code_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_BOOT_CODE); + boot_data_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_BOOT_DATA); + exception_code_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_EXCEPTIONS_CODE); + if (!boot_code_entry || !boot_data_entry || !exception_code_entry) + return -EINVAL; + + WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.code_obj->gem, boot_code_entry->alloc_offset, + &mips_data->boot_code_dma_addr)); + WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.data_obj->gem, boot_data_entry->alloc_offset, + &mips_data->boot_data_dma_addr)); + WARN_ON(pvr_gem_get_dma_addr(fw_dev->mem.code_obj->gem, + exception_code_entry->alloc_offset, + &mips_data->exception_code_dma_addr)); + + stack_entry = pvr_fw_find_layout_entry(pvr_dev, MIPS_STACK); + if (!stack_entry) + return -EINVAL; + + boot_data = (struct rogue_mipsfw_boot_data *)(fw_data_ptr + boot_data_entry->alloc_offset + + ROGUE_MIPSFW_BOOTLDR_CONF_OFFSET); + + WARN_ON(pvr_fw_object_get_dma_addr(fw_dev->mem.data_obj, stack_entry->alloc_offset, + &dma_addr)); + boot_data->stack_phys_addr = dma_addr; + + boot_data->reg_base = pvr_dev->regs_resource->start; + + for (page_nr = 0; page_nr < ARRAY_SIZE(boot_data->pt_phys_addr); page_nr++) { + /* Firmware expects 4k pages, but host page size might be different. */ + u32 src_page_nr = (page_nr * ROGUE_MIPSFW_PAGE_SIZE_4K) >> PAGE_SHIFT; + u32 page_offset = (page_nr * ROGUE_MIPSFW_PAGE_SIZE_4K) & ~PAGE_MASK; + + boot_data->pt_phys_addr[page_nr] = mips_data->pt_dma_addr[src_page_nr] + + page_offset; + } + + boot_data->pt_log2_page_size = ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K; + boot_data->pt_num_pages = ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES; + boot_data->reserved1 = 0; + boot_data->reserved2 = 0; + + return 0; +} + +static int +pvr_mips_wrapper_init(struct pvr_device *pvr_dev) +{ + struct pvr_fw_mips_data *mips_data = pvr_dev->fw_dev.processor_data.mips_data; + const u64 remap_settings = ROGUE_MIPSFW_BOOT_REMAP_LOG2_SEGMENT_SIZE; + u32 phys_bus_width; + + int err = PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width); + + if (WARN_ON(err)) + return err; + + /* Currently MIPS FW only supported with physical bus width > 32 bits. */ + if (WARN_ON(phys_bus_width <= 32)) + return -EINVAL; + + pvr_cr_write32(pvr_dev, ROGUE_CR_MIPS_WRAPPER_CONFIG, + (ROGUE_MIPSFW_REGISTERS_VIRTUAL_BASE >> + ROGUE_MIPSFW_WRAPPER_CONFIG_REGBANK_ADDR_ALIGN) | + ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_MICROMIPS); + + /* Configure remap for boot code, boot data and exceptions code areas. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1, + ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN | + ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MODE_ENABLE_EN); + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2, + (mips_data->boot_code_dma_addr & + ~ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings); + + if (PVR_HAS_QUIRK(pvr_dev, 63553)) { + /* + * WA always required on 36 bit cores, to avoid continuous unmapped memory accesses + * to address 0x0. + */ + WARN_ON(phys_bus_width != 36); + + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1, + ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MODE_ENABLE_EN); + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2, + (mips_data->boot_code_dma_addr & + ~ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_ADDR_OUT_CLRMSK) | + remap_settings); + } + + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1, + ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN | + ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MODE_ENABLE_EN); + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2, + (mips_data->boot_data_dma_addr & + ~ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings); + + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1, + ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN | + ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MODE_ENABLE_EN); + pvr_cr_write64(pvr_dev, ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2, + (mips_data->exception_code_dma_addr & + ~ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_ADDR_OUT_CLRMSK) | remap_settings); + + /* Garten IDLE bit controlled by MIPS. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG, + ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META); + + /* Turn on the EJTAG probe. */ + pvr_cr_write32(pvr_dev, ROGUE_CR_MIPS_DEBUG_CONFIG, 0); + + return 0; +} + +static u32 +pvr_mips_get_fw_addr_with_offset(struct pvr_fw_object *fw_obj, u32 offset) +{ + struct pvr_device *pvr_dev = to_pvr_device(gem_from_pvr_gem(fw_obj->gem)->dev); + + /* MIPS cacheability is determined by page table. */ + return ((fw_obj->fw_addr_offset + offset) & pvr_dev->fw_dev.fw_heap_info.offset_mask) | + ROGUE_FW_HEAP_MIPS_BASE; +} + +static bool +pvr_mips_has_fixed_data_addr(void) +{ + return true; +} + +const struct pvr_fw_defs pvr_fw_defs_mips = { + .init = pvr_mips_init, + .fini = pvr_mips_fini, + .fw_process = pvr_mips_fw_process, + .vm_map = pvr_vm_mips_map, + .vm_unmap = pvr_vm_mips_unmap, + .get_fw_addr_with_offset = pvr_mips_get_fw_addr_with_offset, + .wrapper_init = pvr_mips_wrapper_init, + .has_fixed_data_addr = pvr_mips_has_fixed_data_addr, + .irq = { + .enable_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE, + .status_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS, + .clear_reg = ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR, + .event_mask = ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_EVENT_EN, + .clear_mask = ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_EVENT_EN, + }, +}; diff --git a/drivers/gpu/drm/imagination/pvr_fw_mips.h b/drivers/gpu/drm/imagination/pvr_fw_mips.h new file mode 100644 index 000000000000..408dbe63a90c --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_mips.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_MIPS_H +#define PVR_FW_MIPS_H + +#include "pvr_rogue_mips.h" + +#include <asm/page.h> +#include <linux/types.h> + +/* Forward declaration from pvr_gem.h. */ +struct pvr_gem_object; + +#define PVR_MIPS_PT_PAGE_COUNT ((ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * ROGUE_MIPSFW_PAGE_SIZE_4K) \ + >> PAGE_SHIFT) +/** + * struct pvr_fw_mips_data - MIPS-specific data + */ +struct pvr_fw_mips_data { + /** + * @pt_pages: Pages containing MIPS pagetable. + */ + struct page *pt_pages[PVR_MIPS_PT_PAGE_COUNT]; + + /** @pt: Pointer to CPU mapping of MIPS pagetable. */ + u32 *pt; + + /** @pt_dma_addr: DMA mappings of MIPS pagetable. */ + dma_addr_t pt_dma_addr[PVR_MIPS_PT_PAGE_COUNT]; + + /** @boot_code_dma_addr: DMA address of MIPS boot code. */ + dma_addr_t boot_code_dma_addr; + + /** @boot_data_dma_addr: DMA address of MIPS boot data. */ + dma_addr_t boot_data_dma_addr; + + /** @exception_code_dma_addr: DMA address of MIPS exception code. */ + dma_addr_t exception_code_dma_addr; + + /** @cache_policy: Cache policy for this processor. */ + u32 cache_policy; + + /** @pfn_mask: PFN mask for MIPS pagetable. */ + u32 pfn_mask; +}; + +#endif /* PVR_FW_MIPS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw_startstop.c b/drivers/gpu/drm/imagination/pvr_fw_startstop.c new file mode 100644 index 000000000000..36cec227cfe3 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_startstop.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_fw.h" +#include "pvr_fw_meta.h" +#include "pvr_fw_startstop.h" +#include "pvr_rogue_cr_defs.h" +#include "pvr_rogue_meta.h" +#include "pvr_vm.h" + +#include <linux/compiler.h> +#include <linux/delay.h> +#include <linux/ktime.h> +#include <linux/types.h> + +#define POLL_TIMEOUT_USEC 1000000 + +static void +rogue_axi_ace_list_init(struct pvr_device *pvr_dev) +{ + /* Setup AXI-ACE config. Set everything to outer cache. */ + u64 reg_val = + (3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_SHIFT) | + (3U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_SHIFT) | + (2U << ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_SHIFT); + + pvr_cr_write64(pvr_dev, ROGUE_CR_AXI_ACE_LITE_CONFIGURATION, reg_val); +} + +static void +rogue_bif_init(struct pvr_device *pvr_dev) +{ + dma_addr_t pc_dma_addr; + u64 pc_addr; + + /* Acquire the address of the Kernel Page Catalogue. */ + pc_dma_addr = pvr_vm_get_page_table_root_addr(pvr_dev->kernel_vm_ctx); + + /* Write the kernel catalogue base. */ + pc_addr = ((((u64)pc_dma_addr >> ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSHIFT) + << ROGUE_CR_BIF_CAT_BASE0_ADDR_SHIFT) & + ~ROGUE_CR_BIF_CAT_BASE0_ADDR_CLRMSK); + + pvr_cr_write64(pvr_dev, BIF_CAT_BASEX(MMU_CONTEXT_MAPPING_FWPRIV), + pc_addr); +} + +static int +rogue_slc_init(struct pvr_device *pvr_dev) +{ + u16 slc_cache_line_size_bits; + u32 reg_val; + int err; + + /* + * SLC Misc control. + * + * Note: This is a 64bit register and we set only the lower 32bits + * leaving the top 32bits (ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS) + * unchanged from the HW default. + */ + reg_val = (pvr_cr_read32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC) & + ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_EN) | + ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH1; + + err = PVR_FEATURE_VALUE(pvr_dev, slc_cache_line_size_bits, &slc_cache_line_size_bits); + if (err) + return err; + + /* Bypass burst combiner if SLC line size is smaller than 1024 bits. */ + if (slc_cache_line_size_bits < 1024) + reg_val |= ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_EN; + + if (PVR_HAS_QUIRK(pvr_dev, 71242) && !PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support)) + reg_val |= ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_EN; + + pvr_cr_write32(pvr_dev, ROGUE_CR_SLC_CTRL_MISC, reg_val); + + return 0; +} + +/** + * pvr_fw_start() - Start FW processor and boot firmware + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, or + * * Any error returned by rogue_slc_init(). + */ +int +pvr_fw_start(struct pvr_device *pvr_dev) +{ + bool has_reset2 = PVR_HAS_FEATURE(pvr_dev, xe_tpu2); + u64 soft_reset_mask; + int err; + + if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe)) + soft_reset_mask = ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL; + else + soft_reset_mask = ROGUE_CR_SOFT_RESET_MASKFULL; + + if (PVR_HAS_FEATURE(pvr_dev, sys_bus_secure_reset)) { + /* + * Disable the default sys_bus_secure protection to perform + * minimal setup. + */ + pvr_cr_write32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE, 0); + (void)pvr_cr_read32(pvr_dev, ROGUE_CR_SYS_BUS_SECURE); /* Fence write */ + } + + /* Set Rogue in soft-reset. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask); + if (has_reset2) + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, ROGUE_CR_SOFT_RESET2_MASKFULL); + + /* Read soft-reset to fence previous write in order to clear the SOCIF pipeline. */ + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET); + if (has_reset2) + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2); + + /* Take Rascal and Dust out of reset. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, + soft_reset_mask ^ ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN); + if (has_reset2) + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0); + + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET); + if (has_reset2) + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2); + + /* Take everything out of reset but the FW processor. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_GARTEN_EN); + if (has_reset2) + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET2, 0); + + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET); + if (has_reset2) + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET2); + + err = rogue_slc_init(pvr_dev); + if (err) + goto err_reset; + + /* Initialise Firmware wrapper. */ + pvr_dev->fw_dev.defs->wrapper_init(pvr_dev); + + /* We must init the AXI-ACE interface before first BIF transaction. */ + rogue_axi_ace_list_init(pvr_dev); + + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { + /* Initialise BIF. */ + rogue_bif_init(pvr_dev); + } + + /* Need to wait for at least 16 cycles before taking the FW processor out of reset ... */ + udelay(3); + + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, 0x0); + (void)pvr_cr_read64(pvr_dev, ROGUE_CR_SOFT_RESET); + + /* ... and afterwards. */ + udelay(3); + + return 0; + +err_reset: + /* Put everything back into soft-reset. */ + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, soft_reset_mask); + + return err; +} + +/** + * pvr_fw_stop() - Stop FW processor + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_cr_poll_reg32(). + */ +int +pvr_fw_stop(struct pvr_device *pvr_dev) +{ + const u32 sidekick_idle_mask = ROGUE_CR_SIDEKICK_IDLE_MASKFULL & + ~(ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN | + ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN | + ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN); + bool skip_garten_idle = false; + u32 reg_value; + int err; + + /* + * Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper. + * For cores with the LAYOUT_MARS feature, SIDEKICK would have been + * powered down by the FW. + */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask, + sidekick_idle_mask, POLL_TIMEOUT_USEC); + if (err) + return err; + + /* Unset MTS DM association with threads. */ + pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC, + ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_MASKFULL & + ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK); + pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC, + ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL & + ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK); + pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC, + ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL & + ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK); + pvr_cr_write32(pvr_dev, ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC, + ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_MASKFULL & + ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK); + + /* Extra Idle checks. */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_STATUS_MMU, 0, + ROGUE_CR_BIF_STATUS_MMU_MASKFULL, + POLL_TIMEOUT_USEC); + if (err) + return err; + + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_STATUS_MMU, 0, + ROGUE_CR_BIFPM_STATUS_MMU_MASKFULL, + POLL_TIMEOUT_USEC); + if (err) + return err; + + if (!PVR_HAS_FEATURE(pvr_dev, xt_top_infrastructure)) { + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIF_READS_EXT_STATUS, 0, + ROGUE_CR_BIF_READS_EXT_STATUS_MASKFULL, + POLL_TIMEOUT_USEC); + if (err) + return err; + } + + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_BIFPM_READS_EXT_STATUS, 0, + ROGUE_CR_BIFPM_READS_EXT_STATUS_MASKFULL, + POLL_TIMEOUT_USEC); + if (err) + return err; + + err = pvr_cr_poll_reg64(pvr_dev, ROGUE_CR_SLC_STATUS1, 0, + ROGUE_CR_SLC_STATUS1_MASKFULL, + POLL_TIMEOUT_USEC); + if (err) + return err; + + /* + * Wait for SLC to signal IDLE. + * For cores with the LAYOUT_MARS feature, SLC would have been powered + * down by the FW. + */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SLC_IDLE, + ROGUE_CR_SLC_IDLE_MASKFULL, + ROGUE_CR_SLC_IDLE_MASKFULL, POLL_TIMEOUT_USEC); + if (err) + return err; + + /* + * Wait for Sidekick/Jones to signal IDLE except for the Garten Wrapper. + * For cores with the LAYOUT_MARS feature, SIDEKICK would have been powered + * down by the FW. + */ + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, sidekick_idle_mask, + sidekick_idle_mask, POLL_TIMEOUT_USEC); + if (err) + return err; + + if (pvr_dev->fw_dev.processor_type == PVR_FW_PROCESSOR_TYPE_META) { + err = pvr_meta_cr_read32(pvr_dev, META_CR_TxVECINT_BHALT, ®_value); + if (err) + return err; + + /* + * Wait for Sidekick/Jones to signal IDLE including the Garten + * Wrapper if there is no debugger attached (TxVECINT_BHALT = + * 0x0). + */ + if (reg_value) + skip_garten_idle = true; + } + + if (!skip_garten_idle) { + err = pvr_cr_poll_reg32(pvr_dev, ROGUE_CR_SIDEKICK_IDLE, + ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN, + ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN, + POLL_TIMEOUT_USEC); + if (err) + return err; + } + + if (PVR_HAS_FEATURE(pvr_dev, pbe2_in_xe)) + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, + ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL); + else + pvr_cr_write64(pvr_dev, ROGUE_CR_SOFT_RESET, ROGUE_CR_SOFT_RESET_MASKFULL); + + return 0; +} diff --git a/drivers/gpu/drm/imagination/pvr_fw_startstop.h b/drivers/gpu/drm/imagination/pvr_fw_startstop.h new file mode 100644 index 000000000000..a3cef061bd60 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_startstop.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_STARTSTOP_H +#define PVR_FW_STARTSTOP_H + +/* Forward declaration from pvr_device.h. */ +struct pvr_device; + +int pvr_fw_start(struct pvr_device *pvr_dev); +int pvr_fw_stop(struct pvr_device *pvr_dev); + +#endif /* PVR_FW_STARTSTOP_H */ diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c new file mode 100644 index 000000000000..87a42fb6ace6 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_gem.h" +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_fwif_sf.h" +#include "pvr_fw_trace.h" + +#include <drm/drm_drv.h> +#include <drm/drm_file.h> + +#include <linux/build_bug.h> +#include <linux/dcache.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +static void +tracebuf_ctrl_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_tracebuf *tracebuf_ctrl = cpu_ptr; + struct pvr_fw_trace *fw_trace = priv; + u32 thread_nr; + + tracebuf_ctrl->tracebuf_size_in_dwords = ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS; + tracebuf_ctrl->tracebuf_flags = 0; + + if (fw_trace->group_mask) + tracebuf_ctrl->log_type = fw_trace->group_mask | ROGUE_FWIF_LOG_TYPE_TRACE; + else + tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE; + + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) { + struct rogue_fwif_tracebuf_space *tracebuf_space = + &tracebuf_ctrl->tracebuf[thread_nr]; + struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr]; + + pvr_fw_object_get_fw_addr(trace_buffer->buf_obj, + &tracebuf_space->trace_buffer_fw_addr); + + tracebuf_space->trace_buffer = trace_buffer->buf; + tracebuf_space->trace_pointer = 0; + } +} + +int pvr_fw_trace_init(struct pvr_device *pvr_dev) +{ + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + u32 thread_nr; + int err; + + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) { + struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr]; + + trace_buffer->buf = + pvr_fw_object_create_and_map(pvr_dev, + ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS * + sizeof(*trace_buffer->buf), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED | + PVR_BO_FW_NO_CLEAR_ON_RESET, + NULL, NULL, &trace_buffer->buf_obj); + if (IS_ERR(trace_buffer->buf)) { + drm_err(drm_dev, "Unable to allocate trace buffer\n"); + err = PTR_ERR(trace_buffer->buf); + trace_buffer->buf = NULL; + goto err_free_buf; + } + } + + /* TODO: Provide control of group mask. */ + fw_trace->group_mask = 0; + + fw_trace->tracebuf_ctrl = + pvr_fw_object_create_and_map(pvr_dev, + sizeof(*fw_trace->tracebuf_ctrl), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED | + PVR_BO_FW_NO_CLEAR_ON_RESET, + tracebuf_ctrl_init, fw_trace, + &fw_trace->tracebuf_ctrl_obj); + if (IS_ERR(fw_trace->tracebuf_ctrl)) { + drm_err(drm_dev, "Unable to allocate trace buffer control structure\n"); + err = PTR_ERR(fw_trace->tracebuf_ctrl); + goto err_free_buf; + } + + BUILD_BUG_ON(ARRAY_SIZE(fw_trace->tracebuf_ctrl->tracebuf) != + ARRAY_SIZE(fw_trace->buffers)); + + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) { + struct rogue_fwif_tracebuf_space *tracebuf_space = + &fw_trace->tracebuf_ctrl->tracebuf[thread_nr]; + struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr]; + + trace_buffer->tracebuf_space = tracebuf_space; + } + + return 0; + +err_free_buf: + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) { + struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr]; + + if (trace_buffer->buf) + pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj); + } + + return err; +} + +void pvr_fw_trace_fini(struct pvr_device *pvr_dev) +{ + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; + u32 thread_nr; + + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) { + struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr]; + + pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj); + } + pvr_fw_object_unmap_and_destroy(fw_trace->tracebuf_ctrl_obj); +} + +/** + * update_logtype() - Send KCCB command to trigger FW to update logtype + * @pvr_dev: Target PowerVR device + * @group_mask: New log group mask. + * + * Returns: + * * 0 on success, + * * Any error returned by pvr_kccb_send_cmd(), or + * * -%EIO if the device is lost. + */ +static int +update_logtype(struct pvr_device *pvr_dev, u32 group_mask) +{ + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; + struct rogue_fwif_kccb_cmd cmd; + int idx; + int err; + + if (group_mask) + fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask; + else + fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE; + + fw_trace->group_mask = group_mask; + + down_read(&pvr_dev->reset_sem); + if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) { + err = -EIO; + goto err_up_read; + } + + cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE; + cmd.kccb_flags = 0; + + err = pvr_kccb_send_cmd(pvr_dev, &cmd, NULL); + + drm_dev_exit(idx); + +err_up_read: + up_read(&pvr_dev->reset_sem); + + return err; +} + +#if defined(CONFIG_DEBUG_FS) + +static int fw_trace_group_mask_show(struct seq_file *m, void *data) +{ + struct pvr_device *pvr_dev = m->private; + + seq_printf(m, "%08x\n", pvr_dev->fw_dev.fw_trace.group_mask); + + return 0; +} + +static int fw_trace_group_mask_open(struct inode *inode, struct file *file) +{ + return single_open(file, fw_trace_group_mask_show, inode->i_private); +} + +static ssize_t fw_trace_group_mask_write(struct file *file, const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct pvr_device *pvr_dev = m->private; + u32 new_group_mask; + int err; + + err = kstrtouint_from_user(ubuf, len, 0, &new_group_mask); + if (err) + return err; + + err = update_logtype(pvr_dev, new_group_mask); + if (err) + return err; + + pvr_dev->fw_dev.fw_trace.group_mask = new_group_mask; + + return (ssize_t)len; +} + +static const struct file_operations pvr_fw_trace_group_mask_fops = { + .owner = THIS_MODULE, + .open = fw_trace_group_mask_open, + .read = seq_read, + .write = fw_trace_group_mask_write, + .llseek = default_llseek, + .release = single_release, +}; + +struct pvr_fw_trace_seq_data { + /** @buffer: Pointer to copy of trace data. */ + u32 *buffer; + + /** @start_offset: Starting offset in trace data, as reported by FW. */ + u32 start_offset; + + /** @idx: Current index into trace data. */ + u32 idx; + + /** @assert_buf: Trace assert buffer, as reported by FW. */ + struct rogue_fwif_file_info_buf assert_buf; +}; + +static u32 find_sfid(u32 id) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(stid_fmts); i++) { + if (stid_fmts[i].id == id) + return i; + } + + return ROGUE_FW_SF_LAST; +} + +static u32 read_fw_trace(struct pvr_fw_trace_seq_data *trace_seq_data, u32 offset) +{ + u32 idx; + + idx = trace_seq_data->idx + offset; + if (idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) + return 0; + + idx = (idx + trace_seq_data->start_offset) % ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS; + return trace_seq_data->buffer[idx]; +} + +/** + * fw_trace_get_next() - Advance trace index to next entry + * @trace_seq_data: Trace sequence data. + * + * Returns: + * * %true if trace index is now pointing to a valid entry, or + * * %false if trace index is pointing to an invalid entry, or has hit the end + * of the trace. + */ +static bool fw_trace_get_next(struct pvr_fw_trace_seq_data *trace_seq_data) +{ + u32 id, sf_id; + + while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) { + id = read_fw_trace(trace_seq_data, 0); + trace_seq_data->idx++; + if (!ROGUE_FW_LOG_VALIDID(id)) + continue; + if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) { + /* Assertion failure marks the end of the trace. */ + return false; + } + + sf_id = find_sfid(id); + if (sf_id == ROGUE_FW_SF_FIRST) + continue; + if (sf_id == ROGUE_FW_SF_LAST) { + /* + * Could not match with an ID in the SF table, trace is + * most likely corrupt from this point. + */ + return false; + } + + /* Skip over the timestamp, and any parameters. */ + trace_seq_data->idx += 2 + ROGUE_FW_SF_PARAMNUM(id); + + /* Ensure index is now pointing to a valid trace entry. */ + id = read_fw_trace(trace_seq_data, 0); + if (!ROGUE_FW_LOG_VALIDID(id)) + continue; + + return true; + }; + + /* Hit end of trace data. */ + return false; +} + +/** + * fw_trace_get_first() - Find first valid entry in trace + * @trace_seq_data: Trace sequence data. + * + * Skips over invalid (usually zero) and ROGUE_FW_SF_FIRST entries. + * + * If the trace has no valid entries, this function will exit with the trace + * index pointing to the end of the trace. trace_seq_show() will return an error + * in this state. + */ +static void fw_trace_get_first(struct pvr_fw_trace_seq_data *trace_seq_data) +{ + trace_seq_data->idx = 0; + + while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) { + u32 id = read_fw_trace(trace_seq_data, 0); + + if (ROGUE_FW_LOG_VALIDID(id)) { + u32 sf_id = find_sfid(id); + + if (sf_id != ROGUE_FW_SF_FIRST) + break; + } + trace_seq_data->idx++; + } +} + +static void *fw_trace_seq_start(struct seq_file *s, loff_t *pos) +{ + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; + u32 i; + + /* Reset trace index, then advance to *pos. */ + fw_trace_get_first(trace_seq_data); + + for (i = 0; i < *pos; i++) { + if (!fw_trace_get_next(trace_seq_data)) + return NULL; + } + + return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL; +} + +static void *fw_trace_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; + + (*pos)++; + if (!fw_trace_get_next(trace_seq_data)) + return NULL; + + return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL; +} + +static void fw_trace_seq_stop(struct seq_file *s, void *v) +{ +} + +static int fw_trace_seq_show(struct seq_file *s, void *v) +{ + struct pvr_fw_trace_seq_data *trace_seq_data = s->private; + u64 timestamp; + u32 id; + u32 sf_id; + + if (trace_seq_data->idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) + return -EINVAL; + + id = read_fw_trace(trace_seq_data, 0); + /* Index is not pointing at a valid entry. */ + if (!ROGUE_FW_LOG_VALIDID(id)) + return -EINVAL; + + sf_id = find_sfid(id); + /* Index is not pointing at a valid entry. */ + if (sf_id == ROGUE_FW_SF_LAST) + return -EINVAL; + + timestamp = read_fw_trace(trace_seq_data, 1) | + ((u64)read_fw_trace(trace_seq_data, 2) << 32); + timestamp = (timestamp & ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK) >> + ROGUE_FWT_TIMESTAMP_TIME_SHIFT; + + seq_printf(s, "[%llu] : ", timestamp); + if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) { + seq_printf(s, "ASSERTION %s failed at %s:%u", + trace_seq_data->assert_buf.info, + trace_seq_data->assert_buf.path, + trace_seq_data->assert_buf.line_num); + } else { + seq_printf(s, stid_fmts[sf_id].name, + read_fw_trace(trace_seq_data, 3), + read_fw_trace(trace_seq_data, 4), + read_fw_trace(trace_seq_data, 5), + read_fw_trace(trace_seq_data, 6), + read_fw_trace(trace_seq_data, 7), + read_fw_trace(trace_seq_data, 8), + read_fw_trace(trace_seq_data, 9), + read_fw_trace(trace_seq_data, 10), + read_fw_trace(trace_seq_data, 11), + read_fw_trace(trace_seq_data, 12), + read_fw_trace(trace_seq_data, 13), + read_fw_trace(trace_seq_data, 14), + read_fw_trace(trace_seq_data, 15), + read_fw_trace(trace_seq_data, 16), + read_fw_trace(trace_seq_data, 17), + read_fw_trace(trace_seq_data, 18), + read_fw_trace(trace_seq_data, 19), + read_fw_trace(trace_seq_data, 20), + read_fw_trace(trace_seq_data, 21), + read_fw_trace(trace_seq_data, 22)); + } + seq_puts(s, "\n"); + return 0; +} + +static const struct seq_operations pvr_fw_trace_seq_ops = { + .start = fw_trace_seq_start, + .next = fw_trace_seq_next, + .stop = fw_trace_seq_stop, + .show = fw_trace_seq_show +}; + +static int fw_trace_open(struct inode *inode, struct file *file) +{ + struct pvr_fw_trace_buffer *trace_buffer = inode->i_private; + struct rogue_fwif_tracebuf_space *tracebuf_space = + trace_buffer->tracebuf_space; + struct pvr_fw_trace_seq_data *trace_seq_data; + int err; + + trace_seq_data = kzalloc(sizeof(*trace_seq_data), GFP_KERNEL); + if (!trace_seq_data) + return -ENOMEM; + + trace_seq_data->buffer = kcalloc(ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS, + sizeof(*trace_seq_data->buffer), GFP_KERNEL); + if (!trace_seq_data->buffer) { + err = -ENOMEM; + goto err_free_data; + } + + /* + * Take a local copy of the trace buffer, as firmware may still be + * writing to it. This will exist as long as this file is open. + */ + memcpy(trace_seq_data->buffer, trace_buffer->buf, + ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS * sizeof(u32)); + trace_seq_data->start_offset = READ_ONCE(tracebuf_space->trace_pointer); + trace_seq_data->assert_buf = tracebuf_space->assert_buf; + fw_trace_get_first(trace_seq_data); + + err = seq_open(file, &pvr_fw_trace_seq_ops); + if (err) + goto err_free_buffer; + + ((struct seq_file *)file->private_data)->private = trace_seq_data; + + return 0; + +err_free_buffer: + kfree(trace_seq_data->buffer); + +err_free_data: + kfree(trace_seq_data); + + return err; +} + +static int fw_trace_release(struct inode *inode, struct file *file) +{ + struct pvr_fw_trace_seq_data *trace_seq_data = + ((struct seq_file *)file->private_data)->private; + + seq_release(inode, file); + kfree(trace_seq_data->buffer); + kfree(trace_seq_data); + + return 0; +} + +static const struct file_operations pvr_fw_trace_fops = { + .owner = THIS_MODULE, + .open = fw_trace_open, + .read = seq_read, + .llseek = seq_lseek, + .release = fw_trace_release, +}; + +void +pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, u32 new_mask) +{ + if (old_mask != new_mask) + update_logtype(pvr_dev, new_mask); +} + +void +pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir) +{ + struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace; + u32 thread_nr; + + static_assert(ARRAY_SIZE(fw_trace->buffers) <= 10, + "The filename buffer is only large enough for a single-digit thread count"); + + for (thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); ++thread_nr) { + char filename[8]; + + snprintf(filename, ARRAY_SIZE(filename), "trace_%u", thread_nr); + debugfs_create_file(filename, 0400, dir, + &fw_trace->buffers[thread_nr], + &pvr_fw_trace_fops); + } +} +#endif diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.h b/drivers/gpu/drm/imagination/pvr_fw_trace.h new file mode 100644 index 000000000000..0074d2b18da0 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_fw_trace.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_FW_TRACE_H +#define PVR_FW_TRACE_H + +#include <drm/drm_file.h> +#include <linux/types.h> + +#include "pvr_rogue_fwif.h" + +/* Forward declaration from pvr_device.h. */ +struct pvr_device; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +/* Forward declarations from pvr_rogue_fwif.h */ +struct rogue_fwif_tracebuf; +struct rogue_fwif_tracebuf_space; + +/** + * struct pvr_fw_trace_buffer - Structure representing a trace buffer + */ +struct pvr_fw_trace_buffer { + /** @buf_obj: FW buffer object representing trace buffer. */ + struct pvr_fw_object *buf_obj; + + /** @buf: Pointer to CPU mapping of trace buffer. */ + u32 *buf; + + /** + * @tracebuf_space: Pointer to FW tracebuf_space structure for this + * trace buffer. + */ + struct rogue_fwif_tracebuf_space *tracebuf_space; +}; + +/** + * struct pvr_fw_trace - Device firmware trace data + */ +struct pvr_fw_trace { + /** + * @tracebuf_ctrl_obj: Object representing FW trace buffer control + * structure. + */ + struct pvr_fw_object *tracebuf_ctrl_obj; + + /** + * @tracebuf_ctrl: Pointer to CPU mapping of FW trace buffer control + * structure. + */ + struct rogue_fwif_tracebuf *tracebuf_ctrl; + + /** + * @buffers: Array representing the actual trace buffers owned by this + * device. + */ + struct pvr_fw_trace_buffer buffers[ROGUE_FW_THREAD_MAX]; + + /** @group_mask: Mask of enabled trace groups. */ + u32 group_mask; +}; + +int pvr_fw_trace_init(struct pvr_device *pvr_dev); +void pvr_fw_trace_fini(struct pvr_device *pvr_dev); + +#if defined(CONFIG_DEBUG_FS) +/* Forward declaration from <linux/dcache.h>. */ +struct dentry; + +void pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, + u32 new_mask); + +void pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir); +#endif /* defined(CONFIG_DEBUG_FS) */ + +#endif /* PVR_FW_TRACE_H */ diff --git a/drivers/gpu/drm/imagination/pvr_gem.c b/drivers/gpu/drm/imagination/pvr_gem.c new file mode 100644 index 000000000000..6a8c81fe8c1e --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_gem.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_gem.h" +#include "pvr_vm.h" + +#include <drm/drm_gem.h> +#include <drm/drm_prime.h> + +#include <linux/compiler.h> +#include <linux/compiler_attributes.h> +#include <linux/dma-buf.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/gfp.h> +#include <linux/iosys-map.h> +#include <linux/log2.h> +#include <linux/mutex.h> +#include <linux/pagemap.h> +#include <linux/refcount.h> +#include <linux/scatterlist.h> + +static void pvr_gem_object_free(struct drm_gem_object *obj) +{ + drm_gem_shmem_object_free(obj); +} + +static int pvr_gem_mmap(struct drm_gem_object *gem_obj, struct vm_area_struct *vma) +{ + struct pvr_gem_object *pvr_obj = gem_to_pvr_gem(gem_obj); + struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj); + + if (!(pvr_obj->flags & DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS)) + return -EINVAL; + + return drm_gem_shmem_mmap(shmem_obj, vma); +} + +static const struct drm_gem_object_funcs pvr_gem_object_funcs = { + .free = pvr_gem_object_free, + .print_info = drm_gem_shmem_object_print_info, + .pin = drm_gem_shmem_object_pin, + .unpin = drm_gem_shmem_object_unpin, + .get_sg_table = drm_gem_shmem_object_get_sg_table, + .vmap = drm_gem_shmem_object_vmap, + .vunmap = drm_gem_shmem_object_vunmap, + .mmap = pvr_gem_mmap, + .vm_ops = &drm_gem_shmem_vm_ops, +}; + +/** + * pvr_gem_object_flags_validate() - Verify that a collection of PowerVR GEM + * mapping and/or creation flags form a valid combination. + * @flags: PowerVR GEM mapping/creation flags to validate. + * + * This function explicitly allows kernel-only flags. All ioctl entrypoints + * should do their own validation as well as relying on this function. + * + * Return: + * * %true if @flags contains valid mapping and/or creation flags, or + * * %false otherwise. + */ +static bool +pvr_gem_object_flags_validate(u64 flags) +{ + static const u64 invalid_combinations[] = { + /* + * Memory flagged as PM/FW-protected cannot be mapped to + * userspace. To make this explicit, we require that the two + * flags allowing each of these respective features are never + * specified together. + */ + (DRM_PVR_BO_PM_FW_PROTECT | + DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS), + }; + + int i; + + /* + * Check for bits set in undefined regions. Reserved regions refer to + * options that can only be set by the kernel. These are explicitly + * allowed in most cases, and must be checked specifically in IOCTL + * callback code. + */ + if ((flags & PVR_BO_UNDEFINED_MASK) != 0) + return false; + + /* + * Check for all combinations of flags marked as invalid in the array + * above. + */ + for (i = 0; i < ARRAY_SIZE(invalid_combinations); ++i) { + u64 combo = invalid_combinations[i]; + + if ((flags & combo) == combo) + return false; + } + + return true; +} + +/** + * pvr_gem_object_into_handle() - Convert a reference to an object into a + * userspace-accessible handle. + * @pvr_obj: [IN] Target PowerVR-specific object. + * @pvr_file: [IN] File to associate the handle with. + * @handle: [OUT] Pointer to store the created handle in. Remains unmodified if + * an error is encountered. + * + * If an error is encountered, ownership of @pvr_obj will not have been + * transferred. If this function succeeds, however, further use of @pvr_obj is + * considered undefined behaviour unless another reference to it is explicitly + * held. + * + * Return: + * * 0 on success, or + * * Any error encountered while attempting to allocate a handle on @pvr_file. + */ +int +pvr_gem_object_into_handle(struct pvr_gem_object *pvr_obj, + struct pvr_file *pvr_file, u32 *handle) +{ + struct drm_gem_object *gem_obj = gem_from_pvr_gem(pvr_obj); + struct drm_file *file = from_pvr_file(pvr_file); + + u32 new_handle; + int err; + + err = drm_gem_handle_create(file, gem_obj, &new_handle); + if (err) + return err; + + /* + * Release our reference to @pvr_obj, effectively transferring + * ownership to the handle. + */ + pvr_gem_object_put(pvr_obj); + + /* + * Do not store the new handle in @handle until no more errors can + * occur. + */ + *handle = new_handle; + + return 0; +} + +/** + * pvr_gem_object_from_handle() - Obtain a reference to an object from a + * userspace handle. + * @pvr_file: PowerVR-specific file to which @handle is associated. + * @handle: Userspace handle referencing the target object. + * + * On return, @handle always maintains its reference to the requested object + * (if it had one in the first place). If this function succeeds, the returned + * object will hold an additional reference. When the caller is finished with + * the returned object, they should call pvr_gem_object_put() on it to release + * this reference. + * + * Return: + * * A pointer to the requested PowerVR-specific object on success, or + * * %NULL otherwise. + */ +struct pvr_gem_object * +pvr_gem_object_from_handle(struct pvr_file *pvr_file, u32 handle) +{ + struct drm_file *file = from_pvr_file(pvr_file); + struct drm_gem_object *gem_obj; + + gem_obj = drm_gem_object_lookup(file, handle); + if (!gem_obj) + return NULL; + + return gem_to_pvr_gem(gem_obj); +} + +/** + * pvr_gem_object_vmap() - Map a PowerVR GEM object into CPU virtual address + * space. + * @pvr_obj: Target PowerVR GEM object. + * + * Once the caller is finished with the CPU mapping, they must call + * pvr_gem_object_vunmap() on @pvr_obj. + * + * If @pvr_obj is CPU-cached, dma_sync_sgtable_for_cpu() is called to make + * sure the CPU mapping is consistent. + * + * Return: + * * A pointer to the CPU mapping on success, + * * -%ENOMEM if the mapping fails, or + * * Any error encountered while attempting to acquire a reference to the + * backing pages for @pvr_obj. + */ +void * +pvr_gem_object_vmap(struct pvr_gem_object *pvr_obj) +{ + struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj); + struct drm_gem_object *obj = gem_from_pvr_gem(pvr_obj); + struct iosys_map map; + int err; + + dma_resv_lock(obj->resv, NULL); + + err = drm_gem_shmem_vmap(shmem_obj, &map); + if (err) + goto err_unlock; + + if (pvr_obj->flags & PVR_BO_CPU_CACHED) { + struct device *dev = shmem_obj->base.dev->dev; + + /* If shmem_obj->sgt is NULL, that means the buffer hasn't been mapped + * in GPU space yet. + */ + if (shmem_obj->sgt) + dma_sync_sgtable_for_cpu(dev, shmem_obj->sgt, DMA_BIDIRECTIONAL); + } + + dma_resv_unlock(obj->resv); + + return map.vaddr; + +err_unlock: + dma_resv_unlock(obj->resv); + + return ERR_PTR(err); +} + +/** + * pvr_gem_object_vunmap() - Unmap a PowerVR memory object from CPU virtual + * address space. + * @pvr_obj: Target PowerVR GEM object. + * + * If @pvr_obj is CPU-cached, dma_sync_sgtable_for_device() is called to make + * sure the GPU mapping is consistent. + */ +void +pvr_gem_object_vunmap(struct pvr_gem_object *pvr_obj) +{ + struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj); + struct iosys_map map = IOSYS_MAP_INIT_VADDR(shmem_obj->vaddr); + struct drm_gem_object *obj = gem_from_pvr_gem(pvr_obj); + + if (WARN_ON(!map.vaddr)) + return; + + dma_resv_lock(obj->resv, NULL); + + if (pvr_obj->flags & PVR_BO_CPU_CACHED) { + struct device *dev = shmem_obj->base.dev->dev; + + /* If shmem_obj->sgt is NULL, that means the buffer hasn't been mapped + * in GPU space yet. + */ + if (shmem_obj->sgt) + dma_sync_sgtable_for_device(dev, shmem_obj->sgt, DMA_BIDIRECTIONAL); + } + + drm_gem_shmem_vunmap(shmem_obj, &map); + + dma_resv_unlock(obj->resv); +} + +/** + * pvr_gem_object_zero() - Zeroes the physical memory behind an object. + * @pvr_obj: Target PowerVR GEM object. + * + * Return: + * * 0 on success, or + * * Any error encountered while attempting to map @pvr_obj to the CPU (see + * pvr_gem_object_vmap()). + */ +static int +pvr_gem_object_zero(struct pvr_gem_object *pvr_obj) +{ + void *cpu_ptr; + + cpu_ptr = pvr_gem_object_vmap(pvr_obj); + if (IS_ERR(cpu_ptr)) + return PTR_ERR(cpu_ptr); + + memset(cpu_ptr, 0, pvr_gem_object_size(pvr_obj)); + + /* Make sure the zero-ing is done before vumap-ing the object. */ + wmb(); + + pvr_gem_object_vunmap(pvr_obj); + + return 0; +} + +/** + * pvr_gem_create_object() - Allocate and pre-initializes a pvr_gem_object + * @drm_dev: DRM device creating this object. + * @size: Size of the object to allocate in bytes. + * + * Return: + * * The new pre-initialized GEM object on success, + * * -ENOMEM if the allocation failed. + */ +struct drm_gem_object *pvr_gem_create_object(struct drm_device *drm_dev, size_t size) +{ + struct drm_gem_object *gem_obj; + struct pvr_gem_object *pvr_obj; + + pvr_obj = kzalloc(sizeof(*pvr_obj), GFP_KERNEL); + if (!pvr_obj) + return ERR_PTR(-ENOMEM); + + gem_obj = gem_from_pvr_gem(pvr_obj); + gem_obj->funcs = &pvr_gem_object_funcs; + + return gem_obj; +} + +/** + * pvr_gem_object_create() - Creates a PowerVR-specific buffer object. + * @pvr_dev: Target PowerVR device. + * @size: Size of the object to allocate in bytes. Must be greater than zero. + * Any value which is not an exact multiple of the system page size will be + * rounded up to satisfy this condition. + * @flags: Options which affect both this operation and future mapping + * operations performed on the returned object. Must be a combination of + * DRM_PVR_BO_* and/or PVR_BO_* flags. + * + * The created object may be larger than @size, but can never be smaller. To + * get the exact size, call pvr_gem_object_size() on the returned pointer. + * + * Return: + * * The newly-minted PowerVR-specific buffer object on success, + * * -%EINVAL if @size is zero or @flags is not valid, + * * -%ENOMEM if sufficient physical memory cannot be allocated, or + * * Any other error returned by drm_gem_create_mmap_offset(). + */ +struct pvr_gem_object * +pvr_gem_object_create(struct pvr_device *pvr_dev, size_t size, u64 flags) +{ + struct drm_gem_shmem_object *shmem_obj; + struct pvr_gem_object *pvr_obj; + struct sg_table *sgt; + int err; + + /* Verify @size and @flags before continuing. */ + if (size == 0 || !pvr_gem_object_flags_validate(flags)) + return ERR_PTR(-EINVAL); + + shmem_obj = drm_gem_shmem_create(from_pvr_device(pvr_dev), size); + if (IS_ERR(shmem_obj)) + return ERR_CAST(shmem_obj); + + shmem_obj->pages_mark_dirty_on_put = true; + shmem_obj->map_wc = !(flags & PVR_BO_CPU_CACHED); + pvr_obj = shmem_gem_to_pvr_gem(shmem_obj); + pvr_obj->flags = flags; + + sgt = drm_gem_shmem_get_pages_sgt(shmem_obj); + if (IS_ERR(sgt)) { + err = PTR_ERR(sgt); + goto err_shmem_object_free; + } + + dma_sync_sgtable_for_device(shmem_obj->base.dev->dev, sgt, + DMA_BIDIRECTIONAL); + + /* + * Do this last because pvr_gem_object_zero() requires a fully + * configured instance of struct pvr_gem_object. + */ + pvr_gem_object_zero(pvr_obj); + + return pvr_obj; + +err_shmem_object_free: + drm_gem_shmem_free(shmem_obj); + + return ERR_PTR(err); +} + +/** + * pvr_gem_get_dma_addr() - Get DMA address for given offset in object + * @pvr_obj: Pointer to object to lookup address in. + * @offset: Offset within object to lookup address at. + * @dma_addr_out: Pointer to location to store DMA address. + * + * Returns: + * * 0 on success, or + * * -%EINVAL if object is not currently backed, or if @offset is out of valid + * range for this object. + */ +int +pvr_gem_get_dma_addr(struct pvr_gem_object *pvr_obj, u32 offset, + dma_addr_t *dma_addr_out) +{ + struct drm_gem_shmem_object *shmem_obj = shmem_gem_from_pvr_gem(pvr_obj); + u32 accumulated_offset = 0; + struct scatterlist *sgl; + unsigned int sgt_idx; + + WARN_ON(!shmem_obj->sgt); + for_each_sgtable_dma_sg(shmem_obj->sgt, sgl, sgt_idx) { + u32 new_offset = accumulated_offset + sg_dma_len(sgl); + + if (offset >= accumulated_offset && offset < new_offset) { + *dma_addr_out = sg_dma_address(sgl) + + (offset - accumulated_offset); + return 0; + } + + accumulated_offset = new_offset; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/imagination/pvr_gem.h b/drivers/gpu/drm/imagination/pvr_gem.h new file mode 100644 index 000000000000..e0e5ea509a2e --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_gem.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_GEM_H +#define PVR_GEM_H + +#include "pvr_rogue_heap_config.h" +#include "pvr_rogue_meta.h" + +#include <uapi/drm/pvr_drm.h> + +#include <drm/drm_gem.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_mm.h> + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/const.h> +#include <linux/compiler_attributes.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/refcount.h> +#include <linux/scatterlist.h> +#include <linux/sizes.h> +#include <linux/types.h> + +/* Forward declaration from "pvr_device.h". */ +struct pvr_device; +struct pvr_file; + +/** + * DOC: Flags for DRM_IOCTL_PVR_CREATE_BO (kernel-only) + * + * Kernel-only values allowed in &pvr_gem_object->flags. The majority of options + * for this field are specified in the UAPI header "pvr_drm.h" with a + * DRM_PVR_BO_ prefix. To distinguish these internal options (which must exist + * in ranges marked as "reserved" in the UAPI header), we drop the DRM prefix. + * The public options should be used directly, DRM prefix and all. + * + * To avoid potentially confusing gaps in the UAPI options, these kernel-only + * options are specified "in reverse", starting at bit 63. + * + * We use "reserved" to refer to bits defined here and not exposed in the UAPI. + * Bits not defined anywhere are "undefined". + * + * CPU mapping options + * :PVR_BO_CPU_CACHED: By default, all GEM objects are mapped write-combined on the CPU. Set this + * flag to override this behaviour and map the object cached. + * + * Firmware options + * :PVR_BO_FW_NO_CLEAR_ON_RESET: By default, all FW objects are cleared and reinitialised on hard + * reset. Set this flag to override this behaviour and preserve buffer contents on reset. + */ +#define PVR_BO_CPU_CACHED BIT_ULL(63) + +#define PVR_BO_FW_NO_CLEAR_ON_RESET BIT_ULL(62) + +#define PVR_BO_KERNEL_FLAGS_MASK (PVR_BO_CPU_CACHED | PVR_BO_FW_NO_CLEAR_ON_RESET) + +/* Bits 61..3 are undefined. */ +/* Bits 2..0 are defined in the UAPI. */ + +/* Other utilities. */ +#define PVR_BO_UNDEFINED_MASK ~(PVR_BO_KERNEL_FLAGS_MASK | DRM_PVR_BO_FLAGS_MASK) + +/* + * All firmware-mapped memory uses (mostly) the same flags. Specifically, + * firmware-mapped memory should be: + * * Read/write on the device, + * * Read/write on the CPU, and + * * Write-combined on the CPU. + * + * The only variation is in caching on the device. + */ +#define PVR_BO_FW_FLAGS_DEVICE_CACHED (ULL(0)) +#define PVR_BO_FW_FLAGS_DEVICE_UNCACHED DRM_PVR_BO_BYPASS_DEVICE_CACHE + +/** + * struct pvr_gem_object - powervr-specific wrapper for &struct drm_gem_object + */ +struct pvr_gem_object { + /** + * @base: The underlying &struct drm_gem_shmem_object. + * + * Do not access this member directly, instead call + * shem_gem_from_pvr_gem(). + */ + struct drm_gem_shmem_object base; + + /** + * @flags: Options set at creation-time. Some of these options apply to + * the creation operation itself (which are stored here for reference) + * with the remainder used for mapping options to both the device and + * CPU. These are used every time this object is mapped, but may be + * changed after creation. + * + * Must be a combination of DRM_PVR_BO_* and/or PVR_BO_* flags. + * + * .. note:: + * + * This member is declared const to indicate that none of these + * options may change or be changed throughout the object's + * lifetime. + */ + u64 flags; + +}; + +static_assert(offsetof(struct pvr_gem_object, base) == 0, + "offsetof(struct pvr_gem_object, base) not zero"); + +#define shmem_gem_from_pvr_gem(pvr_obj) (&(pvr_obj)->base) + +#define shmem_gem_to_pvr_gem(shmem_obj) container_of_const(shmem_obj, struct pvr_gem_object, base) + +#define gem_from_pvr_gem(pvr_obj) (&(pvr_obj)->base.base) + +#define gem_to_pvr_gem(gem_obj) container_of_const(gem_obj, struct pvr_gem_object, base.base) + +/* Functions defined in pvr_gem.c */ + +struct drm_gem_object *pvr_gem_create_object(struct drm_device *drm_dev, size_t size); + +struct pvr_gem_object *pvr_gem_object_create(struct pvr_device *pvr_dev, + size_t size, u64 flags); + +int pvr_gem_object_into_handle(struct pvr_gem_object *pvr_obj, + struct pvr_file *pvr_file, u32 *handle); +struct pvr_gem_object *pvr_gem_object_from_handle(struct pvr_file *pvr_file, + u32 handle); + +static __always_inline struct sg_table * +pvr_gem_object_get_pages_sgt(struct pvr_gem_object *pvr_obj) +{ + return drm_gem_shmem_get_pages_sgt(shmem_gem_from_pvr_gem(pvr_obj)); +} + +void *pvr_gem_object_vmap(struct pvr_gem_object *pvr_obj); +void pvr_gem_object_vunmap(struct pvr_gem_object *pvr_obj); + +int pvr_gem_get_dma_addr(struct pvr_gem_object *pvr_obj, u32 offset, + dma_addr_t *dma_addr_out); + +/** + * pvr_gem_object_get() - Acquire reference on pvr_gem_object + * @pvr_obj: Pointer to object to acquire reference on. + */ +static __always_inline void +pvr_gem_object_get(struct pvr_gem_object *pvr_obj) +{ + drm_gem_object_get(gem_from_pvr_gem(pvr_obj)); +} + +/** + * pvr_gem_object_put() - Release reference on pvr_gem_object + * @pvr_obj: Pointer to object to release reference on. + */ +static __always_inline void +pvr_gem_object_put(struct pvr_gem_object *pvr_obj) +{ + drm_gem_object_put(gem_from_pvr_gem(pvr_obj)); +} + +static __always_inline size_t +pvr_gem_object_size(struct pvr_gem_object *pvr_obj) +{ + return gem_from_pvr_gem(pvr_obj)->size; +} + +#endif /* PVR_GEM_H */ diff --git a/drivers/gpu/drm/imagination/pvr_hwrt.c b/drivers/gpu/drm/imagination/pvr_hwrt.c new file mode 100644 index 000000000000..c4213c18489e --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_hwrt.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_free_list.h" +#include "pvr_hwrt.h" +#include "pvr_gem.h" +#include "pvr_rogue_cr_defs_client.h" +#include "pvr_rogue_fwif.h" + +#include <drm/drm_gem.h> +#include <linux/bitops.h> +#include <linux/math.h> +#include <linux/slab.h> +#include <linux/xarray.h> +#include <uapi/drm/pvr_drm.h> + +static_assert(ROGUE_FWIF_NUM_RTDATAS == 2); +static_assert(ROGUE_FWIF_NUM_GEOMDATAS == 1); +static_assert(ROGUE_FWIF_NUM_RTDATA_FREELISTS == 2); + +/* + * struct pvr_rt_mtile_info - Render target macrotile information + */ +struct pvr_rt_mtile_info { + u32 mtile_x[3]; + u32 mtile_y[3]; + u32 tile_max_x; + u32 tile_max_y; + u32 tile_size_x; + u32 tile_size_y; + u32 num_tiles_x; + u32 num_tiles_y; +}; + +/* Size of Shadow Render Target Cache entry */ +#define SRTC_ENTRY_SIZE sizeof(u32) +/* Size of Renders Accumulation Array entry */ +#define RAA_ENTRY_SIZE sizeof(u32) + +static int +hwrt_init_kernel_structure(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_hwrt_dataset_args *args, + struct pvr_hwrt_dataset *hwrt) +{ + struct pvr_device *pvr_dev = pvr_file->pvr_dev; + int err; + int i; + + hwrt->pvr_dev = pvr_dev; + hwrt->max_rts = args->layers; + + /* Get pointers to the free lists */ + for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) { + hwrt->free_lists[i] = pvr_free_list_lookup(pvr_file, args->free_list_handles[i]); + if (!hwrt->free_lists[i]) { + err = -EINVAL; + goto err_put_free_lists; + } + } + + if (hwrt->free_lists[ROGUE_FW_LOCAL_FREELIST]->current_pages < + pvr_get_free_list_min_pages(pvr_dev)) { + err = -EINVAL; + goto err_put_free_lists; + } + + return 0; + +err_put_free_lists: + for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) { + pvr_free_list_put(hwrt->free_lists[i]); + hwrt->free_lists[i] = NULL; + } + + return err; +} + +static void +hwrt_fini_kernel_structure(struct pvr_hwrt_dataset *hwrt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) { + pvr_free_list_put(hwrt->free_lists[i]); + hwrt->free_lists[i] = NULL; + } +} + +static void +hwrt_fini_common_fw_structure(struct pvr_hwrt_dataset *hwrt) +{ + pvr_fw_object_destroy(hwrt->common_fw_obj); +} + +static int +get_cr_isp_mtile_size_val(struct pvr_device *pvr_dev, u32 samples, + struct pvr_rt_mtile_info *info, u32 *value_out) +{ + u32 x = info->mtile_x[0]; + u32 y = info->mtile_y[0]; + u32 samples_per_pixel; + int err; + + err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel); + if (err) + return err; + + if (samples_per_pixel == 1) { + if (samples >= 4) + x <<= 1; + if (samples >= 2) + y <<= 1; + } else if (samples_per_pixel == 2) { + if (samples >= 8) + x <<= 1; + if (samples >= 4) + y <<= 1; + } else if (samples_per_pixel == 4) { + if (samples >= 8) + y <<= 1; + } else { + WARN(true, "Unsupported ISP samples per pixel value"); + return -EINVAL; + } + + *value_out = ((x << ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK) | + ((y << ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK); + + return 0; +} + +static int +get_cr_multisamplectl_val(u32 samples, bool y_flip, u64 *value_out) +{ + static const struct { + u8 x[8]; + u8 y[8]; + } sample_positions[4] = { + /* 1 sample */ + { + .x = { 8 }, + .y = { 8 }, + }, + /* 2 samples */ + { + .x = { 12, 4 }, + .y = { 12, 4 }, + }, + /* 4 samples */ + { + .x = { 6, 14, 2, 10 }, + .y = { 2, 6, 10, 14 }, + }, + /* 8 samples */ + { + .x = { 9, 7, 13, 5, 3, 1, 11, 15 }, + .y = { 5, 11, 9, 3, 13, 7, 15, 1 }, + }, + }; + const int idx = fls(samples) - 1; + u64 value = 0; + + if (idx < 0 || idx > 3) + return -EINVAL; + + for (u32 i = 0; i < 8; i++) { + value |= ((u64)sample_positions[idx].x[i]) << (i * 8); + if (y_flip) + value |= (((u64)(16 - sample_positions[idx].y[i]) & 0xf)) << (i * 8 + 4); + else + value |= ((u64)sample_positions[idx].y[i]) << (i * 8 + 4); + } + + *value_out = value; + + return 0; +} + +static int +get_cr_te_aa_val(struct pvr_device *pvr_dev, u32 samples, u32 *value_out) +{ + u32 samples_per_pixel; + u32 value = 0; + int err = 0; + + err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel); + if (err) + return err; + + switch (samples_per_pixel) { + case 1: + if (samples >= 2) + value |= ROGUE_CR_TE_AA_Y_EN; + if (samples >= 4) + value |= ROGUE_CR_TE_AA_X_EN; + break; + case 2: + if (samples >= 2) + value |= ROGUE_CR_TE_AA_X2_EN; + if (samples >= 4) + value |= ROGUE_CR_TE_AA_Y_EN; + if (samples >= 8) + value |= ROGUE_CR_TE_AA_X_EN; + break; + case 4: + if (samples >= 2) + value |= ROGUE_CR_TE_AA_X2_EN; + if (samples >= 4) + value |= ROGUE_CR_TE_AA_Y2_EN; + if (samples >= 8) + value |= ROGUE_CR_TE_AA_Y_EN; + break; + default: + WARN(true, "Unsupported ISP samples per pixel value"); + return -EINVAL; + } + + *value_out = value; + + return 0; +} + +static void +hwrtdata_common_init(void *cpu_ptr, void *priv) +{ + struct pvr_hwrt_dataset *hwrt = priv; + + memcpy(cpu_ptr, &hwrt->common, sizeof(hwrt->common)); +} + +static int +hwrt_init_common_fw_structure(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_hwrt_dataset_args *args, + struct pvr_hwrt_dataset *hwrt) +{ + struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args; + struct pvr_device *pvr_dev = pvr_file->pvr_dev; + struct pvr_rt_mtile_info info; + int err; + + err = PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &info.tile_size_x); + if (WARN_ON(err)) + return err; + + err = PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &info.tile_size_y); + if (WARN_ON(err)) + return err; + + info.num_tiles_x = DIV_ROUND_UP(args->width, info.tile_size_x); + info.num_tiles_y = DIV_ROUND_UP(args->height, info.tile_size_y); + + if (PVR_HAS_FEATURE(pvr_dev, simple_parameter_format_version)) { + u32 parameter_format; + + err = PVR_FEATURE_VALUE(pvr_dev, simple_parameter_format_version, + ¶meter_format); + if (WARN_ON(err)) + return err; + + WARN_ON(parameter_format != 2); + + /* + * Set up 16 macrotiles with a multiple of 2x2 tiles per macrotile, which is + * aligned to a tile group. + */ + info.mtile_x[0] = DIV_ROUND_UP(info.num_tiles_x, 8) * 2; + info.mtile_y[0] = DIV_ROUND_UP(info.num_tiles_y, 8) * 2; + info.mtile_x[1] = 0; + info.mtile_y[1] = 0; + info.mtile_x[2] = 0; + info.mtile_y[2] = 0; + info.tile_max_x = round_up(info.num_tiles_x, 2) - 1; + info.tile_max_y = round_up(info.num_tiles_y, 2) - 1; + } else { + /* Set up 16 macrotiles with a multiple of 4x4 tiles per macrotile. */ + info.mtile_x[0] = round_up(DIV_ROUND_UP(info.num_tiles_x, 4), 4); + info.mtile_y[0] = round_up(DIV_ROUND_UP(info.num_tiles_y, 4), 4); + info.mtile_x[1] = info.mtile_x[0] * 2; + info.mtile_y[1] = info.mtile_y[0] * 2; + info.mtile_x[2] = info.mtile_x[0] * 3; + info.mtile_y[2] = info.mtile_y[0] * 3; + info.tile_max_x = info.num_tiles_x - 1; + info.tile_max_y = info.num_tiles_y - 1; + } + + hwrt->common.geom_caches_need_zeroing = false; + + hwrt->common.isp_merge_lower_x = args->isp_merge_lower_x; + hwrt->common.isp_merge_lower_y = args->isp_merge_lower_y; + hwrt->common.isp_merge_upper_x = args->isp_merge_upper_x; + hwrt->common.isp_merge_upper_y = args->isp_merge_upper_y; + hwrt->common.isp_merge_scale_x = args->isp_merge_scale_x; + hwrt->common.isp_merge_scale_y = args->isp_merge_scale_y; + + err = get_cr_multisamplectl_val(args->samples, false, + &hwrt->common.multi_sample_ctl); + if (err) + return err; + + err = get_cr_multisamplectl_val(args->samples, true, + &hwrt->common.flipped_multi_sample_ctl); + if (err) + return err; + + hwrt->common.mtile_stride = info.mtile_x[0] * info.mtile_y[0]; + + err = get_cr_te_aa_val(pvr_dev, args->samples, &hwrt->common.teaa); + if (err) + return err; + + hwrt->common.screen_pixel_max = + (((args->width - 1) << ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT) & + ~ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK) | + (((args->height - 1) << ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT) & + ~ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK); + + hwrt->common.te_screen = + ((info.tile_max_x << ROGUE_CR_TE_SCREEN_XMAX_SHIFT) & + ~ROGUE_CR_TE_SCREEN_XMAX_CLRMSK) | + ((info.tile_max_y << ROGUE_CR_TE_SCREEN_YMAX_SHIFT) & + ~ROGUE_CR_TE_SCREEN_YMAX_CLRMSK); + hwrt->common.te_mtile1 = + ((info.mtile_x[0] << ROGUE_CR_TE_MTILE1_X1_SHIFT) & ~ROGUE_CR_TE_MTILE1_X1_CLRMSK) | + ((info.mtile_x[1] << ROGUE_CR_TE_MTILE1_X2_SHIFT) & ~ROGUE_CR_TE_MTILE1_X2_CLRMSK) | + ((info.mtile_x[2] << ROGUE_CR_TE_MTILE1_X3_SHIFT) & ~ROGUE_CR_TE_MTILE1_X3_CLRMSK); + hwrt->common.te_mtile2 = + ((info.mtile_y[0] << ROGUE_CR_TE_MTILE2_Y1_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y1_CLRMSK) | + ((info.mtile_y[1] << ROGUE_CR_TE_MTILE2_Y2_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y2_CLRMSK) | + ((info.mtile_y[2] << ROGUE_CR_TE_MTILE2_Y3_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y3_CLRMSK); + + err = get_cr_isp_mtile_size_val(pvr_dev, args->samples, &info, + &hwrt->common.isp_mtile_size); + if (err) + return err; + + hwrt->common.tpc_stride = geom_data_args->tpc_stride; + hwrt->common.tpc_size = geom_data_args->tpc_size; + + hwrt->common.rgn_header_size = args->region_header_size; + + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata_common), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, hwrtdata_common_init, hwrt, + &hwrt->common_fw_obj); + + return err; +} + +static void +hwrt_fw_data_init(void *cpu_ptr, void *priv) +{ + struct pvr_hwrt_data *hwrt_data = priv; + + memcpy(cpu_ptr, &hwrt_data->data, sizeof(hwrt_data->data)); +} + +static int +hwrt_data_init_fw_structure(struct pvr_file *pvr_file, + struct pvr_hwrt_dataset *hwrt, + struct drm_pvr_ioctl_create_hwrt_dataset_args *args, + struct drm_pvr_create_hwrt_rt_data_args *rt_data_args, + struct pvr_hwrt_data *hwrt_data) +{ + struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args; + struct pvr_device *pvr_dev = pvr_file->pvr_dev; + struct rogue_fwif_rta_ctl *rta_ctl; + int free_list_i; + int err; + + pvr_fw_object_get_fw_addr(hwrt->common_fw_obj, + &hwrt_data->data.hwrt_data_common_fw_addr); + + for (free_list_i = 0; free_list_i < ARRAY_SIZE(hwrt->free_lists); free_list_i++) { + pvr_fw_object_get_fw_addr(hwrt->free_lists[free_list_i]->fw_obj, + &hwrt_data->data.freelists_fw_addr[free_list_i]); + } + + hwrt_data->data.tail_ptrs_dev_addr = geom_data_args->tpc_dev_addr; + hwrt_data->data.vheap_table_dev_addr = geom_data_args->vheap_table_dev_addr; + hwrt_data->data.rtc_dev_addr = geom_data_args->rtc_dev_addr; + + hwrt_data->data.pm_mlist_dev_addr = rt_data_args->pm_mlist_dev_addr; + hwrt_data->data.macrotile_array_dev_addr = rt_data_args->macrotile_array_dev_addr; + hwrt_data->data.rgn_header_dev_addr = rt_data_args->region_header_dev_addr; + + rta_ctl = &hwrt_data->data.rta_ctl; + + rta_ctl->render_target_index = 0; + rta_ctl->active_render_targets = 0; + rta_ctl->valid_render_targets_fw_addr = 0; + rta_ctl->rta_num_partial_renders_fw_addr = 0; + rta_ctl->max_rts = args->layers; + + if (args->layers > 1) { + err = pvr_fw_object_create(pvr_dev, args->layers * SRTC_ENTRY_SIZE, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &hwrt_data->srtc_obj); + if (err) + return err; + pvr_fw_object_get_fw_addr(hwrt_data->srtc_obj, + &rta_ctl->valid_render_targets_fw_addr); + + err = pvr_fw_object_create(pvr_dev, args->layers * RAA_ENTRY_SIZE, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &hwrt_data->raa_obj); + if (err) + goto err_put_shadow_rt_cache; + pvr_fw_object_get_fw_addr(hwrt_data->raa_obj, + &rta_ctl->rta_num_partial_renders_fw_addr); + } + + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + hwrt_fw_data_init, hwrt_data, &hwrt_data->fw_obj); + if (err) + goto err_put_raa_obj; + + pvr_free_list_add_hwrt(hwrt->free_lists[0], hwrt_data); + + return 0; + +err_put_raa_obj: + if (args->layers > 1) + pvr_fw_object_destroy(hwrt_data->raa_obj); + +err_put_shadow_rt_cache: + if (args->layers > 1) + pvr_fw_object_destroy(hwrt_data->srtc_obj); + + return err; +} + +static void +hwrt_data_fini_fw_structure(struct pvr_hwrt_dataset *hwrt, int hwrt_nr) +{ + struct pvr_hwrt_data *hwrt_data = &hwrt->data[hwrt_nr]; + + pvr_free_list_remove_hwrt(hwrt->free_lists[0], hwrt_data); + + if (hwrt->max_rts > 1) { + pvr_fw_object_destroy(hwrt_data->raa_obj); + pvr_fw_object_destroy(hwrt_data->srtc_obj); + } + + pvr_fw_object_destroy(hwrt_data->fw_obj); +} + +/** + * pvr_hwrt_dataset_create() - Create a new HWRT dataset + * @pvr_file: Pointer to pvr_file structure. + * @args: Creation arguments from userspace. + * + * Return: + * * Pointer to new HWRT, or + * * ERR_PTR(-%ENOMEM) on out of memory. + */ +struct pvr_hwrt_dataset * +pvr_hwrt_dataset_create(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_hwrt_dataset_args *args) +{ + struct pvr_hwrt_dataset *hwrt; + int err; + + /* Create and fill out the kernel structure */ + hwrt = kzalloc(sizeof(*hwrt), GFP_KERNEL); + + if (!hwrt) + return ERR_PTR(-ENOMEM); + + kref_init(&hwrt->ref_count); + + err = hwrt_init_kernel_structure(pvr_file, args, hwrt); + if (err < 0) + goto err_free; + + err = hwrt_init_common_fw_structure(pvr_file, args, hwrt); + if (err < 0) + goto err_free; + + for (int i = 0; i < ARRAY_SIZE(hwrt->data); i++) { + err = hwrt_data_init_fw_structure(pvr_file, hwrt, args, + &args->rt_data_args[i], + &hwrt->data[i]); + if (err < 0) { + i--; + /* Destroy already created structures. */ + for (; i >= 0; i--) + hwrt_data_fini_fw_structure(hwrt, i); + goto err_free; + } + + hwrt->data[i].hwrt_dataset = hwrt; + } + + return hwrt; + +err_free: + pvr_hwrt_dataset_put(hwrt); + + return ERR_PTR(err); +} + +static void +pvr_hwrt_dataset_release(struct kref *ref_count) +{ + struct pvr_hwrt_dataset *hwrt = + container_of(ref_count, struct pvr_hwrt_dataset, ref_count); + + for (int i = ARRAY_SIZE(hwrt->data) - 1; i >= 0; i--) { + WARN_ON(pvr_fw_structure_cleanup(hwrt->pvr_dev, ROGUE_FWIF_CLEANUP_HWRTDATA, + hwrt->data[i].fw_obj, 0)); + hwrt_data_fini_fw_structure(hwrt, i); + } + + hwrt_fini_common_fw_structure(hwrt); + hwrt_fini_kernel_structure(hwrt); + + kfree(hwrt); +} + +/** + * pvr_destroy_hwrt_datasets_for_file: Destroy any HWRT datasets associated + * with the given file. + * @pvr_file: Pointer to pvr_file structure. + * + * Removes all HWRT datasets associated with @pvr_file from the device + * hwrt_dataset list and drops initial references. HWRT datasets will then be + * destroyed once all outstanding references are dropped. + */ +void pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file) +{ + struct pvr_hwrt_dataset *hwrt; + unsigned long handle; + + xa_for_each(&pvr_file->hwrt_handles, handle, hwrt) { + (void)hwrt; + pvr_hwrt_dataset_put(xa_erase(&pvr_file->hwrt_handles, handle)); + } +} + +/** + * pvr_hwrt_dataset_put() - Release reference on HWRT dataset + * @hwrt: Pointer to HWRT dataset to release reference on + */ +void +pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt) +{ + if (hwrt) + kref_put(&hwrt->ref_count, pvr_hwrt_dataset_release); +} diff --git a/drivers/gpu/drm/imagination/pvr_hwrt.h b/drivers/gpu/drm/imagination/pvr_hwrt.h new file mode 100644 index 000000000000..76992948d047 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_hwrt.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_HWRT_H +#define PVR_HWRT_H + +#include <linux/compiler_attributes.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/types.h> +#include <linux/xarray.h> +#include <uapi/drm/pvr_drm.h> + +#include "pvr_device.h" +#include "pvr_rogue_fwif_shared.h" + +/* Forward declaration from pvr_free_list.h. */ +struct pvr_free_list; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +/** + * struct pvr_hwrt_data - structure representing HWRT data + */ +struct pvr_hwrt_data { + /** @fw_obj: FW object representing the FW-side structure. */ + struct pvr_fw_object *fw_obj; + + /** @data: Local copy of FW-side structure. */ + struct rogue_fwif_hwrtdata data; + + /** @freelist_node: List node connecting this HWRT to the local freelist. */ + struct list_head freelist_node; + + /** + * @srtc_obj: FW object representing shadow render target cache. + * + * Only valid if @max_rts > 1. + */ + struct pvr_fw_object *srtc_obj; + + /** + * @raa_obj: FW object representing renders accumulation array. + * + * Only valid if @max_rts > 1. + */ + struct pvr_fw_object *raa_obj; + + /** @hwrt_dataset: Back pointer to owning HWRT dataset. */ + struct pvr_hwrt_dataset *hwrt_dataset; +}; + +/** + * struct pvr_hwrt_dataset - structure representing a HWRT data set. + */ +struct pvr_hwrt_dataset { + /** @ref_count: Reference count of object. */ + struct kref ref_count; + + /** @pvr_dev: Pointer to device that owns this object. */ + struct pvr_device *pvr_dev; + + /** @common_fw_obj: FW object representing common FW-side structure. */ + struct pvr_fw_object *common_fw_obj; + + struct rogue_fwif_hwrtdata_common common; + + /** @data: HWRT data structures belonging to this set. */ + struct pvr_hwrt_data data[ROGUE_FWIF_NUM_RTDATAS]; + + /** @free_lists: Free lists used by HWRT data set. */ + struct pvr_free_list *free_lists[ROGUE_FWIF_NUM_RTDATA_FREELISTS]; + + /** @max_rts: Maximum render targets for this HWRT data set. */ + u16 max_rts; +}; + +struct pvr_hwrt_dataset * +pvr_hwrt_dataset_create(struct pvr_file *pvr_file, + struct drm_pvr_ioctl_create_hwrt_dataset_args *args); + +void +pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file); + +/** + * pvr_hwrt_dataset_lookup() - Lookup HWRT dataset pointer from handle + * @pvr_file: Pointer to pvr_file structure. + * @handle: Object handle. + * + * Takes reference on dataset object. Call pvr_hwrt_dataset_put() to release. + * + * Returns: + * * The requested object on success, or + * * %NULL on failure (object does not exist in list, or is not a HWRT + * dataset) + */ +static __always_inline struct pvr_hwrt_dataset * +pvr_hwrt_dataset_lookup(struct pvr_file *pvr_file, u32 handle) +{ + struct pvr_hwrt_dataset *hwrt; + + xa_lock(&pvr_file->hwrt_handles); + hwrt = xa_load(&pvr_file->hwrt_handles, handle); + + if (hwrt) + kref_get(&hwrt->ref_count); + + xa_unlock(&pvr_file->hwrt_handles); + + return hwrt; +} + +void +pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt); + +/** + * pvr_hwrt_data_lookup() - Lookup HWRT data pointer from handle and index + * @pvr_file: Pointer to pvr_file structure. + * @handle: Object handle. + * @index: Index of RT data within dataset. + * + * Takes reference on dataset object. Call pvr_hwrt_data_put() to release. + * + * Returns: + * * The requested object on success, or + * * %NULL on failure (object does not exist in list, or is not a HWRT + * dataset, or index is out of range) + */ +static __always_inline struct pvr_hwrt_data * +pvr_hwrt_data_lookup(struct pvr_file *pvr_file, u32 handle, u32 index) +{ + struct pvr_hwrt_dataset *hwrt_dataset = pvr_hwrt_dataset_lookup(pvr_file, handle); + + if (hwrt_dataset) { + if (index < ARRAY_SIZE(hwrt_dataset->data)) + return &hwrt_dataset->data[index]; + + pvr_hwrt_dataset_put(hwrt_dataset); + } + + return NULL; +} + +/** + * pvr_hwrt_data_put() - Release reference on HWRT data + * @hwrt: Pointer to HWRT data to release reference on + */ +static __always_inline void +pvr_hwrt_data_put(struct pvr_hwrt_data *hwrt) +{ + if (hwrt) + pvr_hwrt_dataset_put(hwrt->hwrt_dataset); +} + +static __always_inline struct pvr_hwrt_data * +pvr_hwrt_data_get(struct pvr_hwrt_data *hwrt) +{ + if (hwrt) + kref_get(&hwrt->hwrt_dataset->ref_count); + + return hwrt; +} + +#endif /* PVR_HWRT_H */ diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagination/pvr_job.c new file mode 100644 index 000000000000..9d0812710295 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_job.c @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_context.h" +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_gem.h" +#include "pvr_hwrt.h" +#include "pvr_job.h" +#include "pvr_mmu.h" +#include "pvr_power.h" +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_fwif_client.h" +#include "pvr_stream.h" +#include "pvr_stream_defs.h" +#include "pvr_sync.h" + +#include <drm/drm_exec.h> +#include <drm/drm_gem.h> +#include <linux/types.h> +#include <uapi/drm/pvr_drm.h> + +static void pvr_job_release(struct kref *kref) +{ + struct pvr_job *job = container_of(kref, struct pvr_job, ref_count); + + xa_erase(&job->pvr_dev->job_ids, job->id); + + pvr_hwrt_data_put(job->hwrt); + pvr_context_put(job->ctx); + + WARN_ON(job->paired_job); + + pvr_queue_job_cleanup(job); + pvr_job_release_pm_ref(job); + + kfree(job->cmd); + kfree(job); +} + +/** + * pvr_job_put() - Release reference on job + * @job: Target job. + */ +void +pvr_job_put(struct pvr_job *job) +{ + if (job) + kref_put(&job->ref_count, pvr_job_release); +} + +/** + * pvr_job_process_stream() - Build job FW structure from stream + * @pvr_dev: Device pointer. + * @cmd_defs: Stream definition. + * @stream: Pointer to command stream. + * @stream_size: Size of command stream, in bytes. + * @job: Pointer to job. + * + * Caller is responsible for freeing the output structure. + * + * Returns: + * * 0 on success, + * * -%ENOMEM on out of memory, or + * * -%EINVAL on malformed stream. + */ +static int +pvr_job_process_stream(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs, + void *stream, u32 stream_size, struct pvr_job *job) +{ + int err; + + job->cmd = kzalloc(cmd_defs->dest_size, GFP_KERNEL); + if (!job->cmd) + return -ENOMEM; + + job->cmd_len = cmd_defs->dest_size; + + err = pvr_stream_process(pvr_dev, cmd_defs, stream, stream_size, job->cmd); + if (err) + kfree(job->cmd); + + return err; +} + +static int pvr_fw_cmd_init(struct pvr_device *pvr_dev, struct pvr_job *job, + const struct pvr_stream_cmd_defs *stream_def, + u64 stream_userptr, u32 stream_len) +{ + void *stream; + int err; + + stream = kzalloc(stream_len, GFP_KERNEL); + if (!stream) + return -ENOMEM; + + if (copy_from_user(stream, u64_to_user_ptr(stream_userptr), stream_len)) { + err = -EFAULT; + goto err_free_stream; + } + + err = pvr_job_process_stream(pvr_dev, stream_def, stream, stream_len, job); + +err_free_stream: + kfree(stream); + + return err; +} + +static u32 +convert_geom_flags(u32 in_flags) +{ + u32 out_flags = 0; + + if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST) + out_flags |= ROGUE_GEOM_FLAGS_FIRSTKICK; + if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST) + out_flags |= ROGUE_GEOM_FLAGS_LASTKICK; + if (in_flags & DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE) + out_flags |= ROGUE_GEOM_FLAGS_SINGLE_CORE; + + return out_flags; +} + +static u32 +convert_frag_flags(u32 in_flags) +{ + u32 out_flags = 0; + + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE) + out_flags |= ROGUE_FRAG_FLAGS_SINGLE_CORE; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER) + out_flags |= ROGUE_FRAG_FLAGS_DEPTHBUFFER; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER) + out_flags |= ROGUE_FRAG_FLAGS_STENCILBUFFER; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP) + out_flags |= ROGUE_FRAG_FLAGS_PREVENT_CDM_OVERLAP; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER) + out_flags |= ROGUE_FRAG_FLAGS_SCRATCHBUFFER; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS) + out_flags |= ROGUE_FRAG_FLAGS_GET_VIS_RESULTS; + if (in_flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE) + out_flags |= ROGUE_FRAG_FLAGS_DISABLE_PIXELMERGE; + + return out_flags; +} + +static int +pvr_geom_job_fw_cmd_init(struct pvr_job *job, + struct drm_pvr_job *args) +{ + struct rogue_fwif_cmd_geom *cmd; + int err; + + if (args->flags & ~DRM_PVR_SUBMIT_JOB_GEOM_CMD_FLAGS_MASK) + return -EINVAL; + + if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER) + return -EINVAL; + + if (!job->hwrt) + return -EINVAL; + + job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_GEOM; + err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_geom_stream, + args->cmd_stream, args->cmd_stream_len); + if (err) + return err; + + cmd = job->cmd; + cmd->cmd_shared.cmn.frame_num = 0; + cmd->flags = convert_geom_flags(args->flags); + pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr); + return 0; +} + +static int +pvr_frag_job_fw_cmd_init(struct pvr_job *job, + struct drm_pvr_job *args) +{ + struct rogue_fwif_cmd_frag *cmd; + int err; + + if (args->flags & ~DRM_PVR_SUBMIT_JOB_FRAG_CMD_FLAGS_MASK) + return -EINVAL; + + if (job->ctx->type != DRM_PVR_CTX_TYPE_RENDER) + return -EINVAL; + + if (!job->hwrt) + return -EINVAL; + + job->fw_ccb_cmd_type = (args->flags & DRM_PVR_SUBMIT_JOB_FRAG_CMD_PARTIAL_RENDER) ? + ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR : + ROGUE_FWIF_CCB_CMD_TYPE_FRAG; + err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_frag_stream, + args->cmd_stream, args->cmd_stream_len); + if (err) + return err; + + cmd = job->cmd; + cmd->cmd_shared.cmn.frame_num = 0; + cmd->flags = convert_frag_flags(args->flags); + pvr_fw_object_get_fw_addr(job->hwrt->fw_obj, &cmd->cmd_shared.hwrt_data_fw_addr); + return 0; +} + +static u32 +convert_compute_flags(u32 in_flags) +{ + u32 out_flags = 0; + + if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP) + out_flags |= ROGUE_COMPUTE_FLAG_PREVENT_ALL_OVERLAP; + if (in_flags & DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE) + out_flags |= ROGUE_COMPUTE_FLAG_SINGLE_CORE; + + return out_flags; +} + +static int +pvr_compute_job_fw_cmd_init(struct pvr_job *job, + struct drm_pvr_job *args) +{ + struct rogue_fwif_cmd_compute *cmd; + int err; + + if (args->flags & ~DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_FLAGS_MASK) + return -EINVAL; + + if (job->ctx->type != DRM_PVR_CTX_TYPE_COMPUTE) + return -EINVAL; + + job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_CDM; + err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_compute_stream, + args->cmd_stream, args->cmd_stream_len); + if (err) + return err; + + cmd = job->cmd; + cmd->common.frame_num = 0; + cmd->flags = convert_compute_flags(args->flags); + return 0; +} + +static u32 +convert_transfer_flags(u32 in_flags) +{ + u32 out_flags = 0; + + if (in_flags & DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE) + out_flags |= ROGUE_TRANSFER_FLAGS_SINGLE_CORE; + + return out_flags; +} + +static int +pvr_transfer_job_fw_cmd_init(struct pvr_job *job, + struct drm_pvr_job *args) +{ + struct rogue_fwif_cmd_transfer *cmd; + int err; + + if (args->flags & ~DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_FLAGS_MASK) + return -EINVAL; + + if (job->ctx->type != DRM_PVR_CTX_TYPE_TRANSFER_FRAG) + return -EINVAL; + + job->fw_ccb_cmd_type = ROGUE_FWIF_CCB_CMD_TYPE_TQ_3D; + err = pvr_fw_cmd_init(job->pvr_dev, job, &pvr_cmd_transfer_stream, + args->cmd_stream, args->cmd_stream_len); + if (err) + return err; + + cmd = job->cmd; + cmd->common.frame_num = 0; + cmd->flags = convert_transfer_flags(args->flags); + return 0; +} + +static int +pvr_job_fw_cmd_init(struct pvr_job *job, + struct drm_pvr_job *args) +{ + switch (args->type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return pvr_geom_job_fw_cmd_init(job, args); + + case DRM_PVR_JOB_TYPE_FRAGMENT: + return pvr_frag_job_fw_cmd_init(job, args); + + case DRM_PVR_JOB_TYPE_COMPUTE: + return pvr_compute_job_fw_cmd_init(job, args); + + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return pvr_transfer_job_fw_cmd_init(job, args); + + default: + return -EINVAL; + } +} + +/** + * struct pvr_job_data - Helper container for pairing jobs with the + * sync_ops supplied for them by the user. + */ +struct pvr_job_data { + /** @job: Pointer to the job. */ + struct pvr_job *job; + + /** @sync_ops: Pointer to the sync_ops associated with @job. */ + struct drm_pvr_sync_op *sync_ops; + + /** @sync_op_count: Number of members of @sync_ops. */ + u32 sync_op_count; +}; + +/** + * prepare_job_syncs() - Prepare all sync objects for a single job. + * @pvr_file: PowerVR file. + * @job_data: Precreated job and sync_ops array. + * @signal_array: xarray to receive signal sync objects. + * + * Returns: + * * 0 on success, or + * * Any error code returned by pvr_sync_signal_array_collect_ops(), + * pvr_sync_add_deps_to_job(), drm_sched_job_add_resv_dependencies() or + * pvr_sync_signal_array_update_fences(). + */ +static int +prepare_job_syncs(struct pvr_file *pvr_file, + struct pvr_job_data *job_data, + struct xarray *signal_array) +{ + struct dma_fence *done_fence; + int err = pvr_sync_signal_array_collect_ops(signal_array, + from_pvr_file(pvr_file), + job_data->sync_op_count, + job_data->sync_ops); + + if (err) + return err; + + err = pvr_sync_add_deps_to_job(pvr_file, &job_data->job->base, + job_data->sync_op_count, + job_data->sync_ops, signal_array); + if (err) + return err; + + if (job_data->job->hwrt) { + /* The geometry job writes the HWRT region headers, which are + * then read by the fragment job. + */ + struct drm_gem_object *obj = + gem_from_pvr_gem(job_data->job->hwrt->fw_obj->gem); + enum dma_resv_usage usage = + dma_resv_usage_rw(job_data->job->type == + DRM_PVR_JOB_TYPE_GEOMETRY); + + dma_resv_lock(obj->resv, NULL); + err = drm_sched_job_add_resv_dependencies(&job_data->job->base, + obj->resv, usage); + dma_resv_unlock(obj->resv); + if (err) + return err; + } + + /* We need to arm the job to get the job done fence. */ + done_fence = pvr_queue_job_arm(job_data->job); + + err = pvr_sync_signal_array_update_fences(signal_array, + job_data->sync_op_count, + job_data->sync_ops, + done_fence); + return err; +} + +/** + * prepare_job_syncs_for_each() - Prepare all sync objects for an array of jobs. + * @file: PowerVR file. + * @job_data: Array of precreated jobs and their sync_ops. + * @job_count: Number of jobs. + * @signal_array: xarray to receive signal sync objects. + * + * Returns: + * * 0 on success, or + * * Any error code returned by pvr_vm_bind_job_prepare_syncs(). + */ +static int +prepare_job_syncs_for_each(struct pvr_file *pvr_file, + struct pvr_job_data *job_data, + u32 *job_count, + struct xarray *signal_array) +{ + for (u32 i = 0; i < *job_count; i++) { + int err = prepare_job_syncs(pvr_file, &job_data[i], + signal_array); + + if (err) { + *job_count = i; + return err; + } + } + + return 0; +} + +static struct pvr_job * +create_job(struct pvr_device *pvr_dev, + struct pvr_file *pvr_file, + struct drm_pvr_job *args) +{ + struct pvr_job *job = NULL; + int err; + + if (!args->cmd_stream || !args->cmd_stream_len) + return ERR_PTR(-EINVAL); + + if (args->type != DRM_PVR_JOB_TYPE_GEOMETRY && + args->type != DRM_PVR_JOB_TYPE_FRAGMENT && + (args->hwrt.set_handle || args->hwrt.data_index)) + return ERR_PTR(-EINVAL); + + job = kzalloc(sizeof(*job), GFP_KERNEL); + if (!job) + return ERR_PTR(-ENOMEM); + + kref_init(&job->ref_count); + job->type = args->type; + job->pvr_dev = pvr_dev; + + err = xa_alloc(&pvr_dev->job_ids, &job->id, job, xa_limit_32b, GFP_KERNEL); + if (err) + goto err_put_job; + + job->ctx = pvr_context_lookup(pvr_file, args->context_handle); + if (!job->ctx) { + err = -EINVAL; + goto err_put_job; + } + + if (args->hwrt.set_handle) { + job->hwrt = pvr_hwrt_data_lookup(pvr_file, args->hwrt.set_handle, + args->hwrt.data_index); + if (!job->hwrt) { + err = -EINVAL; + goto err_put_job; + } + } + + err = pvr_job_fw_cmd_init(job, args); + if (err) + goto err_put_job; + + err = pvr_queue_job_init(job); + if (err) + goto err_put_job; + + return job; + +err_put_job: + pvr_job_put(job); + return ERR_PTR(err); +} + +/** + * pvr_job_data_fini() - Cleanup all allocs used to set up job submission. + * @job_data: Job data array. + * @job_count: Number of members of @job_data. + */ +static void +pvr_job_data_fini(struct pvr_job_data *job_data, u32 job_count) +{ + for (u32 i = 0; i < job_count; i++) { + pvr_job_put(job_data[i].job); + kvfree(job_data[i].sync_ops); + } +} + +/** + * pvr_job_data_init() - Init an array of created jobs, associating them with + * the appropriate sync_ops args, which will be copied in. + * @pvr_dev: Target PowerVR device. + * @pvr_file: Pointer to PowerVR file structure. + * @job_args: Job args array copied from user. + * @job_count: Number of members of @job_args. + * @job_data_out: Job data array. + */ +static int pvr_job_data_init(struct pvr_device *pvr_dev, + struct pvr_file *pvr_file, + struct drm_pvr_job *job_args, + u32 *job_count, + struct pvr_job_data *job_data_out) +{ + int err = 0, i = 0; + + for (; i < *job_count; i++) { + job_data_out[i].job = + create_job(pvr_dev, pvr_file, &job_args[i]); + err = PTR_ERR_OR_ZERO(job_data_out[i].job); + + if (err) { + *job_count = i; + job_data_out[i].job = NULL; + goto err_cleanup; + } + + err = PVR_UOBJ_GET_ARRAY(job_data_out[i].sync_ops, + &job_args[i].sync_ops); + if (err) { + *job_count = i; + + /* Ensure the job created above is also cleaned up. */ + i++; + goto err_cleanup; + } + + job_data_out[i].sync_op_count = job_args[i].sync_ops.count; + } + + return 0; + +err_cleanup: + pvr_job_data_fini(job_data_out, i); + + return err; +} + +static void +push_jobs(struct pvr_job_data *job_data, u32 job_count) +{ + for (u32 i = 0; i < job_count; i++) + pvr_queue_job_push(job_data[i].job); +} + +static int +prepare_fw_obj_resv(struct drm_exec *exec, struct pvr_fw_object *fw_obj) +{ + return drm_exec_prepare_obj(exec, gem_from_pvr_gem(fw_obj->gem), 1); +} + +static int +jobs_lock_all_objs(struct drm_exec *exec, struct pvr_job_data *job_data, + u32 job_count) +{ + for (u32 i = 0; i < job_count; i++) { + struct pvr_job *job = job_data[i].job; + + /* Grab a lock on a the context, to guard against + * concurrent submission to the same queue. + */ + int err = drm_exec_lock_obj(exec, + gem_from_pvr_gem(job->ctx->fw_obj->gem)); + + if (err) + return err; + + if (job->hwrt) { + err = prepare_fw_obj_resv(exec, + job->hwrt->fw_obj); + if (err) + return err; + } + } + + return 0; +} + +static int +prepare_job_resvs_for_each(struct drm_exec *exec, struct pvr_job_data *job_data, + u32 job_count) +{ + drm_exec_until_all_locked(exec) { + int err = jobs_lock_all_objs(exec, job_data, job_count); + + drm_exec_retry_on_contention(exec); + if (err) + return err; + } + + return 0; +} + +static void +update_job_resvs(struct pvr_job *job) +{ + if (job->hwrt) { + enum dma_resv_usage usage = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ? + DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ; + struct drm_gem_object *obj = gem_from_pvr_gem(job->hwrt->fw_obj->gem); + + dma_resv_add_fence(obj->resv, &job->base.s_fence->finished, usage); + } +} + +static void +update_job_resvs_for_each(struct pvr_job_data *job_data, u32 job_count) +{ + for (u32 i = 0; i < job_count; i++) + update_job_resvs(job_data[i].job); +} + +static bool can_combine_jobs(struct pvr_job *a, struct pvr_job *b) +{ + struct pvr_job *geom_job = a, *frag_job = b; + struct dma_fence *fence; + unsigned long index; + + /* Geometry and fragment jobs can be combined if they are queued to the + * same context and targeting the same HWRT. + */ + if (a->type != DRM_PVR_JOB_TYPE_GEOMETRY || + b->type != DRM_PVR_JOB_TYPE_FRAGMENT || + a->ctx != b->ctx || + a->hwrt != b->hwrt) + return false; + + xa_for_each(&frag_job->base.dependencies, index, fence) { + /* We combine when we see an explicit geom -> frag dep. */ + if (&geom_job->base.s_fence->scheduled == fence) + return true; + } + + return false; +} + +static struct dma_fence * +get_last_queued_job_scheduled_fence(struct pvr_queue *queue, + struct pvr_job_data *job_data, + u32 cur_job_pos) +{ + /* We iterate over the current job array in reverse order to grab the + * last to-be-queued job targeting the same queue. + */ + for (u32 i = cur_job_pos; i > 0; i--) { + struct pvr_job *job = job_data[i - 1].job; + + if (job->ctx == queue->ctx && job->type == queue->type) + return dma_fence_get(&job->base.s_fence->scheduled); + } + + /* If we didn't find any, we just return the last queued job scheduled + * fence attached to the queue. + */ + return dma_fence_get(queue->last_queued_job_scheduled_fence); +} + +static int +pvr_jobs_link_geom_frag(struct pvr_job_data *job_data, u32 *job_count) +{ + for (u32 i = 0; i < *job_count - 1; i++) { + struct pvr_job *geom_job = job_data[i].job; + struct pvr_job *frag_job = job_data[i + 1].job; + struct pvr_queue *frag_queue; + struct dma_fence *f; + + if (!can_combine_jobs(job_data[i].job, job_data[i + 1].job)) + continue; + + /* The fragment job will be submitted by the geometry queue. We + * need to make sure it comes after all the other fragment jobs + * queued before it. + */ + frag_queue = pvr_context_get_queue_for_job(frag_job->ctx, + frag_job->type); + f = get_last_queued_job_scheduled_fence(frag_queue, job_data, + i); + if (f) { + int err = drm_sched_job_add_dependency(&geom_job->base, + f); + if (err) { + *job_count = i; + return err; + } + } + + /* The KCCB slot will be reserved by the geometry job, so we can + * drop the KCCB fence on the fragment job. + */ + pvr_kccb_fence_put(frag_job->kccb_fence); + frag_job->kccb_fence = NULL; + + geom_job->paired_job = frag_job; + frag_job->paired_job = geom_job; + + /* Skip the fragment job we just paired to the geometry job. */ + i++; + } + + return 0; +} + +/** + * pvr_submit_jobs() - Submit jobs to the GPU + * @pvr_dev: Target PowerVR device. + * @pvr_file: Pointer to PowerVR file structure. + * @args: Ioctl args. + * @job_count: Number of jobs in @jobs_args. On error this will be updated + * with the index into @jobs_args where the error occurred. + * + * This initial implementation is entirely synchronous; on return the GPU will + * be idle. This will not be the case for future implementations. + * + * Returns: + * * 0 on success, + * * -%EFAULT if arguments can not be copied from user space, or + * * -%EINVAL on invalid arguments, or + * * Any other error. + */ +int +pvr_submit_jobs(struct pvr_device *pvr_dev, struct pvr_file *pvr_file, + struct drm_pvr_ioctl_submit_jobs_args *args) +{ + struct pvr_job_data *job_data = NULL; + struct drm_pvr_job *job_args; + struct xarray signal_array; + u32 jobs_alloced = 0; + struct drm_exec exec; + int err; + + if (!args->jobs.count) + return -EINVAL; + + err = PVR_UOBJ_GET_ARRAY(job_args, &args->jobs); + if (err) + return err; + + job_data = kvmalloc_array(args->jobs.count, sizeof(*job_data), + GFP_KERNEL | __GFP_ZERO); + if (!job_data) { + err = -ENOMEM; + goto out_free; + } + + err = pvr_job_data_init(pvr_dev, pvr_file, job_args, &args->jobs.count, + job_data); + if (err) + goto out_free; + + jobs_alloced = args->jobs.count; + + /* + * Flush MMU if needed - this has been deferred until now to avoid + * overuse of this expensive operation. + */ + err = pvr_mmu_flush_exec(pvr_dev, false); + if (err) + goto out_job_data_cleanup; + + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES); + + xa_init_flags(&signal_array, XA_FLAGS_ALLOC); + + err = prepare_job_syncs_for_each(pvr_file, job_data, &args->jobs.count, + &signal_array); + if (err) + goto out_exec_fini; + + err = prepare_job_resvs_for_each(&exec, job_data, args->jobs.count); + if (err) + goto out_exec_fini; + + err = pvr_jobs_link_geom_frag(job_data, &args->jobs.count); + if (err) + goto out_exec_fini; + + /* Anything after that point must succeed because we start exposing job + * finished fences to the outside world. + */ + update_job_resvs_for_each(job_data, args->jobs.count); + push_jobs(job_data, args->jobs.count); + pvr_sync_signal_array_push_fences(&signal_array); + err = 0; + +out_exec_fini: + drm_exec_fini(&exec); + pvr_sync_signal_array_cleanup(&signal_array); + +out_job_data_cleanup: + pvr_job_data_fini(job_data, jobs_alloced); + +out_free: + kvfree(job_data); + kvfree(job_args); + + return err; +} diff --git a/drivers/gpu/drm/imagination/pvr_job.h b/drivers/gpu/drm/imagination/pvr_job.h new file mode 100644 index 000000000000..0ca003c5c475 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_job.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_JOB_H +#define PVR_JOB_H + +#include <uapi/drm/pvr_drm.h> + +#include <linux/kref.h> +#include <linux/types.h> + +#include <drm/drm_gem.h> +#include <drm/gpu_scheduler.h> + +#include "pvr_power.h" + +/* Forward declaration from "pvr_context.h". */ +struct pvr_context; + +/* Forward declarations from "pvr_device.h". */ +struct pvr_device; +struct pvr_file; + +/* Forward declarations from "pvr_hwrt.h". */ +struct pvr_hwrt_data; + +/* Forward declaration from "pvr_queue.h". */ +struct pvr_queue; + +struct pvr_job { + /** @base: drm_sched_job object. */ + struct drm_sched_job base; + + /** @ref_count: Refcount for job. */ + struct kref ref_count; + + /** @type: Type of job. */ + enum drm_pvr_job_type type; + + /** @id: Job ID number. */ + u32 id; + + /** + * @paired_job: Job paired to this job. + * + * This field is only meaningful for geometry and fragment jobs. + * + * Paired jobs are executed on the same context, and need to be submitted + * atomically to the FW, to make sure the partial render logic has a + * fragment job to execute when the Parameter Manager runs out of memory. + * + * The geometry job should point to the fragment job it's paired with, + * and the fragment job should point to the geometry job it's paired with. + */ + struct pvr_job *paired_job; + + /** @cccb_fence: Fence used to wait for CCCB space. */ + struct dma_fence *cccb_fence; + + /** @kccb_fence: Fence used to wait for KCCB space. */ + struct dma_fence *kccb_fence; + + /** @done_fence: Fence to signal when the job is done. */ + struct dma_fence *done_fence; + + /** @pvr_dev: Device pointer. */ + struct pvr_device *pvr_dev; + + /** @ctx: Pointer to owning context. */ + struct pvr_context *ctx; + + /** @cmd: Command data. Format depends on @type. */ + void *cmd; + + /** @cmd_len: Length of command data, in bytes. */ + u32 cmd_len; + + /** + * @fw_ccb_cmd_type: Firmware CCB command type. Must be one of %ROGUE_FWIF_CCB_CMD_TYPE_*. + */ + u32 fw_ccb_cmd_type; + + /** @hwrt: HWRT object. Will be NULL for compute and transfer jobs. */ + struct pvr_hwrt_data *hwrt; + + /** + * @has_pm_ref: True if the job has a power ref, thus forcing the GPU to stay on until + * the job is done. + */ + bool has_pm_ref; +}; + +/** + * pvr_job_get() - Take additional reference on job. + * @job: Job pointer. + * + * Call pvr_job_put() to release. + * + * Returns: + * * The requested job on success, or + * * %NULL if no job pointer passed. + */ +static __always_inline struct pvr_job * +pvr_job_get(struct pvr_job *job) +{ + if (job) + kref_get(&job->ref_count); + + return job; +} + +void pvr_job_put(struct pvr_job *job); + +/** + * pvr_job_release_pm_ref() - Release the PM ref if the job acquired it. + * @job: The job to release the PM ref on. + */ +static __always_inline void +pvr_job_release_pm_ref(struct pvr_job *job) +{ + if (job->has_pm_ref) { + pvr_power_put(job->pvr_dev); + job->has_pm_ref = false; + } +} + +/** + * pvr_job_get_pm_ref() - Get a PM ref and attach it to the job. + * @job: The job to attach the PM ref to. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_power_get() otherwise. + */ +static __always_inline int +pvr_job_get_pm_ref(struct pvr_job *job) +{ + int err; + + if (job->has_pm_ref) + return 0; + + err = pvr_power_get(job->pvr_dev); + if (!err) + job->has_pm_ref = true; + + return err; +} + +int pvr_job_wait_first_non_signaled_native_dep(struct pvr_job *job); + +bool pvr_job_non_native_deps_done(struct pvr_job *job); + +int pvr_job_fits_in_cccb(struct pvr_job *job, unsigned long native_dep_count); + +void pvr_job_submit(struct pvr_job *job); + +int pvr_submit_jobs(struct pvr_device *pvr_dev, struct pvr_file *pvr_file, + struct drm_pvr_ioctl_submit_jobs_args *args); + +#endif /* PVR_JOB_H */ diff --git a/drivers/gpu/drm/imagination/pvr_mmu.c b/drivers/gpu/drm/imagination/pvr_mmu.c new file mode 100644 index 000000000000..b71d30e5f380 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_mmu.c @@ -0,0 +1,2637 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_mmu.h" + +#include "pvr_ccb.h" +#include "pvr_device.h" +#include "pvr_fw.h" +#include "pvr_gem.h" +#include "pvr_power.h" +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_mmu_defs.h" + +#include <drm/drm_drv.h> +#include <linux/atomic.h> +#include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/kmemleak.h> +#include <linux/minmax.h> +#include <linux/sizes.h> + +#define PVR_SHIFT_FROM_SIZE(size_) (__builtin_ctzll(size_)) +#define PVR_MASK_FROM_SIZE(size_) (~((size_) - U64_C(1))) + +/* + * The value of the device page size (%PVR_DEVICE_PAGE_SIZE) is currently + * pegged to the host page size (%PAGE_SIZE). This chunk of macro goodness both + * ensures that the selected host page size corresponds to a valid device page + * size and sets up values needed by the MMU code below. + */ +#if (PVR_DEVICE_PAGE_SIZE == SZ_4K) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_4KB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_4KB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_4KB_RANGE_CLRMSK +#elif (PVR_DEVICE_PAGE_SIZE == SZ_16K) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_16KB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_16KB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_16KB_RANGE_CLRMSK +#elif (PVR_DEVICE_PAGE_SIZE == SZ_64K) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_64KB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_64KB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_64KB_RANGE_CLRMSK +#elif (PVR_DEVICE_PAGE_SIZE == SZ_256K) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_256KB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_256KB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_256KB_RANGE_CLRMSK +#elif (PVR_DEVICE_PAGE_SIZE == SZ_1M) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_1MB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_1MB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_1MB_RANGE_CLRMSK +#elif (PVR_DEVICE_PAGE_SIZE == SZ_2M) +# define ROGUE_MMUCTRL_PAGE_SIZE_X ROGUE_MMUCTRL_PAGE_SIZE_2MB +# define ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT ROGUE_MMUCTRL_PAGE_2MB_RANGE_SHIFT +# define ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK ROGUE_MMUCTRL_PAGE_2MB_RANGE_CLRMSK +#else +# error Unsupported device page size PVR_DEVICE_PAGE_SIZE +#endif + +#define ROGUE_MMUCTRL_ENTRIES_PT_VALUE_X \ + (ROGUE_MMUCTRL_ENTRIES_PT_VALUE >> \ + (PVR_DEVICE_PAGE_SHIFT - PVR_SHIFT_FROM_SIZE(SZ_4K))) + +enum pvr_mmu_sync_level { + PVR_MMU_SYNC_LEVEL_NONE = -1, + PVR_MMU_SYNC_LEVEL_0 = 0, + PVR_MMU_SYNC_LEVEL_1 = 1, + PVR_MMU_SYNC_LEVEL_2 = 2, +}; + +#define PVR_MMU_SYNC_LEVEL_0_FLAGS (ROGUE_FWIF_MMUCACHEDATA_FLAGS_PT | \ + ROGUE_FWIF_MMUCACHEDATA_FLAGS_INTERRUPT | \ + ROGUE_FWIF_MMUCACHEDATA_FLAGS_TLB) +#define PVR_MMU_SYNC_LEVEL_1_FLAGS (PVR_MMU_SYNC_LEVEL_0_FLAGS | ROGUE_FWIF_MMUCACHEDATA_FLAGS_PD) +#define PVR_MMU_SYNC_LEVEL_2_FLAGS (PVR_MMU_SYNC_LEVEL_1_FLAGS | ROGUE_FWIF_MMUCACHEDATA_FLAGS_PC) + +/** + * pvr_mmu_set_flush_flags() - Set MMU cache flush flags for next call to + * pvr_mmu_flush_exec(). + * @pvr_dev: Target PowerVR device. + * @flags: MMU flush flags. Must be one of %PVR_MMU_SYNC_LEVEL_*_FLAGS. + * + * This function must be called following any possible change to the MMU page + * tables. + */ +static void pvr_mmu_set_flush_flags(struct pvr_device *pvr_dev, u32 flags) +{ + atomic_fetch_or(flags, &pvr_dev->mmu_flush_cache_flags); +} + +/** + * pvr_mmu_flush_request_all() - Request flush of all MMU caches when + * subsequently calling pvr_mmu_flush_exec(). + * @pvr_dev: Target PowerVR device. + * + * This function must be called following any possible change to the MMU page + * tables. + */ +void pvr_mmu_flush_request_all(struct pvr_device *pvr_dev) +{ + pvr_mmu_set_flush_flags(pvr_dev, PVR_MMU_SYNC_LEVEL_2_FLAGS); +} + +/** + * pvr_mmu_flush_exec() - Execute a flush of all MMU caches previously + * requested. + * @pvr_dev: Target PowerVR device. + * @wait: Do not return until the flush is completed. + * + * This function must be called prior to submitting any new GPU job. The flush + * will complete before the jobs are scheduled, so this can be called once after + * a series of maps. However, a single unmap should always be immediately + * followed by a flush and it should be explicitly waited by setting @wait. + * + * As a failure to flush the MMU caches could risk memory corruption, if the + * flush fails (implying the firmware is not responding) then the GPU device is + * marked as lost. + * + * Returns: + * * 0 on success when @wait is true, or + * * -%EIO if the device is unavailable, or + * * Any error encountered while submitting the flush command via the KCCB. + */ +int pvr_mmu_flush_exec(struct pvr_device *pvr_dev, bool wait) +{ + struct rogue_fwif_kccb_cmd cmd_mmu_cache = {}; + struct rogue_fwif_mmucachedata *cmd_mmu_cache_data = + &cmd_mmu_cache.cmd_data.mmu_cache_data; + int err = 0; + u32 slot; + int idx; + + if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) + return -EIO; + + /* Can't flush MMU if the firmware hasn't booted yet. */ + if (!pvr_dev->fw_dev.booted) + goto err_drm_dev_exit; + + cmd_mmu_cache_data->cache_flags = + atomic_xchg(&pvr_dev->mmu_flush_cache_flags, 0); + + if (!cmd_mmu_cache_data->cache_flags) + goto err_drm_dev_exit; + + cmd_mmu_cache.cmd_type = ROGUE_FWIF_KCCB_CMD_MMUCACHE; + + pvr_fw_object_get_fw_addr(pvr_dev->fw_dev.mem.mmucache_sync_obj, + &cmd_mmu_cache_data->mmu_cache_sync_fw_addr); + cmd_mmu_cache_data->mmu_cache_sync_update_value = 0; + + err = pvr_kccb_send_cmd(pvr_dev, &cmd_mmu_cache, &slot); + if (err) + goto err_reset_and_retry; + + err = pvr_kccb_wait_for_completion(pvr_dev, slot, HZ, NULL); + if (err) + goto err_reset_and_retry; + + drm_dev_exit(idx); + + return 0; + +err_reset_and_retry: + /* + * Flush command failure is most likely the result of a firmware lockup. Hard + * reset the GPU and retry. + */ + err = pvr_power_reset(pvr_dev, true); + if (err) + goto err_drm_dev_exit; /* Device is lost. */ + + /* Retry sending flush request. */ + err = pvr_kccb_send_cmd(pvr_dev, &cmd_mmu_cache, &slot); + if (err) { + pvr_device_lost(pvr_dev); + goto err_drm_dev_exit; + } + + if (wait) { + err = pvr_kccb_wait_for_completion(pvr_dev, slot, HZ, NULL); + if (err) + pvr_device_lost(pvr_dev); + } + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +/** + * DOC: PowerVR Virtual Memory Handling + */ +/** + * DOC: PowerVR Virtual Memory Handling (constants) + * + * .. c:macro:: PVR_IDX_INVALID + * + * Default value for a u16-based index. + * + * This value cannot be zero, since zero is a valid index value. + */ +#define PVR_IDX_INVALID ((u16)(-1)) + +/** + * DOC: MMU backing pages + */ +/** + * DOC: MMU backing pages (constants) + * + * .. c:macro:: PVR_MMU_BACKING_PAGE_SIZE + * + * Page size of a PowerVR device's integrated MMU. The CPU page size must be + * at least as large as this value for the current implementation; this is + * checked at compile-time. + */ +#define PVR_MMU_BACKING_PAGE_SIZE SZ_4K +static_assert(PAGE_SIZE >= PVR_MMU_BACKING_PAGE_SIZE); + +/** + * struct pvr_mmu_backing_page - Represents a single page used to back a page + * table of any level. + * @dma_addr: DMA address of this page. + * @host_ptr: CPU address of this page. + * @pvr_dev: The PowerVR device to which this page is associated. **For + * internal use only.** + */ +struct pvr_mmu_backing_page { + dma_addr_t dma_addr; + void *host_ptr; +/* private: internal use only */ + struct page *raw_page; + struct pvr_device *pvr_dev; +}; + +/** + * pvr_mmu_backing_page_init() - Initialize a MMU backing page. + * @page: Target backing page. + * @pvr_dev: Target PowerVR device. + * + * This function performs three distinct operations: + * + * 1. Allocate a single page, + * 2. Map the page to the CPU, and + * 3. Map the page to DMA-space. + * + * It is expected that @page be zeroed (e.g. from kzalloc()) before calling + * this function. + * + * Return: + * * 0 on success, or + * * -%ENOMEM if allocation of the backing page or mapping of the backing + * page to DMA fails. + */ +static int +pvr_mmu_backing_page_init(struct pvr_mmu_backing_page *page, + struct pvr_device *pvr_dev) +{ + struct device *dev = from_pvr_device(pvr_dev)->dev; + + struct page *raw_page; + int err; + + dma_addr_t dma_addr; + void *host_ptr; + + raw_page = alloc_page(__GFP_ZERO | GFP_KERNEL); + if (!raw_page) + return -ENOMEM; + + host_ptr = vmap(&raw_page, 1, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); + if (!host_ptr) { + err = -ENOMEM; + goto err_free_page; + } + + dma_addr = dma_map_page(dev, raw_page, 0, PVR_MMU_BACKING_PAGE_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + err = -ENOMEM; + goto err_unmap_page; + } + + page->dma_addr = dma_addr; + page->host_ptr = host_ptr; + page->pvr_dev = pvr_dev; + page->raw_page = raw_page; + kmemleak_alloc(page->host_ptr, PAGE_SIZE, 1, GFP_KERNEL); + + return 0; + +err_unmap_page: + vunmap(host_ptr); + +err_free_page: + __free_page(raw_page); + + return err; +} + +/** + * pvr_mmu_backing_page_fini() - Teardown a MMU backing page. + * @page: Target backing page. + * + * This function performs the mirror operations to pvr_mmu_backing_page_init(), + * in reverse order: + * + * 1. Unmap the page from DMA-space, + * 2. Unmap the page from the CPU, and + * 3. Free the page. + * + * It also zeros @page. + * + * It is a no-op to call this function a second (or further) time on any @page. + */ +static void +pvr_mmu_backing_page_fini(struct pvr_mmu_backing_page *page) +{ + struct device *dev = from_pvr_device(page->pvr_dev)->dev; + + /* Do nothing if no allocation is present. */ + if (!page->pvr_dev) + return; + + dma_unmap_page(dev, page->dma_addr, PVR_MMU_BACKING_PAGE_SIZE, + DMA_TO_DEVICE); + + kmemleak_free(page->host_ptr); + vunmap(page->host_ptr); + + __free_page(page->raw_page); + + memset(page, 0, sizeof(*page)); +} + +/** + * pvr_mmu_backing_page_sync() - Flush a MMU backing page from the CPU to the + * device. + * @page: Target backing page. + * + * .. caution:: + * + * **This is potentially an expensive function call.** Only call + * pvr_mmu_backing_page_sync() once you're sure you have no more changes to + * make to the backing page in the immediate future. + */ +static void +pvr_mmu_backing_page_sync(struct pvr_mmu_backing_page *page, u32 flags) +{ + struct pvr_device *pvr_dev = page->pvr_dev; + struct device *dev; + + /* + * Do nothing if no allocation is present. This may be the case if + * we are unmapping pages. + */ + if (!pvr_dev) + return; + + dev = from_pvr_device(pvr_dev)->dev; + + dma_sync_single_for_device(dev, page->dma_addr, + PVR_MMU_BACKING_PAGE_SIZE, DMA_TO_DEVICE); + + pvr_mmu_set_flush_flags(pvr_dev, flags); +} + +/** + * DOC: Raw page tables + */ + +#define PVR_PAGE_TABLE_TYPEOF_ENTRY(level_) \ + typeof_member(struct pvr_page_table_l##level_##_entry_raw, val) + +#define PVR_PAGE_TABLE_FIELD_GET(level_, name_, field_, entry_) \ + (((entry_).val & \ + ~ROGUE_MMUCTRL_##name_##_DATA_##field_##_CLRMSK) >> \ + ROGUE_MMUCTRL_##name_##_DATA_##field_##_SHIFT) + +#define PVR_PAGE_TABLE_FIELD_PREP(level_, name_, field_, val_) \ + ((((PVR_PAGE_TABLE_TYPEOF_ENTRY(level_))(val_)) \ + << ROGUE_MMUCTRL_##name_##_DATA_##field_##_SHIFT) & \ + ~ROGUE_MMUCTRL_##name_##_DATA_##field_##_CLRMSK) + +/** + * struct pvr_page_table_l2_entry_raw - A single entry in a level 2 page table. + * @val: The raw value of this entry. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %ROGUE_MMUCTRL_ENTRY_SIZE_PC_VALUE. + * + * The value stored in this structure can be decoded using the following bitmap: + * + * .. flat-table:: + * :widths: 1 5 + * :stub-columns: 1 + * + * * - 31..4 + * - **Level 1 Page Table Base Address:** Bits 39..12 of the L1 + * page table base address, which is 4KiB aligned. + * + * * - 3..2 + * - *(reserved)* + * + * * - 1 + * - **Pending:** When valid bit is not set, indicates that a valid + * entry is pending and the MMU should wait for the driver to map + * the entry. This is used to support page demand mapping of + * memory. + * + * * - 0 + * - **Valid:** Indicates that the entry contains a valid L1 page + * table. If the valid bit is not set, then an attempted use of + * the page would result in a page fault. + */ +struct pvr_page_table_l2_entry_raw { + u32 val; +} __packed; +static_assert(sizeof(struct pvr_page_table_l2_entry_raw) * 8 == + ROGUE_MMUCTRL_ENTRY_SIZE_PC_VALUE); + +static bool +pvr_page_table_l2_entry_raw_is_valid(struct pvr_page_table_l2_entry_raw entry) +{ + return PVR_PAGE_TABLE_FIELD_GET(2, PC, VALID, entry); +} + +/** + * pvr_page_table_l2_entry_raw_set() - Write a valid entry into a raw level 2 + * page table. + * @entry: Target raw level 2 page table entry. + * @child_table_dma_addr: DMA address of the level 1 page table to be + * associated with @entry. + * + * When calling this function, @child_table_dma_addr must be a valid DMA + * address and a multiple of %ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSIZE. + */ +static void +pvr_page_table_l2_entry_raw_set(struct pvr_page_table_l2_entry_raw *entry, + dma_addr_t child_table_dma_addr) +{ + child_table_dma_addr >>= ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSHIFT; + + WRITE_ONCE(entry->val, + PVR_PAGE_TABLE_FIELD_PREP(2, PC, VALID, true) | + PVR_PAGE_TABLE_FIELD_PREP(2, PC, ENTRY_PENDING, false) | + PVR_PAGE_TABLE_FIELD_PREP(2, PC, PD_BASE, child_table_dma_addr)); +} + +static void +pvr_page_table_l2_entry_raw_clear(struct pvr_page_table_l2_entry_raw *entry) +{ + WRITE_ONCE(entry->val, 0); +} + +/** + * struct pvr_page_table_l1_entry_raw - A single entry in a level 1 page table. + * @val: The raw value of this entry. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %ROGUE_MMUCTRL_ENTRY_SIZE_PD_VALUE. + * + * The value stored in this structure can be decoded using the following bitmap: + * + * .. flat-table:: + * :widths: 1 5 + * :stub-columns: 1 + * + * * - 63..41 + * - *(reserved)* + * + * * - 40 + * - **Pending:** When valid bit is not set, indicates that a valid entry + * is pending and the MMU should wait for the driver to map the entry. + * This is used to support page demand mapping of memory. + * + * * - 39..5 + * - **Level 0 Page Table Base Address:** The way this value is + * interpreted depends on the page size. Bits not specified in the + * table below (e.g. bits 11..5 for page size 4KiB) should be + * considered reserved. + * + * This table shows the bits used in an L1 page table entry to + * represent the Physical Table Base Address for a given Page Size. + * Since each L1 page table entry covers 2MiB of address space, the + * maximum page size is 2MiB. + * + * .. flat-table:: + * :widths: 1 1 1 1 + * :header-rows: 1 + * :stub-columns: 1 + * + * * - Page size + * - L0 page table base address bits + * - Number of L0 page table entries + * - Size of L0 page table + * + * * - 4KiB + * - 39..12 + * - 512 + * - 4KiB + * + * * - 16KiB + * - 39..10 + * - 128 + * - 1KiB + * + * * - 64KiB + * - 39..8 + * - 32 + * - 256B + * + * * - 256KiB + * - 39..6 + * - 8 + * - 64B + * + * * - 1MiB + * - 39..5 (4 = '0') + * - 2 + * - 16B + * + * * - 2MiB + * - 39..5 (4..3 = '00') + * - 1 + * - 8B + * + * * - 4 + * - *(reserved)* + * + * * - 3..1 + * - **Page Size:** Sets the page size, from 4KiB to 2MiB. + * + * * - 0 + * - **Valid:** Indicates that the entry contains a valid L0 page table. + * If the valid bit is not set, then an attempted use of the page would + * result in a page fault. + */ +struct pvr_page_table_l1_entry_raw { + u64 val; +} __packed; +static_assert(sizeof(struct pvr_page_table_l1_entry_raw) * 8 == + ROGUE_MMUCTRL_ENTRY_SIZE_PD_VALUE); + +static bool +pvr_page_table_l1_entry_raw_is_valid(struct pvr_page_table_l1_entry_raw entry) +{ + return PVR_PAGE_TABLE_FIELD_GET(1, PD, VALID, entry); +} + +/** + * pvr_page_table_l1_entry_raw_set() - Write a valid entry into a raw level 1 + * page table. + * @entry: Target raw level 1 page table entry. + * @child_table_dma_addr: DMA address of the level 0 page table to be + * associated with @entry. + * + * When calling this function, @child_table_dma_addr must be a valid DMA + * address and a multiple of 4 KiB. + */ +static void +pvr_page_table_l1_entry_raw_set(struct pvr_page_table_l1_entry_raw *entry, + dma_addr_t child_table_dma_addr) +{ + WRITE_ONCE(entry->val, + PVR_PAGE_TABLE_FIELD_PREP(1, PD, VALID, true) | + PVR_PAGE_TABLE_FIELD_PREP(1, PD, ENTRY_PENDING, false) | + PVR_PAGE_TABLE_FIELD_PREP(1, PD, PAGE_SIZE, ROGUE_MMUCTRL_PAGE_SIZE_X) | + /* + * The use of a 4K-specific macro here is correct. It is + * a future optimization to allocate sub-host-page-sized + * blocks for individual tables, so the condition that any + * page table address is aligned to the size of the + * largest (a 4KB) table currently holds. + */ + (child_table_dma_addr & ~ROGUE_MMUCTRL_PT_BASE_4KB_RANGE_CLRMSK)); +} + +static void +pvr_page_table_l1_entry_raw_clear(struct pvr_page_table_l1_entry_raw *entry) +{ + WRITE_ONCE(entry->val, 0); +} + +/** + * struct pvr_page_table_l0_entry_raw - A single entry in a level 0 page table. + * @val: The raw value of this entry. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %ROGUE_MMUCTRL_ENTRY_SIZE_PT_VALUE. + * + * The value stored in this structure can be decoded using the following bitmap: + * + * .. flat-table:: + * :widths: 1 5 + * :stub-columns: 1 + * + * * - 63 + * - *(reserved)* + * + * * - 62 + * - **PM/FW Protect:** Indicates a protected region which only the + * Parameter Manager (PM) or firmware processor can write to. + * + * * - 61..40 + * - **VP Page (High):** Virtual-physical page used for Parameter Manager + * (PM) memory. This field is only used if the additional level of PB + * virtualization is enabled. The VP Page field is needed by the PM in + * order to correctly reconstitute the free lists after render + * completion. This (High) field holds bits 39..18 of the value; the + * Low field holds bits 17..12. Bits 11..0 are always zero because the + * value is always aligned to the 4KiB page size. + * + * * - 39..12 + * - **Physical Page Address:** The way this value is interpreted depends + * on the page size. Bits not specified in the table below (e.g. bits + * 20..12 for page size 2MiB) should be considered reserved. + * + * This table shows the bits used in an L0 page table entry to represent + * the Physical Page Address for a given page size (as defined in the + * associated L1 page table entry). + * + * .. flat-table:: + * :widths: 1 1 + * :header-rows: 1 + * :stub-columns: 1 + * + * * - Page size + * - Physical address bits + * + * * - 4KiB + * - 39..12 + * + * * - 16KiB + * - 39..14 + * + * * - 64KiB + * - 39..16 + * + * * - 256KiB + * - 39..18 + * + * * - 1MiB + * - 39..20 + * + * * - 2MiB + * - 39..21 + * + * * - 11..6 + * - **VP Page (Low):** Continuation of VP Page (High). + * + * * - 5 + * - **Pending:** When valid bit is not set, indicates that a valid entry + * is pending and the MMU should wait for the driver to map the entry. + * This is used to support page demand mapping of memory. + * + * * - 4 + * - **PM Src:** Set on Parameter Manager (PM) allocated page table + * entries when indicated by the PM. Note that this bit will only be set + * by the PM, not by the device driver. + * + * * - 3 + * - **SLC Bypass Control:** Specifies requests to this page should bypass + * the System Level Cache (SLC), if enabled in SLC configuration. + * + * * - 2 + * - **Cache Coherency:** Indicates that the page is coherent (i.e. it + * does not require a cache flush between operations on the CPU and the + * device). + * + * * - 1 + * - **Read Only:** If set, this bit indicates that the page is read only. + * An attempted write to this page would result in a write-protection + * fault. + * + * * - 0 + * - **Valid:** Indicates that the entry contains a valid page. If the + * valid bit is not set, then an attempted use of the page would result + * in a page fault. + */ +struct pvr_page_table_l0_entry_raw { + u64 val; +} __packed; +static_assert(sizeof(struct pvr_page_table_l0_entry_raw) * 8 == + ROGUE_MMUCTRL_ENTRY_SIZE_PT_VALUE); + +/** + * struct pvr_page_flags_raw - The configurable flags from a single entry in a + * level 0 page table. + * @val: The raw value of these flags. Since these are a strict subset of + * &struct pvr_page_table_l0_entry_raw; use that type for our member here. + * + * The flags stored in this type are: PM/FW Protect; SLC Bypass Control; Cache + * Coherency, and Read Only (bits 62, 3, 2 and 1 respectively). + * + * This type should never be instantiated directly; instead use + * pvr_page_flags_raw_create() to ensure only valid bits of @val are set. + */ +struct pvr_page_flags_raw { + struct pvr_page_table_l0_entry_raw val; +} __packed; +static_assert(sizeof(struct pvr_page_flags_raw) == + sizeof(struct pvr_page_table_l0_entry_raw)); + +static bool +pvr_page_table_l0_entry_raw_is_valid(struct pvr_page_table_l0_entry_raw entry) +{ + return PVR_PAGE_TABLE_FIELD_GET(0, PT, VALID, entry); +} + +/** + * pvr_page_table_l0_entry_raw_set() - Write a valid entry into a raw level 0 + * page table. + * @entry: Target raw level 0 page table entry. + * @dma_addr: DMA address of the physical page to be associated with @entry. + * @flags: Options to be set on @entry. + * + * When calling this function, @child_table_dma_addr must be a valid DMA + * address and a multiple of %PVR_DEVICE_PAGE_SIZE. + * + * The @flags parameter is directly assigned into @entry. It is the callers + * responsibility to ensure that only bits specified in + * &struct pvr_page_flags_raw are set in @flags. + */ +static void +pvr_page_table_l0_entry_raw_set(struct pvr_page_table_l0_entry_raw *entry, + dma_addr_t dma_addr, + struct pvr_page_flags_raw flags) +{ + WRITE_ONCE(entry->val, PVR_PAGE_TABLE_FIELD_PREP(0, PT, VALID, true) | + PVR_PAGE_TABLE_FIELD_PREP(0, PT, ENTRY_PENDING, false) | + (dma_addr & ~ROGUE_MMUCTRL_PAGE_X_RANGE_CLRMSK) | + flags.val.val); +} + +static void +pvr_page_table_l0_entry_raw_clear(struct pvr_page_table_l0_entry_raw *entry) +{ + WRITE_ONCE(entry->val, 0); +} + +/** + * pvr_page_flags_raw_create() - Initialize the flag bits of a raw level 0 page + * table entry. + * @read_only: This page is read-only (see: Read Only). + * @cache_coherent: This page does not require cache flushes (see: Cache + * Coherency). + * @slc_bypass: This page bypasses the device cache (see: SLC Bypass Control). + * @pm_fw_protect: This page is only for use by the firmware or Parameter + * Manager (see PM/FW Protect). + * + * For more details on the use of these four options, see their respective + * entries in the table under &struct pvr_page_table_l0_entry_raw. + * + * Return: + * A new &struct pvr_page_flags_raw instance which can be passed directly to + * pvr_page_table_l0_entry_raw_set() or pvr_page_table_l0_insert(). + */ +static struct pvr_page_flags_raw +pvr_page_flags_raw_create(bool read_only, bool cache_coherent, bool slc_bypass, + bool pm_fw_protect) +{ + struct pvr_page_flags_raw flags; + + flags.val.val = + PVR_PAGE_TABLE_FIELD_PREP(0, PT, READ_ONLY, read_only) | + PVR_PAGE_TABLE_FIELD_PREP(0, PT, CC, cache_coherent) | + PVR_PAGE_TABLE_FIELD_PREP(0, PT, SLC_BYPASS_CTRL, slc_bypass) | + PVR_PAGE_TABLE_FIELD_PREP(0, PT, PM_META_PROTECT, pm_fw_protect); + + return flags; +} + +/** + * struct pvr_page_table_l2_raw - The raw data of a level 2 page table. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %PVR_MMU_BACKING_PAGE_SIZE. + */ +struct pvr_page_table_l2_raw { + /** @entries: The raw values of this table. */ + struct pvr_page_table_l2_entry_raw + entries[ROGUE_MMUCTRL_ENTRIES_PC_VALUE]; +} __packed; +static_assert(sizeof(struct pvr_page_table_l2_raw) == PVR_MMU_BACKING_PAGE_SIZE); + +/** + * struct pvr_page_table_l1_raw - The raw data of a level 1 page table. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %PVR_MMU_BACKING_PAGE_SIZE. + */ +struct pvr_page_table_l1_raw { + /** @entries: The raw values of this table. */ + struct pvr_page_table_l1_entry_raw + entries[ROGUE_MMUCTRL_ENTRIES_PD_VALUE]; +} __packed; +static_assert(sizeof(struct pvr_page_table_l1_raw) == PVR_MMU_BACKING_PAGE_SIZE); + +/** + * struct pvr_page_table_l0_raw - The raw data of a level 0 page table. + * + * This type is a structure for type-checking purposes. At compile-time, its + * size is checked against %PVR_MMU_BACKING_PAGE_SIZE. + * + * .. caution:: + * + * The size of level 0 page tables is variable depending on the page size + * specified in the associated level 1 page table entry. Since the device + * page size in use is pegged to the host page size, it cannot vary at + * runtime. This structure is therefore only defined to contain the required + * number of entries for the current device page size. **You should never + * read or write beyond the last supported entry.** + */ +struct pvr_page_table_l0_raw { + /** @entries: The raw values of this table. */ + struct pvr_page_table_l0_entry_raw + entries[ROGUE_MMUCTRL_ENTRIES_PT_VALUE_X]; +} __packed; +static_assert(sizeof(struct pvr_page_table_l0_raw) <= PVR_MMU_BACKING_PAGE_SIZE); + +/** + * DOC: Mirror page tables + */ + +/* + * We pre-declare these types because they cross-depend on pointers to each + * other. + */ +struct pvr_page_table_l1; +struct pvr_page_table_l0; + +/** + * struct pvr_page_table_l2 - A wrapped level 2 page table. + * + * To access the raw part of this table, use pvr_page_table_l2_get_raw(). + * Alternatively to access a raw entry directly, use + * pvr_page_table_l2_get_entry_raw(). + * + * A level 2 page table forms the root of the page table tree structure, so + * this type has no &parent or &parent_idx members. + */ +struct pvr_page_table_l2 { + /** + * @entries: The children of this node in the page table tree + * structure. These are also mirror tables. The indexing of this array + * is identical to that of the raw equivalent + * (&pvr_page_table_l1_raw.entries). + */ + struct pvr_page_table_l1 *entries[ROGUE_MMUCTRL_ENTRIES_PC_VALUE]; + + /** + * @backing_page: A handle to the memory which holds the raw + * equivalent of this table. **For internal use only.** + */ + struct pvr_mmu_backing_page backing_page; + + /** + * @entry_count: The current number of valid entries (that we know of) + * in this table. This value is essentially a refcount - the table is + * destroyed when this value is decremented to zero by + * pvr_page_table_l2_remove(). + */ + u16 entry_count; +}; + +/** + * pvr_page_table_l2_init() - Initialize a level 2 page table. + * @table: Target level 2 page table. + * @pvr_dev: Target PowerVR device + * + * It is expected that @table be zeroed (e.g. from kzalloc()) before calling + * this function. + * + * Return: + * * 0 on success, or + * * Any error encountered while intializing &table->backing_page using + * pvr_mmu_backing_page_init(). + */ +static int +pvr_page_table_l2_init(struct pvr_page_table_l2 *table, + struct pvr_device *pvr_dev) +{ + return pvr_mmu_backing_page_init(&table->backing_page, pvr_dev); +} + +/** + * pvr_page_table_l2_fini() - Teardown a level 2 page table. + * @table: Target level 2 page table. + * + * It is an error to attempt to use @table after calling this function. + */ +static void +pvr_page_table_l2_fini(struct pvr_page_table_l2 *table) +{ + pvr_mmu_backing_page_fini(&table->backing_page); +} + +/** + * pvr_page_table_l2_sync() - Flush a level 2 page table from the CPU to the + * device. + * @table: Target level 2 page table. + * + * This is just a thin wrapper around pvr_mmu_backing_page_sync(), so the + * warning there applies here too: **Only call pvr_page_table_l2_sync() once + * you're sure you have no more changes to make to** @table **in the immediate + * future.** + * + * If child level 1 page tables of @table also need to be flushed, this should + * be done first using pvr_page_table_l1_sync() *before* calling this function. + */ +static void +pvr_page_table_l2_sync(struct pvr_page_table_l2 *table) +{ + pvr_mmu_backing_page_sync(&table->backing_page, PVR_MMU_SYNC_LEVEL_2_FLAGS); +} + +/** + * pvr_page_table_l2_get_raw() - Access the raw equivalent of a mirror level 2 + * page table. + * @table: Target level 2 page table. + * + * Essentially returns the CPU address of the raw equivalent of @table, cast to + * a &struct pvr_page_table_l2_raw pointer. + * + * You probably want to call pvr_page_table_l2_get_entry_raw() instead. + * + * Return: + * The raw equivalent of @table. + */ +static struct pvr_page_table_l2_raw * +pvr_page_table_l2_get_raw(struct pvr_page_table_l2 *table) +{ + return table->backing_page.host_ptr; +} + +/** + * pvr_page_table_l2_get_entry_raw() - Access an entry from the raw equivalent + * of a mirror level 2 page table. + * @table: Target level 2 page table. + * @idx: Index of the entry to access. + * + * Technically this function returns a pointer to a slot in a raw level 2 page + * table, since the returned "entry" is not guaranteed to be valid. The caller + * must verify the validity of the entry at the returned address (perhaps using + * pvr_page_table_l2_entry_raw_is_valid()) before reading or overwriting it. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before dereferencing the + * returned pointer. + * + * Return: + * A pointer to the requested raw level 2 page table entry. + */ +static struct pvr_page_table_l2_entry_raw * +pvr_page_table_l2_get_entry_raw(struct pvr_page_table_l2 *table, u16 idx) +{ + return &pvr_page_table_l2_get_raw(table)->entries[idx]; +} + +/** + * pvr_page_table_l2_entry_is_valid() - Check if a level 2 page table entry is + * marked as valid. + * @table: Target level 2 page table. + * @idx: Index of the entry to check. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before calling this + * function. + */ +static bool +pvr_page_table_l2_entry_is_valid(struct pvr_page_table_l2 *table, u16 idx) +{ + struct pvr_page_table_l2_entry_raw entry_raw = + *pvr_page_table_l2_get_entry_raw(table, idx); + + return pvr_page_table_l2_entry_raw_is_valid(entry_raw); +} + +/** + * struct pvr_page_table_l1 - A wrapped level 1 page table. + * + * To access the raw part of this table, use pvr_page_table_l1_get_raw(). + * Alternatively to access a raw entry directly, use + * pvr_page_table_l1_get_entry_raw(). + */ +struct pvr_page_table_l1 { + /** + * @entries: The children of this node in the page table tree + * structure. These are also mirror tables. The indexing of this array + * is identical to that of the raw equivalent + * (&pvr_page_table_l0_raw.entries). + */ + struct pvr_page_table_l0 *entries[ROGUE_MMUCTRL_ENTRIES_PD_VALUE]; + + /** + * @backing_page: A handle to the memory which holds the raw + * equivalent of this table. **For internal use only.** + */ + struct pvr_mmu_backing_page backing_page; + + union { + /** + * @parent: The parent of this node in the page table tree structure. + * + * This is also a mirror table. + * + * Only valid when the L1 page table is active. When the L1 page table + * has been removed and queued for destruction, the next_free field + * should be used instead. + */ + struct pvr_page_table_l2 *parent; + + /** + * @next_free: Pointer to the next L1 page table to take/free. + * + * Used to form a linked list of L1 page tables. This is used + * when preallocating tables and when the page table has been + * removed and queued for destruction. + */ + struct pvr_page_table_l1 *next_free; + }; + + /** + * @parent_idx: The index of the entry in the parent table (see + * @parent) which corresponds to this table. + */ + u16 parent_idx; + + /** + * @entry_count: The current number of valid entries (that we know of) + * in this table. This value is essentially a refcount - the table is + * destroyed when this value is decremented to zero by + * pvr_page_table_l1_remove(). + */ + u16 entry_count; +}; + +/** + * pvr_page_table_l1_init() - Initialize a level 1 page table. + * @table: Target level 1 page table. + * @pvr_dev: Target PowerVR device + * + * When this function returns successfully, @table is still not considered + * valid. It must be inserted into the page table tree structure with + * pvr_page_table_l2_insert() before it is ready for use. + * + * It is expected that @table be zeroed (e.g. from kzalloc()) before calling + * this function. + * + * Return: + * * 0 on success, or + * * Any error encountered while intializing &table->backing_page using + * pvr_mmu_backing_page_init(). + */ +static int +pvr_page_table_l1_init(struct pvr_page_table_l1 *table, + struct pvr_device *pvr_dev) +{ + table->parent_idx = PVR_IDX_INVALID; + + return pvr_mmu_backing_page_init(&table->backing_page, pvr_dev); +} + +/** + * pvr_page_table_l1_free() - Teardown a level 1 page table. + * @table: Target level 1 page table. + * + * It is an error to attempt to use @table after calling this function, even + * indirectly. This includes calling pvr_page_table_l2_remove(), which must + * be called *before* pvr_page_table_l1_free(). + */ +static void +pvr_page_table_l1_free(struct pvr_page_table_l1 *table) +{ + pvr_mmu_backing_page_fini(&table->backing_page); + kfree(table); +} + +/** + * pvr_page_table_l1_sync() - Flush a level 1 page table from the CPU to the + * device. + * @table: Target level 1 page table. + * + * This is just a thin wrapper around pvr_mmu_backing_page_sync(), so the + * warning there applies here too: **Only call pvr_page_table_l1_sync() once + * you're sure you have no more changes to make to** @table **in the immediate + * future.** + * + * If child level 0 page tables of @table also need to be flushed, this should + * be done first using pvr_page_table_l0_sync() *before* calling this function. + */ +static void +pvr_page_table_l1_sync(struct pvr_page_table_l1 *table) +{ + pvr_mmu_backing_page_sync(&table->backing_page, PVR_MMU_SYNC_LEVEL_1_FLAGS); +} + +/** + * pvr_page_table_l1_get_raw() - Access the raw equivalent of a mirror level 1 + * page table. + * @table: Target level 1 page table. + * + * Essentially returns the CPU address of the raw equivalent of @table, cast to + * a &struct pvr_page_table_l1_raw pointer. + * + * You probably want to call pvr_page_table_l1_get_entry_raw() instead. + * + * Return: + * The raw equivalent of @table. + */ +static struct pvr_page_table_l1_raw * +pvr_page_table_l1_get_raw(struct pvr_page_table_l1 *table) +{ + return table->backing_page.host_ptr; +} + +/** + * pvr_page_table_l1_get_entry_raw() - Access an entry from the raw equivalent + * of a mirror level 1 page table. + * @table: Target level 1 page table. + * @idx: Index of the entry to access. + * + * Technically this function returns a pointer to a slot in a raw level 1 page + * table, since the returned "entry" is not guaranteed to be valid. The caller + * must verify the validity of the entry at the returned address (perhaps using + * pvr_page_table_l1_entry_raw_is_valid()) before reading or overwriting it. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before dereferencing the + * returned pointer. + * + * Return: + * A pointer to the requested raw level 1 page table entry. + */ +static struct pvr_page_table_l1_entry_raw * +pvr_page_table_l1_get_entry_raw(struct pvr_page_table_l1 *table, u16 idx) +{ + return &pvr_page_table_l1_get_raw(table)->entries[idx]; +} + +/** + * pvr_page_table_l1_entry_is_valid() - Check if a level 1 page table entry is + * marked as valid. + * @table: Target level 1 page table. + * @idx: Index of the entry to check. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before calling this + * function. + */ +static bool +pvr_page_table_l1_entry_is_valid(struct pvr_page_table_l1 *table, u16 idx) +{ + struct pvr_page_table_l1_entry_raw entry_raw = + *pvr_page_table_l1_get_entry_raw(table, idx); + + return pvr_page_table_l1_entry_raw_is_valid(entry_raw); +} + +/** + * struct pvr_page_table_l0 - A wrapped level 0 page table. + * + * To access the raw part of this table, use pvr_page_table_l0_get_raw(). + * Alternatively to access a raw entry directly, use + * pvr_page_table_l0_get_entry_raw(). + * + * There is no mirror representation of an individual page, so this type has no + * &entries member. + */ +struct pvr_page_table_l0 { + /** + * @backing_page: A handle to the memory which holds the raw + * equivalent of this table. **For internal use only.** + */ + struct pvr_mmu_backing_page backing_page; + + union { + /** + * @parent: The parent of this node in the page table tree structure. + * + * This is also a mirror table. + * + * Only valid when the L0 page table is active. When the L0 page table + * has been removed and queued for destruction, the next_free field + * should be used instead. + */ + struct pvr_page_table_l1 *parent; + + /** + * @next_free: Pointer to the next L0 page table to take/free. + * + * Used to form a linked list of L0 page tables. This is used + * when preallocating tables and when the page table has been + * removed and queued for destruction. + */ + struct pvr_page_table_l0 *next_free; + }; + + /** + * @parent_idx: The index of the entry in the parent table (see + * @parent) which corresponds to this table. + */ + u16 parent_idx; + + /** + * @entry_count: The current number of valid entries (that we know of) + * in this table. This value is essentially a refcount - the table is + * destroyed when this value is decremented to zero by + * pvr_page_table_l0_remove(). + */ + u16 entry_count; +}; + +/** + * pvr_page_table_l0_init() - Initialize a level 0 page table. + * @table: Target level 0 page table. + * @pvr_dev: Target PowerVR device + * + * When this function returns successfully, @table is still not considered + * valid. It must be inserted into the page table tree structure with + * pvr_page_table_l1_insert() before it is ready for use. + * + * It is expected that @table be zeroed (e.g. from kzalloc()) before calling + * this function. + * + * Return: + * * 0 on success, or + * * Any error encountered while intializing &table->backing_page using + * pvr_mmu_backing_page_init(). + */ +static int +pvr_page_table_l0_init(struct pvr_page_table_l0 *table, + struct pvr_device *pvr_dev) +{ + table->parent_idx = PVR_IDX_INVALID; + + return pvr_mmu_backing_page_init(&table->backing_page, pvr_dev); +} + +/** + * pvr_page_table_l0_free() - Teardown a level 0 page table. + * @table: Target level 0 page table. + * + * It is an error to attempt to use @table after calling this function, even + * indirectly. This includes calling pvr_page_table_l1_remove(), which must + * be called *before* pvr_page_table_l0_free(). + */ +static void +pvr_page_table_l0_free(struct pvr_page_table_l0 *table) +{ + pvr_mmu_backing_page_fini(&table->backing_page); + kfree(table); +} + +/** + * pvr_page_table_l0_sync() - Flush a level 0 page table from the CPU to the + * device. + * @table: Target level 0 page table. + * + * This is just a thin wrapper around pvr_mmu_backing_page_sync(), so the + * warning there applies here too: **Only call pvr_page_table_l0_sync() once + * you're sure you have no more changes to make to** @table **in the immediate + * future.** + * + * If child pages of @table also need to be flushed, this should be done first + * using a DMA sync function (e.g. dma_sync_sg_for_device()) *before* calling + * this function. + */ +static void +pvr_page_table_l0_sync(struct pvr_page_table_l0 *table) +{ + pvr_mmu_backing_page_sync(&table->backing_page, PVR_MMU_SYNC_LEVEL_0_FLAGS); +} + +/** + * pvr_page_table_l0_get_raw() - Access the raw equivalent of a mirror level 0 + * page table. + * @table: Target level 0 page table. + * + * Essentially returns the CPU address of the raw equivalent of @table, cast to + * a &struct pvr_page_table_l0_raw pointer. + * + * You probably want to call pvr_page_table_l0_get_entry_raw() instead. + * + * Return: + * The raw equivalent of @table. + */ +static struct pvr_page_table_l0_raw * +pvr_page_table_l0_get_raw(struct pvr_page_table_l0 *table) +{ + return table->backing_page.host_ptr; +} + +/** + * pvr_page_table_l0_get_entry_raw() - Access an entry from the raw equivalent + * of a mirror level 0 page table. + * @table: Target level 0 page table. + * @idx: Index of the entry to access. + * + * Technically this function returns a pointer to a slot in a raw level 0 page + * table, since the returned "entry" is not guaranteed to be valid. The caller + * must verify the validity of the entry at the returned address (perhaps using + * pvr_page_table_l0_entry_raw_is_valid()) before reading or overwriting it. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before dereferencing the + * returned pointer. This is espcially important for level 0 page tables, which + * can have a variable number of entries. + * + * Return: + * A pointer to the requested raw level 0 page table entry. + */ +static struct pvr_page_table_l0_entry_raw * +pvr_page_table_l0_get_entry_raw(struct pvr_page_table_l0 *table, u16 idx) +{ + return &pvr_page_table_l0_get_raw(table)->entries[idx]; +} + +/** + * pvr_page_table_l0_entry_is_valid() - Check if a level 0 page table entry is + * marked as valid. + * @table: Target level 0 page table. + * @idx: Index of the entry to check. + * + * The value of @idx is not checked here; it is the callers responsibility to + * ensure @idx refers to a valid index within @table before calling this + * function. + */ +static bool +pvr_page_table_l0_entry_is_valid(struct pvr_page_table_l0 *table, u16 idx) +{ + struct pvr_page_table_l0_entry_raw entry_raw = + *pvr_page_table_l0_get_entry_raw(table, idx); + + return pvr_page_table_l0_entry_raw_is_valid(entry_raw); +} + +/** + * struct pvr_mmu_context - context holding data for operations at page + * catalogue level, intended for use with a VM context. + */ +struct pvr_mmu_context { + /** @pvr_dev: The PVR device associated with the owning VM context. */ + struct pvr_device *pvr_dev; + + /** @page_table_l2: The MMU table root. */ + struct pvr_page_table_l2 page_table_l2; +}; + +/** + * struct pvr_page_table_ptr - A reference to a single physical page as indexed + * by the page table structure. + * + * Intended for embedding in a &struct pvr_mmu_op_context. + */ +struct pvr_page_table_ptr { + /** + * @l1_table: A cached handle to the level 1 page table the + * context is currently traversing. + */ + struct pvr_page_table_l1 *l1_table; + + /** + * @l0_table: A cached handle to the level 0 page table the + * context is currently traversing. + */ + struct pvr_page_table_l0 *l0_table; + + /** + * @l2_idx: Index into the level 2 page table the context is + * currently referencing. + */ + u16 l2_idx; + + /** + * @l1_idx: Index into the level 1 page table the context is + * currently referencing. + */ + u16 l1_idx; + + /** + * @l0_idx: Index into the level 0 page table the context is + * currently referencing. + */ + u16 l0_idx; +}; + +/** + * struct pvr_mmu_op_context - context holding data for individual + * device-virtual mapping operations. Intended for use with a VM bind operation. + */ +struct pvr_mmu_op_context { + /** @mmu_ctx: The MMU context associated with the owning VM context. */ + struct pvr_mmu_context *mmu_ctx; + + /** @map: Data specifically for map operations. */ + struct { + /** + * @sgt: Scatter gather table containing pages pinned for use by + * this context - these are currently pinned when initialising + * the VM bind operation. + */ + struct sg_table *sgt; + + /** @sgt_offset: Start address of the device-virtual mapping. */ + u64 sgt_offset; + + /** + * @l1_prealloc_tables: Preallocated l1 page table objects + * use by this context when creating a page mapping. Linked list + * fully created during initialisation. + */ + struct pvr_page_table_l1 *l1_prealloc_tables; + + /** + * @l0_prealloc_tables: Preallocated l0 page table objects + * use by this context when creating a page mapping. Linked list + * fully created during initialisation. + */ + struct pvr_page_table_l0 *l0_prealloc_tables; + } map; + + /** @unmap: Data specifically for unmap operations. */ + struct { + /** + * @l1_free_tables: Collects page table objects freed by unmap + * ops. Linked list empty at creation. + */ + struct pvr_page_table_l1 *l1_free_tables; + + /** + * @l0_free_tables: Collects page table objects freed by unmap + * ops. Linked list empty at creation. + */ + struct pvr_page_table_l0 *l0_free_tables; + } unmap; + + /** + * @curr_page: A reference to a single physical page as indexed by the + * page table structure. + */ + struct pvr_page_table_ptr curr_page; + + /** + * @sync_level_required: The maximum level of the page table tree + * structure which has (possibly) been modified since it was last + * flushed to the device. + * + * This field should only be set with pvr_mmu_op_context_require_sync() + * or indirectly by pvr_mmu_op_context_sync_partial(). + */ + enum pvr_mmu_sync_level sync_level_required; +}; + +/** + * pvr_page_table_l2_insert() - Insert an entry referring to a level 1 page + * table into a level 2 page table. + * @op_ctx: Target MMU op context pointing at the entry to insert the L1 page + * table into. + * @child_table: Target level 1 page table to be referenced by the new entry. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L2 entry. + * + * It is the caller's responsibility to execute any memory barries to ensure + * that the creation of @child_table is ordered before the L2 entry is inserted. + */ +static void +pvr_page_table_l2_insert(struct pvr_mmu_op_context *op_ctx, + struct pvr_page_table_l1 *child_table) +{ + struct pvr_page_table_l2 *l2_table = + &op_ctx->mmu_ctx->page_table_l2; + struct pvr_page_table_l2_entry_raw *entry_raw = + pvr_page_table_l2_get_entry_raw(l2_table, + op_ctx->curr_page.l2_idx); + + pvr_page_table_l2_entry_raw_set(entry_raw, + child_table->backing_page.dma_addr); + + child_table->parent = l2_table; + child_table->parent_idx = op_ctx->curr_page.l2_idx; + l2_table->entries[op_ctx->curr_page.l2_idx] = child_table; + ++l2_table->entry_count; + op_ctx->curr_page.l1_table = child_table; +} + +/** + * pvr_page_table_l2_remove() - Remove a level 1 page table from a level 2 page + * table. + * @op_ctx: Target MMU op context pointing at the L2 entry to remove. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L2 entry. + */ +static void +pvr_page_table_l2_remove(struct pvr_mmu_op_context *op_ctx) +{ + struct pvr_page_table_l2 *l2_table = + &op_ctx->mmu_ctx->page_table_l2; + struct pvr_page_table_l2_entry_raw *entry_raw = + pvr_page_table_l2_get_entry_raw(l2_table, + op_ctx->curr_page.l1_table->parent_idx); + + WARN_ON(op_ctx->curr_page.l1_table->parent != l2_table); + + pvr_page_table_l2_entry_raw_clear(entry_raw); + + l2_table->entries[op_ctx->curr_page.l1_table->parent_idx] = NULL; + op_ctx->curr_page.l1_table->parent_idx = PVR_IDX_INVALID; + op_ctx->curr_page.l1_table->next_free = op_ctx->unmap.l1_free_tables; + op_ctx->unmap.l1_free_tables = op_ctx->curr_page.l1_table; + op_ctx->curr_page.l1_table = NULL; + + --l2_table->entry_count; +} + +/** + * pvr_page_table_l1_insert() - Insert an entry referring to a level 0 page + * table into a level 1 page table. + * @op_ctx: Target MMU op context pointing at the entry to insert the L0 page + * table into. + * @child_table: L0 page table to insert. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L1 entry. + * + * It is the caller's responsibility to execute any memory barries to ensure + * that the creation of @child_table is ordered before the L1 entry is inserted. + */ +static void +pvr_page_table_l1_insert(struct pvr_mmu_op_context *op_ctx, + struct pvr_page_table_l0 *child_table) +{ + struct pvr_page_table_l1_entry_raw *entry_raw = + pvr_page_table_l1_get_entry_raw(op_ctx->curr_page.l1_table, + op_ctx->curr_page.l1_idx); + + pvr_page_table_l1_entry_raw_set(entry_raw, + child_table->backing_page.dma_addr); + + child_table->parent = op_ctx->curr_page.l1_table; + child_table->parent_idx = op_ctx->curr_page.l1_idx; + op_ctx->curr_page.l1_table->entries[op_ctx->curr_page.l1_idx] = child_table; + ++op_ctx->curr_page.l1_table->entry_count; + op_ctx->curr_page.l0_table = child_table; +} + +/** + * pvr_page_table_l1_remove() - Remove a level 0 page table from a level 1 page + * table. + * @op_ctx: Target MMU op context pointing at the L1 entry to remove. + * + * If this function results in the L1 table becoming empty, it will be removed + * from its parent level 2 page table and destroyed. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L1 entry. + */ +static void +pvr_page_table_l1_remove(struct pvr_mmu_op_context *op_ctx) +{ + struct pvr_page_table_l1_entry_raw *entry_raw = + pvr_page_table_l1_get_entry_raw(op_ctx->curr_page.l0_table->parent, + op_ctx->curr_page.l0_table->parent_idx); + + WARN_ON(op_ctx->curr_page.l0_table->parent != + op_ctx->curr_page.l1_table); + + pvr_page_table_l1_entry_raw_clear(entry_raw); + + op_ctx->curr_page.l1_table->entries[op_ctx->curr_page.l0_table->parent_idx] = NULL; + op_ctx->curr_page.l0_table->parent_idx = PVR_IDX_INVALID; + op_ctx->curr_page.l0_table->next_free = op_ctx->unmap.l0_free_tables; + op_ctx->unmap.l0_free_tables = op_ctx->curr_page.l0_table; + op_ctx->curr_page.l0_table = NULL; + + if (--op_ctx->curr_page.l1_table->entry_count == 0) { + /* Clear the parent L2 page table entry. */ + if (op_ctx->curr_page.l1_table->parent_idx != PVR_IDX_INVALID) + pvr_page_table_l2_remove(op_ctx); + } +} + +/** + * pvr_page_table_l0_insert() - Insert an entry referring to a physical page + * into a level 0 page table. + * @op_ctx: Target MMU op context pointing at the L0 entry to insert. + * @dma_addr: Target DMA address to be referenced by the new entry. + * @flags: Page options to be stored in the new entry. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L0 entry. + */ +static void +pvr_page_table_l0_insert(struct pvr_mmu_op_context *op_ctx, + dma_addr_t dma_addr, struct pvr_page_flags_raw flags) +{ + struct pvr_page_table_l0_entry_raw *entry_raw = + pvr_page_table_l0_get_entry_raw(op_ctx->curr_page.l0_table, + op_ctx->curr_page.l0_idx); + + pvr_page_table_l0_entry_raw_set(entry_raw, dma_addr, flags); + + /* + * There is no entry to set here - we don't keep a mirror of + * individual pages. + */ + + ++op_ctx->curr_page.l0_table->entry_count; +} + +/** + * pvr_page_table_l0_remove() - Remove a physical page from a level 0 page + * table. + * @op_ctx: Target MMU op context pointing at the L0 entry to remove. + * + * If this function results in the L0 table becoming empty, it will be removed + * from its parent L1 page table and destroyed. + * + * It is the caller's responsibility to ensure @op_ctx.curr_page points to a + * valid L0 entry. + */ +static void +pvr_page_table_l0_remove(struct pvr_mmu_op_context *op_ctx) +{ + struct pvr_page_table_l0_entry_raw *entry_raw = + pvr_page_table_l0_get_entry_raw(op_ctx->curr_page.l0_table, + op_ctx->curr_page.l0_idx); + + pvr_page_table_l0_entry_raw_clear(entry_raw); + + /* + * There is no entry to clear here - we don't keep a mirror of + * individual pages. + */ + + if (--op_ctx->curr_page.l0_table->entry_count == 0) { + /* Clear the parent L1 page table entry. */ + if (op_ctx->curr_page.l0_table->parent_idx != PVR_IDX_INVALID) + pvr_page_table_l1_remove(op_ctx); + } +} + +/** + * DOC: Page table index utilities + */ + +/** + * pvr_page_table_l2_idx() - Calculate the level 2 page table index for a + * device-virtual address. + * @device_addr: Target device-virtual address. + * + * This function does not perform any bounds checking - it is the caller's + * responsibility to ensure that @device_addr is valid before interpreting + * the result. + * + * Return: + * The index into a level 2 page table corresponding to @device_addr. + */ +static u16 +pvr_page_table_l2_idx(u64 device_addr) +{ + return (device_addr & ~ROGUE_MMUCTRL_VADDR_PC_INDEX_CLRMSK) >> + ROGUE_MMUCTRL_VADDR_PC_INDEX_SHIFT; +} + +/** + * pvr_page_table_l1_idx() - Calculate the level 1 page table index for a + * device-virtual address. + * @device_addr: Target device-virtual address. + * + * This function does not perform any bounds checking - it is the caller's + * responsibility to ensure that @device_addr is valid before interpreting + * the result. + * + * Return: + * The index into a level 1 page table corresponding to @device_addr. + */ +static u16 +pvr_page_table_l1_idx(u64 device_addr) +{ + return (device_addr & ~ROGUE_MMUCTRL_VADDR_PD_INDEX_CLRMSK) >> + ROGUE_MMUCTRL_VADDR_PD_INDEX_SHIFT; +} + +/** + * pvr_page_table_l0_idx() - Calculate the level 0 page table index for a + * device-virtual address. + * @device_addr: Target device-virtual address. + * + * This function does not perform any bounds checking - it is the caller's + * responsibility to ensure that @device_addr is valid before interpreting + * the result. + * + * Return: + * The index into a level 0 page table corresponding to @device_addr. + */ +static u16 +pvr_page_table_l0_idx(u64 device_addr) +{ + return (device_addr & ~ROGUE_MMUCTRL_VADDR_PT_INDEX_CLRMSK) >> + ROGUE_MMUCTRL_PAGE_X_RANGE_SHIFT; +} + +/** + * DOC: High-level page table operations + */ + +/** + * pvr_page_table_l1_get_or_insert() - Retrieves (optionally inserting if + * necessary) a level 1 page table from the specified level 2 page table entry. + * @op_ctx: Target MMU op context. + * @should_insert: [IN] Specifies whether new page tables should be inserted + * when empty page table entries are encountered during traversal. + * + * Return: + * * 0 on success, or + * + * If @should_insert is %false: + * * -%ENXIO if a level 1 page table would have been inserted. + * + * If @should_insert is %true: + * * Any error encountered while inserting the level 1 page table. + */ +static int +pvr_page_table_l1_get_or_insert(struct pvr_mmu_op_context *op_ctx, + bool should_insert) +{ + struct pvr_page_table_l2 *l2_table = + &op_ctx->mmu_ctx->page_table_l2; + struct pvr_page_table_l1 *table; + + if (pvr_page_table_l2_entry_is_valid(l2_table, + op_ctx->curr_page.l2_idx)) { + op_ctx->curr_page.l1_table = + l2_table->entries[op_ctx->curr_page.l2_idx]; + return 0; + } + + if (!should_insert) + return -ENXIO; + + /* Take a prealloced table. */ + table = op_ctx->map.l1_prealloc_tables; + if (!table) + return -ENOMEM; + + /* Pop */ + op_ctx->map.l1_prealloc_tables = table->next_free; + table->next_free = NULL; + + /* Ensure new table is fully written out before adding to L2 page table. */ + wmb(); + + pvr_page_table_l2_insert(op_ctx, table); + + return 0; +} + +/** + * pvr_page_table_l0_get_or_insert() - Retrieves (optionally inserting if + * necessary) a level 0 page table from the specified level 1 page table entry. + * @op_ctx: Target MMU op context. + * @should_insert: [IN] Specifies whether new page tables should be inserted + * when empty page table entries are encountered during traversal. + * + * Return: + * * 0 on success, + * + * If @should_insert is %false: + * * -%ENXIO if a level 0 page table would have been inserted. + * + * If @should_insert is %true: + * * Any error encountered while inserting the level 0 page table. + */ +static int +pvr_page_table_l0_get_or_insert(struct pvr_mmu_op_context *op_ctx, + bool should_insert) +{ + struct pvr_page_table_l0 *table; + + if (pvr_page_table_l1_entry_is_valid(op_ctx->curr_page.l1_table, + op_ctx->curr_page.l1_idx)) { + op_ctx->curr_page.l0_table = + op_ctx->curr_page.l1_table->entries[op_ctx->curr_page.l1_idx]; + return 0; + } + + if (!should_insert) + return -ENXIO; + + /* Take a prealloced table. */ + table = op_ctx->map.l0_prealloc_tables; + if (!table) + return -ENOMEM; + + /* Pop */ + op_ctx->map.l0_prealloc_tables = table->next_free; + table->next_free = NULL; + + /* Ensure new table is fully written out before adding to L1 page table. */ + wmb(); + + pvr_page_table_l1_insert(op_ctx, table); + + return 0; +} + +/** + * pvr_mmu_context_create() - Create an MMU context. + * @pvr_dev: PVR device associated with owning VM context. + * + * Returns: + * * Newly created MMU context object on success, or + * * -%ENOMEM if no memory is available, + * * Any error code returned by pvr_page_table_l2_init(). + */ +struct pvr_mmu_context *pvr_mmu_context_create(struct pvr_device *pvr_dev) +{ + struct pvr_mmu_context *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + int err; + + if (!ctx) + return ERR_PTR(-ENOMEM); + + err = pvr_page_table_l2_init(&ctx->page_table_l2, pvr_dev); + if (err) + return ERR_PTR(err); + + ctx->pvr_dev = pvr_dev; + + return ctx; +} + +/** + * pvr_mmu_context_destroy() - Destroy an MMU context. + * @ctx: Target MMU context. + */ +void pvr_mmu_context_destroy(struct pvr_mmu_context *ctx) +{ + pvr_page_table_l2_fini(&ctx->page_table_l2); + kfree(ctx); +} + +/** + * pvr_mmu_get_root_table_dma_addr() - Get the DMA address of the root of the + * page table structure behind a VM context. + * @ctx: Target MMU context. + */ +dma_addr_t pvr_mmu_get_root_table_dma_addr(struct pvr_mmu_context *ctx) +{ + return ctx->page_table_l2.backing_page.dma_addr; +} + +/** + * pvr_page_table_l1_alloc() - Allocate a l1 page_table object. + * @ctx: MMU context of owning VM context. + * + * Returns: + * * Newly created page table object on success, or + * * -%ENOMEM if no memory is available, + * * Any error code returned by pvr_page_table_l1_init(). + */ +static struct pvr_page_table_l1 * +pvr_page_table_l1_alloc(struct pvr_mmu_context *ctx) +{ + int err; + + struct pvr_page_table_l1 *table = + kzalloc(sizeof(*table), GFP_KERNEL); + + if (!table) + return ERR_PTR(-ENOMEM); + + err = pvr_page_table_l1_init(table, ctx->pvr_dev); + if (err) { + kfree(table); + return ERR_PTR(err); + } + + return table; +} + +/** + * pvr_page_table_l0_alloc() - Allocate a l0 page_table object. + * @ctx: MMU context of owning VM context. + * + * Returns: + * * Newly created page table object on success, or + * * -%ENOMEM if no memory is available, + * * Any error code returned by pvr_page_table_l0_init(). + */ +static struct pvr_page_table_l0 * +pvr_page_table_l0_alloc(struct pvr_mmu_context *ctx) +{ + int err; + + struct pvr_page_table_l0 *table = + kzalloc(sizeof(*table), GFP_KERNEL); + + if (!table) + return ERR_PTR(-ENOMEM); + + err = pvr_page_table_l0_init(table, ctx->pvr_dev); + if (err) { + kfree(table); + return ERR_PTR(err); + } + + return table; +} + +/** + * pvr_mmu_op_context_require_sync() - Mark an MMU op context as requiring a + * sync operation for the referenced page tables up to a specified level. + * @op_ctx: Target MMU op context. + * @level: Maximum page table level for which a sync is required. + */ +static void +pvr_mmu_op_context_require_sync(struct pvr_mmu_op_context *op_ctx, + enum pvr_mmu_sync_level level) +{ + if (op_ctx->sync_level_required < level) + op_ctx->sync_level_required = level; +} + +/** + * pvr_mmu_op_context_sync_manual() - Trigger a sync of some or all of the + * page tables referenced by a MMU op context. + * @op_ctx: Target MMU op context. + * @level: Maximum page table level to sync. + * + * Do not call this function directly. Instead use + * pvr_mmu_op_context_sync_partial() which is checked against the current + * value of &op_ctx->sync_level_required as set by + * pvr_mmu_op_context_require_sync(). + */ +static void +pvr_mmu_op_context_sync_manual(struct pvr_mmu_op_context *op_ctx, + enum pvr_mmu_sync_level level) +{ + /* + * We sync the page table levels in ascending order (starting from the + * leaf node) to ensure consistency. + */ + + WARN_ON(level < PVR_MMU_SYNC_LEVEL_NONE); + + if (level <= PVR_MMU_SYNC_LEVEL_NONE) + return; + + if (op_ctx->curr_page.l0_table) + pvr_page_table_l0_sync(op_ctx->curr_page.l0_table); + + if (level < PVR_MMU_SYNC_LEVEL_1) + return; + + if (op_ctx->curr_page.l1_table) + pvr_page_table_l1_sync(op_ctx->curr_page.l1_table); + + if (level < PVR_MMU_SYNC_LEVEL_2) + return; + + pvr_page_table_l2_sync(&op_ctx->mmu_ctx->page_table_l2); +} + +/** + * pvr_mmu_op_context_sync_partial() - Trigger a sync of some or all of the + * page tables referenced by a MMU op context. + * @op_ctx: Target MMU op context. + * @level: Requested page table level to sync up to (inclusive). + * + * If @level is greater than the maximum level recorded by @op_ctx as requiring + * a sync operation, only the previously recorded maximum will be used. + * + * Additionally, if @level is greater than or equal to the maximum level + * recorded by @op_ctx as requiring a sync operation, that maximum level will be + * reset as a full sync will be performed. This is equivalent to calling + * pvr_mmu_op_context_sync(). + */ +static void +pvr_mmu_op_context_sync_partial(struct pvr_mmu_op_context *op_ctx, + enum pvr_mmu_sync_level level) +{ + /* + * If the requested sync level is greater than or equal to the + * currently required sync level, we do two things: + * * Don't waste time syncing levels we haven't previously marked as + * requiring a sync, and + * * Reset the required sync level since we are about to sync + * everything that was previously marked as requiring a sync. + */ + if (level >= op_ctx->sync_level_required) { + level = op_ctx->sync_level_required; + op_ctx->sync_level_required = PVR_MMU_SYNC_LEVEL_NONE; + } + + pvr_mmu_op_context_sync_manual(op_ctx, level); +} + +/** + * pvr_mmu_op_context_sync() - Trigger a sync of every page table referenced by + * a MMU op context. + * @op_ctx: Target MMU op context. + * + * The maximum level marked internally as requiring a sync will be reset so + * that subsequent calls to this function will be no-ops unless @op_ctx is + * otherwise updated. + */ +static void +pvr_mmu_op_context_sync(struct pvr_mmu_op_context *op_ctx) +{ + pvr_mmu_op_context_sync_manual(op_ctx, op_ctx->sync_level_required); + + op_ctx->sync_level_required = PVR_MMU_SYNC_LEVEL_NONE; +} + +/** + * pvr_mmu_op_context_load_tables() - Load pointers to tables in each level of + * the page table tree structure needed to reference the physical page + * referenced by a MMU op context. + * @op_ctx: Target MMU op context. + * @should_create: Specifies whether new page tables should be created when + * empty page table entries are encountered during traversal. + * @load_level_required: Maximum page table level to load. + * + * If @should_create is %true, this function may modify the stored required + * sync level of @op_ctx as new page tables are created and inserted into their + * respective parents. + * + * Since there is only one root page table, it is technically incorrect to call + * this function with a value of @load_level_required greater than or equal to + * the root level number. However, this is not explicitly disallowed here. + * + * Return: + * * 0 on success, + * * Any error returned by pvr_page_table_l1_get_or_create() if + * @load_level_required >= 1 except -%ENXIO, or + * * Any error returned by pvr_page_table_l0_get_or_create() if + * @load_level_required >= 0 except -%ENXIO. + */ +static int +pvr_mmu_op_context_load_tables(struct pvr_mmu_op_context *op_ctx, + bool should_create, + enum pvr_mmu_sync_level load_level_required) +{ + const struct pvr_page_table_l1 *l1_head_before = + op_ctx->map.l1_prealloc_tables; + const struct pvr_page_table_l0 *l0_head_before = + op_ctx->map.l0_prealloc_tables; + int err; + + /* Clear tables we're about to fetch in case of error states. */ + if (load_level_required >= PVR_MMU_SYNC_LEVEL_1) + op_ctx->curr_page.l1_table = NULL; + + if (load_level_required >= PVR_MMU_SYNC_LEVEL_0) + op_ctx->curr_page.l0_table = NULL; + + /* Get or create L1 page table. */ + if (load_level_required >= PVR_MMU_SYNC_LEVEL_1) { + err = pvr_page_table_l1_get_or_insert(op_ctx, should_create); + if (err) { + /* + * If @should_create is %false and no L1 page table was + * found, return early but without an error. Since + * pvr_page_table_l1_get_or_create() can only return + * -%ENXIO if @should_create is %false, there is no + * need to check it here. + */ + if (err == -ENXIO) + err = 0; + + return err; + } + } + + /* Get or create L0 page table. */ + if (load_level_required >= PVR_MMU_SYNC_LEVEL_0) { + err = pvr_page_table_l0_get_or_insert(op_ctx, should_create); + if (err) { + /* + * If @should_create is %false and no L0 page table was + * found, return early but without an error. Since + * pvr_page_table_l0_get_or_insert() can only return + * -%ENXIO if @should_create is %false, there is no + * need to check it here. + */ + if (err == -ENXIO) + err = 0; + + /* + * At this point, an L1 page table could have been + * inserted but is now empty due to the failed attempt + * at inserting an L0 page table. In this instance, we + * must remove the empty L1 page table ourselves as + * pvr_page_table_l1_remove() is never called as part + * of the error path in + * pvr_page_table_l0_get_or_insert(). + */ + if (l1_head_before != op_ctx->map.l1_prealloc_tables) { + pvr_page_table_l2_remove(op_ctx); + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_2); + } + + return err; + } + } + + /* + * A sync is only needed if table objects were inserted. This can be + * inferred by checking if the pointer at the head of the linked list + * has changed. + */ + if (l1_head_before != op_ctx->map.l1_prealloc_tables) + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_2); + else if (l0_head_before != op_ctx->map.l0_prealloc_tables) + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_1); + + return 0; +} + +/** + * pvr_mmu_op_context_set_curr_page() - Reassign the current page of an MMU op + * context, syncing any page tables previously assigned to it which are no + * longer relevant. + * @op_ctx: Target MMU op context. + * @device_addr: New pointer target. + * @should_create: Specify whether new page tables should be created when + * empty page table entries are encountered during traversal. + * + * This function performs a full sync on the pointer, regardless of which + * levels are modified. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_mmu_op_context_load_tables(). + */ +static int +pvr_mmu_op_context_set_curr_page(struct pvr_mmu_op_context *op_ctx, + u64 device_addr, bool should_create) +{ + pvr_mmu_op_context_sync(op_ctx); + + op_ctx->curr_page.l2_idx = pvr_page_table_l2_idx(device_addr); + op_ctx->curr_page.l1_idx = pvr_page_table_l1_idx(device_addr); + op_ctx->curr_page.l0_idx = pvr_page_table_l0_idx(device_addr); + op_ctx->curr_page.l1_table = NULL; + op_ctx->curr_page.l0_table = NULL; + + return pvr_mmu_op_context_load_tables(op_ctx, should_create, + PVR_MMU_SYNC_LEVEL_1); +} + +/** + * pvr_mmu_op_context_next_page() - Advance the current page of an MMU op + * context. + * @op_ctx: Target MMU op context. + * @should_create: Specify whether new page tables should be created when + * empty page table entries are encountered during traversal. + * + * If @should_create is %false, it is the caller's responsibility to verify that + * the state of the table references in @op_ctx is valid on return. If -%ENXIO + * is returned, at least one of the table references is invalid. It should be + * noted that @op_ctx as a whole will be left in a valid state if -%ENXIO is + * returned, unlike other error codes. The caller should check which references + * are invalid by comparing them to %NULL. Only &@ptr->l2_table is guaranteed + * to be valid, since it represents the root of the page table tree structure. + * + * Return: + * * 0 on success, + * * -%EPERM if the operation would wrap at the top of the page table + * hierarchy, + * * -%ENXIO if @should_create is %false and a page table of any level would + * have otherwise been created, or + * * Any error returned while attempting to create missing page tables if + * @should_create is %true. + */ +static int +pvr_mmu_op_context_next_page(struct pvr_mmu_op_context *op_ctx, + bool should_create) +{ + s8 load_level_required = PVR_MMU_SYNC_LEVEL_NONE; + + if (++op_ctx->curr_page.l0_idx != ROGUE_MMUCTRL_ENTRIES_PT_VALUE_X) + goto load_tables; + + op_ctx->curr_page.l0_idx = 0; + load_level_required = PVR_MMU_SYNC_LEVEL_0; + + if (++op_ctx->curr_page.l1_idx != ROGUE_MMUCTRL_ENTRIES_PD_VALUE) + goto load_tables; + + op_ctx->curr_page.l1_idx = 0; + load_level_required = PVR_MMU_SYNC_LEVEL_1; + + if (++op_ctx->curr_page.l2_idx != ROGUE_MMUCTRL_ENTRIES_PC_VALUE) + goto load_tables; + + /* + * If the pattern continued, we would set &op_ctx->curr_page.l2_idx to + * zero here. However, that would wrap the top layer of the page table + * hierarchy which is not a valid operation. Instead, we warn and return + * an error. + */ + WARN(true, + "%s(%p) attempted to loop the top of the page table hierarchy", + __func__, op_ctx); + return -EPERM; + + /* If indices have wrapped, we need to load new tables. */ +load_tables: + /* First, flush tables which will be unloaded. */ + pvr_mmu_op_context_sync_partial(op_ctx, load_level_required); + + /* Then load tables from the required level down. */ + return pvr_mmu_op_context_load_tables(op_ctx, should_create, + load_level_required); +} + +/** + * DOC: Single page operations + */ + +/** + * pvr_page_create() - Create a device-virtual memory page and insert it into + * a level 0 page table. + * @op_ctx: Target MMU op context pointing at the device-virtual address of the + * target page. + * @dma_addr: DMA address of the physical page backing the created page. + * @flags: Page options saved on the level 0 page table entry for reading by + * the device. + * + * Return: + * * 0 on success, or + * * -%EEXIST if the requested page already exists. + */ +static int +pvr_page_create(struct pvr_mmu_op_context *op_ctx, dma_addr_t dma_addr, + struct pvr_page_flags_raw flags) +{ + /* Do not create a new page if one already exists. */ + if (pvr_page_table_l0_entry_is_valid(op_ctx->curr_page.l0_table, + op_ctx->curr_page.l0_idx)) { + return -EEXIST; + } + + pvr_page_table_l0_insert(op_ctx, dma_addr, flags); + + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_0); + + return 0; +} + +/** + * pvr_page_destroy() - Destroy a device page after removing it from its + * parent level 0 page table. + * @op_ctx: Target MMU op context. + */ +static void +pvr_page_destroy(struct pvr_mmu_op_context *op_ctx) +{ + /* Do nothing if the page does not exist. */ + if (!pvr_page_table_l0_entry_is_valid(op_ctx->curr_page.l0_table, + op_ctx->curr_page.l0_idx)) { + return; + } + + /* Clear the parent L0 page table entry. */ + pvr_page_table_l0_remove(op_ctx); + + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_0); +} + +/** + * pvr_mmu_op_context_destroy() - Destroy an MMU op context. + * @op_ctx: Target MMU op context. + */ +void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx) +{ + const bool flush_caches = + op_ctx->sync_level_required != PVR_MMU_SYNC_LEVEL_NONE; + + pvr_mmu_op_context_sync(op_ctx); + + /* Unmaps should be flushed immediately. Map flushes can be deferred. */ + if (flush_caches && !op_ctx->map.sgt) + pvr_mmu_flush_exec(op_ctx->mmu_ctx->pvr_dev, true); + + while (op_ctx->map.l0_prealloc_tables) { + struct pvr_page_table_l0 *tmp = op_ctx->map.l0_prealloc_tables; + + op_ctx->map.l0_prealloc_tables = + op_ctx->map.l0_prealloc_tables->next_free; + pvr_page_table_l0_free(tmp); + } + + while (op_ctx->map.l1_prealloc_tables) { + struct pvr_page_table_l1 *tmp = op_ctx->map.l1_prealloc_tables; + + op_ctx->map.l1_prealloc_tables = + op_ctx->map.l1_prealloc_tables->next_free; + pvr_page_table_l1_free(tmp); + } + + while (op_ctx->unmap.l0_free_tables) { + struct pvr_page_table_l0 *tmp = op_ctx->unmap.l0_free_tables; + + op_ctx->unmap.l0_free_tables = + op_ctx->unmap.l0_free_tables->next_free; + pvr_page_table_l0_free(tmp); + } + + while (op_ctx->unmap.l1_free_tables) { + struct pvr_page_table_l1 *tmp = op_ctx->unmap.l1_free_tables; + + op_ctx->unmap.l1_free_tables = + op_ctx->unmap.l1_free_tables->next_free; + pvr_page_table_l1_free(tmp); + } + + kfree(op_ctx); +} + +/** + * pvr_mmu_op_context_create() - Create an MMU op context. + * @ctx: MMU context associated with owning VM context. + * @sgt: Scatter gather table containing pages pinned for use by this context. + * @sgt_offset: Start offset of the requested device-virtual memory mapping. + * @size: Size in bytes of the requested device-virtual memory mapping. For an + * unmapping, this should be zero so that no page tables are allocated. + * + * Returns: + * * Newly created MMU op context object on success, or + * * -%ENOMEM if no memory is available, + * * Any error code returned by pvr_page_table_l2_init(). + */ +struct pvr_mmu_op_context * +pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt, + u64 sgt_offset, u64 size) +{ + int err; + + struct pvr_mmu_op_context *op_ctx = + kzalloc(sizeof(*op_ctx), GFP_KERNEL); + + if (!op_ctx) + return ERR_PTR(-ENOMEM); + + op_ctx->mmu_ctx = ctx; + op_ctx->map.sgt = sgt; + op_ctx->map.sgt_offset = sgt_offset; + op_ctx->sync_level_required = PVR_MMU_SYNC_LEVEL_NONE; + + if (size) { + /* + * The number of page table objects we need to prealloc is + * indicated by the mapping size, start offset and the sizes + * of the areas mapped per PT or PD. The range calculation is + * identical to that for the index into a table for a device + * address, so we reuse those functions here. + */ + const u32 l1_start_idx = pvr_page_table_l2_idx(sgt_offset); + const u32 l1_end_idx = pvr_page_table_l2_idx(sgt_offset + size); + const u32 l1_count = l1_end_idx - l1_start_idx + 1; + const u32 l0_start_idx = pvr_page_table_l1_idx(sgt_offset); + const u32 l0_end_idx = pvr_page_table_l1_idx(sgt_offset + size); + const u32 l0_count = l0_end_idx - l0_start_idx + 1; + + /* + * Alloc and push page table entries until we have enough of + * each type, ending with linked lists of l0 and l1 entries in + * reverse order. + */ + for (int i = 0; i < l1_count; i++) { + struct pvr_page_table_l1 *l1_tmp = + pvr_page_table_l1_alloc(ctx); + + err = PTR_ERR_OR_ZERO(l1_tmp); + if (err) + goto err_cleanup; + + l1_tmp->next_free = op_ctx->map.l1_prealloc_tables; + op_ctx->map.l1_prealloc_tables = l1_tmp; + } + + for (int i = 0; i < l0_count; i++) { + struct pvr_page_table_l0 *l0_tmp = + pvr_page_table_l0_alloc(ctx); + + err = PTR_ERR_OR_ZERO(l0_tmp); + if (err) + goto err_cleanup; + + l0_tmp->next_free = op_ctx->map.l0_prealloc_tables; + op_ctx->map.l0_prealloc_tables = l0_tmp; + } + } + + return op_ctx; + +err_cleanup: + pvr_mmu_op_context_destroy(op_ctx); + + return ERR_PTR(err); +} + +/** + * pvr_mmu_op_context_unmap_curr_page() - Unmap pages from a memory context + * starting from the current page of an MMU op context. + * @op_ctx: Target MMU op context pointing at the first page to unmap. + * @nr_pages: Number of pages to unmap. + * + * Return: + * * 0 on success, or + * * Any error encountered while advancing @op_ctx.curr_page with + * pvr_mmu_op_context_next_page() (except -%ENXIO). + */ +static int +pvr_mmu_op_context_unmap_curr_page(struct pvr_mmu_op_context *op_ctx, + u64 nr_pages) +{ + int err; + + if (nr_pages == 0) + return 0; + + /* + * Destroy first page outside loop, as it doesn't require a page + * advance beforehand. If the L0 page table reference in + * @op_ctx.curr_page is %NULL, there cannot be a mapped page at + * @op_ctx.curr_page (so skip ahead). + */ + if (op_ctx->curr_page.l0_table) + pvr_page_destroy(op_ctx); + + for (u64 page = 1; page < nr_pages; ++page) { + err = pvr_mmu_op_context_next_page(op_ctx, false); + /* + * If the page table tree structure at @op_ctx.curr_page is + * incomplete, skip ahead. We don't care about unmapping pages + * that cannot exist. + * + * FIXME: This could be made more efficient by jumping ahead + * using pvr_mmu_op_context_set_curr_page(). + */ + if (err == -ENXIO) + continue; + else if (err) + return err; + + pvr_page_destroy(op_ctx); + } + + return 0; +} + +/** + * pvr_mmu_unmap() - Unmap pages from a memory context. + * @op_ctx: Target MMU op context. + * @device_addr: First device-virtual address to unmap. + * @size: Size in bytes to unmap. + * + * The total amount of device-virtual memory unmapped is + * @nr_pages * %PVR_DEVICE_PAGE_SIZE. + * + * Returns: + * * 0 on success, or + * * Any error code returned by pvr_page_table_ptr_init(), or + * * Any error code returned by pvr_page_table_ptr_unmap(). + */ +int pvr_mmu_unmap(struct pvr_mmu_op_context *op_ctx, u64 device_addr, u64 size) +{ + int err = pvr_mmu_op_context_set_curr_page(op_ctx, device_addr, false); + + if (err) + return err; + + return pvr_mmu_op_context_unmap_curr_page(op_ctx, + size >> PVR_DEVICE_PAGE_SHIFT); +} + +/** + * pvr_mmu_map_sgl() - Map part of a scatter-gather table entry to + * device-virtual memory. + * @op_ctx: Target MMU op context pointing to the first page that should be + * mapped. + * @sgl: Target scatter-gather table entry. + * @offset: Offset into @sgl to map from. Must result in a starting address + * from @sgl which is CPU page-aligned. + * @size: Size of the memory to be mapped in bytes. Must be a non-zero multiple + * of the device page size. + * @page_flags: Page options to be applied to every device-virtual memory page + * in the created mapping. + * + * Return: + * * 0 on success, + * * -%EINVAL if the range specified by @offset and @size is not completely + * within @sgl, or + * * Any error encountered while creating a page with pvr_page_create(), or + * * Any error encountered while advancing @op_ctx.curr_page with + * pvr_mmu_op_context_next_page(). + */ +static int +pvr_mmu_map_sgl(struct pvr_mmu_op_context *op_ctx, struct scatterlist *sgl, + u64 offset, u64 size, struct pvr_page_flags_raw page_flags) +{ + const unsigned int pages = size >> PVR_DEVICE_PAGE_SHIFT; + dma_addr_t dma_addr = sg_dma_address(sgl) + offset; + const unsigned int dma_len = sg_dma_len(sgl); + struct pvr_page_table_ptr ptr_copy; + unsigned int page; + int err; + + if (size > dma_len || offset > dma_len - size) + return -EINVAL; + + /* + * Before progressing, save a copy of the start pointer so we can use + * it again if we enter an error state and have to destroy pages. + */ + memcpy(&ptr_copy, &op_ctx->curr_page, sizeof(ptr_copy)); + + /* + * Create first page outside loop, as it doesn't require a page advance + * beforehand. + */ + err = pvr_page_create(op_ctx, dma_addr, page_flags); + if (err) + return err; + + for (page = 1; page < pages; ++page) { + err = pvr_mmu_op_context_next_page(op_ctx, true); + if (err) + goto err_destroy_pages; + + dma_addr += PVR_DEVICE_PAGE_SIZE; + + err = pvr_page_create(op_ctx, dma_addr, page_flags); + if (err) + goto err_destroy_pages; + } + + return 0; + +err_destroy_pages: + memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page)); + err = pvr_mmu_op_context_unmap_curr_page(op_ctx, page); + + return err; +} + +/** + * pvr_mmu_map() - Map an object's virtual memory to physical memory. + * @op_ctx: Target MMU op context. + * @size: Size of memory to be mapped in bytes. Must be a non-zero multiple + * of the device page size. + * @flags: Flags from pvr_gem_object associated with the mapping. + * @device_addr: Virtual device address to map to. Must be device page-aligned. + * + * Returns: + * * 0 on success, or + * * Any error code returned by pvr_page_table_ptr_init(), or + * * Any error code returned by pvr_mmu_map_sgl(), or + * * Any error code returned by pvr_page_table_ptr_next_page(). + */ +int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags, + u64 device_addr) +{ + struct pvr_page_table_ptr ptr_copy; + struct pvr_page_flags_raw flags_raw; + struct scatterlist *sgl; + u64 mapped_size = 0; + unsigned int count; + int err; + + if (!size) + return 0; + + if ((op_ctx->map.sgt_offset | size) & ~PVR_DEVICE_PAGE_MASK) + return -EINVAL; + + err = pvr_mmu_op_context_set_curr_page(op_ctx, device_addr, true); + if (err) + return -EINVAL; + + memcpy(&ptr_copy, &op_ctx->curr_page, sizeof(ptr_copy)); + + flags_raw = pvr_page_flags_raw_create(false, false, + flags & DRM_PVR_BO_BYPASS_DEVICE_CACHE, + flags & DRM_PVR_BO_PM_FW_PROTECT); + + /* Map scatter gather table */ + for_each_sgtable_dma_sg(op_ctx->map.sgt, sgl, count) { + const size_t sgl_len = sg_dma_len(sgl); + u64 sgl_offset, map_sgl_len; + + if (sgl_len <= op_ctx->map.sgt_offset) { + op_ctx->map.sgt_offset -= sgl_len; + continue; + } + + sgl_offset = op_ctx->map.sgt_offset; + map_sgl_len = min_t(u64, sgl_len - sgl_offset, size - mapped_size); + + err = pvr_mmu_map_sgl(op_ctx, sgl, sgl_offset, map_sgl_len, + flags_raw); + if (err) + break; + + /* + * Flag the L0 page table as requiring a flush when the MMU op + * context is destroyed. + */ + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_0); + + op_ctx->map.sgt_offset = 0; + mapped_size += map_sgl_len; + + if (mapped_size >= size) + break; + + err = pvr_mmu_op_context_next_page(op_ctx, true); + if (err) + break; + } + + if (err && mapped_size) { + memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page)); + pvr_mmu_op_context_unmap_curr_page(op_ctx, + mapped_size >> PVR_DEVICE_PAGE_SHIFT); + } + + return err; +} diff --git a/drivers/gpu/drm/imagination/pvr_mmu.h b/drivers/gpu/drm/imagination/pvr_mmu.h new file mode 100644 index 000000000000..a8ecd460168d --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_mmu.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_MMU_H +#define PVR_MMU_H + +#include <linux/memory.h> +#include <linux/types.h> + +/* Forward declaration from "pvr_device.h" */ +struct pvr_device; + +/* Forward declaration from "pvr_mmu.c" */ +struct pvr_mmu_context; +struct pvr_mmu_op_context; + +/* Forward declaration from "pvr_vm.c" */ +struct pvr_vm_context; + +/* Forward declaration from <linux/scatterlist.h> */ +struct sg_table; + +/** + * DOC: Public API (constants) + * + * .. c:macro:: PVR_DEVICE_PAGE_SIZE + * + * Fixed page size referenced by leaf nodes in the page table tree + * structure. In the current implementation, this value is pegged to the + * CPU page size (%PAGE_SIZE). It is therefore an error to specify a CPU + * page size which is not also a supported device page size. The supported + * device page sizes are: 4KiB, 16KiB, 64KiB, 256KiB, 1MiB and 2MiB. + * + * .. c:macro:: PVR_DEVICE_PAGE_SHIFT + * + * Shift value used to efficiently multiply or divide by + * %PVR_DEVICE_PAGE_SIZE. + * + * This value is derived from %PVR_DEVICE_PAGE_SIZE. + * + * .. c:macro:: PVR_DEVICE_PAGE_MASK + * + * Mask used to round a value down to the nearest multiple of + * %PVR_DEVICE_PAGE_SIZE. When bitwise negated, it will indicate whether a + * value is already a multiple of %PVR_DEVICE_PAGE_SIZE. + * + * This value is derived from %PVR_DEVICE_PAGE_SIZE. + */ + +/* PVR_DEVICE_PAGE_SIZE determines the page size */ +#define PVR_DEVICE_PAGE_SIZE (PAGE_SIZE) +#define PVR_DEVICE_PAGE_SHIFT (PAGE_SHIFT) +#define PVR_DEVICE_PAGE_MASK (PAGE_MASK) + +/** + * DOC: Page table index utilities (constants) + * + * .. c:macro:: PVR_PAGE_TABLE_ADDR_SPACE_SIZE + * + * Size of device-virtual address space which can be represented in the page + * table structure. + * + * This value is checked at runtime against + * &pvr_device_features.virtual_address_space_bits by + * pvr_vm_create_context(), which will return an error if the feature value + * does not match this constant. + * + * .. admonition:: Future work + * + * It should be possible to support other values of + * &pvr_device_features.virtual_address_space_bits, but so far no + * hardware has been created which advertises an unsupported value. + * + * .. c:macro:: PVR_PAGE_TABLE_ADDR_BITS + * + * Number of bits needed to represent any value less than + * %PVR_PAGE_TABLE_ADDR_SPACE_SIZE exactly. + * + * .. c:macro:: PVR_PAGE_TABLE_ADDR_MASK + * + * Bitmask of device-virtual addresses which are valid in the page table + * structure. + * + * This value is derived from %PVR_PAGE_TABLE_ADDR_SPACE_SIZE, so the same + * notes on that constant apply here. + */ +#define PVR_PAGE_TABLE_ADDR_SPACE_SIZE SZ_1T +#define PVR_PAGE_TABLE_ADDR_BITS __ffs(PVR_PAGE_TABLE_ADDR_SPACE_SIZE) +#define PVR_PAGE_TABLE_ADDR_MASK (PVR_PAGE_TABLE_ADDR_SPACE_SIZE - 1) + +void pvr_mmu_flush_request_all(struct pvr_device *pvr_dev); +int pvr_mmu_flush_exec(struct pvr_device *pvr_dev, bool wait); + +struct pvr_mmu_context *pvr_mmu_context_create(struct pvr_device *pvr_dev); +void pvr_mmu_context_destroy(struct pvr_mmu_context *ctx); + +dma_addr_t pvr_mmu_get_root_table_dma_addr(struct pvr_mmu_context *ctx); + +void pvr_mmu_op_context_destroy(struct pvr_mmu_op_context *op_ctx); +struct pvr_mmu_op_context * +pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, + struct sg_table *sgt, u64 sgt_offset, u64 size); + +int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags, + u64 device_addr); +int pvr_mmu_unmap(struct pvr_mmu_op_context *op_ctx, u64 device_addr, u64 size); + +#endif /* PVR_MMU_H */ diff --git a/drivers/gpu/drm/imagination/pvr_params.c b/drivers/gpu/drm/imagination/pvr_params.c new file mode 100644 index 000000000000..b91759f362c5 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_params.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_params.h" + +#include <linux/cache.h> +#include <linux/moduleparam.h> + +static struct pvr_device_params pvr_device_param_defaults __read_mostly = { +#define X(type_, name_, value_, desc_, ...) .name_ = (value_), + PVR_DEVICE_PARAMS +#undef X +}; + +#define PVR_DEVICE_PARAM_NAMED(name_, type_, desc_) \ + module_param_named(name_, pvr_device_param_defaults.name_, type_, \ + 0400); \ + MODULE_PARM_DESC(name_, desc_); + +/* + * This list of defines must contain every type specified in "pvr_params.h" as + * ``PVR_PARAM_TYPE_*_C``. + */ +#define PVR_PARAM_TYPE_X32_MODPARAM uint + +#define X(type_, name_, value_, desc_, ...) \ + PVR_DEVICE_PARAM_NAMED(name_, PVR_PARAM_TYPE_##type_##_MODPARAM, desc_); +PVR_DEVICE_PARAMS +#undef X + +int +pvr_device_params_init(struct pvr_device_params *params) +{ + /* + * If heap-allocated parameters are added in the future (e.g. + * modparam's charp type), they must be handled specially here (via + * kstrdup() in the case of charp). Since that's not necessary yet, + * a straight copy will do for now. This change will also require a + * pvr_device_params_fini() function to free any heap-allocated copies. + */ + + *params = pvr_device_param_defaults; + + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +#include "pvr_device.h" + +#include <linux/dcache.h> +#include <linux/debugfs.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/stddef.h> + +/* + * This list of defines must contain every type specified in "pvr_params.h" as + * ``PVR_PARAM_TYPE_*_C``. + */ +#define PVR_PARAM_TYPE_X32_FMT "0x%08llx" + +#define X_SET(name_, mode_) X_SET_##mode_(name_) +#define X_SET_DEF(name_, update_, mode_) X_SET_DEF_##mode_(name_, update_) + +#define X_SET_RO(name_) NULL +#define X_SET_RW(name_) __pvr_device_param_##name_##set + +#define X_SET_DEF_RO(name_, update_) +#define X_SET_DEF_RW(name_, update_) \ + static int \ + X_SET_RW(name_)(void *data, u64 val) \ + { \ + struct pvr_device *pvr_dev = data; \ + /* This is not just (update_) to suppress -Waddress. */ \ + if ((void *)(update_) != NULL) \ + (update_)(pvr_dev, pvr_dev->params.name_, val); \ + pvr_dev->params.name_ = val; \ + return 0; \ + } + +#define X(type_, name_, value_, desc_, mode_, update_) \ + static int \ + __pvr_device_param_##name_##_get(void *data, u64 *val) \ + { \ + struct pvr_device *pvr_dev = data; \ + *val = pvr_dev->params.name_; \ + return 0; \ + } \ + X_SET_DEF(name_, update_, mode_) \ + static int \ + __pvr_device_param_##name_##_open(struct inode *inode, \ + struct file *file) \ + { \ + __simple_attr_check_format(PVR_PARAM_TYPE_##type_##_FMT, \ + 0ull); \ + return simple_attr_open(inode, file, \ + __pvr_device_param_##name_##_get, \ + X_SET(name_, mode_), \ + PVR_PARAM_TYPE_##type_##_FMT); \ + } +PVR_DEVICE_PARAMS +#undef X + +#undef X_SET +#undef X_SET_RO +#undef X_SET_RW +#undef X_SET_DEF +#undef X_SET_DEF_RO +#undef X_SET_DEF_RW + +static struct { +#define X(type_, name_, value_, desc_, mode_, update_) \ + const struct file_operations name_; + PVR_DEVICE_PARAMS +#undef X +} pvr_device_param_debugfs_fops = { +#define X(type_, name_, value_, desc_, mode_, update_) \ + .name_ = { \ + .owner = THIS_MODULE, \ + .open = __pvr_device_param_##name_##_open, \ + .release = simple_attr_release, \ + .read = simple_attr_read, \ + .write = simple_attr_write, \ + .llseek = generic_file_llseek, \ + }, + PVR_DEVICE_PARAMS +#undef X +}; + +void +pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir) +{ +#define X_MODE(mode_) X_MODE_##mode_ +#define X_MODE_RO 0400 +#define X_MODE_RW 0600 + +#define X(type_, name_, value_, desc_, mode_, update_) \ + debugfs_create_file(#name_, X_MODE(mode_), dir, pvr_dev, \ + &pvr_device_param_debugfs_fops.name_); + PVR_DEVICE_PARAMS +#undef X + +#undef X_MODE +#undef X_MODE_RO +#undef X_MODE_RW +} +#endif diff --git a/drivers/gpu/drm/imagination/pvr_params.h b/drivers/gpu/drm/imagination/pvr_params.h new file mode 100644 index 000000000000..5807915b456b --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_params.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_PARAMS_H +#define PVR_PARAMS_H + +#include "pvr_rogue_fwif.h" + +#include <linux/cache.h> +#include <linux/compiler_attributes.h> + +/* + * This is the definitive list of types allowed in the definition of + * %PVR_DEVICE_PARAMS. + */ +#define PVR_PARAM_TYPE_X32_C u32 + +/* + * This macro defines all device-specific parameters; that is parameters which + * are set independently per device. + * + * The X-macro accepts the following arguments. Arguments marked with [debugfs] + * are ignored when debugfs is disabled; values used for these arguments may + * safely be gated behind CONFIG_DEBUG_FS. + * + * @type_: The definitive list of allowed values is PVR_PARAM_TYPE_*_C. + * @name_: Name of the parameter. This is used both as the field name in C and + * stringified as the parameter name. + * @value_: Initial/default value. + * @desc_: String literal used as help text to describe the usage of this + * parameter. + * @mode_: [debugfs] One of {RO,RW}. The access mode of the debugfs entry for + * this parameter. + * @update_: [debugfs] When debugfs support is enabled, parameters may be + * updated at runtime. When this happens, this function will be + * called to allow changes to propagate. The signature of this + * function is: + * + * void (*)(struct pvr_device *pvr_dev, T old_val, T new_val) + * + * Where T is the C type associated with @type_. + * + * If @mode_ does not allow write access, this function will never be + * called. In this case, or if no update callback is required, you + * should specify NULL for this argument. + */ +#define PVR_DEVICE_PARAMS \ + X(X32, fw_trace_mask, ROGUE_FWIF_LOG_TYPE_NONE, \ + "Enable FW trace for the specified groups. Specifying 0 disables " \ + "all FW tracing.", \ + RW, pvr_fw_trace_mask_update) + +struct pvr_device_params { +#define X(type_, name_, value_, desc_, ...) \ + PVR_PARAM_TYPE_##type_##_C name_; + PVR_DEVICE_PARAMS +#undef X +}; + +int pvr_device_params_init(struct pvr_device_params *params); + +#if defined(CONFIG_DEBUG_FS) +/* Forward declaration from "pvr_device.h". */ +struct pvr_device; + +/* Forward declaration from <linux/dcache.h>. */ +struct dentry; + +void pvr_params_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir); +#endif /* defined(CONFIG_DEBUG_FS) */ + +#endif /* PVR_PARAMS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_power.c b/drivers/gpu/drm/imagination/pvr_power.c new file mode 100644 index 000000000000..ba7816fd28ec --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_power.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_fw.h" +#include "pvr_fw_startstop.h" +#include "pvr_power.h" +#include "pvr_queue.h" +#include "pvr_rogue_fwif.h" + +#include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/timer.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#define POWER_SYNC_TIMEOUT_US (1000000) /* 1s */ + +#define WATCHDOG_TIME_MS (500) + +/** + * pvr_device_lost() - Mark GPU device as lost + * @pvr_dev: Target PowerVR device. + * + * This will cause the DRM device to be unplugged. + */ +void +pvr_device_lost(struct pvr_device *pvr_dev) +{ + if (!pvr_dev->lost) { + pvr_dev->lost = true; + drm_dev_unplug(from_pvr_device(pvr_dev)); + } +} + +static int +pvr_power_send_command(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd *pow_cmd) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + u32 slot_nr; + u32 value; + int err; + + WRITE_ONCE(*fw_dev->power_sync, 0); + + err = pvr_kccb_send_cmd_powered(pvr_dev, pow_cmd, &slot_nr); + if (err) + return err; + + /* Wait for FW to acknowledge. */ + return readl_poll_timeout(pvr_dev->fw_dev.power_sync, value, value != 0, 100, + POWER_SYNC_TIMEOUT_US); +} + +static int +pvr_power_request_idle(struct pvr_device *pvr_dev) +{ + struct rogue_fwif_kccb_cmd pow_cmd; + + /* Send FORCED_IDLE request to FW. */ + pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW; + pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_FORCED_IDLE_REQ; + pow_cmd.cmd_data.pow_data.power_req_data.pow_request_type = ROGUE_FWIF_POWER_FORCE_IDLE; + + return pvr_power_send_command(pvr_dev, &pow_cmd); +} + +static int +pvr_power_request_pwr_off(struct pvr_device *pvr_dev) +{ + struct rogue_fwif_kccb_cmd pow_cmd; + + /* Send POW_OFF request to firmware. */ + pow_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_POW; + pow_cmd.cmd_data.pow_data.pow_type = ROGUE_FWIF_POW_OFF_REQ; + pow_cmd.cmd_data.pow_data.power_req_data.forced = true; + + return pvr_power_send_command(pvr_dev, &pow_cmd); +} + +static int +pvr_power_fw_disable(struct pvr_device *pvr_dev, bool hard_reset) +{ + if (!hard_reset) { + int err; + + cancel_delayed_work_sync(&pvr_dev->watchdog.work); + + err = pvr_power_request_idle(pvr_dev); + if (err) + return err; + + err = pvr_power_request_pwr_off(pvr_dev); + if (err) + return err; + } + + return pvr_fw_stop(pvr_dev); +} + +static int +pvr_power_fw_enable(struct pvr_device *pvr_dev) +{ + int err; + + err = pvr_fw_start(pvr_dev); + if (err) + return err; + + err = pvr_wait_for_fw_boot(pvr_dev); + if (err) { + drm_err(from_pvr_device(pvr_dev), "Firmware failed to boot\n"); + pvr_fw_stop(pvr_dev); + return err; + } + + queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work, + msecs_to_jiffies(WATCHDOG_TIME_MS)); + + return 0; +} + +bool +pvr_power_is_idle(struct pvr_device *pvr_dev) +{ + /* + * FW power state can be out of date if a KCCB command has been submitted but the FW hasn't + * started processing it yet. So also check the KCCB status. + */ + enum rogue_fwif_pow_state pow_state = READ_ONCE(pvr_dev->fw_dev.fwif_sysdata->pow_state); + bool kccb_idle = pvr_kccb_is_idle(pvr_dev); + + return (pow_state == ROGUE_FWIF_POW_IDLE) && kccb_idle; +} + +static bool +pvr_watchdog_kccb_stalled(struct pvr_device *pvr_dev) +{ + /* Check KCCB commands are progressing. */ + u32 kccb_cmds_executed = pvr_dev->fw_dev.fwif_osdata->kccb_cmds_executed; + bool kccb_is_idle = pvr_kccb_is_idle(pvr_dev); + + if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed && !kccb_is_idle) { + pvr_dev->watchdog.kccb_stall_count++; + + /* + * If we have commands pending with no progress for 2 consecutive polls then + * consider KCCB command processing stalled. + */ + if (pvr_dev->watchdog.kccb_stall_count == 2) { + pvr_dev->watchdog.kccb_stall_count = 0; + return true; + } + } else if (pvr_dev->watchdog.old_kccb_cmds_executed == kccb_cmds_executed) { + bool has_active_contexts; + + mutex_lock(&pvr_dev->queues.lock); + has_active_contexts = list_empty(&pvr_dev->queues.active); + mutex_unlock(&pvr_dev->queues.lock); + + if (has_active_contexts) { + /* Send a HEALTH_CHECK command so we can verify FW is still alive. */ + struct rogue_fwif_kccb_cmd health_check_cmd; + + health_check_cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_HEALTH_CHECK; + + pvr_kccb_send_cmd_powered(pvr_dev, &health_check_cmd, NULL); + } + } else { + pvr_dev->watchdog.old_kccb_cmds_executed = kccb_cmds_executed; + pvr_dev->watchdog.kccb_stall_count = 0; + } + + return false; +} + +static void +pvr_watchdog_worker(struct work_struct *work) +{ + struct pvr_device *pvr_dev = container_of(work, struct pvr_device, + watchdog.work.work); + bool stalled; + + if (pvr_dev->lost) + return; + + if (pm_runtime_get_if_in_use(from_pvr_device(pvr_dev)->dev) <= 0) + goto out_requeue; + + if (!pvr_dev->fw_dev.booted) + goto out_pm_runtime_put; + + stalled = pvr_watchdog_kccb_stalled(pvr_dev); + + if (stalled) { + drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset"); + + pvr_power_reset(pvr_dev, true); + /* Device may be lost at this point. */ + } + +out_pm_runtime_put: + pm_runtime_put(from_pvr_device(pvr_dev)->dev); + +out_requeue: + if (!pvr_dev->lost) { + queue_delayed_work(pvr_dev->sched_wq, &pvr_dev->watchdog.work, + msecs_to_jiffies(WATCHDOG_TIME_MS)); + } +} + +/** + * pvr_watchdog_init() - Initialise watchdog for device + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, or + * * -%ENOMEM on out of memory. + */ +int +pvr_watchdog_init(struct pvr_device *pvr_dev) +{ + INIT_DELAYED_WORK(&pvr_dev->watchdog.work, pvr_watchdog_worker); + + return 0; +} + +int +pvr_power_device_suspend(struct device *dev) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct drm_device *drm_dev = platform_get_drvdata(plat_dev); + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + int err = 0; + int idx; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + if (pvr_dev->fw_dev.booted) { + err = pvr_power_fw_disable(pvr_dev, false); + if (err) + goto err_drm_dev_exit; + } + + clk_disable_unprepare(pvr_dev->mem_clk); + clk_disable_unprepare(pvr_dev->sys_clk); + clk_disable_unprepare(pvr_dev->core_clk); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +int +pvr_power_device_resume(struct device *dev) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct drm_device *drm_dev = platform_get_drvdata(plat_dev); + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + err = clk_prepare_enable(pvr_dev->core_clk); + if (err) + goto err_drm_dev_exit; + + err = clk_prepare_enable(pvr_dev->sys_clk); + if (err) + goto err_core_clk_disable; + + err = clk_prepare_enable(pvr_dev->mem_clk); + if (err) + goto err_sys_clk_disable; + + if (pvr_dev->fw_dev.booted) { + err = pvr_power_fw_enable(pvr_dev); + if (err) + goto err_mem_clk_disable; + } + + drm_dev_exit(idx); + + return 0; + +err_mem_clk_disable: + clk_disable_unprepare(pvr_dev->mem_clk); + +err_sys_clk_disable: + clk_disable_unprepare(pvr_dev->sys_clk); + +err_core_clk_disable: + clk_disable_unprepare(pvr_dev->core_clk); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + +int +pvr_power_device_idle(struct device *dev) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct drm_device *drm_dev = platform_get_drvdata(plat_dev); + struct pvr_device *pvr_dev = to_pvr_device(drm_dev); + + return pvr_power_is_idle(pvr_dev) ? 0 : -EBUSY; +} + +/** + * pvr_power_reset() - Reset the GPU + * @pvr_dev: Device pointer + * @hard_reset: %true for hard reset, %false for soft reset + * + * If @hard_reset is %false and the FW processor fails to respond during the reset process, this + * function will attempt a hard reset. + * + * If a hard reset fails then the GPU device is reported as lost. + * + * Returns: + * * 0 on success, or + * * Any error code returned by pvr_power_get, pvr_power_fw_disable or pvr_power_fw_enable(). + */ +int +pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset) +{ + bool queues_disabled = false; + int err; + + /* + * Take a power reference during the reset. This should prevent any interference with the + * power state during reset. + */ + WARN_ON(pvr_power_get(pvr_dev)); + + down_write(&pvr_dev->reset_sem); + + if (pvr_dev->lost) { + err = -EIO; + goto err_up_write; + } + + /* Disable IRQs for the duration of the reset. */ + disable_irq(pvr_dev->irq); + + do { + if (hard_reset) { + pvr_queue_device_pre_reset(pvr_dev); + queues_disabled = true; + } + + err = pvr_power_fw_disable(pvr_dev, hard_reset); + if (!err) { + if (hard_reset) { + pvr_dev->fw_dev.booted = false; + WARN_ON(pm_runtime_force_suspend(from_pvr_device(pvr_dev)->dev)); + + err = pvr_fw_hard_reset(pvr_dev); + if (err) + goto err_device_lost; + + err = pm_runtime_force_resume(from_pvr_device(pvr_dev)->dev); + pvr_dev->fw_dev.booted = true; + if (err) + goto err_device_lost; + } else { + /* Clear the FW faulted flags. */ + pvr_dev->fw_dev.fwif_sysdata->hwr_state_flags &= + ~(ROGUE_FWIF_HWR_FW_FAULT | + ROGUE_FWIF_HWR_RESTART_REQUESTED); + } + + pvr_fw_irq_clear(pvr_dev); + + err = pvr_power_fw_enable(pvr_dev); + } + + if (err && hard_reset) + goto err_device_lost; + + if (err && !hard_reset) { + drm_err(from_pvr_device(pvr_dev), "FW stalled, trying hard reset"); + hard_reset = true; + } + } while (err); + + if (queues_disabled) + pvr_queue_device_post_reset(pvr_dev); + + enable_irq(pvr_dev->irq); + + up_write(&pvr_dev->reset_sem); + + pvr_power_put(pvr_dev); + + return 0; + +err_device_lost: + drm_err(from_pvr_device(pvr_dev), "GPU device lost"); + pvr_device_lost(pvr_dev); + + /* Leave IRQs disabled if the device is lost. */ + + if (queues_disabled) + pvr_queue_device_post_reset(pvr_dev); + +err_up_write: + up_write(&pvr_dev->reset_sem); + + pvr_power_put(pvr_dev); + + return err; +} + +/** + * pvr_watchdog_fini() - Shutdown watchdog for device + * @pvr_dev: Target PowerVR device. + */ +void +pvr_watchdog_fini(struct pvr_device *pvr_dev) +{ + cancel_delayed_work_sync(&pvr_dev->watchdog.work); +} diff --git a/drivers/gpu/drm/imagination/pvr_power.h b/drivers/gpu/drm/imagination/pvr_power.h new file mode 100644 index 000000000000..9a9312dcb2da --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_power.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_POWER_H +#define PVR_POWER_H + +#include "pvr_device.h" + +#include <linux/mutex.h> +#include <linux/pm_runtime.h> + +int pvr_watchdog_init(struct pvr_device *pvr_dev); +void pvr_watchdog_fini(struct pvr_device *pvr_dev); + +void pvr_device_lost(struct pvr_device *pvr_dev); + +bool pvr_power_is_idle(struct pvr_device *pvr_dev); + +int pvr_power_device_suspend(struct device *dev); +int pvr_power_device_resume(struct device *dev); +int pvr_power_device_idle(struct device *dev); + +int pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset); + +static __always_inline int +pvr_power_get(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + + return pm_runtime_resume_and_get(drm_dev->dev); +} + +static __always_inline int +pvr_power_put(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + + return pm_runtime_put(drm_dev->dev); +} + +#endif /* PVR_POWER_H */ diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c new file mode 100644 index 000000000000..d65c3fbedf5a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -0,0 +1,1432 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include <drm/drm_managed.h> +#include <drm/gpu_scheduler.h> + +#include "pvr_cccb.h" +#include "pvr_context.h" +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_job.h" +#include "pvr_queue.h" +#include "pvr_vm.h" + +#include "pvr_rogue_fwif_client.h" + +#define MAX_DEADLINE_MS 30000 + +#define CTX_COMPUTE_CCCB_SIZE_LOG2 15 +#define CTX_FRAG_CCCB_SIZE_LOG2 15 +#define CTX_GEOM_CCCB_SIZE_LOG2 15 +#define CTX_TRANSFER_CCCB_SIZE_LOG2 15 + +static int get_xfer_ctx_state_size(struct pvr_device *pvr_dev) +{ + u32 num_isp_store_registers; + + if (PVR_HAS_FEATURE(pvr_dev, xe_memory_hierarchy)) { + num_isp_store_registers = 1; + } else { + int err; + + err = PVR_FEATURE_VALUE(pvr_dev, num_isp_ipp_pipes, &num_isp_store_registers); + if (WARN_ON(err)) + return err; + } + + return sizeof(struct rogue_fwif_frag_ctx_state) + + (num_isp_store_registers * + sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0])); +} + +static int get_frag_ctx_state_size(struct pvr_device *pvr_dev) +{ + u32 num_isp_store_registers; + int err; + + if (PVR_HAS_FEATURE(pvr_dev, xe_memory_hierarchy)) { + err = PVR_FEATURE_VALUE(pvr_dev, num_raster_pipes, &num_isp_store_registers); + if (WARN_ON(err)) + return err; + + if (PVR_HAS_FEATURE(pvr_dev, gpu_multicore_support)) { + u32 xpu_max_slaves; + + err = PVR_FEATURE_VALUE(pvr_dev, xpu_max_slaves, &xpu_max_slaves); + if (WARN_ON(err)) + return err; + + num_isp_store_registers *= (1 + xpu_max_slaves); + } + } else { + err = PVR_FEATURE_VALUE(pvr_dev, num_isp_ipp_pipes, &num_isp_store_registers); + if (WARN_ON(err)) + return err; + } + + return sizeof(struct rogue_fwif_frag_ctx_state) + + (num_isp_store_registers * + sizeof(((struct rogue_fwif_frag_ctx_state *)0)->frag_reg_isp_store[0])); +} + +static int get_ctx_state_size(struct pvr_device *pvr_dev, enum drm_pvr_job_type type) +{ + switch (type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return sizeof(struct rogue_fwif_geom_ctx_state); + case DRM_PVR_JOB_TYPE_FRAGMENT: + return get_frag_ctx_state_size(pvr_dev); + case DRM_PVR_JOB_TYPE_COMPUTE: + return sizeof(struct rogue_fwif_compute_ctx_state); + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return get_xfer_ctx_state_size(pvr_dev); + } + + WARN(1, "Invalid queue type"); + return -EINVAL; +} + +static u32 get_ctx_offset(enum drm_pvr_job_type type) +{ + switch (type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return offsetof(struct rogue_fwif_fwrendercontext, geom_context); + case DRM_PVR_JOB_TYPE_FRAGMENT: + return offsetof(struct rogue_fwif_fwrendercontext, frag_context); + case DRM_PVR_JOB_TYPE_COMPUTE: + return offsetof(struct rogue_fwif_fwcomputecontext, cdm_context); + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return offsetof(struct rogue_fwif_fwtransfercontext, tq_context); + } + + return 0; +} + +static const char * +pvr_queue_fence_get_driver_name(struct dma_fence *f) +{ + return PVR_DRIVER_NAME; +} + +static void pvr_queue_fence_release(struct dma_fence *f) +{ + struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base); + + pvr_context_put(fence->queue->ctx); + dma_fence_free(f); +} + +static const char * +pvr_queue_job_fence_get_timeline_name(struct dma_fence *f) +{ + struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base); + + switch (fence->queue->type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return "geometry"; + + case DRM_PVR_JOB_TYPE_FRAGMENT: + return "fragment"; + + case DRM_PVR_JOB_TYPE_COMPUTE: + return "compute"; + + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return "transfer"; + } + + WARN(1, "Invalid queue type"); + return "invalid"; +} + +static const char * +pvr_queue_cccb_fence_get_timeline_name(struct dma_fence *f) +{ + struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base); + + switch (fence->queue->type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return "geometry-cccb"; + + case DRM_PVR_JOB_TYPE_FRAGMENT: + return "fragment-cccb"; + + case DRM_PVR_JOB_TYPE_COMPUTE: + return "compute-cccb"; + + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + return "transfer-cccb"; + } + + WARN(1, "Invalid queue type"); + return "invalid"; +} + +static const struct dma_fence_ops pvr_queue_job_fence_ops = { + .get_driver_name = pvr_queue_fence_get_driver_name, + .get_timeline_name = pvr_queue_job_fence_get_timeline_name, + .release = pvr_queue_fence_release, +}; + +/** + * to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is + * backed by a UFO. + * @f: The dma_fence to turn into a pvr_queue_fence. + * + * Return: + * * A non-NULL pvr_queue_fence object if the dma_fence is backed by a UFO, or + * * NULL otherwise. + */ +static struct pvr_queue_fence * +to_pvr_queue_job_fence(struct dma_fence *f) +{ + struct drm_sched_fence *sched_fence = to_drm_sched_fence(f); + + if (sched_fence) + f = sched_fence->parent; + + if (f && f->ops == &pvr_queue_job_fence_ops) + return container_of(f, struct pvr_queue_fence, base); + + return NULL; +} + +static const struct dma_fence_ops pvr_queue_cccb_fence_ops = { + .get_driver_name = pvr_queue_fence_get_driver_name, + .get_timeline_name = pvr_queue_cccb_fence_get_timeline_name, + .release = pvr_queue_fence_release, +}; + +/** + * pvr_queue_fence_put() - Put wrapper for pvr_queue_fence objects. + * @f: The dma_fence object to put. + * + * If the pvr_queue_fence has been initialized, we call dma_fence_put(), + * otherwise we free the object with dma_fence_free(). This allows us + * to do the right thing before and after pvr_queue_fence_init() had been + * called. + */ +static void pvr_queue_fence_put(struct dma_fence *f) +{ + if (!f) + return; + + if (WARN_ON(f->ops && + f->ops != &pvr_queue_cccb_fence_ops && + f->ops != &pvr_queue_job_fence_ops)) + return; + + /* If the fence hasn't been initialized yet, free the object directly. */ + if (f->ops) + dma_fence_put(f); + else + dma_fence_free(f); +} + +/** + * pvr_queue_fence_alloc() - Allocate a pvr_queue_fence fence object + * + * Call this function to allocate job CCCB and done fences. This only + * allocates the objects. Initialization happens when the underlying + * dma_fence object is to be returned to drm_sched (in prepare_job() or + * run_job()). + * + * Return: + * * A valid pointer if the allocation succeeds, or + * * NULL if the allocation fails. + */ +static struct dma_fence * +pvr_queue_fence_alloc(void) +{ + struct pvr_queue_fence *fence; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) + return NULL; + + return &fence->base; +} + +/** + * pvr_queue_fence_init() - Initializes a pvr_queue_fence object. + * @f: The fence to initialize + * @queue: The queue this fence belongs to. + * @fence_ops: The fence operations. + * @fence_ctx: The fence context. + * + * Wrapper around dma_fence_init() that takes care of initializing the + * pvr_queue_fence::queue field too. + */ +static void +pvr_queue_fence_init(struct dma_fence *f, + struct pvr_queue *queue, + const struct dma_fence_ops *fence_ops, + struct pvr_queue_fence_ctx *fence_ctx) +{ + struct pvr_queue_fence *fence = container_of(f, struct pvr_queue_fence, base); + + pvr_context_get(queue->ctx); + fence->queue = queue; + dma_fence_init(&fence->base, fence_ops, + &fence_ctx->lock, fence_ctx->id, + atomic_inc_return(&fence_ctx->seqno)); +} + +/** + * pvr_queue_cccb_fence_init() - Initializes a CCCB fence object. + * @fence: The fence to initialize. + * @queue: The queue this fence belongs to. + * + * Initializes a fence that can be used to wait for CCCB space. + * + * Should be called in the ::prepare_job() path, so the fence returned to + * drm_sched is valid. + */ +static void +pvr_queue_cccb_fence_init(struct dma_fence *fence, struct pvr_queue *queue) +{ + pvr_queue_fence_init(fence, queue, &pvr_queue_cccb_fence_ops, + &queue->cccb_fence_ctx.base); +} + +/** + * pvr_queue_job_fence_init() - Initializes a job done fence object. + * @fence: The fence to initialize. + * @queue: The queue this fence belongs to. + * + * Initializes a fence that will be signaled when the GPU is done executing + * a job. + * + * Should be called *before* the ::run_job() path, so the fence is initialised + * before being placed in the pending_list. + */ +static void +pvr_queue_job_fence_init(struct dma_fence *fence, struct pvr_queue *queue) +{ + pvr_queue_fence_init(fence, queue, &pvr_queue_job_fence_ops, + &queue->job_fence_ctx); +} + +/** + * pvr_queue_fence_ctx_init() - Queue fence context initialization. + * @fence_ctx: The context to initialize + */ +static void +pvr_queue_fence_ctx_init(struct pvr_queue_fence_ctx *fence_ctx) +{ + spin_lock_init(&fence_ctx->lock); + fence_ctx->id = dma_fence_context_alloc(1); + atomic_set(&fence_ctx->seqno, 0); +} + +static u32 ufo_cmds_size(u32 elem_count) +{ + /* We can pass at most ROGUE_FWIF_CCB_CMD_MAX_UFOS per UFO-related command. */ + u32 full_cmd_count = elem_count / ROGUE_FWIF_CCB_CMD_MAX_UFOS; + u32 remaining_elems = elem_count % ROGUE_FWIF_CCB_CMD_MAX_UFOS; + u32 size = full_cmd_count * + pvr_cccb_get_size_of_cmd_with_hdr(ROGUE_FWIF_CCB_CMD_MAX_UFOS * + sizeof(struct rogue_fwif_ufo)); + + if (remaining_elems) { + size += pvr_cccb_get_size_of_cmd_with_hdr(remaining_elems * + sizeof(struct rogue_fwif_ufo)); + } + + return size; +} + +static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count) +{ + /* One UFO cmd for the fence signaling, one UFO cmd per native fence native, + * and a command for the job itself. + */ + return ufo_cmds_size(1) + ufo_cmds_size(ufo_wait_count) + + pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len); +} + +/** + * job_count_remaining_native_deps() - Count the number of non-signaled native dependencies. + * @job: Job to operate on. + * + * Returns: Number of non-signaled native deps remaining. + */ +static unsigned long job_count_remaining_native_deps(struct pvr_job *job) +{ + unsigned long remaining_count = 0; + struct dma_fence *fence = NULL; + unsigned long index; + + xa_for_each(&job->base.dependencies, index, fence) { + struct pvr_queue_fence *jfence; + + jfence = to_pvr_queue_job_fence(fence); + if (!jfence) + continue; + + if (!dma_fence_is_signaled(&jfence->base)) + remaining_count++; + } + + return remaining_count; +} + +/** + * pvr_queue_get_job_cccb_fence() - Get the CCCB fence attached to a job. + * @queue: The queue this job will be submitted to. + * @job: The job to get the CCCB fence on. + * + * The CCCB fence is a synchronization primitive allowing us to delay job + * submission until there's enough space in the CCCB to submit the job. + * + * Return: + * * NULL if there's enough space in the CCCB to submit this job, or + * * A valid dma_fence object otherwise. + */ +static struct dma_fence * +pvr_queue_get_job_cccb_fence(struct pvr_queue *queue, struct pvr_job *job) +{ + struct pvr_queue_fence *cccb_fence; + unsigned int native_deps_remaining; + + /* If the fence is NULL, that means we already checked that we had + * enough space in the cccb for our job. + */ + if (!job->cccb_fence) + return NULL; + + mutex_lock(&queue->cccb_fence_ctx.job_lock); + + /* Count remaining native dependencies and check if the job fits in the CCCB. */ + native_deps_remaining = job_count_remaining_native_deps(job); + if (pvr_cccb_cmdseq_fits(&queue->cccb, job_cmds_size(job, native_deps_remaining))) { + pvr_queue_fence_put(job->cccb_fence); + job->cccb_fence = NULL; + goto out_unlock; + } + + /* There should be no job attached to the CCCB fence context: + * drm_sched_entity guarantees that jobs are submitted one at a time. + */ + if (WARN_ON(queue->cccb_fence_ctx.job)) + pvr_job_put(queue->cccb_fence_ctx.job); + + queue->cccb_fence_ctx.job = pvr_job_get(job); + + /* Initialize the fence before returning it. */ + cccb_fence = container_of(job->cccb_fence, struct pvr_queue_fence, base); + if (!WARN_ON(cccb_fence->queue)) + pvr_queue_cccb_fence_init(job->cccb_fence, queue); + +out_unlock: + mutex_unlock(&queue->cccb_fence_ctx.job_lock); + + return dma_fence_get(job->cccb_fence); +} + +/** + * pvr_queue_get_job_kccb_fence() - Get the KCCB fence attached to a job. + * @queue: The queue this job will be submitted to. + * @job: The job to get the KCCB fence on. + * + * The KCCB fence is a synchronization primitive allowing us to delay job + * submission until there's enough space in the KCCB to submit the job. + * + * Return: + * * NULL if there's enough space in the KCCB to submit this job, or + * * A valid dma_fence object otherwise. + */ +static struct dma_fence * +pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job) +{ + struct pvr_device *pvr_dev = queue->ctx->pvr_dev; + struct dma_fence *kccb_fence = NULL; + + /* If the fence is NULL, that means we already checked that we had + * enough space in the KCCB for our job. + */ + if (!job->kccb_fence) + return NULL; + + if (!WARN_ON(job->kccb_fence->ops)) { + kccb_fence = pvr_kccb_reserve_slot(pvr_dev, job->kccb_fence); + job->kccb_fence = NULL; + } + + return kccb_fence; +} + +static struct dma_fence * +pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job) +{ + struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ? + job->paired_job : NULL; + struct dma_fence *f; + unsigned long index; + + if (!frag_job) + return NULL; + + xa_for_each(&frag_job->base.dependencies, index, f) { + /* Skip already signaled fences. */ + if (dma_fence_is_signaled(f)) + continue; + + /* Skip our own fence. */ + if (f == &job->base.s_fence->scheduled) + continue; + + return dma_fence_get(f); + } + + return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity); +} + +/** + * pvr_queue_prepare_job() - Return the next internal dependencies expressed as a dma_fence. + * @sched_job: The job to query the next internal dependency on + * @s_entity: The entity this job is queue on. + * + * After iterating over drm_sched_job::dependencies, drm_sched let the driver return + * its own internal dependencies. We use this function to return our internal dependencies. + */ +static struct dma_fence * +pvr_queue_prepare_job(struct drm_sched_job *sched_job, + struct drm_sched_entity *s_entity) +{ + struct pvr_job *job = container_of(sched_job, struct pvr_job, base); + struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity); + struct dma_fence *internal_dep = NULL; + + /* + * Initialize the done_fence, so we can signal it. This must be done + * here because otherwise by the time of run_job() the job will end up + * in the pending list without a valid fence. + */ + if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { + /* + * This will be called on a paired fragment job after being + * submitted to firmware. We can tell if this is the case and + * bail early from whether run_job() has been called on the + * geometry job, which would issue a pm ref. + */ + if (job->paired_job->has_pm_ref) + return NULL; + + /* + * In this case we need to use the job's own ctx to initialise + * the done_fence. The other steps are done in the ctx of the + * paired geometry job. + */ + pvr_queue_job_fence_init(job->done_fence, + job->ctx->queues.fragment); + } else { + pvr_queue_job_fence_init(job->done_fence, queue); + } + + /* CCCB fence is used to make sure we have enough space in the CCCB to + * submit our commands. + */ + internal_dep = pvr_queue_get_job_cccb_fence(queue, job); + + /* KCCB fence is used to make sure we have a KCCB slot to queue our + * CMD_KICK. + */ + if (!internal_dep) + internal_dep = pvr_queue_get_job_kccb_fence(queue, job); + + /* Any extra internal dependency should be added here, using the following + * pattern: + * + * if (!internal_dep) + * internal_dep = pvr_queue_get_job_xxxx_fence(queue, job); + */ + + /* The paired job fence should come last, when everything else is ready. */ + if (!internal_dep) + internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job); + + return internal_dep; +} + +/** + * pvr_queue_update_active_state_locked() - Update the queue active state. + * @queue: Queue to update the state on. + * + * Locked version of pvr_queue_update_active_state(). Must be called with + * pvr_device::queue::lock held. + */ +static void pvr_queue_update_active_state_locked(struct pvr_queue *queue) +{ + struct pvr_device *pvr_dev = queue->ctx->pvr_dev; + + lockdep_assert_held(&pvr_dev->queues.lock); + + /* The queue is temporary out of any list when it's being reset, + * we don't want a call to pvr_queue_update_active_state_locked() + * to re-insert it behind our back. + */ + if (list_empty(&queue->node)) + return; + + if (!atomic_read(&queue->in_flight_job_count)) + list_move_tail(&queue->node, &pvr_dev->queues.idle); + else + list_move_tail(&queue->node, &pvr_dev->queues.active); +} + +/** + * pvr_queue_update_active_state() - Update the queue active state. + * @queue: Queue to update the state on. + * + * Active state is based on the in_flight_job_count value. + * + * Updating the active state implies moving the queue in or out of the + * active queue list, which also defines whether the queue is checked + * or not when a FW event is received. + * + * This function should be called any time a job is submitted or it done + * fence is signaled. + */ +static void pvr_queue_update_active_state(struct pvr_queue *queue) +{ + struct pvr_device *pvr_dev = queue->ctx->pvr_dev; + + mutex_lock(&pvr_dev->queues.lock); + pvr_queue_update_active_state_locked(queue); + mutex_unlock(&pvr_dev->queues.lock); +} + +static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) +{ + struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler); + struct rogue_fwif_ufo ufos[ROGUE_FWIF_CCB_CMD_MAX_UFOS]; + struct pvr_cccb *cccb = &queue->cccb; + struct pvr_queue_fence *jfence; + struct dma_fence *fence; + unsigned long index; + u32 ufo_count = 0; + + /* We need to add the queue to the active list before updating the CCCB, + * otherwise we might miss the FW event informing us that something + * happened on this queue. + */ + atomic_inc(&queue->in_flight_job_count); + pvr_queue_update_active_state(queue); + + xa_for_each(&job->base.dependencies, index, fence) { + jfence = to_pvr_queue_job_fence(fence); + if (!jfence) + continue; + + /* Skip the partial render fence, we will place it at the end. */ + if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job && + &job->paired_job->base.s_fence->scheduled == fence) + continue; + + if (dma_fence_is_signaled(&jfence->base)) + continue; + + pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj, + &ufos[ufo_count].addr); + ufos[ufo_count++].value = jfence->base.seqno; + + if (ufo_count == ARRAY_SIZE(ufos)) { + pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_FENCE_PR, + sizeof(ufos), ufos, 0, 0); + ufo_count = 0; + } + } + + /* Partial render fence goes last. */ + if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) { + jfence = to_pvr_queue_job_fence(job->paired_job->done_fence); + if (!WARN_ON(!jfence)) { + pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj, + &ufos[ufo_count].addr); + ufos[ufo_count++].value = job->paired_job->done_fence->seqno; + } + } + + if (ufo_count) { + pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_FENCE_PR, + sizeof(ufos[0]) * ufo_count, ufos, 0, 0); + } + + if (job->type == DRM_PVR_JOB_TYPE_GEOMETRY && job->paired_job) { + struct rogue_fwif_cmd_geom *cmd = job->cmd; + + /* Reference value for the partial render test is the current queue fence + * seqno minus one. + */ + pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, + &cmd->partial_render_geom_frag_fence.addr); + cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1; + } + + /* Submit job to FW */ + pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd, + job->id, job->id); + + /* Signal the job fence. */ + pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, &ufos[0].addr); + ufos[0].value = job->done_fence->seqno; + pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_UPDATE, + sizeof(ufos[0]), ufos, 0, 0); +} + +/** + * pvr_queue_run_job() - Submit a job to the FW. + * @sched_job: The job to submit. + * + * This function is called when all non-native dependencies have been met and + * when the commands resulting from this job are guaranteed to fit in the CCCB. + */ +static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) +{ + struct pvr_job *job = container_of(sched_job, struct pvr_job, base); + struct pvr_device *pvr_dev = job->pvr_dev; + int err; + + /* The fragment job is issued along the geometry job when we use combined + * geom+frag kicks. When we get there, we should simply return the + * done_fence that's been initialized earlier. + */ + if (job->paired_job && job->type == DRM_PVR_JOB_TYPE_FRAGMENT && + job->done_fence->ops) { + return dma_fence_get(job->done_fence); + } + + /* The only kind of jobs that can be paired are geometry and fragment, and + * we bail out early if we see a fragment job that's paired with a geomtry + * job. + * Paired jobs must also target the same context and point to the same + * HWRT. + */ + if (WARN_ON(job->paired_job && + (job->type != DRM_PVR_JOB_TYPE_GEOMETRY || + job->paired_job->type != DRM_PVR_JOB_TYPE_FRAGMENT || + job->hwrt != job->paired_job->hwrt || + job->ctx != job->paired_job->ctx))) + return ERR_PTR(-EINVAL); + + err = pvr_job_get_pm_ref(job); + if (WARN_ON(err)) + return ERR_PTR(err); + + if (job->paired_job) { + err = pvr_job_get_pm_ref(job->paired_job); + if (WARN_ON(err)) + return ERR_PTR(err); + } + + /* Submit our job to the CCCB */ + pvr_queue_submit_job_to_cccb(job); + + if (job->paired_job) { + struct pvr_job *geom_job = job; + struct pvr_job *frag_job = job->paired_job; + struct pvr_queue *geom_queue = job->ctx->queues.geometry; + struct pvr_queue *frag_queue = job->ctx->queues.fragment; + + /* Submit the fragment job along the geometry job and send a combined kick. */ + pvr_queue_submit_job_to_cccb(frag_job); + pvr_cccb_send_kccb_combined_kick(pvr_dev, + &geom_queue->cccb, &frag_queue->cccb, + pvr_context_get_fw_addr(geom_job->ctx) + + geom_queue->ctx_offset, + pvr_context_get_fw_addr(frag_job->ctx) + + frag_queue->ctx_offset, + job->hwrt, + frag_job->fw_ccb_cmd_type == + ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR); + } else { + struct pvr_queue *queue = container_of(job->base.sched, + struct pvr_queue, scheduler); + + pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb, + pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset, + job->hwrt); + } + + return dma_fence_get(job->done_fence); +} + +static void pvr_queue_stop(struct pvr_queue *queue, struct pvr_job *bad_job) +{ + drm_sched_stop(&queue->scheduler, bad_job ? &bad_job->base : NULL); +} + +static void pvr_queue_start(struct pvr_queue *queue) +{ + struct pvr_job *job; + + /* Make sure we CPU-signal the UFO object, so other queues don't get + * blocked waiting on it. + */ + *queue->timeline_ufo.value = atomic_read(&queue->job_fence_ctx.seqno); + + list_for_each_entry(job, &queue->scheduler.pending_list, base.list) { + if (dma_fence_is_signaled(job->done_fence)) { + /* Jobs might have completed after drm_sched_stop() was called. + * In that case, re-assign the parent field to the done_fence. + */ + WARN_ON(job->base.s_fence->parent); + job->base.s_fence->parent = dma_fence_get(job->done_fence); + } else { + /* If we had unfinished jobs, flag the entity as guilty so no + * new job can be submitted. + */ + atomic_set(&queue->ctx->faulty, 1); + } + } + + drm_sched_start(&queue->scheduler, true); +} + +/** + * pvr_queue_timedout_job() - Handle a job timeout event. + * @s_job: The job this timeout occurred on. + * + * FIXME: We don't do anything here to unblock the situation, we just stop+start + * the scheduler, and re-assign parent fences in the middle. + * + * Return: + * * DRM_GPU_SCHED_STAT_NOMINAL. + */ +static enum drm_gpu_sched_stat +pvr_queue_timedout_job(struct drm_sched_job *s_job) +{ + struct drm_gpu_scheduler *sched = s_job->sched; + struct pvr_queue *queue = container_of(sched, struct pvr_queue, scheduler); + struct pvr_device *pvr_dev = queue->ctx->pvr_dev; + struct pvr_job *job; + u32 job_count = 0; + + dev_err(sched->dev, "Job timeout\n"); + + /* Before we stop the scheduler, make sure the queue is out of any list, so + * any call to pvr_queue_update_active_state_locked() that might happen + * until the scheduler is really stopped doesn't end up re-inserting the + * queue in the active list. This would cause + * pvr_queue_signal_done_fences() and drm_sched_stop() to race with each + * other when accessing the pending_list, since drm_sched_stop() doesn't + * grab the job_list_lock when modifying the list (it's assuming the + * only other accessor is the scheduler, and it's safe to not grab the + * lock since it's stopped). + */ + mutex_lock(&pvr_dev->queues.lock); + list_del_init(&queue->node); + mutex_unlock(&pvr_dev->queues.lock); + + drm_sched_stop(sched, s_job); + + /* Re-assign job parent fences. */ + list_for_each_entry(job, &sched->pending_list, base.list) { + job->base.s_fence->parent = dma_fence_get(job->done_fence); + job_count++; + } + WARN_ON(atomic_read(&queue->in_flight_job_count) != job_count); + + /* Re-insert the queue in the proper list, and kick a queue processing + * operation if there were jobs pending. + */ + mutex_lock(&pvr_dev->queues.lock); + if (!job_count) { + list_move_tail(&queue->node, &pvr_dev->queues.idle); + } else { + atomic_set(&queue->in_flight_job_count, job_count); + list_move_tail(&queue->node, &pvr_dev->queues.active); + pvr_queue_process(queue); + } + mutex_unlock(&pvr_dev->queues.lock); + + drm_sched_start(sched, true); + + return DRM_GPU_SCHED_STAT_NOMINAL; +} + +/** + * pvr_queue_free_job() - Release the reference the scheduler had on a job object. + * @sched_job: Job object to free. + */ +static void pvr_queue_free_job(struct drm_sched_job *sched_job) +{ + struct pvr_job *job = container_of(sched_job, struct pvr_job, base); + + drm_sched_job_cleanup(sched_job); + job->paired_job = NULL; + pvr_job_put(job); +} + +static const struct drm_sched_backend_ops pvr_queue_sched_ops = { + .prepare_job = pvr_queue_prepare_job, + .run_job = pvr_queue_run_job, + .timedout_job = pvr_queue_timedout_job, + .free_job = pvr_queue_free_job, +}; + +/** + * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO object + * @f: Fence to test. + * + * A UFO-backed fence is a fence that can be signaled or waited upon FW-side. + * pvr_job::done_fence objects are backed by the timeline UFO attached to the queue + * they are pushed to, but those fences are not directly exposed to the outside + * world, so we also need to check if the fence we're being passed is a + * drm_sched_fence that was coming from our driver. + */ +bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f) +{ + struct drm_sched_fence *sched_fence = f ? to_drm_sched_fence(f) : NULL; + + if (sched_fence && + sched_fence->sched->ops == &pvr_queue_sched_ops) + return true; + + if (f && f->ops == &pvr_queue_job_fence_ops) + return true; + + return false; +} + +/** + * pvr_queue_signal_done_fences() - Signal done fences. + * @queue: Queue to check. + * + * Signal done fences of jobs whose seqno is less than the current value of + * the UFO object attached to the queue. + */ +static void +pvr_queue_signal_done_fences(struct pvr_queue *queue) +{ + struct pvr_job *job, *tmp_job; + u32 cur_seqno; + + spin_lock(&queue->scheduler.job_list_lock); + cur_seqno = *queue->timeline_ufo.value; + list_for_each_entry_safe(job, tmp_job, &queue->scheduler.pending_list, base.list) { + if ((int)(cur_seqno - lower_32_bits(job->done_fence->seqno)) < 0) + break; + + if (!dma_fence_is_signaled(job->done_fence)) { + dma_fence_signal(job->done_fence); + pvr_job_release_pm_ref(job); + atomic_dec(&queue->in_flight_job_count); + } + } + spin_unlock(&queue->scheduler.job_list_lock); +} + +/** + * pvr_queue_check_job_waiting_for_cccb_space() - Check if the job waiting for CCCB space + * can be unblocked + * pushed to the CCCB + * @queue: Queue to check + * + * If we have a job waiting for CCCB, and this job now fits in the CCCB, we signal + * its CCCB fence, which should kick drm_sched. + */ +static void +pvr_queue_check_job_waiting_for_cccb_space(struct pvr_queue *queue) +{ + struct pvr_queue_fence *cccb_fence; + u32 native_deps_remaining; + struct pvr_job *job; + + mutex_lock(&queue->cccb_fence_ctx.job_lock); + job = queue->cccb_fence_ctx.job; + if (!job) + goto out_unlock; + + /* If we have a job attached to the CCCB fence context, its CCCB fence + * shouldn't be NULL. + */ + if (WARN_ON(!job->cccb_fence)) { + job = NULL; + goto out_unlock; + } + + /* If we get there, CCCB fence has to be initialized. */ + cccb_fence = container_of(job->cccb_fence, struct pvr_queue_fence, base); + if (WARN_ON(!cccb_fence->queue)) { + job = NULL; + goto out_unlock; + } + + /* Evict signaled dependencies before checking for CCCB space. + * If the job fits, signal the CCCB fence, this should unblock + * the drm_sched_entity. + */ + native_deps_remaining = job_count_remaining_native_deps(job); + if (!pvr_cccb_cmdseq_fits(&queue->cccb, job_cmds_size(job, native_deps_remaining))) { + job = NULL; + goto out_unlock; + } + + dma_fence_signal(job->cccb_fence); + pvr_queue_fence_put(job->cccb_fence); + job->cccb_fence = NULL; + queue->cccb_fence_ctx.job = NULL; + +out_unlock: + mutex_unlock(&queue->cccb_fence_ctx.job_lock); + + pvr_job_put(job); +} + +/** + * pvr_queue_process() - Process events that happened on a queue. + * @queue: Queue to check + * + * Signal job fences and check if jobs waiting for CCCB space can be unblocked. + */ +void pvr_queue_process(struct pvr_queue *queue) +{ + lockdep_assert_held(&queue->ctx->pvr_dev->queues.lock); + + pvr_queue_check_job_waiting_for_cccb_space(queue); + pvr_queue_signal_done_fences(queue); + pvr_queue_update_active_state_locked(queue); +} + +static u32 get_dm_type(struct pvr_queue *queue) +{ + switch (queue->type) { + case DRM_PVR_JOB_TYPE_GEOMETRY: + return PVR_FWIF_DM_GEOM; + case DRM_PVR_JOB_TYPE_TRANSFER_FRAG: + case DRM_PVR_JOB_TYPE_FRAGMENT: + return PVR_FWIF_DM_FRAG; + case DRM_PVR_JOB_TYPE_COMPUTE: + return PVR_FWIF_DM_CDM; + } + + return ~0; +} + +/** + * init_fw_context() - Initializes the queue part of a FW context. + * @queue: Queue object to initialize the FW context for. + * @fw_ctx_map: The FW context CPU mapping. + * + * FW contexts are containing various states, one of them being a per-queue state + * that needs to be initialized for each queue being exposed by a context. This + * function takes care of that. + */ +static void init_fw_context(struct pvr_queue *queue, void *fw_ctx_map) +{ + struct pvr_context *ctx = queue->ctx; + struct pvr_fw_object *fw_mem_ctx_obj = pvr_vm_get_fw_mem_context(ctx->vm_ctx); + struct rogue_fwif_fwcommoncontext *cctx_fw; + struct pvr_cccb *cccb = &queue->cccb; + + cctx_fw = fw_ctx_map + queue->ctx_offset; + cctx_fw->ccbctl_fw_addr = cccb->ctrl_fw_addr; + cctx_fw->ccb_fw_addr = cccb->cccb_fw_addr; + + cctx_fw->dm = get_dm_type(queue); + cctx_fw->priority = ctx->priority; + cctx_fw->priority_seq_num = 0; + cctx_fw->max_deadline_ms = MAX_DEADLINE_MS; + cctx_fw->pid = task_tgid_nr(current); + cctx_fw->server_common_context_id = ctx->ctx_id; + + pvr_fw_object_get_fw_addr(fw_mem_ctx_obj, &cctx_fw->fw_mem_context_fw_addr); + + pvr_fw_object_get_fw_addr(queue->reg_state_obj, &cctx_fw->context_state_addr); +} + +/** + * pvr_queue_cleanup_fw_context() - Wait for the FW context to be idle and clean it up. + * @queue: Queue on FW context to clean up. + * + * Return: + * * 0 on success, + * * Any error returned by pvr_fw_structure_cleanup() otherwise. + */ +static int pvr_queue_cleanup_fw_context(struct pvr_queue *queue) +{ + if (!queue->ctx->fw_obj) + return 0; + + return pvr_fw_structure_cleanup(queue->ctx->pvr_dev, + ROGUE_FWIF_CLEANUP_FWCOMMONCONTEXT, + queue->ctx->fw_obj, queue->ctx_offset); +} + +/** + * pvr_queue_job_init() - Initialize queue related fields in a pvr_job object. + * @job: The job to initialize. + * + * Bind the job to a queue and allocate memory to guarantee pvr_queue_job_arm() + * and pvr_queue_job_push() can't fail. We also make sure the context type is + * valid and the job can fit in the CCCB. + * + * Return: + * * 0 on success, or + * * An error code if something failed. + */ +int pvr_queue_job_init(struct pvr_job *job) +{ + /* Fragment jobs need at least one native fence wait on the geometry job fence. */ + u32 min_native_dep_count = job->type == DRM_PVR_JOB_TYPE_FRAGMENT ? 1 : 0; + struct pvr_queue *queue; + int err; + + if (atomic_read(&job->ctx->faulty)) + return -EIO; + + queue = pvr_context_get_queue_for_job(job->ctx, job->type); + if (!queue) + return -EINVAL; + + if (!pvr_cccb_cmdseq_can_fit(&queue->cccb, job_cmds_size(job, min_native_dep_count))) + return -E2BIG; + + err = drm_sched_job_init(&job->base, &queue->entity, 1, THIS_MODULE); + if (err) + return err; + + job->cccb_fence = pvr_queue_fence_alloc(); + job->kccb_fence = pvr_kccb_fence_alloc(); + job->done_fence = pvr_queue_fence_alloc(); + if (!job->cccb_fence || !job->kccb_fence || !job->done_fence) + return -ENOMEM; + + return 0; +} + +/** + * pvr_queue_job_arm() - Arm a job object. + * @job: The job to arm. + * + * Initializes fences and return the drm_sched finished fence so it can + * be exposed to the outside world. Once this function is called, you should + * make sure the job is pushed using pvr_queue_job_push(), or guarantee that + * no one grabbed a reference to the returned fence. The latter can happen if + * we do multi-job submission, and something failed when creating/initializing + * a job. In that case, we know the fence didn't leave the driver, and we + * can thus guarantee nobody will wait on an dead fence object. + * + * Return: + * * A dma_fence object. + */ +struct dma_fence *pvr_queue_job_arm(struct pvr_job *job) +{ + drm_sched_job_arm(&job->base); + + return &job->base.s_fence->finished; +} + +/** + * pvr_queue_job_cleanup() - Cleanup fence/scheduler related fields in the job object. + * @job: The job to cleanup. + * + * Should be called in the job release path. + */ +void pvr_queue_job_cleanup(struct pvr_job *job) +{ + pvr_queue_fence_put(job->done_fence); + pvr_queue_fence_put(job->cccb_fence); + pvr_kccb_fence_put(job->kccb_fence); + + if (job->base.s_fence) + drm_sched_job_cleanup(&job->base); +} + +/** + * pvr_queue_job_push() - Push a job to its queue. + * @job: The job to push. + * + * Must be called after pvr_queue_job_init() and after all dependencies + * have been added to the job. This will effectively queue the job to + * the drm_sched_entity attached to the queue. We grab a reference on + * the job object, so the caller is free to drop its reference when it's + * done accessing the job object. + */ +void pvr_queue_job_push(struct pvr_job *job) +{ + struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler); + + /* Keep track of the last queued job scheduled fence for combined submit. */ + dma_fence_put(queue->last_queued_job_scheduled_fence); + queue->last_queued_job_scheduled_fence = dma_fence_get(&job->base.s_fence->scheduled); + + pvr_job_get(job); + drm_sched_entity_push_job(&job->base); +} + +static void reg_state_init(void *cpu_ptr, void *priv) +{ + struct pvr_queue *queue = priv; + + if (queue->type == DRM_PVR_JOB_TYPE_GEOMETRY) { + struct rogue_fwif_geom_ctx_state *geom_ctx_state_fw = cpu_ptr; + + geom_ctx_state_fw->geom_core[0].geom_reg_vdm_call_stack_pointer_init = + queue->callstack_addr; + } +} + +/** + * pvr_queue_create() - Create a queue object. + * @ctx: The context this queue will be attached to. + * @type: The type of jobs being pushed to this queue. + * @args: The arguments passed to the context creation function. + * @fw_ctx_map: CPU mapping of the FW context object. + * + * Create a queue object that will be used to queue and track jobs. + * + * Return: + * * A valid pointer to a pvr_queue object, or + * * An error pointer if the creation/initialization failed. + */ +struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, + enum drm_pvr_job_type type, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map) +{ + static const struct { + u32 cccb_size; + const char *name; + } props[] = { + [DRM_PVR_JOB_TYPE_GEOMETRY] = { + .cccb_size = CTX_GEOM_CCCB_SIZE_LOG2, + .name = "geometry", + }, + [DRM_PVR_JOB_TYPE_FRAGMENT] = { + .cccb_size = CTX_FRAG_CCCB_SIZE_LOG2, + .name = "fragment" + }, + [DRM_PVR_JOB_TYPE_COMPUTE] = { + .cccb_size = CTX_COMPUTE_CCCB_SIZE_LOG2, + .name = "compute" + }, + [DRM_PVR_JOB_TYPE_TRANSFER_FRAG] = { + .cccb_size = CTX_TRANSFER_CCCB_SIZE_LOG2, + .name = "transfer_frag" + }, + }; + struct pvr_device *pvr_dev = ctx->pvr_dev; + struct drm_gpu_scheduler *sched; + struct pvr_queue *queue; + int ctx_state_size, err; + void *cpu_map; + + if (WARN_ON(type >= sizeof(props))) + return ERR_PTR(-EINVAL); + + switch (ctx->type) { + case DRM_PVR_CTX_TYPE_RENDER: + if (type != DRM_PVR_JOB_TYPE_GEOMETRY && + type != DRM_PVR_JOB_TYPE_FRAGMENT) + return ERR_PTR(-EINVAL); + break; + case DRM_PVR_CTX_TYPE_COMPUTE: + if (type != DRM_PVR_JOB_TYPE_COMPUTE) + return ERR_PTR(-EINVAL); + break; + case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: + if (type != DRM_PVR_JOB_TYPE_TRANSFER_FRAG) + return ERR_PTR(-EINVAL); + break; + default: + return ERR_PTR(-EINVAL); + } + + ctx_state_size = get_ctx_state_size(pvr_dev, type); + if (ctx_state_size < 0) + return ERR_PTR(ctx_state_size); + + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) + return ERR_PTR(-ENOMEM); + + queue->type = type; + queue->ctx_offset = get_ctx_offset(type); + queue->ctx = ctx; + queue->callstack_addr = args->callstack_addr; + sched = &queue->scheduler; + INIT_LIST_HEAD(&queue->node); + mutex_init(&queue->cccb_fence_ctx.job_lock); + pvr_queue_fence_ctx_init(&queue->cccb_fence_ctx.base); + pvr_queue_fence_ctx_init(&queue->job_fence_ctx); + + err = pvr_cccb_init(pvr_dev, &queue->cccb, props[type].cccb_size, props[type].name); + if (err) + goto err_free_queue; + + err = pvr_fw_object_create(pvr_dev, ctx_state_size, + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + reg_state_init, queue, &queue->reg_state_obj); + if (err) + goto err_cccb_fini; + + init_fw_context(queue, fw_ctx_map); + + if (type != DRM_PVR_JOB_TYPE_GEOMETRY && type != DRM_PVR_JOB_TYPE_FRAGMENT && + args->callstack_addr) { + err = -EINVAL; + goto err_release_reg_state; + } + + cpu_map = pvr_fw_object_create_and_map(pvr_dev, sizeof(*queue->timeline_ufo.value), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &queue->timeline_ufo.fw_obj); + if (IS_ERR(cpu_map)) { + err = PTR_ERR(cpu_map); + goto err_release_reg_state; + } + + queue->timeline_ufo.value = cpu_map; + + err = drm_sched_init(&queue->scheduler, + &pvr_queue_sched_ops, + pvr_dev->sched_wq, 1, 64 * 1024, 1, + msecs_to_jiffies(500), + pvr_dev->sched_wq, NULL, "pvr-queue", + pvr_dev->base.dev); + if (err) + goto err_release_ufo; + + err = drm_sched_entity_init(&queue->entity, + DRM_SCHED_PRIORITY_MIN, + &sched, 1, &ctx->faulty); + if (err) + goto err_sched_fini; + + mutex_lock(&pvr_dev->queues.lock); + list_add_tail(&queue->node, &pvr_dev->queues.idle); + mutex_unlock(&pvr_dev->queues.lock); + + return queue; + +err_sched_fini: + drm_sched_fini(&queue->scheduler); + +err_release_ufo: + pvr_fw_object_unmap_and_destroy(queue->timeline_ufo.fw_obj); + +err_release_reg_state: + pvr_fw_object_destroy(queue->reg_state_obj); + +err_cccb_fini: + pvr_cccb_fini(&queue->cccb); + +err_free_queue: + mutex_destroy(&queue->cccb_fence_ctx.job_lock); + kfree(queue); + + return ERR_PTR(err); +} + +void pvr_queue_device_pre_reset(struct pvr_device *pvr_dev) +{ + struct pvr_queue *queue; + + mutex_lock(&pvr_dev->queues.lock); + list_for_each_entry(queue, &pvr_dev->queues.idle, node) + pvr_queue_stop(queue, NULL); + list_for_each_entry(queue, &pvr_dev->queues.active, node) + pvr_queue_stop(queue, NULL); + mutex_unlock(&pvr_dev->queues.lock); +} + +void pvr_queue_device_post_reset(struct pvr_device *pvr_dev) +{ + struct pvr_queue *queue; + + mutex_lock(&pvr_dev->queues.lock); + list_for_each_entry(queue, &pvr_dev->queues.active, node) + pvr_queue_start(queue); + list_for_each_entry(queue, &pvr_dev->queues.idle, node) + pvr_queue_start(queue); + mutex_unlock(&pvr_dev->queues.lock); +} + +/** + * pvr_queue_kill() - Kill a queue. + * @queue: The queue to kill. + * + * Kill the queue so no new jobs can be pushed. Should be called when the + * context handle is destroyed. The queue object might last longer if jobs + * are still in flight and holding a reference to the context this queue + * belongs to. + */ +void pvr_queue_kill(struct pvr_queue *queue) +{ + drm_sched_entity_destroy(&queue->entity); + dma_fence_put(queue->last_queued_job_scheduled_fence); + queue->last_queued_job_scheduled_fence = NULL; +} + +/** + * pvr_queue_destroy() - Destroy a queue. + * @queue: The queue to destroy. + * + * Cleanup the queue and free the resources attached to it. Should be + * called from the context release function. + */ +void pvr_queue_destroy(struct pvr_queue *queue) +{ + if (!queue) + return; + + mutex_lock(&queue->ctx->pvr_dev->queues.lock); + list_del_init(&queue->node); + mutex_unlock(&queue->ctx->pvr_dev->queues.lock); + + drm_sched_fini(&queue->scheduler); + drm_sched_entity_fini(&queue->entity); + + if (WARN_ON(queue->last_queued_job_scheduled_fence)) + dma_fence_put(queue->last_queued_job_scheduled_fence); + + pvr_queue_cleanup_fw_context(queue); + + pvr_fw_object_unmap_and_destroy(queue->timeline_ufo.fw_obj); + pvr_fw_object_destroy(queue->reg_state_obj); + pvr_cccb_fini(&queue->cccb); + mutex_destroy(&queue->cccb_fence_ctx.job_lock); + kfree(queue); +} + +/** + * pvr_queue_device_init() - Device-level initialization of queue related fields. + * @pvr_dev: The device to initialize. + * + * Initializes all fields related to queue management in pvr_device. + * + * Return: + * * 0 on success, or + * * An error code on failure. + */ +int pvr_queue_device_init(struct pvr_device *pvr_dev) +{ + int err; + + INIT_LIST_HEAD(&pvr_dev->queues.active); + INIT_LIST_HEAD(&pvr_dev->queues.idle); + err = drmm_mutex_init(from_pvr_device(pvr_dev), &pvr_dev->queues.lock); + if (err) + return err; + + pvr_dev->sched_wq = alloc_workqueue("powervr-sched", WQ_UNBOUND, 0); + if (!pvr_dev->sched_wq) + return -ENOMEM; + + return 0; +} + +/** + * pvr_queue_device_fini() - Device-level cleanup of queue related fields. + * @pvr_dev: The device to cleanup. + * + * Cleanup/free all queue-related resources attached to a pvr_device object. + */ +void pvr_queue_device_fini(struct pvr_device *pvr_dev) +{ + destroy_workqueue(pvr_dev->sched_wq); +} diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h new file mode 100644 index 000000000000..b5ce2c742150 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_QUEUE_H +#define PVR_QUEUE_H + +#include <drm/gpu_scheduler.h> + +#include "pvr_cccb.h" +#include "pvr_device.h" + +struct pvr_context; +struct pvr_queue; + +/** + * struct pvr_queue_fence_ctx - Queue fence context + * + * Used to implement dma_fence_ops for pvr_job::{done,cccb}_fence. + */ +struct pvr_queue_fence_ctx { + /** @id: Fence context ID allocated with dma_fence_context_alloc(). */ + u64 id; + + /** @seqno: Sequence number incremented each time a fence is created. */ + atomic_t seqno; + + /** @lock: Lock used to synchronize access to fences allocated by this context. */ + spinlock_t lock; +}; + +/** + * struct pvr_queue_cccb_fence_ctx - CCCB fence context + * + * Context used to manage fences controlling access to the CCCB. No fences are + * issued if there's enough space in the CCCB to push job commands. + */ +struct pvr_queue_cccb_fence_ctx { + /** @base: Base queue fence context. */ + struct pvr_queue_fence_ctx base; + + /** + * @job: Job waiting for CCCB space. + * + * Thanks to the serializationg done at the drm_sched_entity level, + * there's no more than one job waiting for CCCB at a given time. + * + * This field is NULL if no jobs are currently waiting for CCCB space. + * + * Must be accessed with @job_lock held. + */ + struct pvr_job *job; + + /** @lock: Lock protecting access to the job object. */ + struct mutex job_lock; +}; + +/** + * struct pvr_queue_fence - Queue fence object + */ +struct pvr_queue_fence { + /** @base: Base dma_fence. */ + struct dma_fence base; + + /** @queue: Queue that created this fence. */ + struct pvr_queue *queue; +}; + +/** + * struct pvr_queue - Job queue + * + * Used to queue and track execution of pvr_job objects. + */ +struct pvr_queue { + /** @scheduler: Single entity scheduler use to push jobs to this queue. */ + struct drm_gpu_scheduler scheduler; + + /** @entity: Scheduling entity backing this queue. */ + struct drm_sched_entity entity; + + /** @type: Type of jobs queued to this queue. */ + enum drm_pvr_job_type type; + + /** @ctx: Context object this queue is bound to. */ + struct pvr_context *ctx; + + /** @node: Used to add the queue to the active/idle queue list. */ + struct list_head node; + + /** + * @in_flight_job_count: Number of jobs submitted to the CCCB that + * have not been processed yet. + */ + atomic_t in_flight_job_count; + + /** + * @cccb_fence_ctx: CCCB fence context. + * + * Used to control access to the CCCB is full, such that we don't + * end up trying to push commands to the CCCB if there's not enough + * space to receive all commands needed for a job to complete. + */ + struct pvr_queue_cccb_fence_ctx cccb_fence_ctx; + + /** @job_fence_ctx: Job fence context object. */ + struct pvr_queue_fence_ctx job_fence_ctx; + + /** @timeline_ufo: Timeline UFO for the context queue. */ + struct { + /** @fw_obj: FW object representing the UFO value. */ + struct pvr_fw_object *fw_obj; + + /** @value: CPU mapping of the UFO value. */ + u32 *value; + } timeline_ufo; + + /** + * last_queued_job_scheduled_fence: The scheduled fence of the last + * job queued to this queue. + * + * We use it to insert frag -> geom dependencies when issuing combined + * geom+frag jobs, to guarantee that the fragment job that's part of + * the combined operation comes after all fragment jobs that were queued + * before it. + */ + struct dma_fence *last_queued_job_scheduled_fence; + + /** @cccb: Client Circular Command Buffer. */ + struct pvr_cccb cccb; + + /** @reg_state_obj: FW object representing the register state of this queue. */ + struct pvr_fw_object *reg_state_obj; + + /** @ctx_offset: Offset of the queue context in the FW context object. */ + u32 ctx_offset; + + /** @callstack_addr: Initial call stack address for register state object. */ + u64 callstack_addr; +}; + +bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f); + +int pvr_queue_job_init(struct pvr_job *job); + +void pvr_queue_job_cleanup(struct pvr_job *job); + +void pvr_queue_job_push(struct pvr_job *job); + +struct dma_fence *pvr_queue_job_arm(struct pvr_job *job); + +struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, + enum drm_pvr_job_type type, + struct drm_pvr_ioctl_create_context_args *args, + void *fw_ctx_map); + +void pvr_queue_kill(struct pvr_queue *queue); + +void pvr_queue_destroy(struct pvr_queue *queue); + +void pvr_queue_process(struct pvr_queue *queue); + +void pvr_queue_device_pre_reset(struct pvr_device *pvr_dev); + +void pvr_queue_device_post_reset(struct pvr_device *pvr_dev); + +int pvr_queue_device_init(struct pvr_device *pvr_dev); + +void pvr_queue_device_fini(struct pvr_device *pvr_dev); + +#endif /* PVR_QUEUE_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_cr_defs.h b/drivers/gpu/drm/imagination/pvr_rogue_cr_defs.h new file mode 100644 index 000000000000..2a90d02796d3 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_cr_defs.h @@ -0,0 +1,6193 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +/* *** Autogenerated C -- do not edit *** */ + +#ifndef PVR_ROGUE_CR_DEFS_H +#define PVR_ROGUE_CR_DEFS_H + +/* clang-format off */ + +#define ROGUE_CR_DEFS_REVISION 1 + +/* Register ROGUE_CR_RASTERISATION_INDIRECT */ +#define ROGUE_CR_RASTERISATION_INDIRECT 0x8238U +#define ROGUE_CR_RASTERISATION_INDIRECT_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_RASTERISATION_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_RASTERISATION_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_PBE_INDIRECT */ +#define ROGUE_CR_PBE_INDIRECT 0x83E0U +#define ROGUE_CR_PBE_INDIRECT_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_PBE_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_PBE_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_PBE_PERF_INDIRECT */ +#define ROGUE_CR_PBE_PERF_INDIRECT 0x83D8U +#define ROGUE_CR_PBE_PERF_INDIRECT_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_PBE_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_PBE_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_TPU_PERF_INDIRECT */ +#define ROGUE_CR_TPU_PERF_INDIRECT 0x83F0U +#define ROGUE_CR_TPU_PERF_INDIRECT_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_TPU_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_TPU_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF8U + +/* Register ROGUE_CR_RASTERISATION_PERF_INDIRECT */ +#define ROGUE_CR_RASTERISATION_PERF_INDIRECT 0x8318U +#define ROGUE_CR_RASTERISATION_PERF_INDIRECT_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_RASTERISATION_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_RASTERISATION_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_TPU_MCU_L0_PERF_INDIRECT */ +#define ROGUE_CR_TPU_MCU_L0_PERF_INDIRECT 0x8028U +#define ROGUE_CR_TPU_MCU_L0_PERF_INDIRECT_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_TPU_MCU_L0_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_TPU_MCU_L0_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF8U + +/* Register ROGUE_CR_USC_PERF_INDIRECT */ +#define ROGUE_CR_USC_PERF_INDIRECT 0x8030U +#define ROGUE_CR_USC_PERF_INDIRECT_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_USC_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_USC_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_BLACKPEARL_INDIRECT */ +#define ROGUE_CR_BLACKPEARL_INDIRECT 0x8388U +#define ROGUE_CR_BLACKPEARL_INDIRECT_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_BLACKPEARL_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_BLACKPEARL_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_BLACKPEARL_PERF_INDIRECT */ +#define ROGUE_CR_BLACKPEARL_PERF_INDIRECT 0x83F8U +#define ROGUE_CR_BLACKPEARL_PERF_INDIRECT_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_BLACKPEARL_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_BLACKPEARL_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_TEXAS3_PERF_INDIRECT */ +#define ROGUE_CR_TEXAS3_PERF_INDIRECT 0x83D0U +#define ROGUE_CR_TEXAS3_PERF_INDIRECT_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_TEXAS3_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_TEXAS3_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFF8U + +/* Register ROGUE_CR_TEXAS_PERF_INDIRECT */ +#define ROGUE_CR_TEXAS_PERF_INDIRECT 0x8288U +#define ROGUE_CR_TEXAS_PERF_INDIRECT_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_TEXAS_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_TEXAS_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_BX_TU_PERF_INDIRECT */ +#define ROGUE_CR_BX_TU_PERF_INDIRECT 0xC900U +#define ROGUE_CR_BX_TU_PERF_INDIRECT_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_BX_TU_PERF_INDIRECT_ADDRESS_SHIFT 0U +#define ROGUE_CR_BX_TU_PERF_INDIRECT_ADDRESS_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_CLK_CTRL */ +#define ROGUE_CR_CLK_CTRL 0x0000U +#define ROGUE_CR_CLK_CTRL__PBE2_XE__MASKFULL 0xFFFFFF003F3FFFFFULL +#define ROGUE_CR_CLK_CTRL__S7_TOP__MASKFULL 0xCFCF03000F3F3F0FULL +#define ROGUE_CR_CLK_CTRL_MASKFULL 0xFFFFFF003F3FFFFFULL +#define ROGUE_CR_CLK_CTRL_BIF_TEXAS_SHIFT 62U +#define ROGUE_CR_CLK_CTRL_BIF_TEXAS_CLRMSK 0x3FFFFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_BIF_TEXAS_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_TEXAS_ON 0x4000000000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_TEXAS_AUTO 0x8000000000000000ULL +#define ROGUE_CR_CLK_CTRL_IPP_SHIFT 60U +#define ROGUE_CR_CLK_CTRL_IPP_CLRMSK 0xCFFFFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_IPP_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_IPP_ON 0x1000000000000000ULL +#define ROGUE_CR_CLK_CTRL_IPP_AUTO 0x2000000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBC_SHIFT 58U +#define ROGUE_CR_CLK_CTRL_FBC_CLRMSK 0xF3FFFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_FBC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBC_ON 0x0400000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBC_AUTO 0x0800000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBDC_SHIFT 56U +#define ROGUE_CR_CLK_CTRL_FBDC_CLRMSK 0xFCFFFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_FBDC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBDC_ON 0x0100000000000000ULL +#define ROGUE_CR_CLK_CTRL_FBDC_AUTO 0x0200000000000000ULL +#define ROGUE_CR_CLK_CTRL_FB_TLCACHE_SHIFT 54U +#define ROGUE_CR_CLK_CTRL_FB_TLCACHE_CLRMSK 0xFF3FFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_FB_TLCACHE_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_FB_TLCACHE_ON 0x0040000000000000ULL +#define ROGUE_CR_CLK_CTRL_FB_TLCACHE_AUTO 0x0080000000000000ULL +#define ROGUE_CR_CLK_CTRL_USCS_SHIFT 52U +#define ROGUE_CR_CLK_CTRL_USCS_CLRMSK 0xFFCFFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_USCS_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_USCS_ON 0x0010000000000000ULL +#define ROGUE_CR_CLK_CTRL_USCS_AUTO 0x0020000000000000ULL +#define ROGUE_CR_CLK_CTRL_PBE_SHIFT 50U +#define ROGUE_CR_CLK_CTRL_PBE_CLRMSK 0xFFF3FFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_PBE_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_PBE_ON 0x0004000000000000ULL +#define ROGUE_CR_CLK_CTRL_PBE_AUTO 0x0008000000000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L1_SHIFT 48U +#define ROGUE_CR_CLK_CTRL_MCU_L1_CLRMSK 0xFFFCFFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_MCU_L1_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L1_ON 0x0001000000000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L1_AUTO 0x0002000000000000ULL +#define ROGUE_CR_CLK_CTRL_CDM_SHIFT 46U +#define ROGUE_CR_CLK_CTRL_CDM_CLRMSK 0xFFFF3FFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_CDM_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_CDM_ON 0x0000400000000000ULL +#define ROGUE_CR_CLK_CTRL_CDM_AUTO 0x0000800000000000ULL +#define ROGUE_CR_CLK_CTRL_SIDEKICK_SHIFT 44U +#define ROGUE_CR_CLK_CTRL_SIDEKICK_CLRMSK 0xFFFFCFFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_SIDEKICK_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_SIDEKICK_ON 0x0000100000000000ULL +#define ROGUE_CR_CLK_CTRL_SIDEKICK_AUTO 0x0000200000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_SIDEKICK_SHIFT 42U +#define ROGUE_CR_CLK_CTRL_BIF_SIDEKICK_CLRMSK 0xFFFFF3FFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_BIF_SIDEKICK_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_SIDEKICK_ON 0x0000040000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_SIDEKICK_AUTO 0x0000080000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_SHIFT 40U +#define ROGUE_CR_CLK_CTRL_BIF_CLRMSK 0xFFFFFCFFFFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_BIF_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_ON 0x0000010000000000ULL +#define ROGUE_CR_CLK_CTRL_BIF_AUTO 0x0000020000000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_MCU_DEMUX_SHIFT 28U +#define ROGUE_CR_CLK_CTRL_TPU_MCU_DEMUX_CLRMSK 0xFFFFFFFFCFFFFFFFULL +#define ROGUE_CR_CLK_CTRL_TPU_MCU_DEMUX_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_MCU_DEMUX_ON 0x0000000010000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_MCU_DEMUX_AUTO 0x0000000020000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L0_SHIFT 26U +#define ROGUE_CR_CLK_CTRL_MCU_L0_CLRMSK 0xFFFFFFFFF3FFFFFFULL +#define ROGUE_CR_CLK_CTRL_MCU_L0_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L0_ON 0x0000000004000000ULL +#define ROGUE_CR_CLK_CTRL_MCU_L0_AUTO 0x0000000008000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_SHIFT 24U +#define ROGUE_CR_CLK_CTRL_TPU_CLRMSK 0xFFFFFFFFFCFFFFFFULL +#define ROGUE_CR_CLK_CTRL_TPU_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_ON 0x0000000001000000ULL +#define ROGUE_CR_CLK_CTRL_TPU_AUTO 0x0000000002000000ULL +#define ROGUE_CR_CLK_CTRL_USC_SHIFT 20U +#define ROGUE_CR_CLK_CTRL_USC_CLRMSK 0xFFFFFFFFFFCFFFFFULL +#define ROGUE_CR_CLK_CTRL_USC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_USC_ON 0x0000000000100000ULL +#define ROGUE_CR_CLK_CTRL_USC_AUTO 0x0000000000200000ULL +#define ROGUE_CR_CLK_CTRL_TLA_SHIFT 18U +#define ROGUE_CR_CLK_CTRL_TLA_CLRMSK 0xFFFFFFFFFFF3FFFFULL +#define ROGUE_CR_CLK_CTRL_TLA_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_TLA_ON 0x0000000000040000ULL +#define ROGUE_CR_CLK_CTRL_TLA_AUTO 0x0000000000080000ULL +#define ROGUE_CR_CLK_CTRL_SLC_SHIFT 16U +#define ROGUE_CR_CLK_CTRL_SLC_CLRMSK 0xFFFFFFFFFFFCFFFFULL +#define ROGUE_CR_CLK_CTRL_SLC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_SLC_ON 0x0000000000010000ULL +#define ROGUE_CR_CLK_CTRL_SLC_AUTO 0x0000000000020000ULL +#define ROGUE_CR_CLK_CTRL_UVS_SHIFT 14U +#define ROGUE_CR_CLK_CTRL_UVS_CLRMSK 0xFFFFFFFFFFFF3FFFULL +#define ROGUE_CR_CLK_CTRL_UVS_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_UVS_ON 0x0000000000004000ULL +#define ROGUE_CR_CLK_CTRL_UVS_AUTO 0x0000000000008000ULL +#define ROGUE_CR_CLK_CTRL_PDS_SHIFT 12U +#define ROGUE_CR_CLK_CTRL_PDS_CLRMSK 0xFFFFFFFFFFFFCFFFULL +#define ROGUE_CR_CLK_CTRL_PDS_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_PDS_ON 0x0000000000001000ULL +#define ROGUE_CR_CLK_CTRL_PDS_AUTO 0x0000000000002000ULL +#define ROGUE_CR_CLK_CTRL_VDM_SHIFT 10U +#define ROGUE_CR_CLK_CTRL_VDM_CLRMSK 0xFFFFFFFFFFFFF3FFULL +#define ROGUE_CR_CLK_CTRL_VDM_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_VDM_ON 0x0000000000000400ULL +#define ROGUE_CR_CLK_CTRL_VDM_AUTO 0x0000000000000800ULL +#define ROGUE_CR_CLK_CTRL_PM_SHIFT 8U +#define ROGUE_CR_CLK_CTRL_PM_CLRMSK 0xFFFFFFFFFFFFFCFFULL +#define ROGUE_CR_CLK_CTRL_PM_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_PM_ON 0x0000000000000100ULL +#define ROGUE_CR_CLK_CTRL_PM_AUTO 0x0000000000000200ULL +#define ROGUE_CR_CLK_CTRL_GPP_SHIFT 6U +#define ROGUE_CR_CLK_CTRL_GPP_CLRMSK 0xFFFFFFFFFFFFFF3FULL +#define ROGUE_CR_CLK_CTRL_GPP_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_GPP_ON 0x0000000000000040ULL +#define ROGUE_CR_CLK_CTRL_GPP_AUTO 0x0000000000000080ULL +#define ROGUE_CR_CLK_CTRL_TE_SHIFT 4U +#define ROGUE_CR_CLK_CTRL_TE_CLRMSK 0xFFFFFFFFFFFFFFCFULL +#define ROGUE_CR_CLK_CTRL_TE_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_TE_ON 0x0000000000000010ULL +#define ROGUE_CR_CLK_CTRL_TE_AUTO 0x0000000000000020ULL +#define ROGUE_CR_CLK_CTRL_TSP_SHIFT 2U +#define ROGUE_CR_CLK_CTRL_TSP_CLRMSK 0xFFFFFFFFFFFFFFF3ULL +#define ROGUE_CR_CLK_CTRL_TSP_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_TSP_ON 0x0000000000000004ULL +#define ROGUE_CR_CLK_CTRL_TSP_AUTO 0x0000000000000008ULL +#define ROGUE_CR_CLK_CTRL_ISP_SHIFT 0U +#define ROGUE_CR_CLK_CTRL_ISP_CLRMSK 0xFFFFFFFFFFFFFFFCULL +#define ROGUE_CR_CLK_CTRL_ISP_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL_ISP_ON 0x0000000000000001ULL +#define ROGUE_CR_CLK_CTRL_ISP_AUTO 0x0000000000000002ULL + +/* Register ROGUE_CR_CLK_STATUS */ +#define ROGUE_CR_CLK_STATUS 0x0008U +#define ROGUE_CR_CLK_STATUS__PBE2_XE__MASKFULL 0x00000001FFF077FFULL +#define ROGUE_CR_CLK_STATUS__S7_TOP__MASKFULL 0x00000001B3101773ULL +#define ROGUE_CR_CLK_STATUS_MASKFULL 0x00000001FFF077FFULL +#define ROGUE_CR_CLK_STATUS_MCU_FBTC_SHIFT 32U +#define ROGUE_CR_CLK_STATUS_MCU_FBTC_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_CLK_STATUS_MCU_FBTC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_MCU_FBTC_RUNNING 0x0000000100000000ULL +#define ROGUE_CR_CLK_STATUS_BIF_TEXAS_SHIFT 31U +#define ROGUE_CR_CLK_STATUS_BIF_TEXAS_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_CLK_STATUS_BIF_TEXAS_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_BIF_TEXAS_RUNNING 0x0000000080000000ULL +#define ROGUE_CR_CLK_STATUS_IPP_SHIFT 30U +#define ROGUE_CR_CLK_STATUS_IPP_CLRMSK 0xFFFFFFFFBFFFFFFFULL +#define ROGUE_CR_CLK_STATUS_IPP_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_IPP_RUNNING 0x0000000040000000ULL +#define ROGUE_CR_CLK_STATUS_FBC_SHIFT 29U +#define ROGUE_CR_CLK_STATUS_FBC_CLRMSK 0xFFFFFFFFDFFFFFFFULL +#define ROGUE_CR_CLK_STATUS_FBC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_FBC_RUNNING 0x0000000020000000ULL +#define ROGUE_CR_CLK_STATUS_FBDC_SHIFT 28U +#define ROGUE_CR_CLK_STATUS_FBDC_CLRMSK 0xFFFFFFFFEFFFFFFFULL +#define ROGUE_CR_CLK_STATUS_FBDC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_FBDC_RUNNING 0x0000000010000000ULL +#define ROGUE_CR_CLK_STATUS_FB_TLCACHE_SHIFT 27U +#define ROGUE_CR_CLK_STATUS_FB_TLCACHE_CLRMSK 0xFFFFFFFFF7FFFFFFULL +#define ROGUE_CR_CLK_STATUS_FB_TLCACHE_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_FB_TLCACHE_RUNNING 0x0000000008000000ULL +#define ROGUE_CR_CLK_STATUS_USCS_SHIFT 26U +#define ROGUE_CR_CLK_STATUS_USCS_CLRMSK 0xFFFFFFFFFBFFFFFFULL +#define ROGUE_CR_CLK_STATUS_USCS_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_USCS_RUNNING 0x0000000004000000ULL +#define ROGUE_CR_CLK_STATUS_PBE_SHIFT 25U +#define ROGUE_CR_CLK_STATUS_PBE_CLRMSK 0xFFFFFFFFFDFFFFFFULL +#define ROGUE_CR_CLK_STATUS_PBE_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_PBE_RUNNING 0x0000000002000000ULL +#define ROGUE_CR_CLK_STATUS_MCU_L1_SHIFT 24U +#define ROGUE_CR_CLK_STATUS_MCU_L1_CLRMSK 0xFFFFFFFFFEFFFFFFULL +#define ROGUE_CR_CLK_STATUS_MCU_L1_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_MCU_L1_RUNNING 0x0000000001000000ULL +#define ROGUE_CR_CLK_STATUS_CDM_SHIFT 23U +#define ROGUE_CR_CLK_STATUS_CDM_CLRMSK 0xFFFFFFFFFF7FFFFFULL +#define ROGUE_CR_CLK_STATUS_CDM_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_CDM_RUNNING 0x0000000000800000ULL +#define ROGUE_CR_CLK_STATUS_SIDEKICK_SHIFT 22U +#define ROGUE_CR_CLK_STATUS_SIDEKICK_CLRMSK 0xFFFFFFFFFFBFFFFFULL +#define ROGUE_CR_CLK_STATUS_SIDEKICK_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_SIDEKICK_RUNNING 0x0000000000400000ULL +#define ROGUE_CR_CLK_STATUS_BIF_SIDEKICK_SHIFT 21U +#define ROGUE_CR_CLK_STATUS_BIF_SIDEKICK_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_CLK_STATUS_BIF_SIDEKICK_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_BIF_SIDEKICK_RUNNING 0x0000000000200000ULL +#define ROGUE_CR_CLK_STATUS_BIF_SHIFT 20U +#define ROGUE_CR_CLK_STATUS_BIF_CLRMSK 0xFFFFFFFFFFEFFFFFULL +#define ROGUE_CR_CLK_STATUS_BIF_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_BIF_RUNNING 0x0000000000100000ULL +#define ROGUE_CR_CLK_STATUS_TPU_MCU_DEMUX_SHIFT 14U +#define ROGUE_CR_CLK_STATUS_TPU_MCU_DEMUX_CLRMSK 0xFFFFFFFFFFFFBFFFULL +#define ROGUE_CR_CLK_STATUS_TPU_MCU_DEMUX_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_TPU_MCU_DEMUX_RUNNING 0x0000000000004000ULL +#define ROGUE_CR_CLK_STATUS_MCU_L0_SHIFT 13U +#define ROGUE_CR_CLK_STATUS_MCU_L0_CLRMSK 0xFFFFFFFFFFFFDFFFULL +#define ROGUE_CR_CLK_STATUS_MCU_L0_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_MCU_L0_RUNNING 0x0000000000002000ULL +#define ROGUE_CR_CLK_STATUS_TPU_SHIFT 12U +#define ROGUE_CR_CLK_STATUS_TPU_CLRMSK 0xFFFFFFFFFFFFEFFFULL +#define ROGUE_CR_CLK_STATUS_TPU_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_TPU_RUNNING 0x0000000000001000ULL +#define ROGUE_CR_CLK_STATUS_USC_SHIFT 10U +#define ROGUE_CR_CLK_STATUS_USC_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_CLK_STATUS_USC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_USC_RUNNING 0x0000000000000400ULL +#define ROGUE_CR_CLK_STATUS_TLA_SHIFT 9U +#define ROGUE_CR_CLK_STATUS_TLA_CLRMSK 0xFFFFFFFFFFFFFDFFULL +#define ROGUE_CR_CLK_STATUS_TLA_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_TLA_RUNNING 0x0000000000000200ULL +#define ROGUE_CR_CLK_STATUS_SLC_SHIFT 8U +#define ROGUE_CR_CLK_STATUS_SLC_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_CLK_STATUS_SLC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_SLC_RUNNING 0x0000000000000100ULL +#define ROGUE_CR_CLK_STATUS_UVS_SHIFT 7U +#define ROGUE_CR_CLK_STATUS_UVS_CLRMSK 0xFFFFFFFFFFFFFF7FULL +#define ROGUE_CR_CLK_STATUS_UVS_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_UVS_RUNNING 0x0000000000000080ULL +#define ROGUE_CR_CLK_STATUS_PDS_SHIFT 6U +#define ROGUE_CR_CLK_STATUS_PDS_CLRMSK 0xFFFFFFFFFFFFFFBFULL +#define ROGUE_CR_CLK_STATUS_PDS_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_PDS_RUNNING 0x0000000000000040ULL +#define ROGUE_CR_CLK_STATUS_VDM_SHIFT 5U +#define ROGUE_CR_CLK_STATUS_VDM_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_CLK_STATUS_VDM_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_VDM_RUNNING 0x0000000000000020ULL +#define ROGUE_CR_CLK_STATUS_PM_SHIFT 4U +#define ROGUE_CR_CLK_STATUS_PM_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_CLK_STATUS_PM_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_PM_RUNNING 0x0000000000000010ULL +#define ROGUE_CR_CLK_STATUS_GPP_SHIFT 3U +#define ROGUE_CR_CLK_STATUS_GPP_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_CLK_STATUS_GPP_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_GPP_RUNNING 0x0000000000000008ULL +#define ROGUE_CR_CLK_STATUS_TE_SHIFT 2U +#define ROGUE_CR_CLK_STATUS_TE_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_CLK_STATUS_TE_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_TE_RUNNING 0x0000000000000004ULL +#define ROGUE_CR_CLK_STATUS_TSP_SHIFT 1U +#define ROGUE_CR_CLK_STATUS_TSP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_CLK_STATUS_TSP_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_TSP_RUNNING 0x0000000000000002ULL +#define ROGUE_CR_CLK_STATUS_ISP_SHIFT 0U +#define ROGUE_CR_CLK_STATUS_ISP_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_CLK_STATUS_ISP_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS_ISP_RUNNING 0x0000000000000001ULL + +/* Register ROGUE_CR_CORE_ID */ +#define ROGUE_CR_CORE_ID__PBVNC 0x0020U +#define ROGUE_CR_CORE_ID__PBVNC__MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_CORE_ID__PBVNC__BRANCH_ID_SHIFT 48U +#define ROGUE_CR_CORE_ID__PBVNC__BRANCH_ID_CLRMSK 0x0000FFFFFFFFFFFFULL +#define ROGUE_CR_CORE_ID__PBVNC__VERSION_ID_SHIFT 32U +#define ROGUE_CR_CORE_ID__PBVNC__VERSION_ID_CLRMSK 0xFFFF0000FFFFFFFFULL +#define ROGUE_CR_CORE_ID__PBVNC__NUMBER_OF_SCALABLE_UNITS_SHIFT 16U +#define ROGUE_CR_CORE_ID__PBVNC__NUMBER_OF_SCALABLE_UNITS_CLRMSK 0xFFFFFFFF0000FFFFULL +#define ROGUE_CR_CORE_ID__PBVNC__CONFIG_ID_SHIFT 0U +#define ROGUE_CR_CORE_ID__PBVNC__CONFIG_ID_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_CORE_ID */ +#define ROGUE_CR_CORE_ID 0x0018U +#define ROGUE_CR_CORE_ID_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_CORE_ID_ID_SHIFT 16U +#define ROGUE_CR_CORE_ID_ID_CLRMSK 0x0000FFFFU +#define ROGUE_CR_CORE_ID_CONFIG_SHIFT 0U +#define ROGUE_CR_CORE_ID_CONFIG_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_CORE_REVISION */ +#define ROGUE_CR_CORE_REVISION 0x0020U +#define ROGUE_CR_CORE_REVISION_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_CORE_REVISION_DESIGNER_SHIFT 24U +#define ROGUE_CR_CORE_REVISION_DESIGNER_CLRMSK 0x00FFFFFFU +#define ROGUE_CR_CORE_REVISION_MAJOR_SHIFT 16U +#define ROGUE_CR_CORE_REVISION_MAJOR_CLRMSK 0xFF00FFFFU +#define ROGUE_CR_CORE_REVISION_MINOR_SHIFT 8U +#define ROGUE_CR_CORE_REVISION_MINOR_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_CORE_REVISION_MAINTENANCE_SHIFT 0U +#define ROGUE_CR_CORE_REVISION_MAINTENANCE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_DESIGNER_REV_FIELD1 */ +#define ROGUE_CR_DESIGNER_REV_FIELD1 0x0028U +#define ROGUE_CR_DESIGNER_REV_FIELD1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_DESIGNER_REV_FIELD1_DESIGNER_REV_FIELD1_SHIFT 0U +#define ROGUE_CR_DESIGNER_REV_FIELD1_DESIGNER_REV_FIELD1_CLRMSK 0x00000000U + +/* Register ROGUE_CR_DESIGNER_REV_FIELD2 */ +#define ROGUE_CR_DESIGNER_REV_FIELD2 0x0030U +#define ROGUE_CR_DESIGNER_REV_FIELD2_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_DESIGNER_REV_FIELD2_DESIGNER_REV_FIELD2_SHIFT 0U +#define ROGUE_CR_DESIGNER_REV_FIELD2_DESIGNER_REV_FIELD2_CLRMSK 0x00000000U + +/* Register ROGUE_CR_CHANGESET_NUMBER */ +#define ROGUE_CR_CHANGESET_NUMBER 0x0040U +#define ROGUE_CR_CHANGESET_NUMBER_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_CHANGESET_NUMBER_CHANGESET_NUMBER_SHIFT 0U +#define ROGUE_CR_CHANGESET_NUMBER_CHANGESET_NUMBER_CLRMSK 0x0000000000000000ULL + +/* Register ROGUE_CR_CLK_XTPLUS_CTRL */ +#define ROGUE_CR_CLK_XTPLUS_CTRL 0x0080U +#define ROGUE_CR_CLK_XTPLUS_CTRL_MASKFULL 0x0000003FFFFF0000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_TDM_SHIFT 36U +#define ROGUE_CR_CLK_XTPLUS_CTRL_TDM_CLRMSK 0xFFFFFFCFFFFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_TDM_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_TDM_ON 0x0000001000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_TDM_AUTO 0x0000002000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_ASTC_SHIFT 34U +#define ROGUE_CR_CLK_XTPLUS_CTRL_ASTC_CLRMSK 0xFFFFFFF3FFFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_ASTC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_ASTC_ON 0x0000000400000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_ASTC_AUTO 0x0000000800000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_IPF_SHIFT 32U +#define ROGUE_CR_CLK_XTPLUS_CTRL_IPF_CLRMSK 0xFFFFFFFCFFFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_IPF_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_IPF_ON 0x0000000100000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_IPF_AUTO 0x0000000200000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_COMPUTE_SHIFT 30U +#define ROGUE_CR_CLK_XTPLUS_CTRL_COMPUTE_CLRMSK 0xFFFFFFFF3FFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_COMPUTE_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_COMPUTE_ON 0x0000000040000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_COMPUTE_AUTO 0x0000000080000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PIXEL_SHIFT 28U +#define ROGUE_CR_CLK_XTPLUS_CTRL_PIXEL_CLRMSK 0xFFFFFFFFCFFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PIXEL_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PIXEL_ON 0x0000000010000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PIXEL_AUTO 0x0000000020000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_VERTEX_SHIFT 26U +#define ROGUE_CR_CLK_XTPLUS_CTRL_VERTEX_CLRMSK 0xFFFFFFFFF3FFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_VERTEX_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_VERTEX_ON 0x0000000004000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_VERTEX_AUTO 0x0000000008000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USCPS_SHIFT 24U +#define ROGUE_CR_CLK_XTPLUS_CTRL_USCPS_CLRMSK 0xFFFFFFFFFCFFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USCPS_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USCPS_ON 0x0000000001000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USCPS_AUTO 0x0000000002000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PDS_SHARED_SHIFT 22U +#define ROGUE_CR_CLK_XTPLUS_CTRL_PDS_SHARED_CLRMSK 0xFFFFFFFFFF3FFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PDS_SHARED_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PDS_SHARED_ON 0x0000000000400000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_PDS_SHARED_AUTO 0x0000000000800000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_BIF_BLACKPEARL_SHIFT 20U +#define ROGUE_CR_CLK_XTPLUS_CTRL_BIF_BLACKPEARL_CLRMSK 0xFFFFFFFFFFCFFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_BIF_BLACKPEARL_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_BIF_BLACKPEARL_ON 0x0000000000100000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_BIF_BLACKPEARL_AUTO 0x0000000000200000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USC_SHARED_SHIFT 18U +#define ROGUE_CR_CLK_XTPLUS_CTRL_USC_SHARED_CLRMSK 0xFFFFFFFFFFF3FFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USC_SHARED_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USC_SHARED_ON 0x0000000000040000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_USC_SHARED_AUTO 0x0000000000080000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_GEOMETRY_SHIFT 16U +#define ROGUE_CR_CLK_XTPLUS_CTRL_GEOMETRY_CLRMSK 0xFFFFFFFFFFFCFFFFULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_GEOMETRY_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_GEOMETRY_ON 0x0000000000010000ULL +#define ROGUE_CR_CLK_XTPLUS_CTRL_GEOMETRY_AUTO 0x0000000000020000ULL + +/* Register ROGUE_CR_CLK_XTPLUS_STATUS */ +#define ROGUE_CR_CLK_XTPLUS_STATUS 0x0088U +#define ROGUE_CR_CLK_XTPLUS_STATUS_MASKFULL 0x00000000000007FFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_TDM_SHIFT 10U +#define ROGUE_CR_CLK_XTPLUS_STATUS_TDM_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_TDM_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_TDM_RUNNING 0x0000000000000400ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_IPF_SHIFT 9U +#define ROGUE_CR_CLK_XTPLUS_STATUS_IPF_CLRMSK 0xFFFFFFFFFFFFFDFFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_IPF_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_IPF_RUNNING 0x0000000000000200ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_COMPUTE_SHIFT 8U +#define ROGUE_CR_CLK_XTPLUS_STATUS_COMPUTE_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_COMPUTE_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_COMPUTE_RUNNING 0x0000000000000100ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_ASTC_SHIFT 7U +#define ROGUE_CR_CLK_XTPLUS_STATUS_ASTC_CLRMSK 0xFFFFFFFFFFFFFF7FULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_ASTC_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_ASTC_RUNNING 0x0000000000000080ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PIXEL_SHIFT 6U +#define ROGUE_CR_CLK_XTPLUS_STATUS_PIXEL_CLRMSK 0xFFFFFFFFFFFFFFBFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PIXEL_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PIXEL_RUNNING 0x0000000000000040ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_VERTEX_SHIFT 5U +#define ROGUE_CR_CLK_XTPLUS_STATUS_VERTEX_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_VERTEX_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_VERTEX_RUNNING 0x0000000000000020ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USCPS_SHIFT 4U +#define ROGUE_CR_CLK_XTPLUS_STATUS_USCPS_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USCPS_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USCPS_RUNNING 0x0000000000000010ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PDS_SHARED_SHIFT 3U +#define ROGUE_CR_CLK_XTPLUS_STATUS_PDS_SHARED_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PDS_SHARED_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_PDS_SHARED_RUNNING 0x0000000000000008ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_BIF_BLACKPEARL_SHIFT 2U +#define ROGUE_CR_CLK_XTPLUS_STATUS_BIF_BLACKPEARL_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_BIF_BLACKPEARL_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_BIF_BLACKPEARL_RUNNING 0x0000000000000004ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USC_SHARED_SHIFT 1U +#define ROGUE_CR_CLK_XTPLUS_STATUS_USC_SHARED_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USC_SHARED_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_USC_SHARED_RUNNING 0x0000000000000002ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_GEOMETRY_SHIFT 0U +#define ROGUE_CR_CLK_XTPLUS_STATUS_GEOMETRY_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_GEOMETRY_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_XTPLUS_STATUS_GEOMETRY_RUNNING 0x0000000000000001ULL + +/* Register ROGUE_CR_SOFT_RESET */ +#define ROGUE_CR_SOFT_RESET 0x0100U +#define ROGUE_CR_SOFT_RESET__PBE2_XE__MASKFULL 0xFFEFFFFFFFFFFC3DULL +#define ROGUE_CR_SOFT_RESET_MASKFULL 0x00E7FFFFFFFFFC3DULL +#define ROGUE_CR_SOFT_RESET_PHANTOM3_CORE_SHIFT 63U +#define ROGUE_CR_SOFT_RESET_PHANTOM3_CORE_CLRMSK 0x7FFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_PHANTOM3_CORE_EN 0x8000000000000000ULL +#define ROGUE_CR_SOFT_RESET_PHANTOM2_CORE_SHIFT 62U +#define ROGUE_CR_SOFT_RESET_PHANTOM2_CORE_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_PHANTOM2_CORE_EN 0x4000000000000000ULL +#define ROGUE_CR_SOFT_RESET_BERNADO2_CORE_SHIFT 61U +#define ROGUE_CR_SOFT_RESET_BERNADO2_CORE_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_BERNADO2_CORE_EN 0x2000000000000000ULL +#define ROGUE_CR_SOFT_RESET_JONES_CORE_SHIFT 60U +#define ROGUE_CR_SOFT_RESET_JONES_CORE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_JONES_CORE_EN 0x1000000000000000ULL +#define ROGUE_CR_SOFT_RESET_TILING_CORE_SHIFT 59U +#define ROGUE_CR_SOFT_RESET_TILING_CORE_CLRMSK 0xF7FFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_TILING_CORE_EN 0x0800000000000000ULL +#define ROGUE_CR_SOFT_RESET_TE3_SHIFT 58U +#define ROGUE_CR_SOFT_RESET_TE3_CLRMSK 0xFBFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_TE3_EN 0x0400000000000000ULL +#define ROGUE_CR_SOFT_RESET_VCE_SHIFT 57U +#define ROGUE_CR_SOFT_RESET_VCE_CLRMSK 0xFDFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_VCE_EN 0x0200000000000000ULL +#define ROGUE_CR_SOFT_RESET_VBS_SHIFT 56U +#define ROGUE_CR_SOFT_RESET_VBS_CLRMSK 0xFEFFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_VBS_EN 0x0100000000000000ULL +#define ROGUE_CR_SOFT_RESET_DPX1_CORE_SHIFT 55U +#define ROGUE_CR_SOFT_RESET_DPX1_CORE_CLRMSK 0xFF7FFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DPX1_CORE_EN 0x0080000000000000ULL +#define ROGUE_CR_SOFT_RESET_DPX0_CORE_SHIFT 54U +#define ROGUE_CR_SOFT_RESET_DPX0_CORE_CLRMSK 0xFFBFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DPX0_CORE_EN 0x0040000000000000ULL +#define ROGUE_CR_SOFT_RESET_FBA_SHIFT 53U +#define ROGUE_CR_SOFT_RESET_FBA_CLRMSK 0xFFDFFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_FBA_EN 0x0020000000000000ULL +#define ROGUE_CR_SOFT_RESET_FB_CDC_SHIFT 51U +#define ROGUE_CR_SOFT_RESET_FB_CDC_CLRMSK 0xFFF7FFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_FB_CDC_EN 0x0008000000000000ULL +#define ROGUE_CR_SOFT_RESET_SH_SHIFT 50U +#define ROGUE_CR_SOFT_RESET_SH_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_SH_EN 0x0004000000000000ULL +#define ROGUE_CR_SOFT_RESET_VRDM_SHIFT 49U +#define ROGUE_CR_SOFT_RESET_VRDM_CLRMSK 0xFFFDFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_VRDM_EN 0x0002000000000000ULL +#define ROGUE_CR_SOFT_RESET_MCU_FBTC_SHIFT 48U +#define ROGUE_CR_SOFT_RESET_MCU_FBTC_CLRMSK 0xFFFEFFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_MCU_FBTC_EN 0x0001000000000000ULL +#define ROGUE_CR_SOFT_RESET_PHANTOM1_CORE_SHIFT 47U +#define ROGUE_CR_SOFT_RESET_PHANTOM1_CORE_CLRMSK 0xFFFF7FFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_PHANTOM1_CORE_EN 0x0000800000000000ULL +#define ROGUE_CR_SOFT_RESET_PHANTOM0_CORE_SHIFT 46U +#define ROGUE_CR_SOFT_RESET_PHANTOM0_CORE_CLRMSK 0xFFFFBFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_PHANTOM0_CORE_EN 0x0000400000000000ULL +#define ROGUE_CR_SOFT_RESET_BERNADO1_CORE_SHIFT 45U +#define ROGUE_CR_SOFT_RESET_BERNADO1_CORE_CLRMSK 0xFFFFDFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_BERNADO1_CORE_EN 0x0000200000000000ULL +#define ROGUE_CR_SOFT_RESET_BERNADO0_CORE_SHIFT 44U +#define ROGUE_CR_SOFT_RESET_BERNADO0_CORE_CLRMSK 0xFFFFEFFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_BERNADO0_CORE_EN 0x0000100000000000ULL +#define ROGUE_CR_SOFT_RESET_IPP_SHIFT 43U +#define ROGUE_CR_SOFT_RESET_IPP_CLRMSK 0xFFFFF7FFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_IPP_EN 0x0000080000000000ULL +#define ROGUE_CR_SOFT_RESET_BIF_TEXAS_SHIFT 42U +#define ROGUE_CR_SOFT_RESET_BIF_TEXAS_CLRMSK 0xFFFFFBFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_BIF_TEXAS_EN 0x0000040000000000ULL +#define ROGUE_CR_SOFT_RESET_TORNADO_CORE_SHIFT 41U +#define ROGUE_CR_SOFT_RESET_TORNADO_CORE_CLRMSK 0xFFFFFDFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_TORNADO_CORE_EN 0x0000020000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_H_CORE_SHIFT 40U +#define ROGUE_CR_SOFT_RESET_DUST_H_CORE_CLRMSK 0xFFFFFEFFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_H_CORE_EN 0x0000010000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_G_CORE_SHIFT 39U +#define ROGUE_CR_SOFT_RESET_DUST_G_CORE_CLRMSK 0xFFFFFF7FFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_G_CORE_EN 0x0000008000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_F_CORE_SHIFT 38U +#define ROGUE_CR_SOFT_RESET_DUST_F_CORE_CLRMSK 0xFFFFFFBFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_F_CORE_EN 0x0000004000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_E_CORE_SHIFT 37U +#define ROGUE_CR_SOFT_RESET_DUST_E_CORE_CLRMSK 0xFFFFFFDFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_E_CORE_EN 0x0000002000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_D_CORE_SHIFT 36U +#define ROGUE_CR_SOFT_RESET_DUST_D_CORE_CLRMSK 0xFFFFFFEFFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_D_CORE_EN 0x0000001000000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_C_CORE_SHIFT 35U +#define ROGUE_CR_SOFT_RESET_DUST_C_CORE_CLRMSK 0xFFFFFFF7FFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_C_CORE_EN 0x0000000800000000ULL +#define ROGUE_CR_SOFT_RESET_MMU_SHIFT 34U +#define ROGUE_CR_SOFT_RESET_MMU_CLRMSK 0xFFFFFFFBFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_MMU_EN 0x0000000400000000ULL +#define ROGUE_CR_SOFT_RESET_BIF1_SHIFT 33U +#define ROGUE_CR_SOFT_RESET_BIF1_CLRMSK 0xFFFFFFFDFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_BIF1_EN 0x0000000200000000ULL +#define ROGUE_CR_SOFT_RESET_GARTEN_SHIFT 32U +#define ROGUE_CR_SOFT_RESET_GARTEN_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_GARTEN_EN 0x0000000100000000ULL +#define ROGUE_CR_SOFT_RESET_CPU_SHIFT 32U +#define ROGUE_CR_SOFT_RESET_CPU_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_CPU_EN 0x0000000100000000ULL +#define ROGUE_CR_SOFT_RESET_RASCAL_CORE_SHIFT 31U +#define ROGUE_CR_SOFT_RESET_RASCAL_CORE_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_SOFT_RESET_RASCAL_CORE_EN 0x0000000080000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_B_CORE_SHIFT 30U +#define ROGUE_CR_SOFT_RESET_DUST_B_CORE_CLRMSK 0xFFFFFFFFBFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_B_CORE_EN 0x0000000040000000ULL +#define ROGUE_CR_SOFT_RESET_DUST_A_CORE_SHIFT 29U +#define ROGUE_CR_SOFT_RESET_DUST_A_CORE_CLRMSK 0xFFFFFFFFDFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_DUST_A_CORE_EN 0x0000000020000000ULL +#define ROGUE_CR_SOFT_RESET_FB_TLCACHE_SHIFT 28U +#define ROGUE_CR_SOFT_RESET_FB_TLCACHE_CLRMSK 0xFFFFFFFFEFFFFFFFULL +#define ROGUE_CR_SOFT_RESET_FB_TLCACHE_EN 0x0000000010000000ULL +#define ROGUE_CR_SOFT_RESET_SLC_SHIFT 27U +#define ROGUE_CR_SOFT_RESET_SLC_CLRMSK 0xFFFFFFFFF7FFFFFFULL +#define ROGUE_CR_SOFT_RESET_SLC_EN 0x0000000008000000ULL +#define ROGUE_CR_SOFT_RESET_TLA_SHIFT 26U +#define ROGUE_CR_SOFT_RESET_TLA_CLRMSK 0xFFFFFFFFFBFFFFFFULL +#define ROGUE_CR_SOFT_RESET_TLA_EN 0x0000000004000000ULL +#define ROGUE_CR_SOFT_RESET_UVS_SHIFT 25U +#define ROGUE_CR_SOFT_RESET_UVS_CLRMSK 0xFFFFFFFFFDFFFFFFULL +#define ROGUE_CR_SOFT_RESET_UVS_EN 0x0000000002000000ULL +#define ROGUE_CR_SOFT_RESET_TE_SHIFT 24U +#define ROGUE_CR_SOFT_RESET_TE_CLRMSK 0xFFFFFFFFFEFFFFFFULL +#define ROGUE_CR_SOFT_RESET_TE_EN 0x0000000001000000ULL +#define ROGUE_CR_SOFT_RESET_GPP_SHIFT 23U +#define ROGUE_CR_SOFT_RESET_GPP_CLRMSK 0xFFFFFFFFFF7FFFFFULL +#define ROGUE_CR_SOFT_RESET_GPP_EN 0x0000000000800000ULL +#define ROGUE_CR_SOFT_RESET_FBDC_SHIFT 22U +#define ROGUE_CR_SOFT_RESET_FBDC_CLRMSK 0xFFFFFFFFFFBFFFFFULL +#define ROGUE_CR_SOFT_RESET_FBDC_EN 0x0000000000400000ULL +#define ROGUE_CR_SOFT_RESET_FBC_SHIFT 21U +#define ROGUE_CR_SOFT_RESET_FBC_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_SOFT_RESET_FBC_EN 0x0000000000200000ULL +#define ROGUE_CR_SOFT_RESET_PM_SHIFT 20U +#define ROGUE_CR_SOFT_RESET_PM_CLRMSK 0xFFFFFFFFFFEFFFFFULL +#define ROGUE_CR_SOFT_RESET_PM_EN 0x0000000000100000ULL +#define ROGUE_CR_SOFT_RESET_PBE_SHIFT 19U +#define ROGUE_CR_SOFT_RESET_PBE_CLRMSK 0xFFFFFFFFFFF7FFFFULL +#define ROGUE_CR_SOFT_RESET_PBE_EN 0x0000000000080000ULL +#define ROGUE_CR_SOFT_RESET_USC_SHARED_SHIFT 18U +#define ROGUE_CR_SOFT_RESET_USC_SHARED_CLRMSK 0xFFFFFFFFFFFBFFFFULL +#define ROGUE_CR_SOFT_RESET_USC_SHARED_EN 0x0000000000040000ULL +#define ROGUE_CR_SOFT_RESET_MCU_L1_SHIFT 17U +#define ROGUE_CR_SOFT_RESET_MCU_L1_CLRMSK 0xFFFFFFFFFFFDFFFFULL +#define ROGUE_CR_SOFT_RESET_MCU_L1_EN 0x0000000000020000ULL +#define ROGUE_CR_SOFT_RESET_BIF_SHIFT 16U +#define ROGUE_CR_SOFT_RESET_BIF_CLRMSK 0xFFFFFFFFFFFEFFFFULL +#define ROGUE_CR_SOFT_RESET_BIF_EN 0x0000000000010000ULL +#define ROGUE_CR_SOFT_RESET_CDM_SHIFT 15U +#define ROGUE_CR_SOFT_RESET_CDM_CLRMSK 0xFFFFFFFFFFFF7FFFULL +#define ROGUE_CR_SOFT_RESET_CDM_EN 0x0000000000008000ULL +#define ROGUE_CR_SOFT_RESET_VDM_SHIFT 14U +#define ROGUE_CR_SOFT_RESET_VDM_CLRMSK 0xFFFFFFFFFFFFBFFFULL +#define ROGUE_CR_SOFT_RESET_VDM_EN 0x0000000000004000ULL +#define ROGUE_CR_SOFT_RESET_TESS_SHIFT 13U +#define ROGUE_CR_SOFT_RESET_TESS_CLRMSK 0xFFFFFFFFFFFFDFFFULL +#define ROGUE_CR_SOFT_RESET_TESS_EN 0x0000000000002000ULL +#define ROGUE_CR_SOFT_RESET_PDS_SHIFT 12U +#define ROGUE_CR_SOFT_RESET_PDS_CLRMSK 0xFFFFFFFFFFFFEFFFULL +#define ROGUE_CR_SOFT_RESET_PDS_EN 0x0000000000001000ULL +#define ROGUE_CR_SOFT_RESET_ISP_SHIFT 11U +#define ROGUE_CR_SOFT_RESET_ISP_CLRMSK 0xFFFFFFFFFFFFF7FFULL +#define ROGUE_CR_SOFT_RESET_ISP_EN 0x0000000000000800ULL +#define ROGUE_CR_SOFT_RESET_TSP_SHIFT 10U +#define ROGUE_CR_SOFT_RESET_TSP_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_SOFT_RESET_TSP_EN 0x0000000000000400ULL +#define ROGUE_CR_SOFT_RESET_SYSARB_SHIFT 5U +#define ROGUE_CR_SOFT_RESET_SYSARB_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_SOFT_RESET_SYSARB_EN 0x0000000000000020ULL +#define ROGUE_CR_SOFT_RESET_TPU_MCU_DEMUX_SHIFT 4U +#define ROGUE_CR_SOFT_RESET_TPU_MCU_DEMUX_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_SOFT_RESET_TPU_MCU_DEMUX_EN 0x0000000000000010ULL +#define ROGUE_CR_SOFT_RESET_MCU_L0_SHIFT 3U +#define ROGUE_CR_SOFT_RESET_MCU_L0_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_SOFT_RESET_MCU_L0_EN 0x0000000000000008ULL +#define ROGUE_CR_SOFT_RESET_TPU_SHIFT 2U +#define ROGUE_CR_SOFT_RESET_TPU_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_SOFT_RESET_TPU_EN 0x0000000000000004ULL +#define ROGUE_CR_SOFT_RESET_USC_SHIFT 0U +#define ROGUE_CR_SOFT_RESET_USC_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_SOFT_RESET_USC_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_SOFT_RESET2 */ +#define ROGUE_CR_SOFT_RESET2 0x0108U +#define ROGUE_CR_SOFT_RESET2_MASKFULL 0x00000000001FFFFFULL +#define ROGUE_CR_SOFT_RESET2_SPFILTER_SHIFT 12U +#define ROGUE_CR_SOFT_RESET2_SPFILTER_CLRMSK 0xFFE00FFFU +#define ROGUE_CR_SOFT_RESET2_TDM_SHIFT 11U +#define ROGUE_CR_SOFT_RESET2_TDM_CLRMSK 0xFFFFF7FFU +#define ROGUE_CR_SOFT_RESET2_TDM_EN 0x00000800U +#define ROGUE_CR_SOFT_RESET2_ASTC_SHIFT 10U +#define ROGUE_CR_SOFT_RESET2_ASTC_CLRMSK 0xFFFFFBFFU +#define ROGUE_CR_SOFT_RESET2_ASTC_EN 0x00000400U +#define ROGUE_CR_SOFT_RESET2_BLACKPEARL_SHIFT 9U +#define ROGUE_CR_SOFT_RESET2_BLACKPEARL_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_SOFT_RESET2_BLACKPEARL_EN 0x00000200U +#define ROGUE_CR_SOFT_RESET2_USCPS_SHIFT 8U +#define ROGUE_CR_SOFT_RESET2_USCPS_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_SOFT_RESET2_USCPS_EN 0x00000100U +#define ROGUE_CR_SOFT_RESET2_IPF_SHIFT 7U +#define ROGUE_CR_SOFT_RESET2_IPF_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_SOFT_RESET2_IPF_EN 0x00000080U +#define ROGUE_CR_SOFT_RESET2_GEOMETRY_SHIFT 6U +#define ROGUE_CR_SOFT_RESET2_GEOMETRY_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SOFT_RESET2_GEOMETRY_EN 0x00000040U +#define ROGUE_CR_SOFT_RESET2_USC_SHARED_SHIFT 5U +#define ROGUE_CR_SOFT_RESET2_USC_SHARED_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SOFT_RESET2_USC_SHARED_EN 0x00000020U +#define ROGUE_CR_SOFT_RESET2_PDS_SHARED_SHIFT 4U +#define ROGUE_CR_SOFT_RESET2_PDS_SHARED_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SOFT_RESET2_PDS_SHARED_EN 0x00000010U +#define ROGUE_CR_SOFT_RESET2_BIF_BLACKPEARL_SHIFT 3U +#define ROGUE_CR_SOFT_RESET2_BIF_BLACKPEARL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SOFT_RESET2_BIF_BLACKPEARL_EN 0x00000008U +#define ROGUE_CR_SOFT_RESET2_PIXEL_SHIFT 2U +#define ROGUE_CR_SOFT_RESET2_PIXEL_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SOFT_RESET2_PIXEL_EN 0x00000004U +#define ROGUE_CR_SOFT_RESET2_CDM_SHIFT 1U +#define ROGUE_CR_SOFT_RESET2_CDM_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SOFT_RESET2_CDM_EN 0x00000002U +#define ROGUE_CR_SOFT_RESET2_VERTEX_SHIFT 0U +#define ROGUE_CR_SOFT_RESET2_VERTEX_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SOFT_RESET2_VERTEX_EN 0x00000001U + +/* Register ROGUE_CR_EVENT_STATUS */ +#define ROGUE_CR_EVENT_STATUS 0x0130U +#define ROGUE_CR_EVENT_STATUS__ROGUEXE__MASKFULL 0x00000000E01DFFFFULL +#define ROGUE_CR_EVENT_STATUS__SIGNALS__MASKFULL 0x00000000E007FFFFULL +#define ROGUE_CR_EVENT_STATUS_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_EVENT_STATUS_TDM_FENCE_FINISHED_SHIFT 31U +#define ROGUE_CR_EVENT_STATUS_TDM_FENCE_FINISHED_CLRMSK 0x7FFFFFFFU +#define ROGUE_CR_EVENT_STATUS_TDM_FENCE_FINISHED_EN 0x80000000U +#define ROGUE_CR_EVENT_STATUS_TDM_BUFFER_STALL_SHIFT 30U +#define ROGUE_CR_EVENT_STATUS_TDM_BUFFER_STALL_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_EVENT_STATUS_TDM_BUFFER_STALL_EN 0x40000000U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_SIGNAL_FAILURE_SHIFT 29U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_SIGNAL_FAILURE_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_EVENT_STATUS_COMPUTE_SIGNAL_FAILURE_EN 0x20000000U +#define ROGUE_CR_EVENT_STATUS_DPX_OUT_OF_MEMORY_SHIFT 28U +#define ROGUE_CR_EVENT_STATUS_DPX_OUT_OF_MEMORY_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_EVENT_STATUS_DPX_OUT_OF_MEMORY_EN 0x10000000U +#define ROGUE_CR_EVENT_STATUS_DPX_MMU_PAGE_FAULT_SHIFT 27U +#define ROGUE_CR_EVENT_STATUS_DPX_MMU_PAGE_FAULT_CLRMSK 0xF7FFFFFFU +#define ROGUE_CR_EVENT_STATUS_DPX_MMU_PAGE_FAULT_EN 0x08000000U +#define ROGUE_CR_EVENT_STATUS_RPM_OUT_OF_MEMORY_SHIFT 26U +#define ROGUE_CR_EVENT_STATUS_RPM_OUT_OF_MEMORY_CLRMSK 0xFBFFFFFFU +#define ROGUE_CR_EVENT_STATUS_RPM_OUT_OF_MEMORY_EN 0x04000000U +#define ROGUE_CR_EVENT_STATUS_FBA_FC3_FINISHED_SHIFT 25U +#define ROGUE_CR_EVENT_STATUS_FBA_FC3_FINISHED_CLRMSK 0xFDFFFFFFU +#define ROGUE_CR_EVENT_STATUS_FBA_FC3_FINISHED_EN 0x02000000U +#define ROGUE_CR_EVENT_STATUS_FBA_FC2_FINISHED_SHIFT 24U +#define ROGUE_CR_EVENT_STATUS_FBA_FC2_FINISHED_CLRMSK 0xFEFFFFFFU +#define ROGUE_CR_EVENT_STATUS_FBA_FC2_FINISHED_EN 0x01000000U +#define ROGUE_CR_EVENT_STATUS_FBA_FC1_FINISHED_SHIFT 23U +#define ROGUE_CR_EVENT_STATUS_FBA_FC1_FINISHED_CLRMSK 0xFF7FFFFFU +#define ROGUE_CR_EVENT_STATUS_FBA_FC1_FINISHED_EN 0x00800000U +#define ROGUE_CR_EVENT_STATUS_FBA_FC0_FINISHED_SHIFT 22U +#define ROGUE_CR_EVENT_STATUS_FBA_FC0_FINISHED_CLRMSK 0xFFBFFFFFU +#define ROGUE_CR_EVENT_STATUS_FBA_FC0_FINISHED_EN 0x00400000U +#define ROGUE_CR_EVENT_STATUS_RDM_FC3_FINISHED_SHIFT 21U +#define ROGUE_CR_EVENT_STATUS_RDM_FC3_FINISHED_CLRMSK 0xFFDFFFFFU +#define ROGUE_CR_EVENT_STATUS_RDM_FC3_FINISHED_EN 0x00200000U +#define ROGUE_CR_EVENT_STATUS_RDM_FC2_FINISHED_SHIFT 20U +#define ROGUE_CR_EVENT_STATUS_RDM_FC2_FINISHED_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_EVENT_STATUS_RDM_FC2_FINISHED_EN 0x00100000U +#define ROGUE_CR_EVENT_STATUS_SAFETY_SHIFT 20U +#define ROGUE_CR_EVENT_STATUS_SAFETY_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_EVENT_STATUS_SAFETY_EN 0x00100000U +#define ROGUE_CR_EVENT_STATUS_RDM_FC1_FINISHED_SHIFT 19U +#define ROGUE_CR_EVENT_STATUS_RDM_FC1_FINISHED_CLRMSK 0xFFF7FFFFU +#define ROGUE_CR_EVENT_STATUS_RDM_FC1_FINISHED_EN 0x00080000U +#define ROGUE_CR_EVENT_STATUS_SLAVE_REQ_SHIFT 19U +#define ROGUE_CR_EVENT_STATUS_SLAVE_REQ_CLRMSK 0xFFF7FFFFU +#define ROGUE_CR_EVENT_STATUS_SLAVE_REQ_EN 0x00080000U +#define ROGUE_CR_EVENT_STATUS_RDM_FC0_FINISHED_SHIFT 18U +#define ROGUE_CR_EVENT_STATUS_RDM_FC0_FINISHED_CLRMSK 0xFFFBFFFFU +#define ROGUE_CR_EVENT_STATUS_RDM_FC0_FINISHED_EN 0x00040000U +#define ROGUE_CR_EVENT_STATUS_TDM_CONTEXT_STORE_FINISHED_SHIFT 18U +#define ROGUE_CR_EVENT_STATUS_TDM_CONTEXT_STORE_FINISHED_CLRMSK 0xFFFBFFFFU +#define ROGUE_CR_EVENT_STATUS_TDM_CONTEXT_STORE_FINISHED_EN 0x00040000U +#define ROGUE_CR_EVENT_STATUS_SHG_FINISHED_SHIFT 17U +#define ROGUE_CR_EVENT_STATUS_SHG_FINISHED_CLRMSK 0xFFFDFFFFU +#define ROGUE_CR_EVENT_STATUS_SHG_FINISHED_EN 0x00020000U +#define ROGUE_CR_EVENT_STATUS_SPFILTER_SIGNAL_UPDATE_SHIFT 17U +#define ROGUE_CR_EVENT_STATUS_SPFILTER_SIGNAL_UPDATE_CLRMSK 0xFFFDFFFFU +#define ROGUE_CR_EVENT_STATUS_SPFILTER_SIGNAL_UPDATE_EN 0x00020000U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_BUFFER_STALL_SHIFT 16U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_BUFFER_STALL_CLRMSK 0xFFFEFFFFU +#define ROGUE_CR_EVENT_STATUS_COMPUTE_BUFFER_STALL_EN 0x00010000U +#define ROGUE_CR_EVENT_STATUS_USC_TRIGGER_SHIFT 15U +#define ROGUE_CR_EVENT_STATUS_USC_TRIGGER_CLRMSK 0xFFFF7FFFU +#define ROGUE_CR_EVENT_STATUS_USC_TRIGGER_EN 0x00008000U +#define ROGUE_CR_EVENT_STATUS_ZLS_FINISHED_SHIFT 14U +#define ROGUE_CR_EVENT_STATUS_ZLS_FINISHED_CLRMSK 0xFFFFBFFFU +#define ROGUE_CR_EVENT_STATUS_ZLS_FINISHED_EN 0x00004000U +#define ROGUE_CR_EVENT_STATUS_GPIO_ACK_SHIFT 13U +#define ROGUE_CR_EVENT_STATUS_GPIO_ACK_CLRMSK 0xFFFFDFFFU +#define ROGUE_CR_EVENT_STATUS_GPIO_ACK_EN 0x00002000U +#define ROGUE_CR_EVENT_STATUS_GPIO_REQ_SHIFT 12U +#define ROGUE_CR_EVENT_STATUS_GPIO_REQ_CLRMSK 0xFFFFEFFFU +#define ROGUE_CR_EVENT_STATUS_GPIO_REQ_EN 0x00001000U +#define ROGUE_CR_EVENT_STATUS_POWER_ABORT_SHIFT 11U +#define ROGUE_CR_EVENT_STATUS_POWER_ABORT_CLRMSK 0xFFFFF7FFU +#define ROGUE_CR_EVENT_STATUS_POWER_ABORT_EN 0x00000800U +#define ROGUE_CR_EVENT_STATUS_POWER_COMPLETE_SHIFT 10U +#define ROGUE_CR_EVENT_STATUS_POWER_COMPLETE_CLRMSK 0xFFFFFBFFU +#define ROGUE_CR_EVENT_STATUS_POWER_COMPLETE_EN 0x00000400U +#define ROGUE_CR_EVENT_STATUS_MMU_PAGE_FAULT_SHIFT 9U +#define ROGUE_CR_EVENT_STATUS_MMU_PAGE_FAULT_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_EVENT_STATUS_MMU_PAGE_FAULT_EN 0x00000200U +#define ROGUE_CR_EVENT_STATUS_PM_3D_MEM_FREE_SHIFT 8U +#define ROGUE_CR_EVENT_STATUS_PM_3D_MEM_FREE_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_EVENT_STATUS_PM_3D_MEM_FREE_EN 0x00000100U +#define ROGUE_CR_EVENT_STATUS_PM_OUT_OF_MEMORY_SHIFT 7U +#define ROGUE_CR_EVENT_STATUS_PM_OUT_OF_MEMORY_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_EVENT_STATUS_PM_OUT_OF_MEMORY_EN 0x00000080U +#define ROGUE_CR_EVENT_STATUS_TA_TERMINATE_SHIFT 6U +#define ROGUE_CR_EVENT_STATUS_TA_TERMINATE_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_EVENT_STATUS_TA_TERMINATE_EN 0x00000040U +#define ROGUE_CR_EVENT_STATUS_TA_FINISHED_SHIFT 5U +#define ROGUE_CR_EVENT_STATUS_TA_FINISHED_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_EVENT_STATUS_TA_FINISHED_EN 0x00000020U +#define ROGUE_CR_EVENT_STATUS_ISP_END_MACROTILE_SHIFT 4U +#define ROGUE_CR_EVENT_STATUS_ISP_END_MACROTILE_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_EVENT_STATUS_ISP_END_MACROTILE_EN 0x00000010U +#define ROGUE_CR_EVENT_STATUS_PIXELBE_END_RENDER_SHIFT 3U +#define ROGUE_CR_EVENT_STATUS_PIXELBE_END_RENDER_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_EVENT_STATUS_PIXELBE_END_RENDER_EN 0x00000008U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_FINISHED_SHIFT 2U +#define ROGUE_CR_EVENT_STATUS_COMPUTE_FINISHED_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_EVENT_STATUS_COMPUTE_FINISHED_EN 0x00000004U +#define ROGUE_CR_EVENT_STATUS_KERNEL_FINISHED_SHIFT 1U +#define ROGUE_CR_EVENT_STATUS_KERNEL_FINISHED_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_EVENT_STATUS_KERNEL_FINISHED_EN 0x00000002U +#define ROGUE_CR_EVENT_STATUS_TLA_COMPLETE_SHIFT 0U +#define ROGUE_CR_EVENT_STATUS_TLA_COMPLETE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_EVENT_STATUS_TLA_COMPLETE_EN 0x00000001U + +/* Register ROGUE_CR_TIMER */ +#define ROGUE_CR_TIMER 0x0160U +#define ROGUE_CR_TIMER_MASKFULL 0x8000FFFFFFFFFFFFULL +#define ROGUE_CR_TIMER_BIT31_SHIFT 63U +#define ROGUE_CR_TIMER_BIT31_CLRMSK 0x7FFFFFFFFFFFFFFFULL +#define ROGUE_CR_TIMER_BIT31_EN 0x8000000000000000ULL +#define ROGUE_CR_TIMER_VALUE_SHIFT 0U +#define ROGUE_CR_TIMER_VALUE_CLRMSK 0xFFFF000000000000ULL + +/* Register ROGUE_CR_TLA_STATUS */ +#define ROGUE_CR_TLA_STATUS 0x0178U +#define ROGUE_CR_TLA_STATUS_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_TLA_STATUS_BLIT_COUNT_SHIFT 39U +#define ROGUE_CR_TLA_STATUS_BLIT_COUNT_CLRMSK 0x0000007FFFFFFFFFULL +#define ROGUE_CR_TLA_STATUS_REQUEST_SHIFT 7U +#define ROGUE_CR_TLA_STATUS_REQUEST_CLRMSK 0xFFFFFF800000007FULL +#define ROGUE_CR_TLA_STATUS_FIFO_FULLNESS_SHIFT 1U +#define ROGUE_CR_TLA_STATUS_FIFO_FULLNESS_CLRMSK 0xFFFFFFFFFFFFFF81ULL +#define ROGUE_CR_TLA_STATUS_BUSY_SHIFT 0U +#define ROGUE_CR_TLA_STATUS_BUSY_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_TLA_STATUS_BUSY_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_PM_PARTIAL_RENDER_ENABLE */ +#define ROGUE_CR_PM_PARTIAL_RENDER_ENABLE 0x0338U +#define ROGUE_CR_PM_PARTIAL_RENDER_ENABLE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_PM_PARTIAL_RENDER_ENABLE_OP_SHIFT 0U +#define ROGUE_CR_PM_PARTIAL_RENDER_ENABLE_OP_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_PM_PARTIAL_RENDER_ENABLE_OP_EN 0x00000001U + +/* Register ROGUE_CR_SIDEKICK_IDLE */ +#define ROGUE_CR_SIDEKICK_IDLE 0x03C8U +#define ROGUE_CR_SIDEKICK_IDLE_MASKFULL 0x000000000000007FULL +#define ROGUE_CR_SIDEKICK_IDLE_FB_CDC_SHIFT 6U +#define ROGUE_CR_SIDEKICK_IDLE_FB_CDC_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SIDEKICK_IDLE_FB_CDC_EN 0x00000040U +#define ROGUE_CR_SIDEKICK_IDLE_MMU_SHIFT 5U +#define ROGUE_CR_SIDEKICK_IDLE_MMU_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SIDEKICK_IDLE_MMU_EN 0x00000020U +#define ROGUE_CR_SIDEKICK_IDLE_BIF128_SHIFT 4U +#define ROGUE_CR_SIDEKICK_IDLE_BIF128_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SIDEKICK_IDLE_BIF128_EN 0x00000010U +#define ROGUE_CR_SIDEKICK_IDLE_TLA_SHIFT 3U +#define ROGUE_CR_SIDEKICK_IDLE_TLA_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SIDEKICK_IDLE_TLA_EN 0x00000008U +#define ROGUE_CR_SIDEKICK_IDLE_GARTEN_SHIFT 2U +#define ROGUE_CR_SIDEKICK_IDLE_GARTEN_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SIDEKICK_IDLE_GARTEN_EN 0x00000004U +#define ROGUE_CR_SIDEKICK_IDLE_HOSTIF_SHIFT 1U +#define ROGUE_CR_SIDEKICK_IDLE_HOSTIF_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SIDEKICK_IDLE_HOSTIF_EN 0x00000002U +#define ROGUE_CR_SIDEKICK_IDLE_SOCIF_SHIFT 0U +#define ROGUE_CR_SIDEKICK_IDLE_SOCIF_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SIDEKICK_IDLE_SOCIF_EN 0x00000001U + +/* Register ROGUE_CR_MARS_IDLE */ +#define ROGUE_CR_MARS_IDLE 0x08F8U +#define ROGUE_CR_MARS_IDLE_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_MARS_IDLE_MH_SYSARB0_SHIFT 2U +#define ROGUE_CR_MARS_IDLE_MH_SYSARB0_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_MARS_IDLE_MH_SYSARB0_EN 0x00000004U +#define ROGUE_CR_MARS_IDLE_CPU_SHIFT 1U +#define ROGUE_CR_MARS_IDLE_CPU_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_MARS_IDLE_CPU_EN 0x00000002U +#define ROGUE_CR_MARS_IDLE_SOCIF_SHIFT 0U +#define ROGUE_CR_MARS_IDLE_SOCIF_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MARS_IDLE_SOCIF_EN 0x00000001U + +/* Register ROGUE_CR_VDM_CONTEXT_STORE_STATUS */ +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS 0x0430U +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_MASKFULL 0x00000000000000F3ULL +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_LAST_PIPE_SHIFT 4U +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_LAST_PIPE_CLRMSK 0xFFFFFF0FU +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_NEED_RESUME_SHIFT 1U +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_NEED_RESUME_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_NEED_RESUME_EN 0x00000002U +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_COMPLETE_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_COMPLETE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_VDM_CONTEXT_STORE_STATUS_COMPLETE_EN 0x00000001U + +/* Register ROGUE_CR_VDM_CONTEXT_STORE_TASK0 */ +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0 0x0438U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0_PDS_STATE1_SHIFT 32U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0_PDS_STATE1_CLRMSK 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0_PDS_STATE0_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK0_PDS_STATE0_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_VDM_CONTEXT_STORE_TASK1 */ +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK1 0x0440U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK1_PDS_STATE2_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK1_PDS_STATE2_CLRMSK 0x00000000U + +/* Register ROGUE_CR_VDM_CONTEXT_STORE_TASK2 */ +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2 0x0448U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2_STREAM_OUT2_SHIFT 32U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2_STREAM_OUT2_CLRMSK 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2_STREAM_OUT1_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_STORE_TASK2_STREAM_OUT1_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_VDM_CONTEXT_RESUME_TASK0 */ +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0 0x0450U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0_PDS_STATE1_SHIFT 32U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0_PDS_STATE1_CLRMSK 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0_PDS_STATE0_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK0_PDS_STATE0_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_VDM_CONTEXT_RESUME_TASK1 */ +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK1 0x0458U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK1_PDS_STATE2_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK1_PDS_STATE2_CLRMSK 0x00000000U + +/* Register ROGUE_CR_VDM_CONTEXT_RESUME_TASK2 */ +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2 0x0460U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2_STREAM_OUT2_SHIFT 32U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2_STREAM_OUT2_CLRMSK 0x00000000FFFFFFFFULL +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2_STREAM_OUT1_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_RESUME_TASK2_STREAM_OUT1_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_CDM_CONTEXT_STORE_STATUS */ +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS 0x04A0U +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_NEED_RESUME_SHIFT 1U +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_NEED_RESUME_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_NEED_RESUME_EN 0x00000002U +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_COMPLETE_SHIFT 0U +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_COMPLETE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_CDM_CONTEXT_STORE_STATUS_COMPLETE_EN 0x00000001U + +/* Register ROGUE_CR_CDM_CONTEXT_PDS0 */ +#define ROGUE_CR_CDM_CONTEXT_PDS0 0x04A8U +#define ROGUE_CR_CDM_CONTEXT_PDS0_MASKFULL 0xFFFFFFF0FFFFFFF0ULL +#define ROGUE_CR_CDM_CONTEXT_PDS0_DATA_ADDR_SHIFT 36U +#define ROGUE_CR_CDM_CONTEXT_PDS0_DATA_ADDR_CLRMSK 0x0000000FFFFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_PDS0_DATA_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_PDS0_DATA_ADDR_ALIGNSIZE 16U +#define ROGUE_CR_CDM_CONTEXT_PDS0_CODE_ADDR_SHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_PDS0_CODE_ADDR_CLRMSK 0xFFFFFFFF0000000FULL +#define ROGUE_CR_CDM_CONTEXT_PDS0_CODE_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_PDS0_CODE_ADDR_ALIGNSIZE 16U + +/* Register ROGUE_CR_CDM_CONTEXT_PDS1 */ +#define ROGUE_CR_CDM_CONTEXT_PDS1 0x04B0U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__MASKFULL 0x000000007FFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_PDS1_MASKFULL 0x000000003FFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__PDS_SEQ_DEP_SHIFT 30U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__PDS_SEQ_DEP_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__PDS_SEQ_DEP_EN 0x40000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1_PDS_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_CONTEXT_PDS1_PDS_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_PDS_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__USC_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__USC_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__USC_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1_USC_SEQ_DEP_SHIFT 28U +#define ROGUE_CR_CDM_CONTEXT_PDS1_USC_SEQ_DEP_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_USC_SEQ_DEP_EN 0x10000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__TARGET_SHIFT 28U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__TARGET_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__TARGET_EN 0x10000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1_TARGET_SHIFT 27U +#define ROGUE_CR_CDM_CONTEXT_PDS1_TARGET_CLRMSK 0xF7FFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_TARGET_EN 0x08000000U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__UNIFIED_SIZE_SHIFT 22U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__UNIFIED_SIZE_CLRMSK 0xF03FFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_UNIFIED_SIZE_SHIFT 21U +#define ROGUE_CR_CDM_CONTEXT_PDS1_UNIFIED_SIZE_CLRMSK 0xF81FFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__COMMON_SHARED_SHIFT 21U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__COMMON_SHARED_CLRMSK 0xFFDFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__COMMON_SHARED_EN 0x00200000U +#define ROGUE_CR_CDM_CONTEXT_PDS1_COMMON_SHARED_SHIFT 20U +#define ROGUE_CR_CDM_CONTEXT_PDS1_COMMON_SHARED_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_COMMON_SHARED_EN 0x00100000U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__COMMON_SIZE_SHIFT 12U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__COMMON_SIZE_CLRMSK 0xFFE00FFFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_COMMON_SIZE_SHIFT 11U +#define ROGUE_CR_CDM_CONTEXT_PDS1_COMMON_SIZE_CLRMSK 0xFFF007FFU +#define ROGUE_CR_CDM_CONTEXT_PDS1_TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_CONTEXT_PDS1_TEMP_SIZE_CLRMSK 0xFFFFF87FU +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_CONTEXT_PDS1__TEMPSIZE8__TEMP_SIZE_CLRMSK 0xFFFFF07FU +#define ROGUE_CR_CDM_CONTEXT_PDS1_DATA_SIZE_SHIFT 1U +#define ROGUE_CR_CDM_CONTEXT_PDS1_DATA_SIZE_CLRMSK 0xFFFFFF81U +#define ROGUE_CR_CDM_CONTEXT_PDS1_FENCE_SHIFT 0U +#define ROGUE_CR_CDM_CONTEXT_PDS1_FENCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_CDM_CONTEXT_PDS1_FENCE_EN 0x00000001U + +/* Register ROGUE_CR_CDM_TERMINATE_PDS */ +#define ROGUE_CR_CDM_TERMINATE_PDS 0x04B8U +#define ROGUE_CR_CDM_TERMINATE_PDS_MASKFULL 0xFFFFFFF0FFFFFFF0ULL +#define ROGUE_CR_CDM_TERMINATE_PDS_DATA_ADDR_SHIFT 36U +#define ROGUE_CR_CDM_TERMINATE_PDS_DATA_ADDR_CLRMSK 0x0000000FFFFFFFFFULL +#define ROGUE_CR_CDM_TERMINATE_PDS_DATA_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_TERMINATE_PDS_DATA_ADDR_ALIGNSIZE 16U +#define ROGUE_CR_CDM_TERMINATE_PDS_CODE_ADDR_SHIFT 4U +#define ROGUE_CR_CDM_TERMINATE_PDS_CODE_ADDR_CLRMSK 0xFFFFFFFF0000000FULL +#define ROGUE_CR_CDM_TERMINATE_PDS_CODE_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_TERMINATE_PDS_CODE_ADDR_ALIGNSIZE 16U + +/* Register ROGUE_CR_CDM_TERMINATE_PDS1 */ +#define ROGUE_CR_CDM_TERMINATE_PDS1 0x04C0U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__MASKFULL 0x000000007FFFFFFFULL +#define ROGUE_CR_CDM_TERMINATE_PDS1_MASKFULL 0x000000003FFFFFFFULL +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__PDS_SEQ_DEP_SHIFT 30U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__PDS_SEQ_DEP_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__PDS_SEQ_DEP_EN 0x40000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1_PDS_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_TERMINATE_PDS1_PDS_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_PDS_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__USC_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__USC_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__USC_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1_USC_SEQ_DEP_SHIFT 28U +#define ROGUE_CR_CDM_TERMINATE_PDS1_USC_SEQ_DEP_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_USC_SEQ_DEP_EN 0x10000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__TARGET_SHIFT 28U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__TARGET_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__TARGET_EN 0x10000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1_TARGET_SHIFT 27U +#define ROGUE_CR_CDM_TERMINATE_PDS1_TARGET_CLRMSK 0xF7FFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_TARGET_EN 0x08000000U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__UNIFIED_SIZE_SHIFT 22U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__UNIFIED_SIZE_CLRMSK 0xF03FFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_UNIFIED_SIZE_SHIFT 21U +#define ROGUE_CR_CDM_TERMINATE_PDS1_UNIFIED_SIZE_CLRMSK 0xF81FFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__COMMON_SHARED_SHIFT 21U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__COMMON_SHARED_CLRMSK 0xFFDFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__COMMON_SHARED_EN 0x00200000U +#define ROGUE_CR_CDM_TERMINATE_PDS1_COMMON_SHARED_SHIFT 20U +#define ROGUE_CR_CDM_TERMINATE_PDS1_COMMON_SHARED_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_COMMON_SHARED_EN 0x00100000U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__COMMON_SIZE_SHIFT 12U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__COMMON_SIZE_CLRMSK 0xFFE00FFFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_COMMON_SIZE_SHIFT 11U +#define ROGUE_CR_CDM_TERMINATE_PDS1_COMMON_SIZE_CLRMSK 0xFFF007FFU +#define ROGUE_CR_CDM_TERMINATE_PDS1_TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_TERMINATE_PDS1_TEMP_SIZE_CLRMSK 0xFFFFF87FU +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_TERMINATE_PDS1__TEMPSIZE8__TEMP_SIZE_CLRMSK 0xFFFFF07FU +#define ROGUE_CR_CDM_TERMINATE_PDS1_DATA_SIZE_SHIFT 1U +#define ROGUE_CR_CDM_TERMINATE_PDS1_DATA_SIZE_CLRMSK 0xFFFFFF81U +#define ROGUE_CR_CDM_TERMINATE_PDS1_FENCE_SHIFT 0U +#define ROGUE_CR_CDM_TERMINATE_PDS1_FENCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_CDM_TERMINATE_PDS1_FENCE_EN 0x00000001U + +/* Register ROGUE_CR_CDM_CONTEXT_LOAD_PDS0 */ +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0 0x04D8U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_MASKFULL 0xFFFFFFF0FFFFFFF0ULL +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_DATA_ADDR_SHIFT 36U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_DATA_ADDR_CLRMSK 0x0000000FFFFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_DATA_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_DATA_ADDR_ALIGNSIZE 16U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_CODE_ADDR_SHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_CODE_ADDR_CLRMSK 0xFFFFFFFF0000000FULL +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_CODE_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS0_CODE_ADDR_ALIGNSIZE 16U + +/* Register ROGUE_CR_CDM_CONTEXT_LOAD_PDS1 */ +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1 0x04E0U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__MASKFULL 0x000000007FFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_MASKFULL 0x000000003FFFFFFFULL +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__PDS_SEQ_DEP_SHIFT 30U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__PDS_SEQ_DEP_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__PDS_SEQ_DEP_EN 0x40000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_PDS_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_PDS_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_PDS_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__USC_SEQ_DEP_SHIFT 29U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__USC_SEQ_DEP_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__USC_SEQ_DEP_EN 0x20000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_USC_SEQ_DEP_SHIFT 28U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_USC_SEQ_DEP_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_USC_SEQ_DEP_EN 0x10000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__TARGET_SHIFT 28U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__TARGET_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__TARGET_EN 0x10000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_TARGET_SHIFT 27U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_TARGET_CLRMSK 0xF7FFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_TARGET_EN 0x08000000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__UNIFIED_SIZE_SHIFT 22U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__UNIFIED_SIZE_CLRMSK 0xF03FFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_UNIFIED_SIZE_SHIFT 21U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_UNIFIED_SIZE_CLRMSK 0xF81FFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__COMMON_SHARED_SHIFT 21U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__COMMON_SHARED_CLRMSK 0xFFDFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__COMMON_SHARED_EN 0x00200000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_COMMON_SHARED_SHIFT 20U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_COMMON_SHARED_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_COMMON_SHARED_EN 0x00100000U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__COMMON_SIZE_SHIFT 12U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__COMMON_SIZE_CLRMSK 0xFFE00FFFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_COMMON_SIZE_SHIFT 11U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_COMMON_SIZE_CLRMSK 0xFFF007FFU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_TEMP_SIZE_CLRMSK 0xFFFFF87FU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__TEMP_SIZE_SHIFT 7U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1__TEMPSIZE8__TEMP_SIZE_CLRMSK 0xFFFFF07FU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_DATA_SIZE_SHIFT 1U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_DATA_SIZE_CLRMSK 0xFFFFFF81U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_FENCE_SHIFT 0U +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_FENCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_CDM_CONTEXT_LOAD_PDS1_FENCE_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_CONFIG */ +#define ROGUE_CR_MIPS_WRAPPER_CONFIG 0x0810U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_MASKFULL 0x000001030F01FFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_FW_IDLE_ENABLE_SHIFT 40U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_FW_IDLE_ENABLE_CLRMSK 0xFFFFFEFFFFFFFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_FW_IDLE_ENABLE_EN 0x0000010000000000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_DISABLE_BOOT_SHIFT 33U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_DISABLE_BOOT_CLRMSK 0xFFFFFFFDFFFFFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_DISABLE_BOOT_EN 0x0000000200000000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_L2_CACHE_OFF_SHIFT 32U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_L2_CACHE_OFF_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_L2_CACHE_OFF_EN 0x0000000100000000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_OS_ID_SHIFT 25U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_OS_ID_CLRMSK 0xFFFFFFFFF1FFFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_TRUSTED_SHIFT 24U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_TRUSTED_CLRMSK 0xFFFFFFFFFEFFFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_TRUSTED_EN 0x0000000001000000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_SHIFT 16U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_CLRMSK 0xFFFFFFFFFFFEFFFFULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_MIPS32 0x0000000000000000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_BOOT_ISA_MODE_MICROMIPS 0x0000000000010000ULL +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_REGBANK_BASE_ADDR_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_CONFIG_REGBANK_BASE_ADDR_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1 */ +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1 0x0818U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MASKFULL 0x00000000FFFFF001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG1_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2 */ +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2 0x0820U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_MASKFULL 0x000000FFFFFFF1FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_ADDR_OUT_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_ADDR_OUT_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_OS_ID_SHIFT 6U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_OS_ID_CLRMSK 0xFFFFFFFFFFFFFE3FULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_TRUSTED_SHIFT 5U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_TRUSTED_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_TRUSTED_EN 0x0000000000000020ULL +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_REGION_SIZE_POW2_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP1_CONFIG2_REGION_SIZE_POW2_CLRMSK 0xFFFFFFFFFFFFFFE0ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1 */ +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1 0x0828U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MASKFULL 0x00000000FFFFF001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG1_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2 */ +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2 0x0830U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_MASKFULL 0x000000FFFFFFF1FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_ADDR_OUT_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_ADDR_OUT_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_OS_ID_SHIFT 6U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_OS_ID_CLRMSK 0xFFFFFFFFFFFFFE3FULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_TRUSTED_SHIFT 5U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_TRUSTED_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_TRUSTED_EN 0x0000000000000020ULL +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_REGION_SIZE_POW2_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP2_CONFIG2_REGION_SIZE_POW2_CLRMSK 0xFFFFFFFFFFFFFFE0ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1 */ +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1 0x0838U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MASKFULL 0x00000000FFFFF001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG1_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2 */ +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2 0x0840U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_MASKFULL 0x000000FFFFFFF1FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_ADDR_OUT_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_ADDR_OUT_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_OS_ID_SHIFT 6U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_OS_ID_CLRMSK 0xFFFFFFFFFFFFFE3FULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_TRUSTED_SHIFT 5U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_TRUSTED_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_TRUSTED_EN 0x0000000000000020ULL +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_REGION_SIZE_POW2_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP3_CONFIG2_REGION_SIZE_POW2_CLRMSK 0xFFFFFFFFFFFFFFE0ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1 */ +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1 0x0848U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_MASKFULL 0x00000000FFFFF001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG1_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2 */ +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2 0x0850U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_MASKFULL 0x000000FFFFFFF1FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_ADDR_OUT_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_ADDR_OUT_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_OS_ID_SHIFT 6U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_OS_ID_CLRMSK 0xFFFFFFFFFFFFFE3FULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_TRUSTED_SHIFT 5U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_TRUSTED_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_TRUSTED_EN 0x0000000000000020ULL +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_REGION_SIZE_POW2_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP4_CONFIG2_REGION_SIZE_POW2_CLRMSK 0xFFFFFFFFFFFFFFE0ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1 */ +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1 0x0858U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MASKFULL 0x00000000FFFFF001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG1_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2 */ +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2 0x0860U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_MASKFULL 0x000000FFFFFFF1FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_ADDR_OUT_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_ADDR_OUT_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_OS_ID_SHIFT 6U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_OS_ID_CLRMSK 0xFFFFFFFFFFFFFE3FULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_TRUSTED_SHIFT 5U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_TRUSTED_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_TRUSTED_EN 0x0000000000000020ULL +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_REGION_SIZE_POW2_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP5_CONFIG2_REGION_SIZE_POW2_CLRMSK 0xFFFFFFFFFFFFFFE0ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS */ +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS 0x0868U +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_MASKFULL 0x00000001FFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_EVENT_SHIFT 32U +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_EVENT_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_EVENT_EN 0x0000000100000000ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_ADDRESS_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_STATUS_ADDRESS_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR */ +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR 0x0870U +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR_EVENT_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR_EVENT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_ADDR_REMAP_UNMAPPED_CLEAR_EVENT_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG */ +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG 0x0878U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_MASKFULL 0xFFFFFFF7FFFFFFBFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_ADDR_OUT_SHIFT 36U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_ADDR_OUT_CLRMSK 0x0000000FFFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_OS_ID_SHIFT 32U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_OS_ID_CLRMSK 0xFFFFFFF8FFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_TRUSTED_SHIFT 11U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_TRUSTED_CLRMSK 0xFFFFFFFFFFFFF7FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_TRUSTED_EN 0x0000000000000800ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_SHIFT 7U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_CLRMSK 0xFFFFFFFFFFFFF87FULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_4KB 0x0000000000000000ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_16KB 0x0000000000000080ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_64KB 0x0000000000000100ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_256KB 0x0000000000000180ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_1MB 0x0000000000000200ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_4MB 0x0000000000000280ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_16MB 0x0000000000000300ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_64MB 0x0000000000000380ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_REGION_SIZE_256MB 0x0000000000000400ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_ENTRY_SHIFT 1U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_ENTRY_CLRMSK 0xFFFFFFFFFFFFFFC1ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_CONFIG_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ */ +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ 0x0880U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_MASKFULL 0x000000000000003FULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_ENTRY_SHIFT 1U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_ENTRY_CLRMSK 0xFFFFFFC1U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_REQUEST_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_REQUEST_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_READ_REQUEST_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA */ +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA 0x0888U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_MASKFULL 0xFFFFFFF7FFFFFF81ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_ADDR_OUT_SHIFT 36U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_ADDR_OUT_CLRMSK 0x0000000FFFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_OS_ID_SHIFT 32U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_OS_ID_CLRMSK 0xFFFFFFF8FFFFFFFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_BASE_ADDR_IN_SHIFT 12U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_BASE_ADDR_IN_CLRMSK 0xFFFFFFFF00000FFFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_TRUSTED_SHIFT 11U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_TRUSTED_CLRMSK 0xFFFFFFFFFFFFF7FFULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_TRUSTED_EN 0x0000000000000800ULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_REGION_SIZE_SHIFT 7U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_REGION_SIZE_CLRMSK 0xFFFFFFFFFFFFF87FULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_MODE_ENABLE_SHIFT 0U +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_MODE_ENABLE_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MIPS_ADDR_REMAP_RANGE_DATA_MODE_ENABLE_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE */ +#define ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE 0x08A0U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE_EVENT_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE_EVENT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_WRAPPER_IRQ_ENABLE_EVENT_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS */ +#define ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS 0x08A8U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_EVENT_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_EVENT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_WRAPPER_IRQ_STATUS_EVENT_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR */ +#define ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR 0x08B0U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_EVENT_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_EVENT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_WRAPPER_IRQ_CLEAR_EVENT_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE */ +#define ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE 0x08B8U +#define ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE_EVENT_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE_EVENT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_WRAPPER_NMI_ENABLE_EVENT_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_NMI_EVENT */ +#define ROGUE_CR_MIPS_WRAPPER_NMI_EVENT 0x08C0U +#define ROGUE_CR_MIPS_WRAPPER_NMI_EVENT_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_WRAPPER_NMI_EVENT_TRIGGER_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_NMI_EVENT_TRIGGER_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_WRAPPER_NMI_EVENT_TRIGGER_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_DEBUG_CONFIG */ +#define ROGUE_CR_MIPS_DEBUG_CONFIG 0x08C8U +#define ROGUE_CR_MIPS_DEBUG_CONFIG_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_MIPS_DEBUG_CONFIG_DISABLE_PROBE_DEBUG_SHIFT 0U +#define ROGUE_CR_MIPS_DEBUG_CONFIG_DISABLE_PROBE_DEBUG_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_DEBUG_CONFIG_DISABLE_PROBE_DEBUG_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_EXCEPTION_STATUS */ +#define ROGUE_CR_MIPS_EXCEPTION_STATUS 0x08D0U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_MASKFULL 0x000000000000003FULL +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_SLEEP_SHIFT 5U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_SLEEP_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_SLEEP_EN 0x00000020U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NMI_TAKEN_SHIFT 4U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NMI_TAKEN_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NMI_TAKEN_EN 0x00000010U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_EXL_SHIFT 3U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_EXL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_EXL_EN 0x00000008U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_ERL_SHIFT 2U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_ERL_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_NEST_ERL_EN 0x00000004U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_EXL_SHIFT 1U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_EXL_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_EXL_EN 0x00000002U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_ERL_SHIFT 0U +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_ERL_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MIPS_EXCEPTION_STATUS_SI_ERL_EN 0x00000001U + +/* Register ROGUE_CR_MIPS_WRAPPER_STATUS */ +#define ROGUE_CR_MIPS_WRAPPER_STATUS 0x08E8U +#define ROGUE_CR_MIPS_WRAPPER_STATUS_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_MIPS_WRAPPER_STATUS_OUTSTANDING_REQUESTS_SHIFT 0U +#define ROGUE_CR_MIPS_WRAPPER_STATUS_OUTSTANDING_REQUESTS_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_XPU_BROADCAST */ +#define ROGUE_CR_XPU_BROADCAST 0x0890U +#define ROGUE_CR_XPU_BROADCAST_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_XPU_BROADCAST_MASK_SHIFT 0U +#define ROGUE_CR_XPU_BROADCAST_MASK_CLRMSK 0xFFFFFE00U + +/* Register ROGUE_CR_META_SP_MSLVDATAX */ +#define ROGUE_CR_META_SP_MSLVDATAX 0x0A00U +#define ROGUE_CR_META_SP_MSLVDATAX_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_META_SP_MSLVDATAX_MSLVDATAX_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVDATAX_MSLVDATAX_CLRMSK 0x00000000U + +/* Register ROGUE_CR_META_SP_MSLVDATAT */ +#define ROGUE_CR_META_SP_MSLVDATAT 0x0A08U +#define ROGUE_CR_META_SP_MSLVDATAT_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_META_SP_MSLVDATAT_MSLVDATAT_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVDATAT_MSLVDATAT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_META_SP_MSLVCTRL0 */ +#define ROGUE_CR_META_SP_MSLVCTRL0 0x0A10U +#define ROGUE_CR_META_SP_MSLVCTRL0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_META_SP_MSLVCTRL0_ADDR_SHIFT 2U +#define ROGUE_CR_META_SP_MSLVCTRL0_ADDR_CLRMSK 0x00000003U +#define ROGUE_CR_META_SP_MSLVCTRL0_AUTOINCR_SHIFT 1U +#define ROGUE_CR_META_SP_MSLVCTRL0_AUTOINCR_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_META_SP_MSLVCTRL0_AUTOINCR_EN 0x00000002U +#define ROGUE_CR_META_SP_MSLVCTRL0_RD_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVCTRL0_RD_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_META_SP_MSLVCTRL0_RD_EN 0x00000001U + +/* Register ROGUE_CR_META_SP_MSLVCTRL1 */ +#define ROGUE_CR_META_SP_MSLVCTRL1 0x0A18U +#define ROGUE_CR_META_SP_MSLVCTRL1_MASKFULL 0x00000000F7F4003FULL +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERRTHREAD_SHIFT 30U +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERRTHREAD_CLRMSK 0x3FFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_LOCK2_INTERLOCK_SHIFT 29U +#define ROGUE_CR_META_SP_MSLVCTRL1_LOCK2_INTERLOCK_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_LOCK2_INTERLOCK_EN 0x20000000U +#define ROGUE_CR_META_SP_MSLVCTRL1_ATOMIC_INTERLOCK_SHIFT 28U +#define ROGUE_CR_META_SP_MSLVCTRL1_ATOMIC_INTERLOCK_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_ATOMIC_INTERLOCK_EN 0x10000000U +#define ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_SHIFT 26U +#define ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_CLRMSK 0xFBFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN 0x04000000U +#define ROGUE_CR_META_SP_MSLVCTRL1_COREMEM_IDLE_SHIFT 25U +#define ROGUE_CR_META_SP_MSLVCTRL1_COREMEM_IDLE_CLRMSK 0xFDFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_COREMEM_IDLE_EN 0x02000000U +#define ROGUE_CR_META_SP_MSLVCTRL1_READY_SHIFT 24U +#define ROGUE_CR_META_SP_MSLVCTRL1_READY_CLRMSK 0xFEFFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_READY_EN 0x01000000U +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERRID_SHIFT 21U +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERRID_CLRMSK 0xFF1FFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERR_SHIFT 20U +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERR_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_DEFERR_EN 0x00100000U +#define ROGUE_CR_META_SP_MSLVCTRL1_WR_ACTIVE_SHIFT 18U +#define ROGUE_CR_META_SP_MSLVCTRL1_WR_ACTIVE_CLRMSK 0xFFFBFFFFU +#define ROGUE_CR_META_SP_MSLVCTRL1_WR_ACTIVE_EN 0x00040000U +#define ROGUE_CR_META_SP_MSLVCTRL1_THREAD_SHIFT 4U +#define ROGUE_CR_META_SP_MSLVCTRL1_THREAD_CLRMSK 0xFFFFFFCFU +#define ROGUE_CR_META_SP_MSLVCTRL1_TRANS_SIZE_SHIFT 2U +#define ROGUE_CR_META_SP_MSLVCTRL1_TRANS_SIZE_CLRMSK 0xFFFFFFF3U +#define ROGUE_CR_META_SP_MSLVCTRL1_BYTE_ROUND_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVCTRL1_BYTE_ROUND_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_META_SP_MSLVHANDSHKE */ +#define ROGUE_CR_META_SP_MSLVHANDSHKE 0x0A50U +#define ROGUE_CR_META_SP_MSLVHANDSHKE_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_META_SP_MSLVHANDSHKE_INPUT_SHIFT 2U +#define ROGUE_CR_META_SP_MSLVHANDSHKE_INPUT_CLRMSK 0xFFFFFFF3U +#define ROGUE_CR_META_SP_MSLVHANDSHKE_OUTPUT_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVHANDSHKE_OUTPUT_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_META_SP_MSLVT0KICK */ +#define ROGUE_CR_META_SP_MSLVT0KICK 0x0A80U +#define ROGUE_CR_META_SP_MSLVT0KICK_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT0KICK_MSLVT0KICK_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT0KICK_MSLVT0KICK_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT0KICKI */ +#define ROGUE_CR_META_SP_MSLVT0KICKI 0x0A88U +#define ROGUE_CR_META_SP_MSLVT0KICKI_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT0KICKI_MSLVT0KICKI_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT0KICKI_MSLVT0KICKI_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT1KICK */ +#define ROGUE_CR_META_SP_MSLVT1KICK 0x0A90U +#define ROGUE_CR_META_SP_MSLVT1KICK_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT1KICK_MSLVT1KICK_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT1KICK_MSLVT1KICK_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT1KICKI */ +#define ROGUE_CR_META_SP_MSLVT1KICKI 0x0A98U +#define ROGUE_CR_META_SP_MSLVT1KICKI_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT1KICKI_MSLVT1KICKI_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT1KICKI_MSLVT1KICKI_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT2KICK */ +#define ROGUE_CR_META_SP_MSLVT2KICK 0x0AA0U +#define ROGUE_CR_META_SP_MSLVT2KICK_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT2KICK_MSLVT2KICK_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT2KICK_MSLVT2KICK_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT2KICKI */ +#define ROGUE_CR_META_SP_MSLVT2KICKI 0x0AA8U +#define ROGUE_CR_META_SP_MSLVT2KICKI_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT2KICKI_MSLVT2KICKI_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT2KICKI_MSLVT2KICKI_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT3KICK */ +#define ROGUE_CR_META_SP_MSLVT3KICK 0x0AB0U +#define ROGUE_CR_META_SP_MSLVT3KICK_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT3KICK_MSLVT3KICK_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT3KICK_MSLVT3KICK_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVT3KICKI */ +#define ROGUE_CR_META_SP_MSLVT3KICKI 0x0AB8U +#define ROGUE_CR_META_SP_MSLVT3KICKI_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_META_SP_MSLVT3KICKI_MSLVT3KICKI_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVT3KICKI_MSLVT3KICKI_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_META_SP_MSLVRST */ +#define ROGUE_CR_META_SP_MSLVRST 0x0AC0U +#define ROGUE_CR_META_SP_MSLVRST_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_META_SP_MSLVRST_SOFTRESET_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVRST_SOFTRESET_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_META_SP_MSLVRST_SOFTRESET_EN 0x00000001U + +/* Register ROGUE_CR_META_SP_MSLVIRQSTATUS */ +#define ROGUE_CR_META_SP_MSLVIRQSTATUS 0x0AC8U +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_MASKFULL 0x000000000000000CULL +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT3_SHIFT 3U +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT3_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT3_EN 0x00000008U +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_SHIFT 2U +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_META_SP_MSLVIRQSTATUS_TRIGVECT2_EN 0x00000004U + +/* Register ROGUE_CR_META_SP_MSLVIRQENABLE */ +#define ROGUE_CR_META_SP_MSLVIRQENABLE 0x0AD0U +#define ROGUE_CR_META_SP_MSLVIRQENABLE_MASKFULL 0x000000000000000CULL +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT1_SHIFT 3U +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT1_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT1_EN 0x00000008U +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT0_SHIFT 2U +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT0_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_META_SP_MSLVIRQENABLE_EVENT0_EN 0x00000004U + +/* Register ROGUE_CR_META_SP_MSLVIRQLEVEL */ +#define ROGUE_CR_META_SP_MSLVIRQLEVEL 0x0AD8U +#define ROGUE_CR_META_SP_MSLVIRQLEVEL_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_META_SP_MSLVIRQLEVEL_MODE_SHIFT 0U +#define ROGUE_CR_META_SP_MSLVIRQLEVEL_MODE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_META_SP_MSLVIRQLEVEL_MODE_EN 0x00000001U + +/* Register ROGUE_CR_MTS_SCHEDULE */ +#define ROGUE_CR_MTS_SCHEDULE 0x0B00U +#define ROGUE_CR_MTS_SCHEDULE_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE1 */ +#define ROGUE_CR_MTS_SCHEDULE1 0x10B00U +#define ROGUE_CR_MTS_SCHEDULE1_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE1_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE1_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE1_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE1_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE1_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE1_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE1_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE1_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE1_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE1_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE1_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE1_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE1_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE1_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE1_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE1_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE2 */ +#define ROGUE_CR_MTS_SCHEDULE2 0x20B00U +#define ROGUE_CR_MTS_SCHEDULE2_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE2_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE2_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE2_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE2_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE2_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE2_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE2_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE2_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE2_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE2_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE2_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE2_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE2_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE2_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE2_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE2_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE3 */ +#define ROGUE_CR_MTS_SCHEDULE3 0x30B00U +#define ROGUE_CR_MTS_SCHEDULE3_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE3_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE3_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE3_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE3_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE3_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE3_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE3_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE3_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE3_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE3_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE3_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE3_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE3_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE3_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE3_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE3_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE4 */ +#define ROGUE_CR_MTS_SCHEDULE4 0x40B00U +#define ROGUE_CR_MTS_SCHEDULE4_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE4_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE4_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE4_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE4_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE4_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE4_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE4_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE4_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE4_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE4_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE4_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE4_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE4_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE4_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE4_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE4_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE5 */ +#define ROGUE_CR_MTS_SCHEDULE5 0x50B00U +#define ROGUE_CR_MTS_SCHEDULE5_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE5_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE5_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE5_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE5_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE5_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE5_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE5_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE5_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE5_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE5_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE5_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE5_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE5_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE5_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE5_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE5_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE6 */ +#define ROGUE_CR_MTS_SCHEDULE6 0x60B00U +#define ROGUE_CR_MTS_SCHEDULE6_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE6_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE6_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE6_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE6_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE6_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE6_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE6_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE6_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE6_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE6_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE6_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE6_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE6_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE6_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE6_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE6_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_SCHEDULE7 */ +#define ROGUE_CR_MTS_SCHEDULE7 0x70B00U +#define ROGUE_CR_MTS_SCHEDULE7_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_MTS_SCHEDULE7_HOST_SHIFT 8U +#define ROGUE_CR_MTS_SCHEDULE7_HOST_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_MTS_SCHEDULE7_HOST_BG_TIMER 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE7_HOST_HOST 0x00000100U +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_SHIFT 6U +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_PRT0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_PRT1 0x00000040U +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_PRT2 0x00000080U +#define ROGUE_CR_MTS_SCHEDULE7_PRIORITY_PRT3 0x000000C0U +#define ROGUE_CR_MTS_SCHEDULE7_CONTEXT_SHIFT 5U +#define ROGUE_CR_MTS_SCHEDULE7_CONTEXT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SCHEDULE7_CONTEXT_BGCTX 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE7_CONTEXT_INTCTX 0x00000020U +#define ROGUE_CR_MTS_SCHEDULE7_TASK_SHIFT 4U +#define ROGUE_CR_MTS_SCHEDULE7_TASK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SCHEDULE7_TASK_NON_COUNTED 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE7_TASK_COUNTED 0x00000010U +#define ROGUE_CR_MTS_SCHEDULE7_DM_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE7_DM_CLRMSK 0xFFFFFFF0U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM0 0x00000000U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM1 0x00000001U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM2 0x00000002U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM3 0x00000003U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM4 0x00000004U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM5 0x00000005U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM6 0x00000006U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM7 0x00000007U +#define ROGUE_CR_MTS_SCHEDULE7_DM_DM_ALL 0x0000000FU + +/* Register ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC */ +#define ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC 0x0B30U +#define ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_SHIFT 0U +#define ROGUE_CR_MTS_BGCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC */ +#define ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC 0x0B38U +#define ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_SHIFT 0U +#define ROGUE_CR_MTS_BGCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC */ +#define ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC 0x0B40U +#define ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_SHIFT 0U +#define ROGUE_CR_MTS_INTCTX_THREAD0_DM_ASSOC_DM_ASSOC_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC */ +#define ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC 0x0B48U +#define ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_SHIFT 0U +#define ROGUE_CR_MTS_INTCTX_THREAD1_DM_ASSOC_DM_ASSOC_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG */ +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG 0x0B50U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG__S7_TOP__MASKFULL 0x000FF0FFFFFFF701ULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_MASKFULL 0x0000FFFFFFFFF001ULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_SHIFT 44U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PC_BASE_CLRMSK 0xFFFF0FFFFFFFFFFFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG__S7_TOP__FENCE_PC_BASE_SHIFT 44U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG__S7_TOP__FENCE_PC_BASE_CLRMSK 0xFFF00FFFFFFFFFFFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_DM_SHIFT 40U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_DM_CLRMSK 0xFFFFF0FFFFFFFFFFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_ADDR_SHIFT 12U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PERSISTENCE_SHIFT 9U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_PERSISTENCE_CLRMSK 0xFFFFFFFFFFFFF9FFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_SLC_COHERENT_SHIFT 8U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_SLC_COHERENT_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_FENCE_SLC_COHERENT_EN 0x0000000000000100ULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_SHIFT 0U +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_META 0x0000000000000000ULL +#define ROGUE_CR_MTS_GARTEN_WRAPPER_CONFIG_IDLE_CTRL_MTS 0x0000000000000001ULL + +/* Register ROGUE_CR_MTS_DM0_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM0_INTERRUPT_ENABLE 0x0B58U +#define ROGUE_CR_MTS_DM0_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM0_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM0_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_DM1_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM1_INTERRUPT_ENABLE 0x0B60U +#define ROGUE_CR_MTS_DM1_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM1_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM1_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_DM2_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM2_INTERRUPT_ENABLE 0x0B68U +#define ROGUE_CR_MTS_DM2_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM2_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM2_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_DM3_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM3_INTERRUPT_ENABLE 0x0B70U +#define ROGUE_CR_MTS_DM3_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM3_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM3_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_DM4_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM4_INTERRUPT_ENABLE 0x0B78U +#define ROGUE_CR_MTS_DM4_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM4_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM4_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_DM5_INTERRUPT_ENABLE */ +#define ROGUE_CR_MTS_DM5_INTERRUPT_ENABLE 0x0B80U +#define ROGUE_CR_MTS_DM5_INTERRUPT_ENABLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_DM5_INTERRUPT_ENABLE_INT_ENABLE_SHIFT 0U +#define ROGUE_CR_MTS_DM5_INTERRUPT_ENABLE_INT_ENABLE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_INTCTX */ +#define ROGUE_CR_MTS_INTCTX 0x0B98U +#define ROGUE_CR_MTS_INTCTX_MASKFULL 0x000000003FFFFFFFULL +#define ROGUE_CR_MTS_INTCTX_DM_HOST_SCHEDULE_SHIFT 22U +#define ROGUE_CR_MTS_INTCTX_DM_HOST_SCHEDULE_CLRMSK 0xC03FFFFFU +#define ROGUE_CR_MTS_INTCTX_DM_PTR_SHIFT 18U +#define ROGUE_CR_MTS_INTCTX_DM_PTR_CLRMSK 0xFFC3FFFFU +#define ROGUE_CR_MTS_INTCTX_THREAD_ACTIVE_SHIFT 16U +#define ROGUE_CR_MTS_INTCTX_THREAD_ACTIVE_CLRMSK 0xFFFCFFFFU +#define ROGUE_CR_MTS_INTCTX_DM_TIMER_SCHEDULE_SHIFT 8U +#define ROGUE_CR_MTS_INTCTX_DM_TIMER_SCHEDULE_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_MTS_INTCTX_DM_INTERRUPT_SCHEDULE_SHIFT 0U +#define ROGUE_CR_MTS_INTCTX_DM_INTERRUPT_SCHEDULE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_MTS_BGCTX */ +#define ROGUE_CR_MTS_BGCTX 0x0BA0U +#define ROGUE_CR_MTS_BGCTX_MASKFULL 0x0000000000003FFFULL +#define ROGUE_CR_MTS_BGCTX_DM_PTR_SHIFT 10U +#define ROGUE_CR_MTS_BGCTX_DM_PTR_CLRMSK 0xFFFFC3FFU +#define ROGUE_CR_MTS_BGCTX_THREAD_ACTIVE_SHIFT 8U +#define ROGUE_CR_MTS_BGCTX_THREAD_ACTIVE_CLRMSK 0xFFFFFCFFU +#define ROGUE_CR_MTS_BGCTX_DM_NONCOUNTED_SCHEDULE_SHIFT 0U +#define ROGUE_CR_MTS_BGCTX_DM_NONCOUNTED_SCHEDULE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE */ +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE 0x0BA8U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM7_SHIFT 56U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM7_CLRMSK 0x00FFFFFFFFFFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM6_SHIFT 48U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM6_CLRMSK 0xFF00FFFFFFFFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM5_SHIFT 40U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM5_CLRMSK 0xFFFF00FFFFFFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM4_SHIFT 32U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM4_CLRMSK 0xFFFFFF00FFFFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM3_SHIFT 24U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM3_CLRMSK 0xFFFFFFFF00FFFFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM2_SHIFT 16U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM2_CLRMSK 0xFFFFFFFFFF00FFFFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM1_SHIFT 8U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM1_CLRMSK 0xFFFFFFFFFFFF00FFULL +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM0_SHIFT 0U +#define ROGUE_CR_MTS_BGCTX_COUNTED_SCHEDULE_DM0_CLRMSK 0xFFFFFFFFFFFFFF00ULL + +/* Register ROGUE_CR_MTS_GPU_INT_STATUS */ +#define ROGUE_CR_MTS_GPU_INT_STATUS 0x0BB0U +#define ROGUE_CR_MTS_GPU_INT_STATUS_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MTS_GPU_INT_STATUS_STATUS_SHIFT 0U +#define ROGUE_CR_MTS_GPU_INT_STATUS_STATUS_CLRMSK 0x00000000U + +/* Register ROGUE_CR_MTS_SCHEDULE_ENABLE */ +#define ROGUE_CR_MTS_SCHEDULE_ENABLE 0x0BC8U +#define ROGUE_CR_MTS_SCHEDULE_ENABLE_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_MTS_SCHEDULE_ENABLE_MASK_SHIFT 0U +#define ROGUE_CR_MTS_SCHEDULE_ENABLE_MASK_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_IRQ_OS0_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS0_EVENT_STATUS 0x0BD8U +#define ROGUE_CR_IRQ_OS0_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS0_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS0_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS0_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS0_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS0_EVENT_CLEAR 0x0BE8U +#define ROGUE_CR_IRQ_OS0_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS0_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS0_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS0_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS1_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS1_EVENT_STATUS 0x10BD8U +#define ROGUE_CR_IRQ_OS1_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS1_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS1_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS1_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS1_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS1_EVENT_CLEAR 0x10BE8U +#define ROGUE_CR_IRQ_OS1_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS1_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS1_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS1_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS2_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS2_EVENT_STATUS 0x20BD8U +#define ROGUE_CR_IRQ_OS2_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS2_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS2_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS2_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS2_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS2_EVENT_CLEAR 0x20BE8U +#define ROGUE_CR_IRQ_OS2_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS2_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS2_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS2_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS3_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS3_EVENT_STATUS 0x30BD8U +#define ROGUE_CR_IRQ_OS3_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS3_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS3_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS3_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS3_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS3_EVENT_CLEAR 0x30BE8U +#define ROGUE_CR_IRQ_OS3_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS3_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS3_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS3_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS4_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS4_EVENT_STATUS 0x40BD8U +#define ROGUE_CR_IRQ_OS4_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS4_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS4_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS4_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS4_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS4_EVENT_CLEAR 0x40BE8U +#define ROGUE_CR_IRQ_OS4_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS4_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS4_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS4_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS5_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS5_EVENT_STATUS 0x50BD8U +#define ROGUE_CR_IRQ_OS5_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS5_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS5_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS5_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS5_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS5_EVENT_CLEAR 0x50BE8U +#define ROGUE_CR_IRQ_OS5_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS5_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS5_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS5_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS6_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS6_EVENT_STATUS 0x60BD8U +#define ROGUE_CR_IRQ_OS6_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS6_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS6_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS6_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS6_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS6_EVENT_CLEAR 0x60BE8U +#define ROGUE_CR_IRQ_OS6_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS6_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS6_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS6_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS7_EVENT_STATUS */ +#define ROGUE_CR_IRQ_OS7_EVENT_STATUS 0x70BD8U +#define ROGUE_CR_IRQ_OS7_EVENT_STATUS_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS7_EVENT_STATUS_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS7_EVENT_STATUS_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS7_EVENT_STATUS_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_IRQ_OS7_EVENT_CLEAR */ +#define ROGUE_CR_IRQ_OS7_EVENT_CLEAR 0x70BE8U +#define ROGUE_CR_IRQ_OS7_EVENT_CLEAR_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_IRQ_OS7_EVENT_CLEAR_SOURCE_SHIFT 0U +#define ROGUE_CR_IRQ_OS7_EVENT_CLEAR_SOURCE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_IRQ_OS7_EVENT_CLEAR_SOURCE_EN 0x00000001U + +/* Register ROGUE_CR_META_BOOT */ +#define ROGUE_CR_META_BOOT 0x0BF8U +#define ROGUE_CR_META_BOOT_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_META_BOOT_MODE_SHIFT 0U +#define ROGUE_CR_META_BOOT_MODE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_META_BOOT_MODE_EN 0x00000001U + +/* Register ROGUE_CR_GARTEN_SLC */ +#define ROGUE_CR_GARTEN_SLC 0x0BB8U +#define ROGUE_CR_GARTEN_SLC_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_GARTEN_SLC_FORCE_COHERENCY_SHIFT 0U +#define ROGUE_CR_GARTEN_SLC_FORCE_COHERENCY_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_GARTEN_SLC_FORCE_COHERENCY_EN 0x00000001U + +/* Register ROGUE_CR_PPP */ +#define ROGUE_CR_PPP 0x0CD0U +#define ROGUE_CR_PPP_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PPP_CHECKSUM_SHIFT 0U +#define ROGUE_CR_PPP_CHECKSUM_CLRMSK 0x00000000U + +#define ROGUE_CR_ISP_RENDER_DIR_TYPE_MASK 0x00000003U +/* Top-left to bottom-right */ +#define ROGUE_CR_ISP_RENDER_DIR_TYPE_TL2BR 0x00000000U +/* Top-right to bottom-left */ +#define ROGUE_CR_ISP_RENDER_DIR_TYPE_TR2BL 0x00000001U +/* Bottom-left to top-right */ +#define ROGUE_CR_ISP_RENDER_DIR_TYPE_BL2TR 0x00000002U +/* Bottom-right to top-left */ +#define ROGUE_CR_ISP_RENDER_DIR_TYPE_BR2TL 0x00000003U + +#define ROGUE_CR_ISP_RENDER_MODE_TYPE_MASK 0x00000003U +/* Normal render */ +#define ROGUE_CR_ISP_RENDER_MODE_TYPE_NORM 0x00000000U +/* Fast 2D render */ +#define ROGUE_CR_ISP_RENDER_MODE_TYPE_FAST_2D 0x00000002U +/* Fast scale render */ +#define ROGUE_CR_ISP_RENDER_MODE_TYPE_FAST_SCALE 0x00000003U + +/* Register ROGUE_CR_ISP_RENDER */ +#define ROGUE_CR_ISP_RENDER 0x0F08U +#define ROGUE_CR_ISP_RENDER_MASKFULL 0x00000000000001FFULL +#define ROGUE_CR_ISP_RENDER_FAST_RENDER_FORCE_PROTECT_SHIFT 8U +#define ROGUE_CR_ISP_RENDER_FAST_RENDER_FORCE_PROTECT_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_ISP_RENDER_FAST_RENDER_FORCE_PROTECT_EN 0x00000100U +#define ROGUE_CR_ISP_RENDER_PROCESS_PROTECTED_TILES_SHIFT 7U +#define ROGUE_CR_ISP_RENDER_PROCESS_PROTECTED_TILES_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_ISP_RENDER_PROCESS_PROTECTED_TILES_EN 0x00000080U +#define ROGUE_CR_ISP_RENDER_PROCESS_UNPROTECTED_TILES_SHIFT 6U +#define ROGUE_CR_ISP_RENDER_PROCESS_UNPROTECTED_TILES_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_ISP_RENDER_PROCESS_UNPROTECTED_TILES_EN 0x00000040U +#define ROGUE_CR_ISP_RENDER_DISABLE_EOMT_SHIFT 5U +#define ROGUE_CR_ISP_RENDER_DISABLE_EOMT_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_ISP_RENDER_DISABLE_EOMT_EN 0x00000020U +#define ROGUE_CR_ISP_RENDER_RESUME_SHIFT 4U +#define ROGUE_CR_ISP_RENDER_RESUME_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_ISP_RENDER_RESUME_EN 0x00000010U +#define ROGUE_CR_ISP_RENDER_DIR_SHIFT 2U +#define ROGUE_CR_ISP_RENDER_DIR_CLRMSK 0xFFFFFFF3U +#define ROGUE_CR_ISP_RENDER_DIR_TL2BR 0x00000000U +#define ROGUE_CR_ISP_RENDER_DIR_TR2BL 0x00000004U +#define ROGUE_CR_ISP_RENDER_DIR_BL2TR 0x00000008U +#define ROGUE_CR_ISP_RENDER_DIR_BR2TL 0x0000000CU +#define ROGUE_CR_ISP_RENDER_MODE_SHIFT 0U +#define ROGUE_CR_ISP_RENDER_MODE_CLRMSK 0xFFFFFFFCU +#define ROGUE_CR_ISP_RENDER_MODE_NORM 0x00000000U +#define ROGUE_CR_ISP_RENDER_MODE_FAST_2D 0x00000002U +#define ROGUE_CR_ISP_RENDER_MODE_FAST_SCALE 0x00000003U + +/* Register ROGUE_CR_ISP_CTL */ +#define ROGUE_CR_ISP_CTL 0x0F38U +#define ROGUE_CR_ISP_CTL_MASKFULL 0x00000000FFFFF3FFULL +#define ROGUE_CR_ISP_CTL_SKIP_INIT_HDRS_SHIFT 31U +#define ROGUE_CR_ISP_CTL_SKIP_INIT_HDRS_CLRMSK 0x7FFFFFFFU +#define ROGUE_CR_ISP_CTL_SKIP_INIT_HDRS_EN 0x80000000U +#define ROGUE_CR_ISP_CTL_LINE_STYLE_SHIFT 30U +#define ROGUE_CR_ISP_CTL_LINE_STYLE_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_ISP_CTL_LINE_STYLE_EN 0x40000000U +#define ROGUE_CR_ISP_CTL_LINE_STYLE_PIX_SHIFT 29U +#define ROGUE_CR_ISP_CTL_LINE_STYLE_PIX_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_ISP_CTL_LINE_STYLE_PIX_EN 0x20000000U +#define ROGUE_CR_ISP_CTL_PAIR_TILES_VERT_SHIFT 28U +#define ROGUE_CR_ISP_CTL_PAIR_TILES_VERT_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_ISP_CTL_PAIR_TILES_VERT_EN 0x10000000U +#define ROGUE_CR_ISP_CTL_PAIR_TILES_SHIFT 27U +#define ROGUE_CR_ISP_CTL_PAIR_TILES_CLRMSK 0xF7FFFFFFU +#define ROGUE_CR_ISP_CTL_PAIR_TILES_EN 0x08000000U +#define ROGUE_CR_ISP_CTL_CREQ_BUF_EN_SHIFT 26U +#define ROGUE_CR_ISP_CTL_CREQ_BUF_EN_CLRMSK 0xFBFFFFFFU +#define ROGUE_CR_ISP_CTL_CREQ_BUF_EN_EN 0x04000000U +#define ROGUE_CR_ISP_CTL_TILE_AGE_EN_SHIFT 25U +#define ROGUE_CR_ISP_CTL_TILE_AGE_EN_CLRMSK 0xFDFFFFFFU +#define ROGUE_CR_ISP_CTL_TILE_AGE_EN_EN 0x02000000U +#define ROGUE_CR_ISP_CTL_ISP_SAMPLE_POS_MODE_SHIFT 23U +#define ROGUE_CR_ISP_CTL_ISP_SAMPLE_POS_MODE_CLRMSK 0xFE7FFFFFU +#define ROGUE_CR_ISP_CTL_ISP_SAMPLE_POS_MODE_DX9 0x00000000U +#define ROGUE_CR_ISP_CTL_ISP_SAMPLE_POS_MODE_DX10 0x00800000U +#define ROGUE_CR_ISP_CTL_ISP_SAMPLE_POS_MODE_OGL 0x01000000U +#define ROGUE_CR_ISP_CTL_NUM_TILES_PER_USC_SHIFT 21U +#define ROGUE_CR_ISP_CTL_NUM_TILES_PER_USC_CLRMSK 0xFF9FFFFFU +#define ROGUE_CR_ISP_CTL_DBIAS_IS_INT_SHIFT 20U +#define ROGUE_CR_ISP_CTL_DBIAS_IS_INT_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_ISP_CTL_DBIAS_IS_INT_EN 0x00100000U +#define ROGUE_CR_ISP_CTL_OVERLAP_CHECK_MODE_SHIFT 19U +#define ROGUE_CR_ISP_CTL_OVERLAP_CHECK_MODE_CLRMSK 0xFFF7FFFFU +#define ROGUE_CR_ISP_CTL_OVERLAP_CHECK_MODE_EN 0x00080000U +#define ROGUE_CR_ISP_CTL_PT_UPFRONT_DEPTH_DISABLE_SHIFT 18U +#define ROGUE_CR_ISP_CTL_PT_UPFRONT_DEPTH_DISABLE_CLRMSK 0xFFFBFFFFU +#define ROGUE_CR_ISP_CTL_PT_UPFRONT_DEPTH_DISABLE_EN 0x00040000U +#define ROGUE_CR_ISP_CTL_PROCESS_EMPTY_TILES_SHIFT 17U +#define ROGUE_CR_ISP_CTL_PROCESS_EMPTY_TILES_CLRMSK 0xFFFDFFFFU +#define ROGUE_CR_ISP_CTL_PROCESS_EMPTY_TILES_EN 0x00020000U +#define ROGUE_CR_ISP_CTL_SAMPLE_POS_SHIFT 16U +#define ROGUE_CR_ISP_CTL_SAMPLE_POS_CLRMSK 0xFFFEFFFFU +#define ROGUE_CR_ISP_CTL_SAMPLE_POS_EN 0x00010000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_SHIFT 12U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_ONE 0x00000000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_TWO 0x00001000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_THREE 0x00002000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_FOUR 0x00003000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_FIVE 0x00004000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_SIX 0x00005000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_SEVEN 0x00006000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_EIGHT 0x00007000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_NINE 0x00008000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_TEN 0x00009000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_ELEVEN 0x0000A000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_TWELVE 0x0000B000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_THIRTEEN 0x0000C000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_FOURTEEN 0x0000D000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_FIFTEEN 0x0000E000U +#define ROGUE_CR_ISP_CTL_PIPE_ENABLE_PIPE_SIXTEEN 0x0000F000U +#define ROGUE_CR_ISP_CTL_VALID_ID_SHIFT 4U +#define ROGUE_CR_ISP_CTL_VALID_ID_CLRMSK 0xFFFFFC0FU +#define ROGUE_CR_ISP_CTL_UPASS_START_SHIFT 0U +#define ROGUE_CR_ISP_CTL_UPASS_START_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_ISP_STATUS */ +#define ROGUE_CR_ISP_STATUS 0x1038U +#define ROGUE_CR_ISP_STATUS_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_ISP_STATUS_SPLIT_MAX_SHIFT 2U +#define ROGUE_CR_ISP_STATUS_SPLIT_MAX_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_ISP_STATUS_SPLIT_MAX_EN 0x00000004U +#define ROGUE_CR_ISP_STATUS_ACTIVE_SHIFT 1U +#define ROGUE_CR_ISP_STATUS_ACTIVE_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_ISP_STATUS_ACTIVE_EN 0x00000002U +#define ROGUE_CR_ISP_STATUS_EOR_SHIFT 0U +#define ROGUE_CR_ISP_STATUS_EOR_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_ISP_STATUS_EOR_EN 0x00000001U + +/* Register group: ROGUE_CR_ISP_XTP_RESUME, with 64 repeats */ +#define ROGUE_CR_ISP_XTP_RESUME_REPEATCOUNT 64U +/* Register ROGUE_CR_ISP_XTP_RESUME0 */ +#define ROGUE_CR_ISP_XTP_RESUME0 0x3A00U +#define ROGUE_CR_ISP_XTP_RESUME0_MASKFULL 0x00000000003FF3FFULL +#define ROGUE_CR_ISP_XTP_RESUME0_TILE_X_SHIFT 12U +#define ROGUE_CR_ISP_XTP_RESUME0_TILE_X_CLRMSK 0xFFC00FFFU +#define ROGUE_CR_ISP_XTP_RESUME0_TILE_Y_SHIFT 0U +#define ROGUE_CR_ISP_XTP_RESUME0_TILE_Y_CLRMSK 0xFFFFFC00U + +/* Register group: ROGUE_CR_ISP_XTP_STORE, with 32 repeats */ +#define ROGUE_CR_ISP_XTP_STORE_REPEATCOUNT 32U +/* Register ROGUE_CR_ISP_XTP_STORE0 */ +#define ROGUE_CR_ISP_XTP_STORE0 0x3C00U +#define ROGUE_CR_ISP_XTP_STORE0_MASKFULL 0x000000007F3FF3FFULL +#define ROGUE_CR_ISP_XTP_STORE0_ACTIVE_SHIFT 30U +#define ROGUE_CR_ISP_XTP_STORE0_ACTIVE_CLRMSK 0xBFFFFFFFU +#define ROGUE_CR_ISP_XTP_STORE0_ACTIVE_EN 0x40000000U +#define ROGUE_CR_ISP_XTP_STORE0_EOR_SHIFT 29U +#define ROGUE_CR_ISP_XTP_STORE0_EOR_CLRMSK 0xDFFFFFFFU +#define ROGUE_CR_ISP_XTP_STORE0_EOR_EN 0x20000000U +#define ROGUE_CR_ISP_XTP_STORE0_TILE_LAST_SHIFT 28U +#define ROGUE_CR_ISP_XTP_STORE0_TILE_LAST_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_ISP_XTP_STORE0_TILE_LAST_EN 0x10000000U +#define ROGUE_CR_ISP_XTP_STORE0_MT_SHIFT 24U +#define ROGUE_CR_ISP_XTP_STORE0_MT_CLRMSK 0xF0FFFFFFU +#define ROGUE_CR_ISP_XTP_STORE0_TILE_X_SHIFT 12U +#define ROGUE_CR_ISP_XTP_STORE0_TILE_X_CLRMSK 0xFFC00FFFU +#define ROGUE_CR_ISP_XTP_STORE0_TILE_Y_SHIFT 0U +#define ROGUE_CR_ISP_XTP_STORE0_TILE_Y_CLRMSK 0xFFFFFC00U + +/* Register group: ROGUE_CR_BIF_CAT_BASE, with 8 repeats */ +#define ROGUE_CR_BIF_CAT_BASE_REPEATCOUNT 8U +/* Register ROGUE_CR_BIF_CAT_BASE0 */ +#define ROGUE_CR_BIF_CAT_BASE0 0x1200U +#define ROGUE_CR_BIF_CAT_BASE0_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE0_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE0_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE0_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE1 */ +#define ROGUE_CR_BIF_CAT_BASE1 0x1208U +#define ROGUE_CR_BIF_CAT_BASE1_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE1_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE1_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE1_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE1_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE2 */ +#define ROGUE_CR_BIF_CAT_BASE2 0x1210U +#define ROGUE_CR_BIF_CAT_BASE2_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE2_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE2_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE2_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE2_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE3 */ +#define ROGUE_CR_BIF_CAT_BASE3 0x1218U +#define ROGUE_CR_BIF_CAT_BASE3_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE3_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE3_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE3_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE3_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE4 */ +#define ROGUE_CR_BIF_CAT_BASE4 0x1220U +#define ROGUE_CR_BIF_CAT_BASE4_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE4_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE4_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE4_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE4_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE5 */ +#define ROGUE_CR_BIF_CAT_BASE5 0x1228U +#define ROGUE_CR_BIF_CAT_BASE5_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE5_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE5_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE5_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE5_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE6 */ +#define ROGUE_CR_BIF_CAT_BASE6 0x1230U +#define ROGUE_CR_BIF_CAT_BASE6_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE6_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE6_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE6_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE6_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE7 */ +#define ROGUE_CR_BIF_CAT_BASE7 0x1238U +#define ROGUE_CR_BIF_CAT_BASE7_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_BIF_CAT_BASE7_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE7_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_CAT_BASE7_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_CAT_BASE7_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_CAT_BASE_INDEX */ +#define ROGUE_CR_BIF_CAT_BASE_INDEX 0x1240U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_MASKFULL 0x00070707073F0707ULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_RVTX_SHIFT 48U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_RVTX_CLRMSK 0xFFF8FFFFFFFFFFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_RAY_SHIFT 40U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_RAY_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_HOST_SHIFT 32U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_HOST_CLRMSK 0xFFFFFFF8FFFFFFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TLA_SHIFT 24U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TLA_CLRMSK 0xFFFFFFFFF8FFFFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TDM_SHIFT 19U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TDM_CLRMSK 0xFFFFFFFFFFC7FFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_CDM_SHIFT 16U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_CDM_CLRMSK 0xFFFFFFFFFFF8FFFFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_PIXEL_SHIFT 8U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_PIXEL_CLRMSK 0xFFFFFFFFFFFFF8FFULL +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TA_SHIFT 0U +#define ROGUE_CR_BIF_CAT_BASE_INDEX_TA_CLRMSK 0xFFFFFFFFFFFFFFF8ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_VCE0 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0 0x1248U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE0_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_TE0 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0 0x1250U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE0_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_ALIST0 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0 0x1260U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST0_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_VCE1 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1 0x1268U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_VCE1_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_TE1 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1 0x1270U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_TE1_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_PM_CAT_BASE_ALIST1 */ +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1 0x1280U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_MASKFULL 0x0FFFFFFFFFFFF003ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_INIT_PAGE_SHIFT 40U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_INIT_PAGE_CLRMSK 0xF00000FFFFFFFFFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_ADDR_SHIFT 12U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_WRAP_SHIFT 1U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_WRAP_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_WRAP_EN 0x0000000000000002ULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_VALID_SHIFT 0U +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_VALID_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_BIF_PM_CAT_BASE_ALIST1_VALID_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_BIF_MMU_ENTRY_STATUS */ +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS 0x1288U +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_MASKFULL 0x000000FFFFFFF0F3ULL +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_ADDRESS_SHIFT 12U +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_ADDRESS_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_CAT_BASE_SHIFT 4U +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_CAT_BASE_CLRMSK 0xFFFFFFFFFFFFFF0FULL +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_DATA_TYPE_SHIFT 0U +#define ROGUE_CR_BIF_MMU_ENTRY_STATUS_DATA_TYPE_CLRMSK 0xFFFFFFFFFFFFFFFCULL + +/* Register ROGUE_CR_BIF_MMU_ENTRY */ +#define ROGUE_CR_BIF_MMU_ENTRY 0x1290U +#define ROGUE_CR_BIF_MMU_ENTRY_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_BIF_MMU_ENTRY_ENABLE_SHIFT 1U +#define ROGUE_CR_BIF_MMU_ENTRY_ENABLE_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_MMU_ENTRY_ENABLE_EN 0x00000002U +#define ROGUE_CR_BIF_MMU_ENTRY_PENDING_SHIFT 0U +#define ROGUE_CR_BIF_MMU_ENTRY_PENDING_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_MMU_ENTRY_PENDING_EN 0x00000001U + +/* Register ROGUE_CR_BIF_CTRL_INVAL */ +#define ROGUE_CR_BIF_CTRL_INVAL 0x12A0U +#define ROGUE_CR_BIF_CTRL_INVAL_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_BIF_CTRL_INVAL_TLB1_SHIFT 3U +#define ROGUE_CR_BIF_CTRL_INVAL_TLB1_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_BIF_CTRL_INVAL_TLB1_EN 0x00000008U +#define ROGUE_CR_BIF_CTRL_INVAL_PC_SHIFT 2U +#define ROGUE_CR_BIF_CTRL_INVAL_PC_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_CTRL_INVAL_PC_EN 0x00000004U +#define ROGUE_CR_BIF_CTRL_INVAL_PD_SHIFT 1U +#define ROGUE_CR_BIF_CTRL_INVAL_PD_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_CTRL_INVAL_PD_EN 0x00000002U +#define ROGUE_CR_BIF_CTRL_INVAL_PT_SHIFT 0U +#define ROGUE_CR_BIF_CTRL_INVAL_PT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_CTRL_INVAL_PT_EN 0x00000001U + +/* Register ROGUE_CR_BIF_CTRL */ +#define ROGUE_CR_BIF_CTRL 0x12A8U +#define ROGUE_CR_BIF_CTRL__XE_MEM__MASKFULL 0x000000000000033FULL +#define ROGUE_CR_BIF_CTRL_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_CPU_SHIFT 9U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_CPU_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_CPU_EN 0x00000200U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF4_SHIFT 8U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF4_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF4_EN 0x00000100U +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_QUEUE_BYPASS_SHIFT 7U +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_QUEUE_BYPASS_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_QUEUE_BYPASS_EN 0x00000080U +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_AUTO_PREFETCH_SHIFT 6U +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_AUTO_PREFETCH_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_BIF_CTRL_ENABLE_MMU_AUTO_PREFETCH_EN 0x00000040U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF3_SHIFT 5U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF3_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF3_EN 0x00000020U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF2_SHIFT 4U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF2_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF2_EN 0x00000010U +#define ROGUE_CR_BIF_CTRL_PAUSE_BIF1_SHIFT 3U +#define ROGUE_CR_BIF_CTRL_PAUSE_BIF1_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_BIF_CTRL_PAUSE_BIF1_EN 0x00000008U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_PM_SHIFT 2U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_PM_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_PM_EN 0x00000004U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF1_SHIFT 1U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF1_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF1_EN 0x00000002U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF0_SHIFT 0U +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF0_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_CTRL_PAUSE_MMU_BIF0_EN 0x00000001U + +/* Register ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS */ +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS 0x12B0U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_MASKFULL 0x000000000000F775ULL +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_CAT_BASE_SHIFT 12U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_CAT_BASE_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_PAGE_SIZE_SHIFT 8U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_PAGE_SIZE_CLRMSK 0xFFFFF8FFU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_DATA_TYPE_SHIFT 5U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_DATA_TYPE_CLRMSK 0xFFFFFF9FU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_SHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_EN 0x00000010U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_SHIFT 2U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_EN 0x00000004U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_SHIFT 0U +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_FAULT_BANK0_MMU_STATUS_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS */ +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS 0x12B8U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__MASKFULL 0x001FFFFFFFFFFFF0ULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_MASKFULL 0x0007FFFFFFFFFFF0ULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__RNW_SHIFT 52U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__RNW_CLRMSK 0xFFEFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__RNW_EN 0x0010000000000000ULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_RNW_SHIFT 50U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_RNW_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_RNW_EN 0x0004000000000000ULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__TAG_SB_SHIFT 46U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__TAG_SB_CLRMSK 0xFFF03FFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_TAG_SB_SHIFT 44U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_TAG_SB_CLRMSK 0xFFFC0FFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_TAG_ID_SHIFT 40U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_TAG_ID_CLRMSK 0xFFFFF0FFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__TAG_ID_SHIFT 40U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS__XE_MEM__TAG_ID_CLRMSK 0xFFFFC0FFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_SHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_ALIGNSHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_ALIGNSIZE 16U + +/* Register ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS */ +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS 0x12C0U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_MASKFULL 0x000000000000F775ULL +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_CAT_BASE_SHIFT 12U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_CAT_BASE_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_PAGE_SIZE_SHIFT 8U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_PAGE_SIZE_CLRMSK 0xFFFFF8FFU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_DATA_TYPE_SHIFT 5U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_DATA_TYPE_CLRMSK 0xFFFFFF9FU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_RO_SHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_RO_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_RO_EN 0x00000010U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_PM_META_RO_SHIFT 2U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_PM_META_RO_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_PM_META_RO_EN 0x00000004U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_SHIFT 0U +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_FAULT_BANK1_MMU_STATUS_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS */ +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS 0x12C8U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_MASKFULL 0x0007FFFFFFFFFFF0ULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_RNW_SHIFT 50U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_RNW_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_RNW_EN 0x0004000000000000ULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_TAG_SB_SHIFT 44U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_TAG_SB_CLRMSK 0xFFFC0FFFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_TAG_ID_SHIFT 40U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_TAG_ID_CLRMSK 0xFFFFF0FFFFFFFFFFULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_ADDRESS_SHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_ADDRESS_ALIGNSHIFT 4U +#define ROGUE_CR_BIF_FAULT_BANK1_REQ_STATUS_ADDRESS_ALIGNSIZE 16U + +/* Register ROGUE_CR_BIF_MMU_STATUS */ +#define ROGUE_CR_BIF_MMU_STATUS 0x12D0U +#define ROGUE_CR_BIF_MMU_STATUS__XE_MEM__MASKFULL 0x000000001FFFFFF7ULL +#define ROGUE_CR_BIF_MMU_STATUS_MASKFULL 0x000000001FFFFFF7ULL +#define ROGUE_CR_BIF_MMU_STATUS_PM_FAULT_SHIFT 28U +#define ROGUE_CR_BIF_MMU_STATUS_PM_FAULT_CLRMSK 0xEFFFFFFFU +#define ROGUE_CR_BIF_MMU_STATUS_PM_FAULT_EN 0x10000000U +#define ROGUE_CR_BIF_MMU_STATUS_PC_DATA_SHIFT 20U +#define ROGUE_CR_BIF_MMU_STATUS_PC_DATA_CLRMSK 0xF00FFFFFU +#define ROGUE_CR_BIF_MMU_STATUS_PD_DATA_SHIFT 12U +#define ROGUE_CR_BIF_MMU_STATUS_PD_DATA_CLRMSK 0xFFF00FFFU +#define ROGUE_CR_BIF_MMU_STATUS_PT_DATA_SHIFT 4U +#define ROGUE_CR_BIF_MMU_STATUS_PT_DATA_CLRMSK 0xFFFFF00FU +#define ROGUE_CR_BIF_MMU_STATUS_STALLED_SHIFT 2U +#define ROGUE_CR_BIF_MMU_STATUS_STALLED_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_MMU_STATUS_STALLED_EN 0x00000004U +#define ROGUE_CR_BIF_MMU_STATUS_PAUSED_SHIFT 1U +#define ROGUE_CR_BIF_MMU_STATUS_PAUSED_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_MMU_STATUS_PAUSED_EN 0x00000002U +#define ROGUE_CR_BIF_MMU_STATUS_BUSY_SHIFT 0U +#define ROGUE_CR_BIF_MMU_STATUS_BUSY_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_MMU_STATUS_BUSY_EN 0x00000001U + +/* Register group: ROGUE_CR_BIF_TILING_CFG, with 8 repeats */ +#define ROGUE_CR_BIF_TILING_CFG_REPEATCOUNT 8U +/* Register ROGUE_CR_BIF_TILING_CFG0 */ +#define ROGUE_CR_BIF_TILING_CFG0 0x12D8U +#define ROGUE_CR_BIF_TILING_CFG0_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG0_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG0_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG0_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG0_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG0_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG0_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG0_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG0_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG0_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG0_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG0_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG0_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG0_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG1 */ +#define ROGUE_CR_BIF_TILING_CFG1 0x12E0U +#define ROGUE_CR_BIF_TILING_CFG1_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG1_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG1_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG1_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG1_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG1_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG1_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG1_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG1_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG1_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG1_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG1_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG1_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG1_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG2 */ +#define ROGUE_CR_BIF_TILING_CFG2 0x12E8U +#define ROGUE_CR_BIF_TILING_CFG2_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG2_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG2_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG2_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG2_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG2_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG2_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG2_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG2_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG2_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG2_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG2_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG2_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG2_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG3 */ +#define ROGUE_CR_BIF_TILING_CFG3 0x12F0U +#define ROGUE_CR_BIF_TILING_CFG3_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG3_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG3_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG3_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG3_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG3_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG3_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG3_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG3_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG3_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG3_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG3_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG3_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG3_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG4 */ +#define ROGUE_CR_BIF_TILING_CFG4 0x12F8U +#define ROGUE_CR_BIF_TILING_CFG4_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG4_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG4_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG4_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG4_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG4_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG4_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG4_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG4_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG4_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG4_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG4_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG4_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG4_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG5 */ +#define ROGUE_CR_BIF_TILING_CFG5 0x1300U +#define ROGUE_CR_BIF_TILING_CFG5_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG5_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG5_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG5_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG5_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG5_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG5_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG5_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG5_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG5_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG5_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG5_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG5_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG5_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG6 */ +#define ROGUE_CR_BIF_TILING_CFG6 0x1308U +#define ROGUE_CR_BIF_TILING_CFG6_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG6_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG6_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG6_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG6_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG6_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG6_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG6_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG6_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG6_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG6_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG6_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG6_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG6_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_TILING_CFG7 */ +#define ROGUE_CR_BIF_TILING_CFG7 0x1310U +#define ROGUE_CR_BIF_TILING_CFG7_MASKFULL 0xFFFFFFFF0FFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG7_XSTRIDE_SHIFT 61U +#define ROGUE_CR_BIF_TILING_CFG7_XSTRIDE_CLRMSK 0x1FFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG7_ENABLE_SHIFT 60U +#define ROGUE_CR_BIF_TILING_CFG7_ENABLE_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG7_ENABLE_EN 0x1000000000000000ULL +#define ROGUE_CR_BIF_TILING_CFG7_MAX_ADDRESS_SHIFT 32U +#define ROGUE_CR_BIF_TILING_CFG7_MAX_ADDRESS_CLRMSK 0xF0000000FFFFFFFFULL +#define ROGUE_CR_BIF_TILING_CFG7_MAX_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG7_MAX_ADDRESS_ALIGNSIZE 4096U +#define ROGUE_CR_BIF_TILING_CFG7_MIN_ADDRESS_SHIFT 0U +#define ROGUE_CR_BIF_TILING_CFG7_MIN_ADDRESS_CLRMSK 0xFFFFFFFFF0000000ULL +#define ROGUE_CR_BIF_TILING_CFG7_MIN_ADDRESS_ALIGNSHIFT 12U +#define ROGUE_CR_BIF_TILING_CFG7_MIN_ADDRESS_ALIGNSIZE 4096U + +/* Register ROGUE_CR_BIF_READS_EXT_STATUS */ +#define ROGUE_CR_BIF_READS_EXT_STATUS 0x1320U +#define ROGUE_CR_BIF_READS_EXT_STATUS_MASKFULL 0x000000000FFFFFFFULL +#define ROGUE_CR_BIF_READS_EXT_STATUS_MMU_SHIFT 16U +#define ROGUE_CR_BIF_READS_EXT_STATUS_MMU_CLRMSK 0xF000FFFFU +#define ROGUE_CR_BIF_READS_EXT_STATUS_BANK1_SHIFT 0U +#define ROGUE_CR_BIF_READS_EXT_STATUS_BANK1_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIF_READS_INT_STATUS */ +#define ROGUE_CR_BIF_READS_INT_STATUS 0x1328U +#define ROGUE_CR_BIF_READS_INT_STATUS_MASKFULL 0x0000000007FFFFFFULL +#define ROGUE_CR_BIF_READS_INT_STATUS_MMU_SHIFT 16U +#define ROGUE_CR_BIF_READS_INT_STATUS_MMU_CLRMSK 0xF800FFFFU +#define ROGUE_CR_BIF_READS_INT_STATUS_BANK1_SHIFT 0U +#define ROGUE_CR_BIF_READS_INT_STATUS_BANK1_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIFPM_READS_INT_STATUS */ +#define ROGUE_CR_BIFPM_READS_INT_STATUS 0x1330U +#define ROGUE_CR_BIFPM_READS_INT_STATUS_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_BIFPM_READS_INT_STATUS_BANK0_SHIFT 0U +#define ROGUE_CR_BIFPM_READS_INT_STATUS_BANK0_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIFPM_READS_EXT_STATUS */ +#define ROGUE_CR_BIFPM_READS_EXT_STATUS 0x1338U +#define ROGUE_CR_BIFPM_READS_EXT_STATUS_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_BIFPM_READS_EXT_STATUS_BANK0_SHIFT 0U +#define ROGUE_CR_BIFPM_READS_EXT_STATUS_BANK0_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIFPM_STATUS_MMU */ +#define ROGUE_CR_BIFPM_STATUS_MMU 0x1350U +#define ROGUE_CR_BIFPM_STATUS_MMU_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_BIFPM_STATUS_MMU_REQUESTS_SHIFT 0U +#define ROGUE_CR_BIFPM_STATUS_MMU_REQUESTS_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_BIF_STATUS_MMU */ +#define ROGUE_CR_BIF_STATUS_MMU 0x1358U +#define ROGUE_CR_BIF_STATUS_MMU_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_BIF_STATUS_MMU_REQUESTS_SHIFT 0U +#define ROGUE_CR_BIF_STATUS_MMU_REQUESTS_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_BIF_FAULT_READ */ +#define ROGUE_CR_BIF_FAULT_READ 0x13E0U +#define ROGUE_CR_BIF_FAULT_READ_MASKFULL 0x000000FFFFFFFFF0ULL +#define ROGUE_CR_BIF_FAULT_READ_ADDRESS_SHIFT 4U +#define ROGUE_CR_BIF_FAULT_READ_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_BIF_FAULT_READ_ADDRESS_ALIGNSHIFT 4U +#define ROGUE_CR_BIF_FAULT_READ_ADDRESS_ALIGNSIZE 16U + +/* Register ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS */ +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS 0x1430U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_MASKFULL 0x000000000000F775ULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_CAT_BASE_SHIFT 12U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_CAT_BASE_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_PAGE_SIZE_SHIFT 8U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_PAGE_SIZE_CLRMSK 0xFFFFF8FFU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_DATA_TYPE_SHIFT 5U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_DATA_TYPE_CLRMSK 0xFFFFFF9FU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_SHIFT 4U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_RO_EN 0x00000010U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_SHIFT 2U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_PM_META_RO_EN 0x00000004U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_SHIFT 0U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_MMU_STATUS_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS */ +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS 0x1438U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_MASKFULL 0x0007FFFFFFFFFFF0ULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_RNW_SHIFT 50U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_RNW_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_RNW_EN 0x0004000000000000ULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_TAG_SB_SHIFT 44U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_TAG_SB_CLRMSK 0xFFFC0FFFFFFFFFFFULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_TAG_ID_SHIFT 40U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_TAG_ID_CLRMSK 0xFFFFF0FFFFFFFFFFULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_SHIFT 4U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_ALIGNSHIFT 4U +#define ROGUE_CR_TEXAS_BIF_FAULT_BANK0_REQ_STATUS_ADDRESS_ALIGNSIZE 16U + +/* Register ROGUE_CR_MCU_FENCE */ +#define ROGUE_CR_MCU_FENCE 0x1740U +#define ROGUE_CR_MCU_FENCE_MASKFULL 0x000007FFFFFFFFE0ULL +#define ROGUE_CR_MCU_FENCE_DM_SHIFT 40U +#define ROGUE_CR_MCU_FENCE_DM_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_MCU_FENCE_DM_VERTEX 0x0000000000000000ULL +#define ROGUE_CR_MCU_FENCE_DM_PIXEL 0x0000010000000000ULL +#define ROGUE_CR_MCU_FENCE_DM_COMPUTE 0x0000020000000000ULL +#define ROGUE_CR_MCU_FENCE_DM_RAY_VERTEX 0x0000030000000000ULL +#define ROGUE_CR_MCU_FENCE_DM_RAY 0x0000040000000000ULL +#define ROGUE_CR_MCU_FENCE_DM_FASTRENDER 0x0000050000000000ULL +#define ROGUE_CR_MCU_FENCE_ADDR_SHIFT 5U +#define ROGUE_CR_MCU_FENCE_ADDR_CLRMSK 0xFFFFFF000000001FULL +#define ROGUE_CR_MCU_FENCE_ADDR_ALIGNSHIFT 5U +#define ROGUE_CR_MCU_FENCE_ADDR_ALIGNSIZE 32U + +/* Register group: ROGUE_CR_SCRATCH, with 16 repeats */ +#define ROGUE_CR_SCRATCH_REPEATCOUNT 16U +/* Register ROGUE_CR_SCRATCH0 */ +#define ROGUE_CR_SCRATCH0 0x1A00U +#define ROGUE_CR_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH1 */ +#define ROGUE_CR_SCRATCH1 0x1A08U +#define ROGUE_CR_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH2 */ +#define ROGUE_CR_SCRATCH2 0x1A10U +#define ROGUE_CR_SCRATCH2_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH2_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH3 */ +#define ROGUE_CR_SCRATCH3 0x1A18U +#define ROGUE_CR_SCRATCH3_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH3_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH4 */ +#define ROGUE_CR_SCRATCH4 0x1A20U +#define ROGUE_CR_SCRATCH4_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH4_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH4_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH5 */ +#define ROGUE_CR_SCRATCH5 0x1A28U +#define ROGUE_CR_SCRATCH5_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH5_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH5_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH6 */ +#define ROGUE_CR_SCRATCH6 0x1A30U +#define ROGUE_CR_SCRATCH6_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH6_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH6_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH7 */ +#define ROGUE_CR_SCRATCH7 0x1A38U +#define ROGUE_CR_SCRATCH7_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH7_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH7_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH8 */ +#define ROGUE_CR_SCRATCH8 0x1A40U +#define ROGUE_CR_SCRATCH8_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH8_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH8_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH9 */ +#define ROGUE_CR_SCRATCH9 0x1A48U +#define ROGUE_CR_SCRATCH9_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH9_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH9_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH10 */ +#define ROGUE_CR_SCRATCH10 0x1A50U +#define ROGUE_CR_SCRATCH10_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH10_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH10_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH11 */ +#define ROGUE_CR_SCRATCH11 0x1A58U +#define ROGUE_CR_SCRATCH11_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH11_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH11_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH12 */ +#define ROGUE_CR_SCRATCH12 0x1A60U +#define ROGUE_CR_SCRATCH12_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH12_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH12_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH13 */ +#define ROGUE_CR_SCRATCH13 0x1A68U +#define ROGUE_CR_SCRATCH13_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH13_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH13_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH14 */ +#define ROGUE_CR_SCRATCH14 0x1A70U +#define ROGUE_CR_SCRATCH14_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH14_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH14_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SCRATCH15 */ +#define ROGUE_CR_SCRATCH15 0x1A78U +#define ROGUE_CR_SCRATCH15_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SCRATCH15_DATA_SHIFT 0U +#define ROGUE_CR_SCRATCH15_DATA_CLRMSK 0x00000000U + +/* Register group: ROGUE_CR_OS0_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS0_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS0_SCRATCH0 */ +#define ROGUE_CR_OS0_SCRATCH0 0x1A80U +#define ROGUE_CR_OS0_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS0_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS0_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS0_SCRATCH1 */ +#define ROGUE_CR_OS0_SCRATCH1 0x1A88U +#define ROGUE_CR_OS0_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS0_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS0_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS0_SCRATCH2 */ +#define ROGUE_CR_OS0_SCRATCH2 0x1A90U +#define ROGUE_CR_OS0_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS0_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS0_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS0_SCRATCH3 */ +#define ROGUE_CR_OS0_SCRATCH3 0x1A98U +#define ROGUE_CR_OS0_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS0_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS0_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS1_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS1_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS1_SCRATCH0 */ +#define ROGUE_CR_OS1_SCRATCH0 0x11A80U +#define ROGUE_CR_OS1_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS1_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS1_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS1_SCRATCH1 */ +#define ROGUE_CR_OS1_SCRATCH1 0x11A88U +#define ROGUE_CR_OS1_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS1_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS1_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS1_SCRATCH2 */ +#define ROGUE_CR_OS1_SCRATCH2 0x11A90U +#define ROGUE_CR_OS1_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS1_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS1_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS1_SCRATCH3 */ +#define ROGUE_CR_OS1_SCRATCH3 0x11A98U +#define ROGUE_CR_OS1_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS1_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS1_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS2_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS2_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS2_SCRATCH0 */ +#define ROGUE_CR_OS2_SCRATCH0 0x21A80U +#define ROGUE_CR_OS2_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS2_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS2_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS2_SCRATCH1 */ +#define ROGUE_CR_OS2_SCRATCH1 0x21A88U +#define ROGUE_CR_OS2_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS2_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS2_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS2_SCRATCH2 */ +#define ROGUE_CR_OS2_SCRATCH2 0x21A90U +#define ROGUE_CR_OS2_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS2_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS2_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS2_SCRATCH3 */ +#define ROGUE_CR_OS2_SCRATCH3 0x21A98U +#define ROGUE_CR_OS2_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS2_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS2_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS3_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS3_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS3_SCRATCH0 */ +#define ROGUE_CR_OS3_SCRATCH0 0x31A80U +#define ROGUE_CR_OS3_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS3_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS3_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS3_SCRATCH1 */ +#define ROGUE_CR_OS3_SCRATCH1 0x31A88U +#define ROGUE_CR_OS3_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS3_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS3_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS3_SCRATCH2 */ +#define ROGUE_CR_OS3_SCRATCH2 0x31A90U +#define ROGUE_CR_OS3_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS3_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS3_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS3_SCRATCH3 */ +#define ROGUE_CR_OS3_SCRATCH3 0x31A98U +#define ROGUE_CR_OS3_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS3_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS3_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS4_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS4_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS4_SCRATCH0 */ +#define ROGUE_CR_OS4_SCRATCH0 0x41A80U +#define ROGUE_CR_OS4_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS4_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS4_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS4_SCRATCH1 */ +#define ROGUE_CR_OS4_SCRATCH1 0x41A88U +#define ROGUE_CR_OS4_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS4_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS4_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS4_SCRATCH2 */ +#define ROGUE_CR_OS4_SCRATCH2 0x41A90U +#define ROGUE_CR_OS4_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS4_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS4_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS4_SCRATCH3 */ +#define ROGUE_CR_OS4_SCRATCH3 0x41A98U +#define ROGUE_CR_OS4_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS4_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS4_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS5_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS5_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS5_SCRATCH0 */ +#define ROGUE_CR_OS5_SCRATCH0 0x51A80U +#define ROGUE_CR_OS5_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS5_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS5_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS5_SCRATCH1 */ +#define ROGUE_CR_OS5_SCRATCH1 0x51A88U +#define ROGUE_CR_OS5_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS5_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS5_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS5_SCRATCH2 */ +#define ROGUE_CR_OS5_SCRATCH2 0x51A90U +#define ROGUE_CR_OS5_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS5_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS5_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS5_SCRATCH3 */ +#define ROGUE_CR_OS5_SCRATCH3 0x51A98U +#define ROGUE_CR_OS5_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS5_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS5_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS6_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS6_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS6_SCRATCH0 */ +#define ROGUE_CR_OS6_SCRATCH0 0x61A80U +#define ROGUE_CR_OS6_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS6_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS6_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS6_SCRATCH1 */ +#define ROGUE_CR_OS6_SCRATCH1 0x61A88U +#define ROGUE_CR_OS6_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS6_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS6_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS6_SCRATCH2 */ +#define ROGUE_CR_OS6_SCRATCH2 0x61A90U +#define ROGUE_CR_OS6_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS6_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS6_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS6_SCRATCH3 */ +#define ROGUE_CR_OS6_SCRATCH3 0x61A98U +#define ROGUE_CR_OS6_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS6_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS6_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register group: ROGUE_CR_OS7_SCRATCH, with 2 repeats */ +#define ROGUE_CR_OS7_SCRATCH_REPEATCOUNT 2U +/* Register ROGUE_CR_OS7_SCRATCH0 */ +#define ROGUE_CR_OS7_SCRATCH0 0x71A80U +#define ROGUE_CR_OS7_SCRATCH0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS7_SCRATCH0_DATA_SHIFT 0U +#define ROGUE_CR_OS7_SCRATCH0_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS7_SCRATCH1 */ +#define ROGUE_CR_OS7_SCRATCH1 0x71A88U +#define ROGUE_CR_OS7_SCRATCH1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_OS7_SCRATCH1_DATA_SHIFT 0U +#define ROGUE_CR_OS7_SCRATCH1_DATA_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OS7_SCRATCH2 */ +#define ROGUE_CR_OS7_SCRATCH2 0x71A90U +#define ROGUE_CR_OS7_SCRATCH2_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS7_SCRATCH2_DATA_SHIFT 0U +#define ROGUE_CR_OS7_SCRATCH2_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_OS7_SCRATCH3 */ +#define ROGUE_CR_OS7_SCRATCH3 0x71A98U +#define ROGUE_CR_OS7_SCRATCH3_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_OS7_SCRATCH3_DATA_SHIFT 0U +#define ROGUE_CR_OS7_SCRATCH3_DATA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_SPFILTER_SIGNAL_DESCR */ +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR 0x2700U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_SIZE_SHIFT 0U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_SIZE_CLRMSK 0xFFFF0000U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_SIZE_ALIGNSHIFT 4U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_SIZE_ALIGNSIZE 16U + +/* Register ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN */ +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN 0x2708U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN_MASKFULL 0x000000FFFFFFFFF0ULL +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN_ADDR_SHIFT 4U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN_ADDR_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN_ADDR_ALIGNSHIFT 4U +#define ROGUE_CR_SPFILTER_SIGNAL_DESCR_MIN_ADDR_ALIGNSIZE 16U + +/* Register group: ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG, with 16 repeats */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG_REPEATCOUNT 16U +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0 0x3000U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1 0x3008U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG1_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2 0x3010U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG2_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3 0x3018U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG3_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4 0x3020U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG4_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5 0x3028U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG5_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6 0x3030U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG6_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7 0x3038U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG7_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8 0x3040U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG8_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9 0x3048U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG9_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10 0x3050U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG10_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11 0x3058U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG11_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12 0x3060U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG12_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13 0x3068U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG13_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14 0x3070U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG14_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15 */ +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15 0x3078U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_MASKFULL 0x7FFFF7FFFFFFF000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_TRUSTED_SHIFT 62U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_TRUSTED_CLRMSK 0xBFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_TRUSTED_EN 0x4000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_LOAD_STORE_EN_SHIFT 61U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_LOAD_STORE_EN_CLRMSK 0xDFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_LOAD_STORE_EN_EN 0x2000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_FETCH_EN_SHIFT 60U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_FETCH_EN_CLRMSK 0xEFFFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_FETCH_EN_EN 0x1000000000000000ULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_SIZE_SHIFT 44U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_SIZE_CLRMSK 0xF0000FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_CBASE_SHIFT 40U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_CBASE_CLRMSK 0xFFFFF8FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_DEVVADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_DEVVADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_DEVVADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG15_DEVVADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_BOOT */ +#define ROGUE_CR_FWCORE_BOOT 0x3090U +#define ROGUE_CR_FWCORE_BOOT_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_FWCORE_BOOT_ENABLE_SHIFT 0U +#define ROGUE_CR_FWCORE_BOOT_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_BOOT_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_RESET_ADDR */ +#define ROGUE_CR_FWCORE_RESET_ADDR 0x3098U +#define ROGUE_CR_FWCORE_RESET_ADDR_MASKFULL 0x00000000FFFFFFFEULL +#define ROGUE_CR_FWCORE_RESET_ADDR_ADDR_SHIFT 1U +#define ROGUE_CR_FWCORE_RESET_ADDR_ADDR_CLRMSK 0x00000001U +#define ROGUE_CR_FWCORE_RESET_ADDR_ADDR_ALIGNSHIFT 1U +#define ROGUE_CR_FWCORE_RESET_ADDR_ADDR_ALIGNSIZE 2U + +/* Register ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR */ +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR 0x30A0U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR_MASKFULL 0x00000000FFFFFFFEULL +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR_ADDR_SHIFT 1U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR_ADDR_CLRMSK 0x00000001U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR_ADDR_ALIGNSHIFT 1U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_ADDR_ADDR_ALIGNSIZE 2U + +/* Register ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT */ +#define ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT 0x30A8U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT_TRIGGER_EN_SHIFT 0U +#define ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT_TRIGGER_EN_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_WRAPPER_NMI_EVENT_TRIGGER_EN_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS */ +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS 0x30B0U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_MASKFULL 0x000000000000F771ULL +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_CAT_BASE_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_CAT_BASE_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_PAGE_SIZE_SHIFT 8U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_PAGE_SIZE_CLRMSK 0xFFFFF8FFU +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_DATA_TYPE_SHIFT 5U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_DATA_TYPE_CLRMSK 0xFFFFFF9FU +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_RO_SHIFT 4U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_RO_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_RO_EN 0x00000010U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_SHIFT 0U +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_MEM_FAULT_MMU_STATUS_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS */ +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS 0x30B8U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_MASKFULL 0x001FFFFFFFFFFFF0ULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_RNW_SHIFT 52U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_RNW_CLRMSK 0xFFEFFFFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_RNW_EN 0x0010000000000000ULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_TAG_SB_SHIFT 46U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_TAG_SB_CLRMSK 0xFFF03FFFFFFFFFFFULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_TAG_ID_SHIFT 40U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_TAG_ID_CLRMSK 0xFFFFC0FFFFFFFFFFULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_ADDRESS_SHIFT 4U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_ADDRESS_ALIGNSHIFT 4U +#define ROGUE_CR_FWCORE_MEM_FAULT_REQ_STATUS_ADDRESS_ALIGNSIZE 16U + +/* Register ROGUE_CR_FWCORE_MEM_CTRL_INVAL */ +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL 0x30C0U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_TLB_SHIFT 3U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_TLB_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_TLB_EN 0x00000008U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PC_SHIFT 2U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PC_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PC_EN 0x00000004U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PD_SHIFT 1U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PD_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PD_EN 0x00000002U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PT_SHIFT 0U +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_MEM_CTRL_INVAL_PT_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_MEM_MMU_STATUS */ +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS 0x30C8U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_MASKFULL 0x000000000FFFFFF7ULL +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PC_DATA_SHIFT 20U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PC_DATA_CLRMSK 0xF00FFFFFU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PD_DATA_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PD_DATA_CLRMSK 0xFFF00FFFU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PT_DATA_SHIFT 4U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PT_DATA_CLRMSK 0xFFFFF00FU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_STALLED_SHIFT 2U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_STALLED_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_STALLED_EN 0x00000004U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PAUSED_SHIFT 1U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PAUSED_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_PAUSED_EN 0x00000002U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_BUSY_SHIFT 0U +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_BUSY_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_MEM_MMU_STATUS_BUSY_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_MEM_READS_EXT_STATUS */ +#define ROGUE_CR_FWCORE_MEM_READS_EXT_STATUS 0x30D8U +#define ROGUE_CR_FWCORE_MEM_READS_EXT_STATUS_MASKFULL 0x0000000000000FFFULL +#define ROGUE_CR_FWCORE_MEM_READS_EXT_STATUS_MMU_SHIFT 0U +#define ROGUE_CR_FWCORE_MEM_READS_EXT_STATUS_MMU_CLRMSK 0xFFFFF000U + +/* Register ROGUE_CR_FWCORE_MEM_READS_INT_STATUS */ +#define ROGUE_CR_FWCORE_MEM_READS_INT_STATUS 0x30E0U +#define ROGUE_CR_FWCORE_MEM_READS_INT_STATUS_MASKFULL 0x00000000000007FFULL +#define ROGUE_CR_FWCORE_MEM_READS_INT_STATUS_MMU_SHIFT 0U +#define ROGUE_CR_FWCORE_MEM_READS_INT_STATUS_MMU_CLRMSK 0xFFFFF800U + +/* Register ROGUE_CR_FWCORE_WRAPPER_FENCE */ +#define ROGUE_CR_FWCORE_WRAPPER_FENCE 0x30E8U +#define ROGUE_CR_FWCORE_WRAPPER_FENCE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_FWCORE_WRAPPER_FENCE_ID_SHIFT 0U +#define ROGUE_CR_FWCORE_WRAPPER_FENCE_ID_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_WRAPPER_FENCE_ID_EN 0x00000001U + +/* Register group: ROGUE_CR_FWCORE_MEM_CAT_BASE, with 8 repeats */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE_REPEATCOUNT 8U +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE0 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0 0x30F0U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE0_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE1 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1 0x30F8U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE1_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE2 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2 0x3100U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE2_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE3 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3 0x3108U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE3_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE4 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4 0x3110U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE4_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE5 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5 0x3118U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE5_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE6 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6 0x3120U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE6_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_MEM_CAT_BASE7 */ +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7 0x3128U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7_MASKFULL 0x000000FFFFFFF000ULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7_ADDR_SHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7_ADDR_CLRMSK 0xFFFFFF0000000FFFULL +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_FWCORE_MEM_CAT_BASE7_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_FWCORE_WDT_RESET */ +#define ROGUE_CR_FWCORE_WDT_RESET 0x3130U +#define ROGUE_CR_FWCORE_WDT_RESET_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_FWCORE_WDT_RESET_EN_SHIFT 0U +#define ROGUE_CR_FWCORE_WDT_RESET_EN_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_WDT_RESET_EN_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_WDT_CTRL */ +#define ROGUE_CR_FWCORE_WDT_CTRL 0x3138U +#define ROGUE_CR_FWCORE_WDT_CTRL_MASKFULL 0x00000000FFFF1F01ULL +#define ROGUE_CR_FWCORE_WDT_CTRL_PROT_SHIFT 16U +#define ROGUE_CR_FWCORE_WDT_CTRL_PROT_CLRMSK 0x0000FFFFU +#define ROGUE_CR_FWCORE_WDT_CTRL_THRESHOLD_SHIFT 8U +#define ROGUE_CR_FWCORE_WDT_CTRL_THRESHOLD_CLRMSK 0xFFFFE0FFU +#define ROGUE_CR_FWCORE_WDT_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_FWCORE_WDT_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_FWCORE_WDT_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_FWCORE_WDT_COUNT */ +#define ROGUE_CR_FWCORE_WDT_COUNT 0x3140U +#define ROGUE_CR_FWCORE_WDT_COUNT_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_FWCORE_WDT_COUNT_VALUE_SHIFT 0U +#define ROGUE_CR_FWCORE_WDT_COUNT_VALUE_CLRMSK 0x00000000U + +/* Register group: ROGUE_CR_FWCORE_DMI_RESERVED0, with 4 repeats */ +#define ROGUE_CR_FWCORE_DMI_RESERVED0_REPEATCOUNT 4U +/* Register ROGUE_CR_FWCORE_DMI_RESERVED00 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED00 0x3400U +#define ROGUE_CR_FWCORE_DMI_RESERVED00_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED01 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED01 0x3408U +#define ROGUE_CR_FWCORE_DMI_RESERVED01_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED02 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED02 0x3410U +#define ROGUE_CR_FWCORE_DMI_RESERVED02_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED03 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED03 0x3418U +#define ROGUE_CR_FWCORE_DMI_RESERVED03_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_DATA0 */ +#define ROGUE_CR_FWCORE_DMI_DATA0 0x3420U +#define ROGUE_CR_FWCORE_DMI_DATA0_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_DATA1 */ +#define ROGUE_CR_FWCORE_DMI_DATA1 0x3428U +#define ROGUE_CR_FWCORE_DMI_DATA1_MASKFULL 0x0000000000000000ULL + +/* Register group: ROGUE_CR_FWCORE_DMI_RESERVED1, with 5 repeats */ +#define ROGUE_CR_FWCORE_DMI_RESERVED1_REPEATCOUNT 5U +/* Register ROGUE_CR_FWCORE_DMI_RESERVED10 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED10 0x3430U +#define ROGUE_CR_FWCORE_DMI_RESERVED10_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED11 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED11 0x3438U +#define ROGUE_CR_FWCORE_DMI_RESERVED11_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED12 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED12 0x3440U +#define ROGUE_CR_FWCORE_DMI_RESERVED12_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED13 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED13 0x3448U +#define ROGUE_CR_FWCORE_DMI_RESERVED13_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED14 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED14 0x3450U +#define ROGUE_CR_FWCORE_DMI_RESERVED14_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_DMCONTROL */ +#define ROGUE_CR_FWCORE_DMI_DMCONTROL 0x3480U +#define ROGUE_CR_FWCORE_DMI_DMCONTROL_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_DMSTATUS */ +#define ROGUE_CR_FWCORE_DMI_DMSTATUS 0x3488U +#define ROGUE_CR_FWCORE_DMI_DMSTATUS_MASKFULL 0x0000000000000000ULL + +/* Register group: ROGUE_CR_FWCORE_DMI_RESERVED2, with 4 repeats */ +#define ROGUE_CR_FWCORE_DMI_RESERVED2_REPEATCOUNT 4U +/* Register ROGUE_CR_FWCORE_DMI_RESERVED20 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED20 0x3490U +#define ROGUE_CR_FWCORE_DMI_RESERVED20_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED21 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED21 0x3498U +#define ROGUE_CR_FWCORE_DMI_RESERVED21_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED22 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED22 0x34A0U +#define ROGUE_CR_FWCORE_DMI_RESERVED22_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED23 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED23 0x34A8U +#define ROGUE_CR_FWCORE_DMI_RESERVED23_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_ABSTRACTCS */ +#define ROGUE_CR_FWCORE_DMI_ABSTRACTCS 0x34B0U +#define ROGUE_CR_FWCORE_DMI_ABSTRACTCS_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_COMMAND */ +#define ROGUE_CR_FWCORE_DMI_COMMAND 0x34B8U +#define ROGUE_CR_FWCORE_DMI_COMMAND_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_SBCS */ +#define ROGUE_CR_FWCORE_DMI_SBCS 0x35C0U +#define ROGUE_CR_FWCORE_DMI_SBCS_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_SBADDRESS0 */ +#define ROGUE_CR_FWCORE_DMI_SBADDRESS0 0x35C8U +#define ROGUE_CR_FWCORE_DMI_SBADDRESS0_MASKFULL 0x0000000000000000ULL + +/* Register group: ROGUE_CR_FWCORE_DMI_RESERVED3, with 2 repeats */ +#define ROGUE_CR_FWCORE_DMI_RESERVED3_REPEATCOUNT 2U +/* Register ROGUE_CR_FWCORE_DMI_RESERVED30 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED30 0x34D0U +#define ROGUE_CR_FWCORE_DMI_RESERVED30_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_RESERVED31 */ +#define ROGUE_CR_FWCORE_DMI_RESERVED31 0x34D8U +#define ROGUE_CR_FWCORE_DMI_RESERVED31_MASKFULL 0x0000000000000000ULL + +/* Register group: ROGUE_CR_FWCORE_DMI_SBDATA, with 4 repeats */ +#define ROGUE_CR_FWCORE_DMI_SBDATA_REPEATCOUNT 4U +/* Register ROGUE_CR_FWCORE_DMI_SBDATA0 */ +#define ROGUE_CR_FWCORE_DMI_SBDATA0 0x35E0U +#define ROGUE_CR_FWCORE_DMI_SBDATA0_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_SBDATA1 */ +#define ROGUE_CR_FWCORE_DMI_SBDATA1 0x35E8U +#define ROGUE_CR_FWCORE_DMI_SBDATA1_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_SBDATA2 */ +#define ROGUE_CR_FWCORE_DMI_SBDATA2 0x35F0U +#define ROGUE_CR_FWCORE_DMI_SBDATA2_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_SBDATA3 */ +#define ROGUE_CR_FWCORE_DMI_SBDATA3 0x35F8U +#define ROGUE_CR_FWCORE_DMI_SBDATA3_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_FWCORE_DMI_HALTSUM0 */ +#define ROGUE_CR_FWCORE_DMI_HALTSUM0 0x3600U +#define ROGUE_CR_FWCORE_DMI_HALTSUM0_MASKFULL 0x0000000000000000ULL + +/* Register ROGUE_CR_SLC_CTRL_MISC */ +#define ROGUE_CR_SLC_CTRL_MISC 0x3800U +#define ROGUE_CR_SLC_CTRL_MISC_MASKFULL 0xFFFFFFFF01FF010FULL +#define ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS_SHIFT 32U +#define ROGUE_CR_SLC_CTRL_MISC_SCRAMBLE_BITS_CLRMSK 0x00000000FFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_SHIFT 24U +#define ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_CLRMSK 0xFFFFFFFFFEFFFFFFULL +#define ROGUE_CR_SLC_CTRL_MISC_LAZYWB_OVERRIDE_EN 0x0000000001000000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_SHIFT 16U +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_CLRMSK 0xFFFFFFFFFF00FFFFULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_INTERLEAVED_64_BYTE 0x0000000000000000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_INTERLEAVED_128_BYTE 0x0000000000010000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_SIMPLE_HASH1 0x0000000000100000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_SIMPLE_HASH2 0x0000000000110000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH1 0x0000000000200000ULL +#define ROGUE_CR_SLC_CTRL_MISC_ADDR_DECODE_MODE_PVR_HASH2_SCRAMBLE 0x0000000000210000ULL +#define ROGUE_CR_SLC_CTRL_MISC_PAUSE_SHIFT 8U +#define ROGUE_CR_SLC_CTRL_MISC_PAUSE_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_SLC_CTRL_MISC_PAUSE_EN 0x0000000000000100ULL +#define ROGUE_CR_SLC_CTRL_MISC_RESP_PRIORITY_SHIFT 3U +#define ROGUE_CR_SLC_CTRL_MISC_RESP_PRIORITY_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_SLC_CTRL_MISC_RESP_PRIORITY_EN 0x0000000000000008ULL +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_LINE_USE_LIMIT_SHIFT 2U +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_LINE_USE_LIMIT_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_LINE_USE_LIMIT_EN 0x0000000000000004ULL +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_SHIFT 1U +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_SLC_CTRL_MISC_ENABLE_PSG_HAZARD_CHECK_EN 0x0000000000000002ULL +#define ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_SHIFT 0U +#define ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_SLC_CTRL_MISC_BYPASS_BURST_COMBINER_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_SLC_CTRL_FLUSH_INVAL */ +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL 0x3818U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_MASKFULL 0x0000000080000FFFULL +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_LAZY_SHIFT 31U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_LAZY_CLRMSK 0x7FFFFFFFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_LAZY_EN 0x80000000U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FASTRENDER_SHIFT 11U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FASTRENDER_CLRMSK 0xFFFFF7FFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FASTRENDER_EN 0x00000800U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_VERTEX_SHIFT 10U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_VERTEX_CLRMSK 0xFFFFFBFFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_VERTEX_EN 0x00000400U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_SHIFT 9U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_RAY_EN 0x00000200U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FRC_SHIFT 8U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FRC_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_FRC_EN 0x00000100U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXE_SHIFT 7U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXE_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXE_EN 0x00000080U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXD_SHIFT 6U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXD_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_VXD_EN 0x00000040U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_HOST_META_SHIFT 5U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_HOST_META_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_HOST_META_EN 0x00000020U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_MMU_SHIFT 4U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_MMU_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_MMU_EN 0x00000010U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_COMPUTE_SHIFT 3U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_COMPUTE_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_COMPUTE_EN 0x00000008U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_PIXEL_SHIFT 2U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_PIXEL_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_PIXEL_EN 0x00000004U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_TA_SHIFT 1U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_TA_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_DM_TA_EN 0x00000002U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_ALL_SHIFT 0U +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_ALL_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SLC_CTRL_FLUSH_INVAL_ALL_EN 0x00000001U + +/* Register ROGUE_CR_SLC_STATUS0 */ +#define ROGUE_CR_SLC_STATUS0 0x3820U +#define ROGUE_CR_SLC_STATUS0_MASKFULL 0x0000000000000007ULL +#define ROGUE_CR_SLC_STATUS0_FLUSH_INVAL_PENDING_SHIFT 2U +#define ROGUE_CR_SLC_STATUS0_FLUSH_INVAL_PENDING_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SLC_STATUS0_FLUSH_INVAL_PENDING_EN 0x00000004U +#define ROGUE_CR_SLC_STATUS0_INVAL_PENDING_SHIFT 1U +#define ROGUE_CR_SLC_STATUS0_INVAL_PENDING_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SLC_STATUS0_INVAL_PENDING_EN 0x00000002U +#define ROGUE_CR_SLC_STATUS0_FLUSH_PENDING_SHIFT 0U +#define ROGUE_CR_SLC_STATUS0_FLUSH_PENDING_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SLC_STATUS0_FLUSH_PENDING_EN 0x00000001U + +/* Register ROGUE_CR_SLC_CTRL_BYPASS */ +#define ROGUE_CR_SLC_CTRL_BYPASS 0x3828U +#define ROGUE_CR_SLC_CTRL_BYPASS__XE_MEM__MASKFULL 0x0FFFFFFFFFFF7FFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_MASKFULL 0x000000000FFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_ZLS_SHIFT 59U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_ZLS_CLRMSK 0xF7FFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_ZLS_EN 0x0800000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_HEADER_SHIFT 58U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_HEADER_CLRMSK 0xFBFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_HEADER_EN 0x0400000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_HEADER_SHIFT 57U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_HEADER_CLRMSK 0xFDFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_HEADER_EN 0x0200000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_DATA_SHIFT 56U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_DATA_CLRMSK 0xFEFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_ZLS_DATA_EN 0x0100000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_DATA_SHIFT 55U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_DATA_CLRMSK 0xFF7FFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_DECOMP_TCU_DATA_EN 0x0080000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_PBE_SHIFT 54U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_PBE_CLRMSK 0xFFBFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TFBC_COMP_PBE_EN 0x0040000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_DM_COMPUTE_SHIFT 53U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_DM_COMPUTE_CLRMSK 0xFFDFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_DM_COMPUTE_EN 0x0020000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_PDSRW_NOLINEFILL_SHIFT 52U +#define ROGUE_CR_SLC_CTRL_BYPASS_PDSRW_NOLINEFILL_CLRMSK 0xFFEFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_PDSRW_NOLINEFILL_EN 0x0010000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_PBE_NOLINEFILL_SHIFT 51U +#define ROGUE_CR_SLC_CTRL_BYPASS_PBE_NOLINEFILL_CLRMSK 0xFFF7FFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_PBE_NOLINEFILL_EN 0x0008000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBC_SHIFT 50U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBC_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBC_EN 0x0004000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_RREQ_SHIFT 49U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_RREQ_CLRMSK 0xFFFDFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_RREQ_EN 0x0002000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CREQ_SHIFT 48U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CREQ_CLRMSK 0xFFFEFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CREQ_EN 0x0001000000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_PREQ_SHIFT 47U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_PREQ_CLRMSK 0xFFFF7FFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_PREQ_EN 0x0000800000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_DBSC_SHIFT 46U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_DBSC_CLRMSK 0xFFFFBFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_DBSC_EN 0x0000400000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_SHIFT 45U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_CLRMSK 0xFFFFDFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TCU_EN 0x0000200000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PBE_SHIFT 44U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PBE_CLRMSK 0xFFFFEFFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PBE_EN 0x0000100000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_ISP_SHIFT 43U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_ISP_CLRMSK 0xFFFFF7FFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_ISP_EN 0x0000080000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PM_SHIFT 42U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PM_CLRMSK 0xFFFFFBFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PM_EN 0x0000040000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TDM_SHIFT 41U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TDM_CLRMSK 0xFFFFFDFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TDM_EN 0x0000020000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_CDM_SHIFT 40U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_CDM_CLRMSK 0xFFFFFEFFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_CDM_EN 0x0000010000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_PDS_STATE_SHIFT 39U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_PDS_STATE_CLRMSK 0xFFFFFF7FFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_PDS_STATE_EN 0x0000008000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_DB_SHIFT 38U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_DB_CLRMSK 0xFFFFFFBFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_DB_EN 0x0000004000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_VTX_VAR_SHIFT 37U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_VTX_VAR_CLRMSK 0xFFFFFFDFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TSPF_VTX_VAR_EN 0x0000002000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_VDM_SHIFT 36U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_VDM_CLRMSK 0xFFFFFFEFFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_VDM_EN 0x0000001000000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_STREAM_SHIFT 35U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_STREAM_CLRMSK 0xFFFFFFF7FFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_STREAM_EN 0x0000000800000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_REGION_SHIFT 34U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_REGION_CLRMSK 0xFFFFFFFBFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PSG_REGION_EN 0x0000000400000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_VCE_SHIFT 33U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_VCE_CLRMSK 0xFFFFFFFDFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_VCE_EN 0x0000000200000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PPP_SHIFT 32U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PPP_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_PPP_EN 0x0000000100000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FASTRENDER_SHIFT 31U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FASTRENDER_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FASTRENDER_EN 0x0000000080000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PM_ALIST_SHIFT 30U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PM_ALIST_CLRMSK 0xFFFFFFFFBFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PM_ALIST_EN 0x0000000040000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_TE_SHIFT 29U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_TE_CLRMSK 0xFFFFFFFFDFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_TE_EN 0x0000000020000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_VCE_SHIFT 28U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_VCE_CLRMSK 0xFFFFFFFFEFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PB_VCE_EN 0x0000000010000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_VERTEX_SHIFT 27U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_VERTEX_CLRMSK 0xFFFFFFFFF7FFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_VERTEX_EN 0x0000000008000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_SHIFT 26U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_CLRMSK 0xFFFFFFFFFBFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_RAY_EN 0x0000000004000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CPF_SHIFT 25U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CPF_CLRMSK 0xFFFFFFFFFDFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_CPF_EN 0x0000000002000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPU_SHIFT 24U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPU_CLRMSK 0xFFFFFFFFFEFFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPU_EN 0x0000000001000000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBDC_SHIFT 23U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBDC_CLRMSK 0xFFFFFFFFFF7FFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_FBDC_EN 0x0000000000800000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TLA_SHIFT 22U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TLA_CLRMSK 0xFFFFFFFFFFBFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TLA_EN 0x0000000000400000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_N_SHIFT 21U +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_N_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_N_EN 0x0000000000200000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_SHIFT 20U +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_CLRMSK 0xFFFFFFFFFFEFFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_BYP_CC_EN 0x0000000000100000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MCU_SHIFT 19U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MCU_CLRMSK 0xFFFFFFFFFFF7FFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MCU_EN 0x0000000000080000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PDS_SHIFT 18U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PDS_CLRMSK 0xFFFFFFFFFFFBFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_PDS_EN 0x0000000000040000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPF_SHIFT 17U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPF_CLRMSK 0xFFFFFFFFFFFDFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TPF_EN 0x0000000000020000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_TPC_SHIFT 16U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_TPC_CLRMSK 0xFFFFFFFFFFFEFFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_TA_TPC_EN 0x0000000000010000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_OBJ_SHIFT 15U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_OBJ_CLRMSK 0xFFFFFFFFFFFF7FFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_IPF_OBJ_EN 0x0000000000008000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_USC_SHIFT 14U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_USC_CLRMSK 0xFFFFFFFFFFFFBFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_USC_EN 0x0000000000004000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_META_SHIFT 13U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_META_CLRMSK 0xFFFFFFFFFFFFDFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_META_EN 0x0000000000002000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_HOST_SHIFT 12U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_HOST_CLRMSK 0xFFFFFFFFFFFFEFFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_HOST_EN 0x0000000000001000ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PT_SHIFT 11U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PT_CLRMSK 0xFFFFFFFFFFFFF7FFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PT_EN 0x0000000000000800ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PD_SHIFT 10U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PD_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PD_EN 0x0000000000000400ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PC_SHIFT 9U +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PC_CLRMSK 0xFFFFFFFFFFFFFDFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_REQ_MMU_PC_EN 0x0000000000000200ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FRC_SHIFT 8U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FRC_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_FRC_EN 0x0000000000000100ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXE_SHIFT 7U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXE_CLRMSK 0xFFFFFFFFFFFFFF7FULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXE_EN 0x0000000000000080ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXD_SHIFT 6U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXD_CLRMSK 0xFFFFFFFFFFFFFFBFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_VXD_EN 0x0000000000000040ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_HOST_META_SHIFT 5U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_HOST_META_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_HOST_META_EN 0x0000000000000020ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_MMU_SHIFT 4U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_MMU_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_MMU_EN 0x0000000000000010ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_COMPUTE_SHIFT 3U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_COMPUTE_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_COMPUTE_EN 0x0000000000000008ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PIXEL_SHIFT 2U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PIXEL_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_PIXEL_EN 0x0000000000000004ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_TA_SHIFT 1U +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_TA_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_SLC_CTRL_BYPASS_DM_TA_EN 0x0000000000000002ULL +#define ROGUE_CR_SLC_CTRL_BYPASS_ALL_SHIFT 0U +#define ROGUE_CR_SLC_CTRL_BYPASS_ALL_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_SLC_CTRL_BYPASS_ALL_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_SLC_STATUS1 */ +#define ROGUE_CR_SLC_STATUS1 0x3870U +#define ROGUE_CR_SLC_STATUS1_MASKFULL 0x800003FF03FFFFFFULL +#define ROGUE_CR_SLC_STATUS1_PAUSED_SHIFT 63U +#define ROGUE_CR_SLC_STATUS1_PAUSED_CLRMSK 0x7FFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC_STATUS1_PAUSED_EN 0x8000000000000000ULL +#define ROGUE_CR_SLC_STATUS1_READS1_SHIFT 32U +#define ROGUE_CR_SLC_STATUS1_READS1_CLRMSK 0xFFFFFC00FFFFFFFFULL +#define ROGUE_CR_SLC_STATUS1_READS0_SHIFT 16U +#define ROGUE_CR_SLC_STATUS1_READS0_CLRMSK 0xFFFFFFFFFC00FFFFULL +#define ROGUE_CR_SLC_STATUS1_READS1_EXT_SHIFT 8U +#define ROGUE_CR_SLC_STATUS1_READS1_EXT_CLRMSK 0xFFFFFFFFFFFF00FFULL +#define ROGUE_CR_SLC_STATUS1_READS0_EXT_SHIFT 0U +#define ROGUE_CR_SLC_STATUS1_READS0_EXT_CLRMSK 0xFFFFFFFFFFFFFF00ULL + +/* Register ROGUE_CR_SLC_IDLE */ +#define ROGUE_CR_SLC_IDLE 0x3898U +#define ROGUE_CR_SLC_IDLE__XE_MEM__MASKFULL 0x00000000000003FFULL +#define ROGUE_CR_SLC_IDLE_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_SLC_IDLE_MH_SYSARB1_SHIFT 9U +#define ROGUE_CR_SLC_IDLE_MH_SYSARB1_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_SLC_IDLE_MH_SYSARB1_EN 0x00000200U +#define ROGUE_CR_SLC_IDLE_MH_SYSARB0_SHIFT 8U +#define ROGUE_CR_SLC_IDLE_MH_SYSARB0_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_SLC_IDLE_MH_SYSARB0_EN 0x00000100U +#define ROGUE_CR_SLC_IDLE_IMGBV4_SHIFT 7U +#define ROGUE_CR_SLC_IDLE_IMGBV4_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_SLC_IDLE_IMGBV4_EN 0x00000080U +#define ROGUE_CR_SLC_IDLE_CACHE_BANKS_SHIFT 6U +#define ROGUE_CR_SLC_IDLE_CACHE_BANKS_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SLC_IDLE_CACHE_BANKS_EN 0x00000040U +#define ROGUE_CR_SLC_IDLE_RBOFIFO_SHIFT 5U +#define ROGUE_CR_SLC_IDLE_RBOFIFO_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SLC_IDLE_RBOFIFO_EN 0x00000020U +#define ROGUE_CR_SLC_IDLE_FRC_CONV_SHIFT 4U +#define ROGUE_CR_SLC_IDLE_FRC_CONV_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SLC_IDLE_FRC_CONV_EN 0x00000010U +#define ROGUE_CR_SLC_IDLE_VXE_CONV_SHIFT 3U +#define ROGUE_CR_SLC_IDLE_VXE_CONV_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SLC_IDLE_VXE_CONV_EN 0x00000008U +#define ROGUE_CR_SLC_IDLE_VXD_CONV_SHIFT 2U +#define ROGUE_CR_SLC_IDLE_VXD_CONV_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SLC_IDLE_VXD_CONV_EN 0x00000004U +#define ROGUE_CR_SLC_IDLE_BIF1_CONV_SHIFT 1U +#define ROGUE_CR_SLC_IDLE_BIF1_CONV_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SLC_IDLE_BIF1_CONV_EN 0x00000002U +#define ROGUE_CR_SLC_IDLE_CBAR_SHIFT 0U +#define ROGUE_CR_SLC_IDLE_CBAR_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SLC_IDLE_CBAR_EN 0x00000001U + +/* Register ROGUE_CR_SLC_STATUS2 */ +#define ROGUE_CR_SLC_STATUS2 0x3908U +#define ROGUE_CR_SLC_STATUS2_MASKFULL 0x000003FF03FFFFFFULL +#define ROGUE_CR_SLC_STATUS2_READS3_SHIFT 32U +#define ROGUE_CR_SLC_STATUS2_READS3_CLRMSK 0xFFFFFC00FFFFFFFFULL +#define ROGUE_CR_SLC_STATUS2_READS2_SHIFT 16U +#define ROGUE_CR_SLC_STATUS2_READS2_CLRMSK 0xFFFFFFFFFC00FFFFULL +#define ROGUE_CR_SLC_STATUS2_READS3_EXT_SHIFT 8U +#define ROGUE_CR_SLC_STATUS2_READS3_EXT_CLRMSK 0xFFFFFFFFFFFF00FFULL +#define ROGUE_CR_SLC_STATUS2_READS2_EXT_SHIFT 0U +#define ROGUE_CR_SLC_STATUS2_READS2_EXT_CLRMSK 0xFFFFFFFFFFFFFF00ULL + +/* Register ROGUE_CR_SLC_CTRL_MISC2 */ +#define ROGUE_CR_SLC_CTRL_MISC2 0x3930U +#define ROGUE_CR_SLC_CTRL_MISC2_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SLC_CTRL_MISC2_SCRAMBLE_BITS_SHIFT 0U +#define ROGUE_CR_SLC_CTRL_MISC2_SCRAMBLE_BITS_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE */ +#define ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE 0x3938U +#define ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE_BYPASS_SHIFT 0U +#define ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE_BYPASS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SLC_CROSSBAR_LOAD_BALANCE_BYPASS_EN 0x00000001U + +/* Register ROGUE_CR_USC_UVS0_CHECKSUM */ +#define ROGUE_CR_USC_UVS0_CHECKSUM 0x5000U +#define ROGUE_CR_USC_UVS0_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS0_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS0_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVS1_CHECKSUM */ +#define ROGUE_CR_USC_UVS1_CHECKSUM 0x5008U +#define ROGUE_CR_USC_UVS1_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS1_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS1_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVS2_CHECKSUM */ +#define ROGUE_CR_USC_UVS2_CHECKSUM 0x5010U +#define ROGUE_CR_USC_UVS2_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS2_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS2_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVS3_CHECKSUM */ +#define ROGUE_CR_USC_UVS3_CHECKSUM 0x5018U +#define ROGUE_CR_USC_UVS3_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS3_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS3_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PPP_SIGNATURE */ +#define ROGUE_CR_PPP_SIGNATURE 0x5020U +#define ROGUE_CR_PPP_SIGNATURE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PPP_SIGNATURE_VALUE_SHIFT 0U +#define ROGUE_CR_PPP_SIGNATURE_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TE_SIGNATURE */ +#define ROGUE_CR_TE_SIGNATURE 0x5028U +#define ROGUE_CR_TE_SIGNATURE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TE_SIGNATURE_VALUE_SHIFT 0U +#define ROGUE_CR_TE_SIGNATURE_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TE_CHECKSUM */ +#define ROGUE_CR_TE_CHECKSUM 0x5110U +#define ROGUE_CR_TE_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TE_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_TE_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVB_CHECKSUM */ +#define ROGUE_CR_USC_UVB_CHECKSUM 0x5118U +#define ROGUE_CR_USC_UVB_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVB_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVB_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_VCE_CHECKSUM */ +#define ROGUE_CR_VCE_CHECKSUM 0x5030U +#define ROGUE_CR_VCE_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_VCE_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_VCE_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_ISP_PDS_CHECKSUM */ +#define ROGUE_CR_ISP_PDS_CHECKSUM 0x5038U +#define ROGUE_CR_ISP_PDS_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_ISP_PDS_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_ISP_PDS_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_ISP_TPF_CHECKSUM */ +#define ROGUE_CR_ISP_TPF_CHECKSUM 0x5040U +#define ROGUE_CR_ISP_TPF_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_ISP_TPF_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_ISP_TPF_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TFPU_PLANE0_CHECKSUM */ +#define ROGUE_CR_TFPU_PLANE0_CHECKSUM 0x5048U +#define ROGUE_CR_TFPU_PLANE0_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TFPU_PLANE0_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_TFPU_PLANE0_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TFPU_PLANE1_CHECKSUM */ +#define ROGUE_CR_TFPU_PLANE1_CHECKSUM 0x5050U +#define ROGUE_CR_TFPU_PLANE1_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TFPU_PLANE1_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_TFPU_PLANE1_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PBE_CHECKSUM */ +#define ROGUE_CR_PBE_CHECKSUM 0x5058U +#define ROGUE_CR_PBE_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PBE_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_PBE_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PDS_DOUTM_STM_SIGNATURE */ +#define ROGUE_CR_PDS_DOUTM_STM_SIGNATURE 0x5060U +#define ROGUE_CR_PDS_DOUTM_STM_SIGNATURE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PDS_DOUTM_STM_SIGNATURE_VALUE_SHIFT 0U +#define ROGUE_CR_PDS_DOUTM_STM_SIGNATURE_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_IFPU_ISP_CHECKSUM */ +#define ROGUE_CR_IFPU_ISP_CHECKSUM 0x5068U +#define ROGUE_CR_IFPU_ISP_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_IFPU_ISP_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_IFPU_ISP_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVS4_CHECKSUM */ +#define ROGUE_CR_USC_UVS4_CHECKSUM 0x5100U +#define ROGUE_CR_USC_UVS4_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS4_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS4_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_UVS5_CHECKSUM */ +#define ROGUE_CR_USC_UVS5_CHECKSUM 0x5108U +#define ROGUE_CR_USC_UVS5_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_UVS5_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_USC_UVS5_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PPP_CLIP_CHECKSUM */ +#define ROGUE_CR_PPP_CLIP_CHECKSUM 0x5120U +#define ROGUE_CR_PPP_CLIP_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PPP_CLIP_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_PPP_CLIP_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_TA_PHASE */ +#define ROGUE_CR_PERF_TA_PHASE 0x6008U +#define ROGUE_CR_PERF_TA_PHASE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_TA_PHASE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_TA_PHASE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_3D_PHASE */ +#define ROGUE_CR_PERF_3D_PHASE 0x6010U +#define ROGUE_CR_PERF_3D_PHASE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_3D_PHASE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_3D_PHASE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_COMPUTE_PHASE */ +#define ROGUE_CR_PERF_COMPUTE_PHASE 0x6018U +#define ROGUE_CR_PERF_COMPUTE_PHASE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_COMPUTE_PHASE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_COMPUTE_PHASE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_TA_CYCLE */ +#define ROGUE_CR_PERF_TA_CYCLE 0x6020U +#define ROGUE_CR_PERF_TA_CYCLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_TA_CYCLE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_TA_CYCLE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_3D_CYCLE */ +#define ROGUE_CR_PERF_3D_CYCLE 0x6028U +#define ROGUE_CR_PERF_3D_CYCLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_3D_CYCLE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_3D_CYCLE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_COMPUTE_CYCLE */ +#define ROGUE_CR_PERF_COMPUTE_CYCLE 0x6030U +#define ROGUE_CR_PERF_COMPUTE_CYCLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_COMPUTE_CYCLE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_COMPUTE_CYCLE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_TA_OR_3D_CYCLE */ +#define ROGUE_CR_PERF_TA_OR_3D_CYCLE 0x6038U +#define ROGUE_CR_PERF_TA_OR_3D_CYCLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_TA_OR_3D_CYCLE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_TA_OR_3D_CYCLE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_INITIAL_TA_CYCLE */ +#define ROGUE_CR_PERF_INITIAL_TA_CYCLE 0x6040U +#define ROGUE_CR_PERF_INITIAL_TA_CYCLE_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_INITIAL_TA_CYCLE_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_INITIAL_TA_CYCLE_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC0_READ_STALL */ +#define ROGUE_CR_PERF_SLC0_READ_STALL 0x60B8U +#define ROGUE_CR_PERF_SLC0_READ_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC0_READ_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC0_READ_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC0_WRITE_STALL */ +#define ROGUE_CR_PERF_SLC0_WRITE_STALL 0x60C0U +#define ROGUE_CR_PERF_SLC0_WRITE_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC0_WRITE_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC0_WRITE_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC1_READ_STALL */ +#define ROGUE_CR_PERF_SLC1_READ_STALL 0x60E0U +#define ROGUE_CR_PERF_SLC1_READ_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC1_READ_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC1_READ_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC1_WRITE_STALL */ +#define ROGUE_CR_PERF_SLC1_WRITE_STALL 0x60E8U +#define ROGUE_CR_PERF_SLC1_WRITE_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC1_WRITE_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC1_WRITE_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC2_READ_STALL */ +#define ROGUE_CR_PERF_SLC2_READ_STALL 0x6158U +#define ROGUE_CR_PERF_SLC2_READ_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC2_READ_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC2_READ_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC2_WRITE_STALL */ +#define ROGUE_CR_PERF_SLC2_WRITE_STALL 0x6160U +#define ROGUE_CR_PERF_SLC2_WRITE_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC2_WRITE_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC2_WRITE_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC3_READ_STALL */ +#define ROGUE_CR_PERF_SLC3_READ_STALL 0x6180U +#define ROGUE_CR_PERF_SLC3_READ_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC3_READ_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC3_READ_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_SLC3_WRITE_STALL */ +#define ROGUE_CR_PERF_SLC3_WRITE_STALL 0x6188U +#define ROGUE_CR_PERF_SLC3_WRITE_STALL_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_SLC3_WRITE_STALL_COUNT_SHIFT 0U +#define ROGUE_CR_PERF_SLC3_WRITE_STALL_COUNT_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PERF_3D_SPINUP */ +#define ROGUE_CR_PERF_3D_SPINUP 0x6220U +#define ROGUE_CR_PERF_3D_SPINUP_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PERF_3D_SPINUP_CYCLES_SHIFT 0U +#define ROGUE_CR_PERF_3D_SPINUP_CYCLES_CLRMSK 0x00000000U + +/* Register ROGUE_CR_AXI_ACE_LITE_CONFIGURATION */ +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION 0x38C0U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_MASKFULL 0x00003FFFFFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ENABLE_FENCE_OUT_SHIFT 45U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ENABLE_FENCE_OUT_CLRMSK 0xFFFFDFFFFFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ENABLE_FENCE_OUT_EN 0x0000200000000000ULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_OSID_SECURITY_SHIFT 37U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_OSID_SECURITY_CLRMSK 0xFFFFE01FFFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITELINEUNIQUE_SHIFT 36U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITELINEUNIQUE_CLRMSK \ + 0xFFFFFFEFFFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITELINEUNIQUE_EN \ + 0x0000001000000000ULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITE_SHIFT 35U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITE_CLRMSK 0xFFFFFFF7FFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_WRITE_EN 0x0000000800000000ULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_READ_SHIFT 34U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_READ_CLRMSK 0xFFFFFFFBFFFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_DISABLE_COHERENT_READ_EN 0x0000000400000000ULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_SHIFT 30U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_CACHE_MAINTENANCE_CLRMSK 0xFFFFFFFC3FFFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_SHIFT 26U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_COHERENT_CLRMSK 0xFFFFFFFFC3FFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_SHIFT 22U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_COHERENT_CLRMSK 0xFFFFFFFFFC3FFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_BARRIER_SHIFT 20U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_BARRIER_CLRMSK 0xFFFFFFFFFFCFFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_BARRIER_SHIFT 18U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_BARRIER_CLRMSK 0xFFFFFFFFFFF3FFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_SHIFT 16U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_CACHE_MAINTENANCE_CLRMSK 0xFFFFFFFFFFFCFFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_SHIFT 14U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_COHERENT_CLRMSK 0xFFFFFFFFFFFF3FFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_SHIFT 12U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_COHERENT_CLRMSK 0xFFFFFFFFFFFFCFFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_SHIFT 10U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARDOMAIN_NON_SNOOPING_CLRMSK 0xFFFFFFFFFFFFF3FFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_SHIFT 8U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWDOMAIN_NON_SNOOPING_CLRMSK 0xFFFFFFFFFFFFFCFFULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_NON_SNOOPING_SHIFT 4U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_ARCACHE_NON_SNOOPING_CLRMSK 0xFFFFFFFFFFFFFF0FULL +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_NON_SNOOPING_SHIFT 0U +#define ROGUE_CR_AXI_ACE_LITE_CONFIGURATION_AWCACHE_NON_SNOOPING_CLRMSK 0xFFFFFFFFFFFFFFF0ULL + +/* Register ROGUE_CR_POWER_ESTIMATE_RESULT */ +#define ROGUE_CR_POWER_ESTIMATE_RESULT 0x6328U +#define ROGUE_CR_POWER_ESTIMATE_RESULT_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_POWER_ESTIMATE_RESULT_VALUE_SHIFT 0U +#define ROGUE_CR_POWER_ESTIMATE_RESULT_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TA_PERF */ +#define ROGUE_CR_TA_PERF 0x7600U +#define ROGUE_CR_TA_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_TA_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_TA_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_TA_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_TA_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_TA_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_TA_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_TA_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_TA_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_TA_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_TA_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_TA_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_TA_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_TA_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_TA_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_TA_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_TA_PERF_SELECT0 */ +#define ROGUE_CR_TA_PERF_SELECT0 0x7608U +#define ROGUE_CR_TA_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TA_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TA_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TA_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_TA_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TA_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TA_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TA_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TA_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TA_PERF_SELECT1 */ +#define ROGUE_CR_TA_PERF_SELECT1 0x7610U +#define ROGUE_CR_TA_PERF_SELECT1_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TA_PERF_SELECT1_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TA_PERF_SELECT1_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT1_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TA_PERF_SELECT1_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT1_MODE_SHIFT 21U +#define ROGUE_CR_TA_PERF_SELECT1_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT1_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TA_PERF_SELECT1_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TA_PERF_SELECT1_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TA_PERF_SELECT1_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TA_PERF_SELECT1_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TA_PERF_SELECT2 */ +#define ROGUE_CR_TA_PERF_SELECT2 0x7618U +#define ROGUE_CR_TA_PERF_SELECT2_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TA_PERF_SELECT2_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TA_PERF_SELECT2_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT2_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TA_PERF_SELECT2_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT2_MODE_SHIFT 21U +#define ROGUE_CR_TA_PERF_SELECT2_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT2_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TA_PERF_SELECT2_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TA_PERF_SELECT2_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TA_PERF_SELECT2_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TA_PERF_SELECT2_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TA_PERF_SELECT3 */ +#define ROGUE_CR_TA_PERF_SELECT3 0x7620U +#define ROGUE_CR_TA_PERF_SELECT3_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TA_PERF_SELECT3_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TA_PERF_SELECT3_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT3_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TA_PERF_SELECT3_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT3_MODE_SHIFT 21U +#define ROGUE_CR_TA_PERF_SELECT3_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TA_PERF_SELECT3_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TA_PERF_SELECT3_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TA_PERF_SELECT3_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TA_PERF_SELECT3_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TA_PERF_SELECT3_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TA_PERF_SELECTED_BITS */ +#define ROGUE_CR_TA_PERF_SELECTED_BITS 0x7648U +#define ROGUE_CR_TA_PERF_SELECTED_BITS_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG3_SHIFT 48U +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG3_CLRMSK 0x0000FFFFFFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG2_SHIFT 32U +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG2_CLRMSK 0xFFFF0000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG1_SHIFT 16U +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG1_CLRMSK 0xFFFFFFFF0000FFFFULL +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG0_SHIFT 0U +#define ROGUE_CR_TA_PERF_SELECTED_BITS_REG0_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TA_PERF_COUNTER_0 */ +#define ROGUE_CR_TA_PERF_COUNTER_0 0x7650U +#define ROGUE_CR_TA_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_TA_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TA_PERF_COUNTER_1 */ +#define ROGUE_CR_TA_PERF_COUNTER_1 0x7658U +#define ROGUE_CR_TA_PERF_COUNTER_1_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_COUNTER_1_REG_SHIFT 0U +#define ROGUE_CR_TA_PERF_COUNTER_1_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TA_PERF_COUNTER_2 */ +#define ROGUE_CR_TA_PERF_COUNTER_2 0x7660U +#define ROGUE_CR_TA_PERF_COUNTER_2_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_COUNTER_2_REG_SHIFT 0U +#define ROGUE_CR_TA_PERF_COUNTER_2_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TA_PERF_COUNTER_3 */ +#define ROGUE_CR_TA_PERF_COUNTER_3 0x7668U +#define ROGUE_CR_TA_PERF_COUNTER_3_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TA_PERF_COUNTER_3_REG_SHIFT 0U +#define ROGUE_CR_TA_PERF_COUNTER_3_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_RASTERISATION_PERF */ +#define ROGUE_CR_RASTERISATION_PERF 0x7700U +#define ROGUE_CR_RASTERISATION_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_RASTERISATION_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_RASTERISATION_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_RASTERISATION_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_RASTERISATION_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_RASTERISATION_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_RASTERISATION_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_RASTERISATION_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_RASTERISATION_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_RASTERISATION_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_RASTERISATION_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_RASTERISATION_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_RASTERISATION_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_RASTERISATION_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_RASTERISATION_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_RASTERISATION_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_RASTERISATION_PERF_SELECT0 */ +#define ROGUE_CR_RASTERISATION_PERF_SELECT0 0x7708U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_RASTERISATION_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_RASTERISATION_PERF_COUNTER_0 */ +#define ROGUE_CR_RASTERISATION_PERF_COUNTER_0 0x7750U +#define ROGUE_CR_RASTERISATION_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_RASTERISATION_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_RASTERISATION_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_HUB_BIFPMCACHE_PERF */ +#define ROGUE_CR_HUB_BIFPMCACHE_PERF 0x7800U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0 */ +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0 0x7808U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_HUB_BIFPMCACHE_PERF_COUNTER_0 */ +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_COUNTER_0 0x7850U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_HUB_BIFPMCACHE_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TPU_MCU_L0_PERF */ +#define ROGUE_CR_TPU_MCU_L0_PERF 0x7900U +#define ROGUE_CR_TPU_MCU_L0_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_TPU_MCU_L0_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_TPU_MCU_L0_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_TPU_MCU_L0_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_TPU_MCU_L0_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_TPU_MCU_L0_PERF_SELECT0 */ +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0 0x7908U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TPU_MCU_L0_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TPU_MCU_L0_PERF_COUNTER_0 */ +#define ROGUE_CR_TPU_MCU_L0_PERF_COUNTER_0 0x7950U +#define ROGUE_CR_TPU_MCU_L0_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TPU_MCU_L0_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_TPU_MCU_L0_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_USC_PERF */ +#define ROGUE_CR_USC_PERF 0x8100U +#define ROGUE_CR_USC_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_USC_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_USC_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_USC_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_USC_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_USC_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_USC_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_USC_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_USC_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_USC_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_USC_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_USC_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_USC_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_USC_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_USC_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_USC_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_USC_PERF_SELECT0 */ +#define ROGUE_CR_USC_PERF_SELECT0 0x8108U +#define ROGUE_CR_USC_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_USC_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_USC_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_USC_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_USC_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_USC_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_USC_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_USC_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_USC_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_USC_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_USC_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_USC_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_USC_PERF_COUNTER_0 */ +#define ROGUE_CR_USC_PERF_COUNTER_0 0x8150U +#define ROGUE_CR_USC_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_USC_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_USC_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_JONES_IDLE */ +#define ROGUE_CR_JONES_IDLE 0x8328U +#define ROGUE_CR_JONES_IDLE_MASKFULL 0x0000000000007FFFULL +#define ROGUE_CR_JONES_IDLE_TDM_SHIFT 14U +#define ROGUE_CR_JONES_IDLE_TDM_CLRMSK 0xFFFFBFFFU +#define ROGUE_CR_JONES_IDLE_TDM_EN 0x00004000U +#define ROGUE_CR_JONES_IDLE_FB_CDC_TLA_SHIFT 13U +#define ROGUE_CR_JONES_IDLE_FB_CDC_TLA_CLRMSK 0xFFFFDFFFU +#define ROGUE_CR_JONES_IDLE_FB_CDC_TLA_EN 0x00002000U +#define ROGUE_CR_JONES_IDLE_FB_CDC_SHIFT 12U +#define ROGUE_CR_JONES_IDLE_FB_CDC_CLRMSK 0xFFFFEFFFU +#define ROGUE_CR_JONES_IDLE_FB_CDC_EN 0x00001000U +#define ROGUE_CR_JONES_IDLE_MMU_SHIFT 11U +#define ROGUE_CR_JONES_IDLE_MMU_CLRMSK 0xFFFFF7FFU +#define ROGUE_CR_JONES_IDLE_MMU_EN 0x00000800U +#define ROGUE_CR_JONES_IDLE_TLA_SHIFT 10U +#define ROGUE_CR_JONES_IDLE_TLA_CLRMSK 0xFFFFFBFFU +#define ROGUE_CR_JONES_IDLE_TLA_EN 0x00000400U +#define ROGUE_CR_JONES_IDLE_GARTEN_SHIFT 9U +#define ROGUE_CR_JONES_IDLE_GARTEN_CLRMSK 0xFFFFFDFFU +#define ROGUE_CR_JONES_IDLE_GARTEN_EN 0x00000200U +#define ROGUE_CR_JONES_IDLE_HOSTIF_SHIFT 8U +#define ROGUE_CR_JONES_IDLE_HOSTIF_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_JONES_IDLE_HOSTIF_EN 0x00000100U +#define ROGUE_CR_JONES_IDLE_SOCIF_SHIFT 7U +#define ROGUE_CR_JONES_IDLE_SOCIF_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_JONES_IDLE_SOCIF_EN 0x00000080U +#define ROGUE_CR_JONES_IDLE_TILING_SHIFT 6U +#define ROGUE_CR_JONES_IDLE_TILING_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_JONES_IDLE_TILING_EN 0x00000040U +#define ROGUE_CR_JONES_IDLE_IPP_SHIFT 5U +#define ROGUE_CR_JONES_IDLE_IPP_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_JONES_IDLE_IPP_EN 0x00000020U +#define ROGUE_CR_JONES_IDLE_USCS_SHIFT 4U +#define ROGUE_CR_JONES_IDLE_USCS_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_JONES_IDLE_USCS_EN 0x00000010U +#define ROGUE_CR_JONES_IDLE_PM_SHIFT 3U +#define ROGUE_CR_JONES_IDLE_PM_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_JONES_IDLE_PM_EN 0x00000008U +#define ROGUE_CR_JONES_IDLE_CDM_SHIFT 2U +#define ROGUE_CR_JONES_IDLE_CDM_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_JONES_IDLE_CDM_EN 0x00000004U +#define ROGUE_CR_JONES_IDLE_VDM_SHIFT 1U +#define ROGUE_CR_JONES_IDLE_VDM_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_JONES_IDLE_VDM_EN 0x00000002U +#define ROGUE_CR_JONES_IDLE_BIF_SHIFT 0U +#define ROGUE_CR_JONES_IDLE_BIF_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_JONES_IDLE_BIF_EN 0x00000001U + +/* Register ROGUE_CR_TORNADO_PERF */ +#define ROGUE_CR_TORNADO_PERF 0x8228U +#define ROGUE_CR_TORNADO_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_TORNADO_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_TORNADO_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_TORNADO_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_TORNADO_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_TORNADO_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_TORNADO_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_TORNADO_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_TORNADO_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_TORNADO_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_TORNADO_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_TORNADO_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_TORNADO_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_TORNADO_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_TORNADO_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_TORNADO_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_TORNADO_PERF_SELECT0 */ +#define ROGUE_CR_TORNADO_PERF_SELECT0 0x8230U +#define ROGUE_CR_TORNADO_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TORNADO_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TORNADO_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_TORNADO_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TORNADO_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_TORNADO_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TORNADO_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TORNADO_PERF_COUNTER_0 */ +#define ROGUE_CR_TORNADO_PERF_COUNTER_0 0x8268U +#define ROGUE_CR_TORNADO_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TORNADO_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_TORNADO_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_TEXAS_PERF */ +#define ROGUE_CR_TEXAS_PERF 0x8290U +#define ROGUE_CR_TEXAS_PERF_MASKFULL 0x000000000000007FULL +#define ROGUE_CR_TEXAS_PERF_CLR_5_SHIFT 6U +#define ROGUE_CR_TEXAS_PERF_CLR_5_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_TEXAS_PERF_CLR_5_EN 0x00000040U +#define ROGUE_CR_TEXAS_PERF_CLR_4_SHIFT 5U +#define ROGUE_CR_TEXAS_PERF_CLR_4_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_TEXAS_PERF_CLR_4_EN 0x00000020U +#define ROGUE_CR_TEXAS_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_TEXAS_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_TEXAS_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_TEXAS_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_TEXAS_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_TEXAS_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_TEXAS_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_TEXAS_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_TEXAS_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_TEXAS_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_TEXAS_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_TEXAS_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_TEXAS_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_TEXAS_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_TEXAS_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_TEXAS_PERF_SELECT0 */ +#define ROGUE_CR_TEXAS_PERF_SELECT0 0x8298U +#define ROGUE_CR_TEXAS_PERF_SELECT0_MASKFULL 0x3FFF3FFF803FFFFFULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_TEXAS_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_TEXAS_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_MODE_SHIFT 31U +#define ROGUE_CR_TEXAS_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_MODE_EN 0x0000000080000000ULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_TEXAS_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFC0FFFFULL +#define ROGUE_CR_TEXAS_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_TEXAS_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_TEXAS_PERF_COUNTER_0 */ +#define ROGUE_CR_TEXAS_PERF_COUNTER_0 0x82D8U +#define ROGUE_CR_TEXAS_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_TEXAS_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_TEXAS_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_JONES_PERF */ +#define ROGUE_CR_JONES_PERF 0x8330U +#define ROGUE_CR_JONES_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_JONES_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_JONES_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_JONES_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_JONES_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_JONES_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_JONES_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_JONES_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_JONES_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_JONES_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_JONES_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_JONES_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_JONES_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_JONES_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_JONES_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_JONES_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_JONES_PERF_SELECT0 */ +#define ROGUE_CR_JONES_PERF_SELECT0 0x8338U +#define ROGUE_CR_JONES_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_JONES_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_JONES_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_JONES_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_JONES_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_JONES_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_JONES_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_JONES_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_JONES_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_JONES_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_JONES_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_JONES_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_JONES_PERF_COUNTER_0 */ +#define ROGUE_CR_JONES_PERF_COUNTER_0 0x8368U +#define ROGUE_CR_JONES_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_JONES_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_JONES_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_BLACKPEARL_PERF */ +#define ROGUE_CR_BLACKPEARL_PERF 0x8400U +#define ROGUE_CR_BLACKPEARL_PERF_MASKFULL 0x000000000000007FULL +#define ROGUE_CR_BLACKPEARL_PERF_CLR_5_SHIFT 6U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_5_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_BLACKPEARL_PERF_CLR_5_EN 0x00000040U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_4_SHIFT 5U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_4_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_BLACKPEARL_PERF_CLR_4_EN 0x00000020U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BLACKPEARL_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BLACKPEARL_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_BLACKPEARL_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BLACKPEARL_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_BLACKPEARL_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_BLACKPEARL_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BLACKPEARL_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_BLACKPEARL_PERF_SELECT0 */ +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0 0x8408U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_MASKFULL 0x3FFF3FFF803FFFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_MODE_SHIFT 31U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_MODE_EN 0x0000000080000000ULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFC0FFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_BLACKPEARL_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_BLACKPEARL_PERF_COUNTER_0 */ +#define ROGUE_CR_BLACKPEARL_PERF_COUNTER_0 0x8448U +#define ROGUE_CR_BLACKPEARL_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_BLACKPEARL_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_BLACKPEARL_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_PBE_PERF */ +#define ROGUE_CR_PBE_PERF 0x8478U +#define ROGUE_CR_PBE_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_PBE_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_PBE_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_PBE_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_PBE_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_PBE_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_PBE_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_PBE_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_PBE_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_PBE_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_PBE_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_PBE_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_PBE_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_PBE_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_PBE_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_PBE_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_PBE_PERF_SELECT0 */ +#define ROGUE_CR_PBE_PERF_SELECT0 0x8480U +#define ROGUE_CR_PBE_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_PBE_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_PBE_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_PBE_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_PBE_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_PBE_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_PBE_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_PBE_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_PBE_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_PBE_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_PBE_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_PBE_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_PBE_PERF_COUNTER_0 */ +#define ROGUE_CR_PBE_PERF_COUNTER_0 0x84B0U +#define ROGUE_CR_PBE_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_PBE_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_PBE_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_OCP_REVINFO */ +#define ROGUE_CR_OCP_REVINFO 0x9000U +#define ROGUE_CR_OCP_REVINFO_MASKFULL 0x00000007FFFFFFFFULL +#define ROGUE_CR_OCP_REVINFO_HWINFO_SYSBUS_SHIFT 33U +#define ROGUE_CR_OCP_REVINFO_HWINFO_SYSBUS_CLRMSK 0xFFFFFFF9FFFFFFFFULL +#define ROGUE_CR_OCP_REVINFO_HWINFO_MEMBUS_SHIFT 32U +#define ROGUE_CR_OCP_REVINFO_HWINFO_MEMBUS_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_OCP_REVINFO_HWINFO_MEMBUS_EN 0x0000000100000000ULL +#define ROGUE_CR_OCP_REVINFO_REVISION_SHIFT 0U +#define ROGUE_CR_OCP_REVINFO_REVISION_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_OCP_SYSCONFIG */ +#define ROGUE_CR_OCP_SYSCONFIG 0x9010U +#define ROGUE_CR_OCP_SYSCONFIG_MASKFULL 0x0000000000000FFFULL +#define ROGUE_CR_OCP_SYSCONFIG_DUST2_STANDBY_MODE_SHIFT 10U +#define ROGUE_CR_OCP_SYSCONFIG_DUST2_STANDBY_MODE_CLRMSK 0xFFFFF3FFU +#define ROGUE_CR_OCP_SYSCONFIG_DUST1_STANDBY_MODE_SHIFT 8U +#define ROGUE_CR_OCP_SYSCONFIG_DUST1_STANDBY_MODE_CLRMSK 0xFFFFFCFFU +#define ROGUE_CR_OCP_SYSCONFIG_DUST0_STANDBY_MODE_SHIFT 6U +#define ROGUE_CR_OCP_SYSCONFIG_DUST0_STANDBY_MODE_CLRMSK 0xFFFFFF3FU +#define ROGUE_CR_OCP_SYSCONFIG_RASCAL_STANDBYMODE_SHIFT 4U +#define ROGUE_CR_OCP_SYSCONFIG_RASCAL_STANDBYMODE_CLRMSK 0xFFFFFFCFU +#define ROGUE_CR_OCP_SYSCONFIG_STANDBY_MODE_SHIFT 2U +#define ROGUE_CR_OCP_SYSCONFIG_STANDBY_MODE_CLRMSK 0xFFFFFFF3U +#define ROGUE_CR_OCP_SYSCONFIG_IDLE_MODE_SHIFT 0U +#define ROGUE_CR_OCP_SYSCONFIG_IDLE_MODE_CLRMSK 0xFFFFFFFCU + +/* Register ROGUE_CR_OCP_IRQSTATUS_RAW_0 */ +#define ROGUE_CR_OCP_IRQSTATUS_RAW_0 0x9020U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_0_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_RAW_0_INIT_MINTERRUPT_RAW_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_0_INIT_MINTERRUPT_RAW_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_RAW_0_INIT_MINTERRUPT_RAW_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQSTATUS_RAW_1 */ +#define ROGUE_CR_OCP_IRQSTATUS_RAW_1 0x9028U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_1_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_RAW_1_TARGET_SINTERRUPT_RAW_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_1_TARGET_SINTERRUPT_RAW_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_RAW_1_TARGET_SINTERRUPT_RAW_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQSTATUS_RAW_2 */ +#define ROGUE_CR_OCP_IRQSTATUS_RAW_2 0x9030U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_2_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_RAW_2_RGX_IRQ_RAW_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_RAW_2_RGX_IRQ_RAW_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_RAW_2_RGX_IRQ_RAW_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQSTATUS_0 */ +#define ROGUE_CR_OCP_IRQSTATUS_0 0x9038U +#define ROGUE_CR_OCP_IRQSTATUS_0_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_0_INIT_MINTERRUPT_STATUS_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_0_INIT_MINTERRUPT_STATUS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_0_INIT_MINTERRUPT_STATUS_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQSTATUS_1 */ +#define ROGUE_CR_OCP_IRQSTATUS_1 0x9040U +#define ROGUE_CR_OCP_IRQSTATUS_1_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_1_TARGET_SINTERRUPT_STATUS_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_1_TARGET_SINTERRUPT_STATUS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_1_TARGET_SINTERRUPT_STATUS_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQSTATUS_2 */ +#define ROGUE_CR_OCP_IRQSTATUS_2 0x9048U +#define ROGUE_CR_OCP_IRQSTATUS_2_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQSTATUS_2_RGX_IRQ_STATUS_SHIFT 0U +#define ROGUE_CR_OCP_IRQSTATUS_2_RGX_IRQ_STATUS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQSTATUS_2_RGX_IRQ_STATUS_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_SET_0 */ +#define ROGUE_CR_OCP_IRQENABLE_SET_0 0x9050U +#define ROGUE_CR_OCP_IRQENABLE_SET_0_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_SET_0_INIT_MINTERRUPT_ENABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_SET_0_INIT_MINTERRUPT_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_SET_0_INIT_MINTERRUPT_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_SET_1 */ +#define ROGUE_CR_OCP_IRQENABLE_SET_1 0x9058U +#define ROGUE_CR_OCP_IRQENABLE_SET_1_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_SET_1_TARGET_SINTERRUPT_ENABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_SET_1_TARGET_SINTERRUPT_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_SET_1_TARGET_SINTERRUPT_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_SET_2 */ +#define ROGUE_CR_OCP_IRQENABLE_SET_2 0x9060U +#define ROGUE_CR_OCP_IRQENABLE_SET_2_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_SET_2_RGX_IRQ_ENABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_SET_2_RGX_IRQ_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_SET_2_RGX_IRQ_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_CLR_0 */ +#define ROGUE_CR_OCP_IRQENABLE_CLR_0 0x9068U +#define ROGUE_CR_OCP_IRQENABLE_CLR_0_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_CLR_0_INIT_MINTERRUPT_DISABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_CLR_0_INIT_MINTERRUPT_DISABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_CLR_0_INIT_MINTERRUPT_DISABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_CLR_1 */ +#define ROGUE_CR_OCP_IRQENABLE_CLR_1 0x9070U +#define ROGUE_CR_OCP_IRQENABLE_CLR_1_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_CLR_1_TARGET_SINTERRUPT_DISABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_CLR_1_TARGET_SINTERRUPT_DISABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_CLR_1_TARGET_SINTERRUPT_DISABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQENABLE_CLR_2 */ +#define ROGUE_CR_OCP_IRQENABLE_CLR_2 0x9078U +#define ROGUE_CR_OCP_IRQENABLE_CLR_2_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_IRQENABLE_CLR_2_RGX_IRQ_DISABLE_SHIFT 0U +#define ROGUE_CR_OCP_IRQENABLE_CLR_2_RGX_IRQ_DISABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_IRQENABLE_CLR_2_RGX_IRQ_DISABLE_EN 0x00000001U + +/* Register ROGUE_CR_OCP_IRQ_EVENT */ +#define ROGUE_CR_OCP_IRQ_EVENT 0x9080U +#define ROGUE_CR_OCP_IRQ_EVENT_MASKFULL 0x00000000000FFFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNEXPECTED_RDATA_SHIFT 19U +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNEXPECTED_RDATA_CLRMSK 0xFFFFFFFFFFF7FFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNEXPECTED_RDATA_EN 0x0000000000080000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNSUPPORTED_MCMD_SHIFT 18U +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNSUPPORTED_MCMD_CLRMSK 0xFFFFFFFFFFFBFFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETH_RCVD_UNSUPPORTED_MCMD_EN 0x0000000000040000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNEXPECTED_RDATA_SHIFT 17U +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNEXPECTED_RDATA_CLRMSK 0xFFFFFFFFFFFDFFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNEXPECTED_RDATA_EN 0x0000000000020000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNSUPPORTED_MCMD_SHIFT 16U +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNSUPPORTED_MCMD_CLRMSK 0xFFFFFFFFFFFEFFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_TARGETS_RCVD_UNSUPPORTED_MCMD_EN 0x0000000000010000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_IMG_PAGE_BOUNDARY_CROSS_SHIFT 15U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_IMG_PAGE_BOUNDARY_CROSS_CLRMSK 0xFFFFFFFFFFFF7FFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_IMG_PAGE_BOUNDARY_CROSS_EN 0x0000000000008000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_RESP_ERR_FAIL_SHIFT 14U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_RESP_ERR_FAIL_CLRMSK 0xFFFFFFFFFFFFBFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_RESP_ERR_FAIL_EN 0x0000000000004000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_UNUSED_TAGID_SHIFT 13U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_UNUSED_TAGID_CLRMSK 0xFFFFFFFFFFFFDFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RCVD_UNUSED_TAGID_EN 0x0000000000002000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RDATA_FIFO_OVERFILL_SHIFT 12U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RDATA_FIFO_OVERFILL_CLRMSK 0xFFFFFFFFFFFFEFFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT3_RDATA_FIFO_OVERFILL_EN 0x0000000000001000ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_IMG_PAGE_BOUNDARY_CROSS_SHIFT 11U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_IMG_PAGE_BOUNDARY_CROSS_CLRMSK 0xFFFFFFFFFFFFF7FFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_IMG_PAGE_BOUNDARY_CROSS_EN 0x0000000000000800ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_RESP_ERR_FAIL_SHIFT 10U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_RESP_ERR_FAIL_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_RESP_ERR_FAIL_EN 0x0000000000000400ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_UNUSED_TAGID_SHIFT 9U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_UNUSED_TAGID_CLRMSK 0xFFFFFFFFFFFFFDFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RCVD_UNUSED_TAGID_EN 0x0000000000000200ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RDATA_FIFO_OVERFILL_SHIFT 8U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RDATA_FIFO_OVERFILL_CLRMSK 0xFFFFFFFFFFFFFEFFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT2_RDATA_FIFO_OVERFILL_EN 0x0000000000000100ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_IMG_PAGE_BOUNDARY_CROSS_SHIFT 7U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_IMG_PAGE_BOUNDARY_CROSS_CLRMSK 0xFFFFFFFFFFFFFF7FULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_IMG_PAGE_BOUNDARY_CROSS_EN 0x0000000000000080ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_RESP_ERR_FAIL_SHIFT 6U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_RESP_ERR_FAIL_CLRMSK 0xFFFFFFFFFFFFFFBFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_RESP_ERR_FAIL_EN 0x0000000000000040ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_UNUSED_TAGID_SHIFT 5U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_UNUSED_TAGID_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RCVD_UNUSED_TAGID_EN 0x0000000000000020ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RDATA_FIFO_OVERFILL_SHIFT 4U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RDATA_FIFO_OVERFILL_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT1_RDATA_FIFO_OVERFILL_EN 0x0000000000000010ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_IMG_PAGE_BOUNDARY_CROSS_SHIFT 3U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_IMG_PAGE_BOUNDARY_CROSS_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_IMG_PAGE_BOUNDARY_CROSS_EN 0x0000000000000008ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_RESP_ERR_FAIL_SHIFT 2U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_RESP_ERR_FAIL_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_RESP_ERR_FAIL_EN 0x0000000000000004ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_UNUSED_TAGID_SHIFT 1U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_UNUSED_TAGID_CLRMSK 0xFFFFFFFFFFFFFFFDULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RCVD_UNUSED_TAGID_EN 0x0000000000000002ULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RDATA_FIFO_OVERFILL_SHIFT 0U +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RDATA_FIFO_OVERFILL_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_OCP_IRQ_EVENT_INIT0_RDATA_FIFO_OVERFILL_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_OCP_DEBUG_CONFIG */ +#define ROGUE_CR_OCP_DEBUG_CONFIG 0x9088U +#define ROGUE_CR_OCP_DEBUG_CONFIG_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_OCP_DEBUG_CONFIG_REG_SHIFT 0U +#define ROGUE_CR_OCP_DEBUG_CONFIG_REG_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_OCP_DEBUG_CONFIG_REG_EN 0x00000001U + +/* Register ROGUE_CR_OCP_DEBUG_STATUS */ +#define ROGUE_CR_OCP_DEBUG_STATUS 0x9090U +#define ROGUE_CR_OCP_DEBUG_STATUS_MASKFULL 0x001F1F77FFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SDISCACK_SHIFT 51U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SDISCACK_CLRMSK 0xFFE7FFFFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SCONNECT_SHIFT 50U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SCONNECT_CLRMSK 0xFFFBFFFFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SCONNECT_EN 0x0004000000000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_MCONNECT_SHIFT 48U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_MCONNECT_CLRMSK 0xFFFCFFFFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SDISCACK_SHIFT 43U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SDISCACK_CLRMSK 0xFFFFE7FFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SCONNECT_SHIFT 42U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SCONNECT_CLRMSK 0xFFFFFBFFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SCONNECT_EN 0x0000040000000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_MCONNECT_SHIFT 40U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_MCONNECT_CLRMSK 0xFFFFFCFFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_BUSY_SHIFT 38U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_BUSY_CLRMSK 0xFFFFFFBFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_BUSY_EN 0x0000004000000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_CMD_FIFO_FULL_SHIFT 37U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_CMD_FIFO_FULL_CLRMSK 0xFFFFFFDFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_CMD_FIFO_FULL_EN 0x0000002000000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SRESP_ERROR_SHIFT 36U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SRESP_ERROR_CLRMSK 0xFFFFFFEFFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETH_SRESP_ERROR_EN 0x0000001000000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_BUSY_SHIFT 34U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_BUSY_CLRMSK 0xFFFFFFFBFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_BUSY_EN 0x0000000400000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_CMD_FIFO_FULL_SHIFT 33U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_CMD_FIFO_FULL_CLRMSK 0xFFFFFFFDFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_CMD_FIFO_FULL_EN 0x0000000200000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SRESP_ERROR_SHIFT 32U +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SRESP_ERROR_CLRMSK 0xFFFFFFFEFFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_TARGETS_SRESP_ERROR_EN 0x0000000100000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_RESERVED_SHIFT 31U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_RESERVED_CLRMSK 0xFFFFFFFF7FFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_RESERVED_EN 0x0000000080000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SWAIT_SHIFT 30U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SWAIT_CLRMSK 0xFFFFFFFFBFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SWAIT_EN 0x0000000040000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MDISCREQ_SHIFT 29U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MDISCREQ_CLRMSK 0xFFFFFFFFDFFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MDISCREQ_EN 0x0000000020000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MDISCACK_SHIFT 27U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MDISCACK_CLRMSK 0xFFFFFFFFE7FFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SCONNECT_SHIFT 26U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SCONNECT_CLRMSK 0xFFFFFFFFFBFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_SCONNECT_EN 0x0000000004000000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MCONNECT_SHIFT 24U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT3_MCONNECT_CLRMSK 0xFFFFFFFFFCFFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_RESERVED_SHIFT 23U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_RESERVED_CLRMSK 0xFFFFFFFFFF7FFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_RESERVED_EN 0x0000000000800000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SWAIT_SHIFT 22U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SWAIT_CLRMSK 0xFFFFFFFFFFBFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SWAIT_EN 0x0000000000400000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MDISCREQ_SHIFT 21U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MDISCREQ_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MDISCREQ_EN 0x0000000000200000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MDISCACK_SHIFT 19U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MDISCACK_CLRMSK 0xFFFFFFFFFFE7FFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SCONNECT_SHIFT 18U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SCONNECT_CLRMSK 0xFFFFFFFFFFFBFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_SCONNECT_EN 0x0000000000040000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MCONNECT_SHIFT 16U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT2_MCONNECT_CLRMSK 0xFFFFFFFFFFFCFFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_RESERVED_SHIFT 15U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_RESERVED_CLRMSK 0xFFFFFFFFFFFF7FFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_RESERVED_EN 0x0000000000008000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SWAIT_SHIFT 14U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SWAIT_CLRMSK 0xFFFFFFFFFFFFBFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SWAIT_EN 0x0000000000004000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MDISCREQ_SHIFT 13U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MDISCREQ_CLRMSK 0xFFFFFFFFFFFFDFFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MDISCREQ_EN 0x0000000000002000ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MDISCACK_SHIFT 11U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MDISCACK_CLRMSK 0xFFFFFFFFFFFFE7FFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SCONNECT_SHIFT 10U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SCONNECT_CLRMSK 0xFFFFFFFFFFFFFBFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_SCONNECT_EN 0x0000000000000400ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MCONNECT_SHIFT 8U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT1_MCONNECT_CLRMSK 0xFFFFFFFFFFFFFCFFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_RESERVED_SHIFT 7U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_RESERVED_CLRMSK 0xFFFFFFFFFFFFFF7FULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_RESERVED_EN 0x0000000000000080ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SWAIT_SHIFT 6U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SWAIT_CLRMSK 0xFFFFFFFFFFFFFFBFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SWAIT_EN 0x0000000000000040ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MDISCREQ_SHIFT 5U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MDISCREQ_CLRMSK 0xFFFFFFFFFFFFFFDFULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MDISCREQ_EN 0x0000000000000020ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MDISCACK_SHIFT 3U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MDISCACK_CLRMSK 0xFFFFFFFFFFFFFFE7ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SCONNECT_SHIFT 2U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SCONNECT_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_SCONNECT_EN 0x0000000000000004ULL +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MCONNECT_SHIFT 0U +#define ROGUE_CR_OCP_DEBUG_STATUS_INIT0_MCONNECT_CLRMSK 0xFFFFFFFFFFFFFFFCULL + +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PM_ALIST_SHIFT 6U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PM_ALIST_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PM_ALIST_EN 0x00000040U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_HOST_SHIFT 5U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_HOST_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_HOST_EN 0x00000020U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_META_SHIFT 4U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_META_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_META_EN 0x00000010U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_ZLS_SHIFT 3U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_ZLS_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_ZLS_EN 0x00000008U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_TE_SHIFT 2U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_TE_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_TE_EN 0x00000004U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_VCE_SHIFT 1U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_VCE_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_PB_VCE_EN 0x00000002U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_TLA_SHIFT 0U +#define ROGUE_CR_BIF_TRUST_DM_TYPE_TLA_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_TRUST_DM_TYPE_TLA_EN 0x00000001U + +#define ROGUE_CR_BIF_TRUST_DM_MASK 0x0000007FU + +/* Register ROGUE_CR_BIF_TRUST */ +#define ROGUE_CR_BIF_TRUST 0xA000U +#define ROGUE_CR_BIF_TRUST_MASKFULL 0x00000000001FFFFFULL +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_VERTEX_DM_TRUSTED_SHIFT 20U +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_VERTEX_DM_TRUSTED_CLRMSK 0xFFEFFFFFU +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_VERTEX_DM_TRUSTED_EN 0x00100000U +#define ROGUE_CR_BIF_TRUST_MCU_RAY_VERTEX_DM_TRUSTED_SHIFT 19U +#define ROGUE_CR_BIF_TRUST_MCU_RAY_VERTEX_DM_TRUSTED_CLRMSK 0xFFF7FFFFU +#define ROGUE_CR_BIF_TRUST_MCU_RAY_VERTEX_DM_TRUSTED_EN 0x00080000U +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_DM_TRUSTED_SHIFT 18U +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_DM_TRUSTED_CLRMSK 0xFFFBFFFFU +#define ROGUE_CR_BIF_TRUST_OTHER_RAY_DM_TRUSTED_EN 0x00040000U +#define ROGUE_CR_BIF_TRUST_MCU_RAY_DM_TRUSTED_SHIFT 17U +#define ROGUE_CR_BIF_TRUST_MCU_RAY_DM_TRUSTED_CLRMSK 0xFFFDFFFFU +#define ROGUE_CR_BIF_TRUST_MCU_RAY_DM_TRUSTED_EN 0x00020000U +#define ROGUE_CR_BIF_TRUST_ENABLE_SHIFT 16U +#define ROGUE_CR_BIF_TRUST_ENABLE_CLRMSK 0xFFFEFFFFU +#define ROGUE_CR_BIF_TRUST_ENABLE_EN 0x00010000U +#define ROGUE_CR_BIF_TRUST_DM_TRUSTED_SHIFT 9U +#define ROGUE_CR_BIF_TRUST_DM_TRUSTED_CLRMSK 0xFFFF01FFU +#define ROGUE_CR_BIF_TRUST_OTHER_COMPUTE_DM_TRUSTED_SHIFT 8U +#define ROGUE_CR_BIF_TRUST_OTHER_COMPUTE_DM_TRUSTED_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_BIF_TRUST_OTHER_COMPUTE_DM_TRUSTED_EN 0x00000100U +#define ROGUE_CR_BIF_TRUST_MCU_COMPUTE_DM_TRUSTED_SHIFT 7U +#define ROGUE_CR_BIF_TRUST_MCU_COMPUTE_DM_TRUSTED_CLRMSK 0xFFFFFF7FU +#define ROGUE_CR_BIF_TRUST_MCU_COMPUTE_DM_TRUSTED_EN 0x00000080U +#define ROGUE_CR_BIF_TRUST_PBE_COMPUTE_DM_TRUSTED_SHIFT 6U +#define ROGUE_CR_BIF_TRUST_PBE_COMPUTE_DM_TRUSTED_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_BIF_TRUST_PBE_COMPUTE_DM_TRUSTED_EN 0x00000040U +#define ROGUE_CR_BIF_TRUST_OTHER_PIXEL_DM_TRUSTED_SHIFT 5U +#define ROGUE_CR_BIF_TRUST_OTHER_PIXEL_DM_TRUSTED_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_BIF_TRUST_OTHER_PIXEL_DM_TRUSTED_EN 0x00000020U +#define ROGUE_CR_BIF_TRUST_MCU_PIXEL_DM_TRUSTED_SHIFT 4U +#define ROGUE_CR_BIF_TRUST_MCU_PIXEL_DM_TRUSTED_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_BIF_TRUST_MCU_PIXEL_DM_TRUSTED_EN 0x00000010U +#define ROGUE_CR_BIF_TRUST_PBE_PIXEL_DM_TRUSTED_SHIFT 3U +#define ROGUE_CR_BIF_TRUST_PBE_PIXEL_DM_TRUSTED_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_BIF_TRUST_PBE_PIXEL_DM_TRUSTED_EN 0x00000008U +#define ROGUE_CR_BIF_TRUST_OTHER_VERTEX_DM_TRUSTED_SHIFT 2U +#define ROGUE_CR_BIF_TRUST_OTHER_VERTEX_DM_TRUSTED_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_BIF_TRUST_OTHER_VERTEX_DM_TRUSTED_EN 0x00000004U +#define ROGUE_CR_BIF_TRUST_MCU_VERTEX_DM_TRUSTED_SHIFT 1U +#define ROGUE_CR_BIF_TRUST_MCU_VERTEX_DM_TRUSTED_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_BIF_TRUST_MCU_VERTEX_DM_TRUSTED_EN 0x00000002U +#define ROGUE_CR_BIF_TRUST_PBE_VERTEX_DM_TRUSTED_SHIFT 0U +#define ROGUE_CR_BIF_TRUST_PBE_VERTEX_DM_TRUSTED_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_BIF_TRUST_PBE_VERTEX_DM_TRUSTED_EN 0x00000001U + +/* Register ROGUE_CR_SYS_BUS_SECURE */ +#define ROGUE_CR_SYS_BUS_SECURE 0xA100U +#define ROGUE_CR_SYS_BUS_SECURE__SECR__MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_SYS_BUS_SECURE_MASKFULL 0x0000000000000001ULL +#define ROGUE_CR_SYS_BUS_SECURE_ENABLE_SHIFT 0U +#define ROGUE_CR_SYS_BUS_SECURE_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SYS_BUS_SECURE_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_FBA_FC0_CHECKSUM */ +#define ROGUE_CR_FBA_FC0_CHECKSUM 0xD170U +#define ROGUE_CR_FBA_FC0_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_FBA_FC0_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_FBA_FC0_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_FBA_FC1_CHECKSUM */ +#define ROGUE_CR_FBA_FC1_CHECKSUM 0xD178U +#define ROGUE_CR_FBA_FC1_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_FBA_FC1_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_FBA_FC1_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_FBA_FC2_CHECKSUM */ +#define ROGUE_CR_FBA_FC2_CHECKSUM 0xD180U +#define ROGUE_CR_FBA_FC2_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_FBA_FC2_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_FBA_FC2_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_FBA_FC3_CHECKSUM */ +#define ROGUE_CR_FBA_FC3_CHECKSUM 0xD188U +#define ROGUE_CR_FBA_FC3_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_FBA_FC3_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_FBA_FC3_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_CLK_CTRL2 */ +#define ROGUE_CR_CLK_CTRL2 0xD200U +#define ROGUE_CR_CLK_CTRL2_MASKFULL 0x0000000000000F33ULL +#define ROGUE_CR_CLK_CTRL2_MCU_FBTC_SHIFT 10U +#define ROGUE_CR_CLK_CTRL2_MCU_FBTC_CLRMSK 0xFFFFFFFFFFFFF3FFULL +#define ROGUE_CR_CLK_CTRL2_MCU_FBTC_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL2_MCU_FBTC_ON 0x0000000000000400ULL +#define ROGUE_CR_CLK_CTRL2_MCU_FBTC_AUTO 0x0000000000000800ULL +#define ROGUE_CR_CLK_CTRL2_VRDM_SHIFT 8U +#define ROGUE_CR_CLK_CTRL2_VRDM_CLRMSK 0xFFFFFFFFFFFFFCFFULL +#define ROGUE_CR_CLK_CTRL2_VRDM_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL2_VRDM_ON 0x0000000000000100ULL +#define ROGUE_CR_CLK_CTRL2_VRDM_AUTO 0x0000000000000200ULL +#define ROGUE_CR_CLK_CTRL2_SH_SHIFT 4U +#define ROGUE_CR_CLK_CTRL2_SH_CLRMSK 0xFFFFFFFFFFFFFFCFULL +#define ROGUE_CR_CLK_CTRL2_SH_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL2_SH_ON 0x0000000000000010ULL +#define ROGUE_CR_CLK_CTRL2_SH_AUTO 0x0000000000000020ULL +#define ROGUE_CR_CLK_CTRL2_FBA_SHIFT 0U +#define ROGUE_CR_CLK_CTRL2_FBA_CLRMSK 0xFFFFFFFFFFFFFFFCULL +#define ROGUE_CR_CLK_CTRL2_FBA_OFF 0x0000000000000000ULL +#define ROGUE_CR_CLK_CTRL2_FBA_ON 0x0000000000000001ULL +#define ROGUE_CR_CLK_CTRL2_FBA_AUTO 0x0000000000000002ULL + +/* Register ROGUE_CR_CLK_STATUS2 */ +#define ROGUE_CR_CLK_STATUS2 0xD208U +#define ROGUE_CR_CLK_STATUS2_MASKFULL 0x0000000000000015ULL +#define ROGUE_CR_CLK_STATUS2_VRDM_SHIFT 4U +#define ROGUE_CR_CLK_STATUS2_VRDM_CLRMSK 0xFFFFFFFFFFFFFFEFULL +#define ROGUE_CR_CLK_STATUS2_VRDM_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS2_VRDM_RUNNING 0x0000000000000010ULL +#define ROGUE_CR_CLK_STATUS2_SH_SHIFT 2U +#define ROGUE_CR_CLK_STATUS2_SH_CLRMSK 0xFFFFFFFFFFFFFFFBULL +#define ROGUE_CR_CLK_STATUS2_SH_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS2_SH_RUNNING 0x0000000000000004ULL +#define ROGUE_CR_CLK_STATUS2_FBA_SHIFT 0U +#define ROGUE_CR_CLK_STATUS2_FBA_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_CLK_STATUS2_FBA_GATED 0x0000000000000000ULL +#define ROGUE_CR_CLK_STATUS2_FBA_RUNNING 0x0000000000000001ULL + +/* Register ROGUE_CR_RPM_SHF_FPL */ +#define ROGUE_CR_RPM_SHF_FPL 0xD520U +#define ROGUE_CR_RPM_SHF_FPL_MASKFULL 0x3FFFFFFFFFFFFFFCULL +#define ROGUE_CR_RPM_SHF_FPL_SIZE_SHIFT 40U +#define ROGUE_CR_RPM_SHF_FPL_SIZE_CLRMSK 0xC00000FFFFFFFFFFULL +#define ROGUE_CR_RPM_SHF_FPL_BASE_SHIFT 2U +#define ROGUE_CR_RPM_SHF_FPL_BASE_CLRMSK 0xFFFFFF0000000003ULL +#define ROGUE_CR_RPM_SHF_FPL_BASE_ALIGNSHIFT 2U +#define ROGUE_CR_RPM_SHF_FPL_BASE_ALIGNSIZE 4U + +/* Register ROGUE_CR_RPM_SHF_FPL_READ */ +#define ROGUE_CR_RPM_SHF_FPL_READ 0xD528U +#define ROGUE_CR_RPM_SHF_FPL_READ_MASKFULL 0x00000000007FFFFFULL +#define ROGUE_CR_RPM_SHF_FPL_READ_TOGGLE_SHIFT 22U +#define ROGUE_CR_RPM_SHF_FPL_READ_TOGGLE_CLRMSK 0xFFBFFFFFU +#define ROGUE_CR_RPM_SHF_FPL_READ_TOGGLE_EN 0x00400000U +#define ROGUE_CR_RPM_SHF_FPL_READ_OFFSET_SHIFT 0U +#define ROGUE_CR_RPM_SHF_FPL_READ_OFFSET_CLRMSK 0xFFC00000U + +/* Register ROGUE_CR_RPM_SHF_FPL_WRITE */ +#define ROGUE_CR_RPM_SHF_FPL_WRITE 0xD530U +#define ROGUE_CR_RPM_SHF_FPL_WRITE_MASKFULL 0x00000000007FFFFFULL +#define ROGUE_CR_RPM_SHF_FPL_WRITE_TOGGLE_SHIFT 22U +#define ROGUE_CR_RPM_SHF_FPL_WRITE_TOGGLE_CLRMSK 0xFFBFFFFFU +#define ROGUE_CR_RPM_SHF_FPL_WRITE_TOGGLE_EN 0x00400000U +#define ROGUE_CR_RPM_SHF_FPL_WRITE_OFFSET_SHIFT 0U +#define ROGUE_CR_RPM_SHF_FPL_WRITE_OFFSET_CLRMSK 0xFFC00000U + +/* Register ROGUE_CR_RPM_SHG_FPL */ +#define ROGUE_CR_RPM_SHG_FPL 0xD538U +#define ROGUE_CR_RPM_SHG_FPL_MASKFULL 0x3FFFFFFFFFFFFFFCULL +#define ROGUE_CR_RPM_SHG_FPL_SIZE_SHIFT 40U +#define ROGUE_CR_RPM_SHG_FPL_SIZE_CLRMSK 0xC00000FFFFFFFFFFULL +#define ROGUE_CR_RPM_SHG_FPL_BASE_SHIFT 2U +#define ROGUE_CR_RPM_SHG_FPL_BASE_CLRMSK 0xFFFFFF0000000003ULL +#define ROGUE_CR_RPM_SHG_FPL_BASE_ALIGNSHIFT 2U +#define ROGUE_CR_RPM_SHG_FPL_BASE_ALIGNSIZE 4U + +/* Register ROGUE_CR_RPM_SHG_FPL_READ */ +#define ROGUE_CR_RPM_SHG_FPL_READ 0xD540U +#define ROGUE_CR_RPM_SHG_FPL_READ_MASKFULL 0x00000000007FFFFFULL +#define ROGUE_CR_RPM_SHG_FPL_READ_TOGGLE_SHIFT 22U +#define ROGUE_CR_RPM_SHG_FPL_READ_TOGGLE_CLRMSK 0xFFBFFFFFU +#define ROGUE_CR_RPM_SHG_FPL_READ_TOGGLE_EN 0x00400000U +#define ROGUE_CR_RPM_SHG_FPL_READ_OFFSET_SHIFT 0U +#define ROGUE_CR_RPM_SHG_FPL_READ_OFFSET_CLRMSK 0xFFC00000U + +/* Register ROGUE_CR_RPM_SHG_FPL_WRITE */ +#define ROGUE_CR_RPM_SHG_FPL_WRITE 0xD548U +#define ROGUE_CR_RPM_SHG_FPL_WRITE_MASKFULL 0x00000000007FFFFFULL +#define ROGUE_CR_RPM_SHG_FPL_WRITE_TOGGLE_SHIFT 22U +#define ROGUE_CR_RPM_SHG_FPL_WRITE_TOGGLE_CLRMSK 0xFFBFFFFFU +#define ROGUE_CR_RPM_SHG_FPL_WRITE_TOGGLE_EN 0x00400000U +#define ROGUE_CR_RPM_SHG_FPL_WRITE_OFFSET_SHIFT 0U +#define ROGUE_CR_RPM_SHG_FPL_WRITE_OFFSET_CLRMSK 0xFFC00000U + +/* Register ROGUE_CR_SH_PERF */ +#define ROGUE_CR_SH_PERF 0xD5F8U +#define ROGUE_CR_SH_PERF_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_SH_PERF_CLR_3_SHIFT 4U +#define ROGUE_CR_SH_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SH_PERF_CLR_3_EN 0x00000010U +#define ROGUE_CR_SH_PERF_CLR_2_SHIFT 3U +#define ROGUE_CR_SH_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SH_PERF_CLR_2_EN 0x00000008U +#define ROGUE_CR_SH_PERF_CLR_1_SHIFT 2U +#define ROGUE_CR_SH_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SH_PERF_CLR_1_EN 0x00000004U +#define ROGUE_CR_SH_PERF_CLR_0_SHIFT 1U +#define ROGUE_CR_SH_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SH_PERF_CLR_0_EN 0x00000002U +#define ROGUE_CR_SH_PERF_CTRL_ENABLE_SHIFT 0U +#define ROGUE_CR_SH_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SH_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register ROGUE_CR_SH_PERF_SELECT0 */ +#define ROGUE_CR_SH_PERF_SELECT0 0xD600U +#define ROGUE_CR_SH_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define ROGUE_CR_SH_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define ROGUE_CR_SH_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define ROGUE_CR_SH_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define ROGUE_CR_SH_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define ROGUE_CR_SH_PERF_SELECT0_MODE_SHIFT 21U +#define ROGUE_CR_SH_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define ROGUE_CR_SH_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define ROGUE_CR_SH_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define ROGUE_CR_SH_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define ROGUE_CR_SH_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define ROGUE_CR_SH_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_SH_PERF_COUNTER_0 */ +#define ROGUE_CR_SH_PERF_COUNTER_0 0xD628U +#define ROGUE_CR_SH_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SH_PERF_COUNTER_0_REG_SHIFT 0U +#define ROGUE_CR_SH_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SHF_SHG_CHECKSUM */ +#define ROGUE_CR_SHF_SHG_CHECKSUM 0xD1C0U +#define ROGUE_CR_SHF_SHG_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SHF_SHG_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_SHF_SHG_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SHF_VERTEX_BIF_CHECKSUM */ +#define ROGUE_CR_SHF_VERTEX_BIF_CHECKSUM 0xD1C8U +#define ROGUE_CR_SHF_VERTEX_BIF_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SHF_VERTEX_BIF_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_SHF_VERTEX_BIF_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SHF_VARY_BIF_CHECKSUM */ +#define ROGUE_CR_SHF_VARY_BIF_CHECKSUM 0xD1D0U +#define ROGUE_CR_SHF_VARY_BIF_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SHF_VARY_BIF_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_SHF_VARY_BIF_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_RPM_BIF_CHECKSUM */ +#define ROGUE_CR_RPM_BIF_CHECKSUM 0xD1D8U +#define ROGUE_CR_RPM_BIF_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_RPM_BIF_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_RPM_BIF_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SHG_BIF_CHECKSUM */ +#define ROGUE_CR_SHG_BIF_CHECKSUM 0xD1E0U +#define ROGUE_CR_SHG_BIF_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SHG_BIF_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_SHG_BIF_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register ROGUE_CR_SHG_FE_BE_CHECKSUM */ +#define ROGUE_CR_SHG_FE_BE_CHECKSUM 0xD1E8U +#define ROGUE_CR_SHG_FE_BE_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_SHG_FE_BE_CHECKSUM_VALUE_SHIFT 0U +#define ROGUE_CR_SHG_FE_BE_CHECKSUM_VALUE_CLRMSK 0x00000000U + +/* Register DPX_CR_BF_PERF */ +#define DPX_CR_BF_PERF 0xC458U +#define DPX_CR_BF_PERF_MASKFULL 0x000000000000001FULL +#define DPX_CR_BF_PERF_CLR_3_SHIFT 4U +#define DPX_CR_BF_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define DPX_CR_BF_PERF_CLR_3_EN 0x00000010U +#define DPX_CR_BF_PERF_CLR_2_SHIFT 3U +#define DPX_CR_BF_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define DPX_CR_BF_PERF_CLR_2_EN 0x00000008U +#define DPX_CR_BF_PERF_CLR_1_SHIFT 2U +#define DPX_CR_BF_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define DPX_CR_BF_PERF_CLR_1_EN 0x00000004U +#define DPX_CR_BF_PERF_CLR_0_SHIFT 1U +#define DPX_CR_BF_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define DPX_CR_BF_PERF_CLR_0_EN 0x00000002U +#define DPX_CR_BF_PERF_CTRL_ENABLE_SHIFT 0U +#define DPX_CR_BF_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define DPX_CR_BF_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register DPX_CR_BF_PERF_SELECT0 */ +#define DPX_CR_BF_PERF_SELECT0 0xC460U +#define DPX_CR_BF_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define DPX_CR_BF_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define DPX_CR_BF_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define DPX_CR_BF_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define DPX_CR_BF_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define DPX_CR_BF_PERF_SELECT0_MODE_SHIFT 21U +#define DPX_CR_BF_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define DPX_CR_BF_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define DPX_CR_BF_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define DPX_CR_BF_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define DPX_CR_BF_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define DPX_CR_BF_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register DPX_CR_BF_PERF_COUNTER_0 */ +#define DPX_CR_BF_PERF_COUNTER_0 0xC488U +#define DPX_CR_BF_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_BF_PERF_COUNTER_0_REG_SHIFT 0U +#define DPX_CR_BF_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register DPX_CR_BT_PERF */ +#define DPX_CR_BT_PERF 0xC3D0U +#define DPX_CR_BT_PERF_MASKFULL 0x000000000000001FULL +#define DPX_CR_BT_PERF_CLR_3_SHIFT 4U +#define DPX_CR_BT_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define DPX_CR_BT_PERF_CLR_3_EN 0x00000010U +#define DPX_CR_BT_PERF_CLR_2_SHIFT 3U +#define DPX_CR_BT_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define DPX_CR_BT_PERF_CLR_2_EN 0x00000008U +#define DPX_CR_BT_PERF_CLR_1_SHIFT 2U +#define DPX_CR_BT_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define DPX_CR_BT_PERF_CLR_1_EN 0x00000004U +#define DPX_CR_BT_PERF_CLR_0_SHIFT 1U +#define DPX_CR_BT_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define DPX_CR_BT_PERF_CLR_0_EN 0x00000002U +#define DPX_CR_BT_PERF_CTRL_ENABLE_SHIFT 0U +#define DPX_CR_BT_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define DPX_CR_BT_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register DPX_CR_BT_PERF_SELECT0 */ +#define DPX_CR_BT_PERF_SELECT0 0xC3D8U +#define DPX_CR_BT_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define DPX_CR_BT_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define DPX_CR_BT_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define DPX_CR_BT_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define DPX_CR_BT_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define DPX_CR_BT_PERF_SELECT0_MODE_SHIFT 21U +#define DPX_CR_BT_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define DPX_CR_BT_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define DPX_CR_BT_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define DPX_CR_BT_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define DPX_CR_BT_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define DPX_CR_BT_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register DPX_CR_BT_PERF_COUNTER_0 */ +#define DPX_CR_BT_PERF_COUNTER_0 0xC420U +#define DPX_CR_BT_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_BT_PERF_COUNTER_0_REG_SHIFT 0U +#define DPX_CR_BT_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register DPX_CR_RQ_USC_DEBUG */ +#define DPX_CR_RQ_USC_DEBUG 0xC110U +#define DPX_CR_RQ_USC_DEBUG_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_RQ_USC_DEBUG_CHECKSUM_SHIFT 0U +#define DPX_CR_RQ_USC_DEBUG_CHECKSUM_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register DPX_CR_BIF_FAULT_BANK_MMU_STATUS */ +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS 0xC5C8U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_MASKFULL 0x000000000000F775ULL +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_CAT_BASE_SHIFT 12U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_CAT_BASE_CLRMSK 0xFFFF0FFFU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_PAGE_SIZE_SHIFT 8U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_PAGE_SIZE_CLRMSK 0xFFFFF8FFU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_DATA_TYPE_SHIFT 5U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_DATA_TYPE_CLRMSK 0xFFFFFF9FU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_RO_SHIFT 4U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_RO_CLRMSK 0xFFFFFFEFU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_RO_EN 0x00000010U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_PM_META_RO_SHIFT 2U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_PM_META_RO_CLRMSK 0xFFFFFFFBU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_PM_META_RO_EN 0x00000004U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_SHIFT 0U +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_CLRMSK 0xFFFFFFFEU +#define DPX_CR_BIF_FAULT_BANK_MMU_STATUS_FAULT_EN 0x00000001U + +/* Register DPX_CR_BIF_FAULT_BANK_REQ_STATUS */ +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS 0xC5D0U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_MASKFULL 0x03FFFFFFFFFFFFF0ULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_RNW_SHIFT 57U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_RNW_CLRMSK 0xFDFFFFFFFFFFFFFFULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_RNW_EN 0x0200000000000000ULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_TAG_SB_SHIFT 44U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_TAG_SB_CLRMSK 0xFE000FFFFFFFFFFFULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_TAG_ID_SHIFT 40U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_TAG_ID_CLRMSK 0xFFFFF0FFFFFFFFFFULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_ADDRESS_SHIFT 4U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_ADDRESS_CLRMSK 0xFFFFFF000000000FULL +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_ADDRESS_ALIGNSHIFT 4U +#define DPX_CR_BIF_FAULT_BANK_REQ_STATUS_ADDRESS_ALIGNSIZE 16U + +/* Register DPX_CR_BIF_MMU_STATUS */ +#define DPX_CR_BIF_MMU_STATUS 0xC5D8U +#define DPX_CR_BIF_MMU_STATUS_MASKFULL 0x000000000FFFFFF7ULL +#define DPX_CR_BIF_MMU_STATUS_PC_DATA_SHIFT 20U +#define DPX_CR_BIF_MMU_STATUS_PC_DATA_CLRMSK 0xF00FFFFFU +#define DPX_CR_BIF_MMU_STATUS_PD_DATA_SHIFT 12U +#define DPX_CR_BIF_MMU_STATUS_PD_DATA_CLRMSK 0xFFF00FFFU +#define DPX_CR_BIF_MMU_STATUS_PT_DATA_SHIFT 4U +#define DPX_CR_BIF_MMU_STATUS_PT_DATA_CLRMSK 0xFFFFF00FU +#define DPX_CR_BIF_MMU_STATUS_STALLED_SHIFT 2U +#define DPX_CR_BIF_MMU_STATUS_STALLED_CLRMSK 0xFFFFFFFBU +#define DPX_CR_BIF_MMU_STATUS_STALLED_EN 0x00000004U +#define DPX_CR_BIF_MMU_STATUS_PAUSED_SHIFT 1U +#define DPX_CR_BIF_MMU_STATUS_PAUSED_CLRMSK 0xFFFFFFFDU +#define DPX_CR_BIF_MMU_STATUS_PAUSED_EN 0x00000002U +#define DPX_CR_BIF_MMU_STATUS_BUSY_SHIFT 0U +#define DPX_CR_BIF_MMU_STATUS_BUSY_CLRMSK 0xFFFFFFFEU +#define DPX_CR_BIF_MMU_STATUS_BUSY_EN 0x00000001U + +/* Register DPX_CR_RT_PERF */ +#define DPX_CR_RT_PERF 0xC700U +#define DPX_CR_RT_PERF_MASKFULL 0x000000000000001FULL +#define DPX_CR_RT_PERF_CLR_3_SHIFT 4U +#define DPX_CR_RT_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define DPX_CR_RT_PERF_CLR_3_EN 0x00000010U +#define DPX_CR_RT_PERF_CLR_2_SHIFT 3U +#define DPX_CR_RT_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define DPX_CR_RT_PERF_CLR_2_EN 0x00000008U +#define DPX_CR_RT_PERF_CLR_1_SHIFT 2U +#define DPX_CR_RT_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define DPX_CR_RT_PERF_CLR_1_EN 0x00000004U +#define DPX_CR_RT_PERF_CLR_0_SHIFT 1U +#define DPX_CR_RT_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define DPX_CR_RT_PERF_CLR_0_EN 0x00000002U +#define DPX_CR_RT_PERF_CTRL_ENABLE_SHIFT 0U +#define DPX_CR_RT_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define DPX_CR_RT_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register DPX_CR_RT_PERF_SELECT0 */ +#define DPX_CR_RT_PERF_SELECT0 0xC708U +#define DPX_CR_RT_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define DPX_CR_RT_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define DPX_CR_RT_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define DPX_CR_RT_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define DPX_CR_RT_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define DPX_CR_RT_PERF_SELECT0_MODE_SHIFT 21U +#define DPX_CR_RT_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define DPX_CR_RT_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define DPX_CR_RT_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define DPX_CR_RT_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define DPX_CR_RT_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define DPX_CR_RT_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register DPX_CR_RT_PERF_COUNTER_0 */ +#define DPX_CR_RT_PERF_COUNTER_0 0xC730U +#define DPX_CR_RT_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_RT_PERF_COUNTER_0_REG_SHIFT 0U +#define DPX_CR_RT_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register DPX_CR_BX_TU_PERF */ +#define DPX_CR_BX_TU_PERF 0xC908U +#define DPX_CR_BX_TU_PERF_MASKFULL 0x000000000000001FULL +#define DPX_CR_BX_TU_PERF_CLR_3_SHIFT 4U +#define DPX_CR_BX_TU_PERF_CLR_3_CLRMSK 0xFFFFFFEFU +#define DPX_CR_BX_TU_PERF_CLR_3_EN 0x00000010U +#define DPX_CR_BX_TU_PERF_CLR_2_SHIFT 3U +#define DPX_CR_BX_TU_PERF_CLR_2_CLRMSK 0xFFFFFFF7U +#define DPX_CR_BX_TU_PERF_CLR_2_EN 0x00000008U +#define DPX_CR_BX_TU_PERF_CLR_1_SHIFT 2U +#define DPX_CR_BX_TU_PERF_CLR_1_CLRMSK 0xFFFFFFFBU +#define DPX_CR_BX_TU_PERF_CLR_1_EN 0x00000004U +#define DPX_CR_BX_TU_PERF_CLR_0_SHIFT 1U +#define DPX_CR_BX_TU_PERF_CLR_0_CLRMSK 0xFFFFFFFDU +#define DPX_CR_BX_TU_PERF_CLR_0_EN 0x00000002U +#define DPX_CR_BX_TU_PERF_CTRL_ENABLE_SHIFT 0U +#define DPX_CR_BX_TU_PERF_CTRL_ENABLE_CLRMSK 0xFFFFFFFEU +#define DPX_CR_BX_TU_PERF_CTRL_ENABLE_EN 0x00000001U + +/* Register DPX_CR_BX_TU_PERF_SELECT0 */ +#define DPX_CR_BX_TU_PERF_SELECT0 0xC910U +#define DPX_CR_BX_TU_PERF_SELECT0_MASKFULL 0x3FFF3FFF003FFFFFULL +#define DPX_CR_BX_TU_PERF_SELECT0_BATCH_MAX_SHIFT 48U +#define DPX_CR_BX_TU_PERF_SELECT0_BATCH_MAX_CLRMSK 0xC000FFFFFFFFFFFFULL +#define DPX_CR_BX_TU_PERF_SELECT0_BATCH_MIN_SHIFT 32U +#define DPX_CR_BX_TU_PERF_SELECT0_BATCH_MIN_CLRMSK 0xFFFFC000FFFFFFFFULL +#define DPX_CR_BX_TU_PERF_SELECT0_MODE_SHIFT 21U +#define DPX_CR_BX_TU_PERF_SELECT0_MODE_CLRMSK 0xFFFFFFFFFFDFFFFFULL +#define DPX_CR_BX_TU_PERF_SELECT0_MODE_EN 0x0000000000200000ULL +#define DPX_CR_BX_TU_PERF_SELECT0_GROUP_SELECT_SHIFT 16U +#define DPX_CR_BX_TU_PERF_SELECT0_GROUP_SELECT_CLRMSK 0xFFFFFFFFFFE0FFFFULL +#define DPX_CR_BX_TU_PERF_SELECT0_BIT_SELECT_SHIFT 0U +#define DPX_CR_BX_TU_PERF_SELECT0_BIT_SELECT_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register DPX_CR_BX_TU_PERF_COUNTER_0 */ +#define DPX_CR_BX_TU_PERF_COUNTER_0 0xC938U +#define DPX_CR_BX_TU_PERF_COUNTER_0_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_BX_TU_PERF_COUNTER_0_REG_SHIFT 0U +#define DPX_CR_BX_TU_PERF_COUNTER_0_REG_CLRMSK 0x00000000U + +/* Register DPX_CR_RS_PDS_RR_CHECKSUM */ +#define DPX_CR_RS_PDS_RR_CHECKSUM 0xC0F0U +#define DPX_CR_RS_PDS_RR_CHECKSUM_MASKFULL 0x00000000FFFFFFFFULL +#define DPX_CR_RS_PDS_RR_CHECKSUM_VALUE_SHIFT 0U +#define DPX_CR_RS_PDS_RR_CHECKSUM_VALUE_CLRMSK 0xFFFFFFFF00000000ULL + +/* Register ROGUE_CR_MMU_CBASE_MAPPING_CONTEXT */ +#define ROGUE_CR_MMU_CBASE_MAPPING_CONTEXT 0xE140U +#define ROGUE_CR_MMU_CBASE_MAPPING_CONTEXT_MASKFULL 0x00000000000000FFULL +#define ROGUE_CR_MMU_CBASE_MAPPING_CONTEXT_ID_SHIFT 0U +#define ROGUE_CR_MMU_CBASE_MAPPING_CONTEXT_ID_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_MMU_CBASE_MAPPING */ +#define ROGUE_CR_MMU_CBASE_MAPPING 0xE148U +#define ROGUE_CR_MMU_CBASE_MAPPING_MASKFULL 0x000000000FFFFFFFULL +#define ROGUE_CR_MMU_CBASE_MAPPING_BASE_ADDR_SHIFT 0U +#define ROGUE_CR_MMU_CBASE_MAPPING_BASE_ADDR_CLRMSK 0xF0000000U +#define ROGUE_CR_MMU_CBASE_MAPPING_BASE_ADDR_ALIGNSHIFT 12U +#define ROGUE_CR_MMU_CBASE_MAPPING_BASE_ADDR_ALIGNSIZE 4096U + +/* Register ROGUE_CR_MMU_FAULT_STATUS */ +#define ROGUE_CR_MMU_FAULT_STATUS 0xE150U +#define ROGUE_CR_MMU_FAULT_STATUS_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_ADDRESS_SHIFT 28U +#define ROGUE_CR_MMU_FAULT_STATUS_ADDRESS_CLRMSK 0x000000000FFFFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_CONTEXT_SHIFT 20U +#define ROGUE_CR_MMU_FAULT_STATUS_CONTEXT_CLRMSK 0xFFFFFFFFF00FFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_TAG_SB_SHIFT 12U +#define ROGUE_CR_MMU_FAULT_STATUS_TAG_SB_CLRMSK 0xFFFFFFFFFFF00FFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_REQ_ID_SHIFT 6U +#define ROGUE_CR_MMU_FAULT_STATUS_REQ_ID_CLRMSK 0xFFFFFFFFFFFFF03FULL +#define ROGUE_CR_MMU_FAULT_STATUS_LEVEL_SHIFT 4U +#define ROGUE_CR_MMU_FAULT_STATUS_LEVEL_CLRMSK 0xFFFFFFFFFFFFFFCFULL +#define ROGUE_CR_MMU_FAULT_STATUS_RNW_SHIFT 3U +#define ROGUE_CR_MMU_FAULT_STATUS_RNW_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_MMU_FAULT_STATUS_RNW_EN 0x0000000000000008ULL +#define ROGUE_CR_MMU_FAULT_STATUS_TYPE_SHIFT 1U +#define ROGUE_CR_MMU_FAULT_STATUS_TYPE_CLRMSK 0xFFFFFFFFFFFFFFF9ULL +#define ROGUE_CR_MMU_FAULT_STATUS_FAULT_SHIFT 0U +#define ROGUE_CR_MMU_FAULT_STATUS_FAULT_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MMU_FAULT_STATUS_FAULT_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_MMU_FAULT_STATUS_META */ +#define ROGUE_CR_MMU_FAULT_STATUS_META 0xE158U +#define ROGUE_CR_MMU_FAULT_STATUS_META_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_ADDRESS_SHIFT 28U +#define ROGUE_CR_MMU_FAULT_STATUS_META_ADDRESS_CLRMSK 0x000000000FFFFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_CONTEXT_SHIFT 20U +#define ROGUE_CR_MMU_FAULT_STATUS_META_CONTEXT_CLRMSK 0xFFFFFFFFF00FFFFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_TAG_SB_SHIFT 12U +#define ROGUE_CR_MMU_FAULT_STATUS_META_TAG_SB_CLRMSK 0xFFFFFFFFFFF00FFFULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_REQ_ID_SHIFT 6U +#define ROGUE_CR_MMU_FAULT_STATUS_META_REQ_ID_CLRMSK 0xFFFFFFFFFFFFF03FULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_LEVEL_SHIFT 4U +#define ROGUE_CR_MMU_FAULT_STATUS_META_LEVEL_CLRMSK 0xFFFFFFFFFFFFFFCFULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_RNW_SHIFT 3U +#define ROGUE_CR_MMU_FAULT_STATUS_META_RNW_CLRMSK 0xFFFFFFFFFFFFFFF7ULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_RNW_EN 0x0000000000000008ULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_TYPE_SHIFT 1U +#define ROGUE_CR_MMU_FAULT_STATUS_META_TYPE_CLRMSK 0xFFFFFFFFFFFFFFF9ULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_FAULT_SHIFT 0U +#define ROGUE_CR_MMU_FAULT_STATUS_META_FAULT_CLRMSK 0xFFFFFFFFFFFFFFFEULL +#define ROGUE_CR_MMU_FAULT_STATUS_META_FAULT_EN 0x0000000000000001ULL + +/* Register ROGUE_CR_SLC3_CTRL_MISC */ +#define ROGUE_CR_SLC3_CTRL_MISC 0xE200U +#define ROGUE_CR_SLC3_CTRL_MISC_MASKFULL 0x0000000000000107ULL +#define ROGUE_CR_SLC3_CTRL_MISC_WRITE_COMBINER_SHIFT 8U +#define ROGUE_CR_SLC3_CTRL_MISC_WRITE_COMBINER_CLRMSK 0xFFFFFEFFU +#define ROGUE_CR_SLC3_CTRL_MISC_WRITE_COMBINER_EN 0x00000100U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_SHIFT 0U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_CLRMSK 0xFFFFFFF8U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_LINEAR 0x00000000U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_IN_PAGE_HASH 0x00000001U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_FIXED_PVR_HASH 0x00000002U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_SCRAMBLE_PVR_HASH 0x00000003U +#define ROGUE_CR_SLC3_CTRL_MISC_ADDR_DECODE_MODE_WEAVED_HASH 0x00000004U + +/* Register ROGUE_CR_SLC3_SCRAMBLE */ +#define ROGUE_CR_SLC3_SCRAMBLE 0xE208U +#define ROGUE_CR_SLC3_SCRAMBLE_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_SCRAMBLE_BITS_SHIFT 0U +#define ROGUE_CR_SLC3_SCRAMBLE_BITS_CLRMSK 0x0000000000000000ULL + +/* Register ROGUE_CR_SLC3_SCRAMBLE2 */ +#define ROGUE_CR_SLC3_SCRAMBLE2 0xE210U +#define ROGUE_CR_SLC3_SCRAMBLE2_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_SCRAMBLE2_BITS_SHIFT 0U +#define ROGUE_CR_SLC3_SCRAMBLE2_BITS_CLRMSK 0x0000000000000000ULL + +/* Register ROGUE_CR_SLC3_SCRAMBLE3 */ +#define ROGUE_CR_SLC3_SCRAMBLE3 0xE218U +#define ROGUE_CR_SLC3_SCRAMBLE3_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_SCRAMBLE3_BITS_SHIFT 0U +#define ROGUE_CR_SLC3_SCRAMBLE3_BITS_CLRMSK 0x0000000000000000ULL + +/* Register ROGUE_CR_SLC3_SCRAMBLE4 */ +#define ROGUE_CR_SLC3_SCRAMBLE4 0xE260U +#define ROGUE_CR_SLC3_SCRAMBLE4_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_SCRAMBLE4_BITS_SHIFT 0U +#define ROGUE_CR_SLC3_SCRAMBLE4_BITS_CLRMSK 0x0000000000000000ULL + +/* Register ROGUE_CR_SLC3_STATUS */ +#define ROGUE_CR_SLC3_STATUS 0xE220U +#define ROGUE_CR_SLC3_STATUS_MASKFULL 0xFFFFFFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_STATUS_WRITES1_SHIFT 48U +#define ROGUE_CR_SLC3_STATUS_WRITES1_CLRMSK 0x0000FFFFFFFFFFFFULL +#define ROGUE_CR_SLC3_STATUS_WRITES0_SHIFT 32U +#define ROGUE_CR_SLC3_STATUS_WRITES0_CLRMSK 0xFFFF0000FFFFFFFFULL +#define ROGUE_CR_SLC3_STATUS_READS1_SHIFT 16U +#define ROGUE_CR_SLC3_STATUS_READS1_CLRMSK 0xFFFFFFFF0000FFFFULL +#define ROGUE_CR_SLC3_STATUS_READS0_SHIFT 0U +#define ROGUE_CR_SLC3_STATUS_READS0_CLRMSK 0xFFFFFFFFFFFF0000ULL + +/* Register ROGUE_CR_SLC3_IDLE */ +#define ROGUE_CR_SLC3_IDLE 0xE228U +#define ROGUE_CR_SLC3_IDLE_MASKFULL 0x00000000000FFFFFULL +#define ROGUE_CR_SLC3_IDLE_ORDERQ_DUST2_SHIFT 18U +#define ROGUE_CR_SLC3_IDLE_ORDERQ_DUST2_CLRMSK 0xFFF3FFFFU +#define ROGUE_CR_SLC3_IDLE_MMU_SHIFT 17U +#define ROGUE_CR_SLC3_IDLE_MMU_CLRMSK 0xFFFDFFFFU +#define ROGUE_CR_SLC3_IDLE_MMU_EN 0x00020000U +#define ROGUE_CR_SLC3_IDLE_RDI_SHIFT 16U +#define ROGUE_CR_SLC3_IDLE_RDI_CLRMSK 0xFFFEFFFFU +#define ROGUE_CR_SLC3_IDLE_RDI_EN 0x00010000U +#define ROGUE_CR_SLC3_IDLE_IMGBV4_SHIFT 12U +#define ROGUE_CR_SLC3_IDLE_IMGBV4_CLRMSK 0xFFFF0FFFU +#define ROGUE_CR_SLC3_IDLE_CACHE_BANKS_SHIFT 4U +#define ROGUE_CR_SLC3_IDLE_CACHE_BANKS_CLRMSK 0xFFFFF00FU +#define ROGUE_CR_SLC3_IDLE_ORDERQ_DUST_SHIFT 2U +#define ROGUE_CR_SLC3_IDLE_ORDERQ_DUST_CLRMSK 0xFFFFFFF3U +#define ROGUE_CR_SLC3_IDLE_ORDERQ_JONES_SHIFT 1U +#define ROGUE_CR_SLC3_IDLE_ORDERQ_JONES_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SLC3_IDLE_ORDERQ_JONES_EN 0x00000002U +#define ROGUE_CR_SLC3_IDLE_XBAR_SHIFT 0U +#define ROGUE_CR_SLC3_IDLE_XBAR_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SLC3_IDLE_XBAR_EN 0x00000001U + +/* Register ROGUE_CR_SLC3_FAULT_STOP_STATUS */ +#define ROGUE_CR_SLC3_FAULT_STOP_STATUS 0xE248U +#define ROGUE_CR_SLC3_FAULT_STOP_STATUS_MASKFULL 0x0000000000001FFFULL +#define ROGUE_CR_SLC3_FAULT_STOP_STATUS_BIF_SHIFT 0U +#define ROGUE_CR_SLC3_FAULT_STOP_STATUS_BIF_CLRMSK 0xFFFFE000U + +/* Register ROGUE_CR_VDM_CONTEXT_STORE_MODE */ +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE 0xF048U +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MASKFULL 0x0000000000000003ULL +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MODE_SHIFT 0U +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MODE_CLRMSK 0xFFFFFFFCU +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MODE_INDEX 0x00000000U +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MODE_INSTANCE 0x00000001U +#define ROGUE_CR_VDM_CONTEXT_STORE_MODE_MODE_LIST 0x00000002U + +/* Register ROGUE_CR_CONTEXT_MAPPING0 */ +#define ROGUE_CR_CONTEXT_MAPPING0 0xF078U +#define ROGUE_CR_CONTEXT_MAPPING0_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING0_2D_SHIFT 24U +#define ROGUE_CR_CONTEXT_MAPPING0_2D_CLRMSK 0x00FFFFFFU +#define ROGUE_CR_CONTEXT_MAPPING0_CDM_SHIFT 16U +#define ROGUE_CR_CONTEXT_MAPPING0_CDM_CLRMSK 0xFF00FFFFU +#define ROGUE_CR_CONTEXT_MAPPING0_3D_SHIFT 8U +#define ROGUE_CR_CONTEXT_MAPPING0_3D_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_CONTEXT_MAPPING0_TA_SHIFT 0U +#define ROGUE_CR_CONTEXT_MAPPING0_TA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_CONTEXT_MAPPING1 */ +#define ROGUE_CR_CONTEXT_MAPPING1 0xF080U +#define ROGUE_CR_CONTEXT_MAPPING1_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_CONTEXT_MAPPING1_HOST_SHIFT 8U +#define ROGUE_CR_CONTEXT_MAPPING1_HOST_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_CONTEXT_MAPPING1_TLA_SHIFT 0U +#define ROGUE_CR_CONTEXT_MAPPING1_TLA_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_CONTEXT_MAPPING2 */ +#define ROGUE_CR_CONTEXT_MAPPING2 0xF088U +#define ROGUE_CR_CONTEXT_MAPPING2_MASKFULL 0x0000000000FFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING2_ALIST0_SHIFT 16U +#define ROGUE_CR_CONTEXT_MAPPING2_ALIST0_CLRMSK 0xFF00FFFFU +#define ROGUE_CR_CONTEXT_MAPPING2_TE0_SHIFT 8U +#define ROGUE_CR_CONTEXT_MAPPING2_TE0_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_CONTEXT_MAPPING2_VCE0_SHIFT 0U +#define ROGUE_CR_CONTEXT_MAPPING2_VCE0_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_CONTEXT_MAPPING3 */ +#define ROGUE_CR_CONTEXT_MAPPING3 0xF090U +#define ROGUE_CR_CONTEXT_MAPPING3_MASKFULL 0x0000000000FFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING3_ALIST1_SHIFT 16U +#define ROGUE_CR_CONTEXT_MAPPING3_ALIST1_CLRMSK 0xFF00FFFFU +#define ROGUE_CR_CONTEXT_MAPPING3_TE1_SHIFT 8U +#define ROGUE_CR_CONTEXT_MAPPING3_TE1_CLRMSK 0xFFFF00FFU +#define ROGUE_CR_CONTEXT_MAPPING3_VCE1_SHIFT 0U +#define ROGUE_CR_CONTEXT_MAPPING3_VCE1_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_BIF_JONES_OUTSTANDING_READ */ +#define ROGUE_CR_BIF_JONES_OUTSTANDING_READ 0xF098U +#define ROGUE_CR_BIF_JONES_OUTSTANDING_READ_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_BIF_JONES_OUTSTANDING_READ_COUNTER_SHIFT 0U +#define ROGUE_CR_BIF_JONES_OUTSTANDING_READ_COUNTER_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIF_BLACKPEARL_OUTSTANDING_READ */ +#define ROGUE_CR_BIF_BLACKPEARL_OUTSTANDING_READ 0xF0A0U +#define ROGUE_CR_BIF_BLACKPEARL_OUTSTANDING_READ_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_BIF_BLACKPEARL_OUTSTANDING_READ_COUNTER_SHIFT 0U +#define ROGUE_CR_BIF_BLACKPEARL_OUTSTANDING_READ_COUNTER_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_BIF_DUST_OUTSTANDING_READ */ +#define ROGUE_CR_BIF_DUST_OUTSTANDING_READ 0xF0A8U +#define ROGUE_CR_BIF_DUST_OUTSTANDING_READ_MASKFULL 0x000000000000FFFFULL +#define ROGUE_CR_BIF_DUST_OUTSTANDING_READ_COUNTER_SHIFT 0U +#define ROGUE_CR_BIF_DUST_OUTSTANDING_READ_COUNTER_CLRMSK 0xFFFF0000U + +/* Register ROGUE_CR_CONTEXT_MAPPING4 */ +#define ROGUE_CR_CONTEXT_MAPPING4 0xF210U +#define ROGUE_CR_CONTEXT_MAPPING4_MASKFULL 0x0000FFFFFFFFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING4_3D_MMU_STACK_SHIFT 40U +#define ROGUE_CR_CONTEXT_MAPPING4_3D_MMU_STACK_CLRMSK 0xFFFF00FFFFFFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING4_3D_UFSTACK_SHIFT 32U +#define ROGUE_CR_CONTEXT_MAPPING4_3D_UFSTACK_CLRMSK 0xFFFFFF00FFFFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING4_3D_FSTACK_SHIFT 24U +#define ROGUE_CR_CONTEXT_MAPPING4_3D_FSTACK_CLRMSK 0xFFFFFFFF00FFFFFFULL +#define ROGUE_CR_CONTEXT_MAPPING4_TA_MMU_STACK_SHIFT 16U +#define ROGUE_CR_CONTEXT_MAPPING4_TA_MMU_STACK_CLRMSK 0xFFFFFFFFFF00FFFFULL +#define ROGUE_CR_CONTEXT_MAPPING4_TA_UFSTACK_SHIFT 8U +#define ROGUE_CR_CONTEXT_MAPPING4_TA_UFSTACK_CLRMSK 0xFFFFFFFFFFFF00FFULL +#define ROGUE_CR_CONTEXT_MAPPING4_TA_FSTACK_SHIFT 0U +#define ROGUE_CR_CONTEXT_MAPPING4_TA_FSTACK_CLRMSK 0xFFFFFFFFFFFFFF00ULL + +/* Register ROGUE_CR_MULTICORE_GPU */ +#define ROGUE_CR_MULTICORE_GPU 0xF300U +#define ROGUE_CR_MULTICORE_GPU_MASKFULL 0x000000000000007FULL +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_FRAGMENT_SHIFT 6U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_FRAGMENT_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_FRAGMENT_EN 0x00000040U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_GEOMETRY_SHIFT 5U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_GEOMETRY_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_GEOMETRY_EN 0x00000020U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_COMPUTE_SHIFT 4U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_COMPUTE_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_COMPUTE_EN 0x00000010U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_PRIMARY_SHIFT 3U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_PRIMARY_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_MULTICORE_GPU_CAPABILITY_PRIMARY_EN 0x00000008U +#define ROGUE_CR_MULTICORE_GPU_ID_SHIFT 0U +#define ROGUE_CR_MULTICORE_GPU_ID_CLRMSK 0xFFFFFFF8U + +/* Register ROGUE_CR_MULTICORE_SYSTEM */ +#define ROGUE_CR_MULTICORE_SYSTEM 0xF308U +#define ROGUE_CR_MULTICORE_SYSTEM_MASKFULL 0x000000000000000FULL +#define ROGUE_CR_MULTICORE_SYSTEM_GPU_COUNT_SHIFT 0U +#define ROGUE_CR_MULTICORE_SYSTEM_GPU_COUNT_CLRMSK 0xFFFFFFF0U + +/* Register ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON */ +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON 0xF310U +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_WORKLOAD_TYPE_SHIFT 30U +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_WORKLOAD_TYPE_CLRMSK 0x3FFFFFFFU +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_SHIFT 8U +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_CLRMSK 0xC00000FFU +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_GPU_ENABLE_SHIFT 0U +#define ROGUE_CR_MULTICORE_FRAGMENT_CTRL_COMMON_GPU_ENABLE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON */ +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON 0xF320U +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_WORKLOAD_TYPE_SHIFT 30U +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_WORKLOAD_TYPE_CLRMSK 0x3FFFFFFFU +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_SHIFT 8U +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_CLRMSK 0xC00000FFU +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_GPU_ENABLE_SHIFT 0U +#define ROGUE_CR_MULTICORE_GEOMETRY_CTRL_COMMON_GPU_ENABLE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON */ +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON 0xF330U +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_MASKFULL 0x00000000FFFFFFFFULL +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_WORKLOAD_TYPE_SHIFT 30U +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_WORKLOAD_TYPE_CLRMSK 0x3FFFFFFFU +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_SHIFT 8U +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_WORKLOAD_EXECUTE_COUNT_CLRMSK 0xC00000FFU +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_GPU_ENABLE_SHIFT 0U +#define ROGUE_CR_MULTICORE_COMPUTE_CTRL_COMMON_GPU_ENABLE_CLRMSK 0xFFFFFF00U + +/* Register ROGUE_CR_ECC_RAM_ERR_INJ */ +#define ROGUE_CR_ECC_RAM_ERR_INJ 0xF340U +#define ROGUE_CR_ECC_RAM_ERR_INJ_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_ECC_RAM_ERR_INJ_SLC_SIDEKICK_SHIFT 4U +#define ROGUE_CR_ECC_RAM_ERR_INJ_SLC_SIDEKICK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_ECC_RAM_ERR_INJ_SLC_SIDEKICK_EN 0x00000010U +#define ROGUE_CR_ECC_RAM_ERR_INJ_USC_SHIFT 3U +#define ROGUE_CR_ECC_RAM_ERR_INJ_USC_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_ECC_RAM_ERR_INJ_USC_EN 0x00000008U +#define ROGUE_CR_ECC_RAM_ERR_INJ_TPU_MCU_L0_SHIFT 2U +#define ROGUE_CR_ECC_RAM_ERR_INJ_TPU_MCU_L0_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_ECC_RAM_ERR_INJ_TPU_MCU_L0_EN 0x00000004U +#define ROGUE_CR_ECC_RAM_ERR_INJ_RASCAL_SHIFT 1U +#define ROGUE_CR_ECC_RAM_ERR_INJ_RASCAL_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_ECC_RAM_ERR_INJ_RASCAL_EN 0x00000002U +#define ROGUE_CR_ECC_RAM_ERR_INJ_MARS_SHIFT 0U +#define ROGUE_CR_ECC_RAM_ERR_INJ_MARS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_ECC_RAM_ERR_INJ_MARS_EN 0x00000001U + +/* Register ROGUE_CR_ECC_RAM_INIT_KICK */ +#define ROGUE_CR_ECC_RAM_INIT_KICK 0xF348U +#define ROGUE_CR_ECC_RAM_INIT_KICK_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_ECC_RAM_INIT_KICK_SLC_SIDEKICK_SHIFT 4U +#define ROGUE_CR_ECC_RAM_INIT_KICK_SLC_SIDEKICK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_ECC_RAM_INIT_KICK_SLC_SIDEKICK_EN 0x00000010U +#define ROGUE_CR_ECC_RAM_INIT_KICK_USC_SHIFT 3U +#define ROGUE_CR_ECC_RAM_INIT_KICK_USC_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_ECC_RAM_INIT_KICK_USC_EN 0x00000008U +#define ROGUE_CR_ECC_RAM_INIT_KICK_TPU_MCU_L0_SHIFT 2U +#define ROGUE_CR_ECC_RAM_INIT_KICK_TPU_MCU_L0_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_ECC_RAM_INIT_KICK_TPU_MCU_L0_EN 0x00000004U +#define ROGUE_CR_ECC_RAM_INIT_KICK_RASCAL_SHIFT 1U +#define ROGUE_CR_ECC_RAM_INIT_KICK_RASCAL_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_ECC_RAM_INIT_KICK_RASCAL_EN 0x00000002U +#define ROGUE_CR_ECC_RAM_INIT_KICK_MARS_SHIFT 0U +#define ROGUE_CR_ECC_RAM_INIT_KICK_MARS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_ECC_RAM_INIT_KICK_MARS_EN 0x00000001U + +/* Register ROGUE_CR_ECC_RAM_INIT_DONE */ +#define ROGUE_CR_ECC_RAM_INIT_DONE 0xF350U +#define ROGUE_CR_ECC_RAM_INIT_DONE_MASKFULL 0x000000000000001FULL +#define ROGUE_CR_ECC_RAM_INIT_DONE_SLC_SIDEKICK_SHIFT 4U +#define ROGUE_CR_ECC_RAM_INIT_DONE_SLC_SIDEKICK_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_ECC_RAM_INIT_DONE_SLC_SIDEKICK_EN 0x00000010U +#define ROGUE_CR_ECC_RAM_INIT_DONE_USC_SHIFT 3U +#define ROGUE_CR_ECC_RAM_INIT_DONE_USC_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_ECC_RAM_INIT_DONE_USC_EN 0x00000008U +#define ROGUE_CR_ECC_RAM_INIT_DONE_TPU_MCU_L0_SHIFT 2U +#define ROGUE_CR_ECC_RAM_INIT_DONE_TPU_MCU_L0_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_ECC_RAM_INIT_DONE_TPU_MCU_L0_EN 0x00000004U +#define ROGUE_CR_ECC_RAM_INIT_DONE_RASCAL_SHIFT 1U +#define ROGUE_CR_ECC_RAM_INIT_DONE_RASCAL_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_ECC_RAM_INIT_DONE_RASCAL_EN 0x00000002U +#define ROGUE_CR_ECC_RAM_INIT_DONE_MARS_SHIFT 0U +#define ROGUE_CR_ECC_RAM_INIT_DONE_MARS_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_ECC_RAM_INIT_DONE_MARS_EN 0x00000001U + +/* Register ROGUE_CR_SAFETY_EVENT_ENABLE */ +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE 0xF390U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__MASKFULL 0x000000000000007FULL +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_SHIFT 6U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_EN 0x00000040U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_SHIFT 5U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_EN 0x00000020U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_SHIFT 4U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_EN 0x00000010U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_SHIFT 3U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_EN 0x00000008U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_SHIFT 2U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_EN 0x00000004U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_SHIFT 1U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_EN 0x00000002U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_SHIFT 0U +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_SAFETY_EVENT_STATUS */ +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE 0xF398U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__MASKFULL 0x000000000000007FULL +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__CPU_PAGE_FAULT_SHIFT 6U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__CPU_PAGE_FAULT_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__CPU_PAGE_FAULT_EN 0x00000040U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__SAFE_COMPUTE_FAIL_SHIFT 5U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__SAFE_COMPUTE_FAIL_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__SAFE_COMPUTE_FAIL_EN 0x00000020U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__WATCHDOG_TIMEOUT_SHIFT 4U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__WATCHDOG_TIMEOUT_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__WATCHDOG_TIMEOUT_EN 0x00000010U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__TRP_FAIL_SHIFT 3U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__TRP_FAIL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__TRP_FAIL_EN 0x00000008U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_FW_SHIFT 2U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_FW_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_FW_EN 0x00000004U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_GPU_SHIFT 1U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_GPU_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__FAULT_GPU_EN 0x00000002U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__GPU_PAGE_FAULT_SHIFT 0U +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__GPU_PAGE_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SAFETY_EVENT_STATUS__ROGUEXE__GPU_PAGE_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_SAFETY_EVENT_CLEAR */ +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE 0xF3A0U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__MASKFULL 0x000000000000007FULL +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__CPU_PAGE_FAULT_SHIFT 6U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__CPU_PAGE_FAULT_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__CPU_PAGE_FAULT_EN 0x00000040U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__SAFE_COMPUTE_FAIL_SHIFT 5U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__SAFE_COMPUTE_FAIL_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__SAFE_COMPUTE_FAIL_EN 0x00000020U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__WATCHDOG_TIMEOUT_SHIFT 4U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__WATCHDOG_TIMEOUT_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__WATCHDOG_TIMEOUT_EN 0x00000010U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__TRP_FAIL_SHIFT 3U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__TRP_FAIL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__TRP_FAIL_EN 0x00000008U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_FW_SHIFT 2U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_FW_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_FW_EN 0x00000004U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_GPU_SHIFT 1U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_GPU_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__FAULT_GPU_EN 0x00000002U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__GPU_PAGE_FAULT_SHIFT 0U +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__GPU_PAGE_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_SAFETY_EVENT_CLEAR__ROGUEXE__GPU_PAGE_FAULT_EN 0x00000001U + +/* Register ROGUE_CR_MTS_SAFETY_EVENT_ENABLE */ +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE 0xF3D8U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__MASKFULL 0x000000000000007FULL +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_SHIFT 6U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_CLRMSK 0xFFFFFFBFU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__CPU_PAGE_FAULT_EN 0x00000040U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_SHIFT 5U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_CLRMSK 0xFFFFFFDFU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__SAFE_COMPUTE_FAIL_EN 0x00000020U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_SHIFT 4U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_CLRMSK 0xFFFFFFEFU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__WATCHDOG_TIMEOUT_EN 0x00000010U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_SHIFT 3U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_CLRMSK 0xFFFFFFF7U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__TRP_FAIL_EN 0x00000008U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_SHIFT 2U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_CLRMSK 0xFFFFFFFBU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_FW_EN 0x00000004U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_SHIFT 1U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_CLRMSK 0xFFFFFFFDU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__FAULT_GPU_EN 0x00000002U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_SHIFT 0U +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_CLRMSK 0xFFFFFFFEU +#define ROGUE_CR_MTS_SAFETY_EVENT_ENABLE__ROGUEXE__GPU_PAGE_FAULT_EN 0x00000001U + +/* clang-format on */ + +#endif /* PVR_ROGUE_CR_DEFS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_cr_defs_client.h b/drivers/gpu/drm/imagination/pvr_rogue_cr_defs_client.h new file mode 100644 index 000000000000..46186b56effc --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_cr_defs_client.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_CR_DEFS_CLIENT_H +#define PVR_ROGUE_CR_DEFS_CLIENT_H + +/* clang-format off */ + +/* + * This register controls the anti-aliasing mode of the Tiling Co-Processor, independent control is + * provided in both X & Y axis. + * This register needs to be set based on the ISP Samples Per Pixel a core supports. + * + * When ISP Samples Per Pixel = 1: + * 2xmsaa is achieved by enabling Y - TE does AA on Y plane only + * 4xmsaa is achieved by enabling Y and X - TE does AA on X and Y plane + * 8xmsaa not supported by XE cores + * + * When ISP Samples Per Pixel = 2: + * 2xmsaa is achieved by enabling X2 - does not affect TE + * 4xmsaa is achieved by enabling Y and X2 - TE does AA on Y plane only + * 8xmsaa is achieved by enabling Y, X and X2 - TE does AA on X and Y plane + * 8xmsaa not supported by XE cores + * + * When ISP Samples Per Pixel = 4: + * 2xmsaa is achieved by enabling X2 - does not affect TE + * 4xmsaa is achieved by enabling Y2 and X2 - TE does AA on Y plane only + * 8xmsaa not supported by XE cores + */ +/* Register ROGUE_CR_TE_AA */ +#define ROGUE_CR_TE_AA 0x0C00U +#define ROGUE_CR_TE_AA_MASKFULL 0x000000000000000Full +/* Y2 + * Indicates 4xmsaa when X2 and Y2 are set to 1. This does not affect TE and is only used within + * TPW. + */ +#define ROGUE_CR_TE_AA_Y2_SHIFT 3 +#define ROGUE_CR_TE_AA_Y2_CLRMSK 0xFFFFFFF7 +#define ROGUE_CR_TE_AA_Y2_EN 0x00000008 +/* Y + * Anti-Aliasing in Y Plane Enabled + */ +#define ROGUE_CR_TE_AA_Y_SHIFT 2 +#define ROGUE_CR_TE_AA_Y_CLRMSK 0xFFFFFFFB +#define ROGUE_CR_TE_AA_Y_EN 0x00000004 +/* X + * Anti-Aliasing in X Plane Enabled + */ +#define ROGUE_CR_TE_AA_X_SHIFT 1 +#define ROGUE_CR_TE_AA_X_CLRMSK 0xFFFFFFFD +#define ROGUE_CR_TE_AA_X_EN 0x00000002 +/* X2 + * 2x Anti-Aliasing Enabled, affects PPP only + */ +#define ROGUE_CR_TE_AA_X2_SHIFT (0U) +#define ROGUE_CR_TE_AA_X2_CLRMSK (0xFFFFFFFEU) +#define ROGUE_CR_TE_AA_X2_EN (0x00000001U) + +/* MacroTile Boundaries X Plane */ +/* Register ROGUE_CR_TE_MTILE1 */ +#define ROGUE_CR_TE_MTILE1 0x0C08 +#define ROGUE_CR_TE_MTILE1_MASKFULL 0x0000000007FFFFFFull +/* X1 default: 0x00000004 + * X1 MacroTile boundary, left tile X for second column of macrotiles (16MT mode) - 32 pixels across + * tile + */ +#define ROGUE_CR_TE_MTILE1_X1_SHIFT 18 +#define ROGUE_CR_TE_MTILE1_X1_CLRMSK 0xF803FFFF +/* X2 default: 0x00000008 + * X2 MacroTile boundary, left tile X for third(16MT) column of macrotiles - 32 pixels across tile + */ +#define ROGUE_CR_TE_MTILE1_X2_SHIFT 9U +#define ROGUE_CR_TE_MTILE1_X2_CLRMSK 0xFFFC01FF +/* X3 default: 0x0000000c + * X3 MacroTile boundary, left tile X for fourth column of macrotiles (16MT) - 32 pixels across tile + */ +#define ROGUE_CR_TE_MTILE1_X3_SHIFT 0 +#define ROGUE_CR_TE_MTILE1_X3_CLRMSK 0xFFFFFE00 + +/* MacroTile Boundaries Y Plane. */ +/* Register ROGUE_CR_TE_MTILE2 */ +#define ROGUE_CR_TE_MTILE2 0x0C10 +#define ROGUE_CR_TE_MTILE2_MASKFULL 0x0000000007FFFFFFull +/* Y1 default: 0x00000004 + * X1 MacroTile boundary, ltop tile Y for second column of macrotiles (16MT mode) - 32 pixels tile + * height + */ +#define ROGUE_CR_TE_MTILE2_Y1_SHIFT 18 +#define ROGUE_CR_TE_MTILE2_Y1_CLRMSK 0xF803FFFF +/* Y2 default: 0x00000008 + * X2 MacroTile boundary, top tile Y for third(16MT) column of macrotiles - 32 pixels tile height + */ +#define ROGUE_CR_TE_MTILE2_Y2_SHIFT 9 +#define ROGUE_CR_TE_MTILE2_Y2_CLRMSK 0xFFFC01FF +/* Y3 default: 0x0000000c + * X3 MacroTile boundary, top tile Y for fourth column of macrotiles (16MT) - 32 pixels tile height + */ +#define ROGUE_CR_TE_MTILE2_Y3_SHIFT 0 +#define ROGUE_CR_TE_MTILE2_Y3_CLRMSK 0xFFFFFE00 + +/* + * In order to perform the tiling operation and generate the display list the maximum screen size + * must be configured in terms of the number of tiles in X & Y axis. + */ + +/* Register ROGUE_CR_TE_SCREEN */ +#define ROGUE_CR_TE_SCREEN 0x0C18U +#define ROGUE_CR_TE_SCREEN_MASKFULL 0x00000000001FF1FFull +/* YMAX default: 0x00000010 + * Maximum Y tile address visible on screen, 32 pixel tile height, 16Kx16K max screen size + */ +#define ROGUE_CR_TE_SCREEN_YMAX_SHIFT 12 +#define ROGUE_CR_TE_SCREEN_YMAX_CLRMSK 0xFFE00FFF +/* XMAX default: 0x00000010 + * Maximum X tile address visible on screen, 32 pixel tile width, 16Kx16K max screen size + */ +#define ROGUE_CR_TE_SCREEN_XMAX_SHIFT 0 +#define ROGUE_CR_TE_SCREEN_XMAX_CLRMSK 0xFFFFFE00 + +/* + * In order to perform the tiling operation and generate the display list the maximum screen size + * must be configured in terms of the number of pixels in X & Y axis since this may not be the same + * as the number of tiles defined in the RGX_CR_TE_SCREEN register. + */ +/* Register ROGUE_CR_PPP_SCREEN */ +#define ROGUE_CR_PPP_SCREEN 0x0C98 +#define ROGUE_CR_PPP_SCREEN_MASKFULL 0x000000007FFF7FFFull +/* PIXYMAX + * Screen height in pixels. (16K x 16K max screen size) + */ +#define ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT 16 +#define ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK 0x8000FFFF +/* PIXXMAX + * Screen width in pixels.(16K x 16K max screen size) + */ +#define ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT 0 +#define ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK 0xFFFF8000 + +/* Register ROGUE_CR_ISP_MTILE_SIZE */ +#define ROGUE_CR_ISP_MTILE_SIZE 0x0F18 +#define ROGUE_CR_ISP_MTILE_SIZE_MASKFULL 0x0000000003FF03FFull +/* X + * Macrotile width, in tiles. A value of zero corresponds to the maximum size + */ +#define ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT 16 +#define ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK 0xFC00FFFF +#define ROGUE_CR_ISP_MTILE_SIZE_X_ALIGNSHIFT 0 +#define ROGUE_CR_ISP_MTILE_SIZE_X_ALIGNSIZE 1 +/* Y + * Macrotile height, in tiles. A value of zero corresponds to the maximum size + */ +#define ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT 0 +#define ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK 0xFFFFFC00 +#define ROGUE_CR_ISP_MTILE_SIZE_Y_ALIGNSHIFT 0 +#define ROGUE_CR_ISP_MTILE_SIZE_Y_ALIGNSIZE 1 + +/* clang-format on */ + +#endif /* PVR_ROGUE_CR_DEFS_CLIENT_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_defs.h b/drivers/gpu/drm/imagination/pvr_rogue_defs.h new file mode 100644 index 000000000000..932b01686008 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_defs.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_DEFS_H +#define PVR_ROGUE_DEFS_H + +#include "pvr_rogue_cr_defs.h" + +#include <linux/bits.h> + +/* + ****************************************************************************** + * ROGUE Defines + ****************************************************************************** + */ + +#define ROGUE_FW_MAX_NUM_OS (8U) +#define ROGUE_FW_HOST_OS (0U) +#define ROGUE_FW_GUEST_OSID_START (1U) + +#define ROGUE_FW_THREAD_0 (0U) +#define ROGUE_FW_THREAD_1 (1U) + +#define GET_ROGUE_CACHE_LINE_SIZE(x) ((((s32)(x)) > 0) ? ((x) / 8) : (0)) + +#define MAX_HW_GEOM_FRAG_CONTEXTS 2U + +#define ROGUE_CR_CLK_CTRL_ALL_ON \ + (0x5555555555555555ull & ROGUE_CR_CLK_CTRL_MASKFULL) +#define ROGUE_CR_CLK_CTRL_ALL_AUTO \ + (0xaaaaaaaaaaaaaaaaull & ROGUE_CR_CLK_CTRL_MASKFULL) +#define ROGUE_CR_CLK_CTRL2_ALL_ON \ + (0x5555555555555555ull & ROGUE_CR_CLK_CTRL2_MASKFULL) +#define ROGUE_CR_CLK_CTRL2_ALL_AUTO \ + (0xaaaaaaaaaaaaaaaaull & ROGUE_CR_CLK_CTRL2_MASKFULL) + +#define ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN \ + (ROGUE_CR_SOFT_RESET_DUST_A_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_B_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_C_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_D_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_E_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_F_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_G_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_H_CORE_EN) + +/* SOFT_RESET Rascal and DUSTs bits */ +#define ROGUE_CR_SOFT_RESET_RASCALDUSTS_EN \ + (ROGUE_CR_SOFT_RESET_RASCAL_CORE_EN | \ + ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN) + +/* SOFT_RESET steps as defined in the TRM */ +#define ROGUE_S7_SOFT_RESET_DUSTS (ROGUE_CR_SOFT_RESET_DUST_n_CORE_EN) + +#define ROGUE_S7_SOFT_RESET_JONES \ + (ROGUE_CR_SOFT_RESET_PM_EN | ROGUE_CR_SOFT_RESET_VDM_EN | \ + ROGUE_CR_SOFT_RESET_ISP_EN) + +#define ROGUE_S7_SOFT_RESET_JONES_ALL \ + (ROGUE_S7_SOFT_RESET_JONES | ROGUE_CR_SOFT_RESET_BIF_EN | \ + ROGUE_CR_SOFT_RESET_SLC_EN | ROGUE_CR_SOFT_RESET_GARTEN_EN) + +#define ROGUE_S7_SOFT_RESET2 \ + (ROGUE_CR_SOFT_RESET2_BLACKPEARL_EN | ROGUE_CR_SOFT_RESET2_PIXEL_EN | \ + ROGUE_CR_SOFT_RESET2_CDM_EN | ROGUE_CR_SOFT_RESET2_VERTEX_EN) + +#define ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT (12U) +#define ROGUE_BIF_PM_PHYSICAL_PAGE_SIZE \ + BIT(ROGUE_BIF_PM_PHYSICAL_PAGE_ALIGNSHIFT) + +#define ROGUE_BIF_PM_VIRTUAL_PAGE_ALIGNSHIFT (14U) +#define ROGUE_BIF_PM_VIRTUAL_PAGE_SIZE BIT(ROGUE_BIF_PM_VIRTUAL_PAGE_ALIGNSHIFT) + +#define ROGUE_BIF_PM_FREELIST_BASE_ADDR_ALIGNSIZE (16U) + +/* + * To get the number of required Dusts, divide the number of + * clusters by 2 and round up + */ +#define ROGUE_REQ_NUM_DUSTS(CLUSTERS) (((CLUSTERS) + 1U) / 2U) + +/* + * To get the number of required Bernado/Phantom(s), divide + * the number of clusters by 4 and round up + */ +#define ROGUE_REQ_NUM_PHANTOMS(CLUSTERS) (((CLUSTERS) + 3U) / 4U) +#define ROGUE_REQ_NUM_BERNADOS(CLUSTERS) (((CLUSTERS) + 3U) / 4U) +#define ROGUE_REQ_NUM_BLACKPEARLS(CLUSTERS) (((CLUSTERS) + 3U) / 4U) + +/* + * FW MMU contexts + */ +#define MMU_CONTEXT_MAPPING_FWPRIV (0x0) /* FW code/private data */ +#define MMU_CONTEXT_MAPPING_FWIF (0x0) /* Host/FW data */ + +/* + * Utility macros to calculate CAT_BASE register addresses + */ +#define BIF_CAT_BASEX(n) \ + (ROGUE_CR_BIF_CAT_BASE0 + \ + (n) * (ROGUE_CR_BIF_CAT_BASE1 - ROGUE_CR_BIF_CAT_BASE0)) + +#define FWCORE_MEM_CAT_BASEX(n) \ + (ROGUE_CR_FWCORE_MEM_CAT_BASE0 + \ + (n) * (ROGUE_CR_FWCORE_MEM_CAT_BASE1 - \ + ROGUE_CR_FWCORE_MEM_CAT_BASE0)) + +/* + * FWCORE wrapper register defines + */ +#define FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_SHIFT \ + ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_SHIFT +#define FWCORE_ADDR_REMAP_CONFIG0_MMU_CONTEXT_CLRMSK \ + ROGUE_CR_FWCORE_ADDR_REMAP_CONFIG0_CBASE_CLRMSK +#define FWCORE_ADDR_REMAP_CONFIG0_SIZE_ALIGNSHIFT (12U) + +#define ROGUE_MAX_COMPUTE_SHARED_REGISTERS (2 * 1024) +#define ROGUE_MAX_VERTEX_SHARED_REGISTERS 1024 +#define ROGUE_MAX_PIXEL_SHARED_REGISTERS 1024 +#define ROGUE_CSRM_LINE_SIZE_IN_DWORDS (64 * 4 * 4) + +#define ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE 64 +#define ROGUE_CDMCTRL_USC_COMMON_SIZE_UPPER 256 + +/* + * The maximum amount of local memory which can be allocated by a single kernel + * (in dwords/32-bit registers). + * + * ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE is in bytes so we divide by four. + */ +#define ROGUE_MAX_PER_KERNEL_LOCAL_MEM_SIZE_REGS ((ROGUE_CDMCTRL_USC_COMMON_SIZE_ALIGNSIZE * \ + ROGUE_CDMCTRL_USC_COMMON_SIZE_UPPER) >> 2) + +/* + ****************************************************************************** + * WA HWBRNs + ****************************************************************************** + */ + +/* GPU CR timer tick in GPU cycles */ +#define ROGUE_CRTIME_TICK_IN_CYCLES (256U) + +/* for nohw multicore return max cores possible to client */ +#define ROGUE_MULTICORE_MAX_NOHW_CORES (4U) + +/* + * If the size of the SLC is less than this value then the TPU bypasses the SLC. + */ +#define ROGUE_TPU_CACHED_SLC_SIZE_THRESHOLD (128U * 1024U) + +/* + * If the size of the SLC is bigger than this value then the TCU must not be + * bypassed in the SLC. + * In XE_MEMORY_HIERARCHY cores, the TCU is bypassed by default. + */ +#define ROGUE_TCU_CACHED_SLC_SIZE_THRESHOLD (32U * 1024U) + +/* + * Register used by the FW to track the current boot stage (not used in MIPS) + */ +#define ROGUE_FW_BOOT_STAGE_REGISTER (ROGUE_CR_POWER_ESTIMATE_RESULT) + +/* + * Virtualisation definitions + */ +#define ROGUE_VIRTUALISATION_REG_SIZE_PER_OS \ + (ROGUE_CR_MTS_SCHEDULE1 - ROGUE_CR_MTS_SCHEDULE) + +/* + * Macro used to indicate which version of HWPerf is active + */ +#define ROGUE_FEATURE_HWPERF_ROGUE + +/* + * Maximum number of cores supported by TRP + */ +#define ROGUE_TRP_MAX_NUM_CORES (4U) + +#endif /* PVR_ROGUE_DEFS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif.h new file mode 100644 index 000000000000..172886be4c82 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif.h @@ -0,0 +1,2188 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_H +#define PVR_ROGUE_FWIF_H + +#include <linux/bits.h> +#include <linux/build_bug.h> +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include "pvr_rogue_defs.h" +#include "pvr_rogue_fwif_common.h" +#include "pvr_rogue_fwif_shared.h" + +/* + **************************************************************************** + * Logging type + **************************************************************************** + */ +#define ROGUE_FWIF_LOG_TYPE_NONE 0x00000000U +#define ROGUE_FWIF_LOG_TYPE_TRACE 0x00000001U +#define ROGUE_FWIF_LOG_TYPE_GROUP_MAIN 0x00000002U +#define ROGUE_FWIF_LOG_TYPE_GROUP_MTS 0x00000004U +#define ROGUE_FWIF_LOG_TYPE_GROUP_CLEANUP 0x00000008U +#define ROGUE_FWIF_LOG_TYPE_GROUP_CSW 0x00000010U +#define ROGUE_FWIF_LOG_TYPE_GROUP_BIF 0x00000020U +#define ROGUE_FWIF_LOG_TYPE_GROUP_PM 0x00000040U +#define ROGUE_FWIF_LOG_TYPE_GROUP_RTD 0x00000080U +#define ROGUE_FWIF_LOG_TYPE_GROUP_SPM 0x00000100U +#define ROGUE_FWIF_LOG_TYPE_GROUP_POW 0x00000200U +#define ROGUE_FWIF_LOG_TYPE_GROUP_HWR 0x00000400U +#define ROGUE_FWIF_LOG_TYPE_GROUP_HWP 0x00000800U +#define ROGUE_FWIF_LOG_TYPE_GROUP_RPM 0x00001000U +#define ROGUE_FWIF_LOG_TYPE_GROUP_DMA 0x00002000U +#define ROGUE_FWIF_LOG_TYPE_GROUP_MISC 0x00004000U +#define ROGUE_FWIF_LOG_TYPE_GROUP_DEBUG 0x80000000U +#define ROGUE_FWIF_LOG_TYPE_GROUP_MASK 0x80007FFEU +#define ROGUE_FWIF_LOG_TYPE_MASK 0x80007FFFU + +/* String used in pvrdebug -h output */ +#define ROGUE_FWIF_LOG_GROUPS_STRING_LIST \ + "main,mts,cleanup,csw,bif,pm,rtd,spm,pow,hwr,hwp,rpm,dma,misc,debug" + +/* Table entry to map log group strings to log type value */ +struct rogue_fwif_log_group_map_entry { + const char *log_group_name; + u32 log_group_type; +}; + +/* + **************************************************************************** + * ROGUE FW signature checks + **************************************************************************** + */ +#define ROGUE_FW_SIG_BUFFER_SIZE_MIN (8192) + +#define ROGUE_FWIF_TIMEDIFF_ID ((0x1UL << 28) | ROGUE_CR_TIMER) + +/* + **************************************************************************** + * Trace Buffer + **************************************************************************** + */ + +/* Default size of ROGUE_FWIF_TRACEBUF_SPACE in DWords */ +#define ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS 12000U +#define ROGUE_FW_TRACE_BUFFER_ASSERT_SIZE 200U +#define ROGUE_FW_THREAD_NUM 1U +#define ROGUE_FW_THREAD_MAX 2U + +#define ROGUE_FW_POLL_TYPE_SET 0x80000000U + +struct rogue_fwif_file_info_buf { + char path[ROGUE_FW_TRACE_BUFFER_ASSERT_SIZE]; + char info[ROGUE_FW_TRACE_BUFFER_ASSERT_SIZE]; + u32 line_num; + u32 padding; +} __aligned(8); + +struct rogue_fwif_tracebuf_space { + u32 trace_pointer; + + u32 trace_buffer_fw_addr; + + /* To be used by host when reading from trace buffer */ + u32 *trace_buffer; + + struct rogue_fwif_file_info_buf assert_buf; +} __aligned(8); + +/* Total number of FW fault logs stored */ +#define ROGUE_FWIF_FWFAULTINFO_MAX (8U) + +struct rogue_fw_fault_info { + aligned_u64 cr_timer; + aligned_u64 os_timer; + + u32 data __aligned(8); + u32 reserved; + struct rogue_fwif_file_info_buf fault_buf; +} __aligned(8); + +enum rogue_fwif_pow_state { + ROGUE_FWIF_POW_OFF, /* idle and ready to full power down */ + ROGUE_FWIF_POW_ON, /* running HW commands */ + ROGUE_FWIF_POW_FORCED_IDLE, /* forced idle */ + ROGUE_FWIF_POW_IDLE, /* idle waiting for host handshake */ +}; + +/* Firmware HWR states */ +/* The HW state is ok or locked up */ +#define ROGUE_FWIF_HWR_HARDWARE_OK BIT(0) +/* Tells if a HWR reset is in progress */ +#define ROGUE_FWIF_HWR_RESET_IN_PROGRESS BIT(1) +/* A DM unrelated lockup has been detected */ +#define ROGUE_FWIF_HWR_GENERAL_LOCKUP BIT(3) +/* At least one DM is running without being close to a lockup */ +#define ROGUE_FWIF_HWR_DM_RUNNING_OK BIT(4) +/* At least one DM is close to lockup */ +#define ROGUE_FWIF_HWR_DM_STALLING BIT(5) +/* The FW has faulted and needs to restart */ +#define ROGUE_FWIF_HWR_FW_FAULT BIT(6) +/* The FW has requested the host to restart it */ +#define ROGUE_FWIF_HWR_RESTART_REQUESTED BIT(7) + +#define ROGUE_FWIF_PHR_STATE_SHIFT (8U) +/* The FW has requested the host to restart it, per PHR configuration */ +#define ROGUE_FWIF_PHR_RESTART_REQUESTED ((1) << ROGUE_FWIF_PHR_STATE_SHIFT) +/* A PHR triggered GPU reset has just finished */ +#define ROGUE_FWIF_PHR_RESTART_FINISHED ((2) << ROGUE_FWIF_PHR_STATE_SHIFT) +#define ROGUE_FWIF_PHR_RESTART_MASK \ + (ROGUE_FWIF_PHR_RESTART_REQUESTED | ROGUE_FWIF_PHR_RESTART_FINISHED) + +#define ROGUE_FWIF_PHR_MODE_OFF (0UL) +#define ROGUE_FWIF_PHR_MODE_RD_RESET (1UL) +#define ROGUE_FWIF_PHR_MODE_FULL_RESET (2UL) + +/* Firmware per-DM HWR states */ +/* DM is working if all flags are cleared */ +#define ROGUE_FWIF_DM_STATE_WORKING (0) +/* DM is idle and ready for HWR */ +#define ROGUE_FWIF_DM_STATE_READY_FOR_HWR BIT(0) +/* DM need to skip to next cmd before resuming processing */ +#define ROGUE_FWIF_DM_STATE_NEEDS_SKIP BIT(2) +/* DM need partial render cleanup before resuming processing */ +#define ROGUE_FWIF_DM_STATE_NEEDS_PR_CLEANUP BIT(3) +/* DM need to increment Recovery Count once fully recovered */ +#define ROGUE_FWIF_DM_STATE_NEEDS_TRACE_CLEAR BIT(4) +/* DM was identified as locking up and causing HWR */ +#define ROGUE_FWIF_DM_STATE_GUILTY_LOCKUP BIT(5) +/* DM was innocently affected by another lockup which caused HWR */ +#define ROGUE_FWIF_DM_STATE_INNOCENT_LOCKUP BIT(6) +/* DM was identified as over-running and causing HWR */ +#define ROGUE_FWIF_DM_STATE_GUILTY_OVERRUNING BIT(7) +/* DM was innocently affected by another DM over-running which caused HWR */ +#define ROGUE_FWIF_DM_STATE_INNOCENT_OVERRUNING BIT(8) +/* DM was forced into HWR as it delayed more important workloads */ +#define ROGUE_FWIF_DM_STATE_HARD_CONTEXT_SWITCH BIT(9) +/* DM was forced into HWR due to an uncorrected GPU ECC error */ +#define ROGUE_FWIF_DM_STATE_GPU_ECC_HWR BIT(10) + +/* Firmware's connection state */ +enum rogue_fwif_connection_fw_state { + /* Firmware is offline */ + ROGUE_FW_CONNECTION_FW_OFFLINE = 0, + /* Firmware is initialised */ + ROGUE_FW_CONNECTION_FW_READY, + /* Firmware connection is fully established */ + ROGUE_FW_CONNECTION_FW_ACTIVE, + /* Firmware is clearing up connection data*/ + ROGUE_FW_CONNECTION_FW_OFFLOADING, + ROGUE_FW_CONNECTION_FW_STATE_COUNT +}; + +/* OS' connection state */ +enum rogue_fwif_connection_os_state { + /* OS is offline */ + ROGUE_FW_CONNECTION_OS_OFFLINE = 0, + /* OS's KM driver is setup and waiting */ + ROGUE_FW_CONNECTION_OS_READY, + /* OS connection is fully established */ + ROGUE_FW_CONNECTION_OS_ACTIVE, + ROGUE_FW_CONNECTION_OS_STATE_COUNT +}; + +struct rogue_fwif_os_runtime_flags { + unsigned int os_state : 3; + unsigned int fl_ok : 1; + unsigned int fl_grow_pending : 1; + unsigned int isolated_os : 1; + unsigned int reserved : 26; +}; + +#define PVR_SLR_LOG_ENTRIES 10 +/* MAX_CLIENT_CCB_NAME not visible to this header */ +#define PVR_SLR_LOG_STRLEN 30 + +struct rogue_fwif_slr_entry { + aligned_u64 timestamp; + u32 fw_ctx_addr; + u32 num_ufos; + char ccb_name[PVR_SLR_LOG_STRLEN]; + char padding[2]; +} __aligned(8); + +#define MAX_THREAD_NUM 2 + +/* firmware trace control data */ +struct rogue_fwif_tracebuf { + u32 log_type; + struct rogue_fwif_tracebuf_space tracebuf[MAX_THREAD_NUM]; + /* + * Member initialised only when sTraceBuf is actually allocated (in + * ROGUETraceBufferInitOnDemandResources) + */ + u32 tracebuf_size_in_dwords; + /* Compatibility and other flags */ + u32 tracebuf_flags; +} __aligned(8); + +/* firmware system data shared with the Host driver */ +struct rogue_fwif_sysdata { + /* Configuration flags from host */ + u32 config_flags; + /* Extended configuration flags from host */ + u32 config_flags_ext; + enum rogue_fwif_pow_state pow_state; + u32 hw_perf_ridx; + u32 hw_perf_widx; + u32 hw_perf_wrap_count; + /* Constant after setup, needed in FW */ + u32 hw_perf_size; + /* The number of times the FW drops a packet due to buffer full */ + u32 hw_perf_drop_count; + + /* + * ui32HWPerfUt, ui32FirstDropOrdinal, ui32LastDropOrdinal only valid + * when FW is built with ROGUE_HWPERF_UTILIZATION & + * ROGUE_HWPERF_DROP_TRACKING defined in rogue_fw_hwperf.c + */ + /* Buffer utilisation, high watermark of bytes in use */ + u32 hw_perf_ut; + /* The ordinal of the first packet the FW dropped */ + u32 first_drop_ordinal; + /* The ordinal of the last packet the FW dropped */ + u32 last_drop_ordinal; + /* State flags for each Operating System mirrored from Fw coremem */ + struct rogue_fwif_os_runtime_flags + os_runtime_flags_mirror[ROGUE_FW_MAX_NUM_OS]; + + struct rogue_fw_fault_info fault_info[ROGUE_FWIF_FWFAULTINFO_MAX]; + u32 fw_faults; + u32 cr_poll_addr[MAX_THREAD_NUM]; + u32 cr_poll_mask[MAX_THREAD_NUM]; + u32 cr_poll_count[MAX_THREAD_NUM]; + aligned_u64 start_idle_time; + +#if defined(SUPPORT_ROGUE_FW_STATS_FRAMEWORK) +# define ROGUE_FWIF_STATS_FRAMEWORK_LINESIZE (8) +# define ROGUE_FWIF_STATS_FRAMEWORK_MAX \ + (2048 * ROGUE_FWIF_STATS_FRAMEWORK_LINESIZE) + u32 fw_stats_buf[ROGUE_FWIF_STATS_FRAMEWORK_MAX] __aligned(8); +#endif + u32 hwr_state_flags; + u32 hwr_recovery_flags[PVR_FWIF_DM_MAX]; + /* Compatibility and other flags */ + u32 fw_sys_data_flags; + /* Identify whether MC config is P-P or P-S */ + u32 mc_config; +} __aligned(8); + +/* per-os firmware shared data */ +struct rogue_fwif_osdata { + /* Configuration flags from an OS */ + u32 fw_os_config_flags; + /* Markers to signal that the host should perform a full sync check */ + u32 fw_sync_check_mark; + u32 host_sync_check_mark; + + u32 forced_updates_requested; + u8 slr_log_wp; + struct rogue_fwif_slr_entry slr_log_first; + struct rogue_fwif_slr_entry slr_log[PVR_SLR_LOG_ENTRIES]; + aligned_u64 last_forced_update_time; + + /* Interrupt count from Threads > */ + u32 interrupt_count[MAX_THREAD_NUM]; + u32 kccb_cmds_executed; + u32 power_sync_fw_addr; + /* Compatibility and other flags */ + u32 fw_os_data_flags; + u32 padding; +} __aligned(8); + +/* Firmware trace time-stamp field breakup */ + +/* ROGUE_CR_TIMER register read (48 bits) value*/ +#define ROGUE_FWT_TIMESTAMP_TIME_SHIFT (0U) +#define ROGUE_FWT_TIMESTAMP_TIME_CLRMSK (0xFFFF000000000000ull) + +/* Extra debug-info (16 bits) */ +#define ROGUE_FWT_TIMESTAMP_DEBUG_INFO_SHIFT (48U) +#define ROGUE_FWT_TIMESTAMP_DEBUG_INFO_CLRMSK ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK + +/* Debug-info sub-fields */ +/* + * Bit 0: ROGUE_CR_EVENT_STATUS_MMU_PAGE_FAULT bit from ROGUE_CR_EVENT_STATUS + * register + */ +#define ROGUE_FWT_DEBUG_INFO_MMU_PAGE_FAULT_SHIFT (0U) +#define ROGUE_FWT_DEBUG_INFO_MMU_PAGE_FAULT_SET \ + BIT(ROGUE_FWT_DEBUG_INFO_MMU_PAGE_FAULT_SHIFT) + +/* Bit 1: ROGUE_CR_BIF_MMU_ENTRY_PENDING bit from ROGUE_CR_BIF_MMU_ENTRY register */ +#define ROGUE_FWT_DEBUG_INFO_MMU_ENTRY_PENDING_SHIFT (1U) +#define ROGUE_FWT_DEBUG_INFO_MMU_ENTRY_PENDING_SET \ + BIT(ROGUE_FWT_DEBUG_INFO_MMU_ENTRY_PENDING_SHIFT) + +/* Bit 2: ROGUE_CR_SLAVE_EVENT register is non-zero */ +#define ROGUE_FWT_DEBUG_INFO_SLAVE_EVENTS_SHIFT (2U) +#define ROGUE_FWT_DEBUG_INFO_SLAVE_EVENTS_SET \ + BIT(ROGUE_FWT_DEBUG_INFO_SLAVE_EVENTS_SHIFT) + +/* Bit 3-15: Unused bits */ + +#define ROGUE_FWT_DEBUG_INFO_STR_MAXLEN 64 +#define ROGUE_FWT_DEBUG_INFO_STR_PREPEND " (debug info: " +#define ROGUE_FWT_DEBUG_INFO_STR_APPEND ")" + +/* + ****************************************************************************** + * HWR Data + ****************************************************************************** + */ +enum rogue_hwrtype { + ROGUE_HWRTYPE_UNKNOWNFAILURE = 0, + ROGUE_HWRTYPE_OVERRUN = 1, + ROGUE_HWRTYPE_POLLFAILURE = 2, + ROGUE_HWRTYPE_BIF0FAULT = 3, + ROGUE_HWRTYPE_BIF1FAULT = 4, + ROGUE_HWRTYPE_TEXASBIF0FAULT = 5, + ROGUE_HWRTYPE_MMUFAULT = 6, + ROGUE_HWRTYPE_MMUMETAFAULT = 7, + ROGUE_HWRTYPE_MIPSTLBFAULT = 8, + ROGUE_HWRTYPE_ECCFAULT = 9, + ROGUE_HWRTYPE_MMURISCVFAULT = 10, +}; + +#define ROGUE_FWIF_HWRTYPE_BIF_BANK_GET(hwr_type) \ + (((hwr_type) == ROGUE_HWRTYPE_BIF0FAULT) ? 0 : 1) + +#define ROGUE_FWIF_HWRTYPE_PAGE_FAULT_GET(hwr_type) \ + ((((hwr_type) == ROGUE_HWRTYPE_BIF0FAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_BIF1FAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_TEXASBIF0FAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_MMUFAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_MMUMETAFAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_MIPSTLBFAULT) || \ + ((hwr_type) == ROGUE_HWRTYPE_MMURISCVFAULT)) \ + ? true \ + : false) + +struct rogue_bifinfo { + aligned_u64 bif_req_status; + aligned_u64 bif_mmu_status; + aligned_u64 pc_address; /* phys address of the page catalogue */ + aligned_u64 reserved; +}; + +struct rogue_eccinfo { + u32 fault_gpu; +}; + +struct rogue_mmuinfo { + aligned_u64 mmu_status[2]; + aligned_u64 pc_address; /* phys address of the page catalogue */ + aligned_u64 reserved; +}; + +struct rogue_pollinfo { + u32 thread_num; + u32 cr_poll_addr; + u32 cr_poll_mask; + u32 cr_poll_last_value; + aligned_u64 reserved; +} __aligned(8); + +struct rogue_tlbinfo { + u32 bad_addr; + u32 entry_lo; +}; + +struct rogue_hwrinfo { + union { + struct rogue_bifinfo bif_info; + struct rogue_mmuinfo mmu_info; + struct rogue_pollinfo poll_info; + struct rogue_tlbinfo tlb_info; + struct rogue_eccinfo ecc_info; + } hwr_data; + + aligned_u64 cr_timer; + aligned_u64 os_timer; + u32 frame_num; + u32 pid; + u32 active_hwrt_data; + u32 hwr_number; + u32 event_status; + u32 hwr_recovery_flags; + enum rogue_hwrtype hwr_type; + u32 dm; + u32 core_id; + aligned_u64 cr_time_of_kick; + aligned_u64 cr_time_hw_reset_start; + aligned_u64 cr_time_hw_reset_finish; + aligned_u64 cr_time_freelist_ready; + aligned_u64 reserved[2]; +} __aligned(8); + +/* Number of first HWR logs recorded (never overwritten by newer logs) */ +#define ROGUE_FWIF_HWINFO_MAX_FIRST 8U +/* Number of latest HWR logs (older logs are overwritten by newer logs) */ +#define ROGUE_FWIF_HWINFO_MAX_LAST 8U +/* Total number of HWR logs stored in a buffer */ +#define ROGUE_FWIF_HWINFO_MAX \ + (ROGUE_FWIF_HWINFO_MAX_FIRST + ROGUE_FWIF_HWINFO_MAX_LAST) +/* Index of the last log in the HWR log buffer */ +#define ROGUE_FWIF_HWINFO_LAST_INDEX (ROGUE_FWIF_HWINFO_MAX - 1U) + +struct rogue_fwif_hwrinfobuf { + struct rogue_hwrinfo hwr_info[ROGUE_FWIF_HWINFO_MAX]; + u32 hwr_counter; + u32 write_index; + u32 dd_req_count; + u32 hwr_info_buf_flags; /* Compatibility and other flags */ + u32 hwr_dm_locked_up_count[PVR_FWIF_DM_MAX]; + u32 hwr_dm_overran_count[PVR_FWIF_DM_MAX]; + u32 hwr_dm_recovered_count[PVR_FWIF_DM_MAX]; + u32 hwr_dm_false_detect_count[PVR_FWIF_DM_MAX]; +} __aligned(8); + +#define ROGUE_FWIF_CTXSWITCH_PROFILE_FAST_EN (1) +#define ROGUE_FWIF_CTXSWITCH_PROFILE_MEDIUM_EN (2) +#define ROGUE_FWIF_CTXSWITCH_PROFILE_SLOW_EN (3) +#define ROGUE_FWIF_CTXSWITCH_PROFILE_NODELAY_EN (4) + +#define ROGUE_FWIF_CDM_ARBITRATION_TASK_DEMAND_EN (1) +#define ROGUE_FWIF_CDM_ARBITRATION_ROUND_ROBIN_EN (2) + +#define ROGUE_FWIF_ISP_SCHEDMODE_VER1_IPP (1) +#define ROGUE_FWIF_ISP_SCHEDMODE_VER2_ISP (2) +/* + ****************************************************************************** + * ROGUE firmware Init Config Data + ****************************************************************************** + */ + +/* Flag definitions affecting the firmware globally */ +#define ROGUE_FWIF_INICFG_CTXSWITCH_MODE_RAND BIT(0) +#define ROGUE_FWIF_INICFG_CTXSWITCH_SRESET_EN BIT(1) +#define ROGUE_FWIF_INICFG_HWPERF_EN BIT(2) +#define ROGUE_FWIF_INICFG_DM_KILL_MODE_RAND_EN BIT(3) +#define ROGUE_FWIF_INICFG_POW_RASCALDUST BIT(4) +/* Bit 5 is reserved. */ +#define ROGUE_FWIF_INICFG_FBCDC_V3_1_EN BIT(6) +#define ROGUE_FWIF_INICFG_CHECK_MLIST_EN BIT(7) +#define ROGUE_FWIF_INICFG_DISABLE_CLKGATING_EN BIT(8) +/* Bit 9 is reserved. */ +/* Bit 10 is reserved. */ +/* Bit 11 is reserved. */ +#define ROGUE_FWIF_INICFG_REGCONFIG_EN BIT(12) +#define ROGUE_FWIF_INICFG_ASSERT_ON_OUTOFMEMORY BIT(13) +#define ROGUE_FWIF_INICFG_HWP_DISABLE_FILTER BIT(14) +/* Bit 15 is reserved. */ +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT (16) +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_FAST \ + (ROGUE_FWIF_CTXSWITCH_PROFILE_FAST_EN \ + << ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT) +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_MEDIUM \ + (ROGUE_FWIF_CTXSWITCH_PROFILE_MEDIUM_EN \ + << ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT) +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SLOW \ + (ROGUE_FWIF_CTXSWITCH_PROFILE_SLOW_EN \ + << ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT) +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_NODELAY \ + (ROGUE_FWIF_CTXSWITCH_PROFILE_NODELAY_EN \ + << ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT) +#define ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_MASK \ + (7 << ROGUE_FWIF_INICFG_CTXSWITCH_PROFILE_SHIFT) +#define ROGUE_FWIF_INICFG_DISABLE_DM_OVERLAP BIT(19) +#define ROGUE_FWIF_INICFG_ASSERT_ON_HWR_TRIGGER BIT(20) +#define ROGUE_FWIF_INICFG_FABRIC_COHERENCY_ENABLED BIT(21) +#define ROGUE_FWIF_INICFG_VALIDATE_IRQ BIT(22) +#define ROGUE_FWIF_INICFG_DISABLE_PDP_EN BIT(23) +#define ROGUE_FWIF_INICFG_SPU_POWER_STATE_MASK_CHANGE_EN BIT(24) +#define ROGUE_FWIF_INICFG_WORKEST BIT(25) +#define ROGUE_FWIF_INICFG_PDVFS BIT(26) +#define ROGUE_FWIF_INICFG_CDM_ARBITRATION_SHIFT (27) +#define ROGUE_FWIF_INICFG_CDM_ARBITRATION_TASK_DEMAND \ + (ROGUE_FWIF_CDM_ARBITRATION_TASK_DEMAND_EN \ + << ROGUE_FWIF_INICFG_CDM_ARBITRATION_SHIFT) +#define ROGUE_FWIF_INICFG_CDM_ARBITRATION_ROUND_ROBIN \ + (ROGUE_FWIF_CDM_ARBITRATION_ROUND_ROBIN_EN \ + << ROGUE_FWIF_INICFG_CDM_ARBITRATION_SHIFT) +#define ROGUE_FWIF_INICFG_CDM_ARBITRATION_MASK \ + (3 << ROGUE_FWIF_INICFG_CDM_ARBITRATION_SHIFT) +#define ROGUE_FWIF_INICFG_ISPSCHEDMODE_SHIFT (29) +#define ROGUE_FWIF_INICFG_ISPSCHEDMODE_NONE (0) +#define ROGUE_FWIF_INICFG_ISPSCHEDMODE_VER1_IPP \ + (ROGUE_FWIF_ISP_SCHEDMODE_VER1_IPP \ + << ROGUE_FWIF_INICFG_ISPSCHEDMODE_SHIFT) +#define ROGUE_FWIF_INICFG_ISPSCHEDMODE_VER2_ISP \ + (ROGUE_FWIF_ISP_SCHEDMODE_VER2_ISP \ + << ROGUE_FWIF_INICFG_ISPSCHEDMODE_SHIFT) +#define ROGUE_FWIF_INICFG_ISPSCHEDMODE_MASK \ + (ROGUE_FWIF_INICFG_ISPSCHEDMODE_VER1_IPP | \ + ROGUE_FWIF_INICFG_ISPSCHEDMODE_VER2_ISP) +#define ROGUE_FWIF_INICFG_VALIDATE_SOCUSC_TIMER BIT(31) + +#define ROGUE_FWIF_INICFG_ALL (0xFFFFFFFFU) + +/* Extended Flag definitions affecting the firmware globally */ +#define ROGUE_FWIF_INICFG_EXT_TFBC_CONTROL_SHIFT (0) +/* [7] YUV10 override + * [6:4] Quality + * [3] Quality enable + * [2:1] Compression scheme + * [0] Lossy group + */ +#define ROGUE_FWIF_INICFG_EXT_TFBC_CONTROL_MASK (0xFF) +#define ROGUE_FWIF_INICFG_EXT_ALL (ROGUE_FWIF_INICFG_EXT_TFBC_CONTROL_MASK) + +/* Flag definitions affecting only workloads submitted by a particular OS */ +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_TDM_EN BIT(0) +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_GEOM_EN BIT(1) +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_FRAG_EN BIT(2) +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_CDM_EN BIT(3) + +#define ROGUE_FWIF_INICFG_OS_LOW_PRIO_CS_TDM BIT(4) +#define ROGUE_FWIF_INICFG_OS_LOW_PRIO_CS_GEOM BIT(5) +#define ROGUE_FWIF_INICFG_OS_LOW_PRIO_CS_FRAG BIT(6) +#define ROGUE_FWIF_INICFG_OS_LOW_PRIO_CS_CDM BIT(7) + +#define ROGUE_FWIF_INICFG_OS_ALL (0xFF) + +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_DM_ALL \ + (ROGUE_FWIF_INICFG_OS_CTXSWITCH_TDM_EN | \ + ROGUE_FWIF_INICFG_OS_CTXSWITCH_GEOM_EN | \ + ROGUE_FWIF_INICFG_OS_CTXSWITCH_FRAG_EN | \ + ROGUE_FWIF_INICFG_OS_CTXSWITCH_CDM_EN) + +#define ROGUE_FWIF_INICFG_OS_CTXSWITCH_CLRMSK \ + ~(ROGUE_FWIF_INICFG_OS_CTXSWITCH_DM_ALL) + +#define ROGUE_FWIF_FILTCFG_TRUNCATE_HALF BIT(3) +#define ROGUE_FWIF_FILTCFG_TRUNCATE_INT BIT(2) +#define ROGUE_FWIF_FILTCFG_NEW_FILTER_MODE BIT(1) + +enum rogue_activepm_conf { + ROGUE_ACTIVEPM_FORCE_OFF = 0, + ROGUE_ACTIVEPM_FORCE_ON = 1, + ROGUE_ACTIVEPM_DEFAULT = 2 +}; + +enum rogue_rd_power_island_conf { + ROGUE_RD_POWER_ISLAND_FORCE_OFF = 0, + ROGUE_RD_POWER_ISLAND_FORCE_ON = 1, + ROGUE_RD_POWER_ISLAND_DEFAULT = 2 +}; + +struct rogue_fw_register_list { + /* Register number */ + u16 reg_num; + /* Indirect register number (or 0 if not used) */ + u16 indirect_reg_num; + /* Start value for indirect register */ + u16 indirect_start_val; + /* End value for indirect register */ + u16 indirect_end_val; +}; + +struct rogue_fwif_dllist_node { + u32 p; + u32 n; +}; + +/* + * This number is used to represent an invalid page catalogue physical address + */ +#define ROGUE_FWIF_INVALID_PC_PHYADDR 0xFFFFFFFFFFFFFFFFLLU + +/* This number is used to represent unallocated page catalog base register */ +#define ROGUE_FW_BIF_INVALID_PCSET 0xFFFFFFFFU + +/* Firmware memory context. */ +struct rogue_fwif_fwmemcontext { + /* device physical address of context's page catalogue */ + aligned_u64 pc_dev_paddr; + /* + * associated page catalog base register (ROGUE_FW_BIF_INVALID_PCSET == + * unallocated) + */ + u32 page_cat_base_reg_set; + /* breakpoint address */ + u32 breakpoint_addr; + /* breakpoint handler address */ + u32 bp_handler_addr; + /* DM and enable control for BP */ + u32 breakpoint_ctl; + /* Compatibility and other flags */ + u32 fw_mem_ctx_flags; + u32 padding; +} __aligned(8); + +/* + * FW context state flags + */ +#define ROGUE_FWIF_CONTEXT_FLAGS_NEED_RESUME (0x00000001U) +#define ROGUE_FWIF_CONTEXT_FLAGS_MC_NEED_RESUME_MASKFULL (0x000000FFU) +#define ROGUE_FWIF_CONTEXT_FLAGS_TDM_HEADER_STALE (0x00000100U) +#define ROGUE_FWIF_CONTEXT_FLAGS_LAST_KICK_SECURE (0x00000200U) + +#define ROGUE_NUM_GEOM_CORES_MAX 4 + +/* + * FW-accessible TA state which must be written out to memory on context store + */ +struct rogue_fwif_geom_ctx_state_per_geom { + /* To store in mid-TA */ + aligned_u64 geom_reg_vdm_call_stack_pointer; + /* Initial value (in case is 'lost' due to a lock-up */ + aligned_u64 geom_reg_vdm_call_stack_pointer_init; + u32 geom_reg_vbs_so_prim[4]; + u16 geom_current_idx; + u16 padding[3]; +} __aligned(8); + +struct rogue_fwif_geom_ctx_state { + /* FW-accessible TA state which must be written out to memory on context store */ + struct rogue_fwif_geom_ctx_state_per_geom geom_core[ROGUE_NUM_GEOM_CORES_MAX]; +} __aligned(8); + +/* + * FW-accessible ISP state which must be written out to memory on context store + */ +struct rogue_fwif_frag_ctx_state { + u32 frag_reg_pm_deallocated_mask_status; + u32 frag_reg_dm_pds_mtilefree_status; + /* Compatibility and other flags */ + u32 ctx_state_flags; + /* + * frag_reg_isp_store should be the last element of the structure as this + * is an array whose size is determined at runtime after detecting the + * ROGUE core + */ + u32 frag_reg_isp_store[]; +} __aligned(8); + +#define ROGUE_FWIF_CTX_USING_BUFFER_A (0) +#define ROGUE_FWIF_CTX_USING_BUFFER_B (1U) + +struct rogue_fwif_compute_ctx_state { + u32 ctx_state_flags; /* Target buffer and other flags */ +}; + +struct rogue_fwif_fwcommoncontext { + /* CCB details for this firmware context */ + u32 ccbctl_fw_addr; /* CCB control */ + u32 ccb_fw_addr; /* CCB base */ + struct rogue_fwif_dma_addr ccb_meta_dma_addr; + + /* Context suspend state */ + /* geom/frag context suspend state, read/written by FW */ + u32 context_state_addr __aligned(8); + + /* Flags e.g. for context switching */ + u32 fw_com_ctx_flags; + u32 priority; + u32 priority_seq_num; + + /* Framework state */ + /* Register updates for Framework */ + u32 rf_cmd_addr __aligned(8); + + /* Statistic updates waiting to be passed back to the host... */ + /* True when some stats are pending */ + bool stats_pending __aligned(4); + /* Number of stores on this context since last update */ + s32 stats_num_stores; + /* Number of OOMs on this context since last update */ + s32 stats_num_out_of_memory; + /* Number of PRs on this context since last update */ + s32 stats_num_partial_renders; + /* Data Master type */ + u32 dm; + /* Device Virtual Address of the signal the context is waiting on */ + aligned_u64 wait_signal_address; + /* List entry for the wait-signal list */ + struct rogue_fwif_dllist_node wait_signal_node __aligned(8); + /* List entry for the buffer stalled list */ + struct rogue_fwif_dllist_node buf_stalled_node __aligned(8); + /* Address of the circular buffer queue pointers */ + aligned_u64 cbuf_queue_ctrl_addr; + + aligned_u64 robustness_address; + /* Max HWR deadline limit in ms */ + u32 max_deadline_ms; + /* Following HWR circular buffer read-offset needs resetting */ + bool read_offset_needs_reset; + + /* List entry for the waiting list */ + struct rogue_fwif_dllist_node waiting_node __aligned(8); + /* List entry for the run list */ + struct rogue_fwif_dllist_node run_node __aligned(8); + /* UFO that last failed (or NULL) */ + struct rogue_fwif_ufo last_failed_ufo; + + /* Memory context */ + u32 fw_mem_context_fw_addr; + + /* References to the host side originators */ + /* the Server Common Context */ + u32 server_common_context_id; + /* associated process ID */ + u32 pid; + + /* True when Geom DM OOM is not allowed */ + bool geom_oom_disabled __aligned(4); +} __aligned(8); + +/* Firmware render context. */ +struct rogue_fwif_fwrendercontext { + /* Geometry firmware context. */ + struct rogue_fwif_fwcommoncontext geom_context; + /* Fragment firmware context. */ + struct rogue_fwif_fwcommoncontext frag_context; + + struct rogue_fwif_static_rendercontext_state static_render_context_state; + + /* Number of commands submitted to the WorkEst FW CCB */ + u32 work_est_ccb_submitted; + + /* Compatibility and other flags */ + u32 fw_render_ctx_flags; +} __aligned(8); + +/* Firmware compute context. */ +struct rogue_fwif_fwcomputecontext { + /* Firmware context for the CDM */ + struct rogue_fwif_fwcommoncontext cdm_context; + + struct rogue_fwif_static_computecontext_state + static_compute_context_state; + + /* Number of commands submitted to the WorkEst FW CCB */ + u32 work_est_ccb_submitted; + + /* Compatibility and other flags */ + u32 compute_ctx_flags; + + u32 wgp_state; + u32 wgp_checksum; + u32 core_mask_a; + u32 core_mask_b; +} __aligned(8); + +/* Firmware TDM context. */ +struct rogue_fwif_fwtdmcontext { + /* Firmware context for the TDM */ + struct rogue_fwif_fwcommoncontext tdm_context; + + /* Number of commands submitted to the WorkEst FW CCB */ + u32 work_est_ccb_submitted; +} __aligned(8); + +/* Firmware TQ3D context. */ +struct rogue_fwif_fwtransfercontext { + /* Firmware context for TQ3D. */ + struct rogue_fwif_fwcommoncontext tq_context; +} __aligned(8); + +/* + ****************************************************************************** + * Defines for CMD_TYPE corruption detection and forward compatibility check + ****************************************************************************** + */ + +/* + * CMD_TYPE 32bit contains: + * 31:16 Reserved for magic value to detect corruption (16 bits) + * 15 Reserved for ROGUE_CCB_TYPE_TASK (1 bit) + * 14:0 Bits available for CMD_TYPEs (15 bits) + */ + +/* Magic value to detect corruption */ +#define ROGUE_CMD_MAGIC_DWORD (0x2ABC) +#define ROGUE_CMD_MAGIC_DWORD_MASK (0xFFFF0000U) +#define ROGUE_CMD_MAGIC_DWORD_SHIFT (16U) +#define ROGUE_CMD_MAGIC_DWORD_SHIFTED \ + (ROGUE_CMD_MAGIC_DWORD << ROGUE_CMD_MAGIC_DWORD_SHIFT) + +/* Kernel CCB control for ROGUE */ +struct rogue_fwif_ccb_ctl { + /* write offset into array of commands (MUST be aligned to 16 bytes!) */ + u32 write_offset; + /* Padding to ensure read and write offsets are in separate cache lines. */ + u8 padding[128 - sizeof(u32)]; + /* read offset into array of commands */ + u32 read_offset; + /* Offset wrapping mask (Total capacity of the CCB - 1) */ + u32 wrap_mask; + /* size of each command in bytes */ + u32 cmd_size; + u32 padding2; +} __aligned(8); + +/* Kernel CCB command structure for ROGUE */ + +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_PT (0x1U) /* MMU_CTRL_INVAL_PT_EN */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_PD (0x2U) /* MMU_CTRL_INVAL_PD_EN */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_PC (0x4U) /* MMU_CTRL_INVAL_PC_EN */ + +/* + * can't use PM_TLB0 bit from BIFPM_CTRL reg because it collides with PT + * bit from BIF_CTRL reg + */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_PMTLB (0x10) +/* BIF_CTRL_INVAL_TLB1_EN */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_TLB \ + (ROGUE_FWIF_MMUCACHEDATA_FLAGS_PMTLB | 0x8) +/* MMU_CTRL_INVAL_ALL_CONTEXTS_EN */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_CTX_ALL (0x800) + +/* indicates FW should interrupt the host */ +#define ROGUE_FWIF_MMUCACHEDATA_FLAGS_INTERRUPT (0x4000000U) + +struct rogue_fwif_mmucachedata { + u32 cache_flags; + u32 mmu_cache_sync_fw_addr; + u32 mmu_cache_sync_update_value; +}; + +#define ROGUE_FWIF_BPDATA_FLAGS_ENABLE BIT(0) +#define ROGUE_FWIF_BPDATA_FLAGS_WRITE BIT(1) +#define ROGUE_FWIF_BPDATA_FLAGS_CTL BIT(2) +#define ROGUE_FWIF_BPDATA_FLAGS_REGS BIT(3) + +struct rogue_fwif_bpdata { + /* Memory context */ + u32 fw_mem_context_fw_addr; + /* Breakpoint address */ + u32 bp_addr; + /* Breakpoint handler */ + u32 bp_handler_addr; + /* Breakpoint control */ + u32 bp_dm; + u32 bp_data_flags; + /* Number of temporary registers to overallocate */ + u32 temp_regs; + /* Number of shared registers to overallocate */ + u32 shared_regs; + /* DM associated with the breakpoint */ + u32 dm; +}; + +#define ROGUE_FWIF_KCCB_CMD_KICK_DATA_MAX_NUM_CLEANUP_CTLS \ + (ROGUE_FWIF_PRBUFFER_MAXSUPPORTED + 1U) /* +1 is RTDATASET cleanup */ + +struct rogue_fwif_kccb_cmd_kick_data { + /* address of the firmware context */ + u32 context_fw_addr; + /* Client CCB woff update */ + u32 client_woff_update; + /* Client CCB wrap mask update after CCCB growth */ + u32 client_wrap_mask_update; + /* number of CleanupCtl pointers attached */ + u32 num_cleanup_ctl; + /* CleanupCtl structures associated with command */ + u32 cleanup_ctl_fw_addr + [ROGUE_FWIF_KCCB_CMD_KICK_DATA_MAX_NUM_CLEANUP_CTLS]; + /* + * offset to the CmdHeader which houses the workload estimation kick + * data. + */ + u32 work_est_cmd_header_offset; +}; + +struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data { + struct rogue_fwif_kccb_cmd_kick_data geom_cmd_kick_data; + struct rogue_fwif_kccb_cmd_kick_data frag_cmd_kick_data; +}; + +struct rogue_fwif_kccb_cmd_force_update_data { + /* address of the firmware context */ + u32 context_fw_addr; + /* Client CCB fence offset */ + u32 ccb_fence_offset; +}; + +enum rogue_fwif_cleanup_type { + /* FW common context cleanup */ + ROGUE_FWIF_CLEANUP_FWCOMMONCONTEXT, + /* FW HW RT data cleanup */ + ROGUE_FWIF_CLEANUP_HWRTDATA, + /* FW freelist cleanup */ + ROGUE_FWIF_CLEANUP_FREELIST, + /* FW ZS Buffer cleanup */ + ROGUE_FWIF_CLEANUP_ZSBUFFER, +}; + +struct rogue_fwif_cleanup_request { + /* Cleanup type */ + enum rogue_fwif_cleanup_type cleanup_type; + union { + /* FW common context to cleanup */ + u32 context_fw_addr; + /* HW RT to cleanup */ + u32 hwrt_data_fw_addr; + /* Freelist to cleanup */ + u32 freelist_fw_addr; + /* ZS Buffer to cleanup */ + u32 zs_buffer_fw_addr; + } cleanup_data; +}; + +enum rogue_fwif_power_type { + ROGUE_FWIF_POW_OFF_REQ = 1, + ROGUE_FWIF_POW_FORCED_IDLE_REQ, + ROGUE_FWIF_POW_NUM_UNITS_CHANGE, + ROGUE_FWIF_POW_APM_LATENCY_CHANGE +}; + +enum rogue_fwif_power_force_idle_type { + ROGUE_FWIF_POWER_FORCE_IDLE = 1, + ROGUE_FWIF_POWER_CANCEL_FORCED_IDLE, + ROGUE_FWIF_POWER_HOST_TIMEOUT, +}; + +struct rogue_fwif_power_request { + /* Type of power request */ + enum rogue_fwif_power_type pow_type; + union { + /* Number of active Dusts */ + u32 num_of_dusts; + /* If the operation is mandatory */ + bool forced __aligned(4); + /* + * Type of Request. Consolidating Force Idle, Cancel Forced + * Idle, Host Timeout + */ + enum rogue_fwif_power_force_idle_type pow_request_type; + } power_req_data; +}; + +struct rogue_fwif_slcflushinvaldata { + /* Context to fence on (only useful when bDMContext == TRUE) */ + u32 context_fw_addr; + /* Invalidate the cache as well as flushing */ + bool inval __aligned(4); + /* The data to flush/invalidate belongs to a specific DM context */ + bool dm_context __aligned(4); + /* Optional address of range (only useful when bDMContext == FALSE) */ + aligned_u64 address; + /* Optional size of range (only useful when bDMContext == FALSE) */ + aligned_u64 size; +}; + +enum rogue_fwif_hwperf_update_config { + ROGUE_FWIF_HWPERF_CTRL_TOGGLE = 0, + ROGUE_FWIF_HWPERF_CTRL_SET = 1, + ROGUE_FWIF_HWPERF_CTRL_EMIT_FEATURES_EV = 2 +}; + +struct rogue_fwif_hwperf_ctrl { + enum rogue_fwif_hwperf_update_config opcode; /* Control operation code */ + aligned_u64 mask; /* Mask of events to toggle */ +}; + +struct rogue_fwif_hwperf_config_enable_blks { + /* Number of ROGUE_HWPERF_CONFIG_MUX_CNTBLK in the array */ + u32 num_blocks; + /* Address of the ROGUE_HWPERF_CONFIG_MUX_CNTBLK array */ + u32 block_configs_fw_addr; +}; + +struct rogue_fwif_hwperf_config_da_blks { + /* Number of ROGUE_HWPERF_CONFIG_CNTBLK in the array */ + u32 num_blocks; + /* Address of the ROGUE_HWPERF_CONFIG_CNTBLK array */ + u32 block_configs_fw_addr; +}; + +struct rogue_fwif_coreclkspeedchange_data { + u32 new_clock_speed; /* New clock speed */ +}; + +#define ROGUE_FWIF_HWPERF_CTRL_BLKS_MAX 16 + +struct rogue_fwif_hwperf_ctrl_blks { + bool enable; + /* Number of block IDs in the array */ + u32 num_blocks; + /* Array of ROGUE_HWPERF_CNTBLK_ID values */ + u16 block_ids[ROGUE_FWIF_HWPERF_CTRL_BLKS_MAX]; +}; + +struct rogue_fwif_hwperf_select_custom_cntrs { + u16 custom_block; + u16 num_counters; + u32 custom_counter_ids_fw_addr; +}; + +struct rogue_fwif_zsbuffer_backing_data { + u32 zs_buffer_fw_addr; /* ZS-Buffer FW address */ + + bool done __aligned(4); /* action backing/unbacking succeeded */ +}; + +struct rogue_fwif_freelist_gs_data { + /* Freelist FW address */ + u32 freelist_fw_addr; + /* Amount of the Freelist change */ + u32 delta_pages; + /* New amount of pages on the freelist (including ready pages) */ + u32 new_pages; + /* Number of ready pages to be held in reserve until OOM */ + u32 ready_pages; +}; + +#define MAX_FREELISTS_SIZE 3 +#define MAX_HW_GEOM_FRAG_CONTEXTS_SIZE 3 + +#define ROGUE_FWIF_MAX_FREELISTS_TO_RECONSTRUCT \ + (MAX_HW_GEOM_FRAG_CONTEXTS_SIZE * MAX_FREELISTS_SIZE * 2U) +#define ROGUE_FWIF_FREELISTS_RECONSTRUCTION_FAILED_FLAG 0x80000000U + +struct rogue_fwif_freelists_reconstruction_data { + u32 freelist_count; + u32 freelist_ids[ROGUE_FWIF_MAX_FREELISTS_TO_RECONSTRUCT]; +}; + +struct rogue_fwif_write_offset_update_data { + /* + * Context to that may need to be resumed following write offset update + */ + u32 context_fw_addr; +} __aligned(8); + +/* + ****************************************************************************** + * Proactive DVFS Structures + ****************************************************************************** + */ +#define NUM_OPP_VALUES 16 + +struct pdvfs_opp { + u32 volt; /* V */ + u32 freq; /* Hz */ +} __aligned(8); + +struct rogue_fwif_pdvfs_opp { + struct pdvfs_opp opp_values[NUM_OPP_VALUES]; + u32 min_opp_point; + u32 max_opp_point; +} __aligned(8); + +struct rogue_fwif_pdvfs_max_freq_data { + u32 max_opp_point; +} __aligned(8); + +struct rogue_fwif_pdvfs_min_freq_data { + u32 min_opp_point; +} __aligned(8); + +/* + ****************************************************************************** + * Register configuration structures + ****************************************************************************** + */ + +#define ROGUE_FWIF_REG_CFG_MAX_SIZE 512 + +enum rogue_fwif_regdata_cmd_type { + ROGUE_FWIF_REGCFG_CMD_ADD = 101, + ROGUE_FWIF_REGCFG_CMD_CLEAR = 102, + ROGUE_FWIF_REGCFG_CMD_ENABLE = 103, + ROGUE_FWIF_REGCFG_CMD_DISABLE = 104 +}; + +enum rogue_fwif_reg_cfg_type { + /* Sidekick power event */ + ROGUE_FWIF_REG_CFG_TYPE_PWR_ON = 0, + /* Rascal / dust power event */ + ROGUE_FWIF_REG_CFG_TYPE_DUST_CHANGE, + /* Geometry kick */ + ROGUE_FWIF_REG_CFG_TYPE_GEOM, + /* Fragment kick */ + ROGUE_FWIF_REG_CFG_TYPE_FRAG, + /* Compute kick */ + ROGUE_FWIF_REG_CFG_TYPE_CDM, + /* TLA kick */ + ROGUE_FWIF_REG_CFG_TYPE_TLA, + /* TDM kick */ + ROGUE_FWIF_REG_CFG_TYPE_TDM, + /* Applies to all types. Keep as last element */ + ROGUE_FWIF_REG_CFG_TYPE_ALL +}; + +struct rogue_fwif_reg_cfg_rec { + u64 sddr; + u64 mask; + u64 value; +}; + +struct rogue_fwif_regconfig_data { + enum rogue_fwif_regdata_cmd_type cmd_type; + enum rogue_fwif_reg_cfg_type reg_config_type; + struct rogue_fwif_reg_cfg_rec reg_config __aligned(8); +}; + +struct rogue_fwif_reg_cfg { + /* + * PDump WRW command write granularity is 32 bits. + * Add padding to ensure array size is 32 bit granular. + */ + u8 num_regs_type[ALIGN((u32)ROGUE_FWIF_REG_CFG_TYPE_ALL, + sizeof(u32))] __aligned(8); + struct rogue_fwif_reg_cfg_rec + reg_configs[ROGUE_FWIF_REG_CFG_MAX_SIZE] __aligned(8); +} __aligned(8); + +enum rogue_fwif_os_state_change { + ROGUE_FWIF_OS_ONLINE = 1, + ROGUE_FWIF_OS_OFFLINE +}; + +struct rogue_fwif_os_state_change_data { + u32 osid; + enum rogue_fwif_os_state_change new_os_state; +} __aligned(8); + +enum rogue_fwif_counter_dump_request { + ROGUE_FWIF_PWR_COUNTER_DUMP_START = 1, + ROGUE_FWIF_PWR_COUNTER_DUMP_STOP, + ROGUE_FWIF_PWR_COUNTER_DUMP_SAMPLE, +}; + +struct rogue_fwif_counter_dump_data { + enum rogue_fwif_counter_dump_request counter_dump_request; +} __aligned(8); + +enum rogue_fwif_kccb_cmd_type { + /* Common commands */ + ROGUE_FWIF_KCCB_CMD_KICK = 101U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + ROGUE_FWIF_KCCB_CMD_MMUCACHE = 102U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + ROGUE_FWIF_KCCB_CMD_BP = 103U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* SLC flush and invalidation request */ + ROGUE_FWIF_KCCB_CMD_SLCFLUSHINVAL = 105U | + ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* + * Requests cleanup of a FW resource (type specified in the command + * data) + */ + ROGUE_FWIF_KCCB_CMD_CLEANUP = 106U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Power request */ + ROGUE_FWIF_KCCB_CMD_POW = 107U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Backing for on-demand ZS-Buffer done */ + ROGUE_FWIF_KCCB_CMD_ZSBUFFER_BACKING_UPDATE = + 108U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Unbacking for on-demand ZS-Buffer done */ + ROGUE_FWIF_KCCB_CMD_ZSBUFFER_UNBACKING_UPDATE = + 109U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Freelist Grow done */ + ROGUE_FWIF_KCCB_CMD_FREELIST_GROW_UPDATE = + 110U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Freelists Reconstruction done */ + ROGUE_FWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE = + 112U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* + * Informs the firmware that the host has added more data to a CDM2 + * Circular Buffer + */ + ROGUE_FWIF_KCCB_CMD_NOTIFY_WRITE_OFFSET_UPDATE = + 114U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Health check request */ + ROGUE_FWIF_KCCB_CMD_HEALTH_CHECK = 115U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Forcing signalling of all unmet UFOs for a given CCB offset */ + ROGUE_FWIF_KCCB_CMD_FORCE_UPDATE = 116U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* There is a geometry and a fragment command in this single kick */ + ROGUE_FWIF_KCCB_CMD_COMBINED_GEOM_FRAG_KICK = 117U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Informs the FW that a Guest OS has come online / offline. */ + ROGUE_FWIF_KCCB_CMD_OS_ONLINE_STATE_CONFIGURE = 118U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Commands only permitted to the native or host OS */ + ROGUE_FWIF_KCCB_CMD_REGCONFIG = 200U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Configure HWPerf events (to be generated) and HWPerf buffer address (if required) */ + ROGUE_FWIF_KCCB_CMD_HWPERF_UPDATE_CONFIG = 201U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Enable or disable multiple HWPerf blocks (reusing existing configuration) */ + ROGUE_FWIF_KCCB_CMD_HWPERF_CTRL_BLKS = 203U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Core clock speed change event */ + ROGUE_FWIF_KCCB_CMD_CORECLKSPEEDCHANGE = 204U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* + * Ask the firmware to update its cached ui32LogType value from the (shared) + * tracebuf control structure + */ + ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE = 206U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Set a maximum frequency/OPP point */ + ROGUE_FWIF_KCCB_CMD_PDVFS_LIMIT_MAX_FREQ = 207U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* + * Changes the relative scheduling priority for a particular OSid. It can + * only be serviced for the Host DDK + */ + ROGUE_FWIF_KCCB_CMD_OSID_PRIORITY_CHANGE = 208U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Set or clear firmware state flags */ + ROGUE_FWIF_KCCB_CMD_STATEFLAGS_CTRL = 209U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Set a minimum frequency/OPP point */ + ROGUE_FWIF_KCCB_CMD_PDVFS_LIMIT_MIN_FREQ = 212U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Configure Periodic Hardware Reset behaviour */ + ROGUE_FWIF_KCCB_CMD_PHR_CFG = 213U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Configure Safety Firmware Watchdog */ + ROGUE_FWIF_KCCB_CMD_WDG_CFG = 215U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Controls counter dumping in the FW */ + ROGUE_FWIF_KCCB_CMD_COUNTER_DUMP = 216U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Configure, clear and enable multiple HWPerf blocks */ + ROGUE_FWIF_KCCB_CMD_HWPERF_CONFIG_ENABLE_BLKS = 217U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Configure the custom counters for HWPerf */ + ROGUE_FWIF_KCCB_CMD_HWPERF_SELECT_CUSTOM_CNTRS = 218U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Configure directly addressable counters for HWPerf */ + ROGUE_FWIF_KCCB_CMD_HWPERF_CONFIG_BLKS = 220U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, +}; + +#define ROGUE_FWIF_LAST_ALLOWED_GUEST_KCCB_CMD \ + (ROGUE_FWIF_KCCB_CMD_REGCONFIG - 1) + +/* Kernel CCB command packet */ +struct rogue_fwif_kccb_cmd { + /* Command type */ + enum rogue_fwif_kccb_cmd_type cmd_type; + /* Compatibility and other flags */ + u32 kccb_flags; + + /* + * NOTE: Make sure that uCmdData is the last member of this struct + * This is to calculate actual command size for device mem copy. + * (Refer ROGUEGetCmdMemCopySize()) + */ + union { + /* Data for Kick command */ + struct rogue_fwif_kccb_cmd_kick_data cmd_kick_data; + /* Data for combined geom/frag Kick command */ + struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data + combined_geom_frag_cmd_kick_data; + /* Data for MMU cache command */ + struct rogue_fwif_mmucachedata mmu_cache_data; + /* Data for Breakpoint Commands */ + struct rogue_fwif_bpdata bp_data; + /* Data for SLC Flush/Inval commands */ + struct rogue_fwif_slcflushinvaldata slc_flush_inval_data; + /* Data for cleanup commands */ + struct rogue_fwif_cleanup_request cleanup_data; + /* Data for power request commands */ + struct rogue_fwif_power_request pow_data; + /* Data for HWPerf control command */ + struct rogue_fwif_hwperf_ctrl hw_perf_ctrl; + /* + * Data for HWPerf configure, clear and enable performance + * counter block command + */ + struct rogue_fwif_hwperf_config_enable_blks + hw_perf_cfg_enable_blks; + /* + * Data for HWPerf enable or disable performance counter block + * commands + */ + struct rogue_fwif_hwperf_ctrl_blks hw_perf_ctrl_blks; + /* Data for HWPerf configure the custom counters to read */ + struct rogue_fwif_hwperf_select_custom_cntrs + hw_perf_select_cstm_cntrs; + /* Data for HWPerf configure Directly Addressable blocks */ + struct rogue_fwif_hwperf_config_da_blks hw_perf_cfg_da_blks; + /* Data for core clock speed change */ + struct rogue_fwif_coreclkspeedchange_data + core_clk_speed_change_data; + /* Feedback for Z/S Buffer backing/unbacking */ + struct rogue_fwif_zsbuffer_backing_data zs_buffer_backing_data; + /* Feedback for Freelist grow/shrink */ + struct rogue_fwif_freelist_gs_data free_list_gs_data; + /* Feedback for Freelists reconstruction*/ + struct rogue_fwif_freelists_reconstruction_data + free_lists_reconstruction_data; + /* Data for custom register configuration */ + struct rogue_fwif_regconfig_data reg_config_data; + /* Data for informing the FW about the write offset update */ + struct rogue_fwif_write_offset_update_data + write_offset_update_data; + /* Data for setting the max frequency/OPP */ + struct rogue_fwif_pdvfs_max_freq_data pdvfs_max_freq_data; + /* Data for setting the min frequency/OPP */ + struct rogue_fwif_pdvfs_min_freq_data pdvfs_min_freq_data; + /* Data for updating the Guest Online states */ + struct rogue_fwif_os_state_change_data cmd_os_online_state_data; + /* Dev address for TBI buffer allocated on demand */ + u32 tbi_buffer_fw_addr; + /* Data for dumping of register ranges */ + struct rogue_fwif_counter_dump_data counter_dump_config_data; + /* Data for signalling all unmet fences for a given CCB */ + struct rogue_fwif_kccb_cmd_force_update_data force_update_data; + } cmd_data __aligned(8); +} __aligned(8); + +PVR_FW_STRUCT_SIZE_ASSERT(struct rogue_fwif_kccb_cmd); + +/* + ****************************************************************************** + * Firmware CCB command structure for ROGUE + ****************************************************************************** + */ + +struct rogue_fwif_fwccb_cmd_zsbuffer_backing_data { + u32 zs_buffer_id; +}; + +struct rogue_fwif_fwccb_cmd_freelist_gs_data { + u32 freelist_id; +}; + +struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data { + u32 freelist_count; + u32 hwr_counter; + u32 freelist_ids[ROGUE_FWIF_MAX_FREELISTS_TO_RECONSTRUCT]; +}; + +/* 1 if a page fault happened */ +#define ROGUE_FWIF_FWCCB_CMD_CONTEXT_RESET_FLAG_PF BIT(0) +/* 1 if applicable to all contexts */ +#define ROGUE_FWIF_FWCCB_CMD_CONTEXT_RESET_FLAG_ALL_CTXS BIT(1) + +struct rogue_fwif_fwccb_cmd_context_reset_data { + /* Context affected by the reset */ + u32 server_common_context_id; + /* Reason for reset */ + enum rogue_context_reset_reason reset_reason; + /* Data Master affected by the reset */ + u32 dm; + /* Job ref running at the time of reset */ + u32 reset_job_ref; + /* ROGUE_FWIF_FWCCB_CMD_CONTEXT_RESET_FLAG bitfield */ + u32 flags; + /* At what page catalog address */ + aligned_u64 pc_address; + /* Page fault address (only when applicable) */ + aligned_u64 fault_address; +}; + +struct rogue_fwif_fwccb_cmd_fw_pagefault_data { + /* Page fault address */ + u64 fw_fault_addr; +}; + +enum rogue_fwif_fwccb_cmd_type { + /* Requests ZSBuffer to be backed with physical pages */ + ROGUE_FWIF_FWCCB_CMD_ZSBUFFER_BACKING = 101U | + ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Requests ZSBuffer to be unbacked */ + ROGUE_FWIF_FWCCB_CMD_ZSBUFFER_UNBACKING = 102U | + ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Requests an on-demand freelist grow/shrink */ + ROGUE_FWIF_FWCCB_CMD_FREELIST_GROW = 103U | + ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Requests freelists reconstruction */ + ROGUE_FWIF_FWCCB_CMD_FREELISTS_RECONSTRUCTION = + 104U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Notifies host of a HWR event on a context */ + ROGUE_FWIF_FWCCB_CMD_CONTEXT_RESET_NOTIFICATION = + 105U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Requests an on-demand debug dump */ + ROGUE_FWIF_FWCCB_CMD_DEBUG_DUMP = 106U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + /* Requests an on-demand update on process stats */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_STATS = 107U | + ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + ROGUE_FWIF_FWCCB_CMD_CORE_CLK_RATE_CHANGE = + 108U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + ROGUE_FWIF_FWCCB_CMD_REQUEST_GPU_RESTART = + 109U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, + + /* Notifies host of a FW pagefault */ + ROGUE_FWIF_FWCCB_CMD_CONTEXT_FW_PF_NOTIFICATION = + 112U | ROGUE_CMD_MAGIC_DWORD_SHIFTED, +}; + +enum rogue_fwif_fwccb_cmd_update_stats_type { + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32TotalNumPartialRenders stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_PARTIAL_RENDERS = 1, + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32TotalNumOutOfMemory stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_OUT_OF_MEMORY, + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32NumGeomStores stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_GEOM_STORES, + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32NumFragStores stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_FRAG_STORES, + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32NumCDMStores stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_CDM_STORES, + /* + * PVRSRVStatsUpdateRenderContextStats should increase the value of the + * ui32NumTDMStores stat + */ + ROGUE_FWIF_FWCCB_CMD_UPDATE_NUM_TDM_STORES +}; + +struct rogue_fwif_fwccb_cmd_update_stats_data { + /* Element to update */ + enum rogue_fwif_fwccb_cmd_update_stats_type element_to_update; + /* The pid of the process whose stats are being updated */ + u32 pid_owner; + /* Adjustment to be made to the statistic */ + s32 adjustment_value; +}; + +struct rogue_fwif_fwccb_cmd_core_clk_rate_change_data { + u32 core_clk_rate; +} __aligned(8); + +struct rogue_fwif_fwccb_cmd { + /* Command type */ + enum rogue_fwif_fwccb_cmd_type cmd_type; + /* Compatibility and other flags */ + u32 fwccb_flags; + + union { + /* Data for Z/S-Buffer on-demand (un)backing*/ + struct rogue_fwif_fwccb_cmd_zsbuffer_backing_data + cmd_zs_buffer_backing; + /* Data for on-demand freelist grow/shrink */ + struct rogue_fwif_fwccb_cmd_freelist_gs_data cmd_free_list_gs; + /* Data for freelists reconstruction */ + struct rogue_fwif_fwccb_cmd_freelists_reconstruction_data + cmd_freelists_reconstruction; + /* Data for context reset notification */ + struct rogue_fwif_fwccb_cmd_context_reset_data + cmd_context_reset_notification; + /* Data for updating process stats */ + struct rogue_fwif_fwccb_cmd_update_stats_data + cmd_update_stats_data; + struct rogue_fwif_fwccb_cmd_core_clk_rate_change_data + cmd_core_clk_rate_change; + struct rogue_fwif_fwccb_cmd_fw_pagefault_data cmd_fw_pagefault; + } cmd_data __aligned(8); +} __aligned(8); + +PVR_FW_STRUCT_SIZE_ASSERT(struct rogue_fwif_fwccb_cmd); + +/* + ****************************************************************************** + * Workload estimation Firmware CCB command structure for ROGUE + ****************************************************************************** + */ +struct rogue_fwif_workest_fwccb_cmd { + /* Index for return data array */ + u16 return_data_index; + /* The cycles the workload took on the hardware */ + u32 cycles_taken; +}; + +/* + ****************************************************************************** + * Client CCB commands for ROGUE + ****************************************************************************** + */ + +/* + * Required memory alignment for 64-bit variables accessible by Meta + * (The gcc meta aligns 64-bit variables to 64-bit; therefore, memory shared + * between the host and meta that contains 64-bit variables has to maintain + * this alignment) + */ +#define ROGUE_FWIF_FWALLOC_ALIGN sizeof(u64) + +#define ROGUE_CCB_TYPE_TASK BIT(15) +#define ROGUE_CCB_FWALLOC_ALIGN(size) \ + (((size) + (ROGUE_FWIF_FWALLOC_ALIGN - 1)) & \ + ~(ROGUE_FWIF_FWALLOC_ALIGN - 1)) + +#define ROGUE_FWIF_CCB_CMD_TYPE_GEOM \ + (201U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_TQ_3D \ + (202U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_FRAG \ + (203U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR \ + (204U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_CDM \ + (205U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_TQ_TDM \ + (206U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_FBSC_INVALIDATE \ + (207U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_TQ_2D \ + (208U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_PRE_TIMESTAMP \ + (209U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_NULL \ + (210U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) +#define ROGUE_FWIF_CCB_CMD_TYPE_ABORT \ + (211U | ROGUE_CMD_MAGIC_DWORD_SHIFTED | ROGUE_CCB_TYPE_TASK) + +/* Leave a gap between CCB specific commands and generic commands */ +#define ROGUE_FWIF_CCB_CMD_TYPE_FENCE (212U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_UPDATE (213U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_RMW_UPDATE \ + (214U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_FENCE_PR (215U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_PRIORITY (216U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +/* + * Pre and Post timestamp commands are supposed to sandwich the DM cmd. The + * padding code with the CCB wrap upsets the FW if we don't have the task type + * bit cleared for POST_TIMESTAMPs. That's why we have 2 different cmd types. + */ +#define ROGUE_FWIF_CCB_CMD_TYPE_POST_TIMESTAMP \ + (217U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_UNFENCED_UPDATE \ + (218U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) +#define ROGUE_FWIF_CCB_CMD_TYPE_UNFENCED_RMW_UPDATE \ + (219U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) + +#define ROGUE_FWIF_CCB_CMD_TYPE_PADDING (221U | ROGUE_CMD_MAGIC_DWORD_SHIFTED) + +struct rogue_fwif_workest_kick_data { + /* Index for the KM Workload estimation return data array */ + u16 return_data_index __aligned(8); + /* Predicted time taken to do the work in cycles */ + u32 cycles_prediction __aligned(8); + /* Deadline for the workload */ + aligned_u64 deadline; +}; + +struct rogue_fwif_ccb_cmd_header { + u32 cmd_type; + u32 cmd_size; + /* + * external job reference - provided by client and used in debug for + * tracking submitted work + */ + u32 ext_job_ref; + /* + * internal job reference - generated by services and used in debug for + * tracking submitted work + */ + u32 int_job_ref; + /* Workload Estimation - Workload Estimation Data */ + struct rogue_fwif_workest_kick_data work_est_kick_data __aligned(8); +}; + +/* + ****************************************************************************** + * Client CCB commands which are only required by the kernel + ****************************************************************************** + */ +struct rogue_fwif_cmd_priority { + s32 priority; +}; + +/* + ****************************************************************************** + * Signature and Checksums Buffer + ****************************************************************************** + */ +struct rogue_fwif_sigbuf_ctl { + /* Ptr to Signature Buffer memory */ + u32 buffer_fw_addr; + /* Amount of space left for storing regs in the buffer */ + u32 left_size_in_regs; +} __aligned(8); + +struct rogue_fwif_counter_dump_ctl { + /* Ptr to counter dump buffer */ + u32 buffer_fw_addr; + /* Amount of space for storing in the buffer */ + u32 size_in_dwords; +} __aligned(8); + +struct rogue_fwif_firmware_gcov_ctl { + /* Ptr to firmware gcov buffer */ + u32 buffer_fw_addr; + /* Amount of space for storing in the buffer */ + u32 size; +} __aligned(8); + +/* + ***************************************************************************** + * ROGUE Compatibility checks + ***************************************************************************** + */ + +/* + * WARNING: Whenever the layout of ROGUE_FWIF_COMPCHECKS_BVNC changes, the + * following define should be increased by 1 to indicate to the compatibility + * logic that layout has changed. + */ +#define ROGUE_FWIF_COMPCHECKS_LAYOUT_VERSION 3 + +struct rogue_fwif_compchecks_bvnc { + /* WARNING: This field must be defined as first one in this structure */ + u32 layout_version; + aligned_u64 bvnc; +} __aligned(8); + +struct rogue_fwif_init_options { + u8 os_count_support; + u8 padding[7]; +} __aligned(8); + +#define ROGUE_FWIF_COMPCHECKS_BVNC_DECLARE_AND_INIT(name) \ + struct rogue_fwif_compchecks_bvnc(name) = { \ + ROGUE_FWIF_COMPCHECKS_LAYOUT_VERSION, \ + 0, \ + } + +static inline void rogue_fwif_compchecks_bvnc_init(struct rogue_fwif_compchecks_bvnc *compchecks) +{ + compchecks->layout_version = ROGUE_FWIF_COMPCHECKS_LAYOUT_VERSION; + compchecks->bvnc = 0; +} + +struct rogue_fwif_compchecks { + /* hardware BVNC (from the ROGUE registers) */ + struct rogue_fwif_compchecks_bvnc hw_bvnc; + /* firmware BVNC */ + struct rogue_fwif_compchecks_bvnc fw_bvnc; + /* identifier of the FW processor version */ + u32 fw_processor_version; + /* software DDK version */ + u32 ddk_version; + /* software DDK build no. */ + u32 ddk_build; + /* build options bit-field */ + u32 build_options; + /* initialisation options bit-field */ + struct rogue_fwif_init_options init_options; + /* Information is valid */ + bool updated __aligned(4); + u32 padding; +} __aligned(8); + +/* + ****************************************************************************** + * Updated configuration post FW data init. + ****************************************************************************** + */ +struct rogue_fwif_runtime_cfg { + /* APM latency in ms before signalling IDLE to the host */ + u32 active_pm_latency_ms; + /* Compatibility and other flags */ + u32 runtime_cfg_flags; + /* + * If set, APM latency does not reset to system default each GPU power + * transition + */ + bool active_pm_latency_persistant __aligned(4); + /* Core clock speed, currently only used to calculate timer ticks */ + u32 core_clock_speed; + /* Last number of dusts change requested by the host */ + u32 default_dusts_num_init; + /* Periodic Hardware Reset configuration values */ + u32 phr_mode; + /* New number of milliseconds C/S is allowed to last */ + u32 hcs_deadline_ms; + /* The watchdog period in microseconds */ + u32 wdg_period_us; + /* Array of priorities per OS */ + u32 osid_priority[ROGUE_FW_MAX_NUM_OS]; + /* On-demand allocated HWPerf buffer address, to be passed to the FW */ + u32 hwperf_buf_fw_addr; + + bool padding __aligned(4); +}; + +/* + ***************************************************************************** + * Control data for ROGUE + ***************************************************************************** + */ + +#define ROGUE_FWIF_HWR_DEBUG_DUMP_ALL (99999U) + +enum rogue_fwif_tpu_dm { + ROGUE_FWIF_TPU_DM_PDM = 0, + ROGUE_FWIF_TPU_DM_VDM = 1, + ROGUE_FWIF_TPU_DM_CDM = 2, + ROGUE_FWIF_TPU_DM_TDM = 3, + ROGUE_FWIF_TPU_DM_LAST +}; + +enum rogue_fwif_gpio_val_mode { + /* No GPIO validation */ + ROGUE_FWIF_GPIO_VAL_OFF = 0, + /* + * Simple test case that initiates by sending data via the GPIO and then + * sends back any data received over the GPIO + */ + ROGUE_FWIF_GPIO_VAL_GENERAL = 1, + /* + * More complex test case that writes and reads data across the entire + * GPIO AP address range. + */ + ROGUE_FWIF_GPIO_VAL_AP = 2, + /* Validates the GPIO Testbench. */ + ROGUE_FWIF_GPIO_VAL_TESTBENCH = 5, + /* Send and then receive each byte in the range 0-255. */ + ROGUE_FWIF_GPIO_VAL_LOOPBACK = 6, + /* Send and then receive each power-of-2 byte in the range 0-255. */ + ROGUE_FWIF_GPIO_VAL_LOOPBACK_LITE = 7, + ROGUE_FWIF_GPIO_VAL_LAST +}; + +enum fw_perf_conf { + FW_PERF_CONF_NONE = 0, + FW_PERF_CONF_ICACHE = 1, + FW_PERF_CONF_DCACHE = 2, + FW_PERF_CONF_JTLB_INSTR = 5, + FW_PERF_CONF_INSTRUCTIONS = 6 +}; + +enum fw_boot_stage { + FW_BOOT_STAGE_TLB_INIT_FAILURE = -2, + FW_BOOT_STAGE_NOT_AVAILABLE = -1, + FW_BOOT_NOT_STARTED = 0, + FW_BOOT_BLDR_STARTED = 1, + FW_BOOT_CACHE_DONE, + FW_BOOT_TLB_DONE, + FW_BOOT_MAIN_STARTED, + FW_BOOT_ALIGNCHECKS_DONE, + FW_BOOT_INIT_DONE, +}; + +/* + * Kernel CCB return slot responses. Usage of bit-fields instead of bare + * integers allows FW to possibly pack-in several responses for each single kCCB + * command. + */ +/* Command executed (return status from FW) */ +#define ROGUE_FWIF_KCCB_RTN_SLOT_CMD_EXECUTED BIT(0) +/* A cleanup was requested but resource busy */ +#define ROGUE_FWIF_KCCB_RTN_SLOT_CLEANUP_BUSY BIT(1) +/* Poll failed in FW for a HW operation to complete */ +#define ROGUE_FWIF_KCCB_RTN_SLOT_POLL_FAILURE BIT(2) +/* Reset value of a kCCB return slot (set by host) */ +#define ROGUE_FWIF_KCCB_RTN_SLOT_NO_RESPONSE 0x0U + +struct rogue_fwif_connection_ctl { + /* Fw-Os connection states */ + enum rogue_fwif_connection_fw_state connection_fw_state; + enum rogue_fwif_connection_os_state connection_os_state; + u32 alive_fw_token; + u32 alive_os_token; +} __aligned(8); + +struct rogue_fwif_osinit { + /* Kernel CCB */ + u32 kernel_ccbctl_fw_addr; + u32 kernel_ccb_fw_addr; + u32 kernel_ccb_rtn_slots_fw_addr; + + /* Firmware CCB */ + u32 firmware_ccbctl_fw_addr; + u32 firmware_ccb_fw_addr; + + /* Workload Estimation Firmware CCB */ + u32 work_est_firmware_ccbctl_fw_addr; + u32 work_est_firmware_ccb_fw_addr; + + u32 rogue_fwif_hwr_info_buf_ctl_fw_addr; + + u32 hwr_debug_dump_limit; + + u32 fw_os_data_fw_addr; + + /* Compatibility checks to be populated by the Firmware */ + struct rogue_fwif_compchecks rogue_comp_checks; +} __aligned(8); + +/* BVNC Features */ +struct rogue_hwperf_bvnc_block { + /* Counter block ID, see ROGUE_HWPERF_CNTBLK_ID */ + u16 block_id; + + /* Number of counters in this block type */ + u16 num_counters; + + /* Number of blocks of this type */ + u16 num_blocks; + + u16 reserved; +}; + +#define ROGUE_HWPERF_MAX_BVNC_LEN (24) + +#define ROGUE_HWPERF_MAX_BVNC_BLOCK_LEN (16U) + +/* BVNC Features */ +struct rogue_hwperf_bvnc { + /* BVNC string */ + char bvnc_string[ROGUE_HWPERF_MAX_BVNC_LEN]; + /* See ROGUE_HWPERF_FEATURE_FLAGS */ + u32 bvnc_km_feature_flags; + /* Number of blocks described in aBvncBlocks */ + u16 num_bvnc_blocks; + /* Number of GPU cores present */ + u16 bvnc_gpu_cores; + /* Supported Performance Blocks for BVNC */ + struct rogue_hwperf_bvnc_block + bvnc_blocks[ROGUE_HWPERF_MAX_BVNC_BLOCK_LEN]; +}; + +PVR_FW_STRUCT_SIZE_ASSERT(struct rogue_hwperf_bvnc); + +struct rogue_fwif_sysinit { + /* Fault read address */ + aligned_u64 fault_phys_addr; + + /* PDS execution base */ + aligned_u64 pds_exec_base; + /* UCS execution base */ + aligned_u64 usc_exec_base; + /* FBCDC bindless texture state table base */ + aligned_u64 fbcdc_state_table_base; + aligned_u64 fbcdc_large_state_table_base; + /* Texture state base */ + aligned_u64 texture_heap_base; + + /* Event filter for Firmware events */ + u64 hw_perf_filter; + + aligned_u64 slc3_fence_dev_addr; + + u32 tpu_trilinear_frac_mask[ROGUE_FWIF_TPU_DM_LAST] __aligned(8); + + /* Signature and Checksum Buffers for DMs */ + struct rogue_fwif_sigbuf_ctl sigbuf_ctl[PVR_FWIF_DM_MAX]; + + struct rogue_fwif_pdvfs_opp pdvfs_opp_info; + + struct rogue_fwif_dma_addr coremem_data_store; + + struct rogue_fwif_counter_dump_ctl counter_dump_ctl; + + u32 filter_flags; + + u32 runtime_cfg_fw_addr; + + u32 trace_buf_ctl_fw_addr; + u32 fw_sys_data_fw_addr; + + u32 gpu_util_fw_cb_ctl_fw_addr; + u32 reg_cfg_fw_addr; + u32 hwperf_ctl_fw_addr; + + u32 align_checks; + + /* Core clock speed at FW boot time */ + u32 initial_core_clock_speed; + + /* APM latency in ms before signalling IDLE to the host */ + u32 active_pm_latency_ms; + + /* Flag to be set by the Firmware after successful start */ + bool firmware_started __aligned(4); + + /* Host/FW Trace synchronisation Partition Marker */ + u32 marker_val; + + /* Firmware initialization complete time */ + u32 firmware_started_timestamp; + + u32 jones_disable_mask; + + /* Firmware performance counter config */ + enum fw_perf_conf firmware_perf; + + /* + * FW Pointer to memory containing core clock rate in Hz. + * Firmware (PDVFS) updates the memory when running on non primary FW + * thread to communicate to host driver. + */ + u32 core_clock_rate_fw_addr; + + enum rogue_fwif_gpio_val_mode gpio_validation_mode; + + /* Used in HWPerf for decoding BVNC Features */ + struct rogue_hwperf_bvnc bvnc_km_feature_flags; + + /* Value to write into ROGUE_CR_TFBC_COMPRESSION_CONTROL */ + u32 tfbc_compression_control; +} __aligned(8); + +/* + ***************************************************************************** + * Timer correlation shared data and defines + ***************************************************************************** + */ + +struct rogue_fwif_time_corr { + aligned_u64 os_timestamp; + aligned_u64 os_mono_timestamp; + aligned_u64 cr_timestamp; + + /* + * Utility variable used to convert CR timer deltas to OS timer deltas + * (nS), where the deltas are relative to the timestamps above: + * deltaOS = (deltaCR * K) >> decimal_shift, see full explanation below + */ + aligned_u64 cr_delta_to_os_delta_kns; + + u32 core_clock_speed; + u32 reserved; +} __aligned(8); + +/* + * The following macros are used to help converting FW timestamps to the Host + * time domain. On the FW the ROGUE_CR_TIMER counter is used to keep track of + * time; it increments by 1 every 256 GPU clock ticks, so the general + * formula to perform the conversion is: + * + * [ GPU clock speed in Hz, if (scale == 10^9) then deltaOS is in nS, + * otherwise if (scale == 10^6) then deltaOS is in uS ] + * + * deltaCR * 256 256 * scale + * deltaOS = --------------- * scale = deltaCR * K [ K = --------------- ] + * GPUclockspeed GPUclockspeed + * + * The actual K is multiplied by 2^20 (and deltaCR * K is divided by 2^20) + * to get some better accuracy and to avoid returning 0 in the integer + * division 256000000/GPUfreq if GPUfreq is greater than 256MHz. + * This is the same as keeping K as a decimal number. + * + * The maximum deltaOS is slightly more than 5hrs for all GPU frequencies + * (deltaCR * K is more or less a constant), and it's relative to the base + * OS timestamp sampled as a part of the timer correlation data. + * This base is refreshed on GPU power-on, DVFS transition and periodic + * frequency calibration (executed every few seconds if the FW is doing + * some work), so as long as the GPU is doing something and one of these + * events is triggered then deltaCR * K will not overflow and deltaOS will be + * correct. + */ + +#define ROGUE_FWIF_CRDELTA_TO_OSDELTA_ACCURACY_SHIFT (20) + +#define ROGUE_FWIF_GET_DELTA_OSTIME_NS(delta_cr, k) \ + (((delta_cr) * (k)) >> ROGUE_FWIF_CRDELTA_TO_OSDELTA_ACCURACY_SHIFT) + +/* + ****************************************************************************** + * GPU Utilisation + ****************************************************************************** + */ + +/* See rogue_common.h for a list of GPU states */ +#define ROGUE_FWIF_GPU_UTIL_TIME_MASK \ + (0xFFFFFFFFFFFFFFFFull & ~ROGUE_FWIF_GPU_UTIL_STATE_MASK) + +#define ROGUE_FWIF_GPU_UTIL_GET_TIME(word) \ + ((word)(&ROGUE_FWIF_GPU_UTIL_TIME_MASK)) +#define ROGUE_FWIF_GPU_UTIL_GET_STATE(word) \ + ((word)(&ROGUE_FWIF_GPU_UTIL_STATE_MASK)) + +/* + * The OS timestamps computed by the FW are approximations of the real time, + * which means they could be slightly behind or ahead the real timer on the + * Host. In some cases we can perform subtractions between FW approximated + * timestamps and real OS timestamps, so we need a form of protection against + * negative results if for instance the FW one is a bit ahead of time. + */ +#define ROGUE_FWIF_GPU_UTIL_GET_PERIOD(newtime, oldtime) \ + (((newtime) > (oldtime)) ? ((newtime) - (oldtime)) : 0U) + +#define ROGUE_FWIF_GPU_UTIL_MAKE_WORD(time, state) \ + (ROGUE_FWIF_GPU_UTIL_GET_TIME(time) | \ + ROGUE_FWIF_GPU_UTIL_GET_STATE(state)) + +/* + * The timer correlation array must be big enough to ensure old entries won't be + * overwritten before all the HWPerf events linked to those entries are + * processed by the MISR. The update frequency of this array depends on how fast + * the system can change state (basically how small the APM latency is) and + * perform DVFS transitions. + * + * The minimum size is 2 (not 1) to avoid race conditions between the FW reading + * an entry while the Host is updating it. With 2 entries in the worst case the + * FW will read old data, which is still quite ok if the Host is updating the + * timer correlation at that time. + */ +#define ROGUE_FWIF_TIME_CORR_ARRAY_SIZE 256U +#define ROGUE_FWIF_TIME_CORR_CURR_INDEX(seqcount) \ + ((seqcount) % ROGUE_FWIF_TIME_CORR_ARRAY_SIZE) + +/* Make sure the timer correlation array size is a power of 2 */ +static_assert((ROGUE_FWIF_TIME_CORR_ARRAY_SIZE & + (ROGUE_FWIF_TIME_CORR_ARRAY_SIZE - 1U)) == 0U, + "ROGUE_FWIF_TIME_CORR_ARRAY_SIZE must be a power of two"); + +struct rogue_fwif_gpu_util_fwcb { + struct rogue_fwif_time_corr time_corr[ROGUE_FWIF_TIME_CORR_ARRAY_SIZE]; + u32 time_corr_seq_count; + + /* Compatibility and other flags */ + u32 gpu_util_flags; + + /* Last GPU state + OS time of the last state update */ + aligned_u64 last_word; + + /* Counters for the amount of time the GPU was active/idle/blocked */ + aligned_u64 stats_counters[PVR_FWIF_GPU_UTIL_STATE_NUM]; +} __aligned(8); + +struct rogue_fwif_rta_ctl { + /* Render number */ + u32 render_target_index; + /* index in RTA */ + u32 current_render_target; + /* total active RTs */ + u32 active_render_targets; + /* total active RTs from the first TA kick, for OOM */ + u32 cumul_active_render_targets; + /* Array of valid RT indices */ + u32 valid_render_targets_fw_addr; + /* Array of number of occurred partial renders per render target */ + u32 rta_num_partial_renders_fw_addr; + /* Number of render targets in the array */ + u32 max_rts; + /* Compatibility and other flags */ + u32 rta_ctl_flags; +} __aligned(8); + +struct rogue_fwif_freelist { + aligned_u64 freelist_dev_addr; + aligned_u64 current_dev_addr; + u32 current_stack_top; + u32 max_pages; + u32 grow_pages; + /* HW pages */ + u32 current_pages; + u32 allocated_page_count; + u32 allocated_mmu_page_count; + u32 freelist_id; + + bool grow_pending __aligned(4); + /* Pages that should be used only when OOM is reached */ + u32 ready_pages; + /* Compatibility and other flags */ + u32 freelist_flags; + /* PM Global PB on which Freelist is loaded */ + u32 pm_global_pb; + u32 padding; +} __aligned(8); + +/* + ****************************************************************************** + * HWRTData + ****************************************************************************** + */ + +/* HWRTData flags */ +/* Deprecated flags 1:0 */ +#define HWRTDATA_HAS_LAST_GEOM BIT(2) +#define HWRTDATA_PARTIAL_RENDERED BIT(3) +#define HWRTDATA_DISABLE_TILE_REORDERING BIT(4) +#define HWRTDATA_NEED_BRN65101_BLIT BIT(5) +#define HWRTDATA_FIRST_BRN65101_STRIP BIT(6) +#define HWRTDATA_NEED_BRN67182_2ND_RENDER BIT(7) + +enum rogue_fwif_rtdata_state { + ROGUE_FWIF_RTDATA_STATE_NONE = 0, + ROGUE_FWIF_RTDATA_STATE_KICK_GEOM, + ROGUE_FWIF_RTDATA_STATE_KICK_GEOM_FIRST, + ROGUE_FWIF_RTDATA_STATE_GEOM_FINISHED, + ROGUE_FWIF_RTDATA_STATE_KICK_FRAG, + ROGUE_FWIF_RTDATA_STATE_FRAG_FINISHED, + ROGUE_FWIF_RTDATA_STATE_FRAG_CONTEXT_STORED, + ROGUE_FWIF_RTDATA_STATE_GEOM_OUTOFMEM, + ROGUE_FWIF_RTDATA_STATE_PARTIALRENDERFINISHED, + /* + * In case of HWR, we can't set the RTDATA state to NONE, as this will + * cause any TA to become a first TA. To ensure all related TA's are + * skipped, we use the HWR state + */ + ROGUE_FWIF_RTDATA_STATE_HWR, + ROGUE_FWIF_RTDATA_STATE_UNKNOWN = 0x7FFFFFFFU +}; + +struct rogue_fwif_hwrtdata_common { + bool geom_caches_need_zeroing __aligned(4); + + u32 screen_pixel_max; + aligned_u64 multi_sample_ctl; + u64 flipped_multi_sample_ctl; + u32 tpc_stride; + u32 tpc_size; + u32 te_screen; + u32 mtile_stride; + u32 teaa; + u32 te_mtile1; + u32 te_mtile2; + u32 isp_merge_lower_x; + u32 isp_merge_lower_y; + u32 isp_merge_upper_x; + u32 isp_merge_upper_y; + u32 isp_merge_scale_x; + u32 isp_merge_scale_y; + u32 rgn_header_size; + u32 isp_mtile_size; + u32 padding; +} __aligned(8); + +struct rogue_fwif_hwrtdata { + /* MList Data Store */ + aligned_u64 pm_mlist_dev_addr; + + aligned_u64 vce_cat_base[4]; + aligned_u64 vce_last_cat_base[4]; + aligned_u64 te_cat_base[4]; + aligned_u64 te_last_cat_base[4]; + aligned_u64 alist_cat_base; + aligned_u64 alist_last_cat_base; + + aligned_u64 pm_alist_stack_pointer; + u32 pm_mlist_stack_pointer; + + u32 hwrt_data_common_fw_addr; + + u32 hwrt_data_flags; + enum rogue_fwif_rtdata_state state; + + u32 freelists_fw_addr[MAX_FREELISTS_SIZE] __aligned(8); + u32 freelist_hwr_snapshot[MAX_FREELISTS_SIZE]; + + aligned_u64 vheap_table_dev_addr; + + struct rogue_fwif_rta_ctl rta_ctl; + + aligned_u64 tail_ptrs_dev_addr; + aligned_u64 macrotile_array_dev_addr; + aligned_u64 rgn_header_dev_addr; + aligned_u64 rtc_dev_addr; + + u32 owner_geom_not_used_by_host __aligned(8); + + bool geom_caches_need_zeroing __aligned(4); + + struct rogue_fwif_cleanup_ctl cleanup_state __aligned(64); +} __aligned(8); + +/* + ****************************************************************************** + * Sync checkpoints + ****************************************************************************** + */ + +#define PVR_SYNC_CHECKPOINT_UNDEF 0x000 +#define PVR_SYNC_CHECKPOINT_ACTIVE 0xac1 /* Checkpoint has not signaled. */ +#define PVR_SYNC_CHECKPOINT_SIGNALED 0x519 /* Checkpoint has signaled. */ +#define PVR_SYNC_CHECKPOINT_ERRORED 0xeff /* Checkpoint has been errored. */ + +#include "pvr_rogue_fwif_check.h" + +#endif /* PVR_ROGUE_FWIF_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h new file mode 100644 index 000000000000..51dc37e78f41 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h @@ -0,0 +1,493 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_CHECK_H +#define PVR_ROGUE_FWIF_CHECK_H + +#include <linux/build_bug.h> + +#define OFFSET_CHECK(type, member, offset) \ + static_assert(offsetof(type, member) == (offset), \ + "offsetof(" #type ", " #member ") incorrect") + +#define SIZE_CHECK(type, size) \ + static_assert(sizeof(type) == (size), #type " is incorrect size") + +OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0); +OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200); +OFFSET_CHECK(struct rogue_fwif_file_info_buf, line_num, 400); +SIZE_CHECK(struct rogue_fwif_file_info_buf, 408); + +OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_pointer, 0); +OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_buffer_fw_addr, 4); +OFFSET_CHECK(struct rogue_fwif_tracebuf_space, trace_buffer, 8); +OFFSET_CHECK(struct rogue_fwif_tracebuf_space, assert_buf, 16); +SIZE_CHECK(struct rogue_fwif_tracebuf_space, 424); + +OFFSET_CHECK(struct rogue_fwif_tracebuf, log_type, 0); +OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf, 8); +OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf_size_in_dwords, 856); +OFFSET_CHECK(struct rogue_fwif_tracebuf, tracebuf_flags, 860); +SIZE_CHECK(struct rogue_fwif_tracebuf, 864); + +OFFSET_CHECK(struct rogue_fw_fault_info, cr_timer, 0); +OFFSET_CHECK(struct rogue_fw_fault_info, os_timer, 8); +OFFSET_CHECK(struct rogue_fw_fault_info, data, 16); +OFFSET_CHECK(struct rogue_fw_fault_info, reserved, 20); +OFFSET_CHECK(struct rogue_fw_fault_info, fault_buf, 24); +SIZE_CHECK(struct rogue_fw_fault_info, 432); + +OFFSET_CHECK(struct rogue_fwif_sysdata, config_flags, 0); +OFFSET_CHECK(struct rogue_fwif_sysdata, config_flags_ext, 4); +OFFSET_CHECK(struct rogue_fwif_sysdata, pow_state, 8); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_ridx, 12); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_widx, 16); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_wrap_count, 20); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_size, 24); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_drop_count, 28); +OFFSET_CHECK(struct rogue_fwif_sysdata, hw_perf_ut, 32); +OFFSET_CHECK(struct rogue_fwif_sysdata, first_drop_ordinal, 36); +OFFSET_CHECK(struct rogue_fwif_sysdata, last_drop_ordinal, 40); +OFFSET_CHECK(struct rogue_fwif_sysdata, os_runtime_flags_mirror, 44); +OFFSET_CHECK(struct rogue_fwif_sysdata, fault_info, 80); +OFFSET_CHECK(struct rogue_fwif_sysdata, fw_faults, 3536); +OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_addr, 3540); +OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_mask, 3548); +OFFSET_CHECK(struct rogue_fwif_sysdata, cr_poll_count, 3556); +OFFSET_CHECK(struct rogue_fwif_sysdata, start_idle_time, 3568); +OFFSET_CHECK(struct rogue_fwif_sysdata, hwr_state_flags, 3576); +OFFSET_CHECK(struct rogue_fwif_sysdata, hwr_recovery_flags, 3580); +OFFSET_CHECK(struct rogue_fwif_sysdata, fw_sys_data_flags, 3616); +OFFSET_CHECK(struct rogue_fwif_sysdata, mc_config, 3620); +SIZE_CHECK(struct rogue_fwif_sysdata, 3624); + +OFFSET_CHECK(struct rogue_fwif_slr_entry, timestamp, 0); +OFFSET_CHECK(struct rogue_fwif_slr_entry, fw_ctx_addr, 8); +OFFSET_CHECK(struct rogue_fwif_slr_entry, num_ufos, 12); +OFFSET_CHECK(struct rogue_fwif_slr_entry, ccb_name, 16); +SIZE_CHECK(struct rogue_fwif_slr_entry, 48); + +OFFSET_CHECK(struct rogue_fwif_osdata, fw_os_config_flags, 0); +OFFSET_CHECK(struct rogue_fwif_osdata, fw_sync_check_mark, 4); +OFFSET_CHECK(struct rogue_fwif_osdata, host_sync_check_mark, 8); +OFFSET_CHECK(struct rogue_fwif_osdata, forced_updates_requested, 12); +OFFSET_CHECK(struct rogue_fwif_osdata, slr_log_wp, 16); +OFFSET_CHECK(struct rogue_fwif_osdata, slr_log_first, 24); +OFFSET_CHECK(struct rogue_fwif_osdata, slr_log, 72); +OFFSET_CHECK(struct rogue_fwif_osdata, last_forced_update_time, 552); +OFFSET_CHECK(struct rogue_fwif_osdata, interrupt_count, 560); +OFFSET_CHECK(struct rogue_fwif_osdata, kccb_cmds_executed, 568); +OFFSET_CHECK(struct rogue_fwif_osdata, power_sync_fw_addr, 572); +OFFSET_CHECK(struct rogue_fwif_osdata, fw_os_data_flags, 576); +SIZE_CHECK(struct rogue_fwif_osdata, 584); + +OFFSET_CHECK(struct rogue_bifinfo, bif_req_status, 0); +OFFSET_CHECK(struct rogue_bifinfo, bif_mmu_status, 8); +OFFSET_CHECK(struct rogue_bifinfo, pc_address, 16); +OFFSET_CHECK(struct rogue_bifinfo, reserved, 24); +SIZE_CHECK(struct rogue_bifinfo, 32); + +OFFSET_CHECK(struct rogue_eccinfo, fault_gpu, 0); +SIZE_CHECK(struct rogue_eccinfo, 4); + +OFFSET_CHECK(struct rogue_mmuinfo, mmu_status, 0); +OFFSET_CHECK(struct rogue_mmuinfo, pc_address, 16); +OFFSET_CHECK(struct rogue_mmuinfo, reserved, 24); +SIZE_CHECK(struct rogue_mmuinfo, 32); + +OFFSET_CHECK(struct rogue_pollinfo, thread_num, 0); +OFFSET_CHECK(struct rogue_pollinfo, cr_poll_addr, 4); +OFFSET_CHECK(struct rogue_pollinfo, cr_poll_mask, 8); +OFFSET_CHECK(struct rogue_pollinfo, cr_poll_last_value, 12); +OFFSET_CHECK(struct rogue_pollinfo, reserved, 16); +SIZE_CHECK(struct rogue_pollinfo, 24); + +OFFSET_CHECK(struct rogue_tlbinfo, bad_addr, 0); +OFFSET_CHECK(struct rogue_tlbinfo, entry_lo, 4); +SIZE_CHECK(struct rogue_tlbinfo, 8); + +OFFSET_CHECK(struct rogue_hwrinfo, hwr_data, 0); +OFFSET_CHECK(struct rogue_hwrinfo, cr_timer, 32); +OFFSET_CHECK(struct rogue_hwrinfo, os_timer, 40); +OFFSET_CHECK(struct rogue_hwrinfo, frame_num, 48); +OFFSET_CHECK(struct rogue_hwrinfo, pid, 52); +OFFSET_CHECK(struct rogue_hwrinfo, active_hwrt_data, 56); +OFFSET_CHECK(struct rogue_hwrinfo, hwr_number, 60); +OFFSET_CHECK(struct rogue_hwrinfo, event_status, 64); +OFFSET_CHECK(struct rogue_hwrinfo, hwr_recovery_flags, 68); +OFFSET_CHECK(struct rogue_hwrinfo, hwr_type, 72); +OFFSET_CHECK(struct rogue_hwrinfo, dm, 76); +OFFSET_CHECK(struct rogue_hwrinfo, core_id, 80); +OFFSET_CHECK(struct rogue_hwrinfo, cr_time_of_kick, 88); +OFFSET_CHECK(struct rogue_hwrinfo, cr_time_hw_reset_start, 96); +OFFSET_CHECK(struct rogue_hwrinfo, cr_time_hw_reset_finish, 104); +OFFSET_CHECK(struct rogue_hwrinfo, cr_time_freelist_ready, 112); +OFFSET_CHECK(struct rogue_hwrinfo, reserved, 120); +SIZE_CHECK(struct rogue_hwrinfo, 136); + +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_info, 0); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_counter, 2176); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, write_index, 2180); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, dd_req_count, 2184); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_info_buf_flags, 2188); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_locked_up_count, 2192); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_overran_count, 2228); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_recovered_count, 2264); +OFFSET_CHECK(struct rogue_fwif_hwrinfobuf, hwr_dm_false_detect_count, 2300); +SIZE_CHECK(struct rogue_fwif_hwrinfobuf, 2336); + +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, pc_dev_paddr, 0); +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, page_cat_base_reg_set, 8); +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, breakpoint_addr, 12); +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, bp_handler_addr, 16); +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, breakpoint_ctl, 20); +OFFSET_CHECK(struct rogue_fwif_fwmemcontext, fw_mem_ctx_flags, 24); +SIZE_CHECK(struct rogue_fwif_fwmemcontext, 32); + +OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vdm_call_stack_pointer, 0); +OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vdm_call_stack_pointer_init, 8); +OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_reg_vbs_so_prim, 16); +OFFSET_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, geom_current_idx, 32); +SIZE_CHECK(struct rogue_fwif_geom_ctx_state_per_geom, 40); + +OFFSET_CHECK(struct rogue_fwif_geom_ctx_state, geom_core, 0); +SIZE_CHECK(struct rogue_fwif_geom_ctx_state, 160); + +OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_pm_deallocated_mask_status, 0); +OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_dm_pds_mtilefree_status, 4); +OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, ctx_state_flags, 8); +OFFSET_CHECK(struct rogue_fwif_frag_ctx_state, frag_reg_isp_store, 12); +SIZE_CHECK(struct rogue_fwif_frag_ctx_state, 16); + +OFFSET_CHECK(struct rogue_fwif_compute_ctx_state, ctx_state_flags, 0); +SIZE_CHECK(struct rogue_fwif_compute_ctx_state, 4); + +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccbctl_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccb_fw_addr, 4); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, ccb_meta_dma_addr, 8); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, context_state_addr, 24); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, fw_com_ctx_flags, 28); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, priority, 32); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, priority_seq_num, 36); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, rf_cmd_addr, 40); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_pending, 44); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_stores, 48); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_out_of_memory, 52); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, stats_num_partial_renders, 56); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, dm, 60); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, wait_signal_address, 64); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, wait_signal_node, 72); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, buf_stalled_node, 80); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, cbuf_queue_ctrl_addr, 88); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, robustness_address, 96); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, max_deadline_ms, 104); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, read_offset_needs_reset, 108); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, waiting_node, 112); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, run_node, 120); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, last_failed_ufo, 128); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, fw_mem_context_fw_addr, 136); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, server_common_context_id, 140); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, pid, 144); +OFFSET_CHECK(struct rogue_fwif_fwcommoncontext, geom_oom_disabled, 148); +SIZE_CHECK(struct rogue_fwif_fwcommoncontext, 152); + +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, write_offset, 0); +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, padding, 4); +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, read_offset, 128); +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, wrap_mask, 132); +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, cmd_size, 136); +OFFSET_CHECK(struct rogue_fwif_ccb_ctl, padding2, 140); +SIZE_CHECK(struct rogue_fwif_ccb_ctl, 144); + +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, context_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, client_woff_update, 4); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, client_wrap_mask_update, 8); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, num_cleanup_ctl, 12); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, cleanup_ctl_fw_addr, 16); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_kick_data, work_est_cmd_header_offset, 28); +SIZE_CHECK(struct rogue_fwif_kccb_cmd_kick_data, 32); + +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, geom_cmd_kick_data, 0); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, frag_cmd_kick_data, 32); +SIZE_CHECK(struct rogue_fwif_kccb_cmd_combined_geom_frag_kick_data, 64); + +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, context_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, ccb_fence_offset, 4); +SIZE_CHECK(struct rogue_fwif_kccb_cmd_force_update_data, 8); + +OFFSET_CHECK(struct rogue_fwif_cleanup_request, cleanup_type, 0); +OFFSET_CHECK(struct rogue_fwif_cleanup_request, cleanup_data, 4); +SIZE_CHECK(struct rogue_fwif_cleanup_request, 8); + +OFFSET_CHECK(struct rogue_fwif_power_request, pow_type, 0); +OFFSET_CHECK(struct rogue_fwif_power_request, power_req_data, 4); +SIZE_CHECK(struct rogue_fwif_power_request, 8); + +OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, context_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, inval, 4); +OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, dm_context, 8); +OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, address, 16); +OFFSET_CHECK(struct rogue_fwif_slcflushinvaldata, size, 24); +SIZE_CHECK(struct rogue_fwif_slcflushinvaldata, 32); + +OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl, opcode, 0); +OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl, mask, 8); +SIZE_CHECK(struct rogue_fwif_hwperf_ctrl, 16); + +OFFSET_CHECK(struct rogue_fwif_hwperf_config_enable_blks, num_blocks, 0); +OFFSET_CHECK(struct rogue_fwif_hwperf_config_enable_blks, block_configs_fw_addr, 4); +SIZE_CHECK(struct rogue_fwif_hwperf_config_enable_blks, 8); + +OFFSET_CHECK(struct rogue_fwif_hwperf_config_da_blks, num_blocks, 0); +OFFSET_CHECK(struct rogue_fwif_hwperf_config_da_blks, block_configs_fw_addr, 4); +SIZE_CHECK(struct rogue_fwif_hwperf_config_da_blks, 8); + +OFFSET_CHECK(struct rogue_fwif_coreclkspeedchange_data, new_clock_speed, 0); +SIZE_CHECK(struct rogue_fwif_coreclkspeedchange_data, 4); + +OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, enable, 0); +OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, num_blocks, 4); +OFFSET_CHECK(struct rogue_fwif_hwperf_ctrl_blks, block_ids, 8); +SIZE_CHECK(struct rogue_fwif_hwperf_ctrl_blks, 40); + +OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, custom_block, 0); +OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, num_counters, 2); +OFFSET_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, custom_counter_ids_fw_addr, 4); +SIZE_CHECK(struct rogue_fwif_hwperf_select_custom_cntrs, 8); + +OFFSET_CHECK(struct rogue_fwif_zsbuffer_backing_data, zs_buffer_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_zsbuffer_backing_data, done, 4); +SIZE_CHECK(struct rogue_fwif_zsbuffer_backing_data, 8); + +OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, freelist_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, delta_pages, 4); +OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, new_pages, 8); +OFFSET_CHECK(struct rogue_fwif_freelist_gs_data, ready_pages, 12); +SIZE_CHECK(struct rogue_fwif_freelist_gs_data, 16); + +OFFSET_CHECK(struct rogue_fwif_freelists_reconstruction_data, freelist_count, 0); +OFFSET_CHECK(struct rogue_fwif_freelists_reconstruction_data, freelist_ids, 4); +SIZE_CHECK(struct rogue_fwif_freelists_reconstruction_data, 76); + +OFFSET_CHECK(struct rogue_fwif_write_offset_update_data, context_fw_addr, 0); +SIZE_CHECK(struct rogue_fwif_write_offset_update_data, 8); + +OFFSET_CHECK(struct rogue_fwif_kccb_cmd, cmd_type, 0); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd, kccb_flags, 4); +OFFSET_CHECK(struct rogue_fwif_kccb_cmd, cmd_data, 8); +SIZE_CHECK(struct rogue_fwif_kccb_cmd, 88); + +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, server_common_context_id, 0); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, reset_reason, 4); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, dm, 8); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, reset_job_ref, 12); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, flags, 16); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, pc_address, 24); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, fault_address, 32); +SIZE_CHECK(struct rogue_fwif_fwccb_cmd_context_reset_data, 40); + +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd_fw_pagefault_data, fw_fault_addr, 0); +SIZE_CHECK(struct rogue_fwif_fwccb_cmd_fw_pagefault_data, 8); + +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, cmd_type, 0); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, fwccb_flags, 4); +OFFSET_CHECK(struct rogue_fwif_fwccb_cmd, cmd_data, 8); +SIZE_CHECK(struct rogue_fwif_fwccb_cmd, 88); + +OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, cmd_type, 0); +OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, cmd_size, 4); +OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, ext_job_ref, 8); +OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, int_job_ref, 12); +OFFSET_CHECK(struct rogue_fwif_ccb_cmd_header, work_est_kick_data, 16); +SIZE_CHECK(struct rogue_fwif_ccb_cmd_header, 40); + +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, active_pm_latency_ms, 0); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, runtime_cfg_flags, 4); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, active_pm_latency_persistant, 8); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, core_clock_speed, 12); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, default_dusts_num_init, 16); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, phr_mode, 20); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, hcs_deadline_ms, 24); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, wdg_period_us, 28); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, osid_priority, 32); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, hwperf_buf_fw_addr, 64); +OFFSET_CHECK(struct rogue_fwif_runtime_cfg, padding, 68); +SIZE_CHECK(struct rogue_fwif_runtime_cfg, 72); + +OFFSET_CHECK(struct rogue_fwif_connection_ctl, connection_fw_state, 0); +OFFSET_CHECK(struct rogue_fwif_connection_ctl, connection_os_state, 4); +OFFSET_CHECK(struct rogue_fwif_connection_ctl, alive_fw_token, 8); +OFFSET_CHECK(struct rogue_fwif_connection_ctl, alive_os_token, 12); +SIZE_CHECK(struct rogue_fwif_connection_ctl, 16); + +OFFSET_CHECK(struct rogue_fwif_compchecks_bvnc, layout_version, 0); +OFFSET_CHECK(struct rogue_fwif_compchecks_bvnc, bvnc, 8); +SIZE_CHECK(struct rogue_fwif_compchecks_bvnc, 16); + +OFFSET_CHECK(struct rogue_fwif_init_options, os_count_support, 0); +SIZE_CHECK(struct rogue_fwif_init_options, 8); + +OFFSET_CHECK(struct rogue_fwif_compchecks, hw_bvnc, 0); +OFFSET_CHECK(struct rogue_fwif_compchecks, fw_bvnc, 16); +OFFSET_CHECK(struct rogue_fwif_compchecks, fw_processor_version, 32); +OFFSET_CHECK(struct rogue_fwif_compchecks, ddk_version, 36); +OFFSET_CHECK(struct rogue_fwif_compchecks, ddk_build, 40); +OFFSET_CHECK(struct rogue_fwif_compchecks, build_options, 44); +OFFSET_CHECK(struct rogue_fwif_compchecks, init_options, 48); +OFFSET_CHECK(struct rogue_fwif_compchecks, updated, 56); +SIZE_CHECK(struct rogue_fwif_compchecks, 64); + +OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccbctl_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccb_fw_addr, 4); +OFFSET_CHECK(struct rogue_fwif_osinit, kernel_ccb_rtn_slots_fw_addr, 8); +OFFSET_CHECK(struct rogue_fwif_osinit, firmware_ccbctl_fw_addr, 12); +OFFSET_CHECK(struct rogue_fwif_osinit, firmware_ccb_fw_addr, 16); +OFFSET_CHECK(struct rogue_fwif_osinit, work_est_firmware_ccbctl_fw_addr, 20); +OFFSET_CHECK(struct rogue_fwif_osinit, work_est_firmware_ccb_fw_addr, 24); +OFFSET_CHECK(struct rogue_fwif_osinit, rogue_fwif_hwr_info_buf_ctl_fw_addr, 28); +OFFSET_CHECK(struct rogue_fwif_osinit, hwr_debug_dump_limit, 32); +OFFSET_CHECK(struct rogue_fwif_osinit, fw_os_data_fw_addr, 36); +OFFSET_CHECK(struct rogue_fwif_osinit, rogue_comp_checks, 40); +SIZE_CHECK(struct rogue_fwif_osinit, 104); + +OFFSET_CHECK(struct rogue_fwif_sigbuf_ctl, buffer_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_sigbuf_ctl, left_size_in_regs, 4); +SIZE_CHECK(struct rogue_fwif_sigbuf_ctl, 8); + +OFFSET_CHECK(struct pdvfs_opp, volt, 0); +OFFSET_CHECK(struct pdvfs_opp, freq, 4); +SIZE_CHECK(struct pdvfs_opp, 8); + +OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, opp_values, 0); +OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, min_opp_point, 128); +OFFSET_CHECK(struct rogue_fwif_pdvfs_opp, max_opp_point, 132); +SIZE_CHECK(struct rogue_fwif_pdvfs_opp, 136); + +OFFSET_CHECK(struct rogue_fwif_counter_dump_ctl, buffer_fw_addr, 0); +OFFSET_CHECK(struct rogue_fwif_counter_dump_ctl, size_in_dwords, 4); +SIZE_CHECK(struct rogue_fwif_counter_dump_ctl, 8); + +OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_string, 0); +OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_km_feature_flags, 24); +OFFSET_CHECK(struct rogue_hwperf_bvnc, num_bvnc_blocks, 28); +OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_gpu_cores, 30); +OFFSET_CHECK(struct rogue_hwperf_bvnc, bvnc_blocks, 32); +SIZE_CHECK(struct rogue_hwperf_bvnc, 160); + +OFFSET_CHECK(struct rogue_fwif_sysinit, fault_phys_addr, 0); +OFFSET_CHECK(struct rogue_fwif_sysinit, pds_exec_base, 8); +OFFSET_CHECK(struct rogue_fwif_sysinit, usc_exec_base, 16); +OFFSET_CHECK(struct rogue_fwif_sysinit, fbcdc_state_table_base, 24); +OFFSET_CHECK(struct rogue_fwif_sysinit, fbcdc_large_state_table_base, 32); +OFFSET_CHECK(struct rogue_fwif_sysinit, texture_heap_base, 40); +OFFSET_CHECK(struct rogue_fwif_sysinit, hw_perf_filter, 48); +OFFSET_CHECK(struct rogue_fwif_sysinit, slc3_fence_dev_addr, 56); +OFFSET_CHECK(struct rogue_fwif_sysinit, tpu_trilinear_frac_mask, 64); +OFFSET_CHECK(struct rogue_fwif_sysinit, sigbuf_ctl, 80); +OFFSET_CHECK(struct rogue_fwif_sysinit, pdvfs_opp_info, 152); +OFFSET_CHECK(struct rogue_fwif_sysinit, coremem_data_store, 288); +OFFSET_CHECK(struct rogue_fwif_sysinit, counter_dump_ctl, 304); +OFFSET_CHECK(struct rogue_fwif_sysinit, filter_flags, 312); +OFFSET_CHECK(struct rogue_fwif_sysinit, runtime_cfg_fw_addr, 316); +OFFSET_CHECK(struct rogue_fwif_sysinit, trace_buf_ctl_fw_addr, 320); +OFFSET_CHECK(struct rogue_fwif_sysinit, fw_sys_data_fw_addr, 324); +OFFSET_CHECK(struct rogue_fwif_sysinit, gpu_util_fw_cb_ctl_fw_addr, 328); +OFFSET_CHECK(struct rogue_fwif_sysinit, reg_cfg_fw_addr, 332); +OFFSET_CHECK(struct rogue_fwif_sysinit, hwperf_ctl_fw_addr, 336); +OFFSET_CHECK(struct rogue_fwif_sysinit, align_checks, 340); +OFFSET_CHECK(struct rogue_fwif_sysinit, initial_core_clock_speed, 344); +OFFSET_CHECK(struct rogue_fwif_sysinit, active_pm_latency_ms, 348); +OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_started, 352); +OFFSET_CHECK(struct rogue_fwif_sysinit, marker_val, 356); +OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_started_timestamp, 360); +OFFSET_CHECK(struct rogue_fwif_sysinit, jones_disable_mask, 364); +OFFSET_CHECK(struct rogue_fwif_sysinit, firmware_perf, 368); +OFFSET_CHECK(struct rogue_fwif_sysinit, core_clock_rate_fw_addr, 372); +OFFSET_CHECK(struct rogue_fwif_sysinit, gpio_validation_mode, 376); +OFFSET_CHECK(struct rogue_fwif_sysinit, bvnc_km_feature_flags, 380); +OFFSET_CHECK(struct rogue_fwif_sysinit, tfbc_compression_control, 540); +SIZE_CHECK(struct rogue_fwif_sysinit, 544); + +OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, time_corr, 0); +OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, time_corr_seq_count, 10240); +OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, gpu_util_flags, 10244); +OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, last_word, 10248); +OFFSET_CHECK(struct rogue_fwif_gpu_util_fwcb, stats_counters, 10256); +SIZE_CHECK(struct rogue_fwif_gpu_util_fwcb, 10280); + +OFFSET_CHECK(struct rogue_fwif_rta_ctl, render_target_index, 0); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, current_render_target, 4); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, active_render_targets, 8); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, cumul_active_render_targets, 12); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, valid_render_targets_fw_addr, 16); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, rta_num_partial_renders_fw_addr, 20); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, max_rts, 24); +OFFSET_CHECK(struct rogue_fwif_rta_ctl, rta_ctl_flags, 28); +SIZE_CHECK(struct rogue_fwif_rta_ctl, 32); + +OFFSET_CHECK(struct rogue_fwif_freelist, freelist_dev_addr, 0); +OFFSET_CHECK(struct rogue_fwif_freelist, current_dev_addr, 8); +OFFSET_CHECK(struct rogue_fwif_freelist, current_stack_top, 16); +OFFSET_CHECK(struct rogue_fwif_freelist, max_pages, 20); +OFFSET_CHECK(struct rogue_fwif_freelist, grow_pages, 24); +OFFSET_CHECK(struct rogue_fwif_freelist, current_pages, 28); +OFFSET_CHECK(struct rogue_fwif_freelist, allocated_page_count, 32); +OFFSET_CHECK(struct rogue_fwif_freelist, allocated_mmu_page_count, 36); +OFFSET_CHECK(struct rogue_fwif_freelist, freelist_id, 40); +OFFSET_CHECK(struct rogue_fwif_freelist, grow_pending, 44); +OFFSET_CHECK(struct rogue_fwif_freelist, ready_pages, 48); +OFFSET_CHECK(struct rogue_fwif_freelist, freelist_flags, 52); +OFFSET_CHECK(struct rogue_fwif_freelist, pm_global_pb, 56); +SIZE_CHECK(struct rogue_fwif_freelist, 64); + +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, geom_caches_need_zeroing, 0); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, screen_pixel_max, 4); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, multi_sample_ctl, 8); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, flipped_multi_sample_ctl, 16); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, tpc_stride, 24); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, tpc_size, 28); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_screen, 32); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, mtile_stride, 36); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, teaa, 40); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_mtile1, 44); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, te_mtile2, 48); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_lower_x, 52); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_lower_y, 56); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_upper_x, 60); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_upper_y, 64); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_scale_x, 68); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_merge_scale_y, 72); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, rgn_header_size, 76); +OFFSET_CHECK(struct rogue_fwif_hwrtdata_common, isp_mtile_size, 80); +SIZE_CHECK(struct rogue_fwif_hwrtdata_common, 88); + +OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_mlist_dev_addr, 0); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, vce_cat_base, 8); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, vce_last_cat_base, 40); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, te_cat_base, 72); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, te_last_cat_base, 104); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, alist_cat_base, 136); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, alist_last_cat_base, 144); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_alist_stack_pointer, 152); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, pm_mlist_stack_pointer, 160); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, hwrt_data_common_fw_addr, 164); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, hwrt_data_flags, 168); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, state, 172); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, freelists_fw_addr, 176); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, freelist_hwr_snapshot, 188); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, vheap_table_dev_addr, 200); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, rta_ctl, 208); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, tail_ptrs_dev_addr, 240); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, macrotile_array_dev_addr, 248); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, rgn_header_dev_addr, 256); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, rtc_dev_addr, 264); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, owner_geom_not_used_by_host, 272); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, geom_caches_need_zeroing, 276); +OFFSET_CHECK(struct rogue_fwif_hwrtdata, cleanup_state, 320); +SIZE_CHECK(struct rogue_fwif_hwrtdata, 384); + +OFFSET_CHECK(struct rogue_fwif_sync_checkpoint, state, 0); +OFFSET_CHECK(struct rogue_fwif_sync_checkpoint, fw_ref_count, 4); +SIZE_CHECK(struct rogue_fwif_sync_checkpoint, 8); + +#endif /* PVR_ROGUE_FWIF_CHECK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client.h new file mode 100644 index 000000000000..6e224400083a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client.h @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_CLIENT_H +#define PVR_ROGUE_FWIF_CLIENT_H + +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/sizes.h> +#include <linux/types.h> + +#include "pvr_rogue_fwif_shared.h" + +/* + * Page size used for Parameter Management. + */ +#define ROGUE_PM_PAGE_SIZE SZ_4K + +/* + * Minimum/Maximum PB size. + * + * Base page size is dependent on core: + * S6/S6XT/S7 = 50 pages + * S8XE = 40 pages + * S8XE with BRN66011 fixed = 25 pages + * + * Minimum PB = Base Pages + (NUM_TE_PIPES-1)*16K + (NUM_VCE_PIPES-1)*64K + + * IF_PM_PREALLOC(NUM_TE_PIPES*16K + NUM_VCE_PIPES*16K) + * + * Maximum PB size must ensure that no PM address space can be fully used, + * because if the full address space was used it would wrap and corrupt itself. + * Since there are two freelists (local is always minimum sized) this can be + * described as following three conditions being met: + * + * (Minimum PB + Maximum PB) < ALIST PM address space size (16GB) + * (Minimum PB + Maximum PB) < TE PM address space size (16GB) / NUM_TE_PIPES + * (Minimum PB + Maximum PB) < VCE PM address space size (16GB) / NUM_VCE_PIPES + * + * Since the max of NUM_TE_PIPES and NUM_VCE_PIPES is 4, we have a hard limit + * of 4GB minus the Minimum PB. For convenience we take the smaller power-of-2 + * value of 2GB. This is far more than any current applications use. + */ +#define ROGUE_PM_MAX_FREELIST_SIZE SZ_2G + +/* + * Flags supported by the geometry DM command i.e. &struct rogue_fwif_cmd_geom. + */ + +#define ROGUE_GEOM_FLAGS_FIRSTKICK BIT_MASK(0) +#define ROGUE_GEOM_FLAGS_LASTKICK BIT_MASK(1) +/* Use single core in a multi core setup. */ +#define ROGUE_GEOM_FLAGS_SINGLE_CORE BIT_MASK(3) + +/* + * Flags supported by the fragment DM command i.e. &struct rogue_fwif_cmd_frag. + */ + +/* Use single core in a multi core setup. */ +#define ROGUE_FRAG_FLAGS_SINGLE_CORE BIT_MASK(3) +/* Indicates whether this render produces visibility results. */ +#define ROGUE_FRAG_FLAGS_GET_VIS_RESULTS BIT_MASK(5) +/* Indicates whether a depth buffer is present. */ +#define ROGUE_FRAG_FLAGS_DEPTHBUFFER BIT_MASK(7) +/* Indicates whether a stencil buffer is present. */ +#define ROGUE_FRAG_FLAGS_STENCILBUFFER BIT_MASK(8) +/* Disable pixel merging for this render. */ +#define ROGUE_FRAG_FLAGS_DISABLE_PIXELMERGE BIT_MASK(15) +/* Indicates whether a scratch buffer is present. */ +#define ROGUE_FRAG_FLAGS_SCRATCHBUFFER BIT_MASK(19) +/* Disallow compute overlapped with this render. */ +#define ROGUE_FRAG_FLAGS_PREVENT_CDM_OVERLAP BIT_MASK(26) + +/* + * Flags supported by the compute DM command i.e. &struct rogue_fwif_cmd_compute. + */ + +#define ROGUE_COMPUTE_FLAG_PREVENT_ALL_OVERLAP BIT_MASK(2) +/*!< Use single core in a multi core setup. */ +#define ROGUE_COMPUTE_FLAG_SINGLE_CORE BIT_MASK(5) + +/* + * Flags supported by the transfer DM command i.e. &struct rogue_fwif_cmd_transfer. + */ + +/*!< Use single core in a multi core setup. */ +#define ROGUE_TRANSFER_FLAGS_SINGLE_CORE BIT_MASK(1) + +/* + ************************************************ + * Parameter/HWRTData control structures. + ************************************************ + */ + +/* + * Configuration registers which need to be loaded by the firmware before a geometry + * job can be started. + */ +struct rogue_fwif_geom_regs { + u64 vdm_ctrl_stream_base; + u64 tpu_border_colour_table; + + /* Only used when feature VDM_DRAWINDIRECT present. */ + u64 vdm_draw_indirect0; + /* Only used when feature VDM_DRAWINDIRECT present. */ + u32 vdm_draw_indirect1; + + u32 ppp_ctrl; + u32 te_psg; + /* Only used when BRN 49927 present. */ + u32 tpu; + + u32 vdm_context_resume_task0_size; + /* Only used when feature VDM_OBJECT_LEVEL_LLS present. */ + u32 vdm_context_resume_task3_size; + + /* Only used when BRN 56279 or BRN 67381 present. */ + u32 pds_ctrl; + + u32 view_idx; + + /* Only used when feature TESSELLATION present */ + u32 pds_coeff_free_prog; + + u32 padding; +}; + +/* Only used when BRN 44455 or BRN 63027 present. */ +struct rogue_fwif_dummy_rgnhdr_init_geom_regs { + u64 te_psgregion_addr; +}; + +/* + * Represents a geometry command that can be used to tile a whole scene's objects as + * per TA behavior. + */ +struct rogue_fwif_cmd_geom { + /* + * rogue_fwif_cmd_geom_frag_shared field must always be at the beginning of the + * struct. + * + * The command struct (rogue_fwif_cmd_geom) is shared between Client and + * Firmware. Kernel is unable to perform read/write operations on the + * command struct, the SHARED region is the only exception from this rule. + * This region must be the first member so that Kernel can easily access it. + * For more info, see rogue_fwif_cmd_geom_frag_shared definition. + */ + struct rogue_fwif_cmd_geom_frag_shared cmd_shared; + + struct rogue_fwif_geom_regs regs __aligned(8); + u32 flags __aligned(8); + + /* + * Holds the geometry/fragment fence value to allow the fragment partial render command + * to go through. + */ + struct rogue_fwif_ufo partial_render_geom_frag_fence; + + /* Only used when BRN 44455 or BRN 63027 present. */ + struct rogue_fwif_dummy_rgnhdr_init_geom_regs dummy_rgnhdr_init_geom_regs __aligned(8); + + /* Only used when BRN 61484 or BRN 66333 present. */ + u32 brn61484_66333_live_rt; + + u32 padding; +}; + +/* + * Configuration registers which need to be loaded by the firmware before ISP + * can be started. + */ +struct rogue_fwif_frag_regs { + u32 usc_pixel_output_ctrl; + +#define ROGUE_MAXIMUM_OUTPUT_REGISTERS_PER_PIXEL 8U + u32 usc_clear_register[ROGUE_MAXIMUM_OUTPUT_REGISTERS_PER_PIXEL]; + + u32 isp_bgobjdepth; + u32 isp_bgobjvals; + u32 isp_aa; + /* Only used when feature S7_TOP_INFRASTRUCTURE present. */ + u32 isp_xtp_pipe_enable; + + u32 isp_ctl; + + /* Only used when BRN 49927 present. */ + u32 tpu; + + u32 event_pixel_pds_info; + + /* Only used when feature CLUSTER_GROUPING present. */ + u32 pixel_phantom; + + u32 view_idx; + + u32 event_pixel_pds_data; + + /* Only used when BRN 65101 present. */ + u32 brn65101_event_pixel_pds_data; + + /* Only used when feature GPU_MULTICORE_SUPPORT or BRN 47217 present. */ + u32 isp_oclqry_stride; + + /* Only used when feature ZLS_SUBTILE present. */ + u32 isp_zls_pixels; + + /* Only used when feature ISP_ZLS_D24_S8_PACKING_OGL_MODE present. */ + u32 rgx_cr_blackpearl_fix; + + /* All values below the ALIGN(8) must be 64 bit. */ + aligned_u64 isp_scissor_base; + u64 isp_dbias_base; + u64 isp_oclqry_base; + u64 isp_zlsctl; + u64 isp_zload_store_base; + u64 isp_stencil_load_store_base; + + /* + * Only used when feature FBCDC_ALGORITHM present and value < 3 or feature + * FB_CDC_V4 present. Additionally, BRNs 48754, 60227, 72310 and 72311 must + * not be present. + */ + u64 fb_cdc_zls; + +#define ROGUE_PBE_WORDS_REQUIRED_FOR_RENDERS 3U + u64 pbe_word[8U][ROGUE_PBE_WORDS_REQUIRED_FOR_RENDERS]; + u64 tpu_border_colour_table; + u64 pds_bgnd[3U]; + + /* Only used when BRN 65101 present. */ + u64 pds_bgnd_brn65101[3U]; + + u64 pds_pr_bgnd[3U]; + + /* Only used when BRN 62850 or 62865 present. */ + u64 isp_dummy_stencil_store_base; + + /* Only used when BRN 66193 present. */ + u64 isp_dummy_depth_store_base; + + /* Only used when BRN 67182 present. */ + u32 rgnhdr_single_rt_size; + /* Only used when BRN 67182 present. */ + u32 rgnhdr_scratch_offset; +}; + +struct rogue_fwif_cmd_frag { + struct rogue_fwif_cmd_geom_frag_shared cmd_shared __aligned(8); + + struct rogue_fwif_frag_regs regs __aligned(8); + /* command control flags. */ + u32 flags; + /* Stride IN BYTES for Z-Buffer in case of RTAs. */ + u32 zls_stride; + /* Stride IN BYTES for S-Buffer in case of RTAs. */ + u32 sls_stride; + + /* Only used if feature GPU_MULTICORE_SUPPORT present. */ + u32 execute_count; +}; + +/* + * Configuration registers which need to be loaded by the firmware before CDM + * can be started. + */ +struct rogue_fwif_compute_regs { + u64 tpu_border_colour_table; + + /* Only used when feature CDM_USER_MODE_QUEUE present. */ + u64 cdm_cb_queue; + + /* Only used when feature CDM_USER_MODE_QUEUE present. */ + u64 cdm_cb_base; + /* Only used when feature CDM_USER_MODE_QUEUE present. */ + u64 cdm_cb; + + /* Only used when feature CDM_USER_MODE_QUEUE is not present. */ + u64 cdm_ctrl_stream_base; + + u64 cdm_context_state_base_addr; + + /* Only used when BRN 49927 is present. */ + u32 tpu; + u32 cdm_resume_pds1; + + /* Only used when feature COMPUTE_MORTON_CAPABLE present. */ + u32 cdm_item; + + /* Only used when feature CLUSTER_GROUPING present. */ + u32 compute_cluster; + + /* Only used when feature TPU_DM_GLOBAL_REGISTERS present. */ + u32 tpu_tag_cdm_ctrl; + + u32 padding; +}; + +struct rogue_fwif_cmd_compute { + /* Common command attributes */ + struct rogue_fwif_cmd_common common __aligned(8); + + /* CDM registers */ + struct rogue_fwif_compute_regs regs; + + /* Control flags */ + u32 flags __aligned(8); + + /* Only used when feature UNIFIED_STORE_VIRTUAL_PARTITIONING present. */ + u32 num_temp_regions; + + /* Only used when feature CDM_USER_MODE_QUEUE present. */ + u32 stream_start_offset; + + /* Only used when feature GPU_MULTICORE_SUPPORT present. */ + u32 execute_count; +}; + +struct rogue_fwif_transfer_regs { + /* + * All 32 bit values should be added in the top section. This then requires only a + * single RGXFW_ALIGN to align all the 64 bit values in the second section. + */ + u32 isp_bgobjvals; + + u32 usc_pixel_output_ctrl; + u32 usc_clear_register0; + u32 usc_clear_register1; + u32 usc_clear_register2; + u32 usc_clear_register3; + + u32 isp_mtile_size; + u32 isp_render_origin; + u32 isp_ctl; + + /* Only used when feature S7_TOP_INFRASTRUCTURE present. */ + u32 isp_xtp_pipe_enable; + u32 isp_aa; + + u32 event_pixel_pds_info; + + u32 event_pixel_pds_code; + u32 event_pixel_pds_data; + + u32 isp_render; + u32 isp_rgn; + + /* Only used when feature GPU_MULTICORE_SUPPORT present. */ + u32 frag_screen; + + /* All values below the aligned_u64 must be 64 bit. */ + aligned_u64 pds_bgnd0_base; + u64 pds_bgnd1_base; + u64 pds_bgnd3_sizeinfo; + + u64 isp_mtile_base; +#define ROGUE_PBE_WORDS_REQUIRED_FOR_TQS 3 + /* TQ_MAX_RENDER_TARGETS * PBE_STATE_SIZE */ + u64 pbe_wordx_mrty[3U * ROGUE_PBE_WORDS_REQUIRED_FOR_TQS]; +}; + +struct rogue_fwif_cmd_transfer { + /* Common command attributes */ + struct rogue_fwif_cmd_common common __aligned(8); + + struct rogue_fwif_transfer_regs regs __aligned(8); + + u32 flags; + + u32 padding; +}; + +#include "pvr_rogue_fwif_client_check.h" + +#endif /* PVR_ROGUE_FWIF_CLIENT_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h new file mode 100644 index 000000000000..54aa4474163e --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_CLIENT_CHECK_H +#define PVR_ROGUE_FWIF_CLIENT_CHECK_H + +#include <linux/build_bug.h> + +#define OFFSET_CHECK(type, member, offset) \ + static_assert(offsetof(type, member) == (offset), \ + "offsetof(" #type ", " #member ") incorrect") + +#define SIZE_CHECK(type, size) \ + static_assert(sizeof(type) == (size), #type " is incorrect size") + +OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_ctrl_stream_base, 0); +OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu_border_colour_table, 8); +OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_draw_indirect0, 16); +OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_draw_indirect1, 24); +OFFSET_CHECK(struct rogue_fwif_geom_regs, ppp_ctrl, 28); +OFFSET_CHECK(struct rogue_fwif_geom_regs, te_psg, 32); +OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu, 36); +OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_context_resume_task0_size, 40); +OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_context_resume_task3_size, 44); +OFFSET_CHECK(struct rogue_fwif_geom_regs, pds_ctrl, 48); +OFFSET_CHECK(struct rogue_fwif_geom_regs, view_idx, 52); +OFFSET_CHECK(struct rogue_fwif_geom_regs, pds_coeff_free_prog, 56); +SIZE_CHECK(struct rogue_fwif_geom_regs, 64); + +OFFSET_CHECK(struct rogue_fwif_dummy_rgnhdr_init_geom_regs, te_psgregion_addr, 0); +SIZE_CHECK(struct rogue_fwif_dummy_rgnhdr_init_geom_regs, 8); + +OFFSET_CHECK(struct rogue_fwif_cmd_geom, cmd_shared, 0); +OFFSET_CHECK(struct rogue_fwif_cmd_geom, regs, 16); +OFFSET_CHECK(struct rogue_fwif_cmd_geom, flags, 80); +OFFSET_CHECK(struct rogue_fwif_cmd_geom, partial_render_geom_frag_fence, 84); +OFFSET_CHECK(struct rogue_fwif_cmd_geom, dummy_rgnhdr_init_geom_regs, 96); +OFFSET_CHECK(struct rogue_fwif_cmd_geom, brn61484_66333_live_rt, 104); +SIZE_CHECK(struct rogue_fwif_cmd_geom, 112); + +OFFSET_CHECK(struct rogue_fwif_frag_regs, usc_pixel_output_ctrl, 0); +OFFSET_CHECK(struct rogue_fwif_frag_regs, usc_clear_register, 4); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_bgobjdepth, 36); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_bgobjvals, 40); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_aa, 44); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_xtp_pipe_enable, 48); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_ctl, 52); +OFFSET_CHECK(struct rogue_fwif_frag_regs, tpu, 56); +OFFSET_CHECK(struct rogue_fwif_frag_regs, event_pixel_pds_info, 60); +OFFSET_CHECK(struct rogue_fwif_frag_regs, pixel_phantom, 64); +OFFSET_CHECK(struct rogue_fwif_frag_regs, view_idx, 68); +OFFSET_CHECK(struct rogue_fwif_frag_regs, event_pixel_pds_data, 72); +OFFSET_CHECK(struct rogue_fwif_frag_regs, brn65101_event_pixel_pds_data, 76); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_oclqry_stride, 80); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zls_pixels, 84); +OFFSET_CHECK(struct rogue_fwif_frag_regs, rgx_cr_blackpearl_fix, 88); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_scissor_base, 96); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dbias_base, 104); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_oclqry_base, 112); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zlsctl, 120); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_zload_store_base, 128); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_stencil_load_store_base, 136); +OFFSET_CHECK(struct rogue_fwif_frag_regs, fb_cdc_zls, 144); +OFFSET_CHECK(struct rogue_fwif_frag_regs, pbe_word, 152); +OFFSET_CHECK(struct rogue_fwif_frag_regs, tpu_border_colour_table, 344); +OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_bgnd, 352); +OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_bgnd_brn65101, 376); +OFFSET_CHECK(struct rogue_fwif_frag_regs, pds_pr_bgnd, 400); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dummy_stencil_store_base, 424); +OFFSET_CHECK(struct rogue_fwif_frag_regs, isp_dummy_depth_store_base, 432); +OFFSET_CHECK(struct rogue_fwif_frag_regs, rgnhdr_single_rt_size, 440); +OFFSET_CHECK(struct rogue_fwif_frag_regs, rgnhdr_scratch_offset, 444); +SIZE_CHECK(struct rogue_fwif_frag_regs, 448); + +OFFSET_CHECK(struct rogue_fwif_cmd_frag, cmd_shared, 0); +OFFSET_CHECK(struct rogue_fwif_cmd_frag, regs, 16); +OFFSET_CHECK(struct rogue_fwif_cmd_frag, flags, 464); +OFFSET_CHECK(struct rogue_fwif_cmd_frag, zls_stride, 468); +OFFSET_CHECK(struct rogue_fwif_cmd_frag, sls_stride, 472); +OFFSET_CHECK(struct rogue_fwif_cmd_frag, execute_count, 476); +SIZE_CHECK(struct rogue_fwif_cmd_frag, 480); + +OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu_border_colour_table, 0); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb_queue, 8); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb_base, 16); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_cb, 24); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_ctrl_stream_base, 32); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_context_state_base_addr, 40); +OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu, 48); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_resume_pds1, 52); +OFFSET_CHECK(struct rogue_fwif_compute_regs, cdm_item, 56); +OFFSET_CHECK(struct rogue_fwif_compute_regs, compute_cluster, 60); +OFFSET_CHECK(struct rogue_fwif_compute_regs, tpu_tag_cdm_ctrl, 64); +SIZE_CHECK(struct rogue_fwif_compute_regs, 72); + +OFFSET_CHECK(struct rogue_fwif_cmd_compute, common, 0); +OFFSET_CHECK(struct rogue_fwif_cmd_compute, regs, 8); +OFFSET_CHECK(struct rogue_fwif_cmd_compute, flags, 80); +OFFSET_CHECK(struct rogue_fwif_cmd_compute, num_temp_regions, 84); +OFFSET_CHECK(struct rogue_fwif_cmd_compute, stream_start_offset, 88); +OFFSET_CHECK(struct rogue_fwif_cmd_compute, execute_count, 92); +SIZE_CHECK(struct rogue_fwif_cmd_compute, 96); + +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_bgobjvals, 0); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_pixel_output_ctrl, 4); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register0, 8); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register1, 12); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register2, 16); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, usc_clear_register3, 20); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_mtile_size, 24); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_render_origin, 28); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_ctl, 32); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_xtp_pipe_enable, 36); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_aa, 40); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_info, 44); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_code, 48); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, event_pixel_pds_data, 52); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_render, 56); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_rgn, 60); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, frag_screen, 64); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd0_base, 72); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd1_base, 80); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, pds_bgnd3_sizeinfo, 88); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, isp_mtile_base, 96); +OFFSET_CHECK(struct rogue_fwif_transfer_regs, pbe_wordx_mrty, 104); +SIZE_CHECK(struct rogue_fwif_transfer_regs, 176); + +OFFSET_CHECK(struct rogue_fwif_cmd_transfer, common, 0); +OFFSET_CHECK(struct rogue_fwif_cmd_transfer, regs, 8); +OFFSET_CHECK(struct rogue_fwif_cmd_transfer, flags, 184); +SIZE_CHECK(struct rogue_fwif_cmd_transfer, 192); + +#endif /* PVR_ROGUE_FWIF_CLIENT_CHECK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_common.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_common.h new file mode 100644 index 000000000000..6ebb95ba98a6 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_common.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_COMMON_H +#define PVR_ROGUE_FWIF_COMMON_H + +#include <linux/build_bug.h> + +/* + * This macro represents a mask of LSBs that must be zero on data structure + * sizes and offsets to ensure they are 8-byte granular on types shared between + * the FW and host driver. + */ +#define PVR_FW_ALIGNMENT_LSB 7U + +/* Macro to test structure size alignment. */ +#define PVR_FW_STRUCT_SIZE_ASSERT(_a) \ + static_assert((sizeof(_a) & PVR_FW_ALIGNMENT_LSB) == 0U, \ + "Size of " #_a " is not properly aligned") + +/* The master definition for data masters known to the firmware. */ + +#define PVR_FWIF_DM_GP (0) +/* Either TDM or 2D DM is present. */ +/* When the 'tla' feature is present in the hw (as per @pvr_device_features). */ +#define PVR_FWIF_DM_2D (1) +/* + * When the 'fastrender_dm' feature is present in the hw (as per + * @pvr_device_features). + */ +#define PVR_FWIF_DM_TDM (1) + +#define PVR_FWIF_DM_GEOM (2) +#define PVR_FWIF_DM_FRAG (3) +#define PVR_FWIF_DM_CDM (4) +#define PVR_FWIF_DM_RAY (5) +#define PVR_FWIF_DM_GEOM2 (6) +#define PVR_FWIF_DM_GEOM3 (7) +#define PVR_FWIF_DM_GEOM4 (8) + +#define PVR_FWIF_DM_LAST PVR_FWIF_DM_GEOM4 + +/* Maximum number of DM in use: GP, 2D/TDM, GEOM, 3D, CDM, RAY, GEOM2, GEOM3, GEOM4 */ +#define PVR_FWIF_DM_MAX (PVR_FWIF_DM_LAST + 1U) + +/* GPU Utilisation states */ +#define PVR_FWIF_GPU_UTIL_STATE_IDLE 0U +#define PVR_FWIF_GPU_UTIL_STATE_ACTIVE 1U +#define PVR_FWIF_GPU_UTIL_STATE_BLOCKED 2U +#define PVR_FWIF_GPU_UTIL_STATE_NUM 3U +#define PVR_FWIF_GPU_UTIL_STATE_MASK 0x3ULL + +/* + * Maximum amount of register writes that can be done by the register + * programmer (FW or META DMA). This is not a HW limitation, it is only + * a protection against malformed inputs to the register programmer. + */ +#define PVR_MAX_NUM_REGISTER_PROGRAMMER_WRITES 128U + +#endif /* PVR_ROGUE_FWIF_COMMON_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_dev_info.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_dev_info.h new file mode 100644 index 000000000000..168277bce948 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_dev_info.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef __PVR_ROGUE_FWIF_DEV_INFO_H__ +#define __PVR_ROGUE_FWIF_DEV_INFO_H__ + +enum { + PVR_FW_HAS_BRN_44079 = 0, + PVR_FW_HAS_BRN_47217, + PVR_FW_HAS_BRN_48492, + PVR_FW_HAS_BRN_48545, + PVR_FW_HAS_BRN_49927, + PVR_FW_HAS_BRN_50767, + PVR_FW_HAS_BRN_51764, + PVR_FW_HAS_BRN_62269, + PVR_FW_HAS_BRN_63142, + PVR_FW_HAS_BRN_63553, + PVR_FW_HAS_BRN_66011, + PVR_FW_HAS_BRN_71242, + + PVR_FW_HAS_BRN_MAX +}; + +enum { + PVR_FW_HAS_ERN_35421 = 0, + PVR_FW_HAS_ERN_38020, + PVR_FW_HAS_ERN_38748, + PVR_FW_HAS_ERN_42064, + PVR_FW_HAS_ERN_42290, + PVR_FW_HAS_ERN_42606, + PVR_FW_HAS_ERN_47025, + PVR_FW_HAS_ERN_57596, + + PVR_FW_HAS_ERN_MAX +}; + +enum { + PVR_FW_HAS_FEATURE_AXI_ACELITE = 0, + PVR_FW_HAS_FEATURE_CDM_CONTROL_STREAM_FORMAT, + PVR_FW_HAS_FEATURE_CLUSTER_GROUPING, + PVR_FW_HAS_FEATURE_COMMON_STORE_SIZE_IN_DWORDS, + PVR_FW_HAS_FEATURE_COMPUTE, + PVR_FW_HAS_FEATURE_COMPUTE_MORTON_CAPABLE, + PVR_FW_HAS_FEATURE_COMPUTE_OVERLAP, + PVR_FW_HAS_FEATURE_COREID_PER_OS, + PVR_FW_HAS_FEATURE_DYNAMIC_DUST_POWER, + PVR_FW_HAS_FEATURE_ECC_RAMS, + PVR_FW_HAS_FEATURE_FBCDC, + PVR_FW_HAS_FEATURE_FBCDC_ALGORITHM, + PVR_FW_HAS_FEATURE_FBCDC_ARCHITECTURE, + PVR_FW_HAS_FEATURE_FBC_MAX_DEFAULT_DESCRIPTORS, + PVR_FW_HAS_FEATURE_FBC_MAX_LARGE_DESCRIPTORS, + PVR_FW_HAS_FEATURE_FB_CDC_V4, + PVR_FW_HAS_FEATURE_GPU_MULTICORE_SUPPORT, + PVR_FW_HAS_FEATURE_GPU_VIRTUALISATION, + PVR_FW_HAS_FEATURE_GS_RTA_SUPPORT, + PVR_FW_HAS_FEATURE_IRQ_PER_OS, + PVR_FW_HAS_FEATURE_ISP_MAX_TILES_IN_FLIGHT, + PVR_FW_HAS_FEATURE_ISP_SAMPLES_PER_PIXEL, + PVR_FW_HAS_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE, + PVR_FW_HAS_FEATURE_LAYOUT_MARS, + PVR_FW_HAS_FEATURE_MAX_PARTITIONS, + PVR_FW_HAS_FEATURE_META, + PVR_FW_HAS_FEATURE_META_COREMEM_SIZE, + PVR_FW_HAS_FEATURE_MIPS, + PVR_FW_HAS_FEATURE_NUM_CLUSTERS, + PVR_FW_HAS_FEATURE_NUM_ISP_IPP_PIPES, + PVR_FW_HAS_FEATURE_NUM_OSIDS, + PVR_FW_HAS_FEATURE_NUM_RASTER_PIPES, + PVR_FW_HAS_FEATURE_PBE2_IN_XE, + PVR_FW_HAS_FEATURE_PBVNC_COREID_REG, + PVR_FW_HAS_FEATURE_PERFBUS, + PVR_FW_HAS_FEATURE_PERF_COUNTER_BATCH, + PVR_FW_HAS_FEATURE_PHYS_BUS_WIDTH, + PVR_FW_HAS_FEATURE_RISCV_FW_PROCESSOR, + PVR_FW_HAS_FEATURE_ROGUEXE, + PVR_FW_HAS_FEATURE_S7_TOP_INFRASTRUCTURE, + PVR_FW_HAS_FEATURE_SIMPLE_INTERNAL_PARAMETER_FORMAT, + PVR_FW_HAS_FEATURE_SIMPLE_INTERNAL_PARAMETER_FORMAT_V2, + PVR_FW_HAS_FEATURE_SIMPLE_PARAMETER_FORMAT_VERSION, + PVR_FW_HAS_FEATURE_SLC_BANKS, + PVR_FW_HAS_FEATURE_SLC_CACHE_LINE_SIZE_BITS, + PVR_FW_HAS_FEATURE_SLC_SIZE_CONFIGURABLE, + PVR_FW_HAS_FEATURE_SLC_SIZE_IN_KILOBYTES, + PVR_FW_HAS_FEATURE_SOC_TIMER, + PVR_FW_HAS_FEATURE_SYS_BUS_SECURE_RESET, + PVR_FW_HAS_FEATURE_TESSELLATION, + PVR_FW_HAS_FEATURE_TILE_REGION_PROTECTION, + PVR_FW_HAS_FEATURE_TILE_SIZE_X, + PVR_FW_HAS_FEATURE_TILE_SIZE_Y, + PVR_FW_HAS_FEATURE_TLA, + PVR_FW_HAS_FEATURE_TPU_CEM_DATAMASTER_GLOBAL_REGISTERS, + PVR_FW_HAS_FEATURE_TPU_DM_GLOBAL_REGISTERS, + PVR_FW_HAS_FEATURE_TPU_FILTERING_MODE_CONTROL, + PVR_FW_HAS_FEATURE_USC_MIN_OUTPUT_REGISTERS_PER_PIX, + PVR_FW_HAS_FEATURE_VDM_DRAWINDIRECT, + PVR_FW_HAS_FEATURE_VDM_OBJECT_LEVEL_LLS, + PVR_FW_HAS_FEATURE_VIRTUAL_ADDRESS_SPACE_BITS, + PVR_FW_HAS_FEATURE_WATCHDOG_TIMER, + PVR_FW_HAS_FEATURE_WORKGROUP_PROTECTION, + PVR_FW_HAS_FEATURE_XE_ARCHITECTURE, + PVR_FW_HAS_FEATURE_XE_MEMORY_HIERARCHY, + PVR_FW_HAS_FEATURE_XE_TPU2, + PVR_FW_HAS_FEATURE_XPU_MAX_REGBANKS_ADDR_WIDTH, + PVR_FW_HAS_FEATURE_XPU_MAX_SLAVES, + PVR_FW_HAS_FEATURE_XPU_REGISTER_BROADCAST, + PVR_FW_HAS_FEATURE_XT_TOP_INFRASTRUCTURE, + PVR_FW_HAS_FEATURE_ZLS_SUBTILE, + + PVR_FW_HAS_FEATURE_MAX +}; + +#endif /* __PVR_ROGUE_FWIF_DEV_INFO_H__ */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_resetframework.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_resetframework.h new file mode 100644 index 000000000000..1db1f4c532bc --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_resetframework.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_RESETFRAMEWORK_H +#define PVR_ROGUE_FWIF_RESETFRAMEWORK_H + +#include <linux/bits.h> +#include <linux/types.h> + +#include "pvr_rogue_fwif_shared.h" + +struct rogue_fwif_rf_registers { + union { + u64 cdmreg_cdm_cb_base; + u64 cdmreg_cdm_ctrl_stream_base; + }; + u64 cdmreg_cdm_cb_queue; + u64 cdmreg_cdm_cb; +}; + +struct rogue_fwif_rf_cmd { + /* THIS MUST BE THE LAST MEMBER OF THE CONTAINING STRUCTURE */ + struct rogue_fwif_rf_registers fw_registers __aligned(8); +}; + +#define ROGUE_FWIF_RF_CMD_SIZE sizeof(struct rogue_fwif_rf_cmd) + +#endif /* PVR_ROGUE_FWIF_RESETFRAMEWORK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_sf.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_sf.h new file mode 100644 index 000000000000..571954182f33 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_sf.h @@ -0,0 +1,1648 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_SF_H +#define PVR_ROGUE_FWIF_SF_H + +/* + ****************************************************************************** + * *DO*NOT* rearrange or delete lines in rogue_fw_log_sfgroups or stid_fmts + * WILL BREAK fw tracing message compatibility with previous + * fw versions. Only add new ones, if so required. + ****************************************************************************** + */ + +/* Available log groups. */ +enum rogue_fw_log_sfgroups { + ROGUE_FW_GROUP_NULL, + ROGUE_FW_GROUP_MAIN, + ROGUE_FW_GROUP_CLEANUP, + ROGUE_FW_GROUP_CSW, + ROGUE_FW_GROUP_PM, + ROGUE_FW_GROUP_RTD, + ROGUE_FW_GROUP_SPM, + ROGUE_FW_GROUP_MTS, + ROGUE_FW_GROUP_BIF, + ROGUE_FW_GROUP_MISC, + ROGUE_FW_GROUP_POW, + ROGUE_FW_GROUP_HWR, + ROGUE_FW_GROUP_HWP, + ROGUE_FW_GROUP_RPM, + ROGUE_FW_GROUP_DMA, + ROGUE_FW_GROUP_DBG, +}; + +#define PVR_SF_STRING_MAX_SIZE 256U + +/* pair of string format id and string formats */ +struct rogue_fw_stid_fmt { + u32 id; + char name[PVR_SF_STRING_MAX_SIZE]; +}; + +/* + * The symbolic names found in the table above are assigned an u32 value of + * the following format: + * 31 30 28 27 20 19 16 15 12 11 0 bits + * - --- ---- ---- ---- ---- ---- ---- ---- + * 0-11: id number + * 12-15: group id number + * 16-19: number of parameters + * 20-27: unused + * 28-30: active: identify SF packet, otherwise regular int32 + * 31: reserved for signed/unsigned compatibility + * + * The following macro assigns those values to the enum generated SF ids list. + */ +#define ROGUE_FW_LOG_IDMARKER (0x70000000U) +#define ROGUE_FW_LOG_CREATESFID(a, b, e) ((u32)(a) | ((u32)(b) << 12) | ((u32)(e) << 16) | \ + ROGUE_FW_LOG_IDMARKER) + +#define ROGUE_FW_LOG_IDMASK (0xFFF00000) +#define ROGUE_FW_LOG_VALIDID(I) (((I) & ROGUE_FW_LOG_IDMASK) == ROGUE_FW_LOG_IDMARKER) + +/* Return the group id that the given (enum generated) id belongs to */ +#define ROGUE_FW_SF_GID(x) (((u32)(x) >> 12) & 0xfU) +/* Returns how many arguments the SF(string format) for the given (enum generated) id requires */ +#define ROGUE_FW_SF_PARAMNUM(x) (((u32)(x) >> 16) & 0xfU) + +/* pair of string format id and string formats */ +struct rogue_km_stid_fmt { + u32 id; + const char *name; +}; + +static const struct rogue_km_stid_fmt stid_fmts[] = { + { ROGUE_FW_LOG_CREATESFID(0, ROGUE_FW_GROUP_NULL, 0), + "You should not use this string" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_MAIN, 6), + "Kick 3D: FWCtx 0x%08.8x @ %d, RTD 0x%08x. Partial render:%d, CSW resume:%d, prio:%d" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_MAIN, 2), + "3D finished, HWRTData0State=%x, HWRTData1State=%x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_MAIN, 4), + "Kick 3D TQ: FWCtx 0x%08.8x @ %d, CSW resume:%d, prio: %d" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_MAIN, 0), + "3D Transfer finished" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_MAIN, 3), + "Kick Compute: FWCtx 0x%08.8x @ %d, prio: %d" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_MAIN, 0), + "Compute finished" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_MAIN, 7), + "Kick TA: FWCtx 0x%08.8x @ %d, RTD 0x%08x. First kick:%d, Last kick:%d, CSW resume:%d, prio:%d" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_MAIN, 0), + "TA finished" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_MAIN, 0), + "Restart TA after partial render" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_MAIN, 0), + "Resume TA without partial render" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_MAIN, 2), + "Out of memory! Context 0x%08x, HWRTData 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_MAIN, 3), + "Kick TLA: FWCtx 0x%08.8x @ %d, prio:%d" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_MAIN, 0), + "TLA finished" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_MAIN, 3), + "cCCB Woff update = %d, DM = %d, FWCtx = 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_MAIN, 2), + "UFO Checks for FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_MAIN, 3), + "UFO Check: [0x%08.8x] is 0x%08.8x requires 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_MAIN, 0), + "UFO Checks succeeded" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_MAIN, 3), + "UFO PR-Check: [0x%08.8x] is 0x%08.8x requires >= 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_MAIN, 1), + "UFO SPM PR-Checks for FWCtx 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_MAIN, 4), + "UFO SPM special PR-Check: [0x%08.8x] is 0x%08.8x requires >= ????????, [0x%08.8x] is ???????? requires 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_MAIN, 2), + "UFO Updates for FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_MAIN, 2), + "UFO Update: [0x%08.8x] = 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_MAIN, 1), + "ASSERT Failed: line %d of:" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_MAIN, 2), + "HWR: Lockup detected on DM%d, FWCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_MAIN, 3), + "HWR: Reset fw state for DM%d, FWCtx: 0x%08.8x, MemCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_MAIN, 0), + "HWR: Reset HW" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_MAIN, 0), + "HWR: Lockup recovered." }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_MAIN, 1), + "HWR: False lockup detected for DM%u" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_MAIN, 3), + "Alignment check %d failed: host = 0x%x, fw = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_MAIN, 0), + "GP USC triggered" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_MAIN, 2), + "Overallocating %u temporary registers and %u shared registers for breakpoint handler" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_MAIN, 1), + "Setting breakpoint: Addr 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_MAIN, 0), + "Store breakpoint state" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_MAIN, 0), + "Unsetting BP Registers" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_MAIN, 1), + "Active RTs expected to be zero, actually %u" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_MAIN, 1), + "RTC present, %u active render targets" }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_MAIN, 1), + "Estimated Power 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_MAIN, 1), + "RTA render target %u" }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_MAIN, 2), + "Kick RTA render %u of %u" }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_MAIN, 3), + "HWR sizes check %d failed: addresses = %d, sizes = %d" }, + { ROGUE_FW_LOG_CREATESFID(42, ROGUE_FW_GROUP_MAIN, 1), + "Pow: DUSTS_ENABLE = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(43, ROGUE_FW_GROUP_MAIN, 2), + "Pow: On(1)/Off(0): %d, Units: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(44, ROGUE_FW_GROUP_MAIN, 2), + "Pow: Changing number of dusts from %d to %d" }, + { ROGUE_FW_LOG_CREATESFID(45, ROGUE_FW_GROUP_MAIN, 0), + "Pow: Sidekick ready to be powered down" }, + { ROGUE_FW_LOG_CREATESFID(46, ROGUE_FW_GROUP_MAIN, 2), + "Pow: Request to change num of dusts to %d (bPowRascalDust=%d)" }, + { ROGUE_FW_LOG_CREATESFID(47, ROGUE_FW_GROUP_MAIN, 0), + "No ZS Buffer used for partial render (store)" }, + { ROGUE_FW_LOG_CREATESFID(48, ROGUE_FW_GROUP_MAIN, 0), + "No Depth/Stencil Buffer used for partial render (load)" }, + { ROGUE_FW_LOG_CREATESFID(49, ROGUE_FW_GROUP_MAIN, 2), + "HWR: Lock-up DM%d FWCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(50, ROGUE_FW_GROUP_MAIN, 7), + "MLIST%d checker: CatBase TE=0x%08x (%d Pages), VCE=0x%08x (%d Pages), ALIST=0x%08x, IsTA=%d" }, + { ROGUE_FW_LOG_CREATESFID(51, ROGUE_FW_GROUP_MAIN, 3), + "MLIST%d checker: MList[%d] = 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(52, ROGUE_FW_GROUP_MAIN, 1), + "MLIST%d OK" }, + { ROGUE_FW_LOG_CREATESFID(53, ROGUE_FW_GROUP_MAIN, 1), + "MLIST%d is empty" }, + { ROGUE_FW_LOG_CREATESFID(54, ROGUE_FW_GROUP_MAIN, 8), + "MLIST%d checker: CatBase TE=0x%08x%08x, VCE=0x%08x%08x, ALIST=0x%08x%08x, IsTA=%d" }, + { ROGUE_FW_LOG_CREATESFID(55, ROGUE_FW_GROUP_MAIN, 0), + "3D OQ flush kick" }, + { ROGUE_FW_LOG_CREATESFID(56, ROGUE_FW_GROUP_MAIN, 1), + "HWPerf block ID (0x%x) unsupported by device" }, + { ROGUE_FW_LOG_CREATESFID(57, ROGUE_FW_GROUP_MAIN, 2), + "Setting breakpoint: Addr 0x%08.8x DM%u" }, + { ROGUE_FW_LOG_CREATESFID(58, ROGUE_FW_GROUP_MAIN, 3), + "Kick RTU: FWCtx 0x%08.8x @ %d, prio: %d" }, + { ROGUE_FW_LOG_CREATESFID(59, ROGUE_FW_GROUP_MAIN, 1), + "RDM finished on context %u" }, + { ROGUE_FW_LOG_CREATESFID(60, ROGUE_FW_GROUP_MAIN, 3), + "Kick SHG: FWCtx 0x%08.8x @ %d, prio: %d" }, + { ROGUE_FW_LOG_CREATESFID(61, ROGUE_FW_GROUP_MAIN, 0), + "SHG finished" }, + { ROGUE_FW_LOG_CREATESFID(62, ROGUE_FW_GROUP_MAIN, 1), + "FBA finished on context %u" }, + { ROGUE_FW_LOG_CREATESFID(63, ROGUE_FW_GROUP_MAIN, 0), + "UFO Checks failed" }, + { ROGUE_FW_LOG_CREATESFID(64, ROGUE_FW_GROUP_MAIN, 1), + "Kill DM%d start" }, + { ROGUE_FW_LOG_CREATESFID(65, ROGUE_FW_GROUP_MAIN, 1), + "Kill DM%d complete" }, + { ROGUE_FW_LOG_CREATESFID(66, ROGUE_FW_GROUP_MAIN, 2), + "FC%u cCCB Woff update = %u" }, + { ROGUE_FW_LOG_CREATESFID(67, ROGUE_FW_GROUP_MAIN, 4), + "Kick RTU: FWCtx 0x%08.8x @ %d, prio: %d, Frame Context: %d" }, + { ROGUE_FW_LOG_CREATESFID(68, ROGUE_FW_GROUP_MAIN, 0), + "GPU init" }, + { ROGUE_FW_LOG_CREATESFID(69, ROGUE_FW_GROUP_MAIN, 1), + "GPU Units init (# mask: 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(70, ROGUE_FW_GROUP_MAIN, 3), + "Register access cycles: read: %d cycles, write: %d cycles, iterations: %d" }, + { ROGUE_FW_LOG_CREATESFID(71, ROGUE_FW_GROUP_MAIN, 3), + "Register configuration added. Address: 0x%x Value: 0x%x%x" }, + { ROGUE_FW_LOG_CREATESFID(72, ROGUE_FW_GROUP_MAIN, 1), + "Register configuration applied to type %d. (0:pow on, 1:Rascal/dust init, 2-5: TA,3D,CDM,TLA, 6:All)" }, + { ROGUE_FW_LOG_CREATESFID(73, ROGUE_FW_GROUP_MAIN, 0), + "Perform TPC flush." }, + { ROGUE_FW_LOG_CREATESFID(74, ROGUE_FW_GROUP_MAIN, 0), + "GPU has locked up (see HWR logs for more info)" }, + { ROGUE_FW_LOG_CREATESFID(75, ROGUE_FW_GROUP_MAIN, 0), + "HWR has been triggered - GPU has overrun its deadline (see HWR logs)" }, + { ROGUE_FW_LOG_CREATESFID(76, ROGUE_FW_GROUP_MAIN, 0), + "HWR has been triggered - GPU has failed a poll (see HWR logs)" }, + { ROGUE_FW_LOG_CREATESFID(77, ROGUE_FW_GROUP_MAIN, 1), + "Doppler out of memory event for FC %u" }, + { ROGUE_FW_LOG_CREATESFID(78, ROGUE_FW_GROUP_MAIN, 3), + "UFO SPM special PR-Check: [0x%08.8x] is 0x%08.8x requires >= 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(79, ROGUE_FW_GROUP_MAIN, 3), + "UFO SPM special PR-Check: [0x%08.8x] is 0x%08.8x requires 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(80, ROGUE_FW_GROUP_MAIN, 1), + "TIMESTAMP -> [0x%08.8x]" }, + { ROGUE_FW_LOG_CREATESFID(81, ROGUE_FW_GROUP_MAIN, 2), + "UFO RMW Updates for FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(82, ROGUE_FW_GROUP_MAIN, 2), + "UFO Update: [0x%08.8x] = 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(83, ROGUE_FW_GROUP_MAIN, 2), + "Kick Null cmd: FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(84, ROGUE_FW_GROUP_MAIN, 2), + "RPM Out of memory! Context 0x%08x, SH requestor %d" }, + { ROGUE_FW_LOG_CREATESFID(85, ROGUE_FW_GROUP_MAIN, 4), + "Discard RTU due to RPM abort: FWCtx 0x%08.8x @ %d, prio: %d, Frame Context: %d" }, + { ROGUE_FW_LOG_CREATESFID(86, ROGUE_FW_GROUP_MAIN, 4), + "Deferring DM%u from running context 0x%08x @ %d (deferred DMs = 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(87, ROGUE_FW_GROUP_MAIN, 4), + "Deferring DM%u from running context 0x%08x @ %d to let other deferred DMs run (deferred DMs = 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(88, ROGUE_FW_GROUP_MAIN, 4), + "No longer deferring DM%u from running context = 0x%08x @ %d (deferred DMs = 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(89, ROGUE_FW_GROUP_MAIN, 3), + "FWCCB for DM%u is full, we will have to wait for space! (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(90, ROGUE_FW_GROUP_MAIN, 3), + "FWCCB for OSid %u is full, we will have to wait for space! (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(91, ROGUE_FW_GROUP_MAIN, 1), + "Host Sync Partition marker: %d" }, + { ROGUE_FW_LOG_CREATESFID(92, ROGUE_FW_GROUP_MAIN, 1), + "Host Sync Partition repeat: %d" }, + { ROGUE_FW_LOG_CREATESFID(93, ROGUE_FW_GROUP_MAIN, 1), + "Core clock set to %d Hz" }, + { ROGUE_FW_LOG_CREATESFID(94, ROGUE_FW_GROUP_MAIN, 7), + "Compute Queue: FWCtx 0x%08.8x, prio: %d, queue: 0x%08x%08x (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(95, ROGUE_FW_GROUP_MAIN, 3), + "Signal check failed, Required Data: 0x%x, Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(96, ROGUE_FW_GROUP_MAIN, 5), + "Signal update, Snoop Filter: %u, MMU Ctx: %u, Signal Id: %u, Signals Base: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(97, ROGUE_FW_GROUP_MAIN, 4), + "Signalled the previously waiting FWCtx: 0x%08.8x, OSId: %u, Signal Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(98, ROGUE_FW_GROUP_MAIN, 0), + "Compute stalled" }, + { ROGUE_FW_LOG_CREATESFID(99, ROGUE_FW_GROUP_MAIN, 3), + "Compute stalled (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(100, ROGUE_FW_GROUP_MAIN, 3), + "Compute resumed (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(101, ROGUE_FW_GROUP_MAIN, 4), + "Signal update notification from the host, PC Physical Address: 0x%08x%08x, Signal Virtual Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(102, ROGUE_FW_GROUP_MAIN, 4), + "Signal update from DM: %u, OSId: %u, PC Physical Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(103, ROGUE_FW_GROUP_MAIN, 1), + "DM: %u signal check failed" }, + { ROGUE_FW_LOG_CREATESFID(104, ROGUE_FW_GROUP_MAIN, 3), + "Kick TDM: FWCtx 0x%08.8x @ %d, prio:%d" }, + { ROGUE_FW_LOG_CREATESFID(105, ROGUE_FW_GROUP_MAIN, 0), + "TDM finished" }, + { ROGUE_FW_LOG_CREATESFID(106, ROGUE_FW_GROUP_MAIN, 4), + "MMU_PM_CAT_BASE_TE[%d]_PIPE[%d]: 0x%08x 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(107, ROGUE_FW_GROUP_MAIN, 0), + "BRN 54141 HIT" }, + { ROGUE_FW_LOG_CREATESFID(108, ROGUE_FW_GROUP_MAIN, 0), + "BRN 54141 Dummy TA kicked" }, + { ROGUE_FW_LOG_CREATESFID(109, ROGUE_FW_GROUP_MAIN, 0), + "BRN 54141 resume TA" }, + { ROGUE_FW_LOG_CREATESFID(110, ROGUE_FW_GROUP_MAIN, 0), + "BRN 54141 double hit after applying WA" }, + { ROGUE_FW_LOG_CREATESFID(111, ROGUE_FW_GROUP_MAIN, 2), + "BRN 54141 Dummy TA VDM base address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(112, ROGUE_FW_GROUP_MAIN, 4), + "Signal check failed, Required Data: 0x%x, Current Data: 0x%x, Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(113, ROGUE_FW_GROUP_MAIN, 2), + "TDM stalled (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(114, ROGUE_FW_GROUP_MAIN, 1), + "Write Offset update notification for stalled FWCtx 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(115, ROGUE_FW_GROUP_MAIN, 3), + "Changing OSid %d's priority from %u to %u" }, + { ROGUE_FW_LOG_CREATESFID(116, ROGUE_FW_GROUP_MAIN, 0), + "Compute resumed" }, + { ROGUE_FW_LOG_CREATESFID(117, ROGUE_FW_GROUP_MAIN, 7), + "Kick TLA: FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(118, ROGUE_FW_GROUP_MAIN, 7), + "Kick TDM: FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(119, ROGUE_FW_GROUP_MAIN, 11), + "Kick TA: FWCtx 0x%08.8x @ %d, RTD 0x%08x, First kick:%d, Last kick:%d, CSW resume:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(120, ROGUE_FW_GROUP_MAIN, 10), + "Kick 3D: FWCtx 0x%08.8x @ %d, RTD 0x%08x, Partial render:%d, CSW resume:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(121, ROGUE_FW_GROUP_MAIN, 8), + "Kick 3D TQ: FWCtx 0x%08.8x @ %d, CSW resume:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(122, ROGUE_FW_GROUP_MAIN, 6), + "Kick Compute: FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(123, ROGUE_FW_GROUP_MAIN, 8), + "Kick RTU: FWCtx 0x%08.8x @ %d, Frame Context:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(124, ROGUE_FW_GROUP_MAIN, 7), + "Kick SHG: FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(125, ROGUE_FW_GROUP_MAIN, 1), + "Reconfigure CSRM: special coeff support enable %d." }, + { ROGUE_FW_LOG_CREATESFID(127, ROGUE_FW_GROUP_MAIN, 1), + "TA requires max coeff mode, deferring: %d." }, + { ROGUE_FW_LOG_CREATESFID(128, ROGUE_FW_GROUP_MAIN, 1), + "3D requires max coeff mode, deferring: %d." }, + { ROGUE_FW_LOG_CREATESFID(129, ROGUE_FW_GROUP_MAIN, 1), + "Kill DM%d failed" }, + { ROGUE_FW_LOG_CREATESFID(130, ROGUE_FW_GROUP_MAIN, 2), + "Thread Queue is full, we will have to wait for space! (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(131, ROGUE_FW_GROUP_MAIN, 3), + "Thread Queue is fencing, we are waiting for Roff = %d (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(132, ROGUE_FW_GROUP_MAIN, 1), + "DM %d failed to Context Switch on time. Triggered HCS (see HWR logs)." }, + { ROGUE_FW_LOG_CREATESFID(133, ROGUE_FW_GROUP_MAIN, 1), + "HCS changed to %d ms" }, + { ROGUE_FW_LOG_CREATESFID(134, ROGUE_FW_GROUP_MAIN, 4), + "Updating Tiles In Flight (Dusts=%d, PartitionMask=0x%08x, ISPCtl=0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(135, ROGUE_FW_GROUP_MAIN, 2), + " Phantom %d: USCTiles=%d" }, + { ROGUE_FW_LOG_CREATESFID(136, ROGUE_FW_GROUP_MAIN, 0), + "Isolation grouping is disabled" }, + { ROGUE_FW_LOG_CREATESFID(137, ROGUE_FW_GROUP_MAIN, 1), + "Isolation group configured with a priority threshold of %d" }, + { ROGUE_FW_LOG_CREATESFID(138, ROGUE_FW_GROUP_MAIN, 1), + "OS %d has come online" }, + { ROGUE_FW_LOG_CREATESFID(139, ROGUE_FW_GROUP_MAIN, 1), + "OS %d has gone offline" }, + { ROGUE_FW_LOG_CREATESFID(140, ROGUE_FW_GROUP_MAIN, 4), + "Signalled the previously stalled FWCtx: 0x%08.8x, OSId: %u, Signal Address: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(141, ROGUE_FW_GROUP_MAIN, 7), + "TDM Queue: FWCtx 0x%08.8x, prio: %d, queue: 0x%08x%08x (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(142, ROGUE_FW_GROUP_MAIN, 6), + "Reset TDM Queue Read Offset: FWCtx 0x%08.8x, queue: 0x%08x%08x (Roff = %u becomes 0, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(143, ROGUE_FW_GROUP_MAIN, 5), + "User Mode Queue mismatched stream start: FWCtx 0x%08.8x, queue: 0x%08x%08x (Roff = %u, StreamStartOffset = %u)" }, + { ROGUE_FW_LOG_CREATESFID(144, ROGUE_FW_GROUP_MAIN, 0), + "GPU deinit" }, + { ROGUE_FW_LOG_CREATESFID(145, ROGUE_FW_GROUP_MAIN, 0), + "GPU units deinit" }, + { ROGUE_FW_LOG_CREATESFID(146, ROGUE_FW_GROUP_MAIN, 2), + "Initialised OS %d with config flags 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(147, ROGUE_FW_GROUP_MAIN, 2), + "UFO limit exceeded %d/%d" }, + { ROGUE_FW_LOG_CREATESFID(148, ROGUE_FW_GROUP_MAIN, 0), + "3D Dummy stencil store" }, + { ROGUE_FW_LOG_CREATESFID(149, ROGUE_FW_GROUP_MAIN, 3), + "Initialised OS %d with config flags 0x%08x and extended config flags 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(150, ROGUE_FW_GROUP_MAIN, 1), + "Unknown Command (eCmdType=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(151, ROGUE_FW_GROUP_MAIN, 4), + "UFO forced update: FWCtx 0x%08.8x @ %d [0x%08.8x] = 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(152, ROGUE_FW_GROUP_MAIN, 5), + "UFO forced update NOP: FWCtx 0x%08.8x @ %d [0x%08.8x] = 0x%08.8x, reason %d" }, + { ROGUE_FW_LOG_CREATESFID(153, ROGUE_FW_GROUP_MAIN, 3), + "TDM context switch check: Roff %u points to 0x%08x, Match=%u" }, + { ROGUE_FW_LOG_CREATESFID(154, ROGUE_FW_GROUP_MAIN, 6), + "OSid %d CCB init status: %d (1-ok 0-fail): kCCBCtl@0x%x kCCB@0x%x fwCCBCtl@0x%x fwCCB@0x%x" }, + { ROGUE_FW_LOG_CREATESFID(155, ROGUE_FW_GROUP_MAIN, 2), + "FW IRQ # %u @ %u" }, + { ROGUE_FW_LOG_CREATESFID(156, ROGUE_FW_GROUP_MAIN, 3), + "Setting breakpoint: Addr 0x%08.8x DM%u usc_breakpoint_ctrl_dm = %u" }, + { ROGUE_FW_LOG_CREATESFID(157, ROGUE_FW_GROUP_MAIN, 3), + "Invalid KCCB setup for OSid %u: KCCB 0x%08x, KCCB Ctrl 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(158, ROGUE_FW_GROUP_MAIN, 3), + "Invalid KCCB cmd (%u) for OSid %u @ KCCB 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(159, ROGUE_FW_GROUP_MAIN, 4), + "FW FAULT: At line %d in file 0x%08x%08x, additional data=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(160, ROGUE_FW_GROUP_MAIN, 4), + "Invalid breakpoint: MemCtx 0x%08x Addr 0x%08.8x DM%u usc_breakpoint_ctrl_dm = %u" }, + { ROGUE_FW_LOG_CREATESFID(161, ROGUE_FW_GROUP_MAIN, 3), + "Discarding invalid SLC flushinval command for OSid %u: DM %u, FWCtx 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(162, ROGUE_FW_GROUP_MAIN, 4), + "Invalid Write Offset update notification from OSid %u to DM %u: FWCtx 0x%08x, MemCtx 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(163, ROGUE_FW_GROUP_MAIN, 4), + "Null FWCtx in KCCB kick cmd for OSid %u: KCCB 0x%08x, ROff %u, WOff %u" }, + { ROGUE_FW_LOG_CREATESFID(164, ROGUE_FW_GROUP_MAIN, 3), + "Checkpoint CCB for OSid %u is full, signalling host for full check state (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(165, ROGUE_FW_GROUP_MAIN, 8), + "OSid %d CCB init status: %d (1-ok 0-fail): kCCBCtl@0x%x kCCB@0x%x fwCCBCtl@0x%x fwCCB@0x%x chptCCBCtl@0x%x chptCCB@0x%x" }, + { ROGUE_FW_LOG_CREATESFID(166, ROGUE_FW_GROUP_MAIN, 4), + "OSid %d fw state transition request: from %d to %d (0-offline 1-ready 2-active 3-offloading). Status %d (1-ok 0-fail)" }, + { ROGUE_FW_LOG_CREATESFID(167, ROGUE_FW_GROUP_MAIN, 2), + "OSid %u has %u stale commands in its KCCB" }, + { ROGUE_FW_LOG_CREATESFID(168, ROGUE_FW_GROUP_MAIN, 0), + "Applying VCE pause" }, + { ROGUE_FW_LOG_CREATESFID(169, ROGUE_FW_GROUP_MAIN, 3), + "OSid %u KCCB slot %u value updated to %u" }, + { ROGUE_FW_LOG_CREATESFID(170, ROGUE_FW_GROUP_MAIN, 7), + "Unknown KCCB Command: KCCBCtl=0x%08x, KCCB=0x%08x, Roff=%u, Woff=%u, Wrap=%u, Cmd=0x%08x, CmdType=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(171, ROGUE_FW_GROUP_MAIN, 10), + "Unknown Client CCB Command processing fences: FWCtx=0x%08x, CCBCtl=0x%08x, CCB=0x%08x, Roff=%u, Doff=%u, Woff=%u, Wrap=%u, CmdHdr=0x%08x, CmdType=0x%08x, CmdSize=%u" }, + { ROGUE_FW_LOG_CREATESFID(172, ROGUE_FW_GROUP_MAIN, 10), + "Unknown Client CCB Command executing kick: FWCtx=0x%08x, CCBCtl=0x%08x, CCB=0x%08x, Roff=%u, Doff=%u, Woff=%u, Wrap=%u, CmdHdr=0x%08x, CmdType=0x%08x, CmdSize=%u" }, + { ROGUE_FW_LOG_CREATESFID(173, ROGUE_FW_GROUP_MAIN, 2), + "Null FWCtx in KCCB kick cmd for OSid %u with WOff %u" }, + { ROGUE_FW_LOG_CREATESFID(174, ROGUE_FW_GROUP_MAIN, 2), + "Discarding invalid SLC flushinval command for OSid %u, FWCtx 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(175, ROGUE_FW_GROUP_MAIN, 3), + "Invalid Write Offset update notification from OSid %u: FWCtx 0x%08x, MemCtx 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(176, ROGUE_FW_GROUP_MAIN, 2), + "Initialised Firmware with config flags 0x%08x and extended config flags 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(177, ROGUE_FW_GROUP_MAIN, 1), + "Set Periodic Hardware Reset Mode: %d" }, + { ROGUE_FW_LOG_CREATESFID(179, ROGUE_FW_GROUP_MAIN, 3), + "PHR mode %d, FW state: 0x%08x, HWR flags: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(180, ROGUE_FW_GROUP_MAIN, 1), + "PHR mode %d triggered a reset" }, + { ROGUE_FW_LOG_CREATESFID(181, ROGUE_FW_GROUP_MAIN, 2), + "Signal update, Snoop Filter: %u, Signal Id: %u" }, + { ROGUE_FW_LOG_CREATESFID(182, ROGUE_FW_GROUP_MAIN, 1), + "WARNING: Skipping FW KCCB Cmd type %d which is not yet supported on Series8." }, + { ROGUE_FW_LOG_CREATESFID(183, ROGUE_FW_GROUP_MAIN, 4), + "MMU context cache data NULL, but cache flags=0x%x (sync counter=%u, update value=%u) OSId=%u" }, + { ROGUE_FW_LOG_CREATESFID(184, ROGUE_FW_GROUP_MAIN, 5), + "SLC range based flush: Context=%u VAddr=0x%02x%08x, Size=0x%08x, Invalidate=%d" }, + { ROGUE_FW_LOG_CREATESFID(185, ROGUE_FW_GROUP_MAIN, 3), + "FBSC invalidate for Context Set [0x%08x]: Entry mask 0x%08x%08x." }, + { ROGUE_FW_LOG_CREATESFID(186, ROGUE_FW_GROUP_MAIN, 3), + "TDM context switch check: Roff %u was not valid for kick starting at %u, moving back to %u" }, + { ROGUE_FW_LOG_CREATESFID(187, ROGUE_FW_GROUP_MAIN, 2), + "Signal updates: FIFO: %u, Signals: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(188, ROGUE_FW_GROUP_MAIN, 2), + "Invalid FBSC cmd: FWCtx 0x%08x, MemCtx 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(189, ROGUE_FW_GROUP_MAIN, 0), + "Insert BRN68497 WA blit after TDM Context store." }, + { ROGUE_FW_LOG_CREATESFID(190, ROGUE_FW_GROUP_MAIN, 1), + "UFO Updates for previously finished FWCtx 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(191, ROGUE_FW_GROUP_MAIN, 1), + "RTC with RTA present, %u active render targets" }, + { ROGUE_FW_LOG_CREATESFID(192, ROGUE_FW_GROUP_MAIN, 0), + "Invalid RTA Set-up. The ValidRenderTargets array in RTACtl is Null!" }, + { ROGUE_FW_LOG_CREATESFID(193, ROGUE_FW_GROUP_MAIN, 2), + "Block 0x%x / Counter 0x%x INVALID and ignored" }, + { ROGUE_FW_LOG_CREATESFID(194, ROGUE_FW_GROUP_MAIN, 2), + "ECC fault GPU=0x%08x FW=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(195, ROGUE_FW_GROUP_MAIN, 1), + "Processing XPU event on DM = %d" }, + { ROGUE_FW_LOG_CREATESFID(196, ROGUE_FW_GROUP_MAIN, 2), + "OSid %u failed to respond to the virtualisation watchdog in time. Timestamp of its last input = %u" }, + { ROGUE_FW_LOG_CREATESFID(197, ROGUE_FW_GROUP_MAIN, 1), + "GPU-%u has locked up (see HWR logs for more info)" }, + { ROGUE_FW_LOG_CREATESFID(198, ROGUE_FW_GROUP_MAIN, 3), + "Updating Tiles In Flight (Dusts=%d, PartitionMask=0x%08x, ISPCtl=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(199, ROGUE_FW_GROUP_MAIN, 0), + "GPU has locked up (see HWR logs for more info)" }, + { ROGUE_FW_LOG_CREATESFID(200, ROGUE_FW_GROUP_MAIN, 1), + "Reprocessing outstanding XPU events from cores 0x%02x" }, + { ROGUE_FW_LOG_CREATESFID(201, ROGUE_FW_GROUP_MAIN, 3), + "Secondary XPU event on DM=%d, CoreMask=0x%02x, Raised=0x%02x" }, + { ROGUE_FW_LOG_CREATESFID(202, ROGUE_FW_GROUP_MAIN, 8), + "TDM Queue: Core %u, FWCtx 0x%08.8x, prio: %d, queue: 0x%08x%08x (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(203, ROGUE_FW_GROUP_MAIN, 3), + "TDM stalled Core %u (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(204, ROGUE_FW_GROUP_MAIN, 8), + "Compute Queue: Core %u, FWCtx 0x%08.8x, prio: %d, queue: 0x%08x%08x (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(205, ROGUE_FW_GROUP_MAIN, 4), + "Compute stalled core %u (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(206, ROGUE_FW_GROUP_MAIN, 6), + "User Mode Queue mismatched stream start: Core %u, FWCtx 0x%08.8x, queue: 0x%08x%08x (Roff = %u, StreamStartOffset = %u)" }, + { ROGUE_FW_LOG_CREATESFID(207, ROGUE_FW_GROUP_MAIN, 3), + "TDM resumed core %u (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(208, ROGUE_FW_GROUP_MAIN, 4), + "Compute resumed core %u (Roff = %u, Woff = %u, Size = %u)" }, + { ROGUE_FW_LOG_CREATESFID(209, ROGUE_FW_GROUP_MAIN, 2), + " Updated permission for OSid %u to perform MTS kicks: %u (1 = allowed, 0 = not allowed)" }, + { ROGUE_FW_LOG_CREATESFID(210, ROGUE_FW_GROUP_MAIN, 2), + "Mask = 0x%X, mask2 = 0x%X" }, + { ROGUE_FW_LOG_CREATESFID(211, ROGUE_FW_GROUP_MAIN, 3), + " core %u, reg = %u, mask = 0x%X)" }, + { ROGUE_FW_LOG_CREATESFID(212, ROGUE_FW_GROUP_MAIN, 1), + "ECC fault received from safety bus: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(213, ROGUE_FW_GROUP_MAIN, 1), + "Safety Watchdog threshold period set to 0x%x clock cycles" }, + { ROGUE_FW_LOG_CREATESFID(214, ROGUE_FW_GROUP_MAIN, 0), + "MTS Safety Event trigged by the safety watchdog." }, + { ROGUE_FW_LOG_CREATESFID(215, ROGUE_FW_GROUP_MAIN, 3), + "DM%d USC tasks range limit 0 - %d, stride %d" }, + { ROGUE_FW_LOG_CREATESFID(216, ROGUE_FW_GROUP_MAIN, 1), + "ECC fault GPU=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(217, ROGUE_FW_GROUP_MAIN, 0), + "GPU Hardware units reset to prevent transient faults." }, + { ROGUE_FW_LOG_CREATESFID(218, ROGUE_FW_GROUP_MAIN, 2), + "Kick Abort cmd: FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(219, ROGUE_FW_GROUP_MAIN, 7), + "Kick Ray: FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(220, ROGUE_FW_GROUP_MAIN, 0), + "Ray finished" }, + { ROGUE_FW_LOG_CREATESFID(221, ROGUE_FW_GROUP_MAIN, 2), + "State of firmware's private data at boot time: %d (0 = uninitialised, 1 = initialised); Fw State Flags = 0x%08X" }, + { ROGUE_FW_LOG_CREATESFID(222, ROGUE_FW_GROUP_MAIN, 2), + "CFI Timeout detected (%d increasing to %d)" }, + { ROGUE_FW_LOG_CREATESFID(223, ROGUE_FW_GROUP_MAIN, 2), + "CFI Timeout detected for FBM (%d increasing to %d)" }, + { ROGUE_FW_LOG_CREATESFID(224, ROGUE_FW_GROUP_MAIN, 0), + "Geom OOM event not allowed" }, + { ROGUE_FW_LOG_CREATESFID(225, ROGUE_FW_GROUP_MAIN, 4), + "Changing OSid %d's priority from %u to %u; Isolation = %u (0 = off; 1 = on)" }, + { ROGUE_FW_LOG_CREATESFID(226, ROGUE_FW_GROUP_MAIN, 2), + "Skipping already executed TA FWCtx 0x%08.8x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(227, ROGUE_FW_GROUP_MAIN, 2), + "Attempt to execute TA FWCtx 0x%08.8x @ %d ahead of time on other GEOM" }, + { ROGUE_FW_LOG_CREATESFID(228, ROGUE_FW_GROUP_MAIN, 8), + "Kick TDM: Kick ID %u FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(229, ROGUE_FW_GROUP_MAIN, 12), + "Kick TA: Kick ID %u FWCtx 0x%08.8x @ %d, RTD 0x%08x, First kick:%d, Last kick:%d, CSW resume:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(230, ROGUE_FW_GROUP_MAIN, 11), + "Kick 3D: Kick ID %u FWCtx 0x%08.8x @ %d, RTD 0x%08x, Partial render:%d, CSW resume:%d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(231, ROGUE_FW_GROUP_MAIN, 7), + "Kick Compute: Kick ID %u FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(232, ROGUE_FW_GROUP_MAIN, 1), + "TDM finished: Kick ID %u " }, + { ROGUE_FW_LOG_CREATESFID(233, ROGUE_FW_GROUP_MAIN, 1), + "TA finished: Kick ID %u " }, + { ROGUE_FW_LOG_CREATESFID(234, ROGUE_FW_GROUP_MAIN, 3), + "3D finished: Kick ID %u , HWRTData0State=%x, HWRTData1State=%x" }, + { ROGUE_FW_LOG_CREATESFID(235, ROGUE_FW_GROUP_MAIN, 1), + "Compute finished: Kick ID %u " }, + { ROGUE_FW_LOG_CREATESFID(236, ROGUE_FW_GROUP_MAIN, 10), + "Kick TDM: Kick ID %u FWCtx 0x%08.8x @ %d, Base 0x%08x%08x. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(237, ROGUE_FW_GROUP_MAIN, 8), + "Kick Ray: Kick ID %u FWCtx 0x%08.8x @ %d. (PID:%d, prio:%d, frame:%d, ext:0x%08x, int:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(238, ROGUE_FW_GROUP_MAIN, 1), + "Ray finished: Kick ID %u " }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_MTS, 2), + "Bg Task DM = %u, counted = %d" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_MTS, 1), + "Bg Task complete DM = %u" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_MTS, 3), + "Irq Task DM = %u, Breq = %d, SBIrq = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_MTS, 1), + "Irq Task complete DM = %u" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_MTS, 0), + "Kick MTS Bg task DM=All" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_MTS, 1), + "Kick MTS Irq task DM=%d" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_MTS, 2), + "Ready queue debug DM = %u, celltype = %d" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_MTS, 2), + "Ready-to-run debug DM = %u, item = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_MTS, 3), + "Client command header DM = %u, client CCB = 0x%x, cmd = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_MTS, 3), + "Ready-to-run debug OSid = %u, DM = %u, item = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_MTS, 3), + "Ready queue debug DM = %u, celltype = %d, OSid = %u" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_MTS, 3), + "Bg Task DM = %u, counted = %d, OSid = %u" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_MTS, 1), + "Bg Task complete DM Bitfield: %u" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_MTS, 0), + "Irq Task complete." }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_MTS, 7), + "Discarded Command Type: %d OS ID = %d PID = %d context = 0x%08x cccb ROff = 0x%x, due to USC breakpoint hit by OS ID = %d PID = %d." }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_MTS, 4), + "KCCB Slot %u: DM=%u, Cmd=0x%08x, OSid=%u" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_MTS, 2), + "KCCB Slot %u: Return value %u" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_MTS, 1), + "Bg Task OSid = %u" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_MTS, 3), + "KCCB Slot %u: Cmd=0x%08x, OSid=%u" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_MTS, 1), + "Irq Task (EVENT_STATUS=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_MTS, 2), + "VZ sideband test, kicked with OSid=%u from MTS, OSid for test=%u" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_CLEANUP, 1), + "FwCommonContext [0x%08x] cleaned" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_CLEANUP, 3), + "FwCommonContext [0x%08x] is busy: ReadOffset = %d, WriteOffset = %d" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_CLEANUP, 2), + "HWRTData [0x%08x] for DM=%d, received cleanup request" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_CLEANUP, 3), + "HWRTData [0x%08x] HW Context cleaned for DM%u, executed commands = %d" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_CLEANUP, 2), + "HWRTData [0x%08x] HW Context for DM%u is busy" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_CLEANUP, 2), + "HWRTData [0x%08x] HW Context %u cleaned" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_CLEANUP, 1), + "Freelist [0x%08x] cleaned" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_CLEANUP, 1), + "ZSBuffer [0x%08x] cleaned" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_CLEANUP, 3), + "ZSBuffer [0x%08x] is busy: submitted = %d, executed = %d" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_CLEANUP, 4), + "HWRTData [0x%08x] HW Context for DM%u is busy: submitted = %d, executed = %d" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_CLEANUP, 2), + "HW Ray Frame data [0x%08x] for DM=%d, received cleanup request" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_CLEANUP, 3), + "HW Ray Frame Data [0x%08x] cleaned for DM%u, executed commands = %d" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_CLEANUP, 4), + "HW Ray Frame Data [0x%08x] for DM%u is busy: submitted = %d, executed = %d" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_CLEANUP, 2), + "HW Ray Frame Data [0x%08x] HW Context %u cleaned" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_CLEANUP, 1), + "Discarding invalid cleanup request of type 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_CLEANUP, 1), + "Received cleanup request for HWRTData [0x%08x]" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_CLEANUP, 3), + "HWRTData [0x%08x] HW Context is busy: submitted = %d, executed = %d" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_CLEANUP, 3), + "HWRTData [0x%08x] HW Context %u cleaned, executed commands = %d" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_CSW, 1), + "CDM FWCtx 0x%08.8x needs resume" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_CSW, 3), + "*** CDM FWCtx 0x%08.8x resume from snapshot buffer 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_CSW, 1), + "CDM FWCtx shared alloc size load 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_CSW, 0), + "*** CDM FWCtx store complete" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_CSW, 0), + "*** CDM FWCtx store start" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_CSW, 0), + "CDM Soft Reset" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_CSW, 1), + "3D FWCtx 0x%08.8x needs resume" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_CSW, 1), + "*** 3D FWCtx 0x%08.8x resume" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_CSW, 0), + "*** 3D context store complete" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_CSW, 3), + "3D context store pipe state: 0x%08.8x 0x%08.8x 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_CSW, 0), + "*** 3D context store start" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_CSW, 1), + "*** 3D TQ FWCtx 0x%08.8x resume" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_CSW, 1), + "TA FWCtx 0x%08.8x needs resume" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_CSW, 3), + "*** TA FWCtx 0x%08.8x resume from snapshot buffer 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_CSW, 2), + "TA context shared alloc size store 0x%x, load 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_CSW, 0), + "*** TA context store complete" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_CSW, 0), + "*** TA context store start" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_CSW, 3), + "Higher priority context scheduled for DM %u, old prio:%d, new prio:%d" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_CSW, 2), + "Set FWCtx 0x%x priority to %u" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_CSW, 2), + "3D context store pipe%d state: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_CSW, 2), + "3D context resume pipe%d state: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_CSW, 1), + "SHG FWCtx 0x%08.8x needs resume" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_CSW, 3), + "*** SHG FWCtx 0x%08.8x resume from snapshot buffer 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_CSW, 2), + "SHG context shared alloc size store 0x%x, load 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_CSW, 0), + "*** SHG context store complete" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_CSW, 0), + "*** SHG context store start" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_CSW, 1), + "Performing TA indirection, last used pipe %d" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_CSW, 0), + "CDM context store hit ctrl stream terminate. Skip resume." }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_CSW, 4), + "*** CDM FWCtx 0x%08.8x resume from snapshot buffer 0x%08x%08x, shader state %u" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_CSW, 2), + "TA PDS/USC state buffer flip (%d->%d)" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_CSW, 0), + "TA context store hit BRN 52563: vertex store tasks outstanding" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_CSW, 1), + "TA USC poll failed (USC vertex task count: %d)" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_CSW, 0), + "TA context store deferred due to BRN 54141." }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_CSW, 7), + "Higher priority context scheduled for DM %u. Prios (OSid, OSid Prio, Context Prio): Current: %u, %u, %u New: %u, %u, %u" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_CSW, 0), + "*** TDM context store start" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_CSW, 0), + "*** TDM context store complete" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_CSW, 2), + "TDM context needs resume, header [0x%08.8x, 0x%08.8x]" }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_CSW, 8), + "Higher priority context scheduled for DM %u. Prios (OSid, OSid Prio, Context Prio): Current: %u, %u, %u New: %u, %u, %u. Hard Context Switching: %u" }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_CSW, 3), + "3D context store pipe %2d (%2d) state: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_CSW, 3), + "3D context resume pipe %2d (%2d) state: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_CSW, 1), + "*** 3D context store start version %d (1=IPP_TILE, 2=ISP_TILE)" }, + { ROGUE_FW_LOG_CREATESFID(42, ROGUE_FW_GROUP_CSW, 3), + "3D context store pipe%d state: 0x%08.8x%08x" }, + { ROGUE_FW_LOG_CREATESFID(43, ROGUE_FW_GROUP_CSW, 3), + "3D context resume pipe%d state: 0x%08.8x%08x" }, + { ROGUE_FW_LOG_CREATESFID(44, ROGUE_FW_GROUP_CSW, 2), + "3D context resume IPP state: 0x%08.8x%08x" }, + { ROGUE_FW_LOG_CREATESFID(45, ROGUE_FW_GROUP_CSW, 1), + "All 3D pipes empty after ISP tile mode store! IPP_status: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(46, ROGUE_FW_GROUP_CSW, 3), + "TDM context resume pipe%d state: 0x%08.8x%08x" }, + { ROGUE_FW_LOG_CREATESFID(47, ROGUE_FW_GROUP_CSW, 0), + "*** 3D context store start version 4" }, + { ROGUE_FW_LOG_CREATESFID(48, ROGUE_FW_GROUP_CSW, 2), + "Multicore context resume on DM%d active core mask 0x%04.4x" }, + { ROGUE_FW_LOG_CREATESFID(49, ROGUE_FW_GROUP_CSW, 2), + "Multicore context store on DM%d active core mask 0x%04.4x" }, + { ROGUE_FW_LOG_CREATESFID(50, ROGUE_FW_GROUP_CSW, 5), + "TDM context resume Core %d, pipe%d state: 0x%08.8x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(51, ROGUE_FW_GROUP_CSW, 0), + "*** RDM FWCtx store complete" }, + { ROGUE_FW_LOG_CREATESFID(52, ROGUE_FW_GROUP_CSW, 0), + "*** RDM FWCtx store start" }, + { ROGUE_FW_LOG_CREATESFID(53, ROGUE_FW_GROUP_CSW, 1), + "RDM FWCtx 0x%08.8x needs resume" }, + { ROGUE_FW_LOG_CREATESFID(54, ROGUE_FW_GROUP_CSW, 1), + "RDM FWCtx 0x%08.8x resume" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_BIF, 3), + "Activate MemCtx=0x%08x BIFreq=%d secure=%d" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_BIF, 1), + "Deactivate MemCtx=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_BIF, 1), + "Alloc PC reg %d" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_BIF, 2), + "Grab reg set %d refcount now %d" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_BIF, 2), + "Ungrab reg set %d refcount now %d" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_BIF, 6), + "Setup reg=%d BIFreq=%d, expect=0x%08x%08x, actual=0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_BIF, 2), + "Trust enabled:%d, for BIFreq=%d" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_BIF, 9), + "BIF Tiling Cfg %d base 0x%08x%08x len 0x%08x%08x enable %d stride %d --> 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_BIF, 4), + "Wrote the Value %d to OSID0, Cat Base %d, Register's contents are now 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_BIF, 3), + "Wrote the Value %d to OSID1, Context %d, Register's contents are now 0x%04x" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_BIF, 7), + "ui32OSid = %u, Catbase = %u, Reg Address = 0x%x, Reg index = %u, Bitshift index = %u, Val = 0x%08x%08x" }, \ + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_BIF, 5), + "Map GPU memory DevVAddr 0x%x%08x, Size %u, Context ID %u, BIFREQ %u" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_BIF, 1), + "Unmap GPU memory (event status 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_BIF, 3), + "Activate MemCtx=0x%08x DM=%d secure=%d" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_BIF, 6), + "Setup reg=%d DM=%d, expect=0x%08x%08x, actual=0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_BIF, 4), + "Map GPU memory DevVAddr 0x%x%08x, Size %u, Context ID %u" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_BIF, 2), + "Trust enabled:%d, for DM=%d" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_BIF, 5), + "Map GPU memory DevVAddr 0x%x%08x, Size %u, Context ID %u, DM %u" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_BIF, 6), + "Setup register set=%d DM=%d, PC address=0x%08x%08x, OSid=%u, NewPCRegRequired=%d" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_BIF, 3), + "Alloc PC set %d as register range [%u - %u]" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_MISC, 1), + "GPIO write 0x%02x" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_MISC, 1), + "GPIO read 0x%02x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_MISC, 0), + "GPIO enabled" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_MISC, 0), + "GPIO disabled" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_MISC, 1), + "GPIO status=%d (0=OK, 1=Disabled)" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_MISC, 2), + "GPIO_AP: Read address=0x%02x (%d byte(s))" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_MISC, 2), + "GPIO_AP: Write address=0x%02x (%d byte(s))" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_MISC, 0), + "GPIO_AP timeout!" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_MISC, 1), + "GPIO_AP error. GPIO status=%d (0=OK, 1=Disabled)" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_MISC, 1), + "GPIO already read 0x%02x" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_MISC, 2), + "SR: Check buffer %d available returned %d" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_MISC, 1), + "SR: Waiting for buffer %d" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_MISC, 2), + "SR: Timeout waiting for buffer %d (after %d ticks)" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_MISC, 2), + "SR: Skip frame check for strip %d returned %d (0=No skip, 1=Skip frame)" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_MISC, 1), + "SR: Skip remaining strip %d in frame" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_MISC, 1), + "SR: Inform HW that strip %d is a new frame" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_MISC, 1), + "SR: Timeout waiting for INTERRUPT_FRAME_SKIP (after %d ticks)" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_MISC, 1), + "SR: Strip mode is %d" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_MISC, 1), + "SR: Strip Render start (strip %d)" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_MISC, 1), + "SR: Strip Render complete (buffer %d)" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_MISC, 1), + "SR: Strip Render fault (buffer %d)" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_MISC, 1), + "TRP state: %d" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_MISC, 1), + "TRP failure: %d" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_MISC, 1), + "SW TRP State: %d" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_MISC, 1), + "SW TRP failure: %d" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_MISC, 1), + "HW kick event (%u)" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_MISC, 4), + "GPU core (%u/%u): checksum 0x%08x vs. 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_MISC, 6), + "GPU core (%u/%u), unit (%u,%u): checksum 0x%08x vs. 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_MISC, 6), + "HWR: Core%u, Register=0x%08x, OldValue=0x%08x%08x, CurrValue=0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_MISC, 4), + "HWR: USC Core%u, ui32TotalSlotsUsedByDM=0x%08x, psDMHWCtl->ui32USCSlotsUsedByDM=0x%08x, bHWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_MISC, 6), + "HWR: USC Core%u, Register=0x%08x, OldValue=0x%08x%08x, CurrValue=0x%08x%08x" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_PM, 10), + "ALIST%d SP = %u, MLIST%d SP = %u (VCE 0x%08x%08x, TE 0x%08x%08x, ALIST 0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_PM, 8), + "Is TA: %d, finished: %d on HW %u (HWRTData = 0x%08x, MemCtx = 0x%08x). FL different between TA/3D: global:%d, local:%d, mmu:%d" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_PM, 14), + "UFL-3D-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-3D-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), MFL-3D-Base: 0x%08x%08x (SP = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_PM, 14), + "UFL-TA-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-TA-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), MFL-TA-Base: 0x%08x%08x (SP = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_PM, 5), + "Freelist grow completed [0x%08x]: added pages 0x%08x, total pages 0x%08x, new DevVirtAddr 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_PM, 1), + "Grow for freelist ID=0x%08x denied by host" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_PM, 5), + "Freelist update completed [0x%08x]: old total pages 0x%08x, new total pages 0x%08x, new DevVirtAddr 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_PM, 1), + "Reconstruction of freelist ID=0x%08x failed" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_PM, 2), + "Ignored attempt to pause or unpause the DM while there is no relevant operation in progress (0-TA,1-3D): %d, operation(0-unpause, 1-pause): %d" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_PM, 2), + "Force free 3D Context memory, FWCtx: 0x%08x, status(1:success, 0:fail): %d" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_PM, 1), + "PM pause TA ALLOC: PM_PAGE_MANAGEOP set to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_PM, 1), + "PM unpause TA ALLOC: PM_PAGE_MANAGEOP set to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_PM, 1), + "PM pause 3D DALLOC: PM_PAGE_MANAGEOP set to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_PM, 1), + "PM unpause 3D DALLOC: PM_PAGE_MANAGEOP set to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_PM, 1), + "PM ALLOC/DALLOC change was not actioned: PM_PAGE_MANAGEOP_STATUS=0x%x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_PM, 7), + "Is TA: %d, finished: %d on HW %u (HWRTData = 0x%08x, MemCtx = 0x%08x). FL different between TA/3D: global:%d, local:%d" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_PM, 10), + "UFL-3D-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-3D-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_PM, 10), + "UFL-TA-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-TA-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_PM, 7), + "Freelist update completed [0x%08x / FL State 0x%08x%08x]: old total pages 0x%08x, new total pages 0x%08x, new DevVirtAddr 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_PM, 7), + "Freelist update failed [0x%08x / FL State 0x%08x%08x]: old total pages 0x%08x, new total pages 0x%08x, new DevVirtAddr 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_PM, 10), + "UFL-3D-State-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-3D-State-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_PM, 10), + "UFL-TA-State-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u), FL-TA-State-Base: 0x%08x%08x (SP = %u, 4PB = %u, 4PT = %u)" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_PM, 5), + "Freelist 0x%08x base address from HW: 0x%02x%08x (expected value: 0x%02x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_PM, 5), + "Analysis of FL grow: Pause=(%u,%u) Paused+Valid(%u,%u) PMStateBuffer=0x%x" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_PM, 5), + "Attempt FL grow for FL: 0x%08x, new dev address: 0x%02x%08x, new page count: %u, new ready count: %u" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_PM, 5), + "Deferring FL grow for non-loaded FL: 0x%08x, new dev address: 0x%02x%08x, new page count: %u, new ready count: %u" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_PM, 4), + "Is GEOM: %d, finished: %d (HWRTData = 0x%08x, MemCtx = 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_PM, 1), + "3D Timeout Now for FWCtx 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_PM, 1), + "GEOM PM Recycle for FWCtx 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_PM, 1), + "PM running primary config (Core %d)" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_PM, 1), + "PM running secondary config (Core %d)" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_PM, 1), + "PM running tertiary config (Core %d)" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_PM, 1), + "PM running quaternary config (Core %d)" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_RPM, 3), + "Global link list dynamic page count: vertex 0x%x, varying 0x%x, node 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_RPM, 3), + "Global link list static page count: vertex 0x%x, varying 0x%x, node 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_RPM, 0), + "RPM request failed. Waiting for freelist grow." }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_RPM, 0), + "RPM request failed. Aborting the current frame." }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_RPM, 1), + "RPM waiting for pending grow on freelist 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_RPM, 3), + "Request freelist grow [0x%08x] current pages %d, grow size %d" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_RPM, 2), + "Freelist load: SHF = 0x%08x, SHG = 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_RPM, 2), + "SHF FPL register: 0x%08x.0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_RPM, 2), + "SHG FPL register: 0x%08x.0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_RPM, 5), + "Kernel requested RPM grow on freelist (type %d) at 0x%08x from current size %d to new size %d, RPM restart: %d (1=Yes)" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_RPM, 0), + "Restarting SHG" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_RPM, 0), + "Grow failed, aborting the current frame." }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_RPM, 1), + "RPM abort complete on HWFrameData [0x%08x]." }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_RPM, 1), + "RPM freelist cleanup [0x%08x] requires abort to proceed." }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_RPM, 2), + "RPM page table base register: 0x%08x.0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_RPM, 0), + "Issuing RPM abort." }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_RPM, 0), + "RPM OOM received but toggle bits indicate free pages available" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_RPM, 0), + "RPM hardware timeout. Unable to process OOM event." }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_RPM, 5), + "SHF FL (0x%08x) load, FPL: 0x%08x.0x%08x, roff: 0x%08x, woff: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_RPM, 5), + "SHG FL (0x%08x) load, FPL: 0x%08x.0x%08x, roff: 0x%08x, woff: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_RPM, 3), + "SHF FL (0x%08x) store, roff: 0x%08x, woff: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_RPM, 3), + "SHG FL (0x%08x) store, roff: 0x%08x, woff: 0x%08x" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_RTD, 2), + "3D RTData 0x%08x finished on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_RTD, 2), + "3D RTData 0x%08x ready on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_RTD, 4), + "CONTEXT_PB_BASE set to 0x%x, FL different between TA/3D: local: %d, global: %d, mmu: %d" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_RTD, 2), + "Loading VFP table 0x%08x%08x for 3D" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_RTD, 2), + "Loading VFP table 0x%08x%08x for TA" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_RTD, 10), + "Load Freelist 0x%x type: %d (0:local,1:global,2:mmu) for DM%d: TotalPMPages = %d, FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_RTD, 0), + "Perform VHEAP table store" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_RTD, 2), + "RTData 0x%08x: found match in Context=%d: Load=No, Store=No" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_RTD, 2), + "RTData 0x%08x: found NULL in Context=%d: Load=Yes, Store=No" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_RTD, 3), + "RTData 0x%08x: found state 3D finished (0x%08x) in Context=%d: Load=Yes, Store=Yes" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_RTD, 3), + "RTData 0x%08x: found state TA finished (0x%08x) in Context=%d: Load=Yes, Store=Yes" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_RTD, 5), + "Loading stack-pointers for %d (0:MidTA,1:3D) on context %d, MLIST = 0x%08x, ALIST = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_RTD, 10), + "Store Freelist 0x%x type: %d (0:local,1:global,2:mmu) for DM%d: TotalPMPages = %d, FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_RTD, 2), + "TA RTData 0x%08x finished on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_RTD, 2), + "TA RTData 0x%08x loaded on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_RTD, 12), + "Store Freelist 0x%x type: %d (0:local,1:global,2:mmu) for DM%d: FL Total Pages %u (max=%u,grow size=%u), FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_RTD, 12), + "Load Freelist 0x%x type: %d (0:local,1:global,2:mmu) for DM%d: FL Total Pages %u (max=%u,grow size=%u), FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_RTD, 1), + "Freelist 0x%x RESET!!!!!!!!" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_RTD, 5), + "Freelist 0x%x stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_RTD, 3), + "Request reconstruction of Freelist 0x%x type: %d (0:local,1:global,2:mmu) on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_RTD, 1), + "Freelist reconstruction ACK from host (HWR state :%u)" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_RTD, 0), + "Freelist reconstruction completed" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_RTD, 3), + "TA RTData 0x%08x loaded on HW context %u HWRTDataNeedsLoading=%d" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_RTD, 3), + "TE Region headers base 0x%08x%08x (RGNHDR Init: %d)" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_RTD, 8), + "TA Buffers: FWCtx 0x%08x, RT 0x%08x, RTData 0x%08x, VHeap 0x%08x%08x, TPC 0x%08x%08x (MemCtx 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_RTD, 2), + "3D RTData 0x%08x loaded on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_RTD, 4), + "3D Buffers: FWCtx 0x%08x, RT 0x%08x, RTData 0x%08x (MemCtx 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_RTD, 2), + "Restarting TA after partial render, HWRTData0State=0x%x, HWRTData1State=0x%x" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_RTD, 3), + "CONTEXT_PB_BASE set to 0x%x, FL different between TA/3D: local: %d, global: %d" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_RTD, 12), + "Store Freelist 0x%x type: %d (0:local,1:global) for PMDM%d: FL Total Pages %u (max=%u,grow size=%u), FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_RTD, 12), + "Load Freelist 0x%x type: %d (0:local,1:global) for PMDM%d: FL Total Pages %u (max=%u,grow size=%u), FL-addr = 0x%08x%08x, stacktop = 0x%08x%08x, Alloc Page Count = %u, Alloc MMU Page Count = %u" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_RTD, 5), + "3D Buffers: FWCtx 0x%08x, parent RT 0x%08x, RTData 0x%08x on ctx %d, (MemCtx 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_RTD, 7), + "TA Buffers: FWCtx 0x%08x, RTData 0x%08x, VHeap 0x%08x%08x, TPC 0x%08x%08x (MemCtx 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_RTD, 4), + "3D Buffers: FWCtx 0x%08x, RTData 0x%08x on ctx %d, (MemCtx 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_RTD, 6), + "Load Freelist 0x%x type: %d (0:local,1:global) for PMDM%d: FL Total Pages %u (max=%u,grow size=%u)" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_RTD, 1), + "TA RTData 0x%08x marked as killed." }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_RTD, 1), + "3D RTData 0x%08x marked as killed." }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_RTD, 1), + "RTData 0x%08x will be killed after TA restart." }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_RTD, 3), + "RTData 0x%08x Render State Buffer 0x%02x%08x will be reset." }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_RTD, 3), + "GEOM RTData 0x%08x using Render State Buffer 0x%02x%08x." }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_RTD, 3), + "FRAG RTData 0x%08x using Render State Buffer 0x%02x%08x." }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_SPM, 0), + "Force Z-Load for partial render" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_SPM, 0), + "Force Z-Store for partial render" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_SPM, 1), + "3D MemFree: Local FL 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_SPM, 1), + "3D MemFree: MMU FL 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_SPM, 1), + "3D MemFree: Global FL 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_SPM, 6), + "OOM TA/3D PR Check: [0x%08.8x] is 0x%08.8x requires 0x%08.8x, HardwareSync Fence [0x%08.8x] is 0x%08.8x requires 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_SPM, 3), + "OOM TA_cmd=0x%08x, U-FL 0x%08x, N-FL 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_SPM, 5), + "OOM TA_cmd=0x%08x, OOM MMU:%d, U-FL 0x%08x, N-FL 0x%08x, MMU-FL 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_SPM, 0), + "Partial render avoided" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_SPM, 0), + "Partial render discarded" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_SPM, 0), + "Partial Render finished" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_SPM, 0), + "SPM Owner = 3D-BG" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_SPM, 0), + "SPM Owner = 3D-IRQ" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_SPM, 0), + "SPM Owner = NONE" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_SPM, 0), + "SPM Owner = TA-BG" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_SPM, 0), + "SPM Owner = TA-IRQ" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_SPM, 2), + "ZStore address 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_SPM, 2), + "SStore address 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_SPM, 2), + "ZLoad address 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_SPM, 2), + "SLoad address 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_SPM, 0), + "No deferred ZS Buffer provided" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_SPM, 1), + "ZS Buffer successfully populated (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_SPM, 1), + "No need to populate ZS Buffer (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_SPM, 1), + "ZS Buffer successfully unpopulated (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_SPM, 1), + "No need to unpopulate ZS Buffer (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_SPM, 1), + "Send ZS-Buffer backing request to host (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_SPM, 1), + "Send ZS-Buffer unbacking request to host (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_SPM, 1), + "Don't send ZS-Buffer backing request. Previous request still pending (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_SPM, 1), + "Don't send ZS-Buffer unbacking request. Previous request still pending (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_SPM, 1), + "Partial Render waiting for ZBuffer to be backed (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_SPM, 1), + "Partial Render waiting for SBuffer to be backed (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_SPM, 0), + "SPM State = none" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_SPM, 0), + "SPM State = PR blocked" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_SPM, 0), + "SPM State = wait for grow" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_SPM, 0), + "SPM State = wait for HW" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_SPM, 0), + "SPM State = PR running" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_SPM, 0), + "SPM State = PR avoided" }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_SPM, 0), + "SPM State = PR executed" }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_SPM, 2), + "3DMemFree matches freelist 0x%08x (FL type = %u)" }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_SPM, 0), + "Raise the 3DMemFreeDedected flag" }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_SPM, 1), + "Wait for pending grow on Freelist 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(42, ROGUE_FW_GROUP_SPM, 1), + "ZS Buffer failed to be populated (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(43, ROGUE_FW_GROUP_SPM, 5), + "Grow update inconsistency: FL addr: 0x%02x%08x, curr pages: %u, ready: %u, new: %u" }, + { ROGUE_FW_LOG_CREATESFID(44, ROGUE_FW_GROUP_SPM, 4), + "OOM: Resumed TA with ready pages, FL addr: 0x%02x%08x, current pages: %u, SP : %u" }, + { ROGUE_FW_LOG_CREATESFID(45, ROGUE_FW_GROUP_SPM, 5), + "Received grow update, FL addr: 0x%02x%08x, current pages: %u, ready pages: %u, threshold: %u" }, + { ROGUE_FW_LOG_CREATESFID(46, ROGUE_FW_GROUP_SPM, 1), + "No deferred partial render FW (Type=%d) Buffer provided" }, + { ROGUE_FW_LOG_CREATESFID(47, ROGUE_FW_GROUP_SPM, 1), + "No need to populate PR Buffer (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(48, ROGUE_FW_GROUP_SPM, 1), + "No need to unpopulate PR Buffer (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(49, ROGUE_FW_GROUP_SPM, 1), + "Send PR Buffer backing request to host (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(50, ROGUE_FW_GROUP_SPM, 1), + "Send PR Buffer unbacking request to host (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(51, ROGUE_FW_GROUP_SPM, 1), + "Don't send PR Buffer backing request. Previous request still pending (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(52, ROGUE_FW_GROUP_SPM, 1), + "Don't send PR Buffer unbacking request. Previous request still pending (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(53, ROGUE_FW_GROUP_SPM, 2), + "Partial Render waiting for Buffer %d type to be backed (ID=0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(54, ROGUE_FW_GROUP_SPM, 4), + "Received grow update, FL addr: 0x%02x%08x, new pages: %u, ready pages: %u" }, + { ROGUE_FW_LOG_CREATESFID(66, ROGUE_FW_GROUP_SPM, 3), + "OOM TA/3D PR Check: [0x%08.8x] is 0x%08.8x requires 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(67, ROGUE_FW_GROUP_SPM, 3), + "OOM: Resumed TA with ready pages, FL addr: 0x%02x%08x, current pages: %u" }, + { ROGUE_FW_LOG_CREATESFID(68, ROGUE_FW_GROUP_SPM, 3), + "OOM TA/3D PR deadlock unblocked reordering DM%d runlist head from Context 0x%08x to 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(69, ROGUE_FW_GROUP_SPM, 0), + "SPM State = PR force free" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_POW, 4), + "Check Pow state DM%d int: 0x%x, ext: 0x%x, pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_POW, 3), + "GPU idle (might be powered down). Pow state int: 0x%x, ext: 0x%x, flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_POW, 3), + "OS requested pow off (forced = %d), DM%d, pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_POW, 4), + "Initiate powoff query. Inactive DMs: %d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_POW, 2), + "Any RD-DM pending? %d, Any RD-DM Active? %d" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_POW, 3), + "GPU ready to be powered down. Pow state int: 0x%x, ext: 0x%x, flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_POW, 2), + "HW Request On(1)/Off(0): %d, Units: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_POW, 2), + "Request to change num of dusts to %d (Power flags=%d)" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_POW, 2), + "Changing number of dusts from %d to %d" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_POW, 0), + "Sidekick init" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_POW, 1), + "Rascal+Dusts init (# dusts mask: 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_POW, 0), + "Initiate powoff query for RD-DMs." }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_POW, 0), + "Initiate powoff query for TLA-DM." }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_POW, 2), + "Any RD-DM pending? %d, Any RD-DM Active? %d" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_POW, 2), + "TLA-DM pending? %d, TLA-DM Active? %d" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_POW, 1), + "Request power up due to BRN37270. Pow stat int: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_POW, 3), + "Cancel power off request int: 0x%x, ext: 0x%x, pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_POW, 1), + "OS requested forced IDLE, pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_POW, 1), + "OS cancelled forced IDLE, pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_POW, 3), + "Idle timer start. Pow state int: 0x%x, ext: 0x%x, flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_POW, 3), + "Cancel idle timer. Pow state int: 0x%x, ext: 0x%x, flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_POW, 2), + "Active PM latency set to %dms. Core clock: %d Hz" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_POW, 2), + "Compute cluster mask change to 0x%x, %d dusts powered." }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_POW, 0), + "Null command executed, repeating initiate powoff query for RD-DMs." }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_POW, 1), + "Power monitor: Estimate of dynamic energy %u" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_POW, 3), + "Check Pow state: Int: 0x%x, Ext: 0x%x, Pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: New deadline, time = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: New workload, cycles = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Proactive frequency calculated = %u" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Reactive utilisation = %u percent" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: Reactive frequency calculated = %u.%u" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: OPP Point Sent = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: Deadline removed = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: Workload removed = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Throttle to a maximum = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_POW, 0), + "Proactive DVFS: Failed to pass OPP point via GPIO." }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_POW, 0), + "Proactive DVFS: Invalid node passed to function." }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Guest OS attempted to do a privileged action. OSid = %u" }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Unprofiled work started. Total unprofiled work present: %u" }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Unprofiled work finished. Total unprofiled work present: %u" }, + { ROGUE_FW_LOG_CREATESFID(42, ROGUE_FW_GROUP_POW, 0), + "Proactive DVFS: Disabled: Not enabled by host." }, + { ROGUE_FW_LOG_CREATESFID(43, ROGUE_FW_GROUP_POW, 2), + "HW Request Completed(1)/Aborted(0): %d, Ticks: %d" }, + { ROGUE_FW_LOG_CREATESFID(44, ROGUE_FW_GROUP_POW, 1), + "Allowed number of dusts is %d due to BRN59042." }, + { ROGUE_FW_LOG_CREATESFID(45, ROGUE_FW_GROUP_POW, 3), + "Host timed out while waiting for a forced idle state. Pow state int: 0x%x, ext: 0x%x, flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(46, ROGUE_FW_GROUP_POW, 5), + "Check Pow state: Int: 0x%x, Ext: 0x%x, Pow flags: 0x%x, Fence Counters: Check: %u - Update: %u" }, + { ROGUE_FW_LOG_CREATESFID(47, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: OPP Point Sent = 0x%x, Success = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(48, ROGUE_FW_GROUP_POW, 0), + "Proactive DVFS: GPU transitioned to idle" }, + { ROGUE_FW_LOG_CREATESFID(49, ROGUE_FW_GROUP_POW, 0), + "Proactive DVFS: GPU transitioned to active" }, + { ROGUE_FW_LOG_CREATESFID(50, ROGUE_FW_GROUP_POW, 1), + "Power counter dumping: Data truncated writing register %u. Buffer too small." }, + { ROGUE_FW_LOG_CREATESFID(51, ROGUE_FW_GROUP_POW, 0), + "Power controller returned ABORT for last request so retrying." }, + { ROGUE_FW_LOG_CREATESFID(52, ROGUE_FW_GROUP_POW, 2), + "Discarding invalid power request: type 0x%x, DM %u" }, + { ROGUE_FW_LOG_CREATESFID(53, ROGUE_FW_GROUP_POW, 2), + "Detected attempt to cancel forced idle while not forced idle (pow state 0x%x, pow flags 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(54, ROGUE_FW_GROUP_POW, 2), + "Detected attempt to force power off while not forced idle (pow state 0x%x, pow flags 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(55, ROGUE_FW_GROUP_POW, 1), + "Detected attempt to change dust count while not forced idle (pow state 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(56, ROGUE_FW_GROUP_POW, 3), + "Power monitor: Type = %d (0 = power, 1 = energy), Estimate result = 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(57, ROGUE_FW_GROUP_POW, 2), + "Conflicting clock frequency range: OPP min = %u, max = %u" }, + { ROGUE_FW_LOG_CREATESFID(58, ROGUE_FW_GROUP_POW, 1), + "Proactive DVFS: Set floor to a minimum = 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(59, ROGUE_FW_GROUP_POW, 2), + "OS requested pow off (forced = %d), pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(60, ROGUE_FW_GROUP_POW, 1), + "Discarding invalid power request: type 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(61, ROGUE_FW_GROUP_POW, 3), + "Request to change SPU power state mask from 0x%x to 0x%x. Pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(62, ROGUE_FW_GROUP_POW, 2), + "Changing SPU power state mask from 0x%x to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(63, ROGUE_FW_GROUP_POW, 1), + "Detected attempt to change SPU power state mask while not forced idle (pow state 0x%x)" }, + { ROGUE_FW_LOG_CREATESFID(64, ROGUE_FW_GROUP_POW, 1), + "Invalid SPU power mask 0x%x! Changing to 1" }, + { ROGUE_FW_LOG_CREATESFID(65, ROGUE_FW_GROUP_POW, 2), + "Proactive DVFS: Send OPP %u with clock divider value %u" }, + { ROGUE_FW_LOG_CREATESFID(66, ROGUE_FW_GROUP_POW, 0), + "PPA block started in perf validation mode." }, + { ROGUE_FW_LOG_CREATESFID(67, ROGUE_FW_GROUP_POW, 1), + "Reset PPA block state %u (1=reset, 0=recalculate)." }, + { ROGUE_FW_LOG_CREATESFID(68, ROGUE_FW_GROUP_POW, 1), + "Power controller returned ABORT for Core-%d last request so retrying." }, + { ROGUE_FW_LOG_CREATESFID(69, ROGUE_FW_GROUP_POW, 3), + "HW Request On(1)/Off(0): %d, Units: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(70, ROGUE_FW_GROUP_POW, 5), + "Request to change SPU power state mask from 0x%x to 0x%x and RAC from 0x%x to 0x%x. Pow flags: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(71, ROGUE_FW_GROUP_POW, 4), + "Changing SPU power state mask from 0x%x to 0x%x and RAC from 0x%x to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(72, ROGUE_FW_GROUP_POW, 2), + "RAC pending? %d, RAC Active? %d" }, + { ROGUE_FW_LOG_CREATESFID(73, ROGUE_FW_GROUP_POW, 0), + "Initiate powoff query for RAC." }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_HWR, 2), + "Lockup detected on DM%d, FWCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_HWR, 3), + "Reset fw state for DM%d, FWCtx: 0x%08.8x, MemCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_HWR, 0), + "Reset HW" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_HWR, 0), + "Lockup recovered." }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_HWR, 2), + "Lock-up DM%d FWCtx: 0x%08.8x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_HWR, 4), + "Lockup detected: GLB(%d->%d), PER-DM(0x%08x->0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_HWR, 3), + "Early fault detection: GLB(%d->%d), PER-DM(0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_HWR, 3), + "Hold scheduling due lockup: GLB(%d), PER-DM(0x%08x->0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_HWR, 4), + "False lockup detected: GLB(%d->%d), PER-DM(0x%08x->0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_HWR, 4), + "BRN37729: GLB(%d->%d), PER-DM(0x%08x->0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_HWR, 3), + "Freelists reconstructed: GLB(%d->%d), PER-DM(0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_HWR, 4), + "Reconstructing freelists: %u (0-No, 1-Yes): GLB(%d->%d), PER-DM(0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_HWR, 3), + "HW poll %u (0-Unset 1-Set) failed (reg:0x%08x val:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_HWR, 2), + "Discarded cmd on DM%u FWCtx=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_HWR, 6), + "Discarded cmd on DM%u (reason=%u) HWRTData=0x%08x (st: %d), FWCtx 0x%08x @ %d" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_HWR, 2), + "PM fence WA could not be applied, Valid TA Setup: %d, RD powered off: %d" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_HWR, 5), + "FL snapshot RTD 0x%08.8x - local (0x%08.8x): %d, global (0x%08.8x): %d" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_HWR, 8), + "FL check RTD 0x%08.8x, discard: %d - local (0x%08.8x): s%d?=c%d, global (0x%08.8x): s%d?=c%d" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_HWR, 2), + "FL reconstruction 0x%08.8x c%d" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_HWR, 3), + "3D check: missing TA FWCtx 0x%08.8x @ %d, RTD 0x%08x." }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_HWR, 2), + "Reset HW (mmu:%d, extmem: %d)" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_HWR, 4), + "Zero TA caches for FWCtx: 0x%08.8x (TPC addr: 0x%08x%08x, size: %d bytes)" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_HWR, 2), + "Recovery DM%u: Freelists reconstructed. New R-Flags=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_HWR, 5), + "Recovery DM%u: FWCtx 0x%08x skipped to command @ %u. PR=%u. New R-Flags=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_HWR, 1), + "Recovery DM%u: DM fully recovered" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_HWR, 2), + "DM%u: Hold scheduling due to R-Flag = 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_HWR, 0), + "Analysis: Need freelist reconstruction" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_HWR, 2), + "Analysis DM%u: Lockup FWCtx: 0x%08.8x. Need to skip to next command" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_HWR, 2), + "Analysis DM%u: Lockup while TA is OOM FWCtx: 0x%08.8x. Need to skip to next command" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_HWR, 2), + "Analysis DM%u: Lockup while partial render FWCtx: 0x%08.8x. Need PR cleanup" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_HWR, 0), + "GPU has locked up" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_HWR, 1), + "DM%u ready for HWR" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_HWR, 2), + "Recovery DM%u: Updated Recovery counter. New R-Flags=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_HWR, 1), + "Analysis: BRN37729 detected, reset TA and re-kicked 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_HWR, 1), + "DM%u timed out" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_HWR, 1), + "RGX_CR_EVENT_STATUS=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_HWR, 2), + "DM%u lockup falsely detected, R-Flags=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(38, ROGUE_FW_GROUP_HWR, 0), + "GPU has overrun its deadline" }, + { ROGUE_FW_LOG_CREATESFID(39, ROGUE_FW_GROUP_HWR, 0), + "GPU has failed a poll" }, + { ROGUE_FW_LOG_CREATESFID(40, ROGUE_FW_GROUP_HWR, 2), + "RGX DM%u phase count=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(41, ROGUE_FW_GROUP_HWR, 2), + "Reset HW (loop:%d, poll failures: 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(42, ROGUE_FW_GROUP_HWR, 1), + "MMU fault event: 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(43, ROGUE_FW_GROUP_HWR, 1), + "BIF1 page fault detected (Bank1 MMU Status: 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(44, ROGUE_FW_GROUP_HWR, 1), + "Fast CRC Failed. Proceeding to full register checking (DM: %u)." }, + { ROGUE_FW_LOG_CREATESFID(45, ROGUE_FW_GROUP_HWR, 2), + "Meta MMU page fault detected (Meta MMU Status: 0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(46, ROGUE_FW_GROUP_HWR, 2), + "Fast CRC Check result for DM%u is HWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(47, ROGUE_FW_GROUP_HWR, 2), + "Full Signature Check result for DM%u is HWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(48, ROGUE_FW_GROUP_HWR, 3), + "Final result for DM%u is HWRNeeded=%u with HWRChecksToGo=%u" }, + { ROGUE_FW_LOG_CREATESFID(49, ROGUE_FW_GROUP_HWR, 3), + "USC Slots result for DM%u is HWRNeeded=%u USCSlotsUsedByDM=%d" }, + { ROGUE_FW_LOG_CREATESFID(50, ROGUE_FW_GROUP_HWR, 2), + "Deadline counter for DM%u is HWRDeadline=%u" }, + { ROGUE_FW_LOG_CREATESFID(51, ROGUE_FW_GROUP_HWR, 1), + "Holding Scheduling on OSid %u due to pending freelist reconstruction" }, + { ROGUE_FW_LOG_CREATESFID(52, ROGUE_FW_GROUP_HWR, 2), + "Requesting reconstruction for freelist 0x%x (ID=%d)" }, + { ROGUE_FW_LOG_CREATESFID(53, ROGUE_FW_GROUP_HWR, 1), + "Reconstruction of freelist ID=%d complete" }, + { ROGUE_FW_LOG_CREATESFID(54, ROGUE_FW_GROUP_HWR, 4), + "Reconstruction needed for freelist 0x%x (ID=%d) type: %d (0:local,1:global,2:mmu) on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(55, ROGUE_FW_GROUP_HWR, 1), + "Reconstruction of freelist ID=%d failed" }, + { ROGUE_FW_LOG_CREATESFID(56, ROGUE_FW_GROUP_HWR, 4), + "Restricting PDS Tasks to help other stalling DMs (RunningMask=0x%02x, StallingMask=0x%02x, PDS_CTRL=0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(57, ROGUE_FW_GROUP_HWR, 4), + "Unrestricting PDS Tasks again (RunningMask=0x%02x, StallingMask=0x%02x, PDS_CTRL=0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(58, ROGUE_FW_GROUP_HWR, 2), + "USC slots: %u used by DM%u" }, + { ROGUE_FW_LOG_CREATESFID(59, ROGUE_FW_GROUP_HWR, 1), + "USC slots: %u empty" }, + { ROGUE_FW_LOG_CREATESFID(60, ROGUE_FW_GROUP_HWR, 5), + "HCS DM%d's Context Switch failed to meet deadline. Current time: 0x%08x%08x, deadline: 0x%08x%08x" }, + { ROGUE_FW_LOG_CREATESFID(61, ROGUE_FW_GROUP_HWR, 1), + "Begin hardware reset (HWR Counter=%d)" }, + { ROGUE_FW_LOG_CREATESFID(62, ROGUE_FW_GROUP_HWR, 1), + "Finished hardware reset (HWR Counter=%d)" }, + { ROGUE_FW_LOG_CREATESFID(63, ROGUE_FW_GROUP_HWR, 2), + "Holding Scheduling on DM %u for OSid %u due to pending freelist reconstruction" }, + { ROGUE_FW_LOG_CREATESFID(64, ROGUE_FW_GROUP_HWR, 5), + "User Mode Queue ROff reset: FWCtx 0x%08.8x, queue: 0x%08x%08x (Roff = %u becomes StreamStartOffset = %u)" }, + { ROGUE_FW_LOG_CREATESFID(65, ROGUE_FW_GROUP_HWR, 4), + "Reconstruction needed for freelist 0x%x (ID=%d) type: %d (0:local,1:global) on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(66, ROGUE_FW_GROUP_HWR, 3), + "Mips page fault detected (BadVAddr: 0x%08x, EntryLo0: 0x%08x, EntryLo1: 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(67, ROGUE_FW_GROUP_HWR, 1), + "At least one other DM is running okay so DM%u will get another chance" }, + { ROGUE_FW_LOG_CREATESFID(68, ROGUE_FW_GROUP_HWR, 2), + "Reconstructing in FW, FL: 0x%x (ID=%d)" }, + { ROGUE_FW_LOG_CREATESFID(69, ROGUE_FW_GROUP_HWR, 4), + "Zero RTC for FWCtx: 0x%08.8x (RTC addr: 0x%08x%08x, size: %d bytes)" }, + { ROGUE_FW_LOG_CREATESFID(70, ROGUE_FW_GROUP_HWR, 5), + "Reconstruction needed for freelist 0x%x (ID=%d) type: %d (0:local,1:global) phase: %d (0:TA, 1:3D) on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(71, ROGUE_FW_GROUP_HWR, 3), + "Start long HW poll %u (0-Unset 1-Set) for (reg:0x%08x val:0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(72, ROGUE_FW_GROUP_HWR, 1), + "End long HW poll (result=%d)" }, + { ROGUE_FW_LOG_CREATESFID(73, ROGUE_FW_GROUP_HWR, 3), + "DM%u has taken %d ticks and deadline is %d ticks" }, + { ROGUE_FW_LOG_CREATESFID(74, ROGUE_FW_GROUP_HWR, 5), + "USC Watchdog result for DM%u is HWRNeeded=%u Status=%u USCs={0x%x} with HWRChecksToGo=%u" }, + { ROGUE_FW_LOG_CREATESFID(75, ROGUE_FW_GROUP_HWR, 6), + "Reconstruction needed for freelist 0x%x (ID=%d) OSid: %d type: %d (0:local,1:global) phase: %d (0:TA, 1:3D) on HW context %u" }, + { ROGUE_FW_LOG_CREATESFID(76, ROGUE_FW_GROUP_HWR, 1), + "GPU-%u has locked up" }, + { ROGUE_FW_LOG_CREATESFID(77, ROGUE_FW_GROUP_HWR, 1), + "DM%u has locked up" }, + { ROGUE_FW_LOG_CREATESFID(78, ROGUE_FW_GROUP_HWR, 2), + "Core %d RGX_CR_EVENT_STATUS=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(79, ROGUE_FW_GROUP_HWR, 2), + "RGX_CR_MULTICORE_EVENT_STATUS%u=0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(80, ROGUE_FW_GROUP_HWR, 5), + "BIF0 page fault detected (Core %d MMU Status: 0x%08x%08x Req Status: 0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(81, ROGUE_FW_GROUP_HWR, 3), + "MMU page fault detected (Core %d MMU Status: 0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(82, ROGUE_FW_GROUP_HWR, 4), + "MMU page fault detected (Core %d MMU Status: 0x%08x%08x 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(83, ROGUE_FW_GROUP_HWR, 4), + "Reset HW (core:%d of %d, loop:%d, poll failures: 0x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(84, ROGUE_FW_GROUP_HWR, 3), + "Fast CRC Check result for Core%u, DM%u is HWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(85, ROGUE_FW_GROUP_HWR, 3), + "Full Signature Check result for Core%u, DM%u is HWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(86, ROGUE_FW_GROUP_HWR, 4), + "USC Slots result for Core%u, DM%u is HWRNeeded=%u USCSlotsUsedByDM=%d" }, + { ROGUE_FW_LOG_CREATESFID(87, ROGUE_FW_GROUP_HWR, 6), + "USC Watchdog result for Core%u DM%u is HWRNeeded=%u Status=%u USCs={0x%x} with HWRChecksToGo=%u" }, + { ROGUE_FW_LOG_CREATESFID(88, ROGUE_FW_GROUP_HWR, 3), + "RISC-V MMU page fault detected (FWCORE MMU Status 0x%08x Req Status 0x%08x%08x)" }, + { ROGUE_FW_LOG_CREATESFID(89, ROGUE_FW_GROUP_HWR, 2), + "TEXAS1_PFS poll failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(90, ROGUE_FW_GROUP_HWR, 2), + "BIF_PFS poll failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(91, ROGUE_FW_GROUP_HWR, 2), + "MMU_ABORT_PM_STATUS set poll failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(92, ROGUE_FW_GROUP_HWR, 2), + "MMU_ABORT_PM_STATUS unset poll failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(93, ROGUE_FW_GROUP_HWR, 2), + "MMU_CTRL_INVAL poll (all but fw) failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(94, ROGUE_FW_GROUP_HWR, 2), + "MMU_CTRL_INVAL poll (all) failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(95, ROGUE_FW_GROUP_HWR, 3), + "TEXAS%d_PFS poll failed on core %d with value 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(96, ROGUE_FW_GROUP_HWR, 3), + "Extra Registers Check result for Core%u, DM%u is HWRNeeded=%u" }, + { ROGUE_FW_LOG_CREATESFID(97, ROGUE_FW_GROUP_HWR, 1), + "FW attempted to write to read-only GPU address 0x%08x" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_HWP, 2), + "Block 0x%x mapped to Config Idx %u" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_HWP, 1), + "Block 0x%x omitted from event - not enabled in HW" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_HWP, 1), + "Block 0x%x included in event - enabled in HW" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_HWP, 2), + "Select register state hi_0x%x lo_0x%x" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_HWP, 1), + "Counter stream block header word 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_HWP, 1), + "Counter register offset 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_HWP, 1), + "Block 0x%x config unset, skipping" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_HWP, 1), + "Accessing Indirect block 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_HWP, 1), + "Accessing Direct block 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_HWP, 1), + "Programmed counter select register at offset 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_HWP, 2), + "Block register offset 0x%x and value 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_HWP, 1), + "Reading config block from driver 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_HWP, 2), + "Reading block range 0x%x to 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_HWP, 1), + "Recording block 0x%x config from driver" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_HWP, 0), + "Finished reading config block from driver" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_HWP, 2), + "Custom Counter offset: 0x%x value: 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_HWP, 2), + "Select counter n:%u ID:0x%x" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_HWP, 3), + "The counter ID 0x%x is not allowed. The package [b:%u, n:%u] will be discarded" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_HWP, 1), + "Custom Counters filter status %d" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_HWP, 2), + "The Custom block %d is not allowed. Use only blocks lower than %d. The package will be discarded" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_HWP, 2), + "The package will be discarded because it contains %d counters IDs while the upper limit is %d" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_HWP, 2), + "Check Filter 0x%x is 0x%x ?" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_HWP, 1), + "The custom block %u is reset" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_HWP, 1), + "Encountered an invalid command (%d)" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_HWP, 2), + "HWPerf Queue is full, we will have to wait for space! (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(26, ROGUE_FW_GROUP_HWP, 3), + "HWPerf Queue is fencing, we are waiting for Roff = %d (Roff = %u, Woff = %u)" }, + { ROGUE_FW_LOG_CREATESFID(27, ROGUE_FW_GROUP_HWP, 1), + "Custom Counter block: %d" }, + { ROGUE_FW_LOG_CREATESFID(28, ROGUE_FW_GROUP_HWP, 1), + "Block 0x%x ENABLED" }, + { ROGUE_FW_LOG_CREATESFID(29, ROGUE_FW_GROUP_HWP, 1), + "Block 0x%x DISABLED" }, + { ROGUE_FW_LOG_CREATESFID(30, ROGUE_FW_GROUP_HWP, 2), + "Accessing Indirect block 0x%x, instance %u" }, + { ROGUE_FW_LOG_CREATESFID(31, ROGUE_FW_GROUP_HWP, 2), + "Counter register 0x%x, Value 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(32, ROGUE_FW_GROUP_HWP, 1), + "Counters filter status %d" }, + { ROGUE_FW_LOG_CREATESFID(33, ROGUE_FW_GROUP_HWP, 2), + "Block 0x%x mapped to Ctl Idx %u" }, + { ROGUE_FW_LOG_CREATESFID(34, ROGUE_FW_GROUP_HWP, 0), + "Block(s) in use for workload estimation." }, + { ROGUE_FW_LOG_CREATESFID(35, ROGUE_FW_GROUP_HWP, 3), + "GPU %u Cycle counter 0x%x, Value 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(36, ROGUE_FW_GROUP_HWP, 3), + "GPU Mask 0x%x Cycle counter 0x%x, Value 0x%x" }, + { ROGUE_FW_LOG_CREATESFID(37, ROGUE_FW_GROUP_HWP, 1), + "Blocks IGNORED for GPU %u" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_DMA, 5), + "Transfer 0x%02x request: 0x%02x%08x -> 0x%08x, size %u" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_DMA, 4), + "Transfer of type 0x%02x expected on channel %u, 0x%02x found, status %u" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_DMA, 1), + "DMA Interrupt register 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_DMA, 1), + "Waiting for transfer of type 0x%02x completion..." }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_DMA, 3), + "Loading of cCCB data from FW common context 0x%08x (offset: %u, size: %u) failed" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_DMA, 3), + "Invalid load of cCCB data from FW common context 0x%08x (offset: %u, size: %u)" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_DMA, 1), + "Transfer 0x%02x request poll failure" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_DMA, 2), + "Boot transfer(s) failed (code? %u, data? %u), used slower memcpy instead" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_DMA, 7), + "Transfer 0x%02x request on ch. %u: system 0x%02x%08x, coremem 0x%08x, flags 0x%x, size %u" }, + + { ROGUE_FW_LOG_CREATESFID(1, ROGUE_FW_GROUP_DBG, 2), + "0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(2, ROGUE_FW_GROUP_DBG, 1), + "0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(3, ROGUE_FW_GROUP_DBG, 2), + "0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(4, ROGUE_FW_GROUP_DBG, 3), + "0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(5, ROGUE_FW_GROUP_DBG, 4), + "0x%08x 0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(6, ROGUE_FW_GROUP_DBG, 5), + "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(7, ROGUE_FW_GROUP_DBG, 6), + "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(8, ROGUE_FW_GROUP_DBG, 7), + "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(9, ROGUE_FW_GROUP_DBG, 8), + "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x" }, + { ROGUE_FW_LOG_CREATESFID(10, ROGUE_FW_GROUP_DBG, 1), + "%d" }, + { ROGUE_FW_LOG_CREATESFID(11, ROGUE_FW_GROUP_DBG, 2), + "%d %d" }, + { ROGUE_FW_LOG_CREATESFID(12, ROGUE_FW_GROUP_DBG, 3), + "%d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(13, ROGUE_FW_GROUP_DBG, 4), + "%d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(14, ROGUE_FW_GROUP_DBG, 5), + "%d %d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(15, ROGUE_FW_GROUP_DBG, 6), + "%d %d %d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(16, ROGUE_FW_GROUP_DBG, 7), + "%d %d %d %d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(17, ROGUE_FW_GROUP_DBG, 8), + "%d %d %d %d %d %d %d %d" }, + { ROGUE_FW_LOG_CREATESFID(18, ROGUE_FW_GROUP_DBG, 1), + "%u" }, + { ROGUE_FW_LOG_CREATESFID(19, ROGUE_FW_GROUP_DBG, 2), + "%u %u" }, + { ROGUE_FW_LOG_CREATESFID(20, ROGUE_FW_GROUP_DBG, 3), + "%u %u %u" }, + { ROGUE_FW_LOG_CREATESFID(21, ROGUE_FW_GROUP_DBG, 4), + "%u %u %u %u" }, + { ROGUE_FW_LOG_CREATESFID(22, ROGUE_FW_GROUP_DBG, 5), + "%u %u %u %u %u" }, + { ROGUE_FW_LOG_CREATESFID(23, ROGUE_FW_GROUP_DBG, 6), + "%u %u %u %u %u %u" }, + { ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_DBG, 7), + "%u %u %u %u %u %u %u" }, + { ROGUE_FW_LOG_CREATESFID(25, ROGUE_FW_GROUP_DBG, 8), + "%u %u %u %u %u %u %u %u" }, + + { ROGUE_FW_LOG_CREATESFID(65535, ROGUE_FW_GROUP_NULL, 15), + "You should not use this string" }, +}; + +#define ROGUE_FW_SF_FIRST ROGUE_FW_LOG_CREATESFID(0, ROGUE_FW_GROUP_NULL, 0) +#define ROGUE_FW_SF_MAIN_ASSERT_FAILED ROGUE_FW_LOG_CREATESFID(24, ROGUE_FW_GROUP_MAIN, 1) +#define ROGUE_FW_SF_LAST ROGUE_FW_LOG_CREATESFID(65535, ROGUE_FW_GROUP_NULL, 15) + +#endif /* PVR_ROGUE_FWIF_SF_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h new file mode 100644 index 000000000000..6c09c15bf9bd --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_SHARED_H +#define PVR_ROGUE_FWIF_SHARED_H + +#include <linux/compiler.h> +#include <linux/types.h> + +#define ROGUE_FWIF_NUM_RTDATAS 2U +#define ROGUE_FWIF_NUM_GEOMDATAS 1U +#define ROGUE_FWIF_NUM_RTDATA_FREELISTS 2U +#define ROGUE_NUM_GEOM_CORES 1U + +#define ROGUE_NUM_GEOM_CORES_SIZE 2U + +/* + * Maximum number of UFOs in a CCB command. + * The number is based on having 32 sync prims (as originally), plus 32 sync + * checkpoints. + * Once the use of sync prims is no longer supported, we will retain + * the same total (64) as the number of sync checkpoints which may be + * supporting a fence is not visible to the client driver and has to + * allow for the number of different timelines involved in fence merges. + */ +#define ROGUE_FWIF_CCB_CMD_MAX_UFOS (32U + 32U) + +/* + * This is a generic limit imposed on any DM (GEOMETRY,FRAGMENT,CDM,TDM,2D,TRANSFER) + * command passed through the bridge. + * Just across the bridge in the server, any incoming kick command size is + * checked against this maximum limit. + * In case the incoming command size is larger than the specified limit, + * the bridge call is retired with error. + */ +#define ROGUE_FWIF_DM_INDEPENDENT_KICK_CMD_SIZE (1024U) + +#define ROGUE_FWIF_PRBUFFER_START (0) +#define ROGUE_FWIF_PRBUFFER_ZSBUFFER (0) +#define ROGUE_FWIF_PRBUFFER_MSAABUFFER (1) +#define ROGUE_FWIF_PRBUFFER_MAXSUPPORTED (2) + +struct rogue_fwif_dma_addr { + aligned_u64 dev_addr; + u32 fw_addr; + u32 padding; +} __aligned(8); + +struct rogue_fwif_ufo { + u32 addr; + u32 value; +}; + +#define ROGUE_FWIF_UFO_ADDR_IS_SYNC_CHECKPOINT (1) + +struct rogue_fwif_sync_checkpoint { + u32 state; + u32 fw_ref_count; +}; + +struct rogue_fwif_cleanup_ctl { + /* Number of commands received by the FW */ + u32 submitted_commands; + /* Number of commands executed by the FW */ + u32 executed_commands; +} __aligned(8); + +/* + * Used to share frame numbers across UM-KM-FW, + * frame number is set in UM, + * frame number is required in both KM for HTB and FW for FW trace. + * + * May be used to house Kick flags in the future. + */ +struct rogue_fwif_cmd_common { + /* associated frame number */ + u32 frame_num; +}; + +/* + * Geometry and fragment commands require set of firmware addresses that are stored in the Kernel. + * Client has handle(s) to Kernel containers storing these addresses, instead of raw addresses. We + * have to patch/write these addresses in KM to prevent UM from controlling FW addresses directly. + * Typedefs for geometry and fragment commands are shared between Client and Firmware (both + * single-BVNC). Kernel is implemented in a multi-BVNC manner, so it can't use geometry|fragment + * CMD type definitions directly. Therefore we have a SHARED block that is shared between UM-KM-FW + * across all BVNC configurations. + */ +struct rogue_fwif_cmd_geom_frag_shared { + /* Common command attributes */ + struct rogue_fwif_cmd_common cmn; + + /* + * RTData associated with this command, this is used for context + * selection and for storing out HW-context, when TA is switched out for + * continuing later + */ + u32 hwrt_data_fw_addr; + + /* Supported PR Buffers like Z/S/MSAA Scratch */ + u32 pr_buffer_fw_addr[ROGUE_FWIF_PRBUFFER_MAXSUPPORTED]; +}; + +/* + * Client Circular Command Buffer (CCCB) control structure. + * This is shared between the Server and the Firmware and holds byte offsets + * into the CCCB as well as the wrapping mask to aid wrap around. A given + * snapshot of this queue with Cmd 1 running on the GPU might be: + * + * Roff Doff Woff + * [..........|-1----------|=2===|=3===|=4===|~5~~~~|~6~~~~|~7~~~~|..........] + * < runnable commands >< !ready to run > + * + * Cmd 1 : Currently executing on the GPU data master. + * Cmd 2,3,4: Fence dependencies met, commands runnable. + * Cmd 5... : Fence dependency not met yet. + */ +struct rogue_fwif_cccb_ctl { + /* Host write offset into CCB. This must be aligned to 16 bytes. */ + u32 write_offset; + /* + * Firmware read offset into CCB. Points to the command that is runnable + * on GPU, if R!=W + */ + u32 read_offset; + /* + * Firmware fence dependency offset. Points to commands not ready, i.e. + * fence dependencies are not met. + */ + u32 dep_offset; + /* Offset wrapping mask, total capacity in bytes of the CCB-1 */ + u32 wrap_mask; + + /* Only used if SUPPORT_AGP is present. */ + u32 read_offset2; + + /* Only used if SUPPORT_AGP4 is present. */ + u32 read_offset3; + /* Only used if SUPPORT_AGP4 is present. */ + u32 read_offset4; + + u32 padding; +} __aligned(8); + +#define ROGUE_FW_LOCAL_FREELIST (0) +#define ROGUE_FW_GLOBAL_FREELIST (1) +#define ROGUE_FW_FREELIST_TYPE_LAST ROGUE_FW_GLOBAL_FREELIST +#define ROGUE_FW_MAX_FREELISTS (ROGUE_FW_FREELIST_TYPE_LAST + 1U) + +struct rogue_fwif_geom_registers_caswitch { + u64 geom_reg_vdm_context_state_base_addr; + u64 geom_reg_vdm_context_state_resume_addr; + u64 geom_reg_ta_context_state_base_addr; + + struct { + u64 geom_reg_vdm_context_store_task0; + u64 geom_reg_vdm_context_store_task1; + u64 geom_reg_vdm_context_store_task2; + + /* VDM resume state update controls */ + u64 geom_reg_vdm_context_resume_task0; + u64 geom_reg_vdm_context_resume_task1; + u64 geom_reg_vdm_context_resume_task2; + + u64 geom_reg_vdm_context_store_task3; + u64 geom_reg_vdm_context_store_task4; + + u64 geom_reg_vdm_context_resume_task3; + u64 geom_reg_vdm_context_resume_task4; + } geom_state[2]; +}; + +#define ROGUE_FWIF_GEOM_REGISTERS_CSWITCH_SIZE \ + sizeof(struct rogue_fwif_geom_registers_caswitch) + +struct rogue_fwif_cdm_registers_cswitch { + u64 cdmreg_cdm_context_pds0; + u64 cdmreg_cdm_context_pds1; + u64 cdmreg_cdm_terminate_pds; + u64 cdmreg_cdm_terminate_pds1; + + /* CDM resume controls */ + u64 cdmreg_cdm_resume_pds0; + u64 cdmreg_cdm_context_pds0_b; + u64 cdmreg_cdm_resume_pds0_b; +}; + +struct rogue_fwif_static_rendercontext_state { + /* Geom registers for ctx switch */ + struct rogue_fwif_geom_registers_caswitch ctxswitch_regs[ROGUE_NUM_GEOM_CORES_SIZE] + __aligned(8); +}; + +#define ROGUE_FWIF_STATIC_RENDERCONTEXT_SIZE \ + sizeof(struct rogue_fwif_static_rendercontext_state) + +struct rogue_fwif_static_computecontext_state { + /* CDM registers for ctx switch */ + struct rogue_fwif_cdm_registers_cswitch ctxswitch_regs __aligned(8); +}; + +#define ROGUE_FWIF_STATIC_COMPUTECONTEXT_SIZE \ + sizeof(struct rogue_fwif_static_computecontext_state) + +enum rogue_fwif_prbuffer_state { + ROGUE_FWIF_PRBUFFER_UNBACKED = 0, + ROGUE_FWIF_PRBUFFER_BACKED, + ROGUE_FWIF_PRBUFFER_BACKING_PENDING, + ROGUE_FWIF_PRBUFFER_UNBACKING_PENDING, +}; + +struct rogue_fwif_prbuffer { + /* Buffer ID*/ + u32 buffer_id; + /* Needs On-demand Z/S/MSAA Buffer allocation */ + bool on_demand __aligned(4); + /* Z/S/MSAA -Buffer state */ + enum rogue_fwif_prbuffer_state state; + /* Cleanup state */ + struct rogue_fwif_cleanup_ctl cleanup_sate; + /* Compatibility and other flags */ + u32 prbuffer_flags; +} __aligned(8); + +/* Last reset reason for a context. */ +enum rogue_context_reset_reason { + /* No reset reason recorded */ + ROGUE_CONTEXT_RESET_REASON_NONE = 0, + /* Caused a reset due to locking up */ + ROGUE_CONTEXT_RESET_REASON_GUILTY_LOCKUP = 1, + /* Affected by another context locking up */ + ROGUE_CONTEXT_RESET_REASON_INNOCENT_LOCKUP = 2, + /* Overran the global deadline */ + ROGUE_CONTEXT_RESET_REASON_GUILTY_OVERRUNING = 3, + /* Affected by another context overrunning */ + ROGUE_CONTEXT_RESET_REASON_INNOCENT_OVERRUNING = 4, + /* Forced reset to ensure scheduling requirements */ + ROGUE_CONTEXT_RESET_REASON_HARD_CONTEXT_SWITCH = 5, + /* FW Safety watchdog triggered */ + ROGUE_CONTEXT_RESET_REASON_FW_WATCHDOG = 12, + /* FW page fault (no HWR) */ + ROGUE_CONTEXT_RESET_REASON_FW_PAGEFAULT = 13, + /* FW execution error (GPU reset requested) */ + ROGUE_CONTEXT_RESET_REASON_FW_EXEC_ERR = 14, + /* Host watchdog detected FW error */ + ROGUE_CONTEXT_RESET_REASON_HOST_WDG_FW_ERR = 15, + /* Geometry DM OOM event is not allowed */ + ROGUE_CONTEXT_GEOM_OOM_DISABLED = 16, +}; + +struct rogue_context_reset_reason_data { + enum rogue_context_reset_reason reset_reason; + u32 reset_ext_job_ref; +}; + +#include "pvr_rogue_fwif_shared_check.h" + +#endif /* PVR_ROGUE_FWIF_SHARED_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h new file mode 100644 index 000000000000..597ed54bbd3a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_SHARED_CHECK_H +#define PVR_ROGUE_FWIF_SHARED_CHECK_H + +#include <linux/build_bug.h> + +#define OFFSET_CHECK(type, member, offset) \ + static_assert(offsetof(type, member) == (offset), \ + "offsetof(" #type ", " #member ") incorrect") + +#define SIZE_CHECK(type, size) \ + static_assert(sizeof(type) == (size), #type " is incorrect size") + +OFFSET_CHECK(struct rogue_fwif_dma_addr, dev_addr, 0); +OFFSET_CHECK(struct rogue_fwif_dma_addr, fw_addr, 8); +SIZE_CHECK(struct rogue_fwif_dma_addr, 16); + +OFFSET_CHECK(struct rogue_fwif_ufo, addr, 0); +OFFSET_CHECK(struct rogue_fwif_ufo, value, 4); +SIZE_CHECK(struct rogue_fwif_ufo, 8); + +OFFSET_CHECK(struct rogue_fwif_cleanup_ctl, submitted_commands, 0); +OFFSET_CHECK(struct rogue_fwif_cleanup_ctl, executed_commands, 4); +SIZE_CHECK(struct rogue_fwif_cleanup_ctl, 8); + +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, write_offset, 0); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset, 4); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, dep_offset, 8); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, wrap_mask, 12); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset2, 16); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset3, 20); +OFFSET_CHECK(struct rogue_fwif_cccb_ctl, read_offset4, 24); +SIZE_CHECK(struct rogue_fwif_cccb_ctl, 32); + +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_reg_vdm_context_state_base_addr, 0); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_reg_vdm_context_state_resume_addr, 8); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_reg_ta_context_state_base_addr, 16); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task0, 24); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task1, 32); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task2, 40); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task0, 48); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task1, 56); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task2, 64); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task3, 72); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task4, 80); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task3, 88); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task4, 96); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task0, 104); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task1, 112); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task2, 120); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task0, 128); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task1, 136); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task2, 144); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task3, 152); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task4, 160); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task3, 168); +OFFSET_CHECK(struct rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task4, 176); +SIZE_CHECK(struct rogue_fwif_geom_registers_caswitch, 184); + +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0, 0); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds1, 8); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds, 16); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds1, 24); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0, 32); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0_b, 40); +OFFSET_CHECK(struct rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0_b, 48); +SIZE_CHECK(struct rogue_fwif_cdm_registers_cswitch, 56); + +OFFSET_CHECK(struct rogue_fwif_static_rendercontext_state, ctxswitch_regs, 0); +SIZE_CHECK(struct rogue_fwif_static_rendercontext_state, 368); + +OFFSET_CHECK(struct rogue_fwif_static_computecontext_state, ctxswitch_regs, 0); +SIZE_CHECK(struct rogue_fwif_static_computecontext_state, 56); + +OFFSET_CHECK(struct rogue_fwif_cmd_common, frame_num, 0); +SIZE_CHECK(struct rogue_fwif_cmd_common, 4); + +OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, cmn, 0); +OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, hwrt_data_fw_addr, 4); +OFFSET_CHECK(struct rogue_fwif_cmd_geom_frag_shared, pr_buffer_fw_addr, 8); +SIZE_CHECK(struct rogue_fwif_cmd_geom_frag_shared, 16); + +#endif /* PVR_ROGUE_FWIF_SHARED_CHECK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_stream.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_stream.h new file mode 100644 index 000000000000..1c2c4ebedc25 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_stream.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_FWIF_STREAM_H +#define PVR_ROGUE_FWIF_STREAM_H + +/** + * DOC: Streams + * + * Commands are submitted to the kernel driver in the form of streams. + * + * A command stream has the following layout : + * - A 64-bit header containing: + * * A u32 containing the length of the main stream inclusive of the length of the header. + * * A u32 for padding. + * - The main stream data. + * - The extension stream (optional), which is composed of: + * * One or more headers. + * * The extension stream data, corresponding to the extension headers. + * + * The main stream provides the base command data. This has a fixed layout based on the features + * supported by a given GPU. + * + * The extension stream provides the command parameters that are required for BRNs & ERNs for the + * current GPU. This stream is comprised of one or more headers, followed by data for each given + * BRN/ERN. + * + * Each header is a u32 containing a bitmask of quirks & enhancements in the extension stream, a + * "type" field determining the set of quirks & enhancements the bitmask represents, and a + * continuation bit determining whether any more headers are present. The headers are then followed + * by command data; this is specific to each quirk/enhancement. All unused / reserved bits in the + * header must be set to 0. + * + * All parameters and headers in the main and extension streams must be naturally aligned. + * + * If a parameter appears in both the main and extension streams, then the extension parameter is + * used. + */ + +/* + * Stream extension header definition + */ +#define PVR_STREAM_EXTHDR_TYPE_SHIFT 29U +#define PVR_STREAM_EXTHDR_TYPE_MASK (7U << PVR_STREAM_EXTHDR_TYPE_SHIFT) +#define PVR_STREAM_EXTHDR_TYPE_MAX 8U +#define PVR_STREAM_EXTHDR_CONTINUATION BIT(28U) + +#define PVR_STREAM_EXTHDR_DATA_MASK ~(PVR_STREAM_EXTHDR_TYPE_MASK | PVR_STREAM_EXTHDR_CONTINUATION) + +/* + * Stream extension header - Geometry 0 + */ +#define PVR_STREAM_EXTHDR_TYPE_GEOM0 0U + +#define PVR_STREAM_EXTHDR_GEOM0_BRN49927 BIT(0U) + +#define PVR_STREAM_EXTHDR_GEOM0_VALID PVR_STREAM_EXTHDR_GEOM0_BRN49927 + +/* + * Stream extension header - Fragment 0 + */ +#define PVR_STREAM_EXTHDR_TYPE_FRAG0 0U + +#define PVR_STREAM_EXTHDR_FRAG0_BRN47217 BIT(0U) +#define PVR_STREAM_EXTHDR_FRAG0_BRN49927 BIT(1U) + +#define PVR_STREAM_EXTHDR_FRAG0_VALID PVR_STREAM_EXTHDR_FRAG0_BRN49927 + +/* + * Stream extension header - Compute 0 + */ +#define PVR_STREAM_EXTHDR_TYPE_COMPUTE0 0U + +#define PVR_STREAM_EXTHDR_COMPUTE0_BRN49927 BIT(0U) + +#define PVR_STREAM_EXTHDR_COMPUTE0_VALID PVR_STREAM_EXTHDR_COMPUTE0_BRN49927 + +#endif /* PVR_ROGUE_FWIF_STREAM_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h new file mode 100644 index 000000000000..684766006703 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_HEAP_CONFIG_H +#define PVR_ROGUE_HEAP_CONFIG_H + +#include <linux/sizes.h> + +/* + * ROGUE Device Virtual Address Space Definitions + * + * This file defines the ROGUE virtual address heaps that are used in + * application memory contexts. It also shows where the Firmware memory heap + * fits into this, but the firmware heap is only ever created in the + * kernel driver and never exposed to userspace. + * + * ROGUE_PDSCODEDATA_HEAP_BASE and ROGUE_USCCODE_HEAP_BASE will be programmed, + * on a global basis, into ROGUE_CR_PDS_EXEC_BASE and ROGUE_CR_USC_CODE_BASE_* + * respectively. Therefore if client drivers use multiple configs they must + * still be consistent with their definitions for these heaps. + * + * Base addresses have to be a multiple of 4MiB. + * Heaps must not start at 0x0000000000, as this is reserved for internal + * use within the driver. + * Range comments, those starting in column 0 below are a section heading of + * sorts and are above the heaps in that range. Often this is the reserved + * size of the heap within the range. + */ + +/* 0x00_0000_0000 ************************************************************/ + +/* 0x00_0000_0000 - 0x00_0040_0000 */ +/* 0 MiB to 4 MiB, size of 4 MiB : RESERVED */ + +/* 0x00_0040_0000 - 0x7F_FFC0_0000 **/ +/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED **/ + +/* 0x80_0000_0000 ************************************************************/ + +/* 0x80_0000_0000 - 0x9F_FFFF_FFFF **/ +/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP **/ +#define ROGUE_GENERAL_HEAP_BASE 0x8000000000ull +#define ROGUE_GENERAL_HEAP_SIZE SZ_128G + +/* 0xA0_0000_0000 - 0xAF_FFFF_FFFF */ +/* 640 GiB to 704 GiB, size of 64 GiB : FREE */ + +/* B0_0000_0000 - 0xB7_FFFF_FFFF */ +/* 704 GiB to 736 GiB, size of 32 GiB : FREE */ + +/* 0xB8_0000_0000 - 0xBF_FFFF_FFFF */ +/* 736 GiB to 768 GiB, size of 32 GiB : RESERVED */ + +/* 0xC0_0000_0000 ************************************************************/ + +/* 0xC0_0000_0000 - 0xD9_FFFF_FFFF */ +/* 768 GiB to 872 GiB, size of 104 GiB : FREE */ + +/* 0xDA_0000_0000 - 0xDA_FFFF_FFFF */ +/* 872 GiB to 876 GiB, size of 4 GiB : PDSCODEDATA_HEAP */ +#define ROGUE_PDSCODEDATA_HEAP_BASE 0xDA00000000ull +#define ROGUE_PDSCODEDATA_HEAP_SIZE SZ_4G + +/* 0xDB_0000_0000 - 0xDB_FFFF_FFFF */ +/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN **/ +/* + * The BRN63142 quirk workaround requires Region Header memory to be at the top + * of a 16GiB aligned range. This is so when masked with 0x03FFFFFFFF the + * address will avoid aliasing PB addresses. Start at 879.75GiB. Size of 256MiB. + */ +#define ROGUE_RGNHDR_HEAP_BASE 0xDBF0000000ull +#define ROGUE_RGNHDR_HEAP_SIZE SZ_256M + +/* 0xDC_0000_0000 - 0xDF_FFFF_FFFF */ +/* 880 GiB to 896 GiB, size of 16 GiB : FREE */ + +/* 0xE0_0000_0000 - 0xE0_FFFF_FFFF */ +/* 896 GiB to 900 GiB, size of 4 GiB : USCCODE_HEAP */ +#define ROGUE_USCCODE_HEAP_BASE 0xE000000000ull +#define ROGUE_USCCODE_HEAP_SIZE SZ_4G + +/* 0xE1_0000_0000 - 0xE1_BFFF_FFFF */ +/* 900 GiB to 903 GiB, size of 3 GiB : RESERVED */ + +/* 0xE1_C000_000 - 0xE1_FFFF_FFFF */ +/* 903 GiB to 904 GiB, reserved 1 GiB, : FIRMWARE_HEAP */ +#define ROGUE_FW_HEAP_BASE 0xE1C0000000ull + +/* 0xE2_0000_0000 - 0xE3_FFFF_FFFF */ +/* 904 GiB to 912 GiB, size of 8 GiB : FREE */ + +/* 0xE4_0000_0000 - 0xE7_FFFF_FFFF */ +/* 912 GiB to 968 GiB, size of 16 GiB : TRANSFER_FRAG */ +#define ROGUE_TRANSFER_FRAG_HEAP_BASE 0xE400000000ull +#define ROGUE_TRANSFER_FRAG_HEAP_SIZE SZ_16G + +/* 0xE8_0000_0000 - 0xF1_FFFF_FFFF */ +/* 928 GiB to 968 GiB, size of 40 GiB : RESERVED */ + +/* 0xF2_0000_0000 - 0xF2_001F_FFFF **/ +/* 968 GiB to 969 GiB, size of 2 MiB : VISTEST_HEAP */ +#define ROGUE_VISTEST_HEAP_BASE 0xF200000000ull +#define ROGUE_VISTEST_HEAP_SIZE SZ_2M + +/* 0xF2_4000_0000 - 0xF2_FFFF_FFFF */ +/* 969 GiB to 972 GiB, size of 3 GiB : FREE */ + +/* 0xF3_0000_0000 - 0xFF_FFFF_FFFF */ +/* 972 GiB to 1024 GiB, size of 52 GiB : FREE */ + +/* 0xFF_FFFF_FFFF ************************************************************/ + +#endif /* PVR_ROGUE_HEAP_CONFIG_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_meta.h b/drivers/gpu/drm/imagination/pvr_rogue_meta.h new file mode 100644 index 000000000000..3020e6582daa --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_meta.h @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_META_H +#define PVR_ROGUE_META_H + +/***** The META HW register definitions in the file are updated manually *****/ + +#include <linux/bits.h> +#include <linux/types.h> + +/* + ****************************************************************************** + * META registers and MACROS + ***************************************************************************** + */ +#define META_CR_CTRLREG_BASE(t) (0x04800000U + (0x1000U * (t))) + +#define META_CR_TXPRIVEXT (0x048000E8) +#define META_CR_TXPRIVEXT_MINIM_EN BIT(7) + +#define META_CR_SYSC_JTAG_THREAD (0x04830030) +#define META_CR_SYSC_JTAG_THREAD_PRIV_EN (0x00000004) + +#define META_CR_PERF_COUNT0 (0x0480FFE0) +#define META_CR_PERF_COUNT1 (0x0480FFE8) +#define META_CR_PERF_COUNT_CTRL_SHIFT (28) +#define META_CR_PERF_COUNT_CTRL_MASK (0xF0000000) +#define META_CR_PERF_COUNT_CTRL_DCACHEHITS (8 << META_CR_PERF_COUNT_CTRL_SHIFT) +#define META_CR_PERF_COUNT_CTRL_ICACHEHITS (9 << META_CR_PERF_COUNT_CTRL_SHIFT) +#define META_CR_PERF_COUNT_CTRL_ICACHEMISS \ + (0xA << META_CR_PERF_COUNT_CTRL_SHIFT) +#define META_CR_PERF_COUNT_CTRL_ICORE (0xD << META_CR_PERF_COUNT_CTRL_SHIFT) +#define META_CR_PERF_COUNT_THR_SHIFT (24) +#define META_CR_PERF_COUNT_THR_MASK (0x0F000000) +#define META_CR_PERF_COUNT_THR_0 (0x1 << META_CR_PERF_COUNT_THR_SHIFT) +#define META_CR_PERF_COUNT_THR_1 (0x2 << META_CR_PERF_COUNT_THR_1) + +#define META_CR_TxVECINT_BHALT (0x04820500) +#define META_CR_PERF_ICORE0 (0x0480FFD0) +#define META_CR_PERF_ICORE1 (0x0480FFD8) +#define META_CR_PERF_ICORE_DCACHEMISS (0x8) + +#define META_CR_PERF_COUNT(ctrl, thr) \ + ((META_CR_PERF_COUNT_CTRL_##ctrl << META_CR_PERF_COUNT_CTRL_SHIFT) | \ + ((thr) << META_CR_PERF_COUNT_THR_SHIFT)) + +#define META_CR_TXUXXRXDT_OFFSET (META_CR_CTRLREG_BASE(0U) + 0x0000FFF0U) +#define META_CR_TXUXXRXRQ_OFFSET (META_CR_CTRLREG_BASE(0U) + 0x0000FFF8U) + +/* Poll for done. */ +#define META_CR_TXUXXRXRQ_DREADY_BIT (0x80000000U) +/* Set for read. */ +#define META_CR_TXUXXRXRQ_RDnWR_BIT (0x00010000U) +#define META_CR_TXUXXRXRQ_TX_S (12) +#define META_CR_TXUXXRXRQ_RX_S (4) +#define META_CR_TXUXXRXRQ_UXX_S (0) + +/* Internal ctrl regs. */ +#define META_CR_TXUIN_ID (0x0) +/* Data unit regs. */ +#define META_CR_TXUD0_ID (0x1) +/* Data unit regs. */ +#define META_CR_TXUD1_ID (0x2) +/* Address unit regs. */ +#define META_CR_TXUA0_ID (0x3) +/* Address unit regs. */ +#define META_CR_TXUA1_ID (0x4) +/* PC registers. */ +#define META_CR_TXUPC_ID (0x5) + +/* Macros to calculate register access values. */ +#define META_CR_CORE_REG(thr, reg_num, unit) \ + (((u32)(thr) << META_CR_TXUXXRXRQ_TX_S) | \ + ((u32)(reg_num) << META_CR_TXUXXRXRQ_RX_S) | \ + ((u32)(unit) << META_CR_TXUXXRXRQ_UXX_S)) + +#define META_CR_THR0_PC META_CR_CORE_REG(0, 0, META_CR_TXUPC_ID) +#define META_CR_THR0_PCX META_CR_CORE_REG(0, 1, META_CR_TXUPC_ID) +#define META_CR_THR0_SP META_CR_CORE_REG(0, 0, META_CR_TXUA0_ID) + +#define META_CR_THR1_PC META_CR_CORE_REG(1, 0, META_CR_TXUPC_ID) +#define META_CR_THR1_PCX META_CR_CORE_REG(1, 1, META_CR_TXUPC_ID) +#define META_CR_THR1_SP META_CR_CORE_REG(1, 0, META_CR_TXUA0_ID) + +#define SP_ACCESS(thread) META_CR_CORE_REG(thread, 0, META_CR_TXUA0_ID) +#define PC_ACCESS(thread) META_CR_CORE_REG(thread, 0, META_CR_TXUPC_ID) + +#define META_CR_COREREG_ENABLE (0x0000000U) +#define META_CR_COREREG_STATUS (0x0000010U) +#define META_CR_COREREG_DEFR (0x00000A0U) +#define META_CR_COREREG_PRIVEXT (0x00000E8U) + +#define META_CR_T0ENABLE_OFFSET \ + (META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_ENABLE) +#define META_CR_T0STATUS_OFFSET \ + (META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_STATUS) +#define META_CR_T0DEFR_OFFSET (META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_DEFR) +#define META_CR_T0PRIVEXT_OFFSET \ + (META_CR_CTRLREG_BASE(0U) + META_CR_COREREG_PRIVEXT) + +#define META_CR_T1ENABLE_OFFSET \ + (META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_ENABLE) +#define META_CR_T1STATUS_OFFSET \ + (META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_STATUS) +#define META_CR_T1DEFR_OFFSET (META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_DEFR) +#define META_CR_T1PRIVEXT_OFFSET \ + (META_CR_CTRLREG_BASE(1U) + META_CR_COREREG_PRIVEXT) + +#define META_CR_TXENABLE_ENABLE_BIT (0x00000001U) /* Set if running */ +#define META_CR_TXSTATUS_PRIV (0x00020000U) +#define META_CR_TXPRIVEXT_MINIM (0x00000080U) + +#define META_MEM_GLOBAL_RANGE_BIT (0x80000000U) + +#define META_CR_TXCLKCTRL (0x048000B0) +#define META_CR_TXCLKCTRL_ALL_ON (0x55111111) +#define META_CR_TXCLKCTRL_ALL_AUTO (0xAA222222) + +#define META_CR_MMCU_LOCAL_EBCTRL (0x04830600) +#define META_CR_MMCU_LOCAL_EBCTRL_ICWIN (0x3 << 14) +#define META_CR_MMCU_LOCAL_EBCTRL_DCWIN (0x3 << 6) +#define META_CR_SYSC_DCPART(n) (0x04830200 + (n) * 0x8) +#define META_CR_SYSC_DCPARTX_CACHED_WRITE_ENABLE (0x1 << 31) +#define META_CR_SYSC_ICPART(n) (0x04830220 + (n) * 0x8) +#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_OFFSET_TOP_HALF (0x8 << 16) +#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_FULL_CACHE (0xF) +#define META_CR_SYSC_XCPARTX_LOCAL_ADDR_HALF_CACHE (0x7) +#define META_CR_MMCU_DCACHE_CTRL (0x04830018) +#define META_CR_MMCU_ICACHE_CTRL (0x04830020) +#define META_CR_MMCU_XCACHE_CTRL_CACHE_HITS_EN (0x1) + +/* + ****************************************************************************** + * META LDR Format + ****************************************************************************** + */ +/* Block header structure. */ +struct rogue_meta_ldr_block_hdr { + u32 dev_id; + u32 sl_code; + u32 sl_data; + u16 pc_ctrl; + u16 crc; +}; + +/* High level data stream block structure. */ +struct rogue_meta_ldr_l1_data_blk { + u16 cmd; + u16 length; + u32 next; + u32 cmd_data[4]; +}; + +/* High level data stream block structure. */ +struct rogue_meta_ldr_l2_data_blk { + u16 tag; + u16 length; + u32 block_data[4]; +}; + +/* Config command structure. */ +struct rogue_meta_ldr_cfg_blk { + u32 type; + u32 block_data[4]; +}; + +/* Block type definitions */ +#define ROGUE_META_LDR_COMMENT_TYPE_MASK (0x0010U) +#define ROGUE_META_LDR_BLK_IS_COMMENT(x) (((x) & ROGUE_META_LDR_COMMENT_TYPE_MASK) != 0U) + +/* + * Command definitions + * Value Name Description + * 0 LoadMem Load memory with binary data. + * 1 LoadCore Load a set of core registers. + * 2 LoadMMReg Load a set of memory mapped registers. + * 3 StartThreads Set each thread PC and SP, then enable threads. + * 4 ZeroMem Zeros a memory region. + * 5 Config Perform a configuration command. + */ +#define ROGUE_META_LDR_CMD_MASK (0x000FU) + +#define ROGUE_META_LDR_CMD_LOADMEM (0x0000U) +#define ROGUE_META_LDR_CMD_LOADCORE (0x0001U) +#define ROGUE_META_LDR_CMD_LOADMMREG (0x0002U) +#define ROGUE_META_LDR_CMD_START_THREADS (0x0003U) +#define ROGUE_META_LDR_CMD_ZEROMEM (0x0004U) +#define ROGUE_META_LDR_CMD_CONFIG (0x0005U) + +/* + * Config Command definitions + * Value Name Description + * 0 Pause Pause for x times 100 instructions + * 1 Read Read a value from register - No value return needed. + * Utilises effects of issuing reads to certain registers + * 2 Write Write to mem location + * 3 MemSet Set mem to value + * 4 MemCheck check mem for specific value. + */ +#define ROGUE_META_LDR_CFG_PAUSE (0x0000) +#define ROGUE_META_LDR_CFG_READ (0x0001) +#define ROGUE_META_LDR_CFG_WRITE (0x0002) +#define ROGUE_META_LDR_CFG_MEMSET (0x0003) +#define ROGUE_META_LDR_CFG_MEMCHECK (0x0004) + +/* + ****************************************************************************** + * ROGUE FW segmented MMU definitions + ****************************************************************************** + */ +/* All threads can access the segment. */ +#define ROGUE_FW_SEGMMU_ALLTHRS (0xf << 8U) +/* Writable. */ +#define ROGUE_FW_SEGMMU_WRITEABLE (0x1U << 1U) +/* All threads can access and writable. */ +#define ROGUE_FW_SEGMMU_ALLTHRS_WRITEABLE \ + (ROGUE_FW_SEGMMU_ALLTHRS | ROGUE_FW_SEGMMU_WRITEABLE) + +/* Direct map region 10 used for mapping GPU memory - max 8MB. */ +#define ROGUE_FW_SEGMMU_DMAP_GPU_ID (10U) +#define ROGUE_FW_SEGMMU_DMAP_GPU_ADDR_START (0x07000000U) +#define ROGUE_FW_SEGMMU_DMAP_GPU_MAX_SIZE (0x00800000U) + +/* Segment IDs. */ +#define ROGUE_FW_SEGMMU_DATA_ID (1U) +#define ROGUE_FW_SEGMMU_BOOTLDR_ID (2U) +#define ROGUE_FW_SEGMMU_TEXT_ID (ROGUE_FW_SEGMMU_BOOTLDR_ID) + +/* + * SLC caching strategy in S7 and volcanic is emitted through the segment MMU. + * All the segments configured through the macro ROGUE_FW_SEGMMU_OUTADDR_TOP are + * CACHED in the SLC. + * The interface has been kept the same to simplify the code changes. + * The bifdm argument is ignored (no longer relevant) in S7 and volcanic. + */ +#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(pers, slc_policy, mmu_ctx) \ + ((((u64)((pers) & 0x3)) << 52) | (((u64)((mmu_ctx) & 0xFF)) << 44) | \ + (((u64)((slc_policy) & 0x1)) << 40)) +#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC_CACHED(mmu_ctx) \ + ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(0x3, 0x0, mmu_ctx) +#define ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC_UNCACHED(mmu_ctx) \ + ROGUE_FW_SEGMMU_OUTADDR_TOP_VIVT_SLC(0x0, 0x1, mmu_ctx) + +/* + * To configure the Page Catalog and BIF-DM fed into the BIF for Garten + * accesses through this segment. + */ +#define ROGUE_FW_SEGMMU_OUTADDR_TOP_SLC(pc, bifdm) \ + (((u64)((u64)(pc) & 0xFU) << 44U) | ((u64)((u64)(bifdm) & 0xFU) << 40U)) + +#define ROGUE_FW_SEGMMU_META_BIFDM_ID (0x7U) + +/* META segments have 4kB minimum size. */ +#define ROGUE_FW_SEGMMU_ALIGN (0x1000U) + +/* Segmented MMU registers (n = segment id). */ +#define META_CR_MMCU_SEGMENT_N_BASE(n) (0x04850000U + ((n) * 0x10U)) +#define META_CR_MMCU_SEGMENT_N_LIMIT(n) (0x04850004U + ((n) * 0x10U)) +#define META_CR_MMCU_SEGMENT_N_OUTA0(n) (0x04850008U + ((n) * 0x10U)) +#define META_CR_MMCU_SEGMENT_N_OUTA1(n) (0x0485000CU + ((n) * 0x10U)) + +/* + * The following defines must be recalculated if the Meta MMU segments used + * to access Host-FW data are changed + * Current combinations are: + * - SLC uncached, META cached, FW base address 0x70000000 + * - SLC uncached, META uncached, FW base address 0xF0000000 + * - SLC cached, META cached, FW base address 0x10000000 + * - SLC cached, META uncached, FW base address 0x90000000 + */ +#define ROGUE_FW_SEGMMU_DATA_BASE_ADDRESS (0x10000000U) +#define ROGUE_FW_SEGMMU_DATA_META_CACHED (0x0U) +#define ROGUE_FW_SEGMMU_DATA_META_UNCACHED (META_MEM_GLOBAL_RANGE_BIT) +#define ROGUE_FW_SEGMMU_DATA_META_CACHE_MASK (META_MEM_GLOBAL_RANGE_BIT) +/* + * For non-VIVT SLCs the cacheability of the FW data in the SLC is selected in + * the PTEs for the FW data, not in the Meta Segment MMU, which means these + * defines have no real effect in those cases. + */ +#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_CACHED (0x0U) +#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_UNCACHED (0x60000000U) +#define ROGUE_FW_SEGMMU_DATA_VIVT_SLC_CACHE_MASK (0x60000000U) + +/* + ****************************************************************************** + * ROGUE FW Bootloader defaults + ****************************************************************************** + */ +#define ROGUE_FW_BOOTLDR_META_ADDR (0x40000000U) +#define ROGUE_FW_BOOTLDR_DEVV_ADDR_0 (0xC0000000U) +#define ROGUE_FW_BOOTLDR_DEVV_ADDR_1 (0x000000E1) +#define ROGUE_FW_BOOTLDR_DEVV_ADDR \ + ((((u64)ROGUE_FW_BOOTLDR_DEVV_ADDR_1) << 32) | \ + ROGUE_FW_BOOTLDR_DEVV_ADDR_0) +#define ROGUE_FW_BOOTLDR_LIMIT (0x1FFFF000) +#define ROGUE_FW_MAX_BOOTLDR_OFFSET (0x1000) + +/* Bootloader configuration offset is in dwords (512 bytes) */ +#define ROGUE_FW_BOOTLDR_CONF_OFFSET (0x80) + +/* + ****************************************************************************** + * ROGUE META Stack + ****************************************************************************** + */ +#define ROGUE_META_STACK_SIZE (0x1000U) + +/* + ****************************************************************************** + * ROGUE META Core memory + ****************************************************************************** + */ +/* Code and data both map to the same physical memory. */ +#define ROGUE_META_COREMEM_CODE_ADDR (0x80000000U) +#define ROGUE_META_COREMEM_DATA_ADDR (0x82000000U) +#define ROGUE_META_COREMEM_OFFSET_MASK (0x01ffffffU) + +#define ROGUE_META_IS_COREMEM_CODE(a, b) \ + ({ \ + u32 _a = (a), _b = (b); \ + ((_a) >= ROGUE_META_COREMEM_CODE_ADDR) && \ + ((_a) < (ROGUE_META_COREMEM_CODE_ADDR + (_b))); \ + }) +#define ROGUE_META_IS_COREMEM_DATA(a, b) \ + ({ \ + u32 _a = (a), _b = (b); \ + ((_a) >= ROGUE_META_COREMEM_DATA_ADDR) && \ + ((_a) < (ROGUE_META_COREMEM_DATA_ADDR + (_b))); \ + }) +/* + ****************************************************************************** + * 2nd thread + ****************************************************************************** + */ +#define ROGUE_FW_THR1_PC (0x18930000) +#define ROGUE_FW_THR1_SP (0x78890000) + +/* + ****************************************************************************** + * META compatibility + ****************************************************************************** + */ + +#define META_CR_CORE_ID (0x04831000) +#define META_CR_CORE_ID_VER_SHIFT (16U) +#define META_CR_CORE_ID_VER_CLRMSK (0XFF00FFFFU) + +#define ROGUE_CR_META_MTP218_CORE_ID_VALUE 0x19 +#define ROGUE_CR_META_MTP219_CORE_ID_VALUE 0x1E +#define ROGUE_CR_META_LTP218_CORE_ID_VALUE 0x1C +#define ROGUE_CR_META_LTP217_CORE_ID_VALUE 0x1F + +#define ROGUE_FW_PROCESSOR_META "META" + +#endif /* PVR_ROGUE_META_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_mips.h b/drivers/gpu/drm/imagination/pvr_rogue_mips.h new file mode 100644 index 000000000000..41ed618fda3f --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_mips.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_MIPS_H +#define PVR_ROGUE_MIPS_H + +#include <linux/bits.h> +#include <linux/types.h> + +/* Utility defines for memory management. */ +#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K (12) +#define ROGUE_MIPSFW_PAGE_SIZE_4K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K) +#define ROGUE_MIPSFW_PAGE_MASK_4K (ROGUE_MIPSFW_PAGE_SIZE_4K - 1) +#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K (16) +#define ROGUE_MIPSFW_PAGE_SIZE_64K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K) +#define ROGUE_MIPSFW_PAGE_MASK_64K (ROGUE_MIPSFW_PAGE_SIZE_64K - 1) +#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_256K (18) +#define ROGUE_MIPSFW_PAGE_SIZE_256K (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_256K) +#define ROGUE_MIPSFW_PAGE_MASK_256K (ROGUE_MIPSFW_PAGE_SIZE_256K - 1) +#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_1MB (20) +#define ROGUE_MIPSFW_PAGE_SIZE_1MB (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_1MB) +#define ROGUE_MIPSFW_PAGE_MASK_1MB (ROGUE_MIPSFW_PAGE_SIZE_1MB - 1) +#define ROGUE_MIPSFW_LOG2_PAGE_SIZE_4MB (22) +#define ROGUE_MIPSFW_PAGE_SIZE_4MB (0x1 << ROGUE_MIPSFW_LOG2_PAGE_SIZE_4MB) +#define ROGUE_MIPSFW_PAGE_MASK_4MB (ROGUE_MIPSFW_PAGE_SIZE_4MB - 1) +#define ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE (2) +/* log2 page table sizes dependent on FW heap size and page size (for each OS). */ +#define ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_4K(pvr_dev) ((pvr_dev)->fw_dev.fw_heap_info.log2_size - \ + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K + \ + ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE) +#define ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_64K(pvr_dev) ((pvr_dev)->fw_dev.fw_heap_info.log2_size - \ + ROGUE_MIPSFW_LOG2_PAGE_SIZE_64K + \ + ROGUE_MIPSFW_LOG2_PTE_ENTRY_SIZE) +/* Maximum number of page table pages (both Host and MIPS pages). */ +#define ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES (4) +/* Total number of TLB entries. */ +#define ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES (16) +/* "Uncached" caching policy. */ +#define ROGUE_MIPSFW_UNCACHED_CACHE_POLICY (2) +/* "Write-back write-allocate" caching policy. */ +#define ROGUE_MIPSFW_WRITEBACK_CACHE_POLICY (3) +/* "Write-through no write-allocate" caching policy. */ +#define ROGUE_MIPSFW_WRITETHROUGH_CACHE_POLICY (1) +/* Cached policy used by MIPS in case of physical bus on 32 bit. */ +#define ROGUE_MIPSFW_CACHED_POLICY (ROGUE_MIPSFW_WRITEBACK_CACHE_POLICY) +/* Cached policy used by MIPS in case of physical bus on more than 32 bit. */ +#define ROGUE_MIPSFW_CACHED_POLICY_ABOVE_32BIT (ROGUE_MIPSFW_WRITETHROUGH_CACHE_POLICY) +/* Total number of Remap entries. */ +#define ROGUE_MIPSFW_NUMBER_OF_REMAP_ENTRIES (2 * ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES) + +/* MIPS EntryLo/PTE format. */ + +#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_SHIFT (31U) +#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_CLRMSK (0X7FFFFFFF) +#define ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_EN (0X80000000) + +#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_SHIFT (30U) +#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_CLRMSK (0XBFFFFFFF) +#define ROGUE_MIPSFW_ENTRYLO_EXEC_INHIBIT_EN (0X40000000) + +/* Page Frame Number */ +#define ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT (6) +#define ROGUE_MIPSFW_ENTRYLO_PFN_ALIGNSHIFT (12) +/* Mask used for the MIPS Page Table in case of physical bus on 32 bit. */ +#define ROGUE_MIPSFW_ENTRYLO_PFN_MASK (0x03FFFFC0) +#define ROGUE_MIPSFW_ENTRYLO_PFN_SIZE (20) +/* Mask used for the MIPS Page Table in case of physical bus on more than 32 bit. */ +#define ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT (0x3FFFFFC0) +#define ROGUE_MIPSFW_ENTRYLO_PFN_SIZE_ABOVE_32BIT (24) +#define ROGUE_MIPSFW_ADDR_TO_ENTRYLO_PFN_RSHIFT (ROGUE_MIPSFW_ENTRYLO_PFN_ALIGNSHIFT - \ + ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT) + +#define ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT (3U) +#define ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_CLRMSK (0XFFFFFFC7) + +#define ROGUE_MIPSFW_ENTRYLO_DIRTY_SHIFT (2U) +#define ROGUE_MIPSFW_ENTRYLO_DIRTY_CLRMSK (0XFFFFFFFB) +#define ROGUE_MIPSFW_ENTRYLO_DIRTY_EN (0X00000004) + +#define ROGUE_MIPSFW_ENTRYLO_VALID_SHIFT (1U) +#define ROGUE_MIPSFW_ENTRYLO_VALID_CLRMSK (0XFFFFFFFD) +#define ROGUE_MIPSFW_ENTRYLO_VALID_EN (0X00000002) + +#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_SHIFT (0U) +#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_CLRMSK (0XFFFFFFFE) +#define ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN (0X00000001) + +#define ROGUE_MIPSFW_ENTRYLO_DVG (ROGUE_MIPSFW_ENTRYLO_DIRTY_EN | \ + ROGUE_MIPSFW_ENTRYLO_VALID_EN | \ + ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN) +#define ROGUE_MIPSFW_ENTRYLO_UNCACHED (ROGUE_MIPSFW_UNCACHED_CACHE_POLICY << \ + ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT) +#define ROGUE_MIPSFW_ENTRYLO_DVG_UNCACHED (ROGUE_MIPSFW_ENTRYLO_DVG | \ + ROGUE_MIPSFW_ENTRYLO_UNCACHED) + +/* Remap Range Config Addr Out. */ +/* These defines refer to the upper half of the Remap Range Config register. */ +#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_MASK (0x0FFFFFF0) +#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_SHIFT (4) /* wrt upper half of the register. */ +#define ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_ALIGNSHIFT (12) +#define ROGUE_MIPSFW_ADDR_TO_RR_ADDR_OUT_RSHIFT (ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_ALIGNSHIFT - \ + ROGUE_MIPSFW_REMAP_RANGE_ADDR_OUT_SHIFT) + +/* + * Pages to trampoline problematic physical addresses: + * - ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN : 0x1FC0_0000 + * - ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN : 0x1FC0_1000 + * - ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN : 0x1FC0_2000 + * - (benign trampoline) : 0x1FC0_3000 + * that would otherwise be erroneously remapped by the MIPS wrapper. + * (see "Firmware virtual layout and remap configuration" section below) + */ + +#define ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES (2) +#define ROGUE_MIPSFW_TRAMPOLINE_NUMPAGES BIT(ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES) +#define ROGUE_MIPSFW_TRAMPOLINE_SIZE (ROGUE_MIPSFW_TRAMPOLINE_NUMPAGES << \ + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K) +#define ROGUE_MIPSFW_TRAMPOLINE_LOG2_SEGMENT_SIZE (ROGUE_MIPSFW_TRAMPOLINE_LOG2_NUMPAGES + \ + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K) + +#define ROGUE_MIPSFW_TRAMPOLINE_TARGET_PHYS_ADDR (ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN) +#define ROGUE_MIPSFW_TRAMPOLINE_OFFSET(a) ((a) - ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN) + +#define ROGUE_MIPSFW_SENSITIVE_ADDR(a) (ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN == \ + (~((1 << ROGUE_MIPSFW_TRAMPOLINE_LOG2_SEGMENT_SIZE) - 1) \ + & (a))) + +/* Firmware virtual layout and remap configuration. */ +/* + * For each remap region we define: + * - the virtual base used by the Firmware to access code/data through that region + * - the microAptivAP physical address correspondent to the virtual base address, + * used as input address and remapped to the actual physical address + * - log2 of size of the region remapped by the MIPS wrapper, i.e. number of bits from + * the bottom of the base input address that survive onto the output address + * (this defines both the alignment and the maximum size of the remapped region) + * - one or more code/data segments within the remapped region. + */ + +/* Boot remap setup. */ +#define ROGUE_MIPSFW_BOOT_REMAP_VIRTUAL_BASE (0xBFC00000) +#define ROGUE_MIPSFW_BOOT_REMAP_PHYS_ADDR_IN (0x1FC00000) +#define ROGUE_MIPSFW_BOOT_REMAP_LOG2_SEGMENT_SIZE (12) +#define ROGUE_MIPSFW_BOOT_NMI_CODE_VIRTUAL_BASE (ROGUE_MIPSFW_BOOT_REMAP_VIRTUAL_BASE) + +/* Data remap setup. */ +#define ROGUE_MIPSFW_DATA_REMAP_VIRTUAL_BASE (0xBFC01000) +#define ROGUE_MIPSFW_DATA_CACHED_REMAP_VIRTUAL_BASE (0x9FC01000) +#define ROGUE_MIPSFW_DATA_REMAP_PHYS_ADDR_IN (0x1FC01000) +#define ROGUE_MIPSFW_DATA_REMAP_LOG2_SEGMENT_SIZE (12) +#define ROGUE_MIPSFW_BOOT_NMI_DATA_VIRTUAL_BASE (ROGUE_MIPSFW_DATA_REMAP_VIRTUAL_BASE) + +/* Code remap setup. */ +#define ROGUE_MIPSFW_CODE_REMAP_VIRTUAL_BASE (0x9FC02000) +#define ROGUE_MIPSFW_CODE_REMAP_PHYS_ADDR_IN (0x1FC02000) +#define ROGUE_MIPSFW_CODE_REMAP_LOG2_SEGMENT_SIZE (12) +#define ROGUE_MIPSFW_EXCEPTIONS_VIRTUAL_BASE (ROGUE_MIPSFW_CODE_REMAP_VIRTUAL_BASE) + +/* Permanent mappings setup. */ +#define ROGUE_MIPSFW_PT_VIRTUAL_BASE (0xCF000000) +#define ROGUE_MIPSFW_REGISTERS_VIRTUAL_BASE (0xCF800000) +#define ROGUE_MIPSFW_STACK_VIRTUAL_BASE (0xCF600000) + +/* Bootloader configuration data. */ +/* + * Bootloader configuration offset (where ROGUE_MIPSFW_BOOT_DATA lives) + * within the bootloader/NMI data page. + */ +#define ROGUE_MIPSFW_BOOTLDR_CONF_OFFSET (0x0) + +/* NMI shared data. */ +/* Base address of the shared data within the bootloader/NMI data page. */ +#define ROGUE_MIPSFW_NMI_SHARED_DATA_BASE (0x100) +/* Size used by Debug dump data. */ +#define ROGUE_MIPSFW_NMI_SHARED_SIZE (0x2B0) +/* Offsets in the NMI shared area in 32-bit words. */ +#define ROGUE_MIPSFW_NMI_SYNC_FLAG_OFFSET (0x0) +#define ROGUE_MIPSFW_NMI_STATE_OFFSET (0x1) +#define ROGUE_MIPSFW_NMI_ERROR_STATE_SET (0x1) + +/* MIPS boot stage. */ +#define ROGUE_MIPSFW_BOOT_STAGE_OFFSET (0x400) + +/* + * MIPS private data in the bootloader data page. + * Memory below this offset is used by the FW only, no interface data allowed. + */ +#define ROGUE_MIPSFW_PRIVATE_DATA_OFFSET (0x800) + +struct rogue_mipsfw_boot_data { + u64 stack_phys_addr; + u64 reg_base; + u64 pt_phys_addr[ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES]; + u32 pt_log2_page_size; + u32 pt_num_pages; + u32 reserved1; + u32 reserved2; +}; + +#define ROGUE_MIPSFW_GET_OFFSET_IN_DWORDS(offset) ((offset) / sizeof(u32)) +#define ROGUE_MIPSFW_GET_OFFSET_IN_QWORDS(offset) ((offset) / sizeof(u64)) + +/* Used for compatibility checks. */ +#define ROGUE_MIPSFW_ARCHTYPE_VER_CLRMSK (0xFFFFE3FFU) +#define ROGUE_MIPSFW_ARCHTYPE_VER_SHIFT (10U) +#define ROGUE_MIPSFW_CORE_ID_VALUE (0x001U) +#define ROGUE_FW_PROCESSOR_MIPS "MIPS" + +/* microAptivAP cache line size. */ +#define ROGUE_MIPSFW_MICROAPTIVEAP_CACHELINE_SIZE (16U) + +/* + * The SOCIF transactions are identified with the top 16 bits of the physical address emitted by + * the MIPS. + */ +#define ROGUE_MIPSFW_WRAPPER_CONFIG_REGBANK_ADDR_ALIGN (16U) + +/* Values to put in the MIPS selectors for performance counters. */ +/* Icache accesses in COUNTER0. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ICACHE_ACCESSES_C0 (9U) +/* Icache misses in COUNTER1. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ICACHE_MISSES_C1 (9U) + +/* Dcache accesses in COUNTER0. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_DCACHE_ACCESSES_C0 (10U) +/* Dcache misses in COUNTER1. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_DCACHE_MISSES_C1 (11U) + +/* ITLB instruction accesses in COUNTER0. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_ITLB_INSTR_ACCESSES_C0 (5U) +/* JTLB instruction accesses misses in COUNTER1. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_JTLB_INSTR_MISSES_C1 (7U) + + /* Instructions completed in COUNTER0. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_INSTR_COMPLETED_C0 (1U) +/* JTLB data misses in COUNTER1. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_JTLB_DATA_MISSES_C1 (8U) + +/* Shift for the Event field in the MIPS perf ctrl registers. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_EVENT_SHIFT (5U) + +/* Additional flags for performance counters. See MIPS manual for further reference. */ +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_USER_MODE (8U) +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_KERNEL_MODE (2U) +#define ROGUE_MIPSFW_PERF_COUNT_CTRL_COUNT_EXL (1U) + +#define ROGUE_MIPSFW_C0_NBHWIRQ 8 + +/* Macros to decode C0_Cause register. */ +#define ROGUE_MIPSFW_C0_CAUSE_EXCCODE(cause) (((cause) & 0x7c) >> 2) +#define ROGUE_MIPSFW_C0_CAUSE_EXCCODE_FWERROR 9 +/* Use only when Coprocessor Unusable exception. */ +#define ROGUE_MIPSFW_C0_CAUSE_UNUSABLE_UNIT(cause) (((cause) >> 28) & 0x3) +#define ROGUE_MIPSFW_C0_CAUSE_PENDING_HWIRQ(cause) (((cause) & 0x3fc00) >> 10) +#define ROGUE_MIPSFW_C0_CAUSE_FDCIPENDING BIT(21) +#define ROGUE_MIPSFW_C0_CAUSE_IV BIT(23) +#define ROGUE_MIPSFW_C0_CAUSE_IC BIT(25) +#define ROGUE_MIPSFW_C0_CAUSE_PCIPENDING BIT(26) +#define ROGUE_MIPSFW_C0_CAUSE_TIPENDING BIT(30) +#define ROGUE_MIPSFW_C0_CAUSE_BRANCH_DELAY BIT(31) + +/* Macros to decode C0_Debug register. */ +#define ROGUE_MIPSFW_C0_DEBUG_EXCCODE(debug) (((debug) >> 10) & 0x1f) +#define ROGUE_MIPSFW_C0_DEBUG_DSS BIT(0) +#define ROGUE_MIPSFW_C0_DEBUG_DBP BIT(1) +#define ROGUE_MIPSFW_C0_DEBUG_DDBL BIT(2) +#define ROGUE_MIPSFW_C0_DEBUG_DDBS BIT(3) +#define ROGUE_MIPSFW_C0_DEBUG_DIB BIT(4) +#define ROGUE_MIPSFW_C0_DEBUG_DINT BIT(5) +#define ROGUE_MIPSFW_C0_DEBUG_DIBIMPR BIT(6) +#define ROGUE_MIPSFW_C0_DEBUG_DDBLIMPR BIT(18) +#define ROGUE_MIPSFW_C0_DEBUG_DDBSIMPR BIT(19) +#define ROGUE_MIPSFW_C0_DEBUG_IEXI BIT(20) +#define ROGUE_MIPSFW_C0_DEBUG_DBUSEP BIT(21) +#define ROGUE_MIPSFW_C0_DEBUG_CACHEEP BIT(22) +#define ROGUE_MIPSFW_C0_DEBUG_MCHECKP BIT(23) +#define ROGUE_MIPSFW_C0_DEBUG_IBUSEP BIT(24) +#define ROGUE_MIPSFW_C0_DEBUG_DM BIT(30) +#define ROGUE_MIPSFW_C0_DEBUG_DBD BIT(31) + +/* Macros to decode TLB entries. */ +#define ROGUE_MIPSFW_TLB_GET_MASK(page_mask) (((page_mask) >> 13) & 0XFFFFU) +/* Page size in KB. */ +#define ROGUE_MIPSFW_TLB_GET_PAGE_SIZE(page_mask) ((((page_mask) | 0x1FFF) + 1) >> 11) +/* Page size in KB. */ +#define ROGUE_MIPSFW_TLB_GET_PAGE_MASK(page_size) ((((page_size) << 11) - 1) & ~0x7FF) +#define ROGUE_MIPSFW_TLB_GET_VPN2(entry_hi) ((entry_hi) >> 13) +#define ROGUE_MIPSFW_TLB_GET_COHERENCY(entry_lo) (((entry_lo) >> 3) & 0x7U) +#define ROGUE_MIPSFW_TLB_GET_PFN(entry_lo) (((entry_lo) >> 6) & 0XFFFFFU) +/* GET_PA uses a non-standard PFN mask for 36 bit addresses. */ +#define ROGUE_MIPSFW_TLB_GET_PA(entry_lo) (((u64)(entry_lo) & \ + ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT) << 6) +#define ROGUE_MIPSFW_TLB_GET_INHIBIT(entry_lo) (((entry_lo) >> 30) & 0x3U) +#define ROGUE_MIPSFW_TLB_GET_DGV(entry_lo) ((entry_lo) & 0x7U) +#define ROGUE_MIPSFW_TLB_GLOBAL BIT(0) +#define ROGUE_MIPSFW_TLB_VALID BIT(1) +#define ROGUE_MIPSFW_TLB_DIRTY BIT(2) +#define ROGUE_MIPSFW_TLB_XI BIT(30) +#define ROGUE_MIPSFW_TLB_RI BIT(31) + +#define ROGUE_MIPSFW_REMAP_GET_REGION_SIZE(region_size_encoding) (1 << (((region_size_encoding) \ + + 1) << 1)) + +struct rogue_mips_tlb_entry { + u32 tlb_page_mask; + u32 tlb_hi; + u32 tlb_lo0; + u32 tlb_lo1; +}; + +struct rogue_mips_remap_entry { + u32 remap_addr_in; /* Always 4k aligned. */ + u32 remap_addr_out; /* Always 4k aligned. */ + u32 remap_region_size; +}; + +struct rogue_mips_state { + u32 error_state; /* This must come first in the structure. */ + u32 error_epc; + u32 status_register; + u32 cause_register; + u32 bad_register; + u32 epc; + u32 sp; + u32 debug; + u32 depc; + u32 bad_instr; + u32 unmapped_address; + struct rogue_mips_tlb_entry tlb[ROGUE_MIPSFW_NUMBER_OF_TLB_ENTRIES]; + struct rogue_mips_remap_entry remap[ROGUE_MIPSFW_NUMBER_OF_REMAP_ENTRIES]; +}; + +#include "pvr_rogue_mips_check.h" + +#endif /* PVR_ROGUE_MIPS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h new file mode 100644 index 000000000000..824b4bf33ac1 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_ROGUE_MIPS_CHECK_H +#define PVR_ROGUE_MIPS_CHECK_H + +#include <linux/build_bug.h> + +static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) == 0, + "offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) incorrect"); +static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_hi) == 4, + "offsetof(struct rogue_mips_tlb_entry, tlb_hi) incorrect"); +static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo0) == 8, + "offsetof(struct rogue_mips_tlb_entry, tlb_lo0) incorrect"); +static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo1) == 12, + "offsetof(struct rogue_mips_tlb_entry, tlb_lo1) incorrect"); +static_assert(sizeof(struct rogue_mips_tlb_entry) == 16, + "struct rogue_mips_tlb_entry is incorrect size"); + +static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_in) == 0, + "offsetof(struct rogue_mips_remap_entry, remap_addr_in) incorrect"); +static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_out) == 4, + "offsetof(struct rogue_mips_remap_entry, remap_addr_out) incorrect"); +static_assert(offsetof(struct rogue_mips_remap_entry, remap_region_size) == 8, + "offsetof(struct rogue_mips_remap_entry, remap_region_size) incorrect"); +static_assert(sizeof(struct rogue_mips_remap_entry) == 12, + "struct rogue_mips_remap_entry is incorrect size"); + +static_assert(offsetof(struct rogue_mips_state, error_state) == 0, + "offsetof(struct rogue_mips_state, error_state) incorrect"); +static_assert(offsetof(struct rogue_mips_state, error_epc) == 4, + "offsetof(struct rogue_mips_state, error_epc) incorrect"); +static_assert(offsetof(struct rogue_mips_state, status_register) == 8, + "offsetof(struct rogue_mips_state, status_register) incorrect"); +static_assert(offsetof(struct rogue_mips_state, cause_register) == 12, + "offsetof(struct rogue_mips_state, cause_register) incorrect"); +static_assert(offsetof(struct rogue_mips_state, bad_register) == 16, + "offsetof(struct rogue_mips_state, bad_register) incorrect"); +static_assert(offsetof(struct rogue_mips_state, epc) == 20, + "offsetof(struct rogue_mips_state, epc) incorrect"); +static_assert(offsetof(struct rogue_mips_state, sp) == 24, + "offsetof(struct rogue_mips_state, sp) incorrect"); +static_assert(offsetof(struct rogue_mips_state, debug) == 28, + "offsetof(struct rogue_mips_state, debug) incorrect"); +static_assert(offsetof(struct rogue_mips_state, depc) == 32, + "offsetof(struct rogue_mips_state, depc) incorrect"); +static_assert(offsetof(struct rogue_mips_state, bad_instr) == 36, + "offsetof(struct rogue_mips_state, bad_instr) incorrect"); +static_assert(offsetof(struct rogue_mips_state, unmapped_address) == 40, + "offsetof(struct rogue_mips_state, unmapped_address) incorrect"); +static_assert(offsetof(struct rogue_mips_state, tlb) == 44, + "offsetof(struct rogue_mips_state, tlb) incorrect"); +static_assert(offsetof(struct rogue_mips_state, remap) == 300, + "offsetof(struct rogue_mips_state, remap) incorrect"); +static_assert(sizeof(struct rogue_mips_state) == 684, + "struct rogue_mips_state is incorrect size"); + +#endif /* PVR_ROGUE_MIPS_CHECK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_rogue_mmu_defs.h b/drivers/gpu/drm/imagination/pvr_rogue_mmu_defs.h new file mode 100644 index 000000000000..f361ccdd5405 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_rogue_mmu_defs.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +/* *** Autogenerated C -- do not edit *** */ + +#ifndef PVR_ROGUE_MMU_DEFS_H +#define PVR_ROGUE_MMU_DEFS_H + +#define ROGUE_MMU_DEFS_REVISION 0 + +#define ROGUE_BIF_DM_ENCODING_VERTEX (0x00000000U) +#define ROGUE_BIF_DM_ENCODING_PIXEL (0x00000001U) +#define ROGUE_BIF_DM_ENCODING_COMPUTE (0x00000002U) +#define ROGUE_BIF_DM_ENCODING_TLA (0x00000003U) +#define ROGUE_BIF_DM_ENCODING_PB_VCE (0x00000004U) +#define ROGUE_BIF_DM_ENCODING_PB_TE (0x00000005U) +#define ROGUE_BIF_DM_ENCODING_META (0x00000007U) +#define ROGUE_BIF_DM_ENCODING_HOST (0x00000008U) +#define ROGUE_BIF_DM_ENCODING_PM_ALIST (0x00000009U) + +#define ROGUE_MMUCTRL_VADDR_PC_INDEX_SHIFT (30U) +#define ROGUE_MMUCTRL_VADDR_PC_INDEX_CLRMSK (0xFFFFFF003FFFFFFFULL) +#define ROGUE_MMUCTRL_VADDR_PD_INDEX_SHIFT (21U) +#define ROGUE_MMUCTRL_VADDR_PD_INDEX_CLRMSK (0xFFFFFFFFC01FFFFFULL) +#define ROGUE_MMUCTRL_VADDR_PT_INDEX_SHIFT (12U) +#define ROGUE_MMUCTRL_VADDR_PT_INDEX_CLRMSK (0xFFFFFFFFFFE00FFFULL) + +#define ROGUE_MMUCTRL_ENTRIES_PC_VALUE (0x00000400U) +#define ROGUE_MMUCTRL_ENTRIES_PD_VALUE (0x00000200U) +#define ROGUE_MMUCTRL_ENTRIES_PT_VALUE (0x00000200U) + +#define ROGUE_MMUCTRL_ENTRY_SIZE_PC_VALUE (0x00000020U) +#define ROGUE_MMUCTRL_ENTRY_SIZE_PD_VALUE (0x00000040U) +#define ROGUE_MMUCTRL_ENTRY_SIZE_PT_VALUE (0x00000040U) + +#define ROGUE_MMUCTRL_PAGE_SIZE_MASK (0x00000007U) +#define ROGUE_MMUCTRL_PAGE_SIZE_4KB (0x00000000U) +#define ROGUE_MMUCTRL_PAGE_SIZE_16KB (0x00000001U) +#define ROGUE_MMUCTRL_PAGE_SIZE_64KB (0x00000002U) +#define ROGUE_MMUCTRL_PAGE_SIZE_256KB (0x00000003U) +#define ROGUE_MMUCTRL_PAGE_SIZE_1MB (0x00000004U) +#define ROGUE_MMUCTRL_PAGE_SIZE_2MB (0x00000005U) + +#define ROGUE_MMUCTRL_PAGE_4KB_RANGE_SHIFT (12U) +#define ROGUE_MMUCTRL_PAGE_4KB_RANGE_CLRMSK (0xFFFFFF0000000FFFULL) + +#define ROGUE_MMUCTRL_PAGE_16KB_RANGE_SHIFT (14U) +#define ROGUE_MMUCTRL_PAGE_16KB_RANGE_CLRMSK (0xFFFFFF0000003FFFULL) + +#define ROGUE_MMUCTRL_PAGE_64KB_RANGE_SHIFT (16U) +#define ROGUE_MMUCTRL_PAGE_64KB_RANGE_CLRMSK (0xFFFFFF000000FFFFULL) + +#define ROGUE_MMUCTRL_PAGE_256KB_RANGE_SHIFT (18U) +#define ROGUE_MMUCTRL_PAGE_256KB_RANGE_CLRMSK (0xFFFFFF000003FFFFULL) + +#define ROGUE_MMUCTRL_PAGE_1MB_RANGE_SHIFT (20U) +#define ROGUE_MMUCTRL_PAGE_1MB_RANGE_CLRMSK (0xFFFFFF00000FFFFFULL) + +#define ROGUE_MMUCTRL_PAGE_2MB_RANGE_SHIFT (21U) +#define ROGUE_MMUCTRL_PAGE_2MB_RANGE_CLRMSK (0xFFFFFF00001FFFFFULL) + +#define ROGUE_MMUCTRL_PT_BASE_4KB_RANGE_SHIFT (12U) +#define ROGUE_MMUCTRL_PT_BASE_4KB_RANGE_CLRMSK (0xFFFFFF0000000FFFULL) + +#define ROGUE_MMUCTRL_PT_BASE_16KB_RANGE_SHIFT (10U) +#define ROGUE_MMUCTRL_PT_BASE_16KB_RANGE_CLRMSK (0xFFFFFF00000003FFULL) + +#define ROGUE_MMUCTRL_PT_BASE_64KB_RANGE_SHIFT (8U) +#define ROGUE_MMUCTRL_PT_BASE_64KB_RANGE_CLRMSK (0xFFFFFF00000000FFULL) + +#define ROGUE_MMUCTRL_PT_BASE_256KB_RANGE_SHIFT (6U) +#define ROGUE_MMUCTRL_PT_BASE_256KB_RANGE_CLRMSK (0xFFFFFF000000003FULL) + +#define ROGUE_MMUCTRL_PT_BASE_1MB_RANGE_SHIFT (5U) +#define ROGUE_MMUCTRL_PT_BASE_1MB_RANGE_CLRMSK (0xFFFFFF000000001FULL) + +#define ROGUE_MMUCTRL_PT_BASE_2MB_RANGE_SHIFT (5U) +#define ROGUE_MMUCTRL_PT_BASE_2MB_RANGE_CLRMSK (0xFFFFFF000000001FULL) + +#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_SHIFT (62U) +#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_CLRMSK (0xBFFFFFFFFFFFFFFFULL) +#define ROGUE_MMUCTRL_PT_DATA_PM_META_PROTECT_EN (0x4000000000000000ULL) +#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_HI_SHIFT (40U) +#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_HI_CLRMSK (0xC00000FFFFFFFFFFULL) +#define ROGUE_MMUCTRL_PT_DATA_PAGE_SHIFT (12U) +#define ROGUE_MMUCTRL_PT_DATA_PAGE_CLRMSK (0xFFFFFF0000000FFFULL) +#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_LO_SHIFT (6U) +#define ROGUE_MMUCTRL_PT_DATA_VP_PAGE_LO_CLRMSK (0xFFFFFFFFFFFFF03FULL) +#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_SHIFT (5U) +#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFFFFFFFFFFDFULL) +#define ROGUE_MMUCTRL_PT_DATA_ENTRY_PENDING_EN (0x0000000000000020ULL) +#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_SHIFT (4U) +#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_CLRMSK (0xFFFFFFFFFFFFFFEFULL) +#define ROGUE_MMUCTRL_PT_DATA_PM_SRC_EN (0x0000000000000010ULL) +#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_SHIFT (3U) +#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_CLRMSK (0xFFFFFFFFFFFFFFF7ULL) +#define ROGUE_MMUCTRL_PT_DATA_SLC_BYPASS_CTRL_EN (0x0000000000000008ULL) +#define ROGUE_MMUCTRL_PT_DATA_CC_SHIFT (2U) +#define ROGUE_MMUCTRL_PT_DATA_CC_CLRMSK (0xFFFFFFFFFFFFFFFBULL) +#define ROGUE_MMUCTRL_PT_DATA_CC_EN (0x0000000000000004ULL) +#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_SHIFT (1U) +#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_CLRMSK (0xFFFFFFFFFFFFFFFDULL) +#define ROGUE_MMUCTRL_PT_DATA_READ_ONLY_EN (0x0000000000000002ULL) +#define ROGUE_MMUCTRL_PT_DATA_VALID_SHIFT (0U) +#define ROGUE_MMUCTRL_PT_DATA_VALID_CLRMSK (0xFFFFFFFFFFFFFFFEULL) +#define ROGUE_MMUCTRL_PT_DATA_VALID_EN (0x0000000000000001ULL) + +#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_SHIFT (40U) +#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFEFFFFFFFFFFULL) +#define ROGUE_MMUCTRL_PD_DATA_ENTRY_PENDING_EN (0x0000010000000000ULL) +#define ROGUE_MMUCTRL_PD_DATA_PT_BASE_SHIFT (5U) +#define ROGUE_MMUCTRL_PD_DATA_PT_BASE_CLRMSK (0xFFFFFF000000001FULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_SHIFT (1U) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_CLRMSK (0xFFFFFFFFFFFFFFF1ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_4KB (0x0000000000000000ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_16KB (0x0000000000000002ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_64KB (0x0000000000000004ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_256KB (0x0000000000000006ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_1MB (0x0000000000000008ULL) +#define ROGUE_MMUCTRL_PD_DATA_PAGE_SIZE_2MB (0x000000000000000aULL) +#define ROGUE_MMUCTRL_PD_DATA_VALID_SHIFT (0U) +#define ROGUE_MMUCTRL_PD_DATA_VALID_CLRMSK (0xFFFFFFFFFFFFFFFEULL) +#define ROGUE_MMUCTRL_PD_DATA_VALID_EN (0x0000000000000001ULL) + +#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_SHIFT (4U) +#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_CLRMSK (0x0000000FU) +#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSHIFT (12U) +#define ROGUE_MMUCTRL_PC_DATA_PD_BASE_ALIGNSIZE (4096U) +#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_SHIFT (1U) +#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_CLRMSK (0xFFFFFFFDU) +#define ROGUE_MMUCTRL_PC_DATA_ENTRY_PENDING_EN (0x00000002U) +#define ROGUE_MMUCTRL_PC_DATA_VALID_SHIFT (0U) +#define ROGUE_MMUCTRL_PC_DATA_VALID_CLRMSK (0xFFFFFFFEU) +#define ROGUE_MMUCTRL_PC_DATA_VALID_EN (0x00000001U) + +#endif /* PVR_ROGUE_MMU_DEFS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_stream.c b/drivers/gpu/drm/imagination/pvr_stream.c new file mode 100644 index 000000000000..975336a4facf --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_stream.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_rogue_fwif_stream.h" +#include "pvr_stream.h" + +#include <linux/align.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <uapi/drm/pvr_drm.h> + +static __always_inline bool +stream_def_is_supported(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def) +{ + if (stream_def->feature == PVR_FEATURE_NONE) + return true; + + if (!(stream_def->feature & PVR_FEATURE_NOT) && + pvr_device_has_feature(pvr_dev, stream_def->feature)) { + return true; + } + + if ((stream_def->feature & PVR_FEATURE_NOT) && + !pvr_device_has_feature(pvr_dev, stream_def->feature & ~PVR_FEATURE_NOT)) { + return true; + } + + return false; +} + +static int +pvr_stream_get_data(u8 *stream, u32 *stream_offset, u32 stream_size, u32 data_size, u32 align_size, + void *dest) +{ + *stream_offset = ALIGN(*stream_offset, align_size); + + if ((*stream_offset + data_size) > stream_size) + return -EINVAL; + + memcpy(dest, stream + *stream_offset, data_size); + + (*stream_offset) += data_size; + + return 0; +} + +/** + * pvr_stream_process_1() - Process a single stream and fill destination structure + * @pvr_dev: Device pointer. + * @stream_def: Stream definition. + * @nr_entries: Number of entries in &stream_def. + * @stream: Pointer to stream. + * @stream_offset: Starting offset within stream. + * @stream_size: Size of input stream, in bytes. + * @dest: Pointer to destination structure. + * @dest_size: Size of destination structure. + * @stream_offset_out: Pointer to variable to write updated stream offset to. May be NULL. + * + * Returns: + * * 0 on success, or + * * -%EINVAL on malformed stream. + */ +static int +pvr_stream_process_1(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def, + u32 nr_entries, u8 *stream, u32 stream_offset, u32 stream_size, + u8 *dest, u32 dest_size, u32 *stream_offset_out) +{ + int err = 0; + u32 i; + + for (i = 0; i < nr_entries; i++) { + if (stream_def[i].offset >= dest_size) { + err = -EINVAL; + break; + } + + if (!stream_def_is_supported(pvr_dev, &stream_def[i])) + continue; + + switch (stream_def[i].size) { + case PVR_STREAM_SIZE_8: + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u8), + sizeof(u8), dest + stream_def[i].offset); + if (err) + return err; + break; + + case PVR_STREAM_SIZE_16: + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u16), + sizeof(u16), dest + stream_def[i].offset); + if (err) + return err; + break; + + case PVR_STREAM_SIZE_32: + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32), + sizeof(u32), dest + stream_def[i].offset); + if (err) + return err; + break; + + case PVR_STREAM_SIZE_64: + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u64), + sizeof(u64), dest + stream_def[i].offset); + if (err) + return err; + break; + + case PVR_STREAM_SIZE_ARRAY: + err = pvr_stream_get_data(stream, &stream_offset, stream_size, + stream_def[i].array_size, sizeof(u64), + dest + stream_def[i].offset); + if (err) + return err; + break; + } + } + + if (stream_offset_out) + *stream_offset_out = stream_offset; + + return err; +} + +static int +pvr_stream_process_ext_stream(struct pvr_device *pvr_dev, + const struct pvr_stream_cmd_defs *cmd_defs, void *ext_stream, + u32 stream_offset, u32 ext_stream_size, void *dest) +{ + u32 musthave_masks[PVR_STREAM_EXTHDR_TYPE_MAX]; + u32 ext_header; + int err = 0; + u32 i; + + /* Copy "must have" mask from device. We clear this as we process the stream. */ + memcpy(musthave_masks, pvr_dev->stream_musthave_quirks[cmd_defs->type], + sizeof(musthave_masks)); + + do { + const struct pvr_stream_ext_header *header; + u32 type; + u32 data; + + err = pvr_stream_get_data(ext_stream, &stream_offset, ext_stream_size, sizeof(u32), + sizeof(ext_header), &ext_header); + if (err) + return err; + + type = (ext_header & PVR_STREAM_EXTHDR_TYPE_MASK) >> PVR_STREAM_EXTHDR_TYPE_SHIFT; + data = ext_header & PVR_STREAM_EXTHDR_DATA_MASK; + + if (type >= cmd_defs->ext_nr_headers) + return -EINVAL; + + header = &cmd_defs->ext_headers[type]; + if (data & ~header->valid_mask) + return -EINVAL; + + musthave_masks[type] &= ~data; + + for (i = 0; i < header->ext_streams_num; i++) { + const struct pvr_stream_ext_def *ext_def = &header->ext_streams[i]; + + if (!(ext_header & ext_def->header_mask)) + continue; + + if (!pvr_device_has_uapi_quirk(pvr_dev, ext_def->quirk)) + return -EINVAL; + + err = pvr_stream_process_1(pvr_dev, ext_def->stream, ext_def->stream_len, + ext_stream, stream_offset, + ext_stream_size, dest, + cmd_defs->dest_size, &stream_offset); + if (err) + return err; + } + } while (ext_header & PVR_STREAM_EXTHDR_CONTINUATION); + + /* + * Verify that "must have" mask is now zero. If it isn't then one of the "must have" quirks + * for this command was not present. + */ + for (i = 0; i < cmd_defs->ext_nr_headers; i++) { + if (musthave_masks[i]) + return -EINVAL; + } + + return 0; +} + +/** + * pvr_stream_process() - Build FW structure from stream + * @pvr_dev: Device pointer. + * @cmd_defs: Stream definition. + * @stream: Pointer to command stream. + * @stream_size: Size of command stream, in bytes. + * @dest_out: Pointer to destination buffer. + * + * Caller is responsible for freeing the output structure. + * + * Returns: + * * 0 on success, + * * -%ENOMEM on out of memory, or + * * -%EINVAL on malformed stream. + */ +int +pvr_stream_process(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs, + void *stream, u32 stream_size, void *dest_out) +{ + u32 stream_offset = 0; + u32 main_stream_len; + u32 padding; + int err; + + if (!stream || !stream_size) + return -EINVAL; + + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32), + sizeof(u32), &main_stream_len); + if (err) + return err; + + /* + * u32 after stream length is padding to ensure u64 alignment, but may be used for expansion + * in the future. Verify it's zero. + */ + err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32), + sizeof(u32), &padding); + if (err) + return err; + + if (main_stream_len < stream_offset || main_stream_len > stream_size || padding) + return -EINVAL; + + err = pvr_stream_process_1(pvr_dev, cmd_defs->main_stream, cmd_defs->main_stream_len, + stream, stream_offset, main_stream_len, dest_out, + cmd_defs->dest_size, &stream_offset); + if (err) + return err; + + if (stream_offset < stream_size) { + err = pvr_stream_process_ext_stream(pvr_dev, cmd_defs, stream, stream_offset, + stream_size, dest_out); + if (err) + return err; + } else { + u32 i; + + /* + * If we don't have an extension stream then there must not be any "must have" + * quirks for this command. + */ + for (i = 0; i < cmd_defs->ext_nr_headers; i++) { + if (pvr_dev->stream_musthave_quirks[cmd_defs->type][i]) + return -EINVAL; + } + } + + return 0; +} + +/** + * pvr_stream_create_musthave_masks() - Create "must have" masks for streams based on current device + * quirks + * @pvr_dev: Device pointer. + */ +void +pvr_stream_create_musthave_masks(struct pvr_device *pvr_dev) +{ + memset(pvr_dev->stream_musthave_quirks, 0, sizeof(pvr_dev->stream_musthave_quirks)); + + if (pvr_device_has_uapi_quirk(pvr_dev, 47217)) + pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |= + PVR_STREAM_EXTHDR_FRAG0_BRN47217; + + if (pvr_device_has_uapi_quirk(pvr_dev, 49927)) { + pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_GEOM][0] |= + PVR_STREAM_EXTHDR_GEOM0_BRN49927; + pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |= + PVR_STREAM_EXTHDR_FRAG0_BRN49927; + pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_COMPUTE][0] |= + PVR_STREAM_EXTHDR_COMPUTE0_BRN49927; + } +} diff --git a/drivers/gpu/drm/imagination/pvr_stream.h b/drivers/gpu/drm/imagination/pvr_stream.h new file mode 100644 index 000000000000..d92acb3a61d7 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_stream.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_STREAM_H +#define PVR_STREAM_H + +#include <linux/bits.h> +#include <linux/limits.h> +#include <linux/types.h> + +struct pvr_device; + +struct pvr_job; + +enum pvr_stream_type { + PVR_STREAM_TYPE_GEOM = 0, + PVR_STREAM_TYPE_FRAG, + PVR_STREAM_TYPE_COMPUTE, + PVR_STREAM_TYPE_TRANSFER, + PVR_STREAM_TYPE_STATIC_RENDER_CONTEXT, + PVR_STREAM_TYPE_STATIC_COMPUTE_CONTEXT, + + PVR_STREAM_TYPE_MAX +}; + +enum pvr_stream_size { + PVR_STREAM_SIZE_8 = 0, + PVR_STREAM_SIZE_16, + PVR_STREAM_SIZE_32, + PVR_STREAM_SIZE_64, + PVR_STREAM_SIZE_ARRAY, +}; + +#define PVR_FEATURE_NOT BIT(31) +#define PVR_FEATURE_NONE U32_MAX + +struct pvr_stream_def { + u32 offset; + enum pvr_stream_size size; + u32 array_size; + u32 feature; +}; + +struct pvr_stream_ext_def { + const struct pvr_stream_def *stream; + u32 stream_len; + u32 header_mask; + u32 quirk; +}; + +struct pvr_stream_ext_header { + const struct pvr_stream_ext_def *ext_streams; + u32 ext_streams_num; + u32 valid_mask; +}; + +struct pvr_stream_cmd_defs { + enum pvr_stream_type type; + + const struct pvr_stream_def *main_stream; + u32 main_stream_len; + + u32 ext_nr_headers; + const struct pvr_stream_ext_header *ext_headers; + + size_t dest_size; +}; + +int +pvr_stream_process(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs, + void *stream, u32 stream_size, void *dest_out); +void +pvr_stream_create_musthave_masks(struct pvr_device *pvr_dev); + +#endif /* PVR_STREAM_H */ diff --git a/drivers/gpu/drm/imagination/pvr_stream_defs.c b/drivers/gpu/drm/imagination/pvr_stream_defs.c new file mode 100644 index 000000000000..f8bd1a8c01db --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_stream_defs.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device_info.h" +#include "pvr_rogue_fwif_client.h" +#include "pvr_rogue_fwif_stream.h" +#include "pvr_stream.h" +#include "pvr_stream_defs.h" + +#include <linux/stddef.h> +#include <uapi/drm/pvr_drm.h> + +#define PVR_STREAM_DEF_SET(owner, member, _size, _array_size, _feature) \ + { .offset = offsetof(struct owner, member), \ + .size = (_size), \ + .array_size = (_array_size), \ + .feature = (_feature) } + +#define PVR_STREAM_DEF(owner, member, member_size) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, PVR_FEATURE_NONE) + +#define PVR_STREAM_DEF_FEATURE(owner, member, member_size, feature) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, feature) + +#define PVR_STREAM_DEF_NOT_FEATURE(owner, member, member_size, feature) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ ## member_size, 0, \ + (feature) | PVR_FEATURE_NOT) + +#define PVR_STREAM_DEF_ARRAY(owner, member) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \ + sizeof(((struct owner *)0)->member), PVR_FEATURE_NONE) + +#define PVR_STREAM_DEF_ARRAY_FEATURE(owner, member, feature) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \ + sizeof(((struct owner *)0)->member), feature) + +#define PVR_STREAM_DEF_ARRAY_NOT_FEATURE(owner, member, feature) \ + PVR_STREAM_DEF_SET(owner, member, PVR_STREAM_SIZE_ARRAY, \ + sizeof(((struct owner *)0)->member), (feature) | PVR_FEATURE_NOT) + +/* + * When adding new parameters to the stream definition, the new parameters must go after the + * existing parameters, to preserve order. As parameters are naturally aligned, care must be taken + * with respect to implicit padding in the stream; padding should be minimised as much as possible. + */ +static const struct pvr_stream_def rogue_fwif_cmd_geom_stream[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.vdm_ctrl_stream_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.tpu_border_colour_table, 64), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_draw_indirect0, 64, + PVR_FEATURE_VDM_DRAWINDIRECT), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_draw_indirect1, 32, + PVR_FEATURE_VDM_DRAWINDIRECT), + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.ppp_ctrl, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.te_psg, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.vdm_context_resume_task0_size, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.vdm_context_resume_task3_size, 32, + PVR_FEATURE_VDM_OBJECT_LEVEL_LLS), + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.view_idx, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_geom, regs.pds_coeff_free_prog, 32, + PVR_FEATURE_TESSELLATION), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_geom_stream_brn49927[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_geom, regs.tpu, 32), +}; + +static const struct pvr_stream_ext_def cmd_geom_ext_streams_0[] = { + { + .stream = rogue_fwif_cmd_geom_stream_brn49927, + .stream_len = ARRAY_SIZE(rogue_fwif_cmd_geom_stream_brn49927), + .header_mask = PVR_STREAM_EXTHDR_GEOM0_BRN49927, + .quirk = 49927, + }, +}; + +static const struct pvr_stream_ext_header cmd_geom_ext_headers[] = { + { + .ext_streams = cmd_geom_ext_streams_0, + .ext_streams_num = ARRAY_SIZE(cmd_geom_ext_streams_0), + .valid_mask = PVR_STREAM_EXTHDR_GEOM0_VALID, + }, +}; + +const struct pvr_stream_cmd_defs pvr_cmd_geom_stream = { + .type = PVR_STREAM_TYPE_GEOM, + + .main_stream = rogue_fwif_cmd_geom_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_geom_stream), + + .ext_nr_headers = ARRAY_SIZE(cmd_geom_ext_headers), + .ext_headers = cmd_geom_ext_headers, + + .dest_size = sizeof(struct rogue_fwif_cmd_geom), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_frag_stream[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_scissor_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_dbias_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_oclqry_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_zlsctl, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_zload_store_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_stencil_load_store_base, 64), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.fb_cdc_zls, 64, + PVR_FEATURE_REQUIRES_FB_CDC_ZLS_SETUP), + PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pbe_word), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.tpu_border_colour_table, 64), + PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pds_bgnd), + PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.pds_pr_bgnd), + PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_frag, regs.usc_clear_register), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.usc_pixel_output_ctrl, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_bgobjdepth, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_bgobjvals, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_aa, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_xtp_pipe_enable, 32, + PVR_FEATURE_S7_TOP_INFRASTRUCTURE), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_ctl, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.event_pixel_pds_info, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.pixel_phantom, 32, + PVR_FEATURE_CLUSTER_GROUPING), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.view_idx, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.event_pixel_pds_data, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_oclqry_stride, 32, + PVR_FEATURE_GPU_MULTICORE_SUPPORT), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.isp_zls_pixels, 32, + PVR_FEATURE_ZLS_SUBTILE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, regs.rgx_cr_blackpearl_fix, 32, + PVR_FEATURE_ISP_ZLS_D24_S8_PACKING_OGL_MODE), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, zls_stride, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_frag, sls_stride, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_frag, execute_count, 32, + PVR_FEATURE_GPU_MULTICORE_SUPPORT), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_frag_stream_brn47217[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.isp_oclqry_stride, 32), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_frag_stream_brn49927[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_frag, regs.tpu, 32), +}; + +static const struct pvr_stream_ext_def cmd_frag_ext_streams_0[] = { + { + .stream = rogue_fwif_cmd_frag_stream_brn47217, + .stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream_brn47217), + .header_mask = PVR_STREAM_EXTHDR_FRAG0_BRN47217, + .quirk = 47217, + }, + { + .stream = rogue_fwif_cmd_frag_stream_brn49927, + .stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream_brn49927), + .header_mask = PVR_STREAM_EXTHDR_FRAG0_BRN49927, + .quirk = 49927, + }, +}; + +static const struct pvr_stream_ext_header cmd_frag_ext_headers[] = { + { + .ext_streams = cmd_frag_ext_streams_0, + .ext_streams_num = ARRAY_SIZE(cmd_frag_ext_streams_0), + .valid_mask = PVR_STREAM_EXTHDR_FRAG0_VALID, + }, +}; + +const struct pvr_stream_cmd_defs pvr_cmd_frag_stream = { + .type = PVR_STREAM_TYPE_FRAG, + + .main_stream = rogue_fwif_cmd_frag_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_frag_stream), + + .ext_nr_headers = ARRAY_SIZE(cmd_frag_ext_headers), + .ext_headers = cmd_frag_ext_headers, + + .dest_size = sizeof(struct rogue_fwif_cmd_frag), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_compute_stream[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.tpu_border_colour_table, 64), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb_queue, 64, + PVR_FEATURE_CDM_USER_MODE_QUEUE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb_base, 64, + PVR_FEATURE_CDM_USER_MODE_QUEUE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_cb, 64, + PVR_FEATURE_CDM_USER_MODE_QUEUE), + PVR_STREAM_DEF_NOT_FEATURE(rogue_fwif_cmd_compute, regs.cdm_ctrl_stream_base, 64, + PVR_FEATURE_CDM_USER_MODE_QUEUE), + PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.cdm_context_state_base_addr, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.cdm_resume_pds1, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.cdm_item, 32, + PVR_FEATURE_COMPUTE_MORTON_CAPABLE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.compute_cluster, 32, + PVR_FEATURE_CLUSTER_GROUPING), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, regs.tpu_tag_cdm_ctrl, 32, + PVR_FEATURE_TPU_DM_GLOBAL_REGISTERS), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, stream_start_offset, 32, + PVR_FEATURE_CDM_USER_MODE_QUEUE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_compute, execute_count, 32, + PVR_FEATURE_GPU_MULTICORE_SUPPORT), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_compute_stream_brn49927[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_compute, regs.tpu, 32), +}; + +static const struct pvr_stream_ext_def cmd_compute_ext_streams_0[] = { + { + .stream = rogue_fwif_cmd_compute_stream_brn49927, + .stream_len = ARRAY_SIZE(rogue_fwif_cmd_compute_stream_brn49927), + .header_mask = PVR_STREAM_EXTHDR_COMPUTE0_BRN49927, + .quirk = 49927, + }, +}; + +static const struct pvr_stream_ext_header cmd_compute_ext_headers[] = { + { + .ext_streams = cmd_compute_ext_streams_0, + .ext_streams_num = ARRAY_SIZE(cmd_compute_ext_streams_0), + .valid_mask = PVR_STREAM_EXTHDR_COMPUTE0_VALID, + }, +}; + +const struct pvr_stream_cmd_defs pvr_cmd_compute_stream = { + .type = PVR_STREAM_TYPE_COMPUTE, + + .main_stream = rogue_fwif_cmd_compute_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_compute_stream), + + .ext_nr_headers = ARRAY_SIZE(cmd_compute_ext_headers), + .ext_headers = cmd_compute_ext_headers, + + .dest_size = sizeof(struct rogue_fwif_cmd_compute), +}; + +static const struct pvr_stream_def rogue_fwif_cmd_transfer_stream[] = { + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd0_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd1_base, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.pds_bgnd3_sizeinfo, 64), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_mtile_base, 64), + PVR_STREAM_DEF_ARRAY(rogue_fwif_cmd_transfer, regs.pbe_wordx_mrty), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_bgobjvals, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_pixel_output_ctrl, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register0, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register1, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register2, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.usc_clear_register3, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_mtile_size, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_render_origin, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_ctl, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_aa, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_info, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_code, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.event_pixel_pds_data, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_render, 32), + PVR_STREAM_DEF(rogue_fwif_cmd_transfer, regs.isp_rgn, 32), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_transfer, regs.isp_xtp_pipe_enable, 32, + PVR_FEATURE_S7_TOP_INFRASTRUCTURE), + PVR_STREAM_DEF_FEATURE(rogue_fwif_cmd_transfer, regs.frag_screen, 32, + PVR_FEATURE_GPU_MULTICORE_SUPPORT), +}; + +const struct pvr_stream_cmd_defs pvr_cmd_transfer_stream = { + .type = PVR_STREAM_TYPE_TRANSFER, + + .main_stream = rogue_fwif_cmd_transfer_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_cmd_transfer_stream), + + .ext_nr_headers = 0, + + .dest_size = sizeof(struct rogue_fwif_cmd_transfer), +}; + +static const struct pvr_stream_def rogue_fwif_static_render_context_state_stream[] = { + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_reg_vdm_context_state_base_addr, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_reg_vdm_context_state_resume_addr, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_reg_ta_context_state_base_addr, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task0, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task1, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task2, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task3, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_store_task4, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task0, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task1, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task2, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task3, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[0].geom_reg_vdm_context_resume_task4, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task0, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task1, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task2, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task3, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_store_task4, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task0, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task1, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task2, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task3, 64), + PVR_STREAM_DEF(rogue_fwif_geom_registers_caswitch, + geom_state[1].geom_reg_vdm_context_resume_task4, 64), +}; + +const struct pvr_stream_cmd_defs pvr_static_render_context_state_stream = { + .type = PVR_STREAM_TYPE_STATIC_RENDER_CONTEXT, + + .main_stream = rogue_fwif_static_render_context_state_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_static_render_context_state_stream), + + .ext_nr_headers = 0, + + .dest_size = sizeof(struct rogue_fwif_geom_registers_caswitch), +}; + +static const struct pvr_stream_def rogue_fwif_static_compute_context_state_stream[] = { + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds1, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_terminate_pds1, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_context_pds0_b, 64), + PVR_STREAM_DEF(rogue_fwif_cdm_registers_cswitch, cdmreg_cdm_resume_pds0_b, 64), +}; + +const struct pvr_stream_cmd_defs pvr_static_compute_context_state_stream = { + .type = PVR_STREAM_TYPE_STATIC_COMPUTE_CONTEXT, + + .main_stream = rogue_fwif_static_compute_context_state_stream, + .main_stream_len = ARRAY_SIZE(rogue_fwif_static_compute_context_state_stream), + + .ext_nr_headers = 0, + + .dest_size = sizeof(struct rogue_fwif_cdm_registers_cswitch), +}; diff --git a/drivers/gpu/drm/imagination/pvr_stream_defs.h b/drivers/gpu/drm/imagination/pvr_stream_defs.h new file mode 100644 index 000000000000..f33b82165833 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_stream_defs.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_STREAM_DEFS_H +#define PVR_STREAM_DEFS_H + +#include "pvr_stream.h" + +extern const struct pvr_stream_cmd_defs pvr_cmd_geom_stream; +extern const struct pvr_stream_cmd_defs pvr_cmd_frag_stream; +extern const struct pvr_stream_cmd_defs pvr_cmd_compute_stream; +extern const struct pvr_stream_cmd_defs pvr_cmd_transfer_stream; +extern const struct pvr_stream_cmd_defs pvr_static_render_context_state_stream; +extern const struct pvr_stream_cmd_defs pvr_static_compute_context_state_stream; + +#endif /* PVR_STREAM_DEFS_H */ diff --git a/drivers/gpu/drm/imagination/pvr_sync.c b/drivers/gpu/drm/imagination/pvr_sync.c new file mode 100644 index 000000000000..129f646d14ba --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_sync.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include <uapi/drm/pvr_drm.h> + +#include <drm/drm_syncobj.h> +#include <drm/gpu_scheduler.h> +#include <linux/xarray.h> +#include <linux/dma-fence-unwrap.h> + +#include "pvr_device.h" +#include "pvr_queue.h" +#include "pvr_sync.h" + +static int +pvr_check_sync_op(const struct drm_pvr_sync_op *sync_op) +{ + u8 handle_type; + + if (sync_op->flags & ~DRM_PVR_SYNC_OP_FLAGS_MASK) + return -EINVAL; + + handle_type = sync_op->flags & DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_MASK; + if (handle_type != DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ && + handle_type != DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_TIMELINE_SYNCOBJ) + return -EINVAL; + + if (handle_type == DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ && + sync_op->value != 0) + return -EINVAL; + + return 0; +} + +static void +pvr_sync_signal_free(struct pvr_sync_signal *sig_sync) +{ + if (!sig_sync) + return; + + drm_syncobj_put(sig_sync->syncobj); + dma_fence_chain_free(sig_sync->chain); + dma_fence_put(sig_sync->fence); + kfree(sig_sync); +} + +void +pvr_sync_signal_array_cleanup(struct xarray *array) +{ + struct pvr_sync_signal *sig_sync; + unsigned long i; + + xa_for_each(array, i, sig_sync) + pvr_sync_signal_free(sig_sync); + + xa_destroy(array); +} + +static struct pvr_sync_signal * +pvr_sync_signal_array_add(struct xarray *array, struct drm_file *file, u32 handle, u64 point) +{ + struct pvr_sync_signal *sig_sync; + struct dma_fence *cur_fence; + int err; + u32 id; + + sig_sync = kzalloc(sizeof(*sig_sync), GFP_KERNEL); + if (!sig_sync) + return ERR_PTR(-ENOMEM); + + sig_sync->handle = handle; + sig_sync->point = point; + + if (point > 0) { + sig_sync->chain = dma_fence_chain_alloc(); + if (!sig_sync->chain) { + err = -ENOMEM; + goto err_free_sig_sync; + } + } + + sig_sync->syncobj = drm_syncobj_find(file, handle); + if (!sig_sync->syncobj) { + err = -EINVAL; + goto err_free_sig_sync; + } + + /* Retrieve the current fence attached to that point. It's + * perfectly fine to get a NULL fence here, it just means there's + * no fence attached to that point yet. + */ + if (!drm_syncobj_find_fence(file, handle, point, 0, &cur_fence)) + sig_sync->fence = cur_fence; + + err = xa_alloc(array, &id, sig_sync, xa_limit_32b, GFP_KERNEL); + if (err) + goto err_free_sig_sync; + + return sig_sync; + +err_free_sig_sync: + pvr_sync_signal_free(sig_sync); + return ERR_PTR(err); +} + +static struct pvr_sync_signal * +pvr_sync_signal_array_search(struct xarray *array, u32 handle, u64 point) +{ + struct pvr_sync_signal *sig_sync; + unsigned long i; + + xa_for_each(array, i, sig_sync) { + if (handle == sig_sync->handle && point == sig_sync->point) + return sig_sync; + } + + return NULL; +} + +static struct pvr_sync_signal * +pvr_sync_signal_array_get(struct xarray *array, struct drm_file *file, u32 handle, u64 point) +{ + struct pvr_sync_signal *sig_sync; + + sig_sync = pvr_sync_signal_array_search(array, handle, point); + if (sig_sync) + return sig_sync; + + return pvr_sync_signal_array_add(array, file, handle, point); +} + +int +pvr_sync_signal_array_collect_ops(struct xarray *array, + struct drm_file *file, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops) +{ + for (u32 i = 0; i < sync_op_count; i++) { + struct pvr_sync_signal *sig_sync; + int ret; + + if (!(sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL)) + continue; + + ret = pvr_check_sync_op(&sync_ops[i]); + if (ret) + return ret; + + sig_sync = pvr_sync_signal_array_get(array, file, + sync_ops[i].handle, + sync_ops[i].value); + if (IS_ERR(sig_sync)) + return PTR_ERR(sig_sync); + } + + return 0; +} + +int +pvr_sync_signal_array_update_fences(struct xarray *array, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops, + struct dma_fence *done_fence) +{ + for (u32 i = 0; i < sync_op_count; i++) { + struct dma_fence *old_fence; + struct pvr_sync_signal *sig_sync; + + if (!(sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL)) + continue; + + sig_sync = pvr_sync_signal_array_search(array, sync_ops[i].handle, + sync_ops[i].value); + if (WARN_ON(!sig_sync)) + return -EINVAL; + + old_fence = sig_sync->fence; + sig_sync->fence = dma_fence_get(done_fence); + dma_fence_put(old_fence); + + if (WARN_ON(!sig_sync->fence)) + return -EINVAL; + } + + return 0; +} + +void +pvr_sync_signal_array_push_fences(struct xarray *array) +{ + struct pvr_sync_signal *sig_sync; + unsigned long i; + + xa_for_each(array, i, sig_sync) { + if (sig_sync->chain) { + drm_syncobj_add_point(sig_sync->syncobj, sig_sync->chain, + sig_sync->fence, sig_sync->point); + sig_sync->chain = NULL; + } else { + drm_syncobj_replace_fence(sig_sync->syncobj, sig_sync->fence); + } + } +} + +static int +pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f) +{ + struct dma_fence_unwrap iter; + u32 native_fence_count = 0; + struct dma_fence *uf; + int err = 0; + + dma_fence_unwrap_for_each(uf, &iter, f) { + if (pvr_queue_fence_is_ufo_backed(uf)) + native_fence_count++; + } + + /* No need to unwrap the fence if it's fully non-native. */ + if (!native_fence_count) + return drm_sched_job_add_dependency(job, f); + + dma_fence_unwrap_for_each(uf, &iter, f) { + /* There's no dma_fence_unwrap_stop() helper cleaning up the refs + * owned by dma_fence_unwrap(), so let's just iterate over all + * entries without doing anything when something failed. + */ + if (err) + continue; + + if (pvr_queue_fence_is_ufo_backed(uf)) { + struct drm_sched_fence *s_fence = to_drm_sched_fence(uf); + + /* If this is a native dependency, we wait for the scheduled fence, + * and we will let pvr_queue_run_job() issue FW waits. + */ + err = drm_sched_job_add_dependency(job, + dma_fence_get(&s_fence->scheduled)); + } else { + err = drm_sched_job_add_dependency(job, dma_fence_get(uf)); + } + } + + dma_fence_put(f); + return err; +} + +int +pvr_sync_add_deps_to_job(struct pvr_file *pvr_file, struct drm_sched_job *job, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops, + struct xarray *signal_array) +{ + int err = 0; + + if (!sync_op_count) + return 0; + + for (u32 i = 0; i < sync_op_count; i++) { + struct pvr_sync_signal *sig_sync; + struct dma_fence *fence; + + if (sync_ops[i].flags & DRM_PVR_SYNC_OP_FLAG_SIGNAL) + continue; + + err = pvr_check_sync_op(&sync_ops[i]); + if (err) + return err; + + sig_sync = pvr_sync_signal_array_search(signal_array, sync_ops[i].handle, + sync_ops[i].value); + if (sig_sync) { + if (WARN_ON(!sig_sync->fence)) + return -EINVAL; + + fence = dma_fence_get(sig_sync->fence); + } else { + err = drm_syncobj_find_fence(from_pvr_file(pvr_file), sync_ops[i].handle, + sync_ops[i].value, 0, &fence); + if (err) + return err; + } + + err = pvr_sync_add_dep_to_job(job, fence); + if (err) + return err; + } + + return 0; +} diff --git a/drivers/gpu/drm/imagination/pvr_sync.h b/drivers/gpu/drm/imagination/pvr_sync.h new file mode 100644 index 000000000000..db6ccfda104a --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_sync.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_SYNC_H +#define PVR_SYNC_H + +#include <uapi/drm/pvr_drm.h> + +/* Forward declaration from <linux/xarray.h>. */ +struct xarray; + +/* Forward declaration from <drm/drm_file.h>. */ +struct drm_file; + +/* Forward declaration from <drm/gpu_scheduler.h>. */ +struct drm_sched_job; + +/* Forward declaration from "pvr_device.h". */ +struct pvr_file; + +/** + * struct pvr_sync_signal - Object encoding a syncobj signal operation + * + * The job submission logic collects all signal operations in an array of + * pvr_sync_signal objects. This array also serves as a cache to get the + * latest dma_fence when multiple jobs are submitted at once, and one job + * signals a syncobj point that's later waited on by a subsequent job. + */ +struct pvr_sync_signal { + /** @handle: Handle of the syncobj to signal. */ + u32 handle; + + /** + * @point: Point to signal in the syncobj. + * + * Only relevant for timeline syncobjs. + */ + u64 point; + + /** @syncobj: Syncobj retrieved from the handle. */ + struct drm_syncobj *syncobj; + + /** + * @chain: Chain object used to link the new fence with the + * existing timeline syncobj. + * + * Should be zero when manipulating a regular syncobj. + */ + struct dma_fence_chain *chain; + + /** + * @fence: New fence object to attach to the syncobj. + * + * This pointer starts with the current fence bound to + * the <handle,point> pair. + */ + struct dma_fence *fence; +}; + +void +pvr_sync_signal_array_cleanup(struct xarray *array); + +int +pvr_sync_signal_array_collect_ops(struct xarray *array, + struct drm_file *file, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops); + +int +pvr_sync_signal_array_update_fences(struct xarray *array, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops, + struct dma_fence *done_fence); + +void +pvr_sync_signal_array_push_fences(struct xarray *array); + +int +pvr_sync_add_deps_to_job(struct pvr_file *pvr_file, struct drm_sched_job *job, + u32 sync_op_count, + const struct drm_pvr_sync_op *sync_ops, + struct xarray *signal_array); + +#endif /* PVR_SYNC_H */ diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c new file mode 100644 index 000000000000..3ad1366294b9 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -0,0 +1,1107 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_vm.h" + +#include "pvr_device.h" +#include "pvr_drv.h" +#include "pvr_gem.h" +#include "pvr_mmu.h" +#include "pvr_rogue_fwif.h" +#include "pvr_rogue_heap_config.h" + +#include <drm/drm_exec.h> +#include <drm/drm_gem.h> +#include <drm/drm_gpuvm.h> + +#include <linux/container_of.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gfp_types.h> +#include <linux/kref.h> +#include <linux/mutex.h> +#include <linux/stddef.h> + +/** + * DOC: Memory context + * + * This is the "top level" datatype in the VM code. It's exposed in the public + * API as an opaque handle. + */ + +/** + * struct pvr_vm_context - Context type used to represent a single VM. + */ +struct pvr_vm_context { + /** + * @pvr_dev: The PowerVR device to which this context is bound. + * This binding is immutable for the life of the context. + */ + struct pvr_device *pvr_dev; + + /** @mmu_ctx: The context for binding to physical memory. */ + struct pvr_mmu_context *mmu_ctx; + + /** @gpuva_mgr: GPUVA manager object associated with this context. */ + struct drm_gpuvm gpuvm_mgr; + + /** @lock: Global lock on this VM. */ + struct mutex lock; + + /** + * @fw_mem_ctx_obj: Firmware object representing firmware memory + * context. + */ + struct pvr_fw_object *fw_mem_ctx_obj; + + /** @ref_count: Reference count of object. */ + struct kref ref_count; + + /** + * @dummy_gem: GEM object to enable VM reservation. All private BOs + * should use the @dummy_gem.resv and not their own _resv field. + */ + struct drm_gem_object dummy_gem; +}; + +struct pvr_vm_context *pvr_vm_context_get(struct pvr_vm_context *vm_ctx) +{ + if (vm_ctx) + kref_get(&vm_ctx->ref_count); + + return vm_ctx; +} + +/** + * pvr_vm_get_page_table_root_addr() - Get the DMA address of the root of the + * page table structure behind a VM context. + * @vm_ctx: Target VM context. + */ +dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx) +{ + return pvr_mmu_get_root_table_dma_addr(vm_ctx->mmu_ctx); +} + +/** + * pvr_vm_get_dma_resv() - Expose the dma_resv owned by the VM context. + * @vm_ctx: Target VM context. + * + * This is used to allow private BOs to share a dma_resv for faster fence + * updates. + * + * Returns: The dma_resv pointer. + */ +struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx) +{ + return vm_ctx->dummy_gem.resv; +} + +/** + * DOC: Memory mappings + */ + +/** + * struct pvr_vm_gpuva - Wrapper type representing a single VM mapping. + */ +struct pvr_vm_gpuva { + /** @base: The wrapped drm_gpuva object. */ + struct drm_gpuva base; +}; + +static __always_inline +struct pvr_vm_gpuva *to_pvr_vm_gpuva(struct drm_gpuva *gpuva) +{ + return container_of(gpuva, struct pvr_vm_gpuva, base); +} + +enum pvr_vm_bind_type { + PVR_VM_BIND_TYPE_MAP, + PVR_VM_BIND_TYPE_UNMAP, +}; + +/** + * struct pvr_vm_bind_op - Context of a map/unmap operation. + */ +struct pvr_vm_bind_op { + /** @type: Map or unmap. */ + enum pvr_vm_bind_type type; + + /** @pvr_obj: Object associated with mapping (map only). */ + struct pvr_gem_object *pvr_obj; + + /** + * @vm_ctx: VM context where the mapping will be created or destroyed. + */ + struct pvr_vm_context *vm_ctx; + + /** @mmu_op_ctx: MMU op context. */ + struct pvr_mmu_op_context *mmu_op_ctx; + + /** @gpuvm_bo: Prealloced wrapped BO for attaching to the gpuvm. */ + struct drm_gpuvm_bo *gpuvm_bo; + + /** + * @new_va: Prealloced VA mapping object (init in callback). + * Used when creating a mapping. + */ + struct pvr_vm_gpuva *new_va; + + /** + * @prev_va: Prealloced VA mapping object (init in callback). + * Used when a mapping or unmapping operation overlaps an existing + * mapping and splits away the beginning into a new mapping. + */ + struct pvr_vm_gpuva *prev_va; + + /** + * @next_va: Prealloced VA mapping object (init in callback). + * Used when a mapping or unmapping operation overlaps an existing + * mapping and splits away the end into a new mapping. + */ + struct pvr_vm_gpuva *next_va; + + /** @offset: Offset into @pvr_obj to begin mapping from. */ + u64 offset; + + /** @device_addr: Device-virtual address at the start of the mapping. */ + u64 device_addr; + + /** @size: Size of the desired mapping. */ + u64 size; +}; + +/** + * pvr_vm_bind_op_exec() - Execute a single bind op. + * @bind_op: Bind op context. + * + * Returns: + * * 0 on success, + * * Any error code returned by drm_gpuva_sm_map(), drm_gpuva_sm_unmap(), or + * a callback function. + */ +static int pvr_vm_bind_op_exec(struct pvr_vm_bind_op *bind_op) +{ + switch (bind_op->type) { + case PVR_VM_BIND_TYPE_MAP: + return drm_gpuvm_sm_map(&bind_op->vm_ctx->gpuvm_mgr, + bind_op, bind_op->device_addr, + bind_op->size, + gem_from_pvr_gem(bind_op->pvr_obj), + bind_op->offset); + + case PVR_VM_BIND_TYPE_UNMAP: + return drm_gpuvm_sm_unmap(&bind_op->vm_ctx->gpuvm_mgr, + bind_op, bind_op->device_addr, + bind_op->size); + } + + /* + * This shouldn't happen unless something went wrong + * in drm_sched. + */ + WARN_ON(1); + return -EINVAL; +} + +static void pvr_vm_bind_op_fini(struct pvr_vm_bind_op *bind_op) +{ + drm_gpuvm_bo_put(bind_op->gpuvm_bo); + + kfree(bind_op->new_va); + kfree(bind_op->prev_va); + kfree(bind_op->next_va); + + if (bind_op->pvr_obj) + pvr_gem_object_put(bind_op->pvr_obj); + + if (bind_op->mmu_op_ctx) + pvr_mmu_op_context_destroy(bind_op->mmu_op_ctx); +} + +static int +pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op, + struct pvr_vm_context *vm_ctx, + struct pvr_gem_object *pvr_obj, u64 offset, + u64 device_addr, u64 size) +{ + const bool is_user = vm_ctx == vm_ctx->pvr_dev->kernel_vm_ctx; + const u64 pvr_obj_size = pvr_gem_object_size(pvr_obj); + struct sg_table *sgt; + u64 offset_plus_size; + int err; + + if (check_add_overflow(offset, size, &offset_plus_size)) + return -EINVAL; + + if (is_user && + !pvr_find_heap_containing(vm_ctx->pvr_dev, device_addr, size)) { + return -EINVAL; + } + + if (!pvr_device_addr_and_size_are_valid(device_addr, size) || + offset & ~PAGE_MASK || size & ~PAGE_MASK || + offset >= pvr_obj_size || offset_plus_size > pvr_obj_size) + return -EINVAL; + + bind_op->type = PVR_VM_BIND_TYPE_MAP; + + bind_op->gpuvm_bo = drm_gpuvm_bo_create(&vm_ctx->gpuvm_mgr, + gem_from_pvr_gem(pvr_obj)); + if (!bind_op->gpuvm_bo) + return -ENOMEM; + + bind_op->new_va = kzalloc(sizeof(*bind_op->new_va), GFP_KERNEL); + bind_op->prev_va = kzalloc(sizeof(*bind_op->prev_va), GFP_KERNEL); + bind_op->next_va = kzalloc(sizeof(*bind_op->next_va), GFP_KERNEL); + if (!bind_op->new_va || !bind_op->prev_va || !bind_op->next_va) { + err = -ENOMEM; + goto err_bind_op_fini; + } + + /* Pin pages so they're ready for use. */ + sgt = pvr_gem_object_get_pages_sgt(pvr_obj); + err = PTR_ERR_OR_ZERO(sgt); + if (err) + goto err_bind_op_fini; + + bind_op->mmu_op_ctx = + pvr_mmu_op_context_create(vm_ctx->mmu_ctx, sgt, offset, size); + err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx); + if (err) { + bind_op->mmu_op_ctx = NULL; + goto err_bind_op_fini; + } + + bind_op->pvr_obj = pvr_obj; + bind_op->vm_ctx = vm_ctx; + bind_op->device_addr = device_addr; + bind_op->size = size; + bind_op->offset = offset; + + return 0; + +err_bind_op_fini: + pvr_vm_bind_op_fini(bind_op); + + return err; +} + +static int +pvr_vm_bind_op_unmap_init(struct pvr_vm_bind_op *bind_op, + struct pvr_vm_context *vm_ctx, u64 device_addr, + u64 size) +{ + int err; + + if (!pvr_device_addr_and_size_are_valid(device_addr, size)) + return -EINVAL; + + bind_op->type = PVR_VM_BIND_TYPE_UNMAP; + + bind_op->prev_va = kzalloc(sizeof(*bind_op->prev_va), GFP_KERNEL); + bind_op->next_va = kzalloc(sizeof(*bind_op->next_va), GFP_KERNEL); + if (!bind_op->prev_va || !bind_op->next_va) { + err = -ENOMEM; + goto err_bind_op_fini; + } + + bind_op->mmu_op_ctx = + pvr_mmu_op_context_create(vm_ctx->mmu_ctx, NULL, 0, 0); + err = PTR_ERR_OR_ZERO(bind_op->mmu_op_ctx); + if (err) { + bind_op->mmu_op_ctx = NULL; + goto err_bind_op_fini; + } + + bind_op->vm_ctx = vm_ctx; + bind_op->device_addr = device_addr; + bind_op->size = size; + + return 0; + +err_bind_op_fini: + pvr_vm_bind_op_fini(bind_op); + + return err; +} + +static int +pvr_vm_bind_op_lock_resvs(struct drm_exec *exec, struct pvr_vm_bind_op *bind_op) +{ + drm_exec_until_all_locked(exec) { + struct drm_gem_object *r_obj = &bind_op->vm_ctx->dummy_gem; + struct drm_gpuvm *gpuvm = &bind_op->vm_ctx->gpuvm_mgr; + struct pvr_gem_object *pvr_obj = bind_op->pvr_obj; + struct drm_gpuvm_bo *gpuvm_bo; + + /* Acquire lock on the vm_context's reserve object. */ + int err = drm_exec_lock_obj(exec, r_obj); + + drm_exec_retry_on_contention(exec); + if (err) + return err; + + /* Acquire lock on all BOs in the context. */ + list_for_each_entry(gpuvm_bo, &gpuvm->extobj.list, + list.entry.extobj) { + err = drm_exec_lock_obj(exec, gpuvm_bo->obj); + + drm_exec_retry_on_contention(exec); + if (err) + return err; + } + + /* Unmap operations don't have an object to lock. */ + if (!pvr_obj) + break; + + /* Acquire lock on the GEM being mapped. */ + err = drm_exec_lock_obj(exec, + gem_from_pvr_gem(bind_op->pvr_obj)); + + drm_exec_retry_on_contention(exec); + if (err) + return err; + } + + return 0; +} + +/** + * pvr_vm_gpuva_map() - Insert a mapping into a memory context. + * @op: gpuva op containing the remap details. + * @op_ctx: Operation context. + * + * Context: Called by drm_gpuvm_sm_map following a successful mapping while + * @op_ctx.vm_ctx mutex is held. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_mmu_map(). + */ +static int +pvr_vm_gpuva_map(struct drm_gpuva_op *op, void *op_ctx) +{ + struct pvr_gem_object *pvr_gem = gem_to_pvr_gem(op->map.gem.obj); + struct pvr_vm_bind_op *ctx = op_ctx; + int err; + + if ((op->map.gem.offset | op->map.va.range) & ~PVR_DEVICE_PAGE_MASK) + return -EINVAL; + + err = pvr_mmu_map(ctx->mmu_op_ctx, op->map.va.range, pvr_gem->flags, + op->map.va.addr); + if (err) + return err; + + drm_gpuva_map(&ctx->vm_ctx->gpuvm_mgr, &ctx->new_va->base, &op->map); + drm_gpuva_link(&ctx->new_va->base, ctx->gpuvm_bo); + ctx->new_va = NULL; + + return 0; +} + +/** + * pvr_vm_gpuva_unmap() - Remove a mapping from a memory context. + * @op: gpuva op containing the unmap details. + * @op_ctx: Operation context. + * + * Context: Called by drm_gpuvm_sm_unmap following a successful unmapping while + * @op_ctx.vm_ctx mutex is held. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_mmu_unmap(). + */ +static int +pvr_vm_gpuva_unmap(struct drm_gpuva_op *op, void *op_ctx) +{ + struct pvr_vm_bind_op *ctx = op_ctx; + + int err = pvr_mmu_unmap(ctx->mmu_op_ctx, op->unmap.va->va.addr, + op->unmap.va->va.range); + + if (err) + return err; + + drm_gpuva_unmap(&op->unmap); + drm_gpuva_unlink(op->unmap.va); + + return 0; +} + +/** + * pvr_vm_gpuva_remap() - Remap a mapping within a memory context. + * @op: gpuva op containing the remap details. + * @op_ctx: Operation context. + * + * Context: Called by either drm_gpuvm_sm_map or drm_gpuvm_sm_unmap when a + * mapping or unmapping operation causes a region to be split. The + * @op_ctx.vm_ctx mutex is held. + * + * Return: + * * 0 on success, or + * * Any error returned by pvr_vm_gpuva_unmap() or pvr_vm_gpuva_unmap(). + */ +static int +pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_ctx) +{ + struct pvr_vm_bind_op *ctx = op_ctx; + u64 va_start = 0, va_range = 0; + int err; + + drm_gpuva_op_remap_to_unmap_range(&op->remap, &va_start, &va_range); + err = pvr_mmu_unmap(ctx->mmu_op_ctx, va_start, va_range); + if (err) + return err; + + /* No actual remap required: the page table tree depth is fixed to 3, + * and we use 4k page table entries only for now. + */ + drm_gpuva_remap(&ctx->prev_va->base, &ctx->next_va->base, &op->remap); + + if (op->remap.prev) { + pvr_gem_object_get(gem_to_pvr_gem(ctx->prev_va->base.gem.obj)); + drm_gpuva_link(&ctx->prev_va->base, ctx->gpuvm_bo); + ctx->prev_va = NULL; + } + + if (op->remap.next) { + pvr_gem_object_get(gem_to_pvr_gem(ctx->next_va->base.gem.obj)); + drm_gpuva_link(&ctx->next_va->base, ctx->gpuvm_bo); + ctx->next_va = NULL; + } + + drm_gpuva_unlink(op->remap.unmap->va); + + return 0; +} + +/* + * Public API + * + * For an overview of these functions, see *DOC: Public API* in "pvr_vm.h". + */ + +/** + * pvr_device_addr_is_valid() - Tests whether a device-virtual address + * is valid. + * @device_addr: Virtual device address to test. + * + * Return: + * * %true if @device_addr is within the valid range for a device page + * table and is aligned to the device page size, or + * * %false otherwise. + */ +bool +pvr_device_addr_is_valid(u64 device_addr) +{ + return (device_addr & ~PVR_PAGE_TABLE_ADDR_MASK) == 0 && + (device_addr & ~PVR_DEVICE_PAGE_MASK) == 0; +} + +/** + * pvr_device_addr_and_size_are_valid() - Tests whether a device-virtual + * address and associated size are both valid. + * @device_addr: Virtual device address to test. + * @size: Size of the range based at @device_addr to test. + * + * Calling pvr_device_addr_is_valid() twice (once on @size, and again on + * @device_addr + @size) to verify a device-virtual address range initially + * seems intuitive, but it produces a false-negative when the address range + * is right at the end of device-virtual address space. + * + * This function catches that corner case, as well as checking that + * @size is non-zero. + * + * Return: + * * %true if @device_addr is device page aligned; @size is device page + * aligned; the range specified by @device_addr and @size is within the + * bounds of the device-virtual address space, and @size is non-zero, or + * * %false otherwise. + */ +bool +pvr_device_addr_and_size_are_valid(u64 device_addr, u64 size) +{ + return pvr_device_addr_is_valid(device_addr) && + size != 0 && (size & ~PVR_DEVICE_PAGE_MASK) == 0 && + (device_addr + size <= PVR_PAGE_TABLE_ADDR_SPACE_SIZE); +} + +void pvr_gpuvm_free(struct drm_gpuvm *gpuvm) +{ + +} + +static const struct drm_gpuvm_ops pvr_vm_gpuva_ops = { + .vm_free = pvr_gpuvm_free, + .sm_step_map = pvr_vm_gpuva_map, + .sm_step_remap = pvr_vm_gpuva_remap, + .sm_step_unmap = pvr_vm_gpuva_unmap, +}; + +static void +fw_mem_context_init(void *cpu_ptr, void *priv) +{ + struct rogue_fwif_fwmemcontext *fw_mem_ctx = cpu_ptr; + struct pvr_vm_context *vm_ctx = priv; + + fw_mem_ctx->pc_dev_paddr = pvr_vm_get_page_table_root_addr(vm_ctx); + fw_mem_ctx->page_cat_base_reg_set = ROGUE_FW_BIF_INVALID_PCSET; +} + +/** + * pvr_vm_create_context() - Create a new VM context. + * @pvr_dev: Target PowerVR device. + * @is_userspace_context: %true if this context is for userspace. This will + * create a firmware memory context for the VM context + * and disable warnings when tearing down mappings. + * + * Return: + * * A handle to the newly-minted VM context on success, + * * -%EINVAL if the feature "virtual address space bits" on @pvr_dev is + * missing or has an unsupported value, + * * -%ENOMEM if allocation of the structure behind the opaque handle fails, + * or + * * Any error encountered while setting up internal structures. + */ +struct pvr_vm_context * +pvr_vm_create_context(struct pvr_device *pvr_dev, bool is_userspace_context) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + + struct pvr_vm_context *vm_ctx; + u16 device_addr_bits; + + int err; + + err = PVR_FEATURE_VALUE(pvr_dev, virtual_address_space_bits, + &device_addr_bits); + if (err) { + drm_err(drm_dev, + "Failed to get device virtual address space bits\n"); + return ERR_PTR(err); + } + + if (device_addr_bits != PVR_PAGE_TABLE_ADDR_BITS) { + drm_err(drm_dev, + "Device has unsupported virtual address space size\n"); + return ERR_PTR(-EINVAL); + } + + vm_ctx = kzalloc(sizeof(*vm_ctx), GFP_KERNEL); + if (!vm_ctx) + return ERR_PTR(-ENOMEM); + + drm_gem_private_object_init(&pvr_dev->base, &vm_ctx->dummy_gem, 0); + + vm_ctx->pvr_dev = pvr_dev; + kref_init(&vm_ctx->ref_count); + mutex_init(&vm_ctx->lock); + + drm_gpuvm_init(&vm_ctx->gpuvm_mgr, + is_userspace_context ? "PowerVR-user-VM" : "PowerVR-FW-VM", + 0, &pvr_dev->base, &vm_ctx->dummy_gem, + 0, 1ULL << device_addr_bits, 0, 0, &pvr_vm_gpuva_ops); + + vm_ctx->mmu_ctx = pvr_mmu_context_create(pvr_dev); + err = PTR_ERR_OR_ZERO(&vm_ctx->mmu_ctx); + if (err) { + vm_ctx->mmu_ctx = NULL; + goto err_put_ctx; + } + + if (is_userspace_context) { + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_fwmemcontext), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_mem_context_init, vm_ctx, &vm_ctx->fw_mem_ctx_obj); + + if (err) + goto err_page_table_destroy; + } + + return vm_ctx; + +err_page_table_destroy: + pvr_mmu_context_destroy(vm_ctx->mmu_ctx); + +err_put_ctx: + pvr_vm_context_put(vm_ctx); + + return ERR_PTR(err); +} + +/** + * pvr_vm_context_release() - Teardown a VM context. + * @ref_count: Pointer to reference counter of the VM context. + * + * This function ensures that no mappings are left dangling by unmapping them + * all in order of ascending device-virtual address. + */ +static void +pvr_vm_context_release(struct kref *ref_count) +{ + struct pvr_vm_context *vm_ctx = + container_of(ref_count, struct pvr_vm_context, ref_count); + + if (vm_ctx->fw_mem_ctx_obj) + pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj); + + WARN_ON(pvr_vm_unmap(vm_ctx, vm_ctx->gpuvm_mgr.mm_start, + vm_ctx->gpuvm_mgr.mm_range)); + + drm_gpuvm_put(&vm_ctx->gpuvm_mgr); + pvr_mmu_context_destroy(vm_ctx->mmu_ctx); + drm_gem_private_object_fini(&vm_ctx->dummy_gem); + mutex_destroy(&vm_ctx->lock); + + kfree(vm_ctx); +} + +/** + * pvr_vm_context_lookup() - Look up VM context from handle + * @pvr_file: Pointer to pvr_file structure. + * @handle: Object handle. + * + * Takes reference on VM context object. Call pvr_vm_context_put() to release. + * + * Returns: + * * The requested object on success, or + * * %NULL on failure (object does not exist in list, or is not a VM context) + */ +struct pvr_vm_context * +pvr_vm_context_lookup(struct pvr_file *pvr_file, u32 handle) +{ + struct pvr_vm_context *vm_ctx; + + xa_lock(&pvr_file->vm_ctx_handles); + vm_ctx = xa_load(&pvr_file->vm_ctx_handles, handle); + if (vm_ctx) + kref_get(&vm_ctx->ref_count); + + xa_unlock(&pvr_file->vm_ctx_handles); + + return vm_ctx; +} + +/** + * pvr_vm_context_put() - Release a reference on a VM context + * @vm_ctx: Target VM context. + * + * Returns: + * * %true if the VM context was destroyed, or + * * %false if there are any references still remaining. + */ +bool +pvr_vm_context_put(struct pvr_vm_context *vm_ctx) +{ + if (vm_ctx) + return kref_put(&vm_ctx->ref_count, pvr_vm_context_release); + + return true; +} + +/** + * pvr_destroy_vm_contexts_for_file: Destroy any VM contexts associated with the + * given file. + * @pvr_file: Pointer to pvr_file structure. + * + * Removes all vm_contexts associated with @pvr_file from the device VM context + * list and drops initial references. vm_contexts will then be destroyed once + * all outstanding references are dropped. + */ +void pvr_destroy_vm_contexts_for_file(struct pvr_file *pvr_file) +{ + struct pvr_vm_context *vm_ctx; + unsigned long handle; + + xa_for_each(&pvr_file->vm_ctx_handles, handle, vm_ctx) { + /* vm_ctx is not used here because that would create a race with xa_erase */ + pvr_vm_context_put(xa_erase(&pvr_file->vm_ctx_handles, handle)); + } +} + +/** + * pvr_vm_map() - Map a section of physical memory into a section of + * device-virtual memory. + * @vm_ctx: Target VM context. + * @pvr_obj: Target PowerVR memory object. + * @pvr_obj_offset: Offset into @pvr_obj to map from. + * @device_addr: Virtual device address at the start of the requested mapping. + * @size: Size of the requested mapping. + * + * No handle is returned to represent the mapping. Instead, callers should + * remember @device_addr and use that as a handle. + * + * Return: + * * 0 on success, + * * -%EINVAL if @device_addr is not a valid page-aligned device-virtual + * address; the region specified by @pvr_obj_offset and @size does not fall + * entirely within @pvr_obj, or any part of the specified region of @pvr_obj + * is not device-virtual page-aligned, + * * Any error encountered while performing internal operations required to + * destroy the mapping (returned from pvr_vm_gpuva_map or + * pvr_vm_gpuva_remap). + */ +int +pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, + u64 pvr_obj_offset, u64 device_addr, u64 size) +{ + struct pvr_vm_bind_op bind_op = {0}; + struct drm_exec exec; + + int err = pvr_vm_bind_op_map_init(&bind_op, vm_ctx, pvr_obj, + pvr_obj_offset, device_addr, + size); + + if (err) + return err; + + drm_exec_init(&exec, + DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES); + + pvr_gem_object_get(pvr_obj); + + err = pvr_vm_bind_op_lock_resvs(&exec, &bind_op); + if (err) + goto err_cleanup; + + err = pvr_vm_bind_op_exec(&bind_op); + + drm_exec_fini(&exec); + +err_cleanup: + pvr_vm_bind_op_fini(&bind_op); + + return err; +} + +/** + * pvr_vm_unmap() - Unmap an already mapped section of device-virtual memory. + * @vm_ctx: Target VM context. + * @device_addr: Virtual device address at the start of the target mapping. + * @size: Size of the target mapping. + * + * Return: + * * 0 on success, + * * -%EINVAL if @device_addr is not a valid page-aligned device-virtual + * address, + * * Any error encountered while performing internal operations required to + * destroy the mapping (returned from pvr_vm_gpuva_unmap or + * pvr_vm_gpuva_remap). + */ +int +pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size) +{ + struct pvr_vm_bind_op bind_op = {0}; + struct drm_exec exec; + + int err = pvr_vm_bind_op_unmap_init(&bind_op, vm_ctx, device_addr, + size); + + if (err) + return err; + + drm_exec_init(&exec, + DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES); + + err = pvr_vm_bind_op_lock_resvs(&exec, &bind_op); + if (err) + goto err_cleanup; + + err = pvr_vm_bind_op_exec(&bind_op); + + drm_exec_fini(&exec); + +err_cleanup: + pvr_vm_bind_op_fini(&bind_op); + + return err; +} + +/* Static data areas are determined by firmware. */ +static const struct drm_pvr_static_data_area static_data_areas[] = { + { + .area_usage = DRM_PVR_STATIC_DATA_AREA_FENCE, + .location_heap_id = DRM_PVR_HEAP_GENERAL, + .offset = 0, + .size = 128, + }, + { + .area_usage = DRM_PVR_STATIC_DATA_AREA_YUV_CSC, + .location_heap_id = DRM_PVR_HEAP_GENERAL, + .offset = 128, + .size = 1024, + }, + { + .area_usage = DRM_PVR_STATIC_DATA_AREA_VDM_SYNC, + .location_heap_id = DRM_PVR_HEAP_PDS_CODE_DATA, + .offset = 0, + .size = 128, + }, + { + .area_usage = DRM_PVR_STATIC_DATA_AREA_EOT, + .location_heap_id = DRM_PVR_HEAP_PDS_CODE_DATA, + .offset = 128, + .size = 128, + }, + { + .area_usage = DRM_PVR_STATIC_DATA_AREA_VDM_SYNC, + .location_heap_id = DRM_PVR_HEAP_USC_CODE, + .offset = 0, + .size = 128, + }, +}; + +#define GET_RESERVED_SIZE(last_offset, last_size) round_up((last_offset) + (last_size), PAGE_SIZE) + +/* + * The values given to GET_RESERVED_SIZE() are taken from the last entry in the corresponding + * static data area for each heap. + */ +static const struct drm_pvr_heap pvr_heaps[] = { + [DRM_PVR_HEAP_GENERAL] = { + .base = ROGUE_GENERAL_HEAP_BASE, + .size = ROGUE_GENERAL_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, + [DRM_PVR_HEAP_PDS_CODE_DATA] = { + .base = ROGUE_PDSCODEDATA_HEAP_BASE, + .size = ROGUE_PDSCODEDATA_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, + [DRM_PVR_HEAP_USC_CODE] = { + .base = ROGUE_USCCODE_HEAP_BASE, + .size = ROGUE_USCCODE_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, + [DRM_PVR_HEAP_RGNHDR] = { + .base = ROGUE_RGNHDR_HEAP_BASE, + .size = ROGUE_RGNHDR_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, + [DRM_PVR_HEAP_VIS_TEST] = { + .base = ROGUE_VISTEST_HEAP_BASE, + .size = ROGUE_VISTEST_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, + [DRM_PVR_HEAP_TRANSFER_FRAG] = { + .base = ROGUE_TRANSFER_FRAG_HEAP_BASE, + .size = ROGUE_TRANSFER_FRAG_HEAP_SIZE, + .flags = 0, + .page_size_log2 = PVR_DEVICE_PAGE_SHIFT, + }, +}; + +int +pvr_static_data_areas_get(const struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + struct drm_pvr_dev_query_static_data_areas query = {0}; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_static_data_areas); + return 0; + } + + err = PVR_UOBJ_GET(query, args->size, args->pointer); + if (err < 0) + return err; + + if (!query.static_data_areas.array) { + query.static_data_areas.count = ARRAY_SIZE(static_data_areas); + query.static_data_areas.stride = sizeof(struct drm_pvr_static_data_area); + goto copy_out; + } + + if (query.static_data_areas.count > ARRAY_SIZE(static_data_areas)) + query.static_data_areas.count = ARRAY_SIZE(static_data_areas); + + err = PVR_UOBJ_SET_ARRAY(&query.static_data_areas, static_data_areas); + if (err < 0) + return err; + +copy_out: + err = PVR_UOBJ_SET(args->pointer, args->size, query); + if (err < 0) + return err; + + args->size = sizeof(query); + return 0; +} + +int +pvr_heap_info_get(const struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args) +{ + struct drm_pvr_dev_query_heap_info query = {0}; + u64 dest; + int err; + + if (!args->pointer) { + args->size = sizeof(struct drm_pvr_dev_query_heap_info); + return 0; + } + + err = PVR_UOBJ_GET(query, args->size, args->pointer); + if (err < 0) + return err; + + if (!query.heaps.array) { + query.heaps.count = ARRAY_SIZE(pvr_heaps); + query.heaps.stride = sizeof(struct drm_pvr_heap); + goto copy_out; + } + + if (query.heaps.count > ARRAY_SIZE(pvr_heaps)) + query.heaps.count = ARRAY_SIZE(pvr_heaps); + + /* Region header heap is only present if BRN63142 is present. */ + dest = query.heaps.array; + for (size_t i = 0; i < query.heaps.count; i++) { + struct drm_pvr_heap heap = pvr_heaps[i]; + + if (i == DRM_PVR_HEAP_RGNHDR && !PVR_HAS_QUIRK(pvr_dev, 63142)) + heap.size = 0; + + err = PVR_UOBJ_SET(dest, query.heaps.stride, heap); + if (err < 0) + return err; + + dest += query.heaps.stride; + } + +copy_out: + err = PVR_UOBJ_SET(args->pointer, args->size, query); + if (err < 0) + return err; + + args->size = sizeof(query); + return 0; +} + +/** + * pvr_heap_contains_range() - Determine if a given heap contains the specified + * device-virtual address range. + * @pvr_heap: Target heap. + * @start: Inclusive start of the target range. + * @end: Inclusive end of the target range. + * + * It is an error to call this function with values of @start and @end that do + * not satisfy the condition @start <= @end. + */ +static __always_inline bool +pvr_heap_contains_range(const struct drm_pvr_heap *pvr_heap, u64 start, u64 end) +{ + return pvr_heap->base <= start && end < pvr_heap->base + pvr_heap->size; +} + +/** + * pvr_find_heap_containing() - Find a heap which contains the specified + * device-virtual address range. + * @pvr_dev: Target PowerVR device. + * @start: Start of the target range. + * @size: Size of the target range. + * + * Return: + * * A pointer to a constant instance of struct drm_pvr_heap representing the + * heap containing the entire range specified by @start and @size on + * success, or + * * %NULL if no such heap exists. + */ +const struct drm_pvr_heap * +pvr_find_heap_containing(struct pvr_device *pvr_dev, u64 start, u64 size) +{ + u64 end; + + if (check_add_overflow(start, size - 1, &end)) + return NULL; + + /* + * There are no guarantees about the order of address ranges in + * &pvr_heaps, so iterate over the entire array for a heap whose + * range completely encompasses the given range. + */ + for (u32 heap_id = 0; heap_id < ARRAY_SIZE(pvr_heaps); heap_id++) { + /* Filter heaps that present only with an associated quirk */ + if (heap_id == DRM_PVR_HEAP_RGNHDR && + !PVR_HAS_QUIRK(pvr_dev, 63142)) { + continue; + } + + if (pvr_heap_contains_range(&pvr_heaps[heap_id], start, end)) + return &pvr_heaps[heap_id]; + } + + return NULL; +} + +/** + * pvr_vm_find_gem_object() - Look up a buffer object from a given + * device-virtual address. + * @vm_ctx: [IN] Target VM context. + * @device_addr: [IN] Virtual device address at the start of the required + * object. + * @mapped_offset_out: [OUT] Pointer to location to write offset of the start + * of the mapped region within the buffer object. May be + * %NULL if this information is not required. + * @mapped_size_out: [OUT] Pointer to location to write size of the mapped + * region. May be %NULL if this information is not required. + * + * If successful, a reference will be taken on the buffer object. The caller + * must drop the reference with pvr_gem_object_put(). + * + * Return: + * * The PowerVR buffer object mapped at @device_addr if one exists, or + * * %NULL otherwise. + */ +struct pvr_gem_object * +pvr_vm_find_gem_object(struct pvr_vm_context *vm_ctx, u64 device_addr, + u64 *mapped_offset_out, u64 *mapped_size_out) +{ + struct pvr_gem_object *pvr_obj; + struct drm_gpuva *va; + + mutex_lock(&vm_ctx->lock); + + va = drm_gpuva_find_first(&vm_ctx->gpuvm_mgr, device_addr, 1); + if (!va) + goto err_unlock; + + pvr_obj = gem_to_pvr_gem(va->gem.obj); + pvr_gem_object_get(pvr_obj); + + if (mapped_offset_out) + *mapped_offset_out = va->gem.offset; + if (mapped_size_out) + *mapped_size_out = va->va.range; + + mutex_unlock(&vm_ctx->lock); + + return pvr_obj; + +err_unlock: + mutex_unlock(&vm_ctx->lock); + + return NULL; +} + +/** + * pvr_vm_get_fw_mem_context: Get object representing firmware memory context + * @vm_ctx: Target VM context. + * + * Returns: + * * FW object representing firmware memory context, or + * * %NULL if this VM context does not have a firmware memory context. + */ +struct pvr_fw_object * +pvr_vm_get_fw_mem_context(struct pvr_vm_context *vm_ctx) +{ + return vm_ctx->fw_mem_ctx_obj; +} diff --git a/drivers/gpu/drm/imagination/pvr_vm.h b/drivers/gpu/drm/imagination/pvr_vm.h new file mode 100644 index 000000000000..cf8b97553dc8 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_vm.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_VM_H +#define PVR_VM_H + +#include "pvr_rogue_mmu_defs.h" + +#include <uapi/drm/pvr_drm.h> + +#include <linux/types.h> + +/* Forward declaration from "pvr_device.h" */ +struct pvr_device; +struct pvr_file; + +/* Forward declaration from "pvr_gem.h" */ +struct pvr_gem_object; + +/* Forward declaration from "pvr_vm.c" */ +struct pvr_vm_context; + +/* Forward declaration from <uapi/drm/pvr_drm.h> */ +struct drm_pvr_ioctl_get_heap_info_args; + +/* Forward declaration from <drm/drm_exec.h> */ +struct drm_exec; + +/* Functions defined in pvr_vm.c */ + +bool pvr_device_addr_is_valid(u64 device_addr); +bool pvr_device_addr_and_size_are_valid(u64 device_addr, u64 size); + +struct pvr_vm_context *pvr_vm_create_context(struct pvr_device *pvr_dev, + bool is_userspace_context); + +int pvr_vm_map(struct pvr_vm_context *vm_ctx, + struct pvr_gem_object *pvr_obj, u64 pvr_obj_offset, + u64 device_addr, u64 size); +int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size); + +dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx); +struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx); + +int pvr_static_data_areas_get(const struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args); +int pvr_heap_info_get(const struct pvr_device *pvr_dev, + struct drm_pvr_ioctl_dev_query_args *args); +const struct drm_pvr_heap *pvr_find_heap_containing(struct pvr_device *pvr_dev, + u64 addr, u64 size); + +struct pvr_gem_object *pvr_vm_find_gem_object(struct pvr_vm_context *vm_ctx, + u64 device_addr, + u64 *mapped_offset_out, + u64 *mapped_size_out); + +struct pvr_fw_object * +pvr_vm_get_fw_mem_context(struct pvr_vm_context *vm_ctx); + +struct pvr_vm_context *pvr_vm_context_lookup(struct pvr_file *pvr_file, u32 handle); +struct pvr_vm_context *pvr_vm_context_get(struct pvr_vm_context *vm_ctx); +bool pvr_vm_context_put(struct pvr_vm_context *vm_ctx); +void pvr_destroy_vm_contexts_for_file(struct pvr_file *pvr_file); + +#endif /* PVR_VM_H */ diff --git a/drivers/gpu/drm/imagination/pvr_vm_mips.c b/drivers/gpu/drm/imagination/pvr_vm_mips.c new file mode 100644 index 000000000000..7268cf6e630b --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_vm_mips.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#include "pvr_device.h" +#include "pvr_fw_mips.h" +#include "pvr_gem.h" +#include "pvr_mmu.h" +#include "pvr_rogue_mips.h" +#include "pvr_vm.h" +#include "pvr_vm_mips.h" + +#include <drm/drm_managed.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/types.h> + +/** + * pvr_vm_mips_init() - Initialise MIPS FW pagetable + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, + * * -%EINVAL, + * * Any error returned by pvr_gem_object_create(), or + * * And error returned by pvr_gem_object_vmap(). + */ +int +pvr_vm_mips_init(struct pvr_device *pvr_dev) +{ + u32 pt_size = 1 << ROGUE_MIPSFW_LOG2_PAGETABLE_SIZE_4K(pvr_dev); + struct device *dev = from_pvr_device(pvr_dev)->dev; + struct pvr_fw_mips_data *mips_data; + u32 phys_bus_width; + int page_nr; + int err; + + /* Page table size must be at most ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * 4k pages. */ + if (pt_size > ROGUE_MIPSFW_MAX_NUM_PAGETABLE_PAGES * SZ_4K) + return -EINVAL; + + if (PVR_FEATURE_VALUE(pvr_dev, phys_bus_width, &phys_bus_width)) + return -EINVAL; + + mips_data = drmm_kzalloc(from_pvr_device(pvr_dev), sizeof(*mips_data), GFP_KERNEL); + if (!mips_data) + return -ENOMEM; + + for (page_nr = 0; page_nr < ARRAY_SIZE(mips_data->pt_pages); page_nr++) { + mips_data->pt_pages[page_nr] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!mips_data->pt_pages[page_nr]) { + err = -ENOMEM; + goto err_free_pages; + } + + mips_data->pt_dma_addr[page_nr] = dma_map_page(dev, mips_data->pt_pages[page_nr], 0, + PAGE_SIZE, DMA_TO_DEVICE); + if (dma_mapping_error(dev, mips_data->pt_dma_addr[page_nr])) { + err = -ENOMEM; + goto err_free_pages; + } + } + + mips_data->pt = vmap(mips_data->pt_pages, pt_size >> PAGE_SHIFT, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); + if (!mips_data->pt) { + err = -ENOMEM; + goto err_free_pages; + } + + mips_data->pfn_mask = (phys_bus_width > 32) ? ROGUE_MIPSFW_ENTRYLO_PFN_MASK_ABOVE_32BIT : + ROGUE_MIPSFW_ENTRYLO_PFN_MASK; + + mips_data->cache_policy = (phys_bus_width > 32) ? ROGUE_MIPSFW_CACHED_POLICY_ABOVE_32BIT : + ROGUE_MIPSFW_CACHED_POLICY; + + pvr_dev->fw_dev.processor_data.mips_data = mips_data; + + return 0; + +err_free_pages: + for (; page_nr >= 0; page_nr--) { + if (mips_data->pt_dma_addr[page_nr]) + dma_unmap_page(from_pvr_device(pvr_dev)->dev, + mips_data->pt_dma_addr[page_nr], PAGE_SIZE, DMA_TO_DEVICE); + + if (mips_data->pt_pages[page_nr]) + __free_page(mips_data->pt_pages[page_nr]); + } + + return err; +} + +/** + * pvr_vm_mips_fini() - Release MIPS FW pagetable + * @pvr_dev: Target PowerVR device. + */ +void +pvr_vm_mips_fini(struct pvr_device *pvr_dev) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data; + int page_nr; + + vunmap(mips_data->pt); + for (page_nr = ARRAY_SIZE(mips_data->pt_pages) - 1; page_nr >= 0; page_nr--) { + dma_unmap_page(from_pvr_device(pvr_dev)->dev, + mips_data->pt_dma_addr[page_nr], PAGE_SIZE, DMA_TO_DEVICE); + + __free_page(mips_data->pt_pages[page_nr]); + } + + fw_dev->processor_data.mips_data = NULL; +} + +static u32 +get_mips_pte_flags(bool read, bool write, u32 cache_policy) +{ + u32 flags = 0; + + if (read && write) /* Read/write. */ + flags |= ROGUE_MIPSFW_ENTRYLO_DIRTY_EN; + else if (write) /* Write only. */ + flags |= ROGUE_MIPSFW_ENTRYLO_READ_INHIBIT_EN; + else + WARN_ON(!read); + + flags |= cache_policy << ROGUE_MIPSFW_ENTRYLO_CACHE_POLICY_SHIFT; + + flags |= ROGUE_MIPSFW_ENTRYLO_VALID_EN | ROGUE_MIPSFW_ENTRYLO_GLOBAL_EN; + + return flags; +} + +/** + * pvr_vm_mips_map() - Map a FW object into MIPS address space + * @pvr_dev: Target PowerVR device. + * @fw_obj: FW object to map. + * + * Returns: + * * 0 on success, + * * -%EINVAL if object does not reside within FW address space, or + * * Any error returned by pvr_fw_object_get_dma_addr(). + */ +int +pvr_vm_mips_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data; + struct pvr_gem_object *pvr_obj = fw_obj->gem; + const u64 start = fw_obj->fw_mm_node.start; + const u64 size = fw_obj->fw_mm_node.size; + u64 end; + u32 cache_policy; + u32 pte_flags; + u32 start_pfn; + u32 end_pfn; + s32 pfn; + int err; + + if (check_add_overflow(start, size - 1, &end)) + return -EINVAL; + + if (start < ROGUE_FW_HEAP_BASE || + start >= ROGUE_FW_HEAP_BASE + fw_dev->fw_heap_info.raw_size || + end < ROGUE_FW_HEAP_BASE || + end >= ROGUE_FW_HEAP_BASE + fw_dev->fw_heap_info.raw_size || + (start & ROGUE_MIPSFW_PAGE_MASK_4K) || + ((end + 1) & ROGUE_MIPSFW_PAGE_MASK_4K)) + return -EINVAL; + + start_pfn = (start & fw_dev->fw_heap_info.offset_mask) >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K; + end_pfn = (end & fw_dev->fw_heap_info.offset_mask) >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K; + + if (pvr_obj->flags & PVR_BO_FW_FLAGS_DEVICE_UNCACHED) + cache_policy = ROGUE_MIPSFW_UNCACHED_CACHE_POLICY; + else + cache_policy = mips_data->cache_policy; + + pte_flags = get_mips_pte_flags(true, true, cache_policy); + + for (pfn = start_pfn; pfn <= end_pfn; pfn++) { + dma_addr_t dma_addr; + u32 pte; + + err = pvr_fw_object_get_dma_addr(fw_obj, + (pfn - start_pfn) << + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K, + &dma_addr); + if (err) + goto err_unmap_pages; + + pte = ((dma_addr >> ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K) + << ROGUE_MIPSFW_ENTRYLO_PFN_SHIFT) & mips_data->pfn_mask; + pte |= pte_flags; + + WRITE_ONCE(mips_data->pt[pfn], pte); + } + + pvr_mmu_flush_request_all(pvr_dev); + + return 0; + +err_unmap_pages: + for (; pfn >= start_pfn; pfn--) + WRITE_ONCE(mips_data->pt[pfn], 0); + + pvr_mmu_flush_request_all(pvr_dev); + WARN_ON(pvr_mmu_flush_exec(pvr_dev, true)); + + return err; +} + +/** + * pvr_vm_mips_unmap() - Unmap a FW object into MIPS address space + * @pvr_dev: Target PowerVR device. + * @fw_obj: FW object to unmap. + */ +void +pvr_vm_mips_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj) +{ + struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; + struct pvr_fw_mips_data *mips_data = fw_dev->processor_data.mips_data; + const u64 start = fw_obj->fw_mm_node.start; + const u64 size = fw_obj->fw_mm_node.size; + const u64 end = start + size; + + const u32 start_pfn = (start & fw_dev->fw_heap_info.offset_mask) >> + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K; + const u32 end_pfn = (end & fw_dev->fw_heap_info.offset_mask) >> + ROGUE_MIPSFW_LOG2_PAGE_SIZE_4K; + + for (u32 pfn = start_pfn; pfn < end_pfn; pfn++) + WRITE_ONCE(mips_data->pt[pfn], 0); + + pvr_mmu_flush_request_all(pvr_dev); + WARN_ON(pvr_mmu_flush_exec(pvr_dev, true)); +} diff --git a/drivers/gpu/drm/imagination/pvr_vm_mips.h b/drivers/gpu/drm/imagination/pvr_vm_mips.h new file mode 100644 index 000000000000..0fd59f68fb5b --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_vm_mips.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_VM_MIPS_H +#define PVR_VM_MIPS_H + +/* Forward declaration from pvr_device.h. */ +struct pvr_device; + +/* Forward declaration from pvr_gem.h. */ +struct pvr_fw_object; + +int +pvr_vm_mips_init(struct pvr_device *pvr_dev); +void +pvr_vm_mips_fini(struct pvr_device *pvr_dev); +int +pvr_vm_mips_map(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj); +void +pvr_vm_mips_unmap(struct pvr_device *pvr_dev, struct pvr_fw_object *fw_obj); + +#endif /* PVR_VM_MIPS_H */ diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c index b61cec0cc79d..ad5f29ea8f6a 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-drv.c +++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c @@ -80,7 +80,7 @@ err: return err; } -static int dcss_drv_platform_remove(struct platform_device *pdev) +static void dcss_drv_platform_remove(struct platform_device *pdev) { struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev); @@ -88,8 +88,6 @@ static int dcss_drv_platform_remove(struct platform_device *pdev) dcss_dev_destroy(mdrv->dcss); kfree(mdrv); - - return 0; } static void dcss_drv_platform_shutdown(struct platform_device *pdev) @@ -120,7 +118,7 @@ MODULE_DEVICE_TABLE(of, dcss_of_match); static struct platform_driver dcss_platform_driver = { .probe = dcss_drv_platform_probe, - .remove = dcss_drv_platform_remove, + .remove_new = dcss_drv_platform_remove, .shutdown = dcss_drv_platform_shutdown, .driver = { .name = "imx-dcss", diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c index 22b65f4a0e30..0902983374d0 100644 --- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c @@ -515,14 +515,12 @@ static int imx_lcdc_probe(struct platform_device *pdev) return 0; } -static int imx_lcdc_remove(struct platform_device *pdev) +static void imx_lcdc_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); drm_dev_unregister(drm); drm_atomic_helper_shutdown(drm); - - return 0; } static void imx_lcdc_shutdown(struct platform_device *pdev) @@ -536,7 +534,7 @@ static struct platform_driver imx_lcdc_driver = { .of_match_table = imx_lcdc_of_dev_id, }, .probe = imx_lcdc_probe, - .remove = imx_lcdc_remove, + .remove_new = imx_lcdc_remove, .shutdown = imx_lcdc_shutdown, }; module_platform_driver(imx_lcdc_driver); diff --git a/drivers/gpu/drm/kmb/kmb_drv.c b/drivers/gpu/drm/kmb/kmb_drv.c index 24035b53441c..169b83987ce2 100644 --- a/drivers/gpu/drm/kmb/kmb_drv.c +++ b/drivers/gpu/drm/kmb/kmb_drv.c @@ -448,7 +448,7 @@ static const struct drm_driver kmb_driver = { .minor = DRIVER_MINOR, }; -static int kmb_remove(struct platform_device *pdev) +static void kmb_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct drm_device *drm = dev_get_drvdata(dev); @@ -473,7 +473,6 @@ static int kmb_remove(struct platform_device *pdev) /* Unregister DSI host */ kmb_dsi_host_unregister(kmb->kmb_dsi); drm_atomic_helper_shutdown(drm); - return 0; } static int kmb_probe(struct platform_device *pdev) @@ -621,7 +620,7 @@ static SIMPLE_DEV_PM_OPS(kmb_pm_ops, kmb_pm_suspend, kmb_pm_resume); static struct platform_driver kmb_platform_driver = { .probe = kmb_probe, - .remove = kmb_remove, + .remove_new = kmb_remove, .driver = { .name = "kmb-drm", .pm = &kmb_pm_ops, diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c index 02cef0cea657..0bf7105c8748 100644 --- a/drivers/gpu/drm/lima/lima_device.c +++ b/drivers/gpu/drm/lima/lima_device.c @@ -514,7 +514,7 @@ int lima_device_suspend(struct device *dev) /* check any task running */ for (i = 0; i < lima_pipe_num; i++) { - if (atomic_read(&ldev->pipe[i].base.hw_rq_count)) + if (atomic_read(&ldev->pipe[i].base.credit_count)) return -EBUSY; } diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 295f0353a02e..c3bf8cda8498 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c @@ -123,7 +123,7 @@ int lima_sched_task_init(struct lima_sched_task *task, for (i = 0; i < num_bos; i++) drm_gem_object_get(&bos[i]->base.base); - err = drm_sched_job_init(&task->base, &context->base, vm); + err = drm_sched_job_init(&task->base, &context->base, 1, vm); if (err) { kfree(task->bos); return err; @@ -488,7 +488,7 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name) INIT_WORK(&pipe->recover_work, lima_sched_recover_work); - return drm_sched_init(&pipe->base, &lima_sched_ops, + return drm_sched_init(&pipe->base, &lima_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, 1, lima_job_hang_limit, diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index 6bf6367853fb..3fdef3ad4ffd 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -531,16 +531,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) return ret; } -static int mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) +static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(&pdev->dev); - return 0; } struct platform_driver mtk_disp_ovl_adaptor_driver = { .probe = mtk_disp_ovl_adaptor_probe, - .remove = mtk_disp_ovl_adaptor_remove, + .remove_new = mtk_disp_ovl_adaptor_remove, .driver = { .name = "mediatek-disp-ovl-adaptor", .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/mediatek/mtk_ethdr.c b/drivers/gpu/drm/mediatek/mtk_ethdr.c index db7ac666ec5e..6a5d0c345aab 100644 --- a/drivers/gpu/drm/mediatek/mtk_ethdr.c +++ b/drivers/gpu/drm/mediatek/mtk_ethdr.c @@ -346,10 +346,9 @@ static int mtk_ethdr_probe(struct platform_device *pdev) return ret; } -static int mtk_ethdr_remove(struct platform_device *pdev) +static void mtk_ethdr_remove(struct platform_device *pdev) { component_del(&pdev->dev, &mtk_ethdr_component_ops); - return 0; } static const struct of_device_id mtk_ethdr_driver_dt_match[] = { @@ -361,7 +360,7 @@ MODULE_DEVICE_TABLE(of, mtk_ethdr_driver_dt_match); struct platform_driver mtk_ethdr_driver = { .probe = mtk_ethdr_probe, - .remove = mtk_ethdr_remove, + .remove_new = mtk_ethdr_remove, .driver = { .name = "mediatek-disp-ethdr", .owner = THIS_MODULE, diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c index e5fe4e994f43..a6bc1bdb3d0d 100644 --- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c @@ -323,13 +323,11 @@ static int meson_dw_mipi_dsi_probe(struct platform_device *pdev) return 0; } -static int meson_dw_mipi_dsi_remove(struct platform_device *pdev) +static void meson_dw_mipi_dsi_remove(struct platform_device *pdev) { struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev); dw_mipi_dsi_remove(mipi_dsi->dmd); - - return 0; } static const struct of_device_id meson_dw_mipi_dsi_of_table[] = { @@ -340,7 +338,7 @@ MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table); static struct platform_driver meson_dw_mipi_dsi_platform_driver = { .probe = meson_dw_mipi_dsi_probe, - .remove = meson_dw_mipi_dsi_remove, + .remove_new = meson_dw_mipi_dsi_remove, .driver = { .name = DRIVER_NAME, .of_match_table = meson_dw_mipi_dsi_of_table, diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 41b13dec9bef..f62ab5257e66 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -841,7 +841,8 @@ static void suspend_scheduler(struct msm_gpu *gpu) */ for (i = 0; i < gpu->nr_rings; i++) { struct drm_gpu_scheduler *sched = &gpu->rb[i]->sched; - kthread_park(sched->thread); + + drm_sched_wqueue_stop(sched); } } @@ -851,7 +852,8 @@ static void resume_scheduler(struct msm_gpu *gpu) for (i = 0; i < gpu->nr_rings; i++) { struct drm_gpu_scheduler *sched = &gpu->rb[i]->sched; - kthread_unpark(sched->thread); + + drm_sched_wqueue_start(sched); } } diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index 1ccd1edd693c..4c0528794e7a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -406,6 +406,7 @@ static const struct dpu_perf_cfg sc8280xp_perf_data = { .min_llcc_ib = 0, .min_dram_ib = 800000, .danger_lut_tbl = {0xf, 0xffff, 0x0}, + .safe_lut_tbl = {0xfe00, 0xfe00, 0xffff}, .qos_lut_tbl = { {.nentry = ARRAY_SIZE(sc8180x_qos_linear), .entries = sc8180x_qos_linear diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c index 11d9fc2c6bf5..ec933d597e20 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c @@ -844,8 +844,7 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) return 0; fail: - if (mdp5_kms) - mdp5_destroy(mdp5_kms); + mdp5_destroy(mdp5_kms); return ret; } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index e329e03e068d..1b88fb52726f 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -365,9 +365,11 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp, /* reset video pattern flag on disconnect */ if (!hpd) { dp->panel->video_test = false; - drm_dp_set_subconnector_property(dp->dp_display.connector, - connector_status_disconnected, - dp->panel->dpcd, dp->panel->downstream_ports); + if (!dp->dp_display.is_edp) + drm_dp_set_subconnector_property(dp->dp_display.connector, + connector_status_disconnected, + dp->panel->dpcd, + dp->panel->downstream_ports); } dp->dp_display.is_connected = hpd; @@ -396,8 +398,11 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp_link_process_request(dp->link); - drm_dp_set_subconnector_property(dp->dp_display.connector, connector_status_connected, - dp->panel->dpcd, dp->panel->downstream_ports); + if (!dp->dp_display.is_edp) + drm_dp_set_subconnector_property(dp->dp_display.connector, + connector_status_connected, + dp->panel->dpcd, + dp->panel->downstream_ports); edid = dp->panel->edid; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 40e7344180e3..e3bdd7dd4cdc 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -345,6 +345,9 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct dr if (IS_ERR(connector)) return connector; + if (!dp_display->is_edp) + drm_connector_attach_dp_subconnector_property(connector); + drm_connector_attach_encoder(connector, encoder); return connector; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c index 3b1ed02f644d..89a6344bc865 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -918,7 +918,7 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy, if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2)) { if (phy->cphy_mode) { vreg_ctrl_0 = 0x45; - vreg_ctrl_1 = 0x45; + vreg_ctrl_1 = 0x41; glbl_rescode_top_ctrl = 0x00; glbl_rescode_bot_ctrl = 0x00; } else { diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 2aae7d107f33..3f217b578293 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -288,8 +288,6 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv) if (ret) goto err_msm_uninit; - drm_kms_helper_poll_init(ddev); - if (priv->kms_init) { drm_kms_helper_poll_init(ddev); msm_fbdev_setup(ddev); diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 99744de6c05a..c002cabe7b9c 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -48,7 +48,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, return ERR_PTR(ret); } - ret = drm_sched_job_init(&submit->base, queue->entity, queue); + ret = drm_sched_job_init(&submit->base, queue->entity, 1, queue); if (ret) { kfree(submit->hw_fence); kfree(submit); diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index 95257ab0185d..4968568e3b54 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -94,7 +94,7 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, /* currently managing hangcheck ourselves: */ sched_timeout = MAX_SCHEDULE_TIMEOUT; - ret = drm_sched_init(&ring->sched, &msm_sched_ops, + ret = drm_sched_init(&ring->sched, &msm_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, num_hw_submissions, 0, sched_timeout, NULL, NULL, to_msm_bo(ring->bo)->name, gpu->dev->dev); diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 7840b6428afb..11fe75b68e95 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -38,7 +38,9 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_fixed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> @@ -945,7 +947,8 @@ nv50_msto_prepare(struct drm_atomic_state *state, if (ret == 0) { nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, payload->vc_start_slot, payload->time_slots, - payload->pbn, payload->time_slots * mst_state->pbn_div); + payload->pbn, + payload->time_slots * dfixed_trunc(mst_state->pbn_div)); } else { nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); } @@ -982,15 +985,14 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, const int clock = crtc_state->adjusted_mode.clock; asyh->or.bpc = connector->display_info.bpc; - asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3, - false); + asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3 << 4); } mst_state = drm_atomic_get_mst_topology_state(state, &mstm->mgr); if (IS_ERR(mst_state)) return PTR_ERR(mst_state); - if (!mst_state->pbn_div) { + if (!mst_state->pbn_div.full) { struct nouveau_encoder *outp = mstc->mstm->outp; mst_state->pbn_div = drm_dp_get_vc_payload_bw(&mstm->mgr, diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/event.h b/drivers/gpu/drm/nouveau/include/nvkm/core/event.h index 82b267c11147..460459af272d 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/event.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/event.h @@ -14,7 +14,7 @@ struct nvkm_event { int index_nr; spinlock_t refs_lock; - spinlock_t list_lock; + rwlock_t list_lock; int *refs; struct list_head ntfy; @@ -38,7 +38,7 @@ nvkm_event_init(const struct nvkm_event_func *func, struct nvkm_subdev *subdev, int types_nr, int index_nr, struct nvkm_event *event) { spin_lock_init(&event->refs_lock); - spin_lock_init(&event->list_lock); + rwlock_init(&event->list_lock); return __nvkm_event_init(func, subdev, types_nr, index_nr, event); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 0f3bd187ede6..7afad86da64b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -148,10 +148,17 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) * If nouveau_bo_new() allocated this buffer, the GEM object was never * initialized, so don't attempt to release it. */ - if (bo->base.dev) + if (bo->base.dev) { + /* Gem objects not being shared with other VMs get their + * dma_resv from a root GEM object. + */ + if (nvbo->no_share) + drm_gem_object_put(nvbo->r_obj); + drm_gem_object_release(&bo->base); - else + } else { dma_resv_fini(&bo->base._resv); + } kfree(nvbo); } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 07f671cf895e..70c551921a9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -26,6 +26,11 @@ struct nouveau_bo { struct list_head entry; int pbbo_index; bool validate_mapped; + + /* Root GEM object we derive the dma_resv of in case this BO is not + * shared between VMs. + */ + struct drm_gem_object *r_obj; bool no_share; /* GPU address space is independent of CPU word size */ diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index d8c92521226d..f28f9a857458 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -726,6 +726,11 @@ nouveau_display_create(struct drm_device *dev) if (nouveau_modeset != 2) { ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, &disp->disp); + /* no display hw */ + if (ret == -ENODEV) { + ret = 0; + goto disp_create_err; + } if (!ret && (disp->disp.outp_mask || drm->vbios.dcb.entries)) { nouveau_display_create_properties(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 50589f982d1a..f603eaef1560 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -190,6 +190,8 @@ nouveau_cli_work_queue(struct nouveau_cli *cli, struct dma_fence *fence, static void nouveau_cli_fini(struct nouveau_cli *cli) { + struct nouveau_uvmm *uvmm = nouveau_cli_uvmm_locked(cli); + /* All our channels are dead now, which means all the fences they * own are signalled, and all callback functions have been called. * @@ -199,7 +201,8 @@ nouveau_cli_fini(struct nouveau_cli *cli) WARN_ON(!list_empty(&cli->worker)); usif_client_fini(cli); - nouveau_uvmm_fini(&cli->uvmm); + if (uvmm) + nouveau_uvmm_fini(uvmm); nouveau_sched_entity_fini(&cli->sched_entity); nouveau_vmm_fini(&cli->svm); nouveau_vmm_fini(&cli->vmm); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index e73a233c6572..7f7051df84a6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -93,7 +93,10 @@ struct nouveau_cli { struct nvif_mmu mmu; struct nouveau_vmm vmm; struct nouveau_vmm svm; - struct nouveau_uvmm uvmm; + struct { + struct nouveau_uvmm *ptr; + bool disabled; + } uvmm; struct nouveau_sched_entity sched_entity; @@ -121,10 +124,7 @@ struct nouveau_cli_work { static inline struct nouveau_uvmm * nouveau_cli_uvmm(struct nouveau_cli *cli) { - if (!cli || !cli->uvmm.vmm.cli) - return NULL; - - return &cli->uvmm; + return cli ? cli->uvmm.ptr : NULL; } static inline struct nouveau_uvmm * diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index a0d303e5ce3d..49c2bcbef129 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -111,7 +111,8 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) if (vmm->vmm.object.oclass < NVIF_CLASS_VMM_NV50) return 0; - if (nvbo->no_share && uvmm && &uvmm->resv != nvbo->bo.base.resv) + if (nvbo->no_share && uvmm && + drm_gpuvm_resv(&uvmm->base) != nvbo->bo.base.resv) return -EPERM; ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL); @@ -245,7 +246,7 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain, if (unlikely(!uvmm)) return -EINVAL; - resv = &uvmm->resv; + resv = drm_gpuvm_resv(&uvmm->base); } if (!(domain & (NOUVEAU_GEM_DOMAIN_VRAM | NOUVEAU_GEM_DOMAIN_GART))) @@ -288,6 +289,11 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain, if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) nvbo->valid_domains &= domain; + if (nvbo->no_share) { + nvbo->r_obj = drm_gpuvm_resv_obj(&uvmm->base); + drm_gem_object_get(nvbo->r_obj); + } + *pnvbo = nvbo; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.c b/drivers/gpu/drm/nouveau/nouveau_platform.c index 23cd43a7fd19..bf2dc7567ea4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_platform.c +++ b/drivers/gpu/drm/nouveau/nouveau_platform.c @@ -43,11 +43,10 @@ static int nouveau_platform_probe(struct platform_device *pdev) return 0; } -static int nouveau_platform_remove(struct platform_device *pdev) +static void nouveau_platform_remove(struct platform_device *pdev) { struct drm_device *dev = platform_get_drvdata(pdev); nouveau_drm_device_remove(dev); - return 0; } #if IS_ENABLED(CONFIG_OF) @@ -93,5 +92,5 @@ struct platform_driver nouveau_platform_driver = { .of_match_table = of_match_ptr(nouveau_platform_match), }, .probe = nouveau_platform_probe, - .remove = nouveau_platform_remove, + .remove_new = nouveau_platform_remove, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c index 7c376c4ccdcf..e03fd2bc8a11 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sched.c +++ b/drivers/gpu/drm/nouveau/nouveau_sched.c @@ -89,7 +89,7 @@ nouveau_job_init(struct nouveau_job *job, } - ret = drm_sched_job_init(&job->base, &entity->base, NULL); + ret = drm_sched_job_init(&job->base, &entity->base, 1, NULL); if (ret) goto err_free_chains; @@ -435,7 +435,7 @@ int nouveau_sched_init(struct nouveau_drm *drm) if (!drm->sched_wq) return -ENOMEM; - return drm_sched_init(sched, &nouveau_sched_ops, + return drm_sched_init(sched, &nouveau_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, NOUVEAU_SCHED_HW_SUBMISSIONS, 0, job_hang_limit, NULL, NULL, "nouveau_sched", drm->dev->dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index 5cf892c50f43..eda7bb8624f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -62,6 +62,8 @@ struct bind_job_op { enum vm_bind_op op; u32 flags; + struct drm_gpuvm_bo *vm_bo; + struct { u64 addr; u64 range; @@ -929,25 +931,13 @@ nouveau_uvmm_sm_unmap_cleanup(struct nouveau_uvmm *uvmm, static int nouveau_uvmm_validate_range(struct nouveau_uvmm *uvmm, u64 addr, u64 range) { - u64 end = addr + range; - u64 kernel_managed_end = uvmm->kernel_managed_addr + - uvmm->kernel_managed_size; - if (addr & ~PAGE_MASK) return -EINVAL; if (range & ~PAGE_MASK) return -EINVAL; - if (end <= addr) - return -EINVAL; - - if (addr < NOUVEAU_VA_SPACE_START || - end > NOUVEAU_VA_SPACE_END) - return -EINVAL; - - if (addr < kernel_managed_end && - end > uvmm->kernel_managed_addr) + if (!drm_gpuvm_range_valid(&uvmm->base, addr, range)) return -EINVAL; return 0; @@ -1113,22 +1103,28 @@ bind_validate_region(struct nouveau_job *job) } static void -bind_link_gpuvas(struct drm_gpuva_ops *ops, struct nouveau_uvma_prealloc *new) +bind_link_gpuvas(struct bind_job_op *bop) { + struct nouveau_uvma_prealloc *new = &bop->new; + struct drm_gpuvm_bo *vm_bo = bop->vm_bo; + struct drm_gpuva_ops *ops = bop->ops; struct drm_gpuva_op *op; drm_gpuva_for_each_op(op, ops) { switch (op->op) { case DRM_GPUVA_OP_MAP: - drm_gpuva_link(&new->map->va); + drm_gpuva_link(&new->map->va, vm_bo); break; - case DRM_GPUVA_OP_REMAP: + case DRM_GPUVA_OP_REMAP: { + struct drm_gpuva *va = op->remap.unmap->va; + if (op->remap.prev) - drm_gpuva_link(&new->prev->va); + drm_gpuva_link(&new->prev->va, va->vm_bo); if (op->remap.next) - drm_gpuva_link(&new->next->va); - drm_gpuva_unlink(op->remap.unmap->va); + drm_gpuva_link(&new->next->va, va->vm_bo); + drm_gpuva_unlink(va); break; + } case DRM_GPUVA_OP_UNMAP: drm_gpuva_unlink(op->unmap.va); break; @@ -1150,10 +1146,17 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) list_for_each_op(op, &bind_job->ops) { if (op->op == OP_MAP) { - op->gem.obj = drm_gem_object_lookup(job->file_priv, - op->gem.handle); - if (!op->gem.obj) + struct drm_gem_object *obj = op->gem.obj = + drm_gem_object_lookup(job->file_priv, + op->gem.handle); + if (!obj) return -ENOENT; + + dma_resv_lock(obj->resv, NULL); + op->vm_bo = drm_gpuvm_bo_obtain(&uvmm->base, obj); + dma_resv_unlock(obj->resv); + if (IS_ERR(op->vm_bo)) + return PTR_ERR(op->vm_bo); } ret = bind_validate_op(job, op); @@ -1364,7 +1367,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) case OP_UNMAP_SPARSE: case OP_MAP: case OP_UNMAP: - bind_link_gpuvas(op->ops, &op->new); + bind_link_gpuvas(op); break; default: break; @@ -1511,6 +1514,12 @@ nouveau_uvmm_bind_job_free_work_fn(struct work_struct *work) if (!IS_ERR_OR_NULL(op->ops)) drm_gpuva_ops_free(&uvmm->base, op->ops); + if (!IS_ERR_OR_NULL(op->vm_bo)) { + dma_resv_lock(obj->resv, NULL); + drm_gpuvm_bo_put(op->vm_bo); + dma_resv_unlock(obj->resv); + } + if (obj) drm_gem_object_put(obj); } @@ -1648,18 +1657,6 @@ err_free: return ret; } -int -nouveau_uvmm_ioctl_vm_init(struct drm_device *dev, - void *data, - struct drm_file *file_priv) -{ - struct nouveau_cli *cli = nouveau_cli(file_priv); - struct drm_nouveau_vm_init *init = data; - - return nouveau_uvmm_init(&cli->uvmm, cli, init->kernel_managed_addr, - init->kernel_managed_size); -} - static int nouveau_uvmm_vm_bind(struct nouveau_uvmm_bind_job_args *args) { @@ -1776,15 +1773,18 @@ void nouveau_uvmm_bo_map_all(struct nouveau_bo *nvbo, struct nouveau_mem *mem) { struct drm_gem_object *obj = &nvbo->bo.base; + struct drm_gpuvm_bo *vm_bo; struct drm_gpuva *va; dma_resv_assert_held(obj->resv); - drm_gem_for_each_gpuva(va, obj) { - struct nouveau_uvma *uvma = uvma_from_va(va); + drm_gem_for_each_gpuvm_bo(vm_bo, obj) { + drm_gpuvm_bo_for_each_va(va, vm_bo) { + struct nouveau_uvma *uvma = uvma_from_va(va); - nouveau_uvma_map(uvma, mem); - drm_gpuva_invalidate(va, false); + nouveau_uvma_map(uvma, mem); + drm_gpuva_invalidate(va, false); + } } } @@ -1792,29 +1792,53 @@ void nouveau_uvmm_bo_unmap_all(struct nouveau_bo *nvbo) { struct drm_gem_object *obj = &nvbo->bo.base; + struct drm_gpuvm_bo *vm_bo; struct drm_gpuva *va; dma_resv_assert_held(obj->resv); - drm_gem_for_each_gpuva(va, obj) { - struct nouveau_uvma *uvma = uvma_from_va(va); + drm_gem_for_each_gpuvm_bo(vm_bo, obj) { + drm_gpuvm_bo_for_each_va(va, vm_bo) { + struct nouveau_uvma *uvma = uvma_from_va(va); - nouveau_uvma_unmap(uvma); - drm_gpuva_invalidate(va, true); + nouveau_uvma_unmap(uvma); + drm_gpuva_invalidate(va, true); + } } } +static void +nouveau_uvmm_free(struct drm_gpuvm *gpuvm) +{ + struct nouveau_uvmm *uvmm = uvmm_from_gpuvm(gpuvm); + + kfree(uvmm); +} + +static const struct drm_gpuvm_ops gpuvm_ops = { + .vm_free = nouveau_uvmm_free, +}; + int -nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, - u64 kernel_managed_addr, u64 kernel_managed_size) +nouveau_uvmm_ioctl_vm_init(struct drm_device *dev, + void *data, + struct drm_file *file_priv) { + struct nouveau_uvmm *uvmm; + struct nouveau_cli *cli = nouveau_cli(file_priv); + struct drm_device *drm = cli->drm->dev; + struct drm_gem_object *r_obj; + struct drm_nouveau_vm_init *init = data; + u64 kernel_managed_end; int ret; - u64 kernel_managed_end = kernel_managed_addr + kernel_managed_size; - mutex_init(&uvmm->mutex); - dma_resv_init(&uvmm->resv); - mt_init_flags(&uvmm->region_mt, MT_FLAGS_LOCK_EXTERN); - mt_set_external_lock(&uvmm->region_mt, &uvmm->mutex); + if (check_add_overflow(init->kernel_managed_addr, + init->kernel_managed_size, + &kernel_managed_end)) + return -EINVAL; + + if (kernel_managed_end > NOUVEAU_VA_SPACE_END) + return -EINVAL; mutex_lock(&cli->mutex); @@ -1823,39 +1847,48 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, goto out_unlock; } - if (kernel_managed_end <= kernel_managed_addr) { - ret = -EINVAL; + uvmm = kzalloc(sizeof(*uvmm), GFP_KERNEL); + if (!uvmm) { + ret = -ENOMEM; goto out_unlock; } - if (kernel_managed_end > NOUVEAU_VA_SPACE_END) { - ret = -EINVAL; + r_obj = drm_gpuvm_resv_object_alloc(drm); + if (!r_obj) { + kfree(uvmm); + ret = -ENOMEM; goto out_unlock; } - uvmm->kernel_managed_addr = kernel_managed_addr; - uvmm->kernel_managed_size = kernel_managed_size; + mutex_init(&uvmm->mutex); + mt_init_flags(&uvmm->region_mt, MT_FLAGS_LOCK_EXTERN); + mt_set_external_lock(&uvmm->region_mt, &uvmm->mutex); - drm_gpuvm_init(&uvmm->base, cli->name, + drm_gpuvm_init(&uvmm->base, cli->name, 0, drm, r_obj, NOUVEAU_VA_SPACE_START, NOUVEAU_VA_SPACE_END, - kernel_managed_addr, kernel_managed_size, - NULL); + init->kernel_managed_addr, + init->kernel_managed_size, + &gpuvm_ops); + /* GPUVM takes care from here on. */ + drm_gem_object_put(r_obj); ret = nvif_vmm_ctor(&cli->mmu, "uvmm", cli->vmm.vmm.object.oclass, RAW, - kernel_managed_addr, kernel_managed_size, - NULL, 0, &cli->uvmm.vmm.vmm); + init->kernel_managed_addr, + init->kernel_managed_size, + NULL, 0, &uvmm->vmm.vmm); if (ret) - goto out_free_gpuva_mgr; + goto out_gpuvm_fini; - cli->uvmm.vmm.cli = cli; + uvmm->vmm.cli = cli; + cli->uvmm.ptr = uvmm; mutex_unlock(&cli->mutex); return 0; -out_free_gpuva_mgr: - drm_gpuvm_destroy(&uvmm->base); +out_gpuvm_fini: + drm_gpuvm_put(&uvmm->base); out_unlock: mutex_unlock(&cli->mutex); return ret; @@ -1870,9 +1903,6 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) struct nouveau_sched_entity *entity = &cli->sched_entity; struct drm_gpuva *va, *next; - if (!cli) - return; - rmb(); /* for list_empty to work without lock */ wait_event(entity->job.wq, list_empty(&entity->job.list.head)); @@ -1910,8 +1940,6 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) mutex_lock(&cli->mutex); nouveau_vmm_fini(&uvmm->vmm); - drm_gpuvm_destroy(&uvmm->base); + drm_gpuvm_put(&uvmm->base); mutex_unlock(&cli->mutex); - - dma_resv_fini(&uvmm->resv); } diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.h b/drivers/gpu/drm/nouveau/nouveau_uvmm.h index a308c59760a5..f0a6d98ace4f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.h +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.h @@ -12,12 +12,6 @@ struct nouveau_uvmm { struct nouveau_vmm vmm; struct maple_tree region_mt; struct mutex mutex; - struct dma_resv resv; - - u64 kernel_managed_addr; - u64 kernel_managed_size; - - bool disabled; }; struct nouveau_uvma_region { @@ -82,8 +76,6 @@ struct nouveau_uvmm_bind_job_args { #define to_uvmm_bind_job(job) container_of((job), struct nouveau_uvmm_bind_job, base) -int nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, - u64 kernel_managed_addr, u64 kernel_managed_size); void nouveau_uvmm_fini(struct nouveau_uvmm *uvmm); void nouveau_uvmm_bo_map_all(struct nouveau_bo *nvbov, struct nouveau_mem *mem); diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c index 5b71a5a5cd85..cdbc75e3d1f6 100644 --- a/drivers/gpu/drm/nouveau/nv04_fence.c +++ b/drivers/gpu/drm/nouveau/nv04_fence.c @@ -39,7 +39,7 @@ struct nv04_fence_priv { static int nv04_fence_emit(struct nouveau_fence *fence) { - struct nvif_push *push = fence->channel->chan.push; + struct nvif_push *push = unrcu_pointer(fence->channel)->chan.push; int ret = PUSH_WAIT(push, 2); if (ret == 0) { PUSH_NVSQ(push, NV_SW, 0x0150, fence->base.seqno); diff --git a/drivers/gpu/drm/nouveau/nvkm/core/event.c b/drivers/gpu/drm/nouveau/nvkm/core/event.c index a6c877135598..61fed7792e41 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/event.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/event.c @@ -81,17 +81,17 @@ nvkm_event_ntfy_state(struct nvkm_event_ntfy *ntfy) static void nvkm_event_ntfy_remove(struct nvkm_event_ntfy *ntfy) { - spin_lock_irq(&ntfy->event->list_lock); + write_lock_irq(&ntfy->event->list_lock); list_del_init(&ntfy->head); - spin_unlock_irq(&ntfy->event->list_lock); + write_unlock_irq(&ntfy->event->list_lock); } static void nvkm_event_ntfy_insert(struct nvkm_event_ntfy *ntfy) { - spin_lock_irq(&ntfy->event->list_lock); + write_lock_irq(&ntfy->event->list_lock); list_add_tail(&ntfy->head, &ntfy->event->ntfy); - spin_unlock_irq(&ntfy->event->list_lock); + write_unlock_irq(&ntfy->event->list_lock); } static void @@ -176,7 +176,7 @@ nvkm_event_ntfy(struct nvkm_event *event, int id, u32 bits) return; nvkm_trace(event->subdev, "event: ntfy %08x on %d\n", bits, id); - spin_lock_irqsave(&event->list_lock, flags); + read_lock_irqsave(&event->list_lock, flags); list_for_each_entry_safe(ntfy, ntmp, &event->ntfy, head) { if (ntfy->id == id && ntfy->bits & bits) { @@ -185,7 +185,7 @@ nvkm_event_ntfy(struct nvkm_event *event, int id, u32 bits) } } - spin_unlock_irqrestore(&event->list_lock, flags); + read_unlock_irqrestore(&event->list_lock, flags); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c index 87a62d4ff4bd..7d4716dcd512 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c @@ -24,7 +24,6 @@ #include "chan.h" #include "chid.h" #include "cgrp.h" -#include "chid.h" #include "runl.h" #include "priv.h" diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c index 3adbb05ff587..d088e636edc3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/r535.c @@ -539,7 +539,7 @@ r535_fifo_runl_ctor(struct nvkm_fifo *fifo) struct nvkm_runl *runl; struct nvkm_engn *engn; u32 cgids = 2048; - u32 chids = 2048 / CHID_PER_USERD; + u32 chids = 2048; int ret; NV2080_CTRL_FIFO_GET_DEVICE_INFO_TABLE_PARAMS *ctrl; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c index e31f9641114b..dc44f5c7833f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c @@ -689,8 +689,8 @@ r535_gsp_rpc_get(struct nvkm_gsp *gsp, u32 fn, u32 argc) struct nvfw_gsp_rpc *rpc; rpc = r535_gsp_cmdq_get(gsp, ALIGN(sizeof(*rpc) + argc, sizeof(u64))); - if (!rpc) - return NULL; + if (IS_ERR(rpc)) + return ERR_CAST(rpc); rpc->header_version = 0x03000000; rpc->signature = ('C' << 24) | ('P' << 16) | ('R' << 8) | 'V'; @@ -1159,7 +1159,7 @@ static void r535_gsp_acpi_mux_id(acpi_handle handle, u32 id, MUX_METHOD_DATA_ELEMENT *mode, MUX_METHOD_DATA_ELEMENT *part) { - acpi_handle iter = NULL, handle_mux; + acpi_handle iter = NULL, handle_mux = NULL; acpi_status status; unsigned long long value; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index b2835b3ea6f5..6598c9c08ba1 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -69,7 +69,6 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; struct omap_drm_private *priv = dev->dev_private; - bool fence_cookie = dma_fence_begin_signalling(); dispc_runtime_get(priv->dispc); @@ -92,6 +91,8 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) omap_atomic_wait_for_completion(dev, old_state); drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_hw_done(old_state); } else { /* * OMAP3 DSS seems to have issues with the work-around above, @@ -101,11 +102,9 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_commit_planes(dev, old_state, 0); drm_atomic_helper_commit_modeset_enables(dev, old_state); - } - drm_atomic_helper_commit_hw_done(old_state); - - dma_fence_end_signalling(fence_cookie); + drm_atomic_helper_commit_hw_done(old_state); + } /* * Wait for completion of the page flips to ensure that old buffers diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 9323e7b9e384..be8f48e3c1db 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -1709,6 +1709,7 @@ static const struct panel_desc auo_b101uan08_3_desc = { .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_LPM, .init_cmds = auo_b101uan08_3_init_cmd, + .lp11_before_reset = true, }; static const struct drm_display_mode boe_tv105wum_nw0_default_mode = { @@ -1766,11 +1767,11 @@ static const struct panel_desc starry_qfh032011_53g_desc = { }; static const struct drm_display_mode starry_himax83102_j02_default_mode = { - .clock = 161600, + .clock = 162850, .hdisplay = 1200, - .hsync_start = 1200 + 40, - .hsync_end = 1200 + 40 + 20, - .htotal = 1200 + 40 + 20 + 40, + .hsync_start = 1200 + 50, + .hsync_end = 1200 + 50 + 20, + .htotal = 1200 + 50 + 20 + 50, .vdisplay = 1920, .vsync_start = 1920 + 116, .vsync_end = 1920 + 116 + 8, diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 95c8472d878a..f22677373171 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -973,6 +973,8 @@ static const struct panel_desc auo_b116xak01 = { }, .delay = { .hpd_absent = 200, + .unprepare = 500, + .enable = 50, }, }; @@ -1801,6 +1803,12 @@ static const struct panel_delay delay_200_500_e50 = { .enable = 50, }; +static const struct panel_delay delay_200_500_e80 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 80, +}; + static const struct panel_delay delay_200_500_e80_d50 = { .hpd_absent = 200, .unprepare = 500, @@ -1820,6 +1828,19 @@ static const struct panel_delay delay_200_500_e200 = { .enable = 200, }; +static const struct panel_delay delay_200_500_e200_d10 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 200, + .disable = 10, +}; + +static const struct panel_delay delay_200_150_e200 = { + .hpd_absent = 200, + .unprepare = 150, + .enable = 200, +}; + #define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \ { \ .name = _name, \ @@ -1840,34 +1861,69 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x145c, &delay_200_500_e50, "B116XAB01.4"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"), - EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x582d, &delay_200_500_e50, "B133UAN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0xf390, &delay_200_500_e50, "B140XTN07.7"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0715, &delay_200_150_e200, "NT116WHM-N21"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0731, &delay_200_500_e80, "NT116WHM-N42"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0741, &delay_200_500_e200, "NT116WHM-N44"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0786, &delay_200_500_p2e80, "NV116WHM-T01"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x07d1, &boe_nv133fhm_n61.delay, "NV133FHM-N61"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x07f6, &delay_200_500_e200, "NT140FHM-N44"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x082d, &boe_nv133fhm_n61.delay, "NV133FHM-N62"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x08b2, &delay_200_500_e200, "NT140WHM-N49"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x09c3, &delay_200_500_e50, "NT116WHM-N21,836X2"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x094b, &delay_200_500_e50, "NT116WHM-N21"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0951, &delay_200_500_e80, "NV116WHM-N47"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x095f, &delay_200_500_e50, "NE135FBM-N41 v8.1"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x09ae, &delay_200_500_e200, "NT140FHM-N45"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x09dd, &delay_200_500_e50, "NT116WHM-N21"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b43, &delay_200_500_e200, "NV140FHM-T09"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b56, &delay_200_500_e80, "NT140FHM-N47"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1132, &delay_200_500_e80_d50, "N116BGE-EA2"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1138, &innolux_n116bca_ea1.delay, "N116BCA-EA1-RC4"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1139, &delay_200_500_e80_d50, "N116BGE-EA2"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1145, &delay_200_500_e80_d50, "N116BCN-EB1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1152, &delay_200_500_e80_d50, "N116BCN-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1153, &delay_200_500_e80_d50, "N116BGE-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1154, &delay_200_500_e80_d50, "N116BCA-EA2"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1157, &delay_200_500_e80_d50, "N116BGE-EA2"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x115b, &delay_200_500_e80_d50, "N116BCN-EB1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x142b, &delay_200_500_e80_d50, "N140HCA-EAC"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x144f, &delay_200_500_e80_d50, "N140HGA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1468, &delay_200_500_e80, "N140HGA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"), + + EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5c, &delay_200_500_e200, "MB116AN01-2"), + EDP_PANEL_ENTRY('I', 'V', 'O', 0x048e, &delay_200_500_e200_d10, "M116NWR6 R5"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x854a, &delay_200_500_p2e100, "M133NW4J"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x854b, &delay_200_500_p2e100, "R133NW4K-R0"), + EDP_PANEL_ENTRY('I', 'V', 'O', 0x8c4d, &delay_200_150_e200, "R140NWFM R1"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"), + EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"), diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index e7be15b68102..00791ea81e90 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Elida kd35t133 5.5" MIPI-DSI panel driver + * Elida kd35t133 3.5" MIPI-DSI panel driver * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH * * based on @@ -43,7 +43,6 @@ struct kd35t133 { struct regulator *vdd; struct regulator *iovcc; enum drm_panel_orientation orientation; - bool prepared; }; static inline struct kd35t133 *panel_to_kd35t133(struct drm_panel *panel) @@ -91,9 +90,6 @@ static int kd35t133_unprepare(struct drm_panel *panel) struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; - if (!ctx->prepared) - return 0; - ret = mipi_dsi_dcs_set_display_off(dsi); if (ret < 0) dev_err(ctx->dev, "failed to set display off: %d\n", ret); @@ -104,11 +100,11 @@ static int kd35t133_unprepare(struct drm_panel *panel) return ret; } + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->iovcc); regulator_disable(ctx->vdd); - ctx->prepared = false; - return 0; } @@ -118,9 +114,6 @@ static int kd35t133_prepare(struct drm_panel *panel) struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; - if (ctx->prepared) - return 0; - dev_dbg(ctx->dev, "Resetting the panel\n"); ret = regulator_enable(ctx->vdd); if (ret < 0) { @@ -164,8 +157,6 @@ static int kd35t133_prepare(struct drm_panel *panel) msleep(50); - ctx->prepared = true; - return 0; disable_iovcc: @@ -209,11 +200,6 @@ static int kd35t133_get_modes(struct drm_panel *panel, connector->display_info.width_mm = mode->width_mm; connector->display_info.height_mm = mode->height_mm; drm_mode_probed_add(connector, mode); - /* - * TODO: Remove once all drm drivers call - * drm_connector_set_orientation_from_panel() - */ - drm_connector_set_panel_orientation(connector, ctx->orientation); return 1; } @@ -299,27 +285,11 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) return 0; } -static void kd35t133_shutdown(struct mipi_dsi_device *dsi) -{ - struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = drm_panel_unprepare(&ctx->panel); - if (ret < 0) - dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); - - ret = drm_panel_disable(&ctx->panel); - if (ret < 0) - dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); -} - static void kd35t133_remove(struct mipi_dsi_device *dsi) { struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi); int ret; - kd35t133_shutdown(dsi); - ret = mipi_dsi_detach(dsi); if (ret < 0) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); @@ -340,7 +310,6 @@ static struct mipi_dsi_driver kd35t133_driver = { }, .probe = kd35t133_probe, .remove = kd35t133_remove, - .shutdown = kd35t133_shutdown, }; module_mipi_dsi_driver(kd35t133_driver); diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c index 79de6c886292..94d89ffd596b 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c @@ -28,6 +28,7 @@ struct nv3051d_panel_info { unsigned int num_modes; u16 width_mm, height_mm; u32 bus_flags; + u32 mode_flags; }; struct panel_nv3051d { @@ -261,6 +262,8 @@ static int panel_nv3051d_unprepare(struct drm_panel *panel) usleep_range(10000, 15000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->vdd); return 0; @@ -385,15 +388,7 @@ static int panel_nv3051d_probe(struct mipi_dsi_device *dsi) dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - - /* - * The panel in the RG351V is identical to the 353P, except it - * requires MIPI_DSI_CLOCK_NON_CONTINUOUS to operate correctly. - */ - if (of_device_is_compatible(dev->of_node, "anbernic,rg351v-panel")) - dsi->mode_flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS; + dsi->mode_flags = ctx->panel_info->mode_flags; drm_panel_init(&ctx->panel, &dsi->dev, &panel_nv3051d_funcs, DRM_MODE_CONNECTOR_DSI); @@ -481,16 +476,56 @@ static const struct drm_display_mode nv3051d_rgxx3_modes[] = { }, }; -static const struct nv3051d_panel_info nv3051d_rgxx3_info = { +static const struct drm_display_mode nv3051d_rk2023_modes[] = { + { + .hdisplay = 640, + .hsync_start = 640 + 40, + .hsync_end = 640 + 40 + 2, + .htotal = 640 + 40 + 2 + 80, + .vdisplay = 480, + .vsync_start = 480 + 18, + .vsync_end = 480 + 18 + 2, + .vtotal = 480 + 18 + 2 + 4, + .clock = 24150, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct nv3051d_panel_info nv3051d_rg351v_info = { .display_modes = nv3051d_rgxx3_modes, .num_modes = ARRAY_SIZE(nv3051d_rgxx3_modes), .width_mm = 70, .height_mm = 57, .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, +}; + +static const struct nv3051d_panel_info nv3051d_rg353p_info = { + .display_modes = nv3051d_rgxx3_modes, + .num_modes = ARRAY_SIZE(nv3051d_rgxx3_modes), + .width_mm = 70, + .height_mm = 57, + .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, +}; + +static const struct nv3051d_panel_info nv3051d_rk2023_info = { + .display_modes = nv3051d_rk2023_modes, + .num_modes = ARRAY_SIZE(nv3051d_rk2023_modes), + .width_mm = 70, + .height_mm = 57, + .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET, }; static const struct of_device_id newvision_nv3051d_of_match[] = { - { .compatible = "newvision,nv3051d", .data = &nv3051d_rgxx3_info }, + { .compatible = "anbernic,rg351v-panel", .data = &nv3051d_rg351v_info }, + { .compatible = "anbernic,rg353p-panel", .data = &nv3051d_rg353p_info }, + { .compatible = "powkiddy,rk2023-panel", .data = &nv3051d_rk2023_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, newvision_nv3051d_of_match); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35510.c b/drivers/gpu/drm/panel/panel-novatek-nt35510.c index d6dceb858008..83a9cf53d269 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35510.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35510.c @@ -1023,7 +1023,7 @@ static const struct nt35510_config nt35510_hydis_hva40wv1 = { .hdisplay = 480, .hsync_start = 480 + 2, /* HFP = 2 */ .hsync_end = 480 + 2 + 0, /* HSync = 0 */ - .htotal = 480 + 2 + 0 + 5, /* HFP = 5 */ + .htotal = 480 + 2 + 0 + 5, /* HBP = 5 */ .vdisplay = 800, .vsync_start = 800 + 2, /* VFP = 2 */ .vsync_end = 800 + 2 + 0, /* VSync = 0 */ diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 6cd32b909087..9367a4572dcf 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2379,13 +2379,13 @@ static const struct panel_desc innolux_g070y2_t02 = { static const struct display_timing innolux_g101ice_l01_timing = { .pixelclock = { 60400000, 71100000, 74700000 }, .hactive = { 1280, 1280, 1280 }, - .hfront_porch = { 41, 80, 100 }, - .hback_porch = { 40, 79, 99 }, - .hsync_len = { 1, 1, 1 }, + .hfront_porch = { 30, 60, 70 }, + .hback_porch = { 30, 60, 70 }, + .hsync_len = { 22, 40, 60 }, .vactive = { 800, 800, 800 }, - .vfront_porch = { 5, 11, 14 }, - .vback_porch = { 4, 11, 14 }, - .vsync_len = { 1, 1, 1 }, + .vfront_porch = { 3, 8, 14 }, + .vback_porch = { 3, 8, 14 }, + .vsync_len = { 4, 7, 12 }, .flags = DISPLAY_FLAGS_DE_HIGH, }; @@ -2402,6 +2402,7 @@ static const struct panel_desc innolux_g101ice_l01 = { .disable = 200, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 28f7046e1b1a..c90ad5ee34e7 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -403,7 +403,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev) panfrost_job_enable_interrupts(pfdev); } -static int panfrost_device_resume(struct device *dev) +static int panfrost_device_runtime_resume(struct device *dev) { struct panfrost_device *pfdev = dev_get_drvdata(dev); @@ -413,7 +413,7 @@ static int panfrost_device_resume(struct device *dev) return 0; } -static int panfrost_device_suspend(struct device *dev) +static int panfrost_device_runtime_suspend(struct device *dev) { struct panfrost_device *pfdev = dev_get_drvdata(dev); @@ -426,5 +426,75 @@ static int panfrost_device_suspend(struct device *dev) return 0; } -EXPORT_GPL_RUNTIME_DEV_PM_OPS(panfrost_pm_ops, panfrost_device_suspend, - panfrost_device_resume, NULL); +static int panfrost_device_resume(struct device *dev) +{ + struct panfrost_device *pfdev = dev_get_drvdata(dev); + int ret; + + if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF)) { + unsigned long freq = pfdev->pfdevfreq.fast_rate; + struct dev_pm_opp *opp; + + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + dev_pm_opp_set_opp(dev, opp); + dev_pm_opp_put(opp); + } + + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) { + ret = clk_enable(pfdev->clock); + if (ret) + goto err_clk; + + if (pfdev->bus_clock) { + ret = clk_enable(pfdev->bus_clock); + if (ret) + goto err_bus_clk; + } + } + + ret = pm_runtime_force_resume(dev); + if (ret) + goto err_resume; + + return 0; + +err_resume: + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock) + clk_disable(pfdev->bus_clock); +err_bus_clk: + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) + clk_disable(pfdev->clock); +err_clk: + if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF)) + dev_pm_opp_set_opp(dev, NULL); + return ret; +} + +static int panfrost_device_suspend(struct device *dev) +{ + struct panfrost_device *pfdev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) { + if (pfdev->bus_clock) + clk_disable(pfdev->bus_clock); + + clk_disable(pfdev->clock); + } + + if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF)) + dev_pm_opp_set_opp(dev, NULL); + + return 0; +} + +EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = { + RUNTIME_PM_OPS(panfrost_device_runtime_suspend, panfrost_device_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(panfrost_device_suspend, panfrost_device_resume) +}; diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 1ef38f60d5dc..0fc558db6bfd 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -25,6 +25,16 @@ struct panfrost_perfcnt; #define NUM_JOB_SLOTS 3 #define MAX_PM_DOMAINS 5 +/** + * enum panfrost_gpu_pm - Supported kernel power management features + * @GPU_PM_CLK_DIS: Allow disabling clocks during system suspend + * @GPU_PM_VREG_OFF: Allow turning off regulators during system suspend + */ +enum panfrost_gpu_pm { + GPU_PM_CLK_DIS, + GPU_PM_VREG_OFF, +}; + struct panfrost_features { u16 id; u16 revision; @@ -75,6 +85,9 @@ struct panfrost_compatible { /* Vendor implementation quirks callback */ void (*vendor_quirk)(struct panfrost_device *pfdev); + + /* Allowed PM features */ + u8 pm_features; }; struct panfrost_device { diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 7cabf4e3d1f2..a926d71e8131 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -274,7 +274,7 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, ret = drm_sched_job_init(&job->base, &file_priv->sched_entity[slot], - NULL); + 1, NULL); if (ret) goto out_put_job; @@ -734,6 +734,7 @@ static const struct panfrost_compatible mediatek_mt8183_b_data = { .supply_names = mediatek_mt8183_b_supplies, .num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains), .pm_domain_names = mediatek_mt8183_pm_domains, + .pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF), }; static const char * const mediatek_mt8186_pm_domains[] = { "core0", "core1" }; @@ -742,6 +743,7 @@ static const struct panfrost_compatible mediatek_mt8186_data = { .supply_names = mediatek_mt8183_b_supplies, .num_pm_domains = ARRAY_SIZE(mediatek_mt8186_pm_domains), .pm_domain_names = mediatek_mt8186_pm_domains, + .pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF), }; static const char * const mediatek_mt8192_supplies[] = { "mali", NULL }; @@ -752,6 +754,7 @@ static const struct panfrost_compatible mediatek_mt8192_data = { .supply_names = mediatek_mt8192_supplies, .num_pm_domains = ARRAY_SIZE(mediatek_mt8192_pm_domains), .pm_domain_names = mediatek_mt8192_pm_domains, + .pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF), }; static const struct of_device_id dt_match[] = { diff --git a/drivers/gpu/drm/panfrost/panfrost_dump.c b/drivers/gpu/drm/panfrost/panfrost_dump.c index e7942ac449c6..47751302f1bc 100644 --- a/drivers/gpu/drm/panfrost/panfrost_dump.c +++ b/drivers/gpu/drm/panfrost/panfrost_dump.c @@ -220,16 +220,8 @@ void panfrost_core_dump(struct panfrost_job *job) iter.hdr->bomap.data[0] = bomap - bomap_start; - for_each_sgtable_page(bo->base.sgt, &page_iter, 0) { - struct page *page = sg_page_iter_page(&page_iter); - - if (!IS_ERR(page)) { - *bomap++ = page_to_phys(page); - } else { - dev_err(pfdev->dev, "Panfrost Dump: wrong page\n"); - *bomap++ = 0; - } - } + for_each_sgtable_page(bo->base.sgt, &page_iter, 0) + *bomap++ = page_to_phys(sg_page_iter_page(&page_iter)); iter.hdr->bomap.iova = mapping->mmnode.start << PAGE_SHIFT; diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index f0be7e19b13e..09f5e1563ebd 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -60,14 +60,21 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev) gpu_write(pfdev, GPU_INT_MASK, 0); gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); - gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); + gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, - val, val & GPU_IRQ_RESET_COMPLETED, 100, 10000); + val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000); if (ret) { - dev_err(pfdev->dev, "gpu soft reset timed out\n"); - return ret; + dev_err(pfdev->dev, "gpu soft reset timed out, attempting hard reset\n"); + + gpu_write(pfdev, GPU_CMD, GPU_CMD_HARD_RESET); + ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, val, + val & GPU_IRQ_RESET_COMPLETED, 100, 10000); + if (ret) { + dev_err(pfdev->dev, "gpu hard reset timed out\n"); + return ret; + } } gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_MASK_ALL); @@ -362,32 +369,42 @@ unsigned long long panfrost_cycle_counter_read(struct panfrost_device *pfdev) return ((u64)hi << 32) | lo; } +static u64 panfrost_get_core_mask(struct panfrost_device *pfdev) +{ + u64 core_mask; + + if (pfdev->features.l2_present == 1) + return U64_MAX; + + /* + * Only support one core group now. + * ~(l2_present - 1) unsets all bits in l2_present except + * the bottom bit. (l2_present - 2) has all the bits in + * the first core group set. AND them together to generate + * a mask of cores in the first core group. + */ + core_mask = ~(pfdev->features.l2_present - 1) & + (pfdev->features.l2_present - 2); + dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n", + hweight64(core_mask), + hweight64(pfdev->features.shader_present)); + + return core_mask; +} + void panfrost_gpu_power_on(struct panfrost_device *pfdev) { int ret; u32 val; - u64 core_mask = U64_MAX; + u64 core_mask; panfrost_gpu_init_quirks(pfdev); + core_mask = panfrost_get_core_mask(pfdev); - if (pfdev->features.l2_present != 1) { - /* - * Only support one core group now. - * ~(l2_present - 1) unsets all bits in l2_present except - * the bottom bit. (l2_present - 2) has all the bits in - * the first core group set. AND them together to generate - * a mask of cores in the first core group. - */ - core_mask = ~(pfdev->features.l2_present - 1) & - (pfdev->features.l2_present - 2); - dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n", - hweight64(core_mask), - hweight64(pfdev->features.shader_present)); - } gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present & core_mask); ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO, val, val == (pfdev->features.l2_present & core_mask), - 100, 20000); + 10, 20000); if (ret) dev_err(pfdev->dev, "error powering up gpu L2"); @@ -395,22 +412,40 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev) pfdev->features.shader_present & core_mask); ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, val, val == (pfdev->features.shader_present & core_mask), - 100, 20000); + 10, 20000); if (ret) dev_err(pfdev->dev, "error powering up gpu shader"); gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present); ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO, - val, val == pfdev->features.tiler_present, 100, 1000); + val, val == pfdev->features.tiler_present, 10, 1000); if (ret) dev_err(pfdev->dev, "error powering up gpu tiler"); } void panfrost_gpu_power_off(struct panfrost_device *pfdev) { - gpu_write(pfdev, TILER_PWROFF_LO, 0); - gpu_write(pfdev, SHADER_PWROFF_LO, 0); - gpu_write(pfdev, L2_PWROFF_LO, 0); + u64 core_mask = panfrost_get_core_mask(pfdev); + int ret; + u32 val; + + gpu_write(pfdev, SHADER_PWROFF_LO, pfdev->features.shader_present & core_mask); + ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_PWRTRANS_LO, + val, !val, 1, 1000); + if (ret) + dev_err(pfdev->dev, "shader power transition timeout"); + + gpu_write(pfdev, TILER_PWROFF_LO, pfdev->features.tiler_present); + ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_PWRTRANS_LO, + val, !val, 1, 1000); + if (ret) + dev_err(pfdev->dev, "tiler power transition timeout"); + + gpu_write(pfdev, L2_PWROFF_LO, pfdev->features.l2_present & core_mask); + ret = readl_poll_timeout(pfdev->iomem + L2_PWRTRANS_LO, + val, !val, 0, 1000); + if (ret) + dev_err(pfdev->dev, "l2 power transition timeout"); } int panfrost_gpu_init(struct panfrost_device *pfdev) diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index ecd2e035147f..f9446e197428 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -852,7 +852,7 @@ int panfrost_job_init(struct panfrost_device *pfdev) js->queue[j].fence_context = dma_fence_context_alloc(1); ret = drm_sched_init(&js->queue[j].sched, - &panfrost_sched_ops, + &panfrost_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, nentries, 0, msecs_to_jiffies(JOB_TIMEOUT_MS), @@ -963,7 +963,7 @@ int panfrost_job_is_idle(struct panfrost_device *pfdev) for (i = 0; i < NUM_JOB_SLOTS; i++) { /* If there are any jobs in the HW queue, we're not idle */ - if (atomic_read(&js->queue[i].sched.hw_rq_count)) + if (atomic_read(&js->queue[i].sched.credit_count)) return false; } diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h index 55ec807550b3..c25743b05c55 100644 --- a/drivers/gpu/drm/panfrost/panfrost_regs.h +++ b/drivers/gpu/drm/panfrost/panfrost_regs.h @@ -44,6 +44,7 @@ GPU_IRQ_MULTIPLE_FAULT) #define GPU_CMD 0x30 #define GPU_CMD_SOFT_RESET 0x01 +#define GPU_CMD_HARD_RESET 0x02 #define GPU_CMD_PERFCNT_CLEAR 0x03 #define GPU_CMD_PERFCNT_SAMPLE 0x04 #define GPU_CMD_CYCLE_COUNT_START 0x05 diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c index d6ccaf24ee0c..279bf130a18c 100644 --- a/drivers/gpu/drm/radeon/radeon_audio.c +++ b/drivers/gpu/drm/radeon/radeon_audio.c @@ -26,6 +26,7 @@ #include <linux/component.h> #include <drm/drm_crtc.h> +#include <drm/drm_eld.h> #include "dce6_afmt.h" #include "evergreen_hdmi.h" #include "radeon.h" diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index fa6e592e0276..7d561c5a650f 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -4,6 +4,7 @@ * Zheng Yang <zhengyang@rock-chips.com> */ +#include <drm/drm_atomic.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> @@ -55,7 +56,6 @@ struct rk3066_hdmi { unsigned int tmdsclk; struct hdmi_data_info hdmi_data; - struct drm_display_mode previous_mode; }; static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder) @@ -387,21 +387,21 @@ static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, return 0; } -static void -rk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) +static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + int mux, val; - /* Store the display mode for plugin/DPMS poweron events. */ - drm_mode_copy(&hdmi->previous_mode, adj_mode); -} + conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector); + if (WARN_ON(!conn_state)) + return; -static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) -{ - struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); - int mux, val; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); if (mux) @@ -414,10 +414,11 @@ static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder enable select: vop%s\n", (mux) ? "1" : "0"); - rk3066_hdmi_setup(hdmi, &hdmi->previous_mode); + rk3066_hdmi_setup(hdmi, &crtc_state->adjusted_mode); } -static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder) +static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); @@ -434,14 +435,6 @@ static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder) rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); } -static bool -rk3066_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) -{ - return true; -} - static int rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, @@ -457,11 +450,9 @@ rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { - .enable = rk3066_hdmi_encoder_enable, - .disable = rk3066_hdmi_encoder_disable, - .mode_fixup = rk3066_hdmi_encoder_mode_fixup, - .mode_set = rk3066_hdmi_encoder_mode_set, - .atomic_check = rk3066_hdmi_encoder_atomic_check, + .atomic_check = rk3066_hdmi_encoder_atomic_check, + .atomic_enable = rk3066_hdmi_encoder_enable, + .atomic_disable = rk3066_hdmi_encoder_disable, }; static enum drm_connector_status diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 066299894d04..a13473b2d54c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -247,14 +247,22 @@ static inline void vop_cfg_done(struct vop *vop) VOP_REG_SET(vop, common, cfg_done, 1); } -static bool has_rb_swapped(uint32_t format) +static bool has_rb_swapped(uint32_t version, uint32_t format) { switch (format) { case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_BGR888: case DRM_FORMAT_BGR565: return true; + /* + * full framework (IP version 3.x) only need rb swapped for RGB888 and + * little framework (IP version 2.x) only need rb swapped for BGR888, + * check for 3.x to also only rb swap BGR888 for unknown vop version + */ + case DRM_FORMAT_RGB888: + return VOP_MAJOR(version) == 3; + case DRM_FORMAT_BGR888: + return VOP_MAJOR(version) != 3; default: return false; } @@ -1030,7 +1038,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, VOP_WIN_SET(vop, win, dsp_info, dsp_info); VOP_WIN_SET(vop, win, dsp_st, dsp_st); - rb_swap = has_rb_swapped(fb->format->format); + rb_swap = has_rb_swapped(vop->data->version, fb->format->format); VOP_WIN_SET(vop, win, rb_swap, rb_swap); /* diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 6862fb146ace..312da5783362 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -325,11 +325,14 @@ static enum vop2_data_format vop2_convert_format(u32 format) case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: return VOP2_FMT_YUV422SP; + case DRM_FORMAT_NV20: case DRM_FORMAT_Y210: return VOP2_FMT_YUV422SP_10; case DRM_FORMAT_NV24: case DRM_FORMAT_NV42: return VOP2_FMT_YUV444SP; + case DRM_FORMAT_NV30: + return VOP2_FMT_YUV444SP_10; case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: return VOP2_FMT_VYUY422; @@ -414,6 +417,8 @@ static bool vop2_win_uv_swap(u32 format) case DRM_FORMAT_NV16: case DRM_FORMAT_NV24: case DRM_FORMAT_NV15: + case DRM_FORMAT_NV20: + case DRM_FORMAT_NV30: case DRM_FORMAT_YUYV: case DRM_FORMAT_UYVY: return true; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 22288ad7f326..2c45d81983a5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -48,8 +48,10 @@ static const uint32_t formats_rk356x_esmart[] = { DRM_FORMAT_NV15, /* yuv420_10bit linear mode, 2 plane, no padding */ DRM_FORMAT_NV16, /* yuv422_8bit linear mode, 2 plane */ DRM_FORMAT_NV61, /* yuv422_8bit linear mode, 2 plane */ + DRM_FORMAT_NV20, /* yuv422_10bit linear mode, 2 plane, no padding */ DRM_FORMAT_NV24, /* yuv444_8bit linear mode, 2 plane */ DRM_FORMAT_NV42, /* yuv444_8bit linear mode, 2 plane */ + DRM_FORMAT_NV30, /* yuv444_10bit linear mode, 2 plane, no padding */ DRM_FORMAT_YVYU, /* yuv422_8bit[YVYU] linear mode */ DRM_FORMAT_VYUY, /* yuv422_8bit[VYUY] linear mode */ }; diff --git a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h index 3143ecaaff86..f8ed093b7356 100644 --- a/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h +++ b/drivers/gpu/drm/scheduler/gpu_scheduler_trace.h @@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(drm_sched_job, __assign_str(name, sched_job->sched->name); __entry->job_count = spsc_queue_count(&entity->job_queue); __entry->hw_job_count = atomic_read( - &sched_job->sched->hw_rq_count); + &sched_job->sched->credit_count); ), TP_printk("entity=%p, id=%llu, fence=%p, ring=%s, job count:%u, hw job count:%d", __entry->entity, __entry->id, diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 409e4256f6e7..4d42b1e4daa6 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -370,7 +370,7 @@ static void drm_sched_entity_wakeup(struct dma_fence *f, container_of(cb, struct drm_sched_entity, cb); drm_sched_entity_clear_dep(f, cb); - drm_sched_wakeup_if_can_queue(entity->rq->sched); + drm_sched_wakeup(entity->rq->sched, entity); } /** @@ -602,7 +602,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job) if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) drm_sched_rq_update_fifo(entity, submit_ts); - drm_sched_wakeup_if_can_queue(entity->rq->sched); + drm_sched_wakeup(entity->rq->sched, entity); } } EXPORT_SYMBOL(drm_sched_entity_push_job); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 99797a8c836a..044a8c4875ba 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -48,7 +48,30 @@ * through the jobs entity pointer. */ -#include <linux/kthread.h> +/** + * DOC: Flow Control + * + * The DRM GPU scheduler provides a flow control mechanism to regulate the rate + * in which the jobs fetched from scheduler entities are executed. + * + * In this context the &drm_gpu_scheduler keeps track of a driver specified + * credit limit representing the capacity of this scheduler and a credit count; + * every &drm_sched_job carries a driver specified number of credits. + * + * Once a job is executed (but not yet finished), the job's credits contribute + * to the scheduler's credit count until the job is finished. If by executing + * one more job the scheduler's credit count would exceed the scheduler's + * credit limit, the job won't be executed. Instead, the scheduler will wait + * until the credit count has decreased enough to not overflow its credit limit. + * This implies waiting for previously executed jobs. + * + * Optionally, drivers may register a callback (update_job_credits) provided by + * struct drm_sched_backend_ops to update the job's credits dynamically. The + * scheduler executes this callback every time the scheduler considers a job for + * execution and subsequently checks whether the job fits the scheduler's credit + * limit. + */ + #include <linux/wait.h> #include <linux/sched.h> #include <linux/completion.h> @@ -76,6 +99,51 @@ int drm_sched_policy = DRM_SCHED_POLICY_FIFO; MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default)."); module_param_named(sched_policy, drm_sched_policy, int, 0444); +static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched) +{ + u32 credits; + + drm_WARN_ON(sched, check_sub_overflow(sched->credit_limit, + atomic_read(&sched->credit_count), + &credits)); + + return credits; +} + +/** + * drm_sched_can_queue -- Can we queue more to the hardware? + * @sched: scheduler instance + * @entity: the scheduler entity + * + * Return true if we can push at least one more job from @entity, false + * otherwise. + */ +static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched, + struct drm_sched_entity *entity) +{ + struct drm_sched_job *s_job; + + s_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); + if (!s_job) + return false; + + if (sched->ops->update_job_credits) { + s_job->credits = sched->ops->update_job_credits(s_job); + + drm_WARN(sched, !s_job->credits, + "Jobs with zero credits bypass job-flow control.\n"); + } + + /* If a job exceeds the credit limit, truncate it to the credit limit + * itself to guarantee forward progress. + */ + if (drm_WARN(sched, s_job->credits > sched->credit_limit, + "Jobs may not exceed the credit limit, truncate.\n")) + s_job->credits = sched->credit_limit; + + return drm_sched_available_credits(sched) >= s_job->credits; +} + static __always_inline bool drm_sched_entity_compare_before(struct rb_node *a, const struct rb_node *b) { @@ -187,12 +255,18 @@ void drm_sched_rq_remove_entity(struct drm_sched_rq *rq, /** * drm_sched_rq_select_entity_rr - Select an entity which could provide a job to run * + * @sched: the gpu scheduler * @rq: scheduler run queue to check. * - * Try to find a ready entity, returns NULL if none found. + * Try to find the next ready entity. + * + * Return an entity if one is found; return an error-pointer (!NULL) if an + * entity was ready, but the scheduler had insufficient credits to accommodate + * its job; return NULL, if no ready entity was found. */ static struct drm_sched_entity * -drm_sched_rq_select_entity_rr(struct drm_sched_rq *rq) +drm_sched_rq_select_entity_rr(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq) { struct drm_sched_entity *entity; @@ -202,6 +276,14 @@ drm_sched_rq_select_entity_rr(struct drm_sched_rq *rq) if (entity) { list_for_each_entry_continue(entity, &rq->entities, list) { if (drm_sched_entity_is_ready(entity)) { + /* If we can't queue yet, preserve the current + * entity in terms of fairness. + */ + if (!drm_sched_can_queue(sched, entity)) { + spin_unlock(&rq->lock); + return ERR_PTR(-ENOSPC); + } + rq->current_entity = entity; reinit_completion(&entity->entity_idle); spin_unlock(&rq->lock); @@ -211,8 +293,15 @@ drm_sched_rq_select_entity_rr(struct drm_sched_rq *rq) } list_for_each_entry(entity, &rq->entities, list) { - if (drm_sched_entity_is_ready(entity)) { + /* If we can't queue yet, preserve the current entity in + * terms of fairness. + */ + if (!drm_sched_can_queue(sched, entity)) { + spin_unlock(&rq->lock); + return ERR_PTR(-ENOSPC); + } + rq->current_entity = entity; reinit_completion(&entity->entity_idle); spin_unlock(&rq->lock); @@ -231,12 +320,18 @@ drm_sched_rq_select_entity_rr(struct drm_sched_rq *rq) /** * drm_sched_rq_select_entity_fifo - Select an entity which provides a job to run * + * @sched: the gpu scheduler * @rq: scheduler run queue to check. * - * Find oldest waiting ready entity, returns NULL if none found. + * Find oldest waiting ready entity. + * + * Return an entity if one is found; return an error-pointer (!NULL) if an + * entity was ready, but the scheduler had insufficient credits to accommodate + * its job; return NULL, if no ready entity was found. */ static struct drm_sched_entity * -drm_sched_rq_select_entity_fifo(struct drm_sched_rq *rq) +drm_sched_rq_select_entity_fifo(struct drm_gpu_scheduler *sched, + struct drm_sched_rq *rq) { struct rb_node *rb; @@ -246,6 +341,14 @@ drm_sched_rq_select_entity_fifo(struct drm_sched_rq *rq) entity = rb_entry(rb, struct drm_sched_entity, rb_tree_node); if (drm_sched_entity_is_ready(entity)) { + /* If we can't queue yet, preserve the current entity in + * terms of fairness. + */ + if (!drm_sched_can_queue(sched, entity)) { + spin_unlock(&rq->lock); + return ERR_PTR(-ENOSPC); + } + rq->current_entity = entity; reinit_completion(&entity->entity_idle); break; @@ -257,6 +360,42 @@ drm_sched_rq_select_entity_fifo(struct drm_sched_rq *rq) } /** + * drm_sched_run_job_queue - enqueue run-job work + * @sched: scheduler instance + */ +static void drm_sched_run_job_queue(struct drm_gpu_scheduler *sched) +{ + if (!READ_ONCE(sched->pause_submit)) + queue_work(sched->submit_wq, &sched->work_run_job); +} + +/** + * __drm_sched_run_free_queue - enqueue free-job work + * @sched: scheduler instance + */ +static void __drm_sched_run_free_queue(struct drm_gpu_scheduler *sched) +{ + if (!READ_ONCE(sched->pause_submit)) + queue_work(sched->submit_wq, &sched->work_free_job); +} + +/** + * drm_sched_run_free_queue - enqueue free-job work if ready + * @sched: scheduler instance + */ +static void drm_sched_run_free_queue(struct drm_gpu_scheduler *sched) +{ + struct drm_sched_job *job; + + spin_lock(&sched->job_list_lock); + job = list_first_entry_or_null(&sched->pending_list, + struct drm_sched_job, list); + if (job && dma_fence_is_signaled(&job->s_fence->finished)) + __drm_sched_run_free_queue(sched); + spin_unlock(&sched->job_list_lock); +} + +/** * drm_sched_job_done - complete a job * @s_job: pointer to the job which is done * @@ -267,7 +406,7 @@ static void drm_sched_job_done(struct drm_sched_job *s_job, int result) struct drm_sched_fence *s_fence = s_job->s_fence; struct drm_gpu_scheduler *sched = s_fence->sched; - atomic_dec(&sched->hw_rq_count); + atomic_sub(s_job->credits, &sched->credit_count); atomic_dec(sched->score); trace_drm_sched_process_job(s_fence); @@ -275,7 +414,7 @@ static void drm_sched_job_done(struct drm_sched_job *s_job, int result) dma_fence_get(&s_fence->finished); drm_sched_fence_finished(s_fence, result); dma_fence_put(&s_fence->finished); - wake_up_interruptible(&sched->wake_up_worker); + __drm_sched_run_free_queue(sched); } /** @@ -299,10 +438,35 @@ static void drm_sched_job_done_cb(struct dma_fence *f, struct dma_fence_cb *cb) */ static void drm_sched_start_timeout(struct drm_gpu_scheduler *sched) { + lockdep_assert_held(&sched->job_list_lock); + if (sched->timeout != MAX_SCHEDULE_TIMEOUT && !list_empty(&sched->pending_list)) - queue_delayed_work(sched->timeout_wq, &sched->work_tdr, sched->timeout); + mod_delayed_work(sched->timeout_wq, &sched->work_tdr, sched->timeout); +} + +static void drm_sched_start_timeout_unlocked(struct drm_gpu_scheduler *sched) +{ + spin_lock(&sched->job_list_lock); + drm_sched_start_timeout(sched); + spin_unlock(&sched->job_list_lock); +} + +/** + * drm_sched_tdr_queue_imm: - immediately start job timeout handler + * + * @sched: scheduler for which the timeout handling should be started. + * + * Start timeout handling immediately for the named scheduler. + */ +void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched) +{ + spin_lock(&sched->job_list_lock); + sched->timeout = 0; + drm_sched_start_timeout(sched); + spin_unlock(&sched->job_list_lock); } +EXPORT_SYMBOL(drm_sched_tdr_queue_imm); /** * drm_sched_fault - immediately start timeout handler @@ -388,7 +552,7 @@ static void drm_sched_job_timedout(struct work_struct *work) sched = container_of(work, struct drm_gpu_scheduler, work_tdr.work); - /* Protects against concurrent deletion in drm_sched_get_cleanup_job */ + /* Protects against concurrent deletion in drm_sched_get_finished_job */ spin_lock(&sched->job_list_lock); job = list_first_entry_or_null(&sched->pending_list, struct drm_sched_job, list); @@ -416,11 +580,8 @@ static void drm_sched_job_timedout(struct work_struct *work) spin_unlock(&sched->job_list_lock); } - if (status != DRM_GPU_SCHED_STAT_ENODEV) { - spin_lock(&sched->job_list_lock); - drm_sched_start_timeout(sched); - spin_unlock(&sched->job_list_lock); - } + if (status != DRM_GPU_SCHED_STAT_ENODEV) + drm_sched_start_timeout_unlocked(sched); } /** @@ -439,13 +600,13 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) { struct drm_sched_job *s_job, *tmp; - kthread_park(sched->thread); + drm_sched_wqueue_stop(sched); /* * Reinsert back the bad job here - now it's safe as - * drm_sched_get_cleanup_job cannot race against us and release the + * drm_sched_get_finished_job cannot race against us and release the * bad job at this point - we parked (waited for) any in progress - * (earlier) cleanups and drm_sched_get_cleanup_job will not be called + * (earlier) cleanups and drm_sched_get_finished_job will not be called * now until the scheduler thread is unparked. */ if (bad && bad->sched == sched) @@ -468,7 +629,7 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) &s_job->cb)) { dma_fence_put(s_job->s_fence->parent); s_job->s_fence->parent = NULL; - atomic_dec(&sched->hw_rq_count); + atomic_sub(s_job->credits, &sched->credit_count); } else { /* * remove job from pending_list. @@ -529,7 +690,7 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) list_for_each_entry_safe(s_job, tmp, &sched->pending_list, list) { struct dma_fence *fence = s_job->s_fence->parent; - atomic_inc(&sched->hw_rq_count); + atomic_add(s_job->credits, &sched->credit_count); if (!full_recovery) continue; @@ -546,13 +707,10 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery) drm_sched_job_done(s_job, -ECANCELED); } - if (full_recovery) { - spin_lock(&sched->job_list_lock); - drm_sched_start_timeout(sched); - spin_unlock(&sched->job_list_lock); - } + if (full_recovery) + drm_sched_start_timeout_unlocked(sched); - kthread_unpark(sched->thread); + drm_sched_wqueue_start(sched); } EXPORT_SYMBOL(drm_sched_start); @@ -613,6 +771,8 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs); * drm_sched_job_init - init a scheduler job * @job: scheduler job to init * @entity: scheduler entity to use + * @credits: the number of credits this job contributes to the schedulers + * credit limit * @owner: job owner for debugging * * Refer to drm_sched_entity_push_job() documentation @@ -630,7 +790,7 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs); */ int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, - void *owner) + u32 credits, void *owner) { if (!entity->rq) { /* This will most likely be followed by missing frames @@ -641,7 +801,13 @@ int drm_sched_job_init(struct drm_sched_job *job, return -ENOENT; } + if (unlikely(!credits)) { + pr_err("*ERROR* %s: credits cannot be 0!\n", __func__); + return -EINVAL; + } + job->entity = entity; + job->credits = credits; job->s_fence = drm_sched_fence_alloc(entity, owner); if (!job->s_fence) return -ENOMEM; @@ -854,27 +1020,18 @@ void drm_sched_job_cleanup(struct drm_sched_job *job) EXPORT_SYMBOL(drm_sched_job_cleanup); /** - * drm_sched_can_queue -- Can we queue more to the hardware? - * @sched: scheduler instance - * - * Return true if we can push more jobs to the hw, otherwise false. - */ -static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched) -{ - return atomic_read(&sched->hw_rq_count) < - sched->hw_submission_limit; -} - -/** - * drm_sched_wakeup_if_can_queue - Wake up the scheduler + * drm_sched_wakeup - Wake up the scheduler if it is ready to queue * @sched: scheduler instance + * @entity: the scheduler entity * * Wake up the scheduler if we can queue jobs. */ -void drm_sched_wakeup_if_can_queue(struct drm_gpu_scheduler *sched) +void drm_sched_wakeup(struct drm_gpu_scheduler *sched, + struct drm_sched_entity *entity) { - if (drm_sched_can_queue(sched)) - wake_up_interruptible(&sched->wake_up_worker); + if (drm_sched_entity_is_ready(entity)) + if (drm_sched_can_queue(sched, entity)) + drm_sched_run_job_queue(sched); } /** @@ -882,7 +1039,11 @@ void drm_sched_wakeup_if_can_queue(struct drm_gpu_scheduler *sched) * * @sched: scheduler instance * - * Returns the entity to process or NULL if none are found. + * Return an entity to process or NULL if none are found. + * + * Note, that we break out of the for-loop when "entity" is non-null, which can + * also be an error-pointer--this assures we don't process lower priority + * run-queues. See comments in the respectively called functions. */ static struct drm_sched_entity * drm_sched_select_entity(struct drm_gpu_scheduler *sched) @@ -890,23 +1051,20 @@ drm_sched_select_entity(struct drm_gpu_scheduler *sched) struct drm_sched_entity *entity; int i; - if (!drm_sched_can_queue(sched)) - return NULL; - /* Kernel run queue has higher priority than normal run queue*/ for (i = sched->num_rqs - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) { entity = drm_sched_policy == DRM_SCHED_POLICY_FIFO ? - drm_sched_rq_select_entity_fifo(sched->sched_rq[i]) : - drm_sched_rq_select_entity_rr(sched->sched_rq[i]); + drm_sched_rq_select_entity_fifo(sched, sched->sched_rq[i]) : + drm_sched_rq_select_entity_rr(sched, sched->sched_rq[i]); if (entity) break; } - return entity; + return IS_ERR(entity) ? NULL : entity; } /** - * drm_sched_get_cleanup_job - fetch the next finished job to be destroyed + * drm_sched_get_finished_job - fetch the next finished job to be destroyed * * @sched: scheduler instance * @@ -914,7 +1072,7 @@ drm_sched_select_entity(struct drm_gpu_scheduler *sched) * ready for it to be destroyed. */ static struct drm_sched_job * -drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched) +drm_sched_get_finished_job(struct drm_gpu_scheduler *sched) { struct drm_sched_job *job, *next; @@ -934,8 +1092,10 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched) typeof(*next), list); if (next) { - next->s_fence->scheduled.timestamp = - dma_fence_timestamp(&job->s_fence->finished); + if (test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, + &next->s_fence->scheduled.flags)) + next->s_fence->scheduled.timestamp = + dma_fence_timestamp(&job->s_fence->finished); /* start TO timer for next job */ drm_sched_start_timeout(sched); } @@ -985,91 +1145,82 @@ drm_sched_pick_best(struct drm_gpu_scheduler **sched_list, EXPORT_SYMBOL(drm_sched_pick_best); /** - * drm_sched_blocked - check if the scheduler is blocked - * - * @sched: scheduler instance + * drm_sched_free_job_work - worker to call free_job * - * Returns true if blocked, otherwise false. + * @w: free job work */ -static bool drm_sched_blocked(struct drm_gpu_scheduler *sched) +static void drm_sched_free_job_work(struct work_struct *w) { - if (kthread_should_park()) { - kthread_parkme(); - return true; - } + struct drm_gpu_scheduler *sched = + container_of(w, struct drm_gpu_scheduler, work_free_job); + struct drm_sched_job *job; + + if (READ_ONCE(sched->pause_submit)) + return; - return false; + job = drm_sched_get_finished_job(sched); + if (job) + sched->ops->free_job(job); + + drm_sched_run_free_queue(sched); + drm_sched_run_job_queue(sched); } /** - * drm_sched_main - main scheduler thread - * - * @param: scheduler instance + * drm_sched_run_job_work - worker to call run_job * - * Returns 0. + * @w: run job work */ -static int drm_sched_main(void *param) +static void drm_sched_run_job_work(struct work_struct *w) { - struct drm_gpu_scheduler *sched = (struct drm_gpu_scheduler *)param; + struct drm_gpu_scheduler *sched = + container_of(w, struct drm_gpu_scheduler, work_run_job); + struct drm_sched_entity *entity; + struct dma_fence *fence; + struct drm_sched_fence *s_fence; + struct drm_sched_job *sched_job; int r; - sched_set_fifo_low(current); - - while (!kthread_should_stop()) { - struct drm_sched_entity *entity = NULL; - struct drm_sched_fence *s_fence; - struct drm_sched_job *sched_job; - struct dma_fence *fence; - struct drm_sched_job *cleanup_job = NULL; - - wait_event_interruptible(sched->wake_up_worker, - (cleanup_job = drm_sched_get_cleanup_job(sched)) || - (!drm_sched_blocked(sched) && - (entity = drm_sched_select_entity(sched))) || - kthread_should_stop()); - - if (cleanup_job) - sched->ops->free_job(cleanup_job); - - if (!entity) - continue; - - sched_job = drm_sched_entity_pop_job(entity); + if (READ_ONCE(sched->pause_submit)) + return; - if (!sched_job) { - complete_all(&entity->entity_idle); - continue; - } + entity = drm_sched_select_entity(sched); + if (!entity) + return; - s_fence = sched_job->s_fence; + sched_job = drm_sched_entity_pop_job(entity); + if (!sched_job) { + complete_all(&entity->entity_idle); + return; /* No more work */ + } - atomic_inc(&sched->hw_rq_count); - drm_sched_job_begin(sched_job); + s_fence = sched_job->s_fence; - trace_drm_run_job(sched_job, entity); - fence = sched->ops->run_job(sched_job); - complete_all(&entity->entity_idle); - drm_sched_fence_scheduled(s_fence, fence); + atomic_add(sched_job->credits, &sched->credit_count); + drm_sched_job_begin(sched_job); - if (!IS_ERR_OR_NULL(fence)) { - /* Drop for original kref_init of the fence */ - dma_fence_put(fence); + trace_drm_run_job(sched_job, entity); + fence = sched->ops->run_job(sched_job); + complete_all(&entity->entity_idle); + drm_sched_fence_scheduled(s_fence, fence); - r = dma_fence_add_callback(fence, &sched_job->cb, - drm_sched_job_done_cb); - if (r == -ENOENT) - drm_sched_job_done(sched_job, fence->error); - else if (r) - DRM_DEV_ERROR(sched->dev, "fence add callback failed (%d)\n", - r); - } else { - drm_sched_job_done(sched_job, IS_ERR(fence) ? - PTR_ERR(fence) : 0); - } + if (!IS_ERR_OR_NULL(fence)) { + /* Drop for original kref_init of the fence */ + dma_fence_put(fence); - wake_up(&sched->job_scheduled); + r = dma_fence_add_callback(fence, &sched_job->cb, + drm_sched_job_done_cb); + if (r == -ENOENT) + drm_sched_job_done(sched_job, fence->error); + else if (r) + DRM_DEV_ERROR(sched->dev, "fence add callback failed (%d)\n", r); + } else { + drm_sched_job_done(sched_job, IS_ERR(fence) ? + PTR_ERR(fence) : 0); } - return 0; + + wake_up(&sched->job_scheduled); + drm_sched_run_job_queue(sched); } /** @@ -1077,8 +1228,10 @@ static int drm_sched_main(void *param) * * @sched: scheduler instance * @ops: backend operations for this scheduler + * @submit_wq: workqueue to use for submission. If NULL, an ordered wq is + * allocated and used * @num_rqs: number of runqueues, one for each priority, up to DRM_SCHED_PRIORITY_COUNT - * @hw_submission: number of hw submissions that can be in flight + * @credit_limit: the number of credits this scheduler can hold from all jobs * @hang_limit: number of times to allow a job to hang before dropping it * @timeout: timeout value in jiffies for the scheduler * @timeout_wq: workqueue to use for timeout work. If NULL, the system_wq is @@ -1091,14 +1244,15 @@ static int drm_sched_main(void *param) */ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_backend_ops *ops, - u32 num_rqs, uint32_t hw_submission, unsigned int hang_limit, + struct workqueue_struct *submit_wq, + u32 num_rqs, u32 credit_limit, unsigned int hang_limit, long timeout, struct workqueue_struct *timeout_wq, atomic_t *score, const char *name, struct device *dev) { int i, ret; sched->ops = ops; - sched->hw_submission_limit = hw_submission; + sched->credit_limit = credit_limit; sched->name = name; sched->timeout = timeout; sched->timeout_wq = timeout_wq ? : system_wq; @@ -1121,14 +1275,22 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, return 0; } + if (submit_wq) { + sched->submit_wq = submit_wq; + sched->own_submit_wq = false; + } else { + sched->submit_wq = alloc_ordered_workqueue(name, 0); + if (!sched->submit_wq) + return -ENOMEM; + + sched->own_submit_wq = true; + } + ret = -ENOMEM; sched->sched_rq = kmalloc_array(num_rqs, sizeof(*sched->sched_rq), GFP_KERNEL | __GFP_ZERO); - if (!sched->sched_rq) { - drm_err(sched, "%s: out of memory for sched_rq\n", __func__); - return -ENOMEM; - } + if (!sched->sched_rq) + goto Out_free; sched->num_rqs = num_rqs; - ret = -ENOMEM; for (i = DRM_SCHED_PRIORITY_MIN; i < sched->num_rqs; i++) { sched->sched_rq[i] = kzalloc(sizeof(*sched->sched_rq[i]), GFP_KERNEL); if (!sched->sched_rq[i]) @@ -1136,31 +1298,27 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, drm_sched_rq_init(sched, sched->sched_rq[i]); } - init_waitqueue_head(&sched->wake_up_worker); init_waitqueue_head(&sched->job_scheduled); INIT_LIST_HEAD(&sched->pending_list); spin_lock_init(&sched->job_list_lock); - atomic_set(&sched->hw_rq_count, 0); + atomic_set(&sched->credit_count, 0); INIT_DELAYED_WORK(&sched->work_tdr, drm_sched_job_timedout); + INIT_WORK(&sched->work_run_job, drm_sched_run_job_work); + INIT_WORK(&sched->work_free_job, drm_sched_free_job_work); atomic_set(&sched->_score, 0); atomic64_set(&sched->job_id_count, 0); - - /* Each scheduler will run on a seperate kernel thread */ - sched->thread = kthread_run(drm_sched_main, sched, sched->name); - if (IS_ERR(sched->thread)) { - ret = PTR_ERR(sched->thread); - sched->thread = NULL; - DRM_DEV_ERROR(sched->dev, "Failed to create scheduler for %s.\n", name); - goto Out_unroll; - } + sched->pause_submit = false; sched->ready = true; return 0; Out_unroll: for (--i ; i >= DRM_SCHED_PRIORITY_MIN; i--) kfree(sched->sched_rq[i]); +Out_free: kfree(sched->sched_rq); sched->sched_rq = NULL; + if (sched->own_submit_wq) + destroy_workqueue(sched->submit_wq); drm_err(sched, "%s: Failed to setup GPU scheduler--out of memory\n", __func__); return ret; } @@ -1178,8 +1336,7 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) struct drm_sched_entity *s_entity; int i; - if (sched->thread) - kthread_stop(sched->thread); + drm_sched_wqueue_stop(sched); for (i = sched->num_rqs - 1; i >= DRM_SCHED_PRIORITY_MIN; i--) { struct drm_sched_rq *rq = sched->sched_rq[i]; @@ -1202,6 +1359,8 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) /* Confirm no work left behind accessing device structures */ cancel_delayed_work_sync(&sched->work_tdr); + if (sched->own_submit_wq) + destroy_workqueue(sched->submit_wq); sched->ready = false; kfree(sched->sched_rq); sched->sched_rq = NULL; @@ -1252,3 +1411,42 @@ void drm_sched_increase_karma(struct drm_sched_job *bad) } } EXPORT_SYMBOL(drm_sched_increase_karma); + +/** + * drm_sched_wqueue_ready - Is the scheduler ready for submission + * + * @sched: scheduler instance + * + * Returns true if submission is ready + */ +bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched) +{ + return sched->ready; +} +EXPORT_SYMBOL(drm_sched_wqueue_ready); + +/** + * drm_sched_wqueue_stop - stop scheduler submission + * + * @sched: scheduler instance + */ +void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched) +{ + WRITE_ONCE(sched->pause_submit, true); + cancel_work_sync(&sched->work_run_job); + cancel_work_sync(&sched->work_free_job); +} +EXPORT_SYMBOL(drm_sched_wqueue_stop); + +/** + * drm_sched_wqueue_start - start scheduler submission + * + * @sched: scheduler instance + */ +void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched) +{ + WRITE_ONCE(sched->pause_submit, false); + queue_work(sched->submit_wq, &sched->work_run_job); + queue_work(sched->submit_wq, &sched->work_free_job); +} +EXPORT_SYMBOL(drm_sched_wqueue_start); diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index e0174f82e353..bef293922b98 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -808,7 +808,8 @@ static void ssd132x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array) static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect, - u8 *buf, u8 *data_array) + u8 *buf, u8 *data_array, + struct drm_format_conv_state *fmtcnv_state) { struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); struct iosys_map dst; @@ -826,7 +827,7 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, return ret; iosys_map_set_vaddr(&dst, buf); - drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect); + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); @@ -838,7 +839,8 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect, u8 *buf, - u8 *data_array) + u8 *data_array, + struct drm_format_conv_state *fmtcnv_state) { struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); unsigned int dst_pitch = drm_rect_width(rect); @@ -855,7 +857,7 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb, return ret; iosys_map_set_vaddr(&dst, buf); - drm_fb_xrgb8888_to_gray8(&dst, &dst_pitch, vmap, fb, rect); + drm_fb_xrgb8888_to_gray8(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); @@ -871,6 +873,7 @@ static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane, struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state); + struct drm_shadow_plane_state *shadow_plane_state = &ssd130x_state->base; struct drm_crtc *crtc = plane_state->crtc; struct drm_crtc_state *crtc_state = NULL; const struct drm_format_info *fi; @@ -895,6 +898,16 @@ static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane, pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); + if (plane_state->fb->format != fi) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf = drm_format_conv_state_reserve(&shadow_plane_state->fmtcnv_state, + pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); if (!ssd130x_state->buffer) return -ENOMEM; @@ -909,6 +922,7 @@ static int ssd132x_primary_plane_atomic_check(struct drm_plane *plane, struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state); + struct drm_shadow_plane_state *shadow_plane_state = &ssd130x_state->base; struct drm_crtc *crtc = plane_state->crtc; struct drm_crtc_state *crtc_state = NULL; const struct drm_format_info *fi; @@ -933,6 +947,16 @@ static int ssd132x_primary_plane_atomic_check(struct drm_plane *plane, pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); + if (plane_state->fb->format != fi) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf = drm_format_conv_state_reserve(&shadow_plane_state->fmtcnv_state, + pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL); if (!ssd130x_state->buffer) return -ENOMEM; @@ -968,7 +992,8 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane, ssd130x_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip, ssd130x_plane_state->buffer, - ssd130x_crtc_state->data_array); + ssd130x_crtc_state->data_array, + &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); @@ -1002,7 +1027,8 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane, ssd132x_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip, ssd130x_plane_state->buffer, - ssd130x_crtc_state->data_array); + ssd130x_crtc_state->data_array, + &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c index 48183bbd0590..deb3bb96e2a8 100644 --- a/drivers/gpu/drm/sprd/sprd_dpu.c +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -859,16 +859,14 @@ static int sprd_dpu_probe(struct platform_device *pdev) return component_add(&pdev->dev, &dpu_component_ops); } -static int sprd_dpu_remove(struct platform_device *pdev) +static void sprd_dpu_remove(struct platform_device *pdev) { component_del(&pdev->dev, &dpu_component_ops); - - return 0; } struct platform_driver sprd_dpu_driver = { .probe = sprd_dpu_probe, - .remove = sprd_dpu_remove, + .remove_new = sprd_dpu_remove, .driver = { .name = "sprd-dpu-drv", .of_match_table = dpu_match_table, diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c index 0aa39156f2fa..a74cd0caf645 100644 --- a/drivers/gpu/drm/sprd/sprd_drm.c +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -138,10 +138,9 @@ static int sprd_drm_probe(struct platform_device *pdev) return drm_of_component_probe(&pdev->dev, component_compare_of, &drm_component_ops); } -static int sprd_drm_remove(struct platform_device *pdev) +static void sprd_drm_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &drm_component_ops); - return 0; } static void sprd_drm_shutdown(struct platform_device *pdev) @@ -164,7 +163,7 @@ MODULE_DEVICE_TABLE(of, drm_match_table); static struct platform_driver sprd_drm_driver = { .probe = sprd_drm_probe, - .remove = sprd_drm_remove, + .remove_new = sprd_drm_remove, .shutdown = sprd_drm_shutdown, .driver = { .name = "sprd-drm-drv", diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c index d7b143a75601..0b69c140eab3 100644 --- a/drivers/gpu/drm/sprd/sprd_dsi.c +++ b/drivers/gpu/drm/sprd/sprd_dsi.c @@ -1051,18 +1051,16 @@ static int sprd_dsi_probe(struct platform_device *pdev) return mipi_dsi_host_register(&dsi->host); } -static int sprd_dsi_remove(struct platform_device *pdev) +static void sprd_dsi_remove(struct platform_device *pdev) { struct sprd_dsi *dsi = dev_get_drvdata(&pdev->dev); mipi_dsi_host_unregister(&dsi->host); - - return 0; } struct platform_driver sprd_dsi_driver = { .probe = sprd_dsi_probe, - .remove = sprd_dsi_remove, + .remove_new = sprd_dsi_remove, .driver = { .name = "sprd-dsi-drv", .of_match_table = dsi_match_table, diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 0ba3ca3ac509..a1fcee665023 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -24,6 +24,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_debugfs.h> +#include <drm/drm_eld.h> #include <drm/drm_file.h> #include <drm/drm_fourcc.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index d5a3d3f4fece..83341576630d 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -20,6 +20,7 @@ #include <drm/display/drm_scdc_helper.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_debugfs.h> +#include <drm/drm_eld.h> #include <drm/drm_file.h> #include <drm/drm_panel.h> #include <drm/drm_simple_kms_helper.h> diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index ba7baa622675..2645af241ff0 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_connector_test.o \ drm_damage_helper_test.o \ drm_dp_mst_helper_test.o \ + drm_exec_test.o \ drm_format_helper_test.o \ drm_format_test.o \ drm_framebuffer_test.o \ @@ -17,7 +18,6 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_modes_test.o \ drm_plane_helper_test.o \ drm_probe_helper_test.o \ - drm_rect_test.o \ - drm_exec_test.o + drm_rect_test.o CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c index 09ee6f6af896..ea2af6bd9abe 100644 --- a/drivers/gpu/drm/tests/drm_buddy_test.c +++ b/drivers/gpu/drm/tests/drm_buddy_test.c @@ -13,315 +13,11 @@ #include "../lib/drm_random.h" -#define TIMEOUT(name__) \ - unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT - -static unsigned int random_seed; - static inline u64 get_size(int order, u64 chunk_size) { return (1 << order) * chunk_size; } -__printf(2, 3) -static bool __timeout(unsigned long timeout, const char *fmt, ...) -{ - va_list va; - - if (!signal_pending(current)) { - cond_resched(); - if (time_before(jiffies, timeout)) - return false; - } - - if (fmt) { - va_start(va, fmt); - vprintk(fmt, va); - va_end(va); - } - - return true; -} - -static void __dump_block(struct kunit *test, struct drm_buddy *mm, - struct drm_buddy_block *block, bool buddy) -{ - kunit_err(test, "block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%d buddy=%d\n", - block->header, drm_buddy_block_state(block), - drm_buddy_block_order(block), drm_buddy_block_offset(block), - drm_buddy_block_size(mm, block), !block->parent, buddy); -} - -static void dump_block(struct kunit *test, struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - struct drm_buddy_block *buddy; - - __dump_block(test, mm, block, false); - - buddy = drm_get_buddy(block); - if (buddy) - __dump_block(test, mm, buddy, true); -} - -static int check_block(struct kunit *test, struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - struct drm_buddy_block *buddy; - unsigned int block_state; - u64 block_size; - u64 offset; - int err = 0; - - block_state = drm_buddy_block_state(block); - - if (block_state != DRM_BUDDY_ALLOCATED && - block_state != DRM_BUDDY_FREE && block_state != DRM_BUDDY_SPLIT) { - kunit_err(test, "block state mismatch\n"); - err = -EINVAL; - } - - block_size = drm_buddy_block_size(mm, block); - offset = drm_buddy_block_offset(block); - - if (block_size < mm->chunk_size) { - kunit_err(test, "block size smaller than min size\n"); - err = -EINVAL; - } - - /* We can't use is_power_of_2() for a u64 on 32-bit systems. */ - if (block_size & (block_size - 1)) { - kunit_err(test, "block size not power of two\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(block_size, mm->chunk_size)) { - kunit_err(test, "block size not aligned to min size\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(offset, mm->chunk_size)) { - kunit_err(test, "block offset not aligned to min size\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(offset, block_size)) { - kunit_err(test, "block offset not aligned to block size\n"); - err = -EINVAL; - } - - buddy = drm_get_buddy(block); - - if (!buddy && block->parent) { - kunit_err(test, "buddy has gone fishing\n"); - err = -EINVAL; - } - - if (buddy) { - if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { - kunit_err(test, "buddy has wrong offset\n"); - err = -EINVAL; - } - - if (drm_buddy_block_size(mm, buddy) != block_size) { - kunit_err(test, "buddy size mismatch\n"); - err = -EINVAL; - } - - if (drm_buddy_block_state(buddy) == block_state && - block_state == DRM_BUDDY_FREE) { - kunit_err(test, "block and its buddy are free\n"); - err = -EINVAL; - } - } - - return err; -} - -static int check_blocks(struct kunit *test, struct drm_buddy *mm, - struct list_head *blocks, u64 expected_size, bool is_contiguous) -{ - struct drm_buddy_block *block; - struct drm_buddy_block *prev; - u64 total; - int err = 0; - - block = NULL; - prev = NULL; - total = 0; - - list_for_each_entry(block, blocks, link) { - err = check_block(test, mm, block); - - if (!drm_buddy_block_is_allocated(block)) { - kunit_err(test, "block not allocated\n"); - err = -EINVAL; - } - - if (is_contiguous && prev) { - u64 prev_block_size; - u64 prev_offset; - u64 offset; - - prev_offset = drm_buddy_block_offset(prev); - prev_block_size = drm_buddy_block_size(mm, prev); - offset = drm_buddy_block_offset(block); - - if (offset != (prev_offset + prev_block_size)) { - kunit_err(test, "block offset mismatch\n"); - err = -EINVAL; - } - } - - if (err) - break; - - total += drm_buddy_block_size(mm, block); - prev = block; - } - - if (!err) { - if (total != expected_size) { - kunit_err(test, "size mismatch, expected=%llx, found=%llx\n", - expected_size, total); - err = -EINVAL; - } - return err; - } - - if (prev) { - kunit_err(test, "prev block, dump:\n"); - dump_block(test, mm, prev); - } - - kunit_err(test, "bad block, dump:\n"); - dump_block(test, mm, block); - - return err; -} - -static int check_mm(struct kunit *test, struct drm_buddy *mm) -{ - struct drm_buddy_block *root; - struct drm_buddy_block *prev; - unsigned int i; - u64 total; - int err = 0; - - if (!mm->n_roots) { - kunit_err(test, "n_roots is zero\n"); - return -EINVAL; - } - - if (mm->n_roots != hweight64(mm->size)) { - kunit_err(test, "n_roots mismatch, n_roots=%u, expected=%lu\n", - mm->n_roots, hweight64(mm->size)); - return -EINVAL; - } - - root = NULL; - prev = NULL; - total = 0; - - for (i = 0; i < mm->n_roots; ++i) { - struct drm_buddy_block *block; - unsigned int order; - - root = mm->roots[i]; - if (!root) { - kunit_err(test, "root(%u) is NULL\n", i); - err = -EINVAL; - break; - } - - err = check_block(test, mm, root); - - if (!drm_buddy_block_is_free(root)) { - kunit_err(test, "root not free\n"); - err = -EINVAL; - } - - order = drm_buddy_block_order(root); - - if (!i) { - if (order != mm->max_order) { - kunit_err(test, "max order root missing\n"); - err = -EINVAL; - } - } - - if (prev) { - u64 prev_block_size; - u64 prev_offset; - u64 offset; - - prev_offset = drm_buddy_block_offset(prev); - prev_block_size = drm_buddy_block_size(mm, prev); - offset = drm_buddy_block_offset(root); - - if (offset != (prev_offset + prev_block_size)) { - kunit_err(test, "root offset mismatch\n"); - err = -EINVAL; - } - } - - block = list_first_entry_or_null(&mm->free_list[order], - struct drm_buddy_block, link); - if (block != root) { - kunit_err(test, "root mismatch at order=%u\n", order); - err = -EINVAL; - } - - if (err) - break; - - prev = root; - total += drm_buddy_block_size(mm, root); - } - - if (!err) { - if (total != mm->size) { - kunit_err(test, "expected mm size=%llx, found=%llx\n", - mm->size, total); - err = -EINVAL; - } - return err; - } - - if (prev) { - kunit_err(test, "prev root(%u), dump:\n", i - 1); - dump_block(test, mm, prev); - } - - if (root) { - kunit_err(test, "bad root(%u), dump:\n", i); - dump_block(test, mm, root); - } - - return err; -} - -static void mm_config(u64 *size, u64 *chunk_size) -{ - DRM_RND_STATE(prng, random_seed); - u32 s, ms; - - /* Nothing fancy, just try to get an interesting bit pattern */ - - prandom_seed_state(&prng, random_seed); - - /* Let size be a random number of pages up to 8 GB (2M pages) */ - s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); - /* Let the chunk size be a random power of 2 less than size */ - ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); - /* Round size down to the chunk size */ - s &= -ms; - - /* Convert from pages to bytes */ - *chunk_size = (u64)ms << 12; - *size = (u64)s << 12; -} - static void drm_test_buddy_alloc_pathological(struct kunit *test) { u64 mm_size, size, start = 0; @@ -403,96 +99,6 @@ static void drm_test_buddy_alloc_pathological(struct kunit *test) drm_buddy_fini(&mm); } -static void drm_test_buddy_alloc_smoke(struct kunit *test) -{ - u64 mm_size, chunk_size, start = 0; - unsigned long flags = 0; - struct drm_buddy mm; - int *order; - int i; - - DRM_RND_STATE(prng, random_seed); - TIMEOUT(end_time); - - mm_config(&mm_size, &chunk_size); - - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, chunk_size), - "buddy_init failed\n"); - - order = drm_random_order(mm.max_order + 1, &prng); - KUNIT_ASSERT_TRUE(test, order); - - for (i = 0; i <= mm.max_order; ++i) { - struct drm_buddy_block *block; - int max_order = order[i]; - bool timeout = false; - LIST_HEAD(blocks); - u64 total, size; - LIST_HEAD(tmp); - int order, err; - - KUNIT_ASSERT_FALSE_MSG(test, check_mm(test, &mm), - "pre-mm check failed, abort\n"); - - order = max_order; - total = 0; - - do { -retry: - size = get_size(order, chunk_size); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags); - if (err) { - if (err == -ENOMEM) { - KUNIT_FAIL(test, "buddy_alloc hit -ENOMEM with order=%d\n", - order); - } else { - if (order--) { - err = 0; - goto retry; - } - - KUNIT_FAIL(test, "buddy_alloc with order=%d failed\n", - order); - } - - break; - } - - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); - KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); - - list_move_tail(&block->link, &blocks); - KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), order, - "buddy_alloc order mismatch\n"); - - total += drm_buddy_block_size(&mm, block); - - if (__timeout(end_time, NULL)) { - timeout = true; - break; - } - } while (total < mm.size); - - if (!err) - err = check_blocks(test, &mm, &blocks, total, false); - - drm_buddy_free_list(&mm, &blocks); - - if (!err) { - KUNIT_EXPECT_FALSE_MSG(test, check_mm(test, &mm), - "post-mm check failed\n"); - } - - if (err || timeout) - break; - - cond_resched(); - } - - kfree(order); - drm_buddy_fini(&mm); -} - static void drm_test_buddy_alloc_pessimistic(struct kunit *test) { u64 mm_size, size, start = 0; @@ -634,64 +240,6 @@ static void drm_test_buddy_alloc_optimistic(struct kunit *test) drm_buddy_fini(&mm); } -static void drm_test_buddy_alloc_range(struct kunit *test) -{ - unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; - u64 offset, size, rem, chunk_size, end; - unsigned long page_num; - struct drm_buddy mm; - LIST_HEAD(blocks); - - mm_config(&size, &chunk_size); - - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, size, chunk_size), - "buddy_init failed"); - - KUNIT_ASSERT_FALSE_MSG(test, check_mm(test, &mm), - "pre-mm check failed, abort!"); - - rem = mm.size; - offset = 0; - - for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { - struct drm_buddy_block *block; - LIST_HEAD(tmp); - - size = min(page_num * mm.chunk_size, rem); - end = offset + size; - - KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, offset, end, - size, mm.chunk_size, - &tmp, flags), - "alloc_range with offset=%llx, size=%llx failed\n", offset, size); - - block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); - KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_range has no blocks\n"); - - KUNIT_ASSERT_EQ_MSG(test, drm_buddy_block_offset(block), offset, - "alloc_range start offset mismatch, found=%llx, expected=%llx\n", - drm_buddy_block_offset(block), offset); - - KUNIT_ASSERT_FALSE(test, check_blocks(test, &mm, &tmp, size, true)); - - list_splice_tail(&tmp, &blocks); - - offset += size; - - rem -= size; - if (!rem) - break; - - cond_resched(); - } - - drm_buddy_free_list(&mm, &blocks); - - KUNIT_EXPECT_FALSE_MSG(test, check_mm(test, &mm), "post-mm check failed\n"); - - drm_buddy_fini(&mm); -} - static void drm_test_buddy_alloc_limit(struct kunit *test) { u64 size = U64_MAX, start = 0; @@ -727,29 +275,16 @@ static void drm_test_buddy_alloc_limit(struct kunit *test) drm_buddy_fini(&mm); } -static int drm_buddy_suite_init(struct kunit_suite *suite) -{ - while (!random_seed) - random_seed = get_random_u32(); - - kunit_info(suite, "Testing DRM buddy manager, with random_seed=0x%x\n", random_seed); - - return 0; -} - static struct kunit_case drm_buddy_tests[] = { KUNIT_CASE(drm_test_buddy_alloc_limit), - KUNIT_CASE(drm_test_buddy_alloc_range), KUNIT_CASE(drm_test_buddy_alloc_optimistic), KUNIT_CASE(drm_test_buddy_alloc_pessimistic), - KUNIT_CASE(drm_test_buddy_alloc_smoke), KUNIT_CASE(drm_test_buddy_alloc_pathological), {} }; static struct kunit_suite drm_buddy_test_suite = { .name = "drm_buddy", - .suite_init = drm_buddy_suite_init, .test_cases = drm_buddy_tests, }; diff --git a/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c index 545beea33e8c..d916e548fcb1 100644 --- a/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c +++ b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c @@ -42,13 +42,13 @@ static const struct drm_dp_mst_calc_pbn_mode_test drm_dp_mst_calc_pbn_mode_cases .clock = 332880, .bpp = 24, .dsc = true, - .expected = 50 + .expected = 1191 }, { .clock = 324540, .bpp = 24, .dsc = true, - .expected = 49 + .expected = 1161 }, }; @@ -56,7 +56,7 @@ static void drm_test_dp_mst_calc_pbn_mode(struct kunit *test) { const struct drm_dp_mst_calc_pbn_mode_test *params = test->param_value; - KUNIT_EXPECT_EQ(test, drm_dp_calc_pbn_mode(params->clock, params->bpp, params->dsc), + KUNIT_EXPECT_EQ(test, drm_dp_calc_pbn_mode(params->clock, params->bpp << 4), params->expected); } @@ -68,6 +68,152 @@ static void dp_mst_calc_pbn_mode_desc(const struct drm_dp_mst_calc_pbn_mode_test KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_cases, dp_mst_calc_pbn_mode_desc); +struct drm_dp_mst_calc_pbn_div_test { + int link_rate; + int lane_count; + fixed20_12 expected; +}; + +#define fp_init(__int, __frac) { \ + .full = (__int) * (1 << 12) + \ + (__frac) * (1 << 12) / 100000 \ +} + +static const struct drm_dp_mst_calc_pbn_div_test drm_dp_mst_calc_pbn_div_dp1_4_cases[] = { + /* + * UHBR rates (DP Standard v2.1 2.7.6.3, specifying the rounded to + * closest value to 2 decimal places): + * .expected = .link_rate * .lane_count * 0.9671 / 8 / 54 / 100 + * DP1.4 rates (DP Standard v2.1 2.6.4.2): + * .expected = .link_rate * .lane_count * 0.8000 / 8 / 54 / 100 + * + * truncated to 5 decimal places. + */ + { + .link_rate = 2000000, + .lane_count = 4, + .expected = fp_init(179, 9259), /* 179.09259 */ + }, + { + .link_rate = 2000000, + .lane_count = 2, + .expected = fp_init(89, 54629), + }, + { + .link_rate = 2000000, + .lane_count = 1, + .expected = fp_init(44, 77314), + }, + { + .link_rate = 1350000, + .lane_count = 4, + .expected = fp_init(120, 88750), + }, + { + .link_rate = 1350000, + .lane_count = 2, + .expected = fp_init(60, 44375), + }, + { + .link_rate = 1350000, + .lane_count = 1, + .expected = fp_init(30, 22187), + }, + { + .link_rate = 1000000, + .lane_count = 4, + .expected = fp_init(89, 54629), + }, + { + .link_rate = 1000000, + .lane_count = 2, + .expected = fp_init(44, 77314), + }, + { + .link_rate = 1000000, + .lane_count = 1, + .expected = fp_init(22, 38657), + }, + { + .link_rate = 810000, + .lane_count = 4, + .expected = fp_init(60, 0), + }, + { + .link_rate = 810000, + .lane_count = 2, + .expected = fp_init(30, 0), + }, + { + .link_rate = 810000, + .lane_count = 1, + .expected = fp_init(15, 0), + }, + { + .link_rate = 540000, + .lane_count = 4, + .expected = fp_init(40, 0), + }, + { + .link_rate = 540000, + .lane_count = 2, + .expected = fp_init(20, 0), + }, + { + .link_rate = 540000, + .lane_count = 1, + .expected = fp_init(10, 0), + }, + { + .link_rate = 270000, + .lane_count = 4, + .expected = fp_init(20, 0), + }, + { + .link_rate = 270000, + .lane_count = 2, + .expected = fp_init(10, 0), + }, + { + .link_rate = 270000, + .lane_count = 1, + .expected = fp_init(5, 0), + }, + { + .link_rate = 162000, + .lane_count = 4, + .expected = fp_init(12, 0), + }, + { + .link_rate = 162000, + .lane_count = 2, + .expected = fp_init(6, 0), + }, + { + .link_rate = 162000, + .lane_count = 1, + .expected = fp_init(3, 0), + }, +}; + +static void drm_test_dp_mst_calc_pbn_div(struct kunit *test) +{ + const struct drm_dp_mst_calc_pbn_div_test *params = test->param_value; + /* mgr->dev is only needed by drm_dbg_kms(), but it's not called for the test cases. */ + struct drm_dp_mst_topology_mgr *mgr = test->priv; + + KUNIT_EXPECT_EQ(test, drm_dp_get_vc_payload_bw(mgr, params->link_rate, params->lane_count).full, + params->expected.full); +} + +static void dp_mst_calc_pbn_div_desc(const struct drm_dp_mst_calc_pbn_div_test *t, char *desc) +{ + sprintf(desc, "Link rate %d lane count %d", t->link_rate, t->lane_count); +} + +KUNIT_ARRAY_PARAM(drm_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_dp1_4_cases, + dp_mst_calc_pbn_div_desc); + static u8 data[] = { 0xff, 0x00, 0xdd }; struct drm_dp_mst_sideband_msg_req_test { @@ -416,13 +562,27 @@ KUNIT_ARRAY_PARAM(drm_dp_mst_sideband_msg_req, drm_dp_mst_sideband_msg_req_cases static struct kunit_case drm_dp_mst_helper_tests[] = { KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_mode, drm_dp_mst_calc_pbn_mode_gen_params), + KUNIT_CASE_PARAM(drm_test_dp_mst_calc_pbn_div, drm_dp_mst_calc_pbn_div_gen_params), KUNIT_CASE_PARAM(drm_test_dp_mst_sideband_msg_req_decode, drm_dp_mst_sideband_msg_req_gen_params), { } }; +static int drm_dp_mst_helper_tests_init(struct kunit *test) +{ + struct drm_dp_mst_topology_mgr *mgr; + + mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mgr); + + test->priv = mgr; + + return 0; +} + static struct kunit_suite drm_dp_mst_helper_test_suite = { .name = "drm_dp_mst_helper", + .init = drm_dp_mst_helper_tests_init, .test_cases = drm_dp_mst_helper_tests, }; diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c index f6408e56f786..08992636ec05 100644 --- a/drivers/gpu/drm/tests/drm_format_helper_test.c +++ b/drivers/gpu/drm/tests/drm_format_helper_test.c @@ -20,6 +20,10 @@ #define TEST_USE_DEFAULT_PITCH 0 +static unsigned char fmtcnv_state_mem[PAGE_SIZE]; +static struct drm_format_conv_state fmtcnv_state = + DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED(fmtcnv_state_mem, sizeof(fmtcnv_state_mem)); + struct convert_to_gray8_result { unsigned int dst_pitch; const u8 expected[TEST_BUF_SIZE]; @@ -630,8 +634,7 @@ static void drm_test_fb_xrgb8888_to_gray8(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_gray8(&dst, dst_pitch, &src, &fb, ¶ms->clip); - + drm_fb_xrgb8888_to_gray8(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -664,7 +667,7 @@ static void drm_test_fb_xrgb8888_to_rgb332(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_rgb332(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_rgb332(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -697,12 +700,14 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, ¶ms->clip, false); + drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, ¶ms->clip, + &fmtcnv_state, false); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); buf = dst.vaddr; /* restore original value of buf */ - drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip, true); + drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip, + &fmtcnv_state, true); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected_swab, dst_size); @@ -711,7 +716,8 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGB565, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGB565, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); @@ -748,7 +754,7 @@ static void drm_test_fb_xrgb8888_to_xrgb1555(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_xrgb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_xrgb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -757,7 +763,8 @@ static void drm_test_fb_xrgb8888_to_xrgb1555(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB1555, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB1555, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); @@ -794,7 +801,7 @@ static void drm_test_fb_xrgb8888_to_argb1555(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_argb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_argb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -803,7 +810,8 @@ static void drm_test_fb_xrgb8888_to_argb1555(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ARGB1555, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ARGB1555, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); @@ -840,7 +848,7 @@ static void drm_test_fb_xrgb8888_to_rgba5551(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_rgba5551(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_rgba5551(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -849,7 +857,8 @@ static void drm_test_fb_xrgb8888_to_rgba5551(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGBA5551, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGBA5551, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); @@ -890,7 +899,7 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_rgb888(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_rgb888(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); buf = dst.vaddr; /* restore original value of buf */ @@ -898,7 +907,8 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGB888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_RGB888, &src, &fb, ¶ms->clip, + &fmtcnv_state); KUNIT_EXPECT_FALSE(test, blit_result); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -933,7 +943,7 @@ static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_argb8888(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_argb8888(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -942,7 +952,8 @@ static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ARGB8888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ARGB8888, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); @@ -979,7 +990,7 @@ static void drm_test_fb_xrgb8888_to_xrgb2101010(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_xrgb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_xrgb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le32buf_to_cpu(test, buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -989,7 +1000,7 @@ static void drm_test_fb_xrgb8888_to_xrgb2101010(struct kunit *test) int blit_result = 0; blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB2101010, &src, &fb, - ¶ms->clip); + ¶ms->clip, &fmtcnv_state); KUNIT_EXPECT_FALSE(test, blit_result); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -1024,7 +1035,7 @@ static void drm_test_fb_xrgb8888_to_argb2101010(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_argb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_argb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -1034,7 +1045,7 @@ static void drm_test_fb_xrgb8888_to_argb2101010(struct kunit *test) int blit_result = 0; blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ARGB2101010, &src, &fb, - ¶ms->clip); + ¶ms->clip, &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); @@ -1071,7 +1082,7 @@ static void drm_test_fb_xrgb8888_to_mono(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_xrgb8888_to_mono(&dst, dst_pitch, &src, &fb, ¶ms->clip); + drm_fb_xrgb8888_to_mono(&dst, dst_pitch, &src, &fb, ¶ms->clip, &fmtcnv_state); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -1104,7 +1115,7 @@ static void drm_test_fb_swab(struct kunit *test) const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? NULL : &result->dst_pitch; - drm_fb_swab(&dst, dst_pitch, &src, &fb, ¶ms->clip, false); + drm_fb_swab(&dst, dst_pitch, &src, &fb, ¶ms->clip, false, &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -1114,7 +1125,7 @@ static void drm_test_fb_swab(struct kunit *test) int blit_result; blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB8888 | DRM_FORMAT_BIG_ENDIAN, - &src, &fb, ¶ms->clip); + &src, &fb, ¶ms->clip, &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_FALSE(test, blit_result); @@ -1123,7 +1134,8 @@ static void drm_test_fb_swab(struct kunit *test) buf = dst.vaddr; memset(buf, 0, dst_size); - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_BGRX8888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_BGRX8888, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_FALSE(test, blit_result); @@ -1137,7 +1149,8 @@ static void drm_test_fb_swab(struct kunit *test) mock_format.format |= DRM_FORMAT_BIG_ENDIAN; fb.format = &mock_format; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB8888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XRGB8888, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_FALSE(test, blit_result); @@ -1175,7 +1188,8 @@ static void drm_test_fb_xrgb8888_to_abgr8888(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ABGR8888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_ABGR8888, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); @@ -1214,7 +1228,8 @@ static void drm_test_fb_xrgb8888_to_xbgr8888(struct kunit *test) int blit_result = 0; - blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XBGR8888, &src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(&dst, dst_pitch, DRM_FORMAT_XBGR8888, &src, &fb, ¶ms->clip, + &fmtcnv_state); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); @@ -1817,7 +1832,8 @@ static void drm_test_fb_memcpy(struct kunit *test) int blit_result; - blit_result = drm_fb_blit(dst, dst_pitches, params->format, src, &fb, ¶ms->clip); + blit_result = drm_fb_blit(dst, dst_pitches, params->format, src, &fb, ¶ms->clip, + &fmtcnv_state); KUNIT_EXPECT_FALSE(test, blit_result); for (size_t i = 0; i < fb.format->num_planes; i++) { diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c index 05d5e7af6d25..4e9247cf9977 100644 --- a/drivers/gpu/drm/tests/drm_mm_test.c +++ b/drivers/gpu/drm/tests/drm_mm_test.c @@ -17,10 +17,6 @@ #include "../lib/drm_random.h" -static unsigned int random_seed; -static unsigned int max_iterations = 8192; -static unsigned int max_prime = 128; - enum { BEST, BOTTOMUP, @@ -37,10 +33,6 @@ static const struct insert_mode { [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, {} -}, evict_modes[] = { - { "bottom-up", DRM_MM_INSERT_LOW }, - { "top-down", DRM_MM_INSERT_HIGH }, - {} }; static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm) @@ -97,57 +89,6 @@ static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 sta return ok; } -static bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size) -{ - struct drm_mm_node *node, *check, *found; - unsigned long n; - u64 addr; - - if (!assert_no_holes(test, mm)) - return false; - - n = 0; - addr = 0; - drm_mm_for_each_node(node, mm) { - if (node->start != addr) { - KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n", - n, addr, node->start); - return false; - } - - if (node->size != size) { - KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n", - n, size, node->size); - return false; - } - - if (drm_mm_hole_follows(node)) { - KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n); - return false; - } - - found = NULL; - drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { - if (node != check) { - KUNIT_FAIL(test, - "lookup return wrong node, expected start %llx, found %llx\n", - node->start, check->start); - return false; - } - found = check; - } - if (!found) { - KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size); - return false; - } - - addr += size; - n++; - } - - return true; -} - static u64 misalignment(struct drm_mm_node *node, u64 alignment) { u64 rem; @@ -270,215 +211,6 @@ static void drm_test_mm_debug(struct kunit *test) nodes[0].start, nodes[0].size); } -static struct drm_mm_node *set_node(struct drm_mm_node *node, - u64 start, u64 size) -{ - node->start = start; - node->size = size; - return node; -} - -static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node) -{ - int err; - - err = drm_mm_reserve_node(mm, node); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n", - node->start, node->size); - drm_mm_remove_node(node); - } else { - KUNIT_FAIL(test, - "impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", - err, -ENOSPC, node->start, node->size); - } - return false; -} - -static bool noinline_for_stack check_reserve_boundaries(struct kunit *test, struct drm_mm *mm, - unsigned int count, - u64 size) -{ - const struct boundary { - u64 start, size; - const char *name; - } boundaries[] = { -#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" } - B(0, 0), - B(-size, 0), - B(size, 0), - B(size * count, 0), - B(-size, size), - B(-size, -size), - B(-size, 2 * size), - B(0, -size), - B(size, -size), - B(count * size, size), - B(count * size, -size), - B(count * size, count * size), - B(count * size, -count * size), - B(count * size, -(count + 1) * size), - B((count + 1) * size, size), - B((count + 1) * size, -size), - B((count + 1) * size, -2 * size), -#undef B - }; - struct drm_mm_node tmp = {}; - int n; - - for (n = 0; n < ARRAY_SIZE(boundaries); n++) { - if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start, - boundaries[n].size))) { - KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n", - n, boundaries[n].name, count, size); - return false; - } - } - - return true; -} - -static int __drm_test_mm_reserve(struct kunit *test, unsigned int count, u64 size) -{ - DRM_RND_STATE(prng, random_seed); - struct drm_mm mm; - struct drm_mm_node tmp, *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret, err; - - /* For exercising drm_mm_reserve_node(), we want to check that - * reservations outside of the drm_mm range are rejected, and to - * overlapping and otherwise already occupied ranges. Afterwards, - * the tree and nodes should be intact. - */ - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - - ret = -ENOMEM; - order = drm_random_order(count, &prng); - if (!order) - goto err; - - nodes = vzalloc(array_size(count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - if (!check_reserve_boundaries(test, &mm, count, size)) - goto out; - - for (n = 0; n < count; n++) { - nodes[n].start = order[n] * size; - nodes[n].size = size; - - err = drm_mm_reserve_node(&mm, &nodes[n]); - if (err) { - KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n", - n, nodes[n].start); - ret = err; - goto out; - } - - if (!drm_mm_node_allocated(&nodes[n])) { - KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n", - n, nodes[n].start); - goto out; - } - - if (!expect_reserve_fail(test, &mm, &nodes[n])) - goto out; - } - - /* After random insertion the nodes should be in order */ - if (!assert_continuous(test, &mm, size)) - goto out; - - /* Repeated use should then fail */ - drm_random_reorder(order, count, &prng); - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1))) - goto out; - - /* Remove and reinsert should work */ - drm_mm_remove_node(&nodes[order[n]]); - err = drm_mm_reserve_node(&mm, &nodes[order[n]]); - if (err) { - KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n", - n, nodes[n].start); - ret = err; - goto out; - } - } - - if (!assert_continuous(test, &mm, size)) - goto out; - - /* Overlapping use should then fail */ - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count))) - goto out; - } - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n)))) - goto out; - } - - /* Remove several, reinsert, check full */ - for_each_prime_number(n, min(max_prime, count)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - } - - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - err = drm_mm_reserve_node(&mm, node); - if (err) { - KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n", - m, n, node->start); - ret = err; - goto out; - } - } - - o += n; - - if (!assert_continuous(test, &mm, size)) - goto out; - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); - kfree(order); -err: - return ret; -} - -static void drm_test_mm_reserve(struct kunit *test) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - int n; - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size - 1)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size + 1)); - - cond_resched(); - } -} - static bool expect_insert(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color, const struct insert_mode *mode) @@ -503,600 +235,6 @@ static bool expect_insert(struct kunit *test, struct drm_mm *mm, return true; } -static bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size) -{ - struct drm_mm_node tmp = {}; - int err; - - err = drm_mm_insert_node(mm, &tmp, size); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n", - tmp.start, tmp.size); - drm_mm_remove_node(&tmp); - } else { - KUNIT_FAIL(test, - "impossible insert failed with wrong error %d [expected %d], size %llu\n", - err, -ENOSPC, size); - } - return false; -} - -static int __drm_test_mm_insert(struct kunit *test, unsigned int count, u64 size, bool replace) -{ - DRM_RND_STATE(prng, random_seed); - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret; - - /* Fill a range with lots of nodes, check it doesn't fail too early */ - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - - ret = -ENOMEM; - nodes = vmalloc(array_size(count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - order = drm_random_order(count, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - for (mode = insert_modes; mode->name; mode++) { - for (n = 0; n < count; n++) { - struct drm_mm_node tmp; - - node = replace ? &tmp : &nodes[n]; - memset(node, 0, sizeof(*node)); - if (!expect_insert(test, &mm, node, size, 0, n, mode)) { - KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - - if (replace) { - drm_mm_replace_node(&tmp, &nodes[n]); - if (drm_mm_node_allocated(&tmp)) { - KUNIT_FAIL(test, - "replaced old-node still allocated! step %d\n", - n); - goto out; - } - - if (!assert_node(test, &nodes[n], &mm, size, 0, n)) { - KUNIT_FAIL(test, - "replaced node did not inherit parameters, size %llu step %d\n", - size, n); - goto out; - } - - if (tmp.start != nodes[n].start) { - KUNIT_FAIL(test, - "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", - tmp.start, size, nodes[n].start, nodes[n].size); - goto out; - } - } - } - - /* After random insertion the nodes should be in order */ - if (!assert_continuous(test, &mm, size)) - goto out; - - /* Repeated use should then fail */ - if (!expect_insert_fail(test, &mm, size)) - goto out; - - /* Remove one and reinsert, as the only hole it should refill itself */ - for (n = 0; n < count; n++) { - u64 addr = nodes[n].start; - - drm_mm_remove_node(&nodes[n]); - if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) { - KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - - if (nodes[n].start != addr) { - KUNIT_FAIL(test, - "%s reinsert node moved, step %d, expected %llx, found %llx\n", - mode->name, n, addr, nodes[n].start); - goto out; - } - - if (!assert_continuous(test, &mm, size)) - goto out; - } - - /* Remove several, reinsert, check full */ - for_each_prime_number(n, min(max_prime, count)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - } - - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(test, &mm, node, size, 0, n, mode)) { - KUNIT_FAIL(test, - "%s multiple reinsert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - } - - o += n; - - if (!assert_continuous(test, &mm, size)) - goto out; - - if (!expect_insert_fail(test, &mm, size)) - goto out; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); - return ret; -} - -static void drm_test_mm_insert(struct kunit *test) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - unsigned int n; - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, false)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, false)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, false)); - - cond_resched(); - } -} - -static void drm_test_mm_replace(struct kunit *test) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - unsigned int n; - - /* Reuse __drm_test_mm_insert to exercise replacement by inserting a dummy node, - * then replacing it with the intended node. We want to check that - * the tree is intact and all the information we need is carried - * across to the target node. - */ - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, true)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, true)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, true)); - - cond_resched(); - } -} - -static bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node, - u64 size, u64 alignment, unsigned long color, - u64 range_start, u64 range_end, const struct insert_mode *mode) -{ - int err; - - err = drm_mm_insert_node_in_range(mm, node, - size, alignment, color, - range_start, range_end, - mode->mode); - if (err) { - KUNIT_FAIL(test, - "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", - size, alignment, color, mode->name, - range_start, range_end, err); - return false; - } - - if (!assert_node(test, node, mm, size, alignment, color)) { - drm_mm_remove_node(node); - return false; - } - - return true; -} - -static bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm, - u64 size, u64 range_start, u64 range_end) -{ - struct drm_mm_node tmp = {}; - int err; - - err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end, - 0); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - KUNIT_FAIL(test, - "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n", - tmp.start, tmp.size, range_start, range_end); - drm_mm_remove_node(&tmp); - } else { - KUNIT_FAIL(test, - "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n", - err, -ENOSPC, size, range_start, range_end); - } - - return false; -} - -static bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm, - u64 size, u64 start, u64 end) -{ - struct drm_mm_node *node; - unsigned int n; - - if (!expect_insert_in_range_fail(test, mm, size, start, end)) - return false; - - n = div64_u64(start + size - 1, size); - drm_mm_for_each_node(node, mm) { - if (node->start < start || node->start + node->size > end) { - KUNIT_FAIL(test, - "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n", - n, node->start, node->start + node->size, start, end); - return false; - } - - if (node->start != n * size) { - KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n", - n, n * size, node->start); - return false; - } - - if (node->size != size) { - KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n", - n, size, node->size); - return false; - } - - if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) { - KUNIT_FAIL(test, "node %d is followed by a hole!\n", n); - return false; - } - - n++; - } - - if (start > 0) { - node = __drm_mm_interval_first(mm, 0, start - 1); - if (drm_mm_node_allocated(node)) { - KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n", - node->start, node->size, start); - return false; - } - } - - if (end < U64_MAX) { - node = __drm_mm_interval_first(mm, end, U64_MAX); - if (drm_mm_node_allocated(node)) { - KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n", - node->start, node->size, end); - return false; - } - } - - return true; -} - -static int __drm_test_mm_insert_range(struct kunit *test, unsigned int count, u64 size, - u64 start, u64 end) -{ - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int n, start_n, end_n; - int ret; - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - DRM_MM_BUG_ON(end <= start); - - /* Very similar to __drm_test_mm_insert(), but now instead of populating the - * full range of the drm_mm, we try to fill a small portion of it. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - start_n = div64_u64(start + size - 1, size); - end_n = div64_u64(end - size, size); - - for (mode = insert_modes; mode->name; mode++) { - for (n = start_n; n <= end_n; n++) { - if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n, - start, end, mode)) { - KUNIT_FAIL(test, - "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n", - mode->name, size, n, start_n, end_n, start, end); - goto out; - } - } - - if (!assert_contiguous_in_range(test, &mm, size, start, end)) { - KUNIT_FAIL(test, - "%s: range [%llx, %llx] not full after initialisation, size=%llu\n", - mode->name, start, end, size); - goto out; - } - - /* Remove one and reinsert, it should refill itself */ - for (n = start_n; n <= end_n; n++) { - u64 addr = nodes[n].start; - - drm_mm_remove_node(&nodes[n]); - if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n, - start, end, mode)) { - KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n); - goto out; - } - - if (nodes[n].start != addr) { - KUNIT_FAIL(test, - "%s reinsert node moved, step %d, expected %llx, found %llx\n", - mode->name, n, addr, nodes[n].start); - goto out; - } - } - - if (!assert_contiguous_in_range(test, &mm, size, start, end)) { - KUNIT_FAIL(test, - "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n", - mode->name, start, end, size); - goto out; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); - return ret; -} - -static int insert_outside_range(struct kunit *test) -{ - struct drm_mm mm; - const unsigned int start = 1024; - const unsigned int end = 2048; - const unsigned int size = end - start; - - drm_mm_init(&mm, start, size); - - if (!expect_insert_in_range_fail(test, &mm, 1, 0, start)) - return -EINVAL; - - if (!expect_insert_in_range_fail(test, &mm, size, - start - size / 2, start + (size + 1) / 2)) - return -EINVAL; - - if (!expect_insert_in_range_fail(test, &mm, size, - end - (size + 1) / 2, end + size / 2)) - return -EINVAL; - - if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size)) - return -EINVAL; - - drm_mm_takedown(&mm); - return 0; -} - -static void drm_test_mm_insert_range(struct kunit *test) -{ - const unsigned int count = min_t(unsigned int, BIT(13), max_iterations); - unsigned int n; - - /* Check that requests outside the bounds of drm_mm are rejected. */ - KUNIT_ASSERT_FALSE(test, insert_outside_range(test)); - - for_each_prime_number_from(n, 1, 50) { - const u64 size = BIT_ULL(n); - const u64 max = count * size; - - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 1, max)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max - 1)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max / 2)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, - max / 2, max)); - KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, - max / 4 + 1, 3 * max / 4 - 1)); - - cond_resched(); - } -} - -static int prepare_frag(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *nodes, - unsigned int num_insert, const struct insert_mode *mode) -{ - unsigned int size = 4096; - unsigned int i; - - for (i = 0; i < num_insert; i++) { - if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) { - KUNIT_FAIL(test, "%s insert failed\n", mode->name); - return -EINVAL; - } - } - - /* introduce fragmentation by freeing every other node */ - for (i = 0; i < num_insert; i++) { - if (i % 2 == 0) - drm_mm_remove_node(&nodes[i]); - } - - return 0; -} - -static u64 get_insert_time(struct kunit *test, struct drm_mm *mm, - unsigned int num_insert, struct drm_mm_node *nodes, - const struct insert_mode *mode) -{ - unsigned int size = 8192; - ktime_t start; - unsigned int i; - - start = ktime_get(); - for (i = 0; i < num_insert; i++) { - if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) { - KUNIT_FAIL(test, "%s insert failed\n", mode->name); - return 0; - } - } - - return ktime_to_ns(ktime_sub(ktime_get(), start)); -} - -static void drm_test_mm_frag(struct kunit *test) -{ - struct drm_mm mm; - const struct insert_mode *mode; - struct drm_mm_node *nodes, *node, *next; - unsigned int insert_size = 10000; - unsigned int scale_factor = 4; - - /* We need 4 * insert_size nodes to hold intermediate allocated - * drm_mm nodes. - * 1 times for prepare_frag() - * 1 times for get_insert_time() - * 2 times for get_insert_time() - */ - nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - /* For BOTTOMUP and TOPDOWN, we first fragment the - * address space using prepare_frag() and then try to verify - * that insertions scale quadratically from 10k to 20k insertions - */ - drm_mm_init(&mm, 1, U64_MAX - 2); - for (mode = insert_modes; mode->name; mode++) { - u64 insert_time1, insert_time2; - - if (mode->mode != DRM_MM_INSERT_LOW && - mode->mode != DRM_MM_INSERT_HIGH) - continue; - - if (prepare_frag(test, &mm, nodes, insert_size, mode)) - goto err; - - insert_time1 = get_insert_time(test, &mm, insert_size, - nodes + insert_size, mode); - if (insert_time1 == 0) - goto err; - - insert_time2 = get_insert_time(test, &mm, (insert_size * 2), - nodes + insert_size * 2, mode); - if (insert_time2 == 0) - goto err; - - kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n", - mode->name, insert_size, insert_size * 2, insert_time1, insert_time2); - - if (insert_time2 > (scale_factor * insert_time1)) { - KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n", - mode->name, insert_time2 - (scale_factor * insert_time1)); - goto err; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - } - -err: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); -} - -static void drm_test_mm_align(struct kunit *test) -{ - const struct insert_mode *mode; - const unsigned int max_count = min(8192u, max_prime); - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int prime; - - /* For each of the possible insertion modes, we pick a few - * arbitrary alignments and check that the inserted node - * meets our requirements. - */ - - nodes = vzalloc(array_size(max_count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - drm_mm_init(&mm, 1, U64_MAX - 2); - - for (mode = insert_modes; mode->name; mode++) { - unsigned int i = 0; - - for_each_prime_number_from(prime, 1, max_count) { - u64 size = next_prime_number(prime); - - if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) { - KUNIT_FAIL(test, "%s insert failed with alignment=%d", - mode->name, prime); - goto out; - } - - i++; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); -} - static void drm_test_mm_align_pot(struct kunit *test, int max) { struct drm_mm mm; @@ -1144,626 +282,6 @@ static void drm_test_mm_align64(struct kunit *test) drm_test_mm_align_pot(test, 64); } -static void show_scan(struct kunit *test, const struct drm_mm_scan *scan) -{ - kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", - scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color); -} - -static void show_holes(struct kunit *test, const struct drm_mm *mm, int count) -{ - u64 hole_start, hole_end; - struct drm_mm_node *hole; - - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - struct drm_mm_node *next = list_next_entry(hole, node_list); - const char *node1 = NULL, *node2 = NULL; - - if (drm_mm_node_allocated(hole)) - node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ", - hole->start, hole->size, hole->color); - - if (drm_mm_node_allocated(next)) - node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]", - next->start, next->size, next->color); - - kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1, - hole_start, hole_end, hole_end - hole_start, node2); - - kfree(node2); - kfree(node1); - - if (!--count) - break; - } -} - -struct evict_node { - struct drm_mm_node node; - struct list_head link; -}; - -static bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan, - struct evict_node *nodes, unsigned int *order, unsigned int count, - bool use_color, struct list_head *evict_list) -{ - struct evict_node *e, *en; - unsigned int i; - - for (i = 0; i < count; i++) { - e = &nodes[order ? order[i] : i]; - list_add(&e->link, evict_list); - if (drm_mm_scan_add_block(scan, &e->node)) - break; - } - list_for_each_entry_safe(e, en, evict_list, link) { - if (!drm_mm_scan_remove_block(scan, &e->node)) - list_del(&e->link); - } - if (list_empty(evict_list)) { - KUNIT_FAIL(test, - "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", - scan->size, count, scan->alignment, scan->color); - return false; - } - - list_for_each_entry(e, evict_list, link) - drm_mm_remove_node(&e->node); - - if (use_color) { - struct drm_mm_node *node; - - while ((node = drm_mm_scan_color_evict(scan))) { - e = container_of(node, typeof(*e), node); - drm_mm_remove_node(&e->node); - list_add(&e->link, evict_list); - } - } else { - if (drm_mm_scan_color_evict(scan)) { - KUNIT_FAIL(test, - "drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); - return false; - } - } - - return true; -} - -static bool evict_nothing(struct kunit *test, struct drm_mm *mm, - unsigned int total_size, struct evict_node *nodes) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node *node; - unsigned int n; - - drm_mm_scan_init(&scan, mm, 1, 0, 0, 0); - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - list_add(&e->link, &evict_list); - drm_mm_scan_add_block(&scan, &e->node); - } - list_for_each_entry(e, &evict_list, link) - drm_mm_scan_remove_block(&scan, &e->node); - - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - - if (!drm_mm_node_allocated(&e->node)) { - KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n); - return false; - } - - e->link.next = NULL; - } - - drm_mm_for_each_node(node, mm) { - e = container_of(node, typeof(*e), node); - e->link.next = &e->link; - } - - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - - if (!e->link.next) { - KUNIT_FAIL(test, "node[%d] no longer connected!\n", n); - return false; - } - } - - return assert_continuous(test, mm, nodes[0].node.size); -} - -static bool evict_everything(struct kunit *test, struct drm_mm *mm, - unsigned int total_size, struct evict_node *nodes) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - unsigned int n; - int err; - - drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0); - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - list_add(&e->link, &evict_list); - if (drm_mm_scan_add_block(&scan, &e->node)) - break; - } - - err = 0; - list_for_each_entry(e, &evict_list, link) { - if (!drm_mm_scan_remove_block(&scan, &e->node)) { - if (!err) { - KUNIT_FAIL(test, "Node %lld not marked for eviction!\n", - e->node.start); - err = -EINVAL; - } - } - } - if (err) - return false; - - list_for_each_entry(e, &evict_list, link) - drm_mm_remove_node(&e->node); - - if (!assert_one_hole(test, mm, 0, total_size)) - return false; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return false; - } - } - - return assert_continuous(test, mm, nodes[0].node.size); -} - -static int evict_something(struct kunit *test, struct drm_mm *mm, - u64 range_start, u64 range_end, struct evict_node *nodes, - unsigned int *order, unsigned int count, unsigned int size, - unsigned int alignment, const struct insert_mode *mode) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node tmp; - int err; - - drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start, - range_end, mode->mode); - if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list)) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, - DRM_MM_INSERT_EVICT); - if (err) { - KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n", - size, alignment); - show_scan(test, &scan); - show_holes(test, mm, 3); - return err; - } - - if (tmp.start < range_start || tmp.start + tmp.size > range_end) { - KUNIT_FAIL(test, - "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", - tmp.start, tmp.size, range_start, range_end); - err = -EINVAL; - } - - if (!assert_node(test, &tmp, mm, size, alignment, 0) || - drm_mm_hole_follows(&tmp)) { - KUNIT_FAIL(test, - "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", - tmp.size, size, alignment, misalignment(&tmp, alignment), - tmp.start, drm_mm_hole_follows(&tmp)); - err = -EINVAL; - } - - drm_mm_remove_node(&tmp); - if (err) - return err; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return err; - } - } - - if (!assert_continuous(test, mm, nodes[0].node.size)) { - KUNIT_FAIL(test, "range is no longer continuous\n"); - return -EINVAL; - } - - return 0; -} - -static void drm_test_mm_evict(struct kunit *test) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int size = 8192; - const struct insert_mode *mode; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - - /* Here we populate a full drm_mm and then try and insert a new node - * by evicting other nodes in a random order. The drm_mm_scan should - * pick the first matching hole it finds from the random list. We - * repeat that for different allocation strategies, alignments and - * sizes to try and stress the hole finder. - */ - - nodes = vzalloc(array_size(size, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - order = drm_random_order(size, &prng); - if (!order) - goto err_nodes; - - drm_mm_init(&mm, 0, size); - for (n = 0; n < size; n++) { - if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) { - KUNIT_FAIL(test, "insert failed, step %d\n", n); - goto out; - } - } - - /* First check that using the scanner doesn't break the mm */ - if (!evict_nothing(test, &mm, size, nodes)) { - KUNIT_FAIL(test, "evict_nothing() failed\n"); - goto out; - } - if (!evict_everything(test, &mm, size, nodes)) { - KUNIT_FAIL(test, "evict_everything() failed\n"); - goto out; - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= size; n <<= 1) { - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1, - mode)) { - KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n", - mode->name, n); - goto out; - } - } - - for (n = 1; n < size; n <<= 1) { - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, - size / 2, n, mode)) { - KUNIT_FAIL(test, - "%s evict_something(size=%u, alignment=%u) failed\n", - mode->name, size / 2, n); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(size, max_prime)) { - unsigned int nsize = (size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, - nsize, n, mode)) { - KUNIT_FAIL(test, - "%s evict_something(size=%u, alignment=%u) failed\n", - mode->name, nsize, n); - goto out; - } - } - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -} - -static void drm_test_mm_evict_range(struct kunit *test) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int size = 8192; - const unsigned int range_size = size / 2; - const unsigned int range_start = size / 4; - const unsigned int range_end = range_start + range_size; - const struct insert_mode *mode; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - - /* Like drm_test_mm_evict() but now we are limiting the search to a - * small portion of the full drm_mm. - */ - - nodes = vzalloc(array_size(size, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - order = drm_random_order(size, &prng); - if (!order) - goto err_nodes; - - drm_mm_init(&mm, 0, size); - for (n = 0; n < size; n++) { - if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) { - KUNIT_FAIL(test, "insert failed, step %d\n", n); - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, range_start, range_end, nodes, - order, size, n, 1, mode)) { - KUNIT_FAIL(test, - "%s evict_something(size=%u) failed with range [%u, %u]\n", - mode->name, n, range_start, range_end); - goto out; - } - } - - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, range_start, range_end, nodes, - order, size, range_size / 2, n, mode)) { - KUNIT_FAIL(test, - "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", - mode->name, range_size / 2, n, range_start, range_end); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(range_size, max_prime)) { - unsigned int nsize = (range_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, size, &prng); - if (evict_something(test, &mm, range_start, range_end, nodes, - order, size, nsize, n, mode)) { - KUNIT_FAIL(test, - "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", - mode->name, nsize, n, range_start, range_end); - goto out; - } - } - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -} - -static unsigned int node_index(const struct drm_mm_node *node) -{ - return div64_u64(node->start, node->size); -} - -static void drm_test_mm_topdown(struct kunit *test) -{ - const struct insert_mode *topdown = &insert_modes[TOPDOWN]; - - DRM_RND_STATE(prng, random_seed); - const unsigned int count = 8192; - unsigned int size; - unsigned long *bitmap; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - - /* When allocating top-down, we expect to be returned a node - * from a suitable hole at the top of the drm_mm. We check that - * the returned node does match the highest available slot. - */ - - nodes = vzalloc(array_size(count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - bitmap = bitmap_zalloc(count, GFP_KERNEL); - if (!bitmap) - goto err_nodes; - - order = drm_random_order(count, &prng); - if (!order) - goto err_bitmap; - - for (size = 1; size <= 64; size <<= 1) { - drm_mm_init(&mm, 0, size * count); - for (n = 0; n < count; n++) { - if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) { - KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n); - goto out; - } - - if (drm_mm_hole_follows(&nodes[n])) { - KUNIT_FAIL(test, - "hole after topdown insert %d, start=%llx\n, size=%u", - n, nodes[n].start, size); - goto out; - } - - if (!assert_one_hole(test, &mm, 0, size * (count - n - 1))) - goto out; - } - - if (!assert_continuous(test, &mm, size)) - goto out; - - drm_random_reorder(order, count, &prng); - for_each_prime_number_from(n, 1, min(count, max_prime)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - __set_bit(node_index(node), bitmap); - } - - for (m = 0; m < n; m++) { - unsigned int last; - - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) { - KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n); - goto out; - } - - if (drm_mm_hole_follows(node)) { - KUNIT_FAIL(test, - "hole after topdown insert %d/%d, start=%llx\n", - m, n, node->start); - goto out; - } - - last = find_last_bit(bitmap, count); - if (node_index(node) != last) { - KUNIT_FAIL(test, - "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", - m, n, size, last, node_index(node)); - goto out; - } - - __clear_bit(last, bitmap); - } - - DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); - - o += n; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_bitmap: - bitmap_free(bitmap); -err_nodes: - vfree(nodes); -} - -static void drm_test_mm_bottomup(struct kunit *test) -{ - const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; - - DRM_RND_STATE(prng, random_seed); - const unsigned int count = 8192; - unsigned int size; - unsigned long *bitmap; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - - /* Like drm_test_mm_topdown, but instead of searching for the last hole, - * we search for the first. - */ - - nodes = vzalloc(array_size(count, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - bitmap = bitmap_zalloc(count, GFP_KERNEL); - if (!bitmap) - goto err_nodes; - - order = drm_random_order(count, &prng); - if (!order) - goto err_bitmap; - - for (size = 1; size <= 64; size <<= 1) { - drm_mm_init(&mm, 0, size * count); - for (n = 0; n < count; n++) { - if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) { - KUNIT_FAIL(test, - "bottomup insert failed, size %u step %d\n", size, n); - goto out; - } - - if (!assert_one_hole(test, &mm, size * (n + 1), size * count)) - goto out; - } - - if (!assert_continuous(test, &mm, size)) - goto out; - - drm_random_reorder(order, count, &prng); - for_each_prime_number_from(n, 1, min(count, max_prime)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - __set_bit(node_index(node), bitmap); - } - - for (m = 0; m < n; m++) { - unsigned int first; - - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) { - KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n); - goto out; - } - - first = find_first_bit(bitmap, count); - if (node_index(node) != first) { - KUNIT_FAIL(test, - "node %d/%d not inserted into bottom hole, expected %d, found %d\n", - m, n, first, node_index(node)); - goto out; - } - __clear_bit(first, bitmap); - } - - DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); - - o += n; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_bitmap: - bitmap_free(bitmap); -err_nodes: - vfree(nodes); -} - static void drm_test_mm_once(struct kunit *test, unsigned int mode) { struct drm_mm mm; @@ -1817,440 +335,18 @@ static void drm_test_mm_highest(struct kunit *test) drm_test_mm_once(test, DRM_MM_INSERT_HIGH); } -static void separate_adjacent_colors(const struct drm_mm_node *node, - unsigned long color, u64 *start, u64 *end) -{ - if (drm_mm_node_allocated(node) && node->color != color) - ++*start; - - node = list_next_entry(node, node_list); - if (drm_mm_node_allocated(node) && node->color != color) - --*end; -} - -static bool colors_abutt(struct kunit *test, const struct drm_mm_node *node) -{ - if (!drm_mm_hole_follows(node) && - drm_mm_node_allocated(list_next_entry(node, node_list))) { - KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", - node->color, node->start, node->size, - list_next_entry(node, node_list)->color, - list_next_entry(node, node_list)->start, - list_next_entry(node, node_list)->size); - return true; - } - - return false; -} - -static void drm_test_mm_color(struct kunit *test) -{ - const unsigned int count = min(4096u, max_iterations); - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *node, *nn; - unsigned int n; - - /* Color adjustment complicates everything. First we just check - * that when we insert a node we apply any color_adjustment callback. - * The callback we use should ensure that there is a gap between - * any two nodes, and so after each insertion we check that those - * holes are inserted and that they are preserved. - */ - - drm_mm_init(&mm, 0, U64_MAX); - - for (n = 1; n <= count; n++) { - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - goto out; - - if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) { - KUNIT_FAIL(test, "insert failed, step %d\n", n); - kfree(node); - goto out; - } - } - - drm_mm_for_each_node_safe(node, nn, &mm) { - if (node->color != node->size) { - KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n", - node->size, node->color); - - goto out; - } - - drm_mm_remove_node(node); - kfree(node); - } - - /* Now, let's start experimenting with applying a color callback */ - mm.color_adjust = separate_adjacent_colors; - for (mode = insert_modes; mode->name; mode++) { - u64 last; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - goto out; - - node->size = 1 + 2 * count; - node->color = node->size; - - if (drm_mm_reserve_node(&mm, node)) { - KUNIT_FAIL(test, "initial reserve failed!\n"); - goto out; - } - - last = node->start + node->size; - - for (n = 1; n <= count; n++) { - int rem; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - goto out; - - node->start = last; - node->size = n + count; - node->color = node->size; - - if (drm_mm_reserve_node(&mm, node) != -ENOSPC) { - KUNIT_FAIL(test, "reserve %d did not report color overlap!", n); - goto out; - } - - node->start += n + 1; - rem = misalignment(node, n + count); - node->start += n + count - rem; - - if (drm_mm_reserve_node(&mm, node)) { - KUNIT_FAIL(test, "reserve %d failed", n); - goto out; - } - - last = node->start + node->size; - } - - for (n = 1; n <= count; n++) { - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - goto out; - - if (!expect_insert(test, &mm, node, n, n, n, mode)) { - KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n); - kfree(node); - goto out; - } - } - - drm_mm_for_each_node_safe(node, nn, &mm) { - u64 rem; - - if (node->color != node->size) { - KUNIT_FAIL(test, - "%s invalid color stored: expected %lld, found %ld\n", - mode->name, node->size, node->color); - - goto out; - } - - if (colors_abutt(test, node)) - goto out; - - div64_u64_rem(node->start, node->size, &rem); - if (rem) { - KUNIT_FAIL(test, - "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", - mode->name, node->start, node->size, rem); - goto out; - } - - drm_mm_remove_node(node); - kfree(node); - } - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, nn, &mm) { - drm_mm_remove_node(node); - kfree(node); - } - drm_mm_takedown(&mm); -} - -static int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start, - u64 range_end, struct evict_node *nodes, unsigned int *order, - unsigned int count, unsigned int size, unsigned int alignment, - unsigned long color, const struct insert_mode *mode) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node tmp; - int err; - - drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start, - range_end, mode->mode); - if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list)) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, - DRM_MM_INSERT_EVICT); - if (err) { - KUNIT_FAIL(test, - "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", - size, alignment, color, err); - show_scan(test, &scan); - show_holes(test, mm, 3); - return err; - } - - if (tmp.start < range_start || tmp.start + tmp.size > range_end) { - KUNIT_FAIL(test, - "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", - tmp.start, tmp.size, range_start, range_end); - err = -EINVAL; - } - - if (colors_abutt(test, &tmp)) - err = -EINVAL; - - if (!assert_node(test, &tmp, mm, size, alignment, color)) { - KUNIT_FAIL(test, - "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", - tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start); - err = -EINVAL; - } - - drm_mm_remove_node(&tmp); - if (err) - return err; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return err; - } - } - - cond_resched(); - return 0; -} - -static void drm_test_mm_color_evict(struct kunit *test) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int total_size = min(8192u, max_iterations); - const struct insert_mode *mode; - unsigned long color = 0; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - - /* Check that the drm_mm_scan also honours color adjustment when - * choosing its victims to create a hole. Our color_adjust does not - * allow two nodes to be placed together without an intervening hole - * enlarging the set of victims that must be evicted. - */ - - nodes = vzalloc(array_size(total_size, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - order = drm_random_order(total_size, &prng); - if (!order) - goto err_nodes; - - drm_mm_init(&mm, 0, 2 * total_size - 1); - mm.color_adjust = separate_adjacent_colors; - for (n = 0; n < total_size; n++) { - if (!expect_insert(test, &mm, &nodes[n].node, - 1, 0, color++, - &insert_modes[0])) { - KUNIT_FAIL(test, "insert failed, step %d\n", n); - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= total_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, - n, 1, color++, mode)) { - KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n); - goto out; - } - } - - for (n = 1; n < total_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, - total_size / 2, n, color++, mode)) { - KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n", - mode->name, total_size / 2, n); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(total_size, max_prime)) { - unsigned int nsize = (total_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, total_size, &prng); - if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, - nsize, n, color++, mode)) { - KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n", - mode->name, nsize, n); - goto out; - } - } - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -} - -static void drm_test_mm_color_evict_range(struct kunit *test) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int total_size = 8192; - const unsigned int range_size = total_size / 2; - const unsigned int range_start = total_size / 4; - const unsigned int range_end = range_start + range_size; - const struct insert_mode *mode; - unsigned long color = 0; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - - /* Like drm_test_mm_color_evict(), but limited to small portion of the full - * drm_mm range. - */ - - nodes = vzalloc(array_size(total_size, sizeof(*nodes))); - KUNIT_ASSERT_TRUE(test, nodes); - - order = drm_random_order(total_size, &prng); - if (!order) - goto err_nodes; - - drm_mm_init(&mm, 0, 2 * total_size - 1); - mm.color_adjust = separate_adjacent_colors; - for (n = 0; n < total_size; n++) { - if (!expect_insert(test, &mm, &nodes[n].node, - 1, 0, color++, - &insert_modes[0])) { - KUNIT_FAIL(test, "insert failed, step %d\n", n); - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, range_size, &prng); - if (evict_color(test, &mm, range_start, range_end, nodes, order, - total_size, n, 1, color++, mode)) { - KUNIT_FAIL(test, - "%s evict_color(size=%u) failed for range [%x, %x]\n", - mode->name, n, range_start, range_end); - goto out; - } - } - - for (n = 1; n < range_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - if (evict_color(test, &mm, range_start, range_end, nodes, order, - total_size, range_size / 2, n, color++, mode)) { - KUNIT_FAIL(test, - "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", - mode->name, total_size / 2, n, range_start, range_end); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(range_size, max_prime)) { - unsigned int nsize = (range_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, total_size, &prng); - if (evict_color(test, &mm, range_start, range_end, nodes, order, - total_size, nsize, n, color++, mode)) { - KUNIT_FAIL(test, - "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", - mode->name, nsize, n, range_start, range_end); - goto out; - } - } - - cond_resched(); - } - -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -} - -static int drm_mm_suite_init(struct kunit_suite *suite) -{ - while (!random_seed) - random_seed = get_random_u32(); - - kunit_info(suite, - "Testing DRM range manager, with random_seed=0x%x max_iterations=%u max_prime=%u\n", - random_seed, max_iterations, max_prime); - - return 0; -} - -module_param(random_seed, uint, 0400); -module_param(max_iterations, uint, 0400); -module_param(max_prime, uint, 0400); - static struct kunit_case drm_mm_tests[] = { KUNIT_CASE(drm_test_mm_init), KUNIT_CASE(drm_test_mm_debug), - KUNIT_CASE(drm_test_mm_reserve), - KUNIT_CASE(drm_test_mm_insert), - KUNIT_CASE(drm_test_mm_replace), - KUNIT_CASE(drm_test_mm_insert_range), - KUNIT_CASE(drm_test_mm_frag), - KUNIT_CASE(drm_test_mm_align), KUNIT_CASE(drm_test_mm_align32), KUNIT_CASE(drm_test_mm_align64), - KUNIT_CASE(drm_test_mm_evict), - KUNIT_CASE(drm_test_mm_evict_range), - KUNIT_CASE(drm_test_mm_topdown), - KUNIT_CASE(drm_test_mm_bottomup), KUNIT_CASE(drm_test_mm_lowest), KUNIT_CASE(drm_test_mm_highest), - KUNIT_CASE(drm_test_mm_color), - KUNIT_CASE(drm_test_mm_color_evict), - KUNIT_CASE(drm_test_mm_color_evict_range), {} }; static struct kunit_suite drm_mm_test_suite = { .name = "drm_mm", - .suite_init = drm_mm_suite_init, .test_cases = drm_mm_tests, }; diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index c979ad1af236..d096d8d2bc8f 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -4,8 +4,6 @@ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> */ -#include <linux/dma-fence.h> - #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> @@ -25,7 +23,6 @@ static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state) { struct drm_device *ddev = old_state->dev; struct tidss_device *tidss = to_tidss(ddev); - bool fence_cookie = dma_fence_begin_signalling(); dev_dbg(ddev->dev, "%s\n", __func__); @@ -36,7 +33,6 @@ static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_commit_modeset_enables(ddev, old_state); drm_atomic_helper_commit_hw_done(old_state); - dma_fence_end_signalling(fence_cookie); drm_atomic_helper_wait_for_flip_done(ddev, old_state); drm_atomic_helper_cleanup_planes(ddev, old_state); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 8ebd7134ee21..2f6eaac7f659 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -138,7 +138,7 @@ static int tilcdc_irq_install(struct drm_device *dev, unsigned int irq) if (ret) return ret; - priv->irq_enabled = false; + priv->irq_enabled = true; return 0; } diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c index e5b10e41554a..4f8f3172379e 100644 --- a/drivers/gpu/drm/tiny/arcpgu.c +++ b/drivers/gpu/drm/tiny/arcpgu.c @@ -404,14 +404,12 @@ err_unload: return ret; } -static int arcpgu_remove(struct platform_device *pdev) +static void arcpgu_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); drm_dev_unregister(drm); arcpgu_unload(drm); - - return 0; } static const struct of_device_id arcpgu_of_table[] = { @@ -423,7 +421,7 @@ MODULE_DEVICE_TABLE(of, arcpgu_of_table); static struct platform_driver arcpgu_platform_driver = { .probe = arcpgu_probe, - .remove = arcpgu_remove, + .remove_new = arcpgu_remove, .driver = { .name = "arcpgu", .of_match_table = arcpgu_of_table, diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index c5c34cd2edc1..4e3a152f897a 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -411,7 +411,8 @@ static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane, unsigned int offset = drm_fb_clip_offset(pitch, format, &damage); struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset); - drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, &damage); + drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, + &damage, &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index 4ceb68ffac4b..dd8b0a181be9 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -78,7 +78,7 @@ static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) } static void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *rect) + struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); unsigned int height = rect->y2 - rect->y1; @@ -98,7 +98,7 @@ static void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, if (!dbi->dc || !full || swap || fb->format->format == DRM_FORMAT_XRGB8888) { tr = dbidev->tx_buf; - ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap); + ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap, fmtcnv_state); if (ret) goto err_msg; } else { @@ -171,7 +171,8 @@ static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, return; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); drm_dev_exit(idx); } @@ -281,7 +282,8 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); - ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); out_exit: drm_dev_exit(idx); diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index 2d999a0facde..05a72473cfc6 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -758,7 +758,11 @@ static const uint64_t ofdrm_primary_plane_format_modifiers[] = { static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *new_state) { + struct drm_device *dev = plane->dev; + struct ofdrm_device *odev = ofdrm_device_of_dev(dev); struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane); + struct drm_shadow_plane_state *new_shadow_plane_state = + to_drm_shadow_plane_state(new_plane_state); struct drm_framebuffer *new_fb = new_plane_state->fb; struct drm_crtc *new_crtc = new_plane_state->crtc; struct drm_crtc_state *new_crtc_state = NULL; @@ -777,6 +781,16 @@ static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, else if (!new_plane_state->visible) return 0; + if (new_fb->format != odev->format) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, + odev->pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc); new_ofdrm_crtc_state = to_ofdrm_crtc_state(new_crtc_state); @@ -817,7 +831,7 @@ static void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, - &damage); + &damage, &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 73dd4f4289c2..8fd6758f5725 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -509,7 +509,8 @@ static void repaper_get_temperature(struct repaper_epd *epd) epd->factored_stage_time = epd->stage_time * factor10x / 10; } -static int repaper_fb_dirty(struct drm_framebuffer *fb) +static int repaper_fb_dirty(struct drm_framebuffer *fb, + struct drm_format_conv_state *fmtcnv_state) { struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0); struct repaper_epd *epd = drm_to_epd(fb->dev); @@ -545,7 +546,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb) iosys_map_set_vaddr(&dst, buf); iosys_map_set_vaddr(&vmap, dma_obj->vaddr); - drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, &vmap, fb, &clip); + drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, &vmap, fb, &clip, fmtcnv_state); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); @@ -830,13 +831,16 @@ static void repaper_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; + struct drm_format_conv_state fmtcnv_state = DRM_FORMAT_CONV_STATE_INIT; struct drm_rect rect; if (!pipe->crtc.state->active) return; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - repaper_fb_dirty(state->fb); + repaper_fb_dirty(state->fb, &fmtcnv_state); + + drm_format_conv_state_release(&fmtcnv_state); } static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = { diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 5fefc895bca2..34bbbd7b53dd 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -19,6 +19,7 @@ #include <drm/drm_drv.h> #include <drm/drm_fbdev_generic.h> #include <drm/drm_format_helper.h> +#include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_shmem_helper.h> @@ -579,6 +580,44 @@ static const uint64_t simpledrm_primary_plane_format_modifiers[] = { DRM_FORMAT_MOD_INVALID }; +static int simpledrm_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *new_shadow_plane_state = + to_drm_shadow_plane_state(new_plane_state); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + struct drm_device *dev = plane->dev; + struct simpledrm_device *sdev = simpledrm_device_of_dev(dev); + int ret; + + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + + if (new_fb->format != sdev->format) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state, + sdev->pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + return 0; +} + static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -609,7 +648,7 @@ static void simpledrm_primary_plane_helper_atomic_update(struct drm_plane *plane iosys_map_incr(&dst, drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_clip)); drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state->data, - fb, &damage); + fb, &damage, &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); @@ -635,7 +674,7 @@ static void simpledrm_primary_plane_helper_atomic_disable(struct drm_plane *plan static const struct drm_plane_helper_funcs simpledrm_primary_plane_helper_funcs = { DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, - .atomic_check = drm_plane_helper_atomic_check, + .atomic_check = simpledrm_primary_plane_helper_atomic_check, .atomic_update = simpledrm_primary_plane_helper_atomic_update, .atomic_disable = simpledrm_primary_plane_helper_atomic_disable, }; diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index 3cf4eec16a81..7336fa1ddaed 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -64,7 +64,8 @@ static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 }; static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, struct drm_framebuffer *fb, - struct drm_rect *clip) + struct drm_rect *clip, + struct drm_format_conv_state *fmtcnv_state) { size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1); unsigned int x, y; @@ -77,7 +78,7 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, iosys_map_set_vaddr(&dst_map, buf); iosys_map_set_vaddr(&vmap, vaddr); - drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, clip); + drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, clip, fmtcnv_state); src = buf; for (y = clip->y1; y < clip->y2; y++) { @@ -93,7 +94,7 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, } static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *clip) + struct drm_rect *clip, struct drm_format_conv_state *fmtcnv_state) { int ret; @@ -101,7 +102,7 @@ static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuf if (ret) return ret; - st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip); + st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip, fmtcnv_state); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); @@ -109,7 +110,7 @@ static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuf } static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *rect) + struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); struct mipi_dbi *dbi = &dbidev->dbi; @@ -121,7 +122,7 @@ static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); - ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect); + ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect, fmtcnv_state); if (ret) goto err_msg; @@ -160,7 +161,8 @@ static void st7586_pipe_update(struct drm_simple_display_pipe *pipe, return; if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); drm_dev_exit(idx); } @@ -238,7 +240,8 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, msleep(100); - st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect, + &shadow_plane_state->fmtcnv_state); mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); out_exit: diff --git a/drivers/gpu/drm/v3d/Makefile b/drivers/gpu/drm/v3d/Makefile index e8b314137020..4b21b20e4998 100644 --- a/drivers/gpu/drm/v3d/Makefile +++ b/drivers/gpu/drm/v3d/Makefile @@ -11,7 +11,8 @@ v3d-y := \ v3d_mmu.o \ v3d_perfmon.o \ v3d_trace_points.o \ - v3d_sched.o + v3d_sched.o \ + v3d_sysfs.o v3d-$(CONFIG_DEBUG_FS) += v3d_debugfs.o diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index 330669f51fa7..f843a50d5dce 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -12,69 +12,83 @@ #include "v3d_drv.h" #include "v3d_regs.h" -#define REGDEF(reg) { reg, #reg } +#define REGDEF(min_ver, max_ver, reg) { min_ver, max_ver, reg, #reg } struct v3d_reg_def { + u32 min_ver; + u32 max_ver; u32 reg; const char *name; }; static const struct v3d_reg_def v3d_hub_reg_defs[] = { - REGDEF(V3D_HUB_AXICFG), - REGDEF(V3D_HUB_UIFCFG), - REGDEF(V3D_HUB_IDENT0), - REGDEF(V3D_HUB_IDENT1), - REGDEF(V3D_HUB_IDENT2), - REGDEF(V3D_HUB_IDENT3), - REGDEF(V3D_HUB_INT_STS), - REGDEF(V3D_HUB_INT_MSK_STS), - - REGDEF(V3D_MMU_CTL), - REGDEF(V3D_MMU_VIO_ADDR), - REGDEF(V3D_MMU_VIO_ID), - REGDEF(V3D_MMU_DEBUG_INFO), + REGDEF(33, 42, V3D_HUB_AXICFG), + REGDEF(33, 71, V3D_HUB_UIFCFG), + REGDEF(33, 71, V3D_HUB_IDENT0), + REGDEF(33, 71, V3D_HUB_IDENT1), + REGDEF(33, 71, V3D_HUB_IDENT2), + REGDEF(33, 71, V3D_HUB_IDENT3), + REGDEF(33, 71, V3D_HUB_INT_STS), + REGDEF(33, 71, V3D_HUB_INT_MSK_STS), + + REGDEF(33, 71, V3D_MMU_CTL), + REGDEF(33, 71, V3D_MMU_VIO_ADDR), + REGDEF(33, 71, V3D_MMU_VIO_ID), + REGDEF(33, 71, V3D_MMU_DEBUG_INFO), + + REGDEF(71, 71, V3D_GMP_STATUS(71)), + REGDEF(71, 71, V3D_GMP_CFG(71)), + REGDEF(71, 71, V3D_GMP_VIO_ADDR(71)), }; static const struct v3d_reg_def v3d_gca_reg_defs[] = { - REGDEF(V3D_GCA_SAFE_SHUTDOWN), - REGDEF(V3D_GCA_SAFE_SHUTDOWN_ACK), + REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN), + REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN_ACK), }; static const struct v3d_reg_def v3d_core_reg_defs[] = { - REGDEF(V3D_CTL_IDENT0), - REGDEF(V3D_CTL_IDENT1), - REGDEF(V3D_CTL_IDENT2), - REGDEF(V3D_CTL_MISCCFG), - REGDEF(V3D_CTL_INT_STS), - REGDEF(V3D_CTL_INT_MSK_STS), - REGDEF(V3D_CLE_CT0CS), - REGDEF(V3D_CLE_CT0CA), - REGDEF(V3D_CLE_CT0EA), - REGDEF(V3D_CLE_CT1CS), - REGDEF(V3D_CLE_CT1CA), - REGDEF(V3D_CLE_CT1EA), - - REGDEF(V3D_PTB_BPCA), - REGDEF(V3D_PTB_BPCS), - - REGDEF(V3D_GMP_STATUS), - REGDEF(V3D_GMP_CFG), - REGDEF(V3D_GMP_VIO_ADDR), - - REGDEF(V3D_ERR_FDBGO), - REGDEF(V3D_ERR_FDBGB), - REGDEF(V3D_ERR_FDBGS), - REGDEF(V3D_ERR_STAT), + REGDEF(33, 71, V3D_CTL_IDENT0), + REGDEF(33, 71, V3D_CTL_IDENT1), + REGDEF(33, 71, V3D_CTL_IDENT2), + REGDEF(33, 71, V3D_CTL_MISCCFG), + REGDEF(33, 71, V3D_CTL_INT_STS), + REGDEF(33, 71, V3D_CTL_INT_MSK_STS), + REGDEF(33, 71, V3D_CLE_CT0CS), + REGDEF(33, 71, V3D_CLE_CT0CA), + REGDEF(33, 71, V3D_CLE_CT0EA), + REGDEF(33, 71, V3D_CLE_CT1CS), + REGDEF(33, 71, V3D_CLE_CT1CA), + REGDEF(33, 71, V3D_CLE_CT1EA), + + REGDEF(33, 71, V3D_PTB_BPCA), + REGDEF(33, 71, V3D_PTB_BPCS), + + REGDEF(33, 41, V3D_GMP_STATUS(33)), + REGDEF(33, 41, V3D_GMP_CFG(33)), + REGDEF(33, 41, V3D_GMP_VIO_ADDR(33)), + + REGDEF(33, 71, V3D_ERR_FDBGO), + REGDEF(33, 71, V3D_ERR_FDBGB), + REGDEF(33, 71, V3D_ERR_FDBGS), + REGDEF(33, 71, V3D_ERR_STAT), }; static const struct v3d_reg_def v3d_csd_reg_defs[] = { - REGDEF(V3D_CSD_STATUS), - REGDEF(V3D_CSD_CURRENT_CFG0), - REGDEF(V3D_CSD_CURRENT_CFG1), - REGDEF(V3D_CSD_CURRENT_CFG2), - REGDEF(V3D_CSD_CURRENT_CFG3), - REGDEF(V3D_CSD_CURRENT_CFG4), - REGDEF(V3D_CSD_CURRENT_CFG5), - REGDEF(V3D_CSD_CURRENT_CFG6), + REGDEF(41, 71, V3D_CSD_STATUS), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG0(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG1(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG2(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG3(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG4(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG5(41)), + REGDEF(41, 41, V3D_CSD_CURRENT_CFG6(41)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG0(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG1(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG2(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG3(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG4(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG5(71)), + REGDEF(71, 71, V3D_CSD_CURRENT_CFG6(71)), + REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG7), }; static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) @@ -85,38 +99,41 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) int i, core; for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) { - seq_printf(m, "%s (0x%04x): 0x%08x\n", - v3d_hub_reg_defs[i].name, v3d_hub_reg_defs[i].reg, - V3D_READ(v3d_hub_reg_defs[i].reg)); + const struct v3d_reg_def *def = &v3d_hub_reg_defs[i]; + + if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + def->name, def->reg, V3D_READ(def->reg)); + } } - if (v3d->ver < 41) { - for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { + for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) { + const struct v3d_reg_def *def = &v3d_gca_reg_defs[i]; + + if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) { seq_printf(m, "%s (0x%04x): 0x%08x\n", - v3d_gca_reg_defs[i].name, - v3d_gca_reg_defs[i].reg, - V3D_GCA_READ(v3d_gca_reg_defs[i].reg)); + def->name, def->reg, V3D_GCA_READ(def->reg)); } } for (core = 0; core < v3d->cores; core++) { for (i = 0; i < ARRAY_SIZE(v3d_core_reg_defs); i++) { - seq_printf(m, "core %d %s (0x%04x): 0x%08x\n", - core, - v3d_core_reg_defs[i].name, - v3d_core_reg_defs[i].reg, - V3D_CORE_READ(core, - v3d_core_reg_defs[i].reg)); + const struct v3d_reg_def *def = &v3d_core_reg_defs[i]; + + if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) { + seq_printf(m, "core %d %s (0x%04x): 0x%08x\n", + core, def->name, def->reg, + V3D_CORE_READ(core, def->reg)); + } } - if (v3d_has_csd(v3d)) { - for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) { + for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) { + const struct v3d_reg_def *def = &v3d_csd_reg_defs[i]; + + if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) { seq_printf(m, "core %d %s (0x%04x): 0x%08x\n", - core, - v3d_csd_reg_defs[i].name, - v3d_csd_reg_defs[i].reg, - V3D_CORE_READ(core, - v3d_csd_reg_defs[i].reg)); + core, def->name, def->reg, + V3D_CORE_READ(core, def->reg)); } } } @@ -147,8 +164,10 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused) str_yes_no(ident2 & V3D_HUB_IDENT2_WITH_MMU)); seq_printf(m, "TFU: %s\n", str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TFU)); - seq_printf(m, "TSY: %s\n", - str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY)); + if (v3d->ver <= 42) { + seq_printf(m, "TSY: %s\n", + str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY)); + } seq_printf(m, "MSO: %s\n", str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_MSO)); seq_printf(m, "L3C: %s (%dkb)\n", @@ -177,10 +196,14 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused) seq_printf(m, " QPUs: %d\n", nslc * qups); seq_printf(m, " Semaphores: %d\n", V3D_GET_FIELD(ident1, V3D_IDENT1_NSEM)); - seq_printf(m, " BCG int: %d\n", - (ident2 & V3D_IDENT2_BCG_INT) != 0); - seq_printf(m, " Override TMU: %d\n", - (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0); + if (v3d->ver <= 42) { + seq_printf(m, " BCG int: %d\n", + (ident2 & V3D_IDENT2_BCG_INT) != 0); + } + if (v3d->ver < 40) { + seq_printf(m, " Override TMU: %d\n", + (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0); + } } return 0; @@ -212,14 +235,15 @@ static int v3d_measure_clock(struct seq_file *m, void *unused) int measure_ms = 1000; if (v3d->ver >= 40) { + int cycle_count_reg = V3D_PCTR_CYCLE_COUNT(v3d->ver); V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3, - V3D_SET_FIELD(V3D_PCTR_CYCLE_COUNT, + V3D_SET_FIELD(cycle_count_reg, V3D_PCTR_S0)); V3D_CORE_WRITE(core, V3D_V4_PCTR_0_CLR, 1); V3D_CORE_WRITE(core, V3D_V4_PCTR_0_EN, 1); } else { V3D_CORE_WRITE(core, V3D_V3_PCTR_0_PCTRS0, - V3D_PCTR_CYCLE_COUNT); + V3D_PCTR_CYCLE_COUNT(v3d->ver)); V3D_CORE_WRITE(core, V3D_V3_PCTR_0_CLR, 1); V3D_CORE_WRITE(core, V3D_V3_PCTR_0_EN, V3D_V3_PCTR_0_EN_ENABLE | diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index ffbbe9d527d3..44a1ca57d6a4 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/sched/clock.h> #include <linux/reset.h> #include <drm/drm_drv.h> @@ -111,6 +112,10 @@ v3d_open(struct drm_device *dev, struct drm_file *file) v3d_priv->v3d = v3d; for (i = 0; i < V3D_MAX_QUEUES; i++) { + v3d_priv->enabled_ns[i] = 0; + v3d_priv->start_ns[i] = 0; + v3d_priv->jobs_sent[i] = 0; + sched = &v3d->queue[i].sched; drm_sched_entity_init(&v3d_priv->sched_entity[i], DRM_SCHED_PRIORITY_NORMAL, &sched, @@ -136,7 +141,35 @@ v3d_postclose(struct drm_device *dev, struct drm_file *file) kfree(v3d_priv); } -DEFINE_DRM_GEM_FOPS(v3d_drm_fops); +static void v3d_show_fdinfo(struct drm_printer *p, struct drm_file *file) +{ + struct v3d_file_priv *file_priv = file->driver_priv; + u64 timestamp = local_clock(); + enum v3d_queue queue; + + for (queue = 0; queue < V3D_MAX_QUEUES; queue++) { + /* Note that, in case of a GPU reset, the time spent during an + * attempt of executing the job is not computed in the runtime. + */ + drm_printf(p, "drm-engine-%s: \t%llu ns\n", + v3d_queue_to_string(queue), + file_priv->start_ns[queue] ? file_priv->enabled_ns[queue] + + timestamp - file_priv->start_ns[queue] + : file_priv->enabled_ns[queue]); + + /* Note that we only count jobs that completed. Therefore, jobs + * that were resubmitted due to a GPU reset are not computed. + */ + drm_printf(p, "v3d-jobs-%s: \t%llu jobs\n", + v3d_queue_to_string(queue), file_priv->jobs_sent[queue]); + } +} + +static const struct file_operations v3d_drm_fops = { + .owner = THIS_MODULE, + DRM_GEM_FOPS, + .show_fdinfo = drm_show_fdinfo, +}; /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP * protection between clients. Note that render nodes would be @@ -176,6 +209,7 @@ static const struct drm_driver v3d_drm_driver = { .ioctls = v3d_drm_ioctls, .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls), .fops = &v3d_drm_fops, + .show_fdinfo = v3d_show_fdinfo, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -187,6 +221,7 @@ static const struct drm_driver v3d_drm_driver = { static const struct of_device_id v3d_of_match[] = { { .compatible = "brcm,2711-v3d" }, + { .compatible = "brcm,2712-v3d" }, { .compatible = "brcm,7268-v3d" }, { .compatible = "brcm,7278-v3d" }, {}, @@ -281,8 +316,14 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) if (ret) goto irq_disable; + ret = v3d_sysfs_init(dev); + if (ret) + goto drm_unregister; + return 0; +drm_unregister: + drm_dev_unregister(drm); irq_disable: v3d_irq_disable(v3d); gem_destroy: @@ -296,6 +337,9 @@ static void v3d_platform_drm_remove(struct platform_device *pdev) { struct drm_device *drm = platform_get_drvdata(pdev); struct v3d_dev *v3d = to_v3d_dev(drm); + struct device *dev = &pdev->dev; + + v3d_sysfs_destroy(dev); drm_dev_unregister(drm); diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 106454f28956..4c59fefaa0b4 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -21,11 +21,27 @@ struct reset_control; #define V3D_MAX_QUEUES (V3D_CACHE_CLEAN + 1) +static inline char *v3d_queue_to_string(enum v3d_queue queue) +{ + switch (queue) { + case V3D_BIN: return "bin"; + case V3D_RENDER: return "render"; + case V3D_TFU: return "tfu"; + case V3D_CSD: return "csd"; + case V3D_CACHE_CLEAN: return "cache_clean"; + } + return "UNKNOWN"; +} + struct v3d_queue_state { struct drm_gpu_scheduler sched; u64 fence_context; u64 emit_seqno; + + u64 start_ns; + u64 enabled_ns; + u64 jobs_sent; }; /* Performance monitor object. The perform lifetime is controlled by userspace @@ -167,6 +183,12 @@ struct v3d_file_priv { } perfmon; struct drm_sched_entity sched_entity[V3D_MAX_QUEUES]; + + u64 start_ns[V3D_MAX_QUEUES]; + + u64 enabled_ns[V3D_MAX_QUEUES]; + + u64 jobs_sent[V3D_MAX_QUEUES]; }; struct v3d_bo { @@ -238,6 +260,11 @@ struct v3d_job { */ struct v3d_perfmon *perfmon; + /* File descriptor of the process that submitted the job that could be used + * for collecting stats by process of GPU usage. + */ + struct drm_file *file; + /* Callback for the freeing of the job on refcount going to 0. */ void (*free)(struct kref *ref); }; @@ -418,3 +445,7 @@ int v3d_perfmon_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); + +/* v3d_sysfs.c */ +int v3d_sysfs_init(struct device *dev); +void v3d_sysfs_destroy(struct device *dev); diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 2e94ce788c71..9d2ac23c29e3 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -47,9 +47,9 @@ v3d_init_hw_state(struct v3d_dev *v3d) static void v3d_idle_axi(struct v3d_dev *v3d, int core) { - V3D_CORE_WRITE(core, V3D_GMP_CFG, V3D_GMP_CFG_STOP_REQ); + V3D_CORE_WRITE(core, V3D_GMP_CFG(v3d->ver), V3D_GMP_CFG_STOP_REQ); - if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS) & + if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS(v3d->ver)) & (V3D_GMP_STATUS_RD_COUNT_MASK | V3D_GMP_STATUS_WR_COUNT_MASK | V3D_GMP_STATUS_CFG_BUSY)) == 0, 100)) { @@ -415,9 +415,10 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, job = *container; job->v3d = v3d; job->free = free; + job->file = file_priv; ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], - v3d_priv); + 1, v3d_priv); if (ret) goto fail; @@ -1013,8 +1014,12 @@ v3d_gem_init(struct drm_device *dev) u32 pt_size = 4096 * 1024; int ret, i; - for (i = 0; i < V3D_MAX_QUEUES; i++) + for (i = 0; i < V3D_MAX_QUEUES; i++) { v3d->queue[i].fence_context = dma_fence_context_alloc(1); + v3d->queue[i].start_ns = 0; + v3d->queue[i].enabled_ns = 0; + v3d->queue[i].jobs_sent = 0; + } spin_lock_init(&v3d->mm_lock); spin_lock_init(&v3d->job_lock); @@ -1072,6 +1077,8 @@ v3d_gem_destroy(struct drm_device *dev) */ WARN_ON(v3d->bin_job); WARN_ON(v3d->render_job); + WARN_ON(v3d->tfu_job); + WARN_ON(v3d->csd_job); drm_mm_takedown(&v3d->mm); diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c index e714d5318f30..afc76390a197 100644 --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c @@ -14,21 +14,23 @@ */ #include <linux/platform_device.h> +#include <linux/sched/clock.h> #include "v3d_drv.h" #include "v3d_regs.h" #include "v3d_trace.h" -#define V3D_CORE_IRQS ((u32)(V3D_INT_OUTOMEM | \ - V3D_INT_FLDONE | \ - V3D_INT_FRDONE | \ - V3D_INT_CSDDONE | \ - V3D_INT_GMPV)) +#define V3D_CORE_IRQS(ver) ((u32)(V3D_INT_OUTOMEM | \ + V3D_INT_FLDONE | \ + V3D_INT_FRDONE | \ + V3D_INT_CSDDONE(ver) | \ + (ver < 71 ? V3D_INT_GMPV : 0))) -#define V3D_HUB_IRQS ((u32)(V3D_HUB_INT_MMU_WRV | \ - V3D_HUB_INT_MMU_PTI | \ - V3D_HUB_INT_MMU_CAP | \ - V3D_HUB_INT_TFUC)) +#define V3D_HUB_IRQS(ver) ((u32)(V3D_HUB_INT_MMU_WRV | \ + V3D_HUB_INT_MMU_PTI | \ + V3D_HUB_INT_MMU_CAP | \ + V3D_HUB_INT_TFUC | \ + (ver >= 71 ? V3D_V7_HUB_INT_GMPV : 0))) static irqreturn_t v3d_hub_irq(int irq, void *arg); @@ -100,6 +102,18 @@ v3d_irq(int irq, void *arg) if (intsts & V3D_INT_FLDONE) { struct v3d_fence *fence = to_v3d_fence(v3d->bin_job->base.irq_fence); + struct v3d_file_priv *file = v3d->bin_job->base.file->driver_priv; + u64 runtime = local_clock() - file->start_ns[V3D_BIN]; + + file->enabled_ns[V3D_BIN] += local_clock() - file->start_ns[V3D_BIN]; + file->jobs_sent[V3D_BIN]++; + v3d->queue[V3D_BIN].jobs_sent++; + + file->start_ns[V3D_BIN] = 0; + v3d->queue[V3D_BIN].start_ns = 0; + + file->enabled_ns[V3D_BIN] += runtime; + v3d->queue[V3D_BIN].enabled_ns += runtime; trace_v3d_bcl_irq(&v3d->drm, fence->seqno); dma_fence_signal(&fence->base); @@ -109,15 +123,39 @@ v3d_irq(int irq, void *arg) if (intsts & V3D_INT_FRDONE) { struct v3d_fence *fence = to_v3d_fence(v3d->render_job->base.irq_fence); + struct v3d_file_priv *file = v3d->render_job->base.file->driver_priv; + u64 runtime = local_clock() - file->start_ns[V3D_RENDER]; + + file->enabled_ns[V3D_RENDER] += local_clock() - file->start_ns[V3D_RENDER]; + file->jobs_sent[V3D_RENDER]++; + v3d->queue[V3D_RENDER].jobs_sent++; + + file->start_ns[V3D_RENDER] = 0; + v3d->queue[V3D_RENDER].start_ns = 0; + + file->enabled_ns[V3D_RENDER] += runtime; + v3d->queue[V3D_RENDER].enabled_ns += runtime; trace_v3d_rcl_irq(&v3d->drm, fence->seqno); dma_fence_signal(&fence->base); status = IRQ_HANDLED; } - if (intsts & V3D_INT_CSDDONE) { + if (intsts & V3D_INT_CSDDONE(v3d->ver)) { struct v3d_fence *fence = to_v3d_fence(v3d->csd_job->base.irq_fence); + struct v3d_file_priv *file = v3d->csd_job->base.file->driver_priv; + u64 runtime = local_clock() - file->start_ns[V3D_CSD]; + + file->enabled_ns[V3D_CSD] += local_clock() - file->start_ns[V3D_CSD]; + file->jobs_sent[V3D_CSD]++; + v3d->queue[V3D_CSD].jobs_sent++; + + file->start_ns[V3D_CSD] = 0; + v3d->queue[V3D_CSD].start_ns = 0; + + file->enabled_ns[V3D_CSD] += runtime; + v3d->queue[V3D_CSD].enabled_ns += runtime; trace_v3d_csd_irq(&v3d->drm, fence->seqno); dma_fence_signal(&fence->base); @@ -127,7 +165,7 @@ v3d_irq(int irq, void *arg) /* We shouldn't be triggering these if we have GMP in * always-allowed mode. */ - if (intsts & V3D_INT_GMPV) + if (v3d->ver < 71 && (intsts & V3D_INT_GMPV)) dev_err(v3d->drm.dev, "GMP violation\n"); /* V3D 4.2 wires the hub and core IRQs together, so if we & @@ -154,6 +192,18 @@ v3d_hub_irq(int irq, void *arg) if (intsts & V3D_HUB_INT_TFUC) { struct v3d_fence *fence = to_v3d_fence(v3d->tfu_job->base.irq_fence); + struct v3d_file_priv *file = v3d->tfu_job->base.file->driver_priv; + u64 runtime = local_clock() - file->start_ns[V3D_TFU]; + + file->enabled_ns[V3D_TFU] += local_clock() - file->start_ns[V3D_TFU]; + file->jobs_sent[V3D_TFU]++; + v3d->queue[V3D_TFU].jobs_sent++; + + file->start_ns[V3D_TFU] = 0; + v3d->queue[V3D_TFU].start_ns = 0; + + file->enabled_ns[V3D_TFU] += runtime; + v3d->queue[V3D_TFU].enabled_ns += runtime; trace_v3d_tfu_irq(&v3d->drm, fence->seqno); dma_fence_signal(&fence->base); @@ -197,6 +247,11 @@ v3d_hub_irq(int irq, void *arg) status = IRQ_HANDLED; } + if (v3d->ver >= 71 && (intsts & V3D_V7_HUB_INT_GMPV)) { + dev_err(v3d->drm.dev, "GMP Violation\n"); + status = IRQ_HANDLED; + } + return status; } @@ -211,8 +266,8 @@ v3d_irq_init(struct v3d_dev *v3d) * for us. */ for (core = 0; core < v3d->cores; core++) - V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS); - V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS); + V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver)); + V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver)); irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1); if (irq1 == -EPROBE_DEFER) @@ -256,12 +311,12 @@ v3d_irq_enable(struct v3d_dev *v3d) /* Enable our set of interrupts, masking out any others. */ for (core = 0; core < v3d->cores; core++) { - V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS); - V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS); + V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS(v3d->ver)); + V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS(v3d->ver)); } - V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS); - V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS); + V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS(v3d->ver)); + V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS(v3d->ver)); } void @@ -276,8 +331,8 @@ v3d_irq_disable(struct v3d_dev *v3d) /* Clear any pending interrupts we might have left. */ for (core = 0; core < v3d->cores; core++) - V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS); - V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS); + V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver)); + V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver)); cancel_work_sync(&v3d->overflow_mem_work); } diff --git a/drivers/gpu/drm/v3d/v3d_regs.h b/drivers/gpu/drm/v3d/v3d_regs.h index 3663e0d6bf76..1b1a62ad9585 100644 --- a/drivers/gpu/drm/v3d/v3d_regs.h +++ b/drivers/gpu/drm/v3d/v3d_regs.h @@ -57,6 +57,7 @@ #define V3D_HUB_INT_MSK_STS 0x0005c #define V3D_HUB_INT_MSK_SET 0x00060 #define V3D_HUB_INT_MSK_CLR 0x00064 +# define V3D_V7_HUB_INT_GMPV BIT(6) # define V3D_HUB_INT_MMU_WRV BIT(5) # define V3D_HUB_INT_MMU_PTI BIT(4) # define V3D_HUB_INT_MMU_CAP BIT(3) @@ -64,6 +65,7 @@ # define V3D_HUB_INT_TFUC BIT(1) # define V3D_HUB_INT_TFUF BIT(0) +/* GCA registers only exist in V3D < 41 */ #define V3D_GCA_CACHE_CTRL 0x0000c # define V3D_GCA_CACHE_CTRL_FLUSH BIT(0) @@ -86,7 +88,8 @@ # define V3D_TOP_GR_BRIDGE_SW_INIT_1 0x0000c # define V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT BIT(0) -#define V3D_TFU_CS 0x00400 +#define V3D_TFU_CS(ver) ((ver >= 71) ? 0x00700 : 0x00400) + /* Stops current job, empties input fifo. */ # define V3D_TFU_CS_TFURST BIT(31) # define V3D_TFU_CS_CVTCT_MASK V3D_MASK(23, 16) @@ -95,7 +98,7 @@ # define V3D_TFU_CS_NFREE_SHIFT 8 # define V3D_TFU_CS_BUSY BIT(0) -#define V3D_TFU_SU 0x00404 +#define V3D_TFU_SU(ver) ((ver >= 71) ? 0x00704 : 0x00404) /* Interrupt when FINTTHR input slots are free (0 = disabled) */ # define V3D_TFU_SU_FINTTHR_MASK V3D_MASK(13, 8) # define V3D_TFU_SU_FINTTHR_SHIFT 8 @@ -106,39 +109,42 @@ # define V3D_TFU_SU_THROTTLE_MASK V3D_MASK(1, 0) # define V3D_TFU_SU_THROTTLE_SHIFT 0 -#define V3D_TFU_ICFG 0x00408 +#define V3D_TFU_ICFG(ver) ((ver >= 71) ? 0x00708 : 0x00408) /* Interrupt when the conversion is complete. */ # define V3D_TFU_ICFG_IOC BIT(0) /* Input Image Address */ -#define V3D_TFU_IIA 0x0040c +#define V3D_TFU_IIA(ver) ((ver >= 71) ? 0x0070c : 0x0040c) /* Input Chroma Address */ -#define V3D_TFU_ICA 0x00410 +#define V3D_TFU_ICA(ver) ((ver >= 71) ? 0x00710 : 0x00410) /* Input Image Stride */ -#define V3D_TFU_IIS 0x00414 +#define V3D_TFU_IIS(ver) ((ver >= 71) ? 0x00714 : 0x00414) /* Input Image U-Plane Address */ -#define V3D_TFU_IUA 0x00418 +#define V3D_TFU_IUA(ver) ((ver >= 71) ? 0x00718 : 0x00418) +/* Image output config (VD 7.x only) */ +#define V3D_V7_TFU_IOC 0x0071c /* Output Image Address */ -#define V3D_TFU_IOA 0x0041c +#define V3D_TFU_IOA(ver) ((ver >= 71) ? 0x00720 : 0x0041c) /* Image Output Size */ -#define V3D_TFU_IOS 0x00420 +#define V3D_TFU_IOS(ver) ((ver >= 71) ? 0x00724 : 0x00420) /* TFU YUV Coefficient 0 */ -#define V3D_TFU_COEF0 0x00424 -/* Use these regs instead of the defaults. */ +#define V3D_TFU_COEF0(ver) ((ver >= 71) ? 0x00728 : 0x00424) +/* Use these regs instead of the defaults (V3D 4.x only) */ # define V3D_TFU_COEF0_USECOEF BIT(31) /* TFU YUV Coefficient 1 */ -#define V3D_TFU_COEF1 0x00428 +#define V3D_TFU_COEF1(ver) ((ver >= 71) ? 0x0072c : 0x00428) /* TFU YUV Coefficient 2 */ -#define V3D_TFU_COEF2 0x0042c +#define V3D_TFU_COEF2(ver) ((ver >= 71) ? 0x00730 : 0x0042c) /* TFU YUV Coefficient 3 */ -#define V3D_TFU_COEF3 0x00430 +#define V3D_TFU_COEF3(ver) ((ver >= 71) ? 0x00734 : 0x00430) +/* V3D 4.x only */ #define V3D_TFU_CRC 0x00434 /* Per-MMU registers. */ #define V3D_MMUC_CONTROL 0x01000 -# define V3D_MMUC_CONTROL_CLEAR BIT(3) +#define V3D_MMUC_CONTROL_CLEAR(ver) ((ver >= 71) ? BIT(11) : BIT(3)) # define V3D_MMUC_CONTROL_FLUSHING BIT(2) # define V3D_MMUC_CONTROL_FLUSH BIT(1) # define V3D_MMUC_CONTROL_ENABLE BIT(0) @@ -246,7 +252,6 @@ #define V3D_CTL_L2TCACTL 0x00030 # define V3D_L2TCACTL_TMUWCF BIT(8) -# define V3D_L2TCACTL_L2T_NO_WM BIT(4) /* Invalidates cache lines. */ # define V3D_L2TCACTL_FLM_FLUSH 0 /* Removes cachelines without writing dirty lines back. */ @@ -267,8 +272,8 @@ #define V3D_CTL_INT_MSK_CLR 0x00064 # define V3D_INT_QPU_MASK V3D_MASK(27, 16) # define V3D_INT_QPU_SHIFT 16 -# define V3D_INT_CSDDONE BIT(7) -# define V3D_INT_PCTR BIT(6) +#define V3D_INT_CSDDONE(ver) ((ver >= 71) ? BIT(6) : BIT(7)) +#define V3D_INT_PCTR(ver) ((ver >= 71) ? BIT(5) : BIT(6)) # define V3D_INT_GMPV BIT(5) # define V3D_INT_TRFB BIT(4) # define V3D_INT_SPILLUSE BIT(3) @@ -350,21 +355,25 @@ #define V3D_V4_PCTR_0_SRC_X(x) (V3D_V4_PCTR_0_SRC_0_3 + \ 4 * (x)) # define V3D_PCTR_S0_MASK V3D_MASK(6, 0) +# define V3D_V7_PCTR_S0_MASK V3D_MASK(7, 0) # define V3D_PCTR_S0_SHIFT 0 # define V3D_PCTR_S1_MASK V3D_MASK(14, 8) +# define V3D_V7_PCTR_S1_MASK V3D_MASK(15, 8) # define V3D_PCTR_S1_SHIFT 8 # define V3D_PCTR_S2_MASK V3D_MASK(22, 16) +# define V3D_V7_PCTR_S2_MASK V3D_MASK(23, 16) # define V3D_PCTR_S2_SHIFT 16 # define V3D_PCTR_S3_MASK V3D_MASK(30, 24) +# define V3D_V7_PCTR_S3_MASK V3D_MASK(31, 24) # define V3D_PCTR_S3_SHIFT 24 -# define V3D_PCTR_CYCLE_COUNT 32 +#define V3D_PCTR_CYCLE_COUNT(ver) ((ver >= 71) ? 0 : 32) /* Output values of the counters. */ #define V3D_PCTR_0_PCTR0 0x00680 #define V3D_PCTR_0_PCTR31 0x006fc #define V3D_PCTR_0_PCTRX(x) (V3D_PCTR_0_PCTR0 + \ 4 * (x)) -#define V3D_GMP_STATUS 0x00800 +#define V3D_GMP_STATUS(ver) ((ver >= 71) ? 0x00600 : 0x00800) # define V3D_GMP_STATUS_GMPRST BIT(31) # define V3D_GMP_STATUS_WR_COUNT_MASK V3D_MASK(30, 24) # define V3D_GMP_STATUS_WR_COUNT_SHIFT 24 @@ -377,13 +386,13 @@ # define V3D_GMP_STATUS_INVPROT BIT(1) # define V3D_GMP_STATUS_VIO BIT(0) -#define V3D_GMP_CFG 0x00804 +#define V3D_GMP_CFG(ver) ((ver >= 71) ? 0x00604 : 0x00804) # define V3D_GMP_CFG_LBURSTEN BIT(3) # define V3D_GMP_CFG_PGCRSEN BIT() # define V3D_GMP_CFG_STOP_REQ BIT(1) # define V3D_GMP_CFG_PROT_ENABLE BIT(0) -#define V3D_GMP_VIO_ADDR 0x00808 +#define V3D_GMP_VIO_ADDR(ver) ((ver >= 71) ? 0x00608 : 0x00808) #define V3D_GMP_VIO_TYPE 0x0080c #define V3D_GMP_TABLE_ADDR 0x00810 #define V3D_GMP_CLEAR_LOAD 0x00814 @@ -398,25 +407,25 @@ # define V3D_CSD_STATUS_HAVE_CURRENT_DISPATCH BIT(1) # define V3D_CSD_STATUS_HAVE_QUEUED_DISPATCH BIT(0) -#define V3D_CSD_QUEUED_CFG0 0x00904 +#define V3D_CSD_QUEUED_CFG0(ver) ((ver >= 71) ? 0x00930 : 0x00904) # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_MASK V3D_MASK(31, 16) # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_SHIFT 16 # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_MASK V3D_MASK(15, 0) # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_SHIFT 0 -#define V3D_CSD_QUEUED_CFG1 0x00908 +#define V3D_CSD_QUEUED_CFG1(ver) ((ver >= 71) ? 0x00934 : 0x00908) # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_MASK V3D_MASK(31, 16) # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_SHIFT 16 # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_MASK V3D_MASK(15, 0) # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_SHIFT 0 -#define V3D_CSD_QUEUED_CFG2 0x0090c +#define V3D_CSD_QUEUED_CFG2(ver) ((ver >= 71) ? 0x00938 : 0x0090c) # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_MASK V3D_MASK(31, 16) # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_SHIFT 16 # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_MASK V3D_MASK(15, 0) # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_SHIFT 0 -#define V3D_CSD_QUEUED_CFG3 0x00910 +#define V3D_CSD_QUEUED_CFG3(ver) ((ver >= 71) ? 0x0093c : 0x00910) # define V3D_CSD_QUEUED_CFG3_OVERLAP_WITH_PREV BIT(26) # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_MASK V3D_MASK(25, 20) # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_SHIFT 20 @@ -428,23 +437,28 @@ # define V3D_CSD_QUEUED_CFG3_WG_SIZE_SHIFT 0 /* Number of batches, minus 1 */ -#define V3D_CSD_QUEUED_CFG4 0x00914 +#define V3D_CSD_QUEUED_CFG4(ver) ((ver >= 71) ? 0x00940 : 0x00914) /* Shader address, pnan, singleseg, threading, like a shader record. */ -#define V3D_CSD_QUEUED_CFG5 0x00918 +#define V3D_CSD_QUEUED_CFG5(ver) ((ver >= 71) ? 0x00944 : 0x00918) /* Uniforms address (4 byte aligned) */ -#define V3D_CSD_QUEUED_CFG6 0x0091c - -#define V3D_CSD_CURRENT_CFG0 0x00920 -#define V3D_CSD_CURRENT_CFG1 0x00924 -#define V3D_CSD_CURRENT_CFG2 0x00928 -#define V3D_CSD_CURRENT_CFG3 0x0092c -#define V3D_CSD_CURRENT_CFG4 0x00930 -#define V3D_CSD_CURRENT_CFG5 0x00934 -#define V3D_CSD_CURRENT_CFG6 0x00938 - -#define V3D_CSD_CURRENT_ID0 0x0093c +#define V3D_CSD_QUEUED_CFG6(ver) ((ver >= 71) ? 0x00948 : 0x0091c) + +/* V3D 7.x+ only */ +#define V3D_V7_CSD_QUEUED_CFG7 0x0094c + +#define V3D_CSD_CURRENT_CFG0(ver) ((ver >= 71) ? 0x00958 : 0x00920) +#define V3D_CSD_CURRENT_CFG1(ver) ((ver >= 71) ? 0x0095c : 0x00924) +#define V3D_CSD_CURRENT_CFG2(ver) ((ver >= 71) ? 0x00960 : 0x00928) +#define V3D_CSD_CURRENT_CFG3(ver) ((ver >= 71) ? 0x00964 : 0x0092c) +#define V3D_CSD_CURRENT_CFG4(ver) ((ver >= 71) ? 0x00968 : 0x00930) +#define V3D_CSD_CURRENT_CFG5(ver) ((ver >= 71) ? 0x0096c : 0x00934) +#define V3D_CSD_CURRENT_CFG6(ver) ((ver >= 71) ? 0x00970 : 0x00938) +/* V3D 7.x+ only */ +#define V3D_V7_CSD_CURRENT_CFG7 0x00974 + +#define V3D_CSD_CURRENT_ID0(ver) ((ver >= 71) ? 0x00978 : 0x0093c) # define V3D_CSD_CURRENT_ID0_WG_X_MASK V3D_MASK(31, 16) # define V3D_CSD_CURRENT_ID0_WG_X_SHIFT 16 # define V3D_CSD_CURRENT_ID0_WG_IN_SG_MASK V3D_MASK(11, 8) @@ -452,7 +466,7 @@ # define V3D_CSD_CURRENT_ID0_L_IDX_MASK V3D_MASK(7, 0) # define V3D_CSD_CURRENT_ID0_L_IDX_SHIFT 0 -#define V3D_CSD_CURRENT_ID1 0x00940 +#define V3D_CSD_CURRENT_ID1(ver) ((ver >= 71) ? 0x0097c : 0x00940) # define V3D_CSD_CURRENT_ID0_WG_Z_MASK V3D_MASK(31, 16) # define V3D_CSD_CURRENT_ID0_WG_Z_SHIFT 16 # define V3D_CSD_CURRENT_ID0_WG_Y_MASK V3D_MASK(15, 0) diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 038e1ae589c7..fccbea2a5f2e 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -18,6 +18,7 @@ * semaphores to interlock between them. */ +#include <linux/sched/clock.h> #include <linux/kthread.h> #include "v3d_drv.h" @@ -76,6 +77,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) { struct v3d_bin_job *job = to_bin_job(sched_job); struct v3d_dev *v3d = job->base.v3d; + struct v3d_file_priv *file = job->base.file->driver_priv; struct drm_device *dev = &v3d->drm; struct dma_fence *fence; unsigned long irqflags; @@ -107,6 +109,9 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) trace_v3d_submit_cl(dev, false, to_v3d_fence(fence)->seqno, job->start, job->end); + file->start_ns[V3D_BIN] = local_clock(); + v3d->queue[V3D_BIN].start_ns = file->start_ns[V3D_BIN]; + v3d_switch_perfmon(v3d, &job->base); /* Set the current and end address of the control list. @@ -131,6 +136,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) { struct v3d_render_job *job = to_render_job(sched_job); struct v3d_dev *v3d = job->base.v3d; + struct v3d_file_priv *file = job->base.file->driver_priv; struct drm_device *dev = &v3d->drm; struct dma_fence *fence; @@ -158,6 +164,9 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) trace_v3d_submit_cl(dev, true, to_v3d_fence(fence)->seqno, job->start, job->end); + file->start_ns[V3D_RENDER] = local_clock(); + v3d->queue[V3D_RENDER].start_ns = file->start_ns[V3D_RENDER]; + v3d_switch_perfmon(v3d, &job->base); /* XXX: Set the QCFG */ @@ -176,6 +185,7 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) { struct v3d_tfu_job *job = to_tfu_job(sched_job); struct v3d_dev *v3d = job->base.v3d; + struct v3d_file_priv *file = job->base.file->driver_priv; struct drm_device *dev = &v3d->drm; struct dma_fence *fence; @@ -190,20 +200,25 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno); - V3D_WRITE(V3D_TFU_IIA, job->args.iia); - V3D_WRITE(V3D_TFU_IIS, job->args.iis); - V3D_WRITE(V3D_TFU_ICA, job->args.ica); - V3D_WRITE(V3D_TFU_IUA, job->args.iua); - V3D_WRITE(V3D_TFU_IOA, job->args.ioa); - V3D_WRITE(V3D_TFU_IOS, job->args.ios); - V3D_WRITE(V3D_TFU_COEF0, job->args.coef[0]); - if (job->args.coef[0] & V3D_TFU_COEF0_USECOEF) { - V3D_WRITE(V3D_TFU_COEF1, job->args.coef[1]); - V3D_WRITE(V3D_TFU_COEF2, job->args.coef[2]); - V3D_WRITE(V3D_TFU_COEF3, job->args.coef[3]); + file->start_ns[V3D_TFU] = local_clock(); + v3d->queue[V3D_TFU].start_ns = file->start_ns[V3D_TFU]; + + V3D_WRITE(V3D_TFU_IIA(v3d->ver), job->args.iia); + V3D_WRITE(V3D_TFU_IIS(v3d->ver), job->args.iis); + V3D_WRITE(V3D_TFU_ICA(v3d->ver), job->args.ica); + V3D_WRITE(V3D_TFU_IUA(v3d->ver), job->args.iua); + V3D_WRITE(V3D_TFU_IOA(v3d->ver), job->args.ioa); + if (v3d->ver >= 71) + V3D_WRITE(V3D_V7_TFU_IOC, job->args.v71.ioc); + V3D_WRITE(V3D_TFU_IOS(v3d->ver), job->args.ios); + V3D_WRITE(V3D_TFU_COEF0(v3d->ver), job->args.coef[0]); + if (v3d->ver >= 71 || (job->args.coef[0] & V3D_TFU_COEF0_USECOEF)) { + V3D_WRITE(V3D_TFU_COEF1(v3d->ver), job->args.coef[1]); + V3D_WRITE(V3D_TFU_COEF2(v3d->ver), job->args.coef[2]); + V3D_WRITE(V3D_TFU_COEF3(v3d->ver), job->args.coef[3]); } /* ICFG kicks off the job. */ - V3D_WRITE(V3D_TFU_ICFG, job->args.icfg | V3D_TFU_ICFG_IOC); + V3D_WRITE(V3D_TFU_ICFG(v3d->ver), job->args.icfg | V3D_TFU_ICFG_IOC); return fence; } @@ -213,9 +228,10 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) { struct v3d_csd_job *job = to_csd_job(sched_job); struct v3d_dev *v3d = job->base.v3d; + struct v3d_file_priv *file = job->base.file->driver_priv; struct drm_device *dev = &v3d->drm; struct dma_fence *fence; - int i; + int i, csd_cfg0_reg, csd_cfg_reg_count; v3d->csd_job = job; @@ -231,12 +247,17 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) trace_v3d_submit_csd(dev, to_v3d_fence(fence)->seqno); + file->start_ns[V3D_CSD] = local_clock(); + v3d->queue[V3D_CSD].start_ns = file->start_ns[V3D_CSD]; + v3d_switch_perfmon(v3d, &job->base); - for (i = 1; i <= 6; i++) - V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0 + 4 * i, job->args.cfg[i]); + csd_cfg0_reg = V3D_CSD_QUEUED_CFG0(v3d->ver); + csd_cfg_reg_count = v3d->ver < 71 ? 6 : 7; + for (i = 1; i <= csd_cfg_reg_count; i++) + V3D_CORE_WRITE(0, csd_cfg0_reg + 4 * i, job->args.cfg[i]); /* CFG0 write kicks off the job. */ - V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0, job->args.cfg[0]); + V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]); return fence; } @@ -246,9 +267,25 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job) { struct v3d_job *job = to_v3d_job(sched_job); struct v3d_dev *v3d = job->v3d; + struct v3d_file_priv *file = job->file->driver_priv; + u64 runtime; + + file->start_ns[V3D_CACHE_CLEAN] = local_clock(); + v3d->queue[V3D_CACHE_CLEAN].start_ns = file->start_ns[V3D_CACHE_CLEAN]; v3d_clean_caches(v3d); + runtime = local_clock() - file->start_ns[V3D_CACHE_CLEAN]; + + file->enabled_ns[V3D_CACHE_CLEAN] += runtime; + v3d->queue[V3D_CACHE_CLEAN].enabled_ns += runtime; + + file->jobs_sent[V3D_CACHE_CLEAN]++; + v3d->queue[V3D_CACHE_CLEAN].jobs_sent++; + + file->start_ns[V3D_CACHE_CLEAN] = 0; + v3d->queue[V3D_CACHE_CLEAN].start_ns = 0; + return NULL; } @@ -336,7 +373,7 @@ v3d_csd_job_timedout(struct drm_sched_job *sched_job) { struct v3d_csd_job *job = to_csd_job(sched_job); struct v3d_dev *v3d = job->base.v3d; - u32 batches = V3D_CORE_READ(0, V3D_CSD_CURRENT_CFG4); + u32 batches = V3D_CORE_READ(0, V3D_CSD_CURRENT_CFG4(v3d->ver)); /* If we've made progress, skip reset and let the timer get * rearmed. @@ -388,7 +425,7 @@ v3d_sched_init(struct v3d_dev *v3d) int ret; ret = drm_sched_init(&v3d->queue[V3D_BIN].sched, - &v3d_bin_sched_ops, + &v3d_bin_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, hw_jobs_limit, job_hang_limit, msecs_to_jiffies(hang_limit_ms), NULL, @@ -397,7 +434,7 @@ v3d_sched_init(struct v3d_dev *v3d) return ret; ret = drm_sched_init(&v3d->queue[V3D_RENDER].sched, - &v3d_render_sched_ops, + &v3d_render_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, hw_jobs_limit, job_hang_limit, msecs_to_jiffies(hang_limit_ms), NULL, @@ -406,7 +443,7 @@ v3d_sched_init(struct v3d_dev *v3d) goto fail; ret = drm_sched_init(&v3d->queue[V3D_TFU].sched, - &v3d_tfu_sched_ops, + &v3d_tfu_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, hw_jobs_limit, job_hang_limit, msecs_to_jiffies(hang_limit_ms), NULL, @@ -416,7 +453,7 @@ v3d_sched_init(struct v3d_dev *v3d) if (v3d_has_csd(v3d)) { ret = drm_sched_init(&v3d->queue[V3D_CSD].sched, - &v3d_csd_sched_ops, + &v3d_csd_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, hw_jobs_limit, job_hang_limit, msecs_to_jiffies(hang_limit_ms), NULL, @@ -425,7 +462,7 @@ v3d_sched_init(struct v3d_dev *v3d) goto fail; ret = drm_sched_init(&v3d->queue[V3D_CACHE_CLEAN].sched, - &v3d_cache_clean_sched_ops, + &v3d_cache_clean_sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT, hw_jobs_limit, job_hang_limit, msecs_to_jiffies(hang_limit_ms), NULL, diff --git a/drivers/gpu/drm/v3d/v3d_sysfs.c b/drivers/gpu/drm/v3d/v3d_sysfs.c new file mode 100644 index 000000000000..d106845ba890 --- /dev/null +++ b/drivers/gpu/drm/v3d/v3d_sysfs.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Igalia S.L. + */ + +#include <linux/sched/clock.h> +#include <linux/sysfs.h> + +#include "v3d_drv.h" + +static ssize_t +gpu_stats_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct drm_device *drm = dev_get_drvdata(dev); + struct v3d_dev *v3d = to_v3d_dev(drm); + enum v3d_queue queue; + u64 timestamp = local_clock(); + u64 active_runtime; + ssize_t len = 0; + + len += sysfs_emit(buf, "queue\ttimestamp\tjobs\truntime\n"); + + for (queue = 0; queue < V3D_MAX_QUEUES; queue++) { + if (v3d->queue[queue].start_ns) + active_runtime = timestamp - v3d->queue[queue].start_ns; + else + active_runtime = 0; + + /* Each line will display the queue name, timestamp, the number + * of jobs sent to that queue and the runtime, as can be seem here: + * + * queue timestamp jobs runtime + * bin 239043069420 22620 17438164056 + * render 239043069420 22619 27284814161 + * tfu 239043069420 8763 394592566 + * csd 239043069420 3168 10787905530 + * cache_clean 239043069420 6127 237375940 + */ + len += sysfs_emit_at(buf, len, "%s\t%llu\t%llu\t%llu\n", + v3d_queue_to_string(queue), + timestamp, + v3d->queue[queue].jobs_sent, + v3d->queue[queue].enabled_ns + active_runtime); + } + + return len; +} +static DEVICE_ATTR_RO(gpu_stats); + +static struct attribute *v3d_sysfs_entries[] = { + &dev_attr_gpu_stats.attr, + NULL, +}; + +static struct attribute_group v3d_sysfs_attr_group = { + .attrs = v3d_sysfs_entries, +}; + +int +v3d_sysfs_init(struct device *dev) +{ + return sysfs_create_group(&dev->kobj, &v3d_sysfs_attr_group); +} + +void +v3d_sysfs_destroy(struct device *dev) +{ + return sysfs_remove_group(&dev->kobj, &v3d_sysfs_attr_group); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 96365a772f77..bb7d86a0c6a1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -58,6 +58,9 @@ #define MAX_CAPSET_ID 63 #define MAX_RINGS 64 +/* See virtio_gpu_ctx_create. One additional character for NULL terminator. */ +#define DEBUG_NAME_MAX_LEN 65 + struct virtio_gpu_object_params { unsigned long size; bool dumb; @@ -274,6 +277,8 @@ struct virtio_gpu_fpriv { uint64_t base_fence_ctx; uint64_t ring_idx_mask; struct mutex context_lock; + char debug_name[DEBUG_NAME_MAX_LEN]; + bool explicit_debug_name; }; /* virtgpu_ioctl.c */ diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index b24b11f25197..e4f76f315550 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -42,12 +42,19 @@ static void virtio_gpu_create_context_locked(struct virtio_gpu_device *vgdev, struct virtio_gpu_fpriv *vfpriv) { - char dbgname[TASK_COMM_LEN]; + if (vfpriv->explicit_debug_name) { + virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, + vfpriv->context_init, + strlen(vfpriv->debug_name), + vfpriv->debug_name); + } else { + char dbgname[TASK_COMM_LEN]; - get_task_comm(dbgname, current); - virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, - vfpriv->context_init, strlen(dbgname), - dbgname); + get_task_comm(dbgname, current); + virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, + vfpriv->context_init, strlen(dbgname), + dbgname); + } vfpriv->context_created = true; } @@ -107,6 +114,9 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, case VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs: value = vgdev->capset_id_mask; break; + case VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME: + value = vgdev->has_context_init ? 1 : 0; + break; default: return -EINVAL; } @@ -565,8 +575,8 @@ static int virtio_gpu_context_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { int ret = 0; - uint32_t num_params, i, param, value; - uint64_t valid_ring_mask; + uint32_t num_params, i; + uint64_t valid_ring_mask, param, value; size_t len; struct drm_virtgpu_context_set_param *ctx_set_params = NULL; struct virtio_gpu_device *vgdev = dev->dev_private; @@ -580,7 +590,7 @@ static int virtio_gpu_context_init_ioctl(struct drm_device *dev, return -EINVAL; /* Number of unique parameters supported at this time. */ - if (num_params > 3) + if (num_params > 4) return -EINVAL; ctx_set_params = memdup_user(u64_to_user_ptr(args->ctx_set_params), @@ -642,6 +652,21 @@ static int virtio_gpu_context_init_ioctl(struct drm_device *dev, vfpriv->ring_idx_mask = value; break; + case VIRTGPU_CONTEXT_PARAM_DEBUG_NAME: + if (vfpriv->explicit_debug_name) { + ret = -EINVAL; + goto out_unlock; + } + + ret = strncpy_from_user(vfpriv->debug_name, + u64_to_user_ptr(value), + DEBUG_NAME_MAX_LEN - 1); + if (ret < 0) + goto out_unlock; + + vfpriv->explicit_debug_name = true; + ret = 0; + break; default: ret = -EINVAL; goto out_unlock; diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 3ca45975c686..d9e9829b2200 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -345,6 +345,8 @@ static const struct apple_non_apple_keyboard non_apple_keyboards[] = { { "AONE" }, { "GANSS" }, { "Hailuck" }, + { "Jamesdonkey" }, + { "A3R" }, }; static bool apple_is_non_apple_keyboard(struct hid_device *hdev) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index fd61dba88233..78cdfb8b9a7a 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -381,7 +381,7 @@ static int asus_raw_event(struct hid_device *hdev, return 0; } -static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size) +static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t buf_size) { unsigned char *dmabuf; int ret; @@ -404,7 +404,7 @@ static int asus_kbd_set_report(struct hid_device *hdev, u8 *buf, size_t buf_size static int asus_kbd_init(struct hid_device *hdev) { - u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, + const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; int ret; @@ -418,7 +418,7 @@ static int asus_kbd_init(struct hid_device *hdev) static int asus_kbd_get_functions(struct hid_device *hdev, unsigned char *kbd_func) { - u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }; + const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }; u8 *readbuf; int ret; @@ -449,7 +449,7 @@ static int asus_kbd_get_functions(struct hid_device *hdev, static int rog_nkey_led_init(struct hid_device *hdev) { - u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 }; + const u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 }; u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1, @@ -1000,6 +1000,24 @@ static int asus_start_multitouch(struct hid_device *hdev) return 0; } +static int __maybe_unused asus_resume(struct hid_device *hdev) { + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + int ret = 0; + + if (drvdata->kbd_backlight) { + const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, + drvdata->kbd_backlight->cdev.brightness }; + ret = asus_kbd_set_report(hdev, buf, sizeof(buf)); + if (ret < 0) { + hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret); + goto asus_resume_err; + } + } + +asus_resume_err: + return ret; +} + static int __maybe_unused asus_reset_resume(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); @@ -1294,6 +1312,7 @@ static struct hid_driver asus_driver = { .input_configured = asus_input_configured, #ifdef CONFIG_PM .reset_resume = asus_reset_resume, + .resume = asus_resume, #endif .event = asus_event, .raw_event = asus_raw_event diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8992e3c1e769..e0181218ad85 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -702,15 +702,22 @@ static void hid_close_report(struct hid_device *device) * Free a device structure, all reports, and all fields. */ -static void hid_device_release(struct device *dev) +void hiddev_free(struct kref *ref) { - struct hid_device *hid = to_hid_device(dev); + struct hid_device *hid = container_of(ref, struct hid_device, ref); hid_close_report(hid); kfree(hid->dev_rdesc); kfree(hid); } +static void hid_device_release(struct device *dev) +{ + struct hid_device *hid = to_hid_device(dev); + + kref_put(&hid->ref, hiddev_free); +} + /* * Fetch a report description item from the data stream. We support long * items, though they are not used yet. @@ -2846,6 +2853,7 @@ struct hid_device *hid_allocate_device(void) spin_lock_init(&hdev->debug_list_lock); sema_init(&hdev->driver_input_lock, 1); mutex_init(&hdev->ll_open_lock); + kref_init(&hdev->ref); hid_bpf_device_init(hdev); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index e7ef1ea107c9..7dd83ec74f8a 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -1135,6 +1135,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) goto out; } list->hdev = (struct hid_device *) inode->i_private; + kref_get(&list->hdev->ref); file->private_data = list; mutex_init(&list->read_mutex); @@ -1227,6 +1228,8 @@ static int hid_debug_events_release(struct inode *inode, struct file *file) list_del(&list->node); spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); kfifo_free(&list->hid_debug_fifo); + + kref_put(&list->hdev->ref, hiddev_free); kfree(list); return 0; diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c index 558eb08c19ef..281b3a7187ce 100644 --- a/drivers/hid/hid-glorious.c +++ b/drivers/hid/hid-glorious.c @@ -21,6 +21,10 @@ MODULE_DESCRIPTION("HID driver for Glorious PC Gaming Race mice"); * Glorious Model O and O- specify the const flag in the consumer input * report descriptor, which leads to inputs being ignored. Fix this * by patching the descriptor. + * + * Glorious Model I incorrectly specifes the Usage Minimum for its + * keyboard HID report, causing keycodes to be misinterpreted. + * Fix this by setting Usage Minimum to 0 in that report. */ static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) @@ -32,6 +36,10 @@ static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[85] = rdesc[113] = rdesc[141] = \ HID_MAIN_ITEM_VARIABLE | HID_MAIN_ITEM_RELATIVE; } + if (*rsize == 156 && rdesc[41] == 1) { + hid_info(hdev, "patching Glorious Model I keyboard report descriptor\n"); + rdesc[41] = 0; + } return rdesc; } @@ -44,6 +52,8 @@ static void glorious_update_name(struct hid_device *hdev) model = "Model O"; break; case USB_DEVICE_ID_GLORIOUS_MODEL_D: model = "Model D"; break; + case USB_DEVICE_ID_GLORIOUS_MODEL_I: + model = "Model I"; break; } snprintf(hdev->name, sizeof(hdev->name), "%s %s", "Glorious", model); @@ -66,10 +76,12 @@ static int glorious_probe(struct hid_device *hdev, } static const struct hid_device_id glorious_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_GLORIOUS, + { HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH, USB_DEVICE_ID_GLORIOUS_MODEL_O) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GLORIOUS, + { HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH, USB_DEVICE_ID_GLORIOUS_MODEL_D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LAVIEW, + USB_DEVICE_ID_GLORIOUS_MODEL_I) }, { } }; MODULE_DEVICE_TABLE(hid, glorious_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f7973ccd84a2..c6e4e0d1f214 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -511,10 +511,6 @@ #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 -#define USB_VENDOR_ID_GLORIOUS 0x258a -#define USB_DEVICE_ID_GLORIOUS_MODEL_D 0x0033 -#define USB_DEVICE_ID_GLORIOUS_MODEL_O 0x0036 - #define I2C_VENDOR_ID_GOODIX 0x27c6 #define I2C_DEVICE_ID_GOODIX_01F0 0x01f0 @@ -745,6 +741,9 @@ #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 +#define USB_VENDOR_ID_LAVIEW 0x22D4 +#define USB_DEVICE_ID_GLORIOUS_MODEL_I 0x1503 + #define USB_VENDOR_ID_LCPOWER 0x1241 #define USB_DEVICE_ID_LCPOWER_LC1000 0xf767 @@ -869,7 +868,6 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f -#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2 0xc547 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 @@ -1160,6 +1158,10 @@ #define USB_VENDOR_ID_SIGMATEL 0x066F #define USB_DEVICE_ID_SIGMATEL_STMP3780 0x3780 +#define USB_VENDOR_ID_SINOWEALTH 0x258a +#define USB_DEVICE_ID_GLORIOUS_MODEL_D 0x0033 +#define USB_DEVICE_ID_GLORIOUS_MODEL_O 0x0036 + #define USB_VENDOR_ID_SIS_TOUCH 0x0457 #define USB_DEVICE_ID_SIS9200_TOUCH 0x9200 #define USB_DEVICE_ID_SIS817_TOUCH 0x0817 diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 8afe3be683ba..e6a8b6d8eab7 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1695,12 +1695,11 @@ static int logi_dj_raw_event(struct hid_device *hdev, } /* * Mouse-only receivers send unnumbered mouse data. The 27 MHz - * receiver uses 6 byte packets, the nano receiver 8 bytes, - * the lightspeed receiver (Pro X Superlight) 13 bytes. + * receiver uses 6 byte packets, the nano receiver 8 bytes. */ if (djrcv_dev->unnumbered_application == HID_GD_MOUSE && - size <= 13){ - u8 mouse_report[14]; + size <= 8) { + u8 mouse_report[9]; /* Prepend report id */ mouse_report[0] = REPORT_TYPE_MOUSE; @@ -1984,10 +1983,6 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1), .driver_data = recvr_type_gaming_hidpp}, - { /* Logitech lightspeed receiver (0xc547) */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2), - .driver_data = recvr_type_gaming_hidpp}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index 72883e0ce757..aef0785c91cc 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -1142,6 +1142,8 @@ static int mcp2221_probe(struct hid_device *hdev, if (ret) return ret; + hid_device_io_start(hdev); + /* Set I2C bus clock diviser */ if (i2c_clk_freq > 400) i2c_clk_freq = 400; @@ -1157,12 +1159,12 @@ static int mcp2221_probe(struct hid_device *hdev, snprintf(mcp->adapter.name, sizeof(mcp->adapter.name), "MCP2221 usb-i2c bridge"); + i2c_set_adapdata(&mcp->adapter, mcp); ret = devm_i2c_add_adapter(&hdev->dev, &mcp->adapter); if (ret) { hid_err(hdev, "can't add usb-i2c adapter: %d\n", ret); return ret; } - i2c_set_adapdata(&mcp->adapter, mcp); #if IS_REACHABLE(CONFIG_GPIOLIB) /* Setup GPIO chip */ diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index e098cc7b3944..fd5b0637dad6 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2046,6 +2046,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, + /* HONOR GLO-GXXX panel */ + { .driver_data = MT_CLS_VTL, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + 0x347d, 0x7853) }, + /* Ilitek dual touch panel */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 5a48fcaa32f0..ea472923fab0 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -33,6 +33,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD), HID_QUIRK_BADPAD }, { HID_USB_DEVICE(USB_VENDOR_ID_AMI, USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_REVB_ANSI), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM), HID_QUIRK_NOGET }, diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index affcfb243f0f..35f762872b8a 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -63,7 +63,7 @@ static int dw_reg_read(void *context, unsigned int reg, unsigned int *val) { struct dw_i2c_dev *dev = context; - *val = readl_relaxed(dev->base + reg); + *val = readl(dev->base + reg); return 0; } @@ -72,7 +72,7 @@ static int dw_reg_write(void *context, unsigned int reg, unsigned int val) { struct dw_i2c_dev *dev = context; - writel_relaxed(val, dev->base + reg); + writel(val, dev->base + reg); return 0; } @@ -81,7 +81,7 @@ static int dw_reg_read_swab(void *context, unsigned int reg, unsigned int *val) { struct dw_i2c_dev *dev = context; - *val = swab32(readl_relaxed(dev->base + reg)); + *val = swab32(readl(dev->base + reg)); return 0; } @@ -90,7 +90,7 @@ static int dw_reg_write_swab(void *context, unsigned int reg, unsigned int val) { struct dw_i2c_dev *dev = context; - writel_relaxed(swab32(val), dev->base + reg); + writel(swab32(val), dev->base + reg); return 0; } @@ -99,8 +99,8 @@ static int dw_reg_read_word(void *context, unsigned int reg, unsigned int *val) { struct dw_i2c_dev *dev = context; - *val = readw_relaxed(dev->base + reg) | - (readw_relaxed(dev->base + reg + 2) << 16); + *val = readw(dev->base + reg) | + (readw(dev->base + reg + 2) << 16); return 0; } @@ -109,8 +109,8 @@ static int dw_reg_write_word(void *context, unsigned int reg, unsigned int val) { struct dw_i2c_dev *dev = context; - writew_relaxed(val, dev->base + reg); - writew_relaxed(val >> 16, dev->base + reg + 2); + writew(val, dev->base + reg); + writew(val >> 16, dev->base + reg + 2); return 0; } diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 041a76f71a49..e106af83cef4 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -771,8 +771,8 @@ static int ocores_i2c_resume(struct device *dev) return ocores_init(dev, i2c); } -static DEFINE_SIMPLE_DEV_PM_OPS(ocores_i2c_pm, - ocores_i2c_suspend, ocores_i2c_resume); +static DEFINE_NOIRQ_DEV_PM_OPS(ocores_i2c_pm, + ocores_i2c_suspend, ocores_i2c_resume); static struct platform_driver ocores_i2c_driver = { .probe = ocores_i2c_probe, diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 1d7648242749..76f79b68cef8 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -265,6 +265,9 @@ struct pxa_i2c { u32 hs_mask; struct i2c_bus_recovery_info recovery; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_default; + struct pinctrl_state *pinctrl_recovery; }; #define _IBMR(i2c) ((i2c)->reg_ibmr) @@ -1299,12 +1302,13 @@ static void i2c_pxa_prepare_recovery(struct i2c_adapter *adap) */ gpiod_set_value(i2c->recovery.scl_gpiod, ibmr & IBMR_SCLS); gpiod_set_value(i2c->recovery.sda_gpiod, ibmr & IBMR_SDAS); + + WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery)); } static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap) { struct pxa_i2c *i2c = adap->algo_data; - struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; u32 isr; /* @@ -1318,7 +1322,7 @@ static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap) i2c_pxa_do_reset(i2c); } - WARN_ON(pinctrl_select_state(bri->pinctrl, bri->pins_default)); + WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default)); dev_dbg(&i2c->adap.dev, "recovery: IBMR 0x%08x ISR 0x%08x\n", readl(_IBMR(i2c)), readl(_ISR(i2c))); @@ -1340,20 +1344,76 @@ static int i2c_pxa_init_recovery(struct pxa_i2c *i2c) if (IS_ENABLED(CONFIG_I2C_PXA_SLAVE)) return 0; - bri->pinctrl = devm_pinctrl_get(dev); - if (PTR_ERR(bri->pinctrl) == -ENODEV) { - bri->pinctrl = NULL; + i2c->pinctrl = devm_pinctrl_get(dev); + if (PTR_ERR(i2c->pinctrl) == -ENODEV) + i2c->pinctrl = NULL; + if (IS_ERR(i2c->pinctrl)) + return PTR_ERR(i2c->pinctrl); + + if (!i2c->pinctrl) + return 0; + + i2c->pinctrl_default = pinctrl_lookup_state(i2c->pinctrl, + PINCTRL_STATE_DEFAULT); + i2c->pinctrl_recovery = pinctrl_lookup_state(i2c->pinctrl, "recovery"); + + if (IS_ERR(i2c->pinctrl_default) || IS_ERR(i2c->pinctrl_recovery)) { + dev_info(dev, "missing pinmux recovery information: %ld %ld\n", + PTR_ERR(i2c->pinctrl_default), + PTR_ERR(i2c->pinctrl_recovery)); + return 0; + } + + /* + * Claiming GPIOs can influence the pinmux state, and may glitch the + * I2C bus. Do this carefully. + */ + bri->scl_gpiod = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); + if (bri->scl_gpiod == ERR_PTR(-EPROBE_DEFER)) + return -EPROBE_DEFER; + if (IS_ERR(bri->scl_gpiod)) { + dev_info(dev, "missing scl gpio recovery information: %pe\n", + bri->scl_gpiod); + return 0; + } + + /* + * We have SCL. Pull SCL low and wait a bit so that SDA glitches + * have no effect. + */ + gpiod_direction_output(bri->scl_gpiod, 0); + udelay(10); + bri->sda_gpiod = devm_gpiod_get(dev, "sda", GPIOD_OUT_HIGH_OPEN_DRAIN); + + /* Wait a bit in case of a SDA glitch, and then release SCL. */ + udelay(10); + gpiod_direction_output(bri->scl_gpiod, 1); + + if (bri->sda_gpiod == ERR_PTR(-EPROBE_DEFER)) + return -EPROBE_DEFER; + + if (IS_ERR(bri->sda_gpiod)) { + dev_info(dev, "missing sda gpio recovery information: %pe\n", + bri->sda_gpiod); return 0; } - if (IS_ERR(bri->pinctrl)) - return PTR_ERR(bri->pinctrl); bri->prepare_recovery = i2c_pxa_prepare_recovery; bri->unprepare_recovery = i2c_pxa_unprepare_recovery; + bri->recover_bus = i2c_generic_scl_recovery; i2c->adap.bus_recovery_info = bri; - return 0; + /* + * Claiming GPIOs can change the pinmux state, which confuses the + * pinctrl since pinctrl's idea of the current setting is unaffected + * by the pinmux change caused by claiming the GPIO. Work around that + * by switching pinctrl to the GPIO state here. We do it this way to + * avoid glitching the I2C bus. + */ + pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery); + + return pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default); } static int i2c_pxa_probe(struct platform_device *dev) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index a8c89df1a997..9a7a74239eab 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2379,12 +2379,12 @@ retry_baser: break; } + if (!shr) + gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order)); + its_write_baser(its, baser, val); tmp = baser->val; - if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) - tmp &= ~GITS_BASER_SHAREABILITY_MASK; - if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { /* * Shareability didn't stick. Just use @@ -2394,10 +2394,9 @@ retry_baser: * non-cacheable as well. */ shr = tmp & GITS_BASER_SHAREABILITY_MASK; - if (!shr) { + if (!shr) cache = GITS_BASER_nC; - gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order)); - } + goto retry_baser; } @@ -2609,6 +2608,11 @@ static int its_alloc_tables(struct its_node *its) /* erratum 24313: ignore memory access type */ cache = GITS_BASER_nCnB; + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) { + cache = GITS_BASER_nC; + shr = 0; + } + for (i = 0; i < GITS_BASER_NR_REGS; i++) { struct its_baser *baser = its->tables + i; u64 val = its_read_baser(its, baser); diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 05be59ae21b2..6ae2329052c9 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -265,6 +265,7 @@ struct bcache_device { #define BCACHE_DEV_WB_RUNNING 3 #define BCACHE_DEV_RATE_DW_RUNNING 4 int nr_stripes; +#define BCH_MIN_STRIPE_SZ ((4 << 20) >> SECTOR_SHIFT) unsigned int stripe_size; atomic_t *stripe_sectors_dirty; unsigned long *full_dirty_stripes; diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index ae5cbb55861f..de3019972b35 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -1000,6 +1000,9 @@ err: * * The btree node will have either a read or a write lock held, depending on * level and op->lock. + * + * Note: Only error code or btree pointer will be returned, it is unncessary + * for callers to check NULL pointer. */ struct btree *bch_btree_node_get(struct cache_set *c, struct btree_op *op, struct bkey *k, int level, bool write, @@ -1111,6 +1114,10 @@ retry: mutex_unlock(&b->c->bucket_lock); } +/* + * Only error code or btree pointer will be returned, it is unncessary for + * callers to check NULL pointer. + */ struct btree *__bch_btree_node_alloc(struct cache_set *c, struct btree_op *op, int level, bool wait, struct btree *parent) @@ -1368,7 +1375,7 @@ static int btree_gc_coalesce(struct btree *b, struct btree_op *op, memset(new_nodes, 0, sizeof(new_nodes)); closure_init_stack(&cl); - while (nodes < GC_MERGE_NODES && !IS_ERR(r[nodes].b)) + while (nodes < GC_MERGE_NODES && !IS_ERR_OR_NULL(r[nodes].b)) keys += r[nodes++].keys; blocks = btree_default_blocks(b->c) * 2 / 3; @@ -1532,6 +1539,8 @@ static int btree_gc_rewrite_node(struct btree *b, struct btree_op *op, return 0; n = btree_node_alloc_replacement(replace, NULL); + if (IS_ERR(n)) + return 0; /* recheck reserve after allocating replacement node */ if (btree_check_reserve(b, NULL)) { diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 8bd899766372..bfe1685dbae5 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -905,6 +905,8 @@ static int bcache_device_init(struct bcache_device *d, unsigned int block_size, if (!d->stripe_size) d->stripe_size = 1 << 31; + else if (d->stripe_size < BCH_MIN_STRIPE_SZ) + d->stripe_size = roundup(BCH_MIN_STRIPE_SZ, d->stripe_size); n = DIV_ROUND_UP_ULL(sectors, d->stripe_size); if (!n || n > max_stripes) { @@ -2016,7 +2018,7 @@ static int run_cache_set(struct cache_set *c) c->root = bch_btree_node_get(c, NULL, k, j->btree_level, true, NULL); - if (IS_ERR_OR_NULL(c->root)) + if (IS_ERR(c->root)) goto err; list_del_init(&c->root->list); diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 45d8af755de6..a438efb66069 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -1104,7 +1104,7 @@ SHOW(__bch_cache) sum += INITIAL_PRIO - cached[i]; if (n) - do_div(sum, n); + sum = div64_u64(sum, n); for (i = 0; i < ARRAY_SIZE(q); i++) q[i] = INITIAL_PRIO - cached[n * (i + 1) / diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 24c049067f61..3accfdaee6b1 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -913,7 +913,7 @@ static int bch_dirty_init_thread(void *arg) int cur_idx, prev_idx, skip_nr; k = p = NULL; - cur_idx = prev_idx = 0; + prev_idx = 0; bch_btree_iter_init(&c->root->keys, &iter, NULL); k = bch_btree_iter_next_filter(&iter, &c->root->keys, bch_ptr_bad); @@ -977,24 +977,35 @@ static int bch_btre_dirty_init_thread_nr(void) void bch_sectors_dirty_init(struct bcache_device *d) { int i; + struct btree *b = NULL; struct bkey *k = NULL; struct btree_iter iter; struct sectors_dirty_init op; struct cache_set *c = d->c; struct bch_dirty_init_state state; +retry_lock: + b = c->root; + rw_lock(0, b, b->level); + if (b != c->root) { + rw_unlock(0, b); + goto retry_lock; + } + /* Just count root keys if no leaf node */ - rw_lock(0, c->root, c->root->level); if (c->root->level == 0) { bch_btree_op_init(&op.op, -1); op.inode = d->id; op.count = 0; for_each_key_filter(&c->root->keys, - k, &iter, bch_ptr_invalid) + k, &iter, bch_ptr_invalid) { + if (KEY_INODE(k) != op.inode) + continue; sectors_dirty_init_fn(&op.op, c->root, k); + } - rw_unlock(0, c->root); + rw_unlock(0, b); return; } @@ -1014,23 +1025,24 @@ void bch_sectors_dirty_init(struct bcache_device *d) if (atomic_read(&state.enough)) break; + atomic_inc(&state.started); state.infos[i].state = &state; state.infos[i].thread = kthread_run(bch_dirty_init_thread, &state.infos[i], "bch_dirtcnt[%d]", i); if (IS_ERR(state.infos[i].thread)) { pr_err("fails to run thread bch_dirty_init[%d]\n", i); + atomic_dec(&state.started); for (--i; i >= 0; i--) kthread_stop(state.infos[i].thread); goto out; } - atomic_inc(&state.started); } out: /* Must wait for all threads to stop. */ wait_event(state.wait, atomic_read(&state.started) == 0); - rw_unlock(0, c->root); + rw_unlock(0, b); } void bch_cached_dev_writeback_init(struct cached_dev *dc) diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 62eb27639c9b..f03d7dba270c 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -254,7 +254,7 @@ enum evict_result { typedef enum evict_result (*le_predicate)(struct lru_entry *le, void *context); -static struct lru_entry *lru_evict(struct lru *lru, le_predicate pred, void *context) +static struct lru_entry *lru_evict(struct lru *lru, le_predicate pred, void *context, bool no_sleep) { unsigned long tested = 0; struct list_head *h = lru->cursor; @@ -295,7 +295,8 @@ static struct lru_entry *lru_evict(struct lru *lru, le_predicate pred, void *con h = h->next; - cond_resched(); + if (!no_sleep) + cond_resched(); } return NULL; @@ -382,7 +383,10 @@ struct dm_buffer { */ struct buffer_tree { - struct rw_semaphore lock; + union { + struct rw_semaphore lock; + rwlock_t spinlock; + } u; struct rb_root root; } ____cacheline_aligned_in_smp; @@ -393,9 +397,12 @@ struct dm_buffer_cache { * on the locks. */ unsigned int num_locks; + bool no_sleep; struct buffer_tree trees[]; }; +static DEFINE_STATIC_KEY_FALSE(no_sleep_enabled); + static inline unsigned int cache_index(sector_t block, unsigned int num_locks) { return dm_hash_locks_index(block, num_locks); @@ -403,22 +410,34 @@ static inline unsigned int cache_index(sector_t block, unsigned int num_locks) static inline void cache_read_lock(struct dm_buffer_cache *bc, sector_t block) { - down_read(&bc->trees[cache_index(block, bc->num_locks)].lock); + if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep) + read_lock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock); + else + down_read(&bc->trees[cache_index(block, bc->num_locks)].u.lock); } static inline void cache_read_unlock(struct dm_buffer_cache *bc, sector_t block) { - up_read(&bc->trees[cache_index(block, bc->num_locks)].lock); + if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep) + read_unlock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock); + else + up_read(&bc->trees[cache_index(block, bc->num_locks)].u.lock); } static inline void cache_write_lock(struct dm_buffer_cache *bc, sector_t block) { - down_write(&bc->trees[cache_index(block, bc->num_locks)].lock); + if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep) + write_lock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock); + else + down_write(&bc->trees[cache_index(block, bc->num_locks)].u.lock); } static inline void cache_write_unlock(struct dm_buffer_cache *bc, sector_t block) { - up_write(&bc->trees[cache_index(block, bc->num_locks)].lock); + if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep) + write_unlock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock); + else + up_write(&bc->trees[cache_index(block, bc->num_locks)].u.lock); } /* @@ -442,18 +461,32 @@ static void lh_init(struct lock_history *lh, struct dm_buffer_cache *cache, bool static void __lh_lock(struct lock_history *lh, unsigned int index) { - if (lh->write) - down_write(&lh->cache->trees[index].lock); - else - down_read(&lh->cache->trees[index].lock); + if (lh->write) { + if (static_branch_unlikely(&no_sleep_enabled) && lh->cache->no_sleep) + write_lock_bh(&lh->cache->trees[index].u.spinlock); + else + down_write(&lh->cache->trees[index].u.lock); + } else { + if (static_branch_unlikely(&no_sleep_enabled) && lh->cache->no_sleep) + read_lock_bh(&lh->cache->trees[index].u.spinlock); + else + down_read(&lh->cache->trees[index].u.lock); + } } static void __lh_unlock(struct lock_history *lh, unsigned int index) { - if (lh->write) - up_write(&lh->cache->trees[index].lock); - else - up_read(&lh->cache->trees[index].lock); + if (lh->write) { + if (static_branch_unlikely(&no_sleep_enabled) && lh->cache->no_sleep) + write_unlock_bh(&lh->cache->trees[index].u.spinlock); + else + up_write(&lh->cache->trees[index].u.lock); + } else { + if (static_branch_unlikely(&no_sleep_enabled) && lh->cache->no_sleep) + read_unlock_bh(&lh->cache->trees[index].u.spinlock); + else + up_read(&lh->cache->trees[index].u.lock); + } } /* @@ -502,14 +535,18 @@ static struct dm_buffer *list_to_buffer(struct list_head *l) return le_to_buffer(le); } -static void cache_init(struct dm_buffer_cache *bc, unsigned int num_locks) +static void cache_init(struct dm_buffer_cache *bc, unsigned int num_locks, bool no_sleep) { unsigned int i; bc->num_locks = num_locks; + bc->no_sleep = no_sleep; for (i = 0; i < bc->num_locks; i++) { - init_rwsem(&bc->trees[i].lock); + if (no_sleep) + rwlock_init(&bc->trees[i].u.spinlock); + else + init_rwsem(&bc->trees[i].u.lock); bc->trees[i].root = RB_ROOT; } @@ -648,7 +685,7 @@ static struct dm_buffer *__cache_evict(struct dm_buffer_cache *bc, int list_mode struct lru_entry *le; struct dm_buffer *b; - le = lru_evict(&bc->lru[list_mode], __evict_pred, &w); + le = lru_evict(&bc->lru[list_mode], __evict_pred, &w, bc->no_sleep); if (!le) return NULL; @@ -702,7 +739,7 @@ static void __cache_mark_many(struct dm_buffer_cache *bc, int old_mode, int new_ struct evict_wrapper w = {.lh = lh, .pred = pred, .context = context}; while (true) { - le = lru_evict(&bc->lru[old_mode], __evict_pred, &w); + le = lru_evict(&bc->lru[old_mode], __evict_pred, &w, bc->no_sleep); if (!le) break; @@ -915,10 +952,11 @@ static void cache_remove_range(struct dm_buffer_cache *bc, { unsigned int i; + BUG_ON(bc->no_sleep); for (i = 0; i < bc->num_locks; i++) { - down_write(&bc->trees[i].lock); + down_write(&bc->trees[i].u.lock); __remove_range(bc, &bc->trees[i].root, begin, end, pred, release); - up_write(&bc->trees[i].lock); + up_write(&bc->trees[i].u.lock); } } @@ -979,8 +1017,6 @@ struct dm_bufio_client { struct dm_buffer_cache cache; /* must be last member */ }; -static DEFINE_STATIC_KEY_FALSE(no_sleep_enabled); - /*----------------------------------------------------------------*/ #define dm_bufio_in_request() (!!current->bio_list) @@ -1871,7 +1907,8 @@ static void *new_read(struct dm_bufio_client *c, sector_t block, if (need_submit) submit_io(b, REQ_OP_READ, read_endio); - wait_on_bit_io(&b->state, B_READING, TASK_UNINTERRUPTIBLE); + if (nf != NF_GET) /* we already tested this condition above */ + wait_on_bit_io(&b->state, B_READING, TASK_UNINTERRUPTIBLE); if (b->read_error) { int error = blk_status_to_errno(b->read_error); @@ -2421,7 +2458,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign r = -ENOMEM; goto bad_client; } - cache_init(&c->cache, num_locks); + cache_init(&c->cache, num_locks, (flags & DM_BUFIO_CLIENT_NO_SLEEP) != 0); c->bdev = bdev; c->block_size = block_size; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 6de107aff331..2ae8560b6a14 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1673,7 +1673,7 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size) unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM; unsigned int remaining_size; - unsigned int order = MAX_ORDER - 1; + unsigned int order = MAX_ORDER; retry: if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM)) diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c index efd510984e25..5eabdb06c649 100644 --- a/drivers/md/dm-delay.c +++ b/drivers/md/dm-delay.c @@ -33,7 +33,7 @@ struct delay_c { struct work_struct flush_expired_bios; struct list_head delayed_bios; struct task_struct *worker; - atomic_t may_delay; + bool may_delay; struct delay_class read; struct delay_class write; @@ -73,39 +73,6 @@ static inline bool delay_is_fast(struct delay_c *dc) return !!dc->worker; } -static void flush_delayed_bios_fast(struct delay_c *dc, bool flush_all) -{ - struct dm_delay_info *delayed, *next; - - mutex_lock(&delayed_bios_lock); - list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) { - if (flush_all || time_after_eq(jiffies, delayed->expires)) { - struct bio *bio = dm_bio_from_per_bio_data(delayed, - sizeof(struct dm_delay_info)); - list_del(&delayed->list); - dm_submit_bio_remap(bio, NULL); - delayed->class->ops--; - } - } - mutex_unlock(&delayed_bios_lock); -} - -static int flush_worker_fn(void *data) -{ - struct delay_c *dc = data; - - while (1) { - flush_delayed_bios_fast(dc, false); - if (unlikely(list_empty(&dc->delayed_bios))) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } else - cond_resched(); - } - - return 0; -} - static void flush_bios(struct bio *bio) { struct bio *n; @@ -118,36 +85,61 @@ static void flush_bios(struct bio *bio) } } -static struct bio *flush_delayed_bios(struct delay_c *dc, bool flush_all) +static void flush_delayed_bios(struct delay_c *dc, bool flush_all) { struct dm_delay_info *delayed, *next; + struct bio_list flush_bio_list; unsigned long next_expires = 0; - unsigned long start_timer = 0; - struct bio_list flush_bios = { }; + bool start_timer = false; + bio_list_init(&flush_bio_list); mutex_lock(&delayed_bios_lock); list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) { + cond_resched(); if (flush_all || time_after_eq(jiffies, delayed->expires)) { struct bio *bio = dm_bio_from_per_bio_data(delayed, sizeof(struct dm_delay_info)); list_del(&delayed->list); - bio_list_add(&flush_bios, bio); + bio_list_add(&flush_bio_list, bio); delayed->class->ops--; continue; } - if (!start_timer) { - start_timer = 1; - next_expires = delayed->expires; - } else - next_expires = min(next_expires, delayed->expires); + if (!delay_is_fast(dc)) { + if (!start_timer) { + start_timer = true; + next_expires = delayed->expires; + } else { + next_expires = min(next_expires, delayed->expires); + } + } } mutex_unlock(&delayed_bios_lock); if (start_timer) queue_timeout(dc, next_expires); - return bio_list_get(&flush_bios); + flush_bios(bio_list_get(&flush_bio_list)); +} + +static int flush_worker_fn(void *data) +{ + struct delay_c *dc = data; + + while (!kthread_should_stop()) { + flush_delayed_bios(dc, false); + mutex_lock(&delayed_bios_lock); + if (unlikely(list_empty(&dc->delayed_bios))) { + set_current_state(TASK_INTERRUPTIBLE); + mutex_unlock(&delayed_bios_lock); + schedule(); + } else { + mutex_unlock(&delayed_bios_lock); + cond_resched(); + } + } + + return 0; } static void flush_expired_bios(struct work_struct *work) @@ -155,10 +147,7 @@ static void flush_expired_bios(struct work_struct *work) struct delay_c *dc; dc = container_of(work, struct delay_c, flush_expired_bios); - if (delay_is_fast(dc)) - flush_delayed_bios_fast(dc, false); - else - flush_bios(flush_delayed_bios(dc, false)); + flush_delayed_bios(dc, false); } static void delay_dtr(struct dm_target *ti) @@ -177,8 +166,7 @@ static void delay_dtr(struct dm_target *ti) if (dc->worker) kthread_stop(dc->worker); - if (!delay_is_fast(dc)) - mutex_destroy(&dc->timer_lock); + mutex_destroy(&dc->timer_lock); kfree(dc); } @@ -236,7 +224,8 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv) ti->private = dc; INIT_LIST_HEAD(&dc->delayed_bios); - atomic_set(&dc->may_delay, 1); + mutex_init(&dc->timer_lock); + dc->may_delay = true; dc->argc = argc; ret = delay_class_ctr(ti, &dc->read, argv); @@ -282,12 +271,12 @@ out: "dm-delay-flush-worker"); if (IS_ERR(dc->worker)) { ret = PTR_ERR(dc->worker); + dc->worker = NULL; goto bad; } } else { timer_setup(&dc->delay_timer, handle_delayed_timer, 0); INIT_WORK(&dc->flush_expired_bios, flush_expired_bios); - mutex_init(&dc->timer_lock); dc->kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0); if (!dc->kdelayd_wq) { ret = -EINVAL; @@ -312,7 +301,7 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio) struct dm_delay_info *delayed; unsigned long expires = 0; - if (!c->delay || !atomic_read(&dc->may_delay)) + if (!c->delay) return DM_MAPIO_REMAPPED; delayed = dm_per_bio_data(bio, sizeof(struct dm_delay_info)); @@ -321,6 +310,10 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio) delayed->expires = expires = jiffies + msecs_to_jiffies(c->delay); mutex_lock(&delayed_bios_lock); + if (unlikely(!dc->may_delay)) { + mutex_unlock(&delayed_bios_lock); + return DM_MAPIO_REMAPPED; + } c->ops++; list_add_tail(&delayed->list, &dc->delayed_bios); mutex_unlock(&delayed_bios_lock); @@ -337,21 +330,20 @@ static void delay_presuspend(struct dm_target *ti) { struct delay_c *dc = ti->private; - atomic_set(&dc->may_delay, 0); + mutex_lock(&delayed_bios_lock); + dc->may_delay = false; + mutex_unlock(&delayed_bios_lock); - if (delay_is_fast(dc)) - flush_delayed_bios_fast(dc, true); - else { + if (!delay_is_fast(dc)) del_timer_sync(&dc->delay_timer); - flush_bios(flush_delayed_bios(dc, true)); - } + flush_delayed_bios(dc, true); } static void delay_resume(struct dm_target *ti) { struct delay_c *dc = ti->private; - atomic_set(&dc->may_delay, 1); + dc->may_delay = true; } static int delay_map(struct dm_target *ti, struct bio *bio) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 3ef9f018da60..2099c755119e 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -185,7 +185,7 @@ static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io, { if (unlikely(verity_hash(v, verity_io_hash_req(v, io), data, 1 << v->data_dev_block_bits, - verity_io_real_digest(v, io)))) + verity_io_real_digest(v, io), true))) return 0; return memcmp(verity_io_real_digest(v, io), want_digest, @@ -386,7 +386,7 @@ static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io, /* Always re-validate the corrected block against the expected hash */ r = verity_hash(v, verity_io_hash_req(v, io), fio->output, 1 << v->data_dev_block_bits, - verity_io_real_digest(v, io)); + verity_io_real_digest(v, io), true); if (unlikely(r < 0)) return r; diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 26adcfea0302..e115fcfe723c 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -135,20 +135,21 @@ static int verity_hash_update(struct dm_verity *v, struct ahash_request *req, * Wrapper for crypto_ahash_init, which handles verity salting. */ static int verity_hash_init(struct dm_verity *v, struct ahash_request *req, - struct crypto_wait *wait) + struct crypto_wait *wait, bool may_sleep) { int r; ahash_request_set_tfm(req, v->tfm); - ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP | - CRYPTO_TFM_REQ_MAY_BACKLOG, - crypto_req_done, (void *)wait); + ahash_request_set_callback(req, + may_sleep ? CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG : 0, + crypto_req_done, (void *)wait); crypto_init_wait(wait); r = crypto_wait_req(crypto_ahash_init(req), wait); if (unlikely(r < 0)) { - DMERR("crypto_ahash_init failed: %d", r); + if (r != -ENOMEM) + DMERR("crypto_ahash_init failed: %d", r); return r; } @@ -179,12 +180,12 @@ out: } int verity_hash(struct dm_verity *v, struct ahash_request *req, - const u8 *data, size_t len, u8 *digest) + const u8 *data, size_t len, u8 *digest, bool may_sleep) { int r; struct crypto_wait wait; - r = verity_hash_init(v, req, &wait); + r = verity_hash_init(v, req, &wait, may_sleep); if (unlikely(r < 0)) goto out; @@ -322,7 +323,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io, r = verity_hash(v, verity_io_hash_req(v, io), data, 1 << v->hash_dev_block_bits, - verity_io_real_digest(v, io)); + verity_io_real_digest(v, io), !io->in_tasklet); if (unlikely(r < 0)) goto release_ret_r; @@ -556,7 +557,7 @@ static int verity_verify_io(struct dm_verity_io *io) continue; } - r = verity_hash_init(v, req, &wait); + r = verity_hash_init(v, req, &wait, !io->in_tasklet); if (unlikely(r < 0)) return r; @@ -652,7 +653,7 @@ static void verity_tasklet(unsigned long data) io->in_tasklet = true; err = verity_verify_io(io); - if (err == -EAGAIN) { + if (err == -EAGAIN || err == -ENOMEM) { /* fallback to retrying with work-queue */ INIT_WORK(&io->work, verity_work); queue_work(io->v->verify_wq, &io->work); @@ -1033,7 +1034,7 @@ static int verity_alloc_zero_digest(struct dm_verity *v) goto out; r = verity_hash(v, req, zero_data, 1 << v->data_dev_block_bits, - v->zero_digest); + v->zero_digest, true); out: kfree(req); diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 2f555b420367..f96f4e281ee4 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -128,7 +128,7 @@ extern int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io, u8 *data, size_t len)); extern int verity_hash(struct dm_verity *v, struct ahash_request *req, - const u8 *data, size_t len, u8 *digest); + const u8 *data, size_t len, u8 *digest, bool may_sleep); extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, sector_t block, u8 *digest, bool *is_zero); diff --git a/drivers/md/md.c b/drivers/md/md.c index 4ee4593c874a..c94373d64f2c 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8666,7 +8666,8 @@ static void md_end_clone_io(struct bio *bio) struct bio *orig_bio = md_io_clone->orig_bio; struct mddev *mddev = md_io_clone->mddev; - orig_bio->bi_status = bio->bi_status; + if (bio->bi_status && !orig_bio->bi_status) + orig_bio->bi_status = bio->bi_status; if (md_io_clone->start_time) bio_end_io_acct(orig_bio, md_io_clone->start_time); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 51d47eda1c87..8e6cc0e133b7 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1500,6 +1500,10 @@ done: static void bond_setup_by_slave(struct net_device *bond_dev, struct net_device *slave_dev) { + bool was_up = !!(bond_dev->flags & IFF_UP); + + dev_close(bond_dev); + bond_dev->header_ops = slave_dev->header_ops; bond_dev->type = slave_dev->type; @@ -1514,6 +1518,8 @@ static void bond_setup_by_slave(struct net_device *bond_dev, bond_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); bond_dev->flags |= (IFF_POINTOPOINT | IFF_NOARP); } + if (was_up) + dev_open(bond_dev, NULL); } /* On bonding slaves other than the currently active slave, suppress diff --git a/drivers/net/ethernet/amd/pds_core/adminq.c b/drivers/net/ethernet/amd/pds_core/adminq.c index 045fe133f6ee..5beadabc2136 100644 --- a/drivers/net/ethernet/amd/pds_core/adminq.c +++ b/drivers/net/ethernet/amd/pds_core/adminq.c @@ -146,7 +146,7 @@ irqreturn_t pdsc_adminq_isr(int irq, void *data) } queue_work(pdsc->wq, &qcq->work); - pds_core_intr_mask(&pdsc->intr_ctrl[irq], PDS_CORE_INTR_MASK_CLEAR); + pds_core_intr_mask(&pdsc->intr_ctrl[qcq->intx], PDS_CORE_INTR_MASK_CLEAR); return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index f3a7deda9972..e35d3e7006bf 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -15,7 +15,7 @@ #define PDSC_DRV_DESCRIPTION "AMD/Pensando Core Driver" #define PDSC_WATCHDOG_SECS 5 -#define PDSC_QUEUE_NAME_MAX_SZ 32 +#define PDSC_QUEUE_NAME_MAX_SZ 16 #define PDSC_ADMINQ_MIN_LENGTH 16 /* must be a power of two */ #define PDSC_NOTIFYQ_LENGTH 64 /* must be a power of two */ #define PDSC_TEARDOWN_RECOVERY false diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c index 7c1b965d61a9..31940b857e0e 100644 --- a/drivers/net/ethernet/amd/pds_core/dev.c +++ b/drivers/net/ethernet/amd/pds_core/dev.c @@ -261,10 +261,14 @@ static int pdsc_identify(struct pdsc *pdsc) struct pds_core_drv_identity drv = {}; size_t sz; int err; + int n; drv.drv_type = cpu_to_le32(PDS_DRIVER_LINUX); - snprintf(drv.driver_ver_str, sizeof(drv.driver_ver_str), - "%s %s", PDS_CORE_DRV_NAME, utsname()->release); + /* Catching the return quiets a Wformat-truncation complaint */ + n = snprintf(drv.driver_ver_str, sizeof(drv.driver_ver_str), + "%s %s", PDS_CORE_DRV_NAME, utsname()->release); + if (n > sizeof(drv.driver_ver_str)) + dev_dbg(pdsc->dev, "release name truncated, don't care\n"); /* Next let's get some info about the device * We use the devcmd_lock at this level in order to diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index 57f88c8b37de..e9948ea5bbcd 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -104,7 +104,7 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, struct pds_core_fw_list_info fw_list; struct pdsc *pdsc = devlink_priv(dl); union pds_core_dev_comp comp; - char buf[16]; + char buf[32]; int listlen; int err; int i; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 614c0278419b..6b73648b3779 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -682,10 +682,24 @@ static void xgbe_service(struct work_struct *work) static void xgbe_service_timer(struct timer_list *t) { struct xgbe_prv_data *pdata = from_timer(pdata, t, service_timer); + struct xgbe_channel *channel; + unsigned int i; queue_work(pdata->dev_workqueue, &pdata->service_work); mod_timer(&pdata->service_timer, jiffies + HZ); + + if (!pdata->tx_usecs) + return; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel[i]; + if (!channel->tx_ring || channel->tx_timer_active) + break; + channel->tx_timer_active = 1; + mod_timer(&channel->tx_timer, + jiffies + usecs_to_jiffies(pdata->tx_usecs)); + } } static void xgbe_init_timers(struct xgbe_prv_data *pdata) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 6e83ff59172a..32fab5e77246 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -314,10 +314,15 @@ static int xgbe_get_link_ksettings(struct net_device *netdev, cmd->base.phy_address = pdata->phy.address; - cmd->base.autoneg = pdata->phy.autoneg; - cmd->base.speed = pdata->phy.speed; - cmd->base.duplex = pdata->phy.duplex; + if (netif_carrier_ok(netdev)) { + cmd->base.speed = pdata->phy.speed; + cmd->base.duplex = pdata->phy.duplex; + } else { + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + cmd->base.autoneg = pdata->phy.autoneg; cmd->base.port = PORT_NONE; XGBE_LM_COPY(cmd, supported, lks, supported); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 32d2c6fac652..4a2dc705b528 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1193,7 +1193,19 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata) if (pdata->phy.duplex != DUPLEX_FULL) return -EINVAL; - xgbe_set_mode(pdata, mode); + /* Force the mode change for SFI in Fixed PHY config. + * Fixed PHY configs needs PLL to be enabled while doing mode set. + * When the SFP module isn't connected during boot, driver assumes + * AN is ON and attempts autonegotiation. However, if the connected + * SFP comes up in Fixed PHY config, the link will not come up as + * PLL isn't enabled while the initial mode set command is issued. + * So, force the mode change for SFI in Fixed PHY configuration to + * fix link issues. + */ + if (mode == XGBE_MODE_SFI) + xgbe_change_mode(pdata, mode); + else + xgbe_set_mode(pdata, mode); return 0; } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 1dee27349367..48b6191efa56 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6889,7 +6889,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) desc_idx, *post_ptr); drop_it_no_recycle: /* Other statistics kept track of by card. */ - tp->rx_dropped++; + tnapi->rx_dropped++; goto next_pkt; } @@ -7918,8 +7918,10 @@ static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi, segs = skb_gso_segment(skb, tp->dev->features & ~(NETIF_F_TSO | NETIF_F_TSO6)); - if (IS_ERR(segs) || !segs) + if (IS_ERR(segs) || !segs) { + tnapi->tx_dropped++; goto tg3_tso_bug_end; + } skb_list_walk_safe(segs, seg, next) { skb_mark_not_on_list(seg); @@ -8190,7 +8192,7 @@ dma_error: drop: dev_kfree_skb_any(skb); drop_nofree: - tp->tx_dropped++; + tnapi->tx_dropped++; return NETDEV_TX_OK; } @@ -9405,7 +9407,7 @@ static void __tg3_set_rx_mode(struct net_device *); /* tp->lock is held. */ static int tg3_halt(struct tg3 *tp, int kind, bool silent) { - int err; + int err, i; tg3_stop_fw(tp); @@ -9426,6 +9428,13 @@ static int tg3_halt(struct tg3 *tp, int kind, bool silent) /* And make sure the next sample is new data */ memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); + + for (i = 0; i < TG3_IRQ_MAX_VECS; ++i) { + struct tg3_napi *tnapi = &tp->napi[i]; + + tnapi->rx_dropped = 0; + tnapi->tx_dropped = 0; + } } return err; @@ -11975,6 +11984,9 @@ static void tg3_get_nstats(struct tg3 *tp, struct rtnl_link_stats64 *stats) { struct rtnl_link_stats64 *old_stats = &tp->net_stats_prev; struct tg3_hw_stats *hw_stats = tp->hw_stats; + unsigned long rx_dropped; + unsigned long tx_dropped; + int i; stats->rx_packets = old_stats->rx_packets + get_stat64(&hw_stats->rx_ucast_packets) + @@ -12021,8 +12033,26 @@ static void tg3_get_nstats(struct tg3 *tp, struct rtnl_link_stats64 *stats) stats->rx_missed_errors = old_stats->rx_missed_errors + get_stat64(&hw_stats->rx_discards); - stats->rx_dropped = tp->rx_dropped; - stats->tx_dropped = tp->tx_dropped; + /* Aggregate per-queue counters. The per-queue counters are updated + * by a single writer, race-free. The result computed by this loop + * might not be 100% accurate (counters can be updated in the middle of + * the loop) but the next tg3_get_nstats() will recompute the current + * value so it is acceptable. + * + * Note that these counters wrap around at 4G on 32bit machines. + */ + rx_dropped = (unsigned long)(old_stats->rx_dropped); + tx_dropped = (unsigned long)(old_stats->tx_dropped); + + for (i = 0; i < tp->irq_cnt; i++) { + struct tg3_napi *tnapi = &tp->napi[i]; + + rx_dropped += tnapi->rx_dropped; + tx_dropped += tnapi->tx_dropped; + } + + stats->rx_dropped = rx_dropped; + stats->tx_dropped = tx_dropped; } static int tg3_get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index ae5c01bd1110..5016475e5005 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -3018,6 +3018,7 @@ struct tg3_napi { u16 *rx_rcb_prod_idx; struct tg3_rx_prodring_set prodring; struct tg3_rx_buffer_desc *rx_rcb; + unsigned long rx_dropped; u32 tx_prod ____cacheline_aligned; u32 tx_cons; @@ -3026,6 +3027,7 @@ struct tg3_napi { u32 prodmbox; struct tg3_tx_buffer_desc *tx_ring; struct tg3_tx_ring_info *tx_buffers; + unsigned long tx_dropped; dma_addr_t status_mapping; dma_addr_t rx_rcb_mapping; @@ -3220,8 +3222,6 @@ struct tg3 { /* begin "everything else" cacheline(s) section */ - unsigned long rx_dropped; - unsigned long tx_dropped; struct rtnl_link_stats64 net_stats_prev; struct tg3_ethtool_stats estats_prev; diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 5423fe26b4ef..78287cfcbf63 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -432,8 +432,8 @@ static const struct gmac_max_framelen gmac_maxlens[] = { .val = CONFIG0_MAXLEN_1536, }, { - .max_l3_len = 1542, - .val = CONFIG0_MAXLEN_1542, + .max_l3_len = 1548, + .val = CONFIG0_MAXLEN_1548, }, { .max_l3_len = 9212, @@ -1145,6 +1145,7 @@ static int gmac_map_tx_bufs(struct net_device *netdev, struct sk_buff *skb, dma_addr_t mapping; unsigned short mtu; void *buffer; + int ret; mtu = ETH_HLEN; mtu += netdev->mtu; @@ -1159,9 +1160,30 @@ static int gmac_map_tx_bufs(struct net_device *netdev, struct sk_buff *skb, word3 |= mtu; } - if (skb->ip_summed != CHECKSUM_NONE) { + if (skb->len >= ETH_FRAME_LEN) { + /* Hardware offloaded checksumming isn't working on frames + * bigger than 1514 bytes. A hypothesis about this is that the + * checksum buffer is only 1518 bytes, so when the frames get + * bigger they get truncated, or the last few bytes get + * overwritten by the FCS. + * + * Just use software checksumming and bypass on bigger frames. + */ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + ret = skb_checksum_help(skb); + if (ret) + return ret; + } + word1 |= TSS_BYPASS_BIT; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { int tcp = 0; + /* We do not switch off the checksumming on non TCP/UDP + * frames: as is shown from tests, the checksumming engine + * is smart enough to see that a frame is not actually TCP + * or UDP and then just pass it through without any changes + * to the frame. + */ if (skb->protocol == htons(ETH_P_IP)) { word1 |= TSS_IP_CHKSUM_BIT; tcp = ip_hdr(skb)->protocol == IPPROTO_TCP; @@ -1978,15 +2000,6 @@ static int gmac_change_mtu(struct net_device *netdev, int new_mtu) return 0; } -static netdev_features_t gmac_fix_features(struct net_device *netdev, - netdev_features_t features) -{ - if (netdev->mtu + ETH_HLEN + VLAN_HLEN > MTU_SIZE_BIT_MASK) - features &= ~GMAC_OFFLOAD_FEATURES; - - return features; -} - static int gmac_set_features(struct net_device *netdev, netdev_features_t features) { @@ -2212,7 +2225,6 @@ static const struct net_device_ops gmac_351x_ops = { .ndo_set_mac_address = gmac_set_mac_address, .ndo_get_stats64 = gmac_get_stats64, .ndo_change_mtu = gmac_change_mtu, - .ndo_fix_features = gmac_fix_features, .ndo_set_features = gmac_set_features, }; @@ -2464,11 +2476,12 @@ static int gemini_ethernet_port_probe(struct platform_device *pdev) netdev->hw_features = GMAC_OFFLOAD_FEATURES; netdev->features |= GMAC_OFFLOAD_FEATURES | NETIF_F_GRO; - /* We can handle jumbo frames up to 10236 bytes so, let's accept - * payloads of 10236 bytes minus VLAN and ethernet header + /* We can receive jumbo frames up to 10236 bytes but only + * transmit 2047 bytes so, let's accept payloads of 2047 + * bytes minus VLAN and ethernet header */ netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = 10236 - VLAN_ETH_HLEN; + netdev->max_mtu = MTU_SIZE_BIT_MASK - VLAN_ETH_HLEN; port->freeq_refill = 0; netif_napi_add(netdev, &port->napi, gmac_napi_poll); diff --git a/drivers/net/ethernet/cortina/gemini.h b/drivers/net/ethernet/cortina/gemini.h index 9fdf77d5eb37..24bb989981f2 100644 --- a/drivers/net/ethernet/cortina/gemini.h +++ b/drivers/net/ethernet/cortina/gemini.h @@ -502,7 +502,7 @@ union gmac_txdesc_3 { #define SOF_BIT 0x80000000 #define EOF_BIT 0x40000000 #define EOFIE_BIT BIT(29) -#define MTU_SIZE_BIT_MASK 0x1fff +#define MTU_SIZE_BIT_MASK 0x7ff /* Max MTU 2047 bytes */ /* GMAC Tx Descriptor */ struct gmac_txdesc { @@ -787,7 +787,7 @@ union gmac_config0 { #define CONFIG0_MAXLEN_1536 0 #define CONFIG0_MAXLEN_1518 1 #define CONFIG0_MAXLEN_1522 2 -#define CONFIG0_MAXLEN_1542 3 +#define CONFIG0_MAXLEN_1548 3 #define CONFIG0_MAXLEN_9k 4 /* 9212 */ #define CONFIG0_MAXLEN_10k 5 /* 10236 */ #define CONFIG0_MAXLEN_1518__6 6 diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 276f996f95dc..2d42e733837b 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -254,10 +254,13 @@ static int gve_napi_poll(struct napi_struct *napi, int budget) if (block->tx) { if (block->tx->q_num < priv->tx_cfg.num_queues) reschedule |= gve_tx_poll(block, budget); - else + else if (budget) reschedule |= gve_xdp_poll(block, budget); } + if (!budget) + return 0; + if (block->rx) { work_done = gve_rx_poll(block, budget); reschedule |= work_done == budget; @@ -298,6 +301,9 @@ static int gve_napi_poll_dqo(struct napi_struct *napi, int budget) if (block->tx) reschedule |= gve_tx_poll_dqo(block, /*do_clean=*/true); + if (!budget) + return 0; + if (block->rx) { work_done = gve_rx_poll_dqo(block, budget); reschedule |= work_done == budget; diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index e84a066aa1a4..73655347902d 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -1007,10 +1007,6 @@ int gve_rx_poll(struct gve_notify_block *block, int budget) feat = block->napi.dev->features; - /* If budget is 0, do all the work */ - if (budget == 0) - budget = INT_MAX; - if (budget > 0) work_done = gve_clean_rx_done(rx, budget, feat); diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c index 6957a865cff3..9f6ffc4a54f0 100644 --- a/drivers/net/ethernet/google/gve/gve_tx.c +++ b/drivers/net/ethernet/google/gve/gve_tx.c @@ -925,10 +925,6 @@ bool gve_xdp_poll(struct gve_notify_block *block, int budget) bool repoll; u32 to_do; - /* If budget is 0, do all the work */ - if (budget == 0) - budget = INT_MAX; - /* Find out how much work there is to be done */ nic_done = gve_tx_load_event_counter(priv, tx); to_do = min_t(u32, (nic_done - tx->done), budget); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 0b138635bafa..c083d1d10767 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -503,11 +503,14 @@ static void hns3_get_coal_info(struct hns3_enet_tqp_vector *tqp_vector, } sprintf(result[j++], "%d", i); - sprintf(result[j++], "%s", dim_state_str[dim->state]); + sprintf(result[j++], "%s", dim->state < ARRAY_SIZE(dim_state_str) ? + dim_state_str[dim->state] : "unknown"); sprintf(result[j++], "%u", dim->profile_ix); - sprintf(result[j++], "%s", dim_cqe_mode_str[dim->mode]); + sprintf(result[j++], "%s", dim->mode < ARRAY_SIZE(dim_cqe_mode_str) ? + dim_cqe_mode_str[dim->mode] : "unknown"); sprintf(result[j++], "%s", - dim_tune_stat_str[dim->tune_state]); + dim->tune_state < ARRAY_SIZE(dim_tune_stat_str) ? + dim_tune_stat_str[dim->tune_state] : "unknown"); sprintf(result[j++], "%u", dim->steps_left); sprintf(result[j++], "%u", dim->steps_right); sprintf(result[j++], "%u", dim->tired); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 06117502001f..b618797a7e8d 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -5139,7 +5139,7 @@ static int hns3_init_mac_addr(struct net_device *netdev) struct hns3_nic_priv *priv = netdev_priv(netdev); char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN]; struct hnae3_handle *h = priv->ae_handle; - u8 mac_addr_temp[ETH_ALEN]; + u8 mac_addr_temp[ETH_ALEN] = {0}; int ret = 0; if (h->ae_algo->ops->get_mac_addr) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 66e5807903a0..5ea9e59569ef 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -61,6 +61,7 @@ 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 int hclge_update_port_info(struct hclge_dev *hdev); static struct hnae3_ae_algo ae_algo; @@ -3041,6 +3042,9 @@ static void hclge_update_link_status(struct hclge_dev *hdev) if (state != hdev->hw.mac.link) { hdev->hw.mac.link = state; + if (state == HCLGE_LINK_STATUS_UP) + hclge_update_port_info(hdev); + client->ops->link_status_change(handle, state); hclge_config_mac_tnl_int(hdev, state); if (rclient && rclient->ops->link_status_change) @@ -10025,8 +10029,6 @@ static void hclge_rm_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id, struct hclge_vport_vlan_cfg *vlan, *tmp; struct hclge_dev *hdev = vport->back; - mutex_lock(&hdev->vport_lock); - list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) { if (vlan->vlan_id == vlan_id) { if (is_write_tbl && vlan->hd_tbl_status) @@ -10041,8 +10043,6 @@ static void hclge_rm_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id, break; } } - - mutex_unlock(&hdev->vport_lock); } void hclge_rm_vport_all_vlan_table(struct hclge_vport *vport, bool is_del_list) @@ -10451,11 +10451,16 @@ int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto, * handle mailbox. Just record the vlan id, and remove it after * reset finished. */ + mutex_lock(&hdev->vport_lock); if ((test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) || test_bit(HCLGE_STATE_RST_FAIL, &hdev->state)) && is_kill) { set_bit(vlan_id, vport->vlan_del_fail_bmap); + mutex_unlock(&hdev->vport_lock); return -EBUSY; + } else if (!is_kill && test_bit(vlan_id, vport->vlan_del_fail_bmap)) { + clear_bit(vlan_id, vport->vlan_del_fail_bmap); } + mutex_unlock(&hdev->vport_lock); /* when port base vlan enabled, we use port base vlan as the vlan * filter entry. In this case, we don't update vlan filter table @@ -10470,17 +10475,22 @@ int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto, } if (!ret) { - if (!is_kill) + if (!is_kill) { hclge_add_vport_vlan_table(vport, vlan_id, writen_to_tbl); - else if (is_kill && vlan_id != 0) + } else if (is_kill && vlan_id != 0) { + mutex_lock(&hdev->vport_lock); hclge_rm_vport_vlan_table(vport, vlan_id, false); + mutex_unlock(&hdev->vport_lock); + } } else if (is_kill) { /* when remove hw vlan filter failed, record the vlan id, * and try to remove it from hw later, to be consistence * with stack */ + mutex_lock(&hdev->vport_lock); set_bit(vlan_id, vport->vlan_del_fail_bmap); + mutex_unlock(&hdev->vport_lock); } hclge_set_vport_vlan_fltr_change(vport); @@ -10520,6 +10530,7 @@ static void hclge_sync_vlan_filter(struct hclge_dev *hdev) int i, ret, sync_cnt = 0; u16 vlan_id; + mutex_lock(&hdev->vport_lock); /* start from vport 1 for PF is always alive */ for (i = 0; i < hdev->num_alloc_vport; i++) { struct hclge_vport *vport = &hdev->vport[i]; @@ -10530,21 +10541,26 @@ static void hclge_sync_vlan_filter(struct hclge_dev *hdev) ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q), vport->vport_id, vlan_id, true); - if (ret && ret != -EINVAL) + if (ret && ret != -EINVAL) { + mutex_unlock(&hdev->vport_lock); return; + } clear_bit(vlan_id, vport->vlan_del_fail_bmap); hclge_rm_vport_vlan_table(vport, vlan_id, false); hclge_set_vport_vlan_fltr_change(vport); sync_cnt++; - if (sync_cnt >= HCLGE_MAX_SYNC_COUNT) + if (sync_cnt >= HCLGE_MAX_SYNC_COUNT) { + mutex_unlock(&hdev->vport_lock); return; + } vlan_id = find_first_bit(vport->vlan_del_fail_bmap, VLAN_N_VID); } } + mutex_unlock(&hdev->vport_lock); hclge_sync_vlan_fltr_state(hdev); } @@ -11651,6 +11667,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) goto err_msi_irq_uninit; if (hdev->hw.mac.media_type == HNAE3_MEDIA_TYPE_COPPER) { + clear_bit(HNAE3_DEV_SUPPORT_FEC_B, ae_dev->caps); if (hnae3_dev_phy_imp_supported(hdev)) ret = hclge_update_tp_port_info(hdev); else diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index a4d68fb216fb..0aa9beefd1c7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1206,6 +1206,8 @@ static int hclgevf_set_vlan_filter(struct hnae3_handle *handle, test_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state)) && is_kill) { set_bit(vlan_id, hdev->vlan_del_fail_bmap); return -EBUSY; + } else if (!is_kill && test_bit(vlan_id, hdev->vlan_del_fail_bmap)) { + clear_bit(vlan_id, hdev->vlan_del_fail_bmap); } hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_VLAN, @@ -1233,20 +1235,25 @@ static void hclgevf_sync_vlan_filter(struct hclgevf_dev *hdev) int ret, sync_cnt = 0; u16 vlan_id; + if (bitmap_empty(hdev->vlan_del_fail_bmap, VLAN_N_VID)) + return; + + rtnl_lock(); vlan_id = find_first_bit(hdev->vlan_del_fail_bmap, VLAN_N_VID); while (vlan_id != VLAN_N_VID) { ret = hclgevf_set_vlan_filter(handle, htons(ETH_P_8021Q), vlan_id, true); if (ret) - return; + break; clear_bit(vlan_id, hdev->vlan_del_fail_bmap); sync_cnt++; if (sync_cnt >= HCLGEVF_MAX_SYNC_COUNT) - return; + break; vlan_id = find_first_bit(hdev->vlan_del_fail_bmap, VLAN_N_VID); } + rtnl_unlock(); } static int hclgevf_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable) @@ -1974,8 +1981,18 @@ static enum hclgevf_evt_cause hclgevf_check_evt_cause(struct hclgevf_dev *hdev, return HCLGEVF_VECTOR0_EVENT_OTHER; } +static void hclgevf_reset_timer(struct timer_list *t) +{ + struct hclgevf_dev *hdev = from_timer(hdev, t, reset_timer); + + hclgevf_clear_event_cause(hdev, HCLGEVF_VECTOR0_EVENT_RST); + hclgevf_reset_task_schedule(hdev); +} + static irqreturn_t hclgevf_misc_irq_handle(int irq, void *data) { +#define HCLGEVF_RESET_DELAY 5 + enum hclgevf_evt_cause event_cause; struct hclgevf_dev *hdev = data; u32 clearval; @@ -1987,7 +2004,8 @@ static irqreturn_t hclgevf_misc_irq_handle(int irq, void *data) switch (event_cause) { case HCLGEVF_VECTOR0_EVENT_RST: - hclgevf_reset_task_schedule(hdev); + mod_timer(&hdev->reset_timer, + jiffies + msecs_to_jiffies(HCLGEVF_RESET_DELAY)); break; case HCLGEVF_VECTOR0_EVENT_MBX: hclgevf_mbx_handler(hdev); @@ -2930,6 +2948,7 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev) HCLGEVF_DRIVER_NAME); hclgevf_task_schedule(hdev, round_jiffies_relative(HZ)); + timer_setup(&hdev->reset_timer, hclgevf_reset_timer, 0); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 81c16b8c8da2..a73f2bf3a56a 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -219,6 +219,7 @@ struct hclgevf_dev { enum hnae3_reset_type reset_level; unsigned long reset_pending; enum hnae3_reset_type reset_type; + struct timer_list reset_timer; #define HCLGEVF_RESET_REQUESTED 0 #define HCLGEVF_RESET_PENDING 1 diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c index bbf7b14079de..85c2a634c8f9 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c @@ -63,6 +63,9 @@ static int hclgevf_get_mbx_resp(struct hclgevf_dev *hdev, u16 code0, u16 code1, i++; } + /* ensure additional_info will be seen after received_resp */ + smp_rmb(); + if (i >= HCLGEVF_MAX_TRY_TIMES) { dev_err(&hdev->pdev->dev, "VF could not get mbx(%u,%u) resp(=%d) from PF in %d tries\n", @@ -178,6 +181,10 @@ static void hclgevf_handle_mbx_response(struct hclgevf_dev *hdev, resp->resp_status = hclgevf_resp_to_errno(resp_status); memcpy(resp->additional_info, req->msg.resp_data, HCLGE_MBX_MAX_RESP_DATA_SIZE * sizeof(u8)); + + /* ensure additional_info will be seen before setting received_resp */ + smp_wmb(); + if (match_id) { /* If match_id is not zero, it means PF support match_id. * if the match_id is right, VF get the right response, or diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 08d7edccfb8d..3f99eb198245 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -3844,7 +3844,7 @@ static int i40e_vc_add_cloud_filter(struct i40e_vf *vf, u8 *msg) struct i40e_pf *pf = vf->pf; struct i40e_vsi *vsi = NULL; int aq_ret = 0; - int i, ret; + int i; if (!i40e_sync_vf_state(vf, I40E_VF_STATE_ACTIVE)) { aq_ret = -EINVAL; @@ -3868,8 +3868,10 @@ static int i40e_vc_add_cloud_filter(struct i40e_vf *vf, u8 *msg) } cfilter = kzalloc(sizeof(*cfilter), GFP_KERNEL); - if (!cfilter) - return -ENOMEM; + if (!cfilter) { + aq_ret = -ENOMEM; + goto err_out; + } /* parse destination mac address */ for (i = 0; i < ETH_ALEN; i++) @@ -3917,13 +3919,13 @@ static int i40e_vc_add_cloud_filter(struct i40e_vf *vf, u8 *msg) /* Adding cloud filter programmed as TC filter */ if (tcf.dst_port) - ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, true); + aq_ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter, true); else - ret = i40e_add_del_cloud_filter(vsi, cfilter, true); - if (ret) { + aq_ret = i40e_add_del_cloud_filter(vsi, cfilter, true); + if (aq_ret) { dev_err(&pf->pdev->dev, "VF %d: Failed to add cloud filter, err %pe aq_err %s\n", - vf->vf_id, ERR_PTR(ret), + vf->vf_id, ERR_PTR(aq_ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); goto err_free; } diff --git a/drivers/net/ethernet/intel/ice/ice_ddp.c b/drivers/net/ethernet/intel/ice/ice_ddp.c index cfb1580f5850..8b7504a9df31 100644 --- a/drivers/net/ethernet/intel/ice/ice_ddp.c +++ b/drivers/net/ethernet/intel/ice/ice_ddp.c @@ -1479,14 +1479,14 @@ ice_post_dwnld_pkg_actions(struct ice_hw *hw) } /** - * ice_download_pkg + * ice_download_pkg_with_sig_seg * @hw: pointer to the hardware structure * @pkg_hdr: pointer to package header * * Handles the download of a complete package. */ static enum ice_ddp_state -ice_download_pkg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) +ice_download_pkg_with_sig_seg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) { enum ice_aq_err aq_err = hw->adminq.sq_last_status; enum ice_ddp_state state = ICE_DDP_PKG_ERR; @@ -1519,6 +1519,103 @@ ice_download_pkg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr) state = ice_post_dwnld_pkg_actions(hw); ice_release_global_cfg_lock(hw); + + return state; +} + +/** + * ice_dwnld_cfg_bufs + * @hw: pointer to the hardware structure + * @bufs: pointer to an array of buffers + * @count: the number of buffers in the array + * + * Obtains global config lock and downloads the package configuration buffers + * to the firmware. + */ +static enum ice_ddp_state +ice_dwnld_cfg_bufs(struct ice_hw *hw, struct ice_buf *bufs, u32 count) +{ + enum ice_ddp_state state; + struct ice_buf_hdr *bh; + int status; + + if (!bufs || !count) + return ICE_DDP_PKG_ERR; + + /* If the first buffer's first section has its metadata bit set + * then there are no buffers to be downloaded, and the operation is + * considered a success. + */ + bh = (struct ice_buf_hdr *)bufs; + if (le32_to_cpu(bh->section_entry[0].type) & ICE_METADATA_BUF) + return ICE_DDP_PKG_SUCCESS; + + status = ice_acquire_global_cfg_lock(hw, ICE_RES_WRITE); + if (status) { + if (status == -EALREADY) + return ICE_DDP_PKG_ALREADY_LOADED; + return ice_map_aq_err_to_ddp_state(hw->adminq.sq_last_status); + } + + state = ice_dwnld_cfg_bufs_no_lock(hw, bufs, 0, count, true); + if (!state) + state = ice_post_dwnld_pkg_actions(hw); + + ice_release_global_cfg_lock(hw); + + return state; +} + +/** + * ice_download_pkg_without_sig_seg + * @hw: pointer to the hardware structure + * @ice_seg: pointer to the segment of the package to be downloaded + * + * Handles the download of a complete package without signature segment. + */ +static enum ice_ddp_state +ice_download_pkg_without_sig_seg(struct ice_hw *hw, struct ice_seg *ice_seg) +{ + struct ice_buf_table *ice_buf_tbl; + + ice_debug(hw, ICE_DBG_PKG, "Segment format version: %d.%d.%d.%d\n", + ice_seg->hdr.seg_format_ver.major, + ice_seg->hdr.seg_format_ver.minor, + ice_seg->hdr.seg_format_ver.update, + ice_seg->hdr.seg_format_ver.draft); + + ice_debug(hw, ICE_DBG_PKG, "Seg: type 0x%X, size %d, name %s\n", + le32_to_cpu(ice_seg->hdr.seg_type), + le32_to_cpu(ice_seg->hdr.seg_size), ice_seg->hdr.seg_id); + + ice_buf_tbl = ice_find_buf_table(ice_seg); + + ice_debug(hw, ICE_DBG_PKG, "Seg buf count: %d\n", + le32_to_cpu(ice_buf_tbl->buf_count)); + + return ice_dwnld_cfg_bufs(hw, ice_buf_tbl->buf_array, + le32_to_cpu(ice_buf_tbl->buf_count)); +} + +/** + * ice_download_pkg + * @hw: pointer to the hardware structure + * @pkg_hdr: pointer to package header + * @ice_seg: pointer to the segment of the package to be downloaded + * + * Handles the download of a complete package. + */ +static enum ice_ddp_state +ice_download_pkg(struct ice_hw *hw, struct ice_pkg_hdr *pkg_hdr, + struct ice_seg *ice_seg) +{ + enum ice_ddp_state state; + + if (hw->pkg_has_signing_seg) + state = ice_download_pkg_with_sig_seg(hw, pkg_hdr); + else + state = ice_download_pkg_without_sig_seg(hw, ice_seg); + ice_post_pkg_dwnld_vlan_mode_cfg(hw); return state; @@ -2083,7 +2180,7 @@ enum ice_ddp_state ice_init_pkg(struct ice_hw *hw, u8 *buf, u32 len) /* initialize package hints and then download package */ ice_init_pkg_hints(hw, seg); - state = ice_download_pkg(hw, pkg); + state = ice_download_pkg(hw, pkg, seg); if (state == ICE_DDP_PKG_ALREADY_LOADED) { ice_debug(hw, ICE_DBG_INIT, "package previously loaded - no work.\n"); diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 835c419ccc74..86b180cb32a0 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -815,12 +815,6 @@ ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv, struct ice_pf *pf = d->pf; int ret; - if (prio > ICE_DPLL_PRIO_MAX) { - NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d", - ICE_DPLL_PRIO_MAX); - return -EINVAL; - } - mutex_lock(&pf->dplls.lock); ret = ice_dpll_hw_input_prio_set(pf, d, p, prio, extack); mutex_unlock(&pf->dplls.lock); @@ -1756,6 +1750,7 @@ ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu, } d->pf = pf; if (cgu) { + ice_dpll_update_state(pf, d, true); ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d); if (ret) { dpll_device_put(d->dpll); @@ -1796,8 +1791,6 @@ static int ice_dpll_init_worker(struct ice_pf *pf) struct ice_dplls *d = &pf->dplls; struct kthread_worker *kworker; - ice_dpll_update_state(pf, &d->eec, true); - ice_dpll_update_state(pf, &d->pps, true); kthread_init_delayed_work(&d->work, ice_dpll_periodic_work); kworker = kthread_create_worker(0, "ice-dplls-%s", dev_name(ice_pf_to_dev(pf))); @@ -1830,6 +1823,7 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf, int num_pins, i, ret = -EINVAL; struct ice_hw *hw = &pf->hw; struct ice_dpll_pin *pins; + unsigned long caps; u8 freq_supp_num; bool input; @@ -1849,6 +1843,7 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf, } for (i = 0; i < num_pins; i++) { + caps = 0; pins[i].idx = i; pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input); pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input); @@ -1861,8 +1856,8 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf, &dp->input_prio[i]); if (ret) return ret; - pins[i].prop.capabilities |= - DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE; + caps |= (DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE | + DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE); pins[i].prop.phase_range.min = pf->dplls.input_phase_adj_max; pins[i].prop.phase_range.max = @@ -1872,9 +1867,11 @@ ice_dpll_init_info_direct_pins(struct ice_pf *pf, pf->dplls.output_phase_adj_max; pins[i].prop.phase_range.max = -pf->dplls.output_phase_adj_max; + ret = ice_cgu_get_output_pin_state_caps(hw, i, &caps); + if (ret) + return ret; } - pins[i].prop.capabilities |= - DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + pins[i].prop.capabilities = caps; ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type, NULL); if (ret) return ret; diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h index bb32b6d88373..93172e93995b 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -6,7 +6,6 @@ #include "ice.h" -#define ICE_DPLL_PRIO_MAX 0xF #define ICE_DPLL_RCLK_NUM_MAX 4 /** ice_dpll_pin - store info about pins diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 6607fa6fe556..fb9c93f37e84 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -7401,15 +7401,6 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) goto err_vsi_rebuild; } - /* configure PTP timestamping after VSI rebuild */ - if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) { - if (pf->ptp.tx_interrupt_mode == ICE_PTP_TX_INTERRUPT_SELF) - ice_ptp_cfg_timestamp(pf, false); - else if (pf->ptp.tx_interrupt_mode == ICE_PTP_TX_INTERRUPT_ALL) - /* for E82x PHC owner always need to have interrupts */ - ice_ptp_cfg_timestamp(pf, true); - } - err = ice_vsi_rebuild_by_type(pf, ICE_VSI_SWITCHDEV_CTRL); if (err) { dev_err(dev, "Switchdev CTRL VSI rebuild failed: %d\n", err); @@ -7461,6 +7452,9 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type) ice_plug_aux_dev(pf); if (ice_is_feature_supported(pf, ICE_F_SRIOV_LAG)) ice_lag_rebuild(pf); + + /* Restore timestamp mode settings after VSI rebuild */ + ice_ptp_restore_timestamp_mode(pf); return; err_vsi_rebuild: diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 1eddcbe89b0c..71f405f8a6fe 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -256,48 +256,42 @@ ice_verify_pin_e810t(struct ptp_clock_info *info, unsigned int pin, } /** - * ice_ptp_configure_tx_tstamp - Enable or disable Tx timestamp interrupt - * @pf: The PF pointer to search in - * @on: bool value for whether timestamp interrupt is enabled or disabled + * ice_ptp_cfg_tx_interrupt - Configure Tx timestamp interrupt for the device + * @pf: Board private structure + * + * Program the device to respond appropriately to the Tx timestamp interrupt + * cause. */ -static void ice_ptp_configure_tx_tstamp(struct ice_pf *pf, bool on) +static void ice_ptp_cfg_tx_interrupt(struct ice_pf *pf) { + struct ice_hw *hw = &pf->hw; + bool enable; u32 val; + switch (pf->ptp.tx_interrupt_mode) { + case ICE_PTP_TX_INTERRUPT_ALL: + /* React to interrupts across all quads. */ + wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x1f); + enable = true; + break; + case ICE_PTP_TX_INTERRUPT_NONE: + /* Do not react to interrupts on any quad. */ + wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x0); + enable = false; + break; + case ICE_PTP_TX_INTERRUPT_SELF: + default: + enable = pf->ptp.tstamp_config.tx_type == HWTSTAMP_TX_ON; + break; + } + /* Configure the Tx timestamp interrupt */ - val = rd32(&pf->hw, PFINT_OICR_ENA); - if (on) + val = rd32(hw, PFINT_OICR_ENA); + if (enable) val |= PFINT_OICR_TSYN_TX_M; else val &= ~PFINT_OICR_TSYN_TX_M; - wr32(&pf->hw, PFINT_OICR_ENA, val); -} - -/** - * ice_set_tx_tstamp - Enable or disable Tx timestamping - * @pf: The PF pointer to search in - * @on: bool value for whether timestamps are enabled or disabled - */ -static void ice_set_tx_tstamp(struct ice_pf *pf, bool on) -{ - struct ice_vsi *vsi; - u16 i; - - vsi = ice_get_main_vsi(pf); - if (!vsi) - return; - - /* Set the timestamp enable flag for all the Tx rings */ - ice_for_each_txq(vsi, i) { - if (!vsi->tx_rings[i]) - continue; - vsi->tx_rings[i]->ptp_tx = on; - } - - if (pf->ptp.tx_interrupt_mode == ICE_PTP_TX_INTERRUPT_SELF) - ice_ptp_configure_tx_tstamp(pf, on); - - pf->ptp.tstamp_config.tx_type = on ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + wr32(hw, PFINT_OICR_ENA, val); } /** @@ -311,7 +305,7 @@ static void ice_set_rx_tstamp(struct ice_pf *pf, bool on) u16 i; vsi = ice_get_main_vsi(pf); - if (!vsi) + if (!vsi || !vsi->rx_rings) return; /* Set the timestamp flag for all the Rx rings */ @@ -320,23 +314,50 @@ static void ice_set_rx_tstamp(struct ice_pf *pf, bool on) continue; vsi->rx_rings[i]->ptp_rx = on; } +} + +/** + * ice_ptp_disable_timestamp_mode - Disable current timestamp mode + * @pf: Board private structure + * + * Called during preparation for reset to temporarily disable timestamping on + * the device. Called during remove to disable timestamping while cleaning up + * driver resources. + */ +static void ice_ptp_disable_timestamp_mode(struct ice_pf *pf) +{ + struct ice_hw *hw = &pf->hw; + u32 val; + + val = rd32(hw, PFINT_OICR_ENA); + val &= ~PFINT_OICR_TSYN_TX_M; + wr32(hw, PFINT_OICR_ENA, val); - pf->ptp.tstamp_config.rx_filter = on ? HWTSTAMP_FILTER_ALL : - HWTSTAMP_FILTER_NONE; + ice_set_rx_tstamp(pf, false); } /** - * ice_ptp_cfg_timestamp - Configure timestamp for init/deinit + * ice_ptp_restore_timestamp_mode - Restore timestamp configuration * @pf: Board private structure - * @ena: bool value to enable or disable time stamp * - * This function will configure timestamping during PTP initialization - * and deinitialization + * Called at the end of rebuild to restore timestamp configuration after + * a device reset. */ -void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena) +void ice_ptp_restore_timestamp_mode(struct ice_pf *pf) { - ice_set_tx_tstamp(pf, ena); - ice_set_rx_tstamp(pf, ena); + struct ice_hw *hw = &pf->hw; + bool enable_rx; + + ice_ptp_cfg_tx_interrupt(pf); + + enable_rx = pf->ptp.tstamp_config.rx_filter == HWTSTAMP_FILTER_ALL; + ice_set_rx_tstamp(pf, enable_rx); + + /* Trigger an immediate software interrupt to ensure that timestamps + * which occurred during reset are handled now. + */ + wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M); + ice_flush(hw); } /** @@ -2037,10 +2058,10 @@ ice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config) { switch (config->tx_type) { case HWTSTAMP_TX_OFF: - ice_set_tx_tstamp(pf, false); + pf->ptp.tstamp_config.tx_type = HWTSTAMP_TX_OFF; break; case HWTSTAMP_TX_ON: - ice_set_tx_tstamp(pf, true); + pf->ptp.tstamp_config.tx_type = HWTSTAMP_TX_ON; break; default: return -ERANGE; @@ -2048,7 +2069,7 @@ ice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config) switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: - ice_set_rx_tstamp(pf, false); + pf->ptp.tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: @@ -2064,12 +2085,15 @@ ice_ptp_set_timestamp_mode(struct ice_pf *pf, struct hwtstamp_config *config) case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: case HWTSTAMP_FILTER_NTP_ALL: case HWTSTAMP_FILTER_ALL: - ice_set_rx_tstamp(pf, true); + pf->ptp.tstamp_config.rx_filter = HWTSTAMP_FILTER_ALL; break; default: return -ERANGE; } + /* Immediately update the device timestamping mode */ + ice_ptp_restore_timestamp_mode(pf); + return 0; } @@ -2737,7 +2761,7 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf) clear_bit(ICE_FLAG_PTP, pf->flags); /* Disable timestamping for both Tx and Rx */ - ice_ptp_cfg_timestamp(pf, false); + ice_ptp_disable_timestamp_mode(pf); kthread_cancel_delayed_work_sync(&ptp->work); @@ -2803,15 +2827,7 @@ static int ice_ptp_init_owner(struct ice_pf *pf) /* Release the global hardware lock */ ice_ptp_unlock(hw); - if (pf->ptp.tx_interrupt_mode == ICE_PTP_TX_INTERRUPT_ALL) { - /* The clock owner for this device type handles the timestamp - * interrupt for all ports. - */ - ice_ptp_configure_tx_tstamp(pf, true); - - /* React on all quads interrupts for E82x */ - wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x1f); - + if (!ice_is_e810(hw)) { /* Enable quad interrupts */ err = ice_ptp_tx_ena_intr(pf, true, itr); if (err) @@ -2881,13 +2897,6 @@ static int ice_ptp_init_port(struct ice_pf *pf, struct ice_ptp_port *ptp_port) case ICE_PHY_E810: return ice_ptp_init_tx_e810(pf, &ptp_port->tx); case ICE_PHY_E822: - /* Non-owner PFs don't react to any interrupts on E82x, - * neither on own quad nor on others - */ - if (!ice_ptp_pf_handles_tx_interrupt(pf)) { - ice_ptp_configure_tx_tstamp(pf, false); - wr32(hw, PFINT_TSYN_MSK + (0x4 * hw->pf_id), (u32)0x0); - } kthread_init_delayed_work(&ptp_port->ov_work, ice_ptp_wait_for_offsets); @@ -3032,6 +3041,9 @@ void ice_ptp_init(struct ice_pf *pf) /* Start the PHY timestamping block */ ice_ptp_reset_phy_timestamping(pf); + /* Configure initial Tx interrupt settings */ + ice_ptp_cfg_tx_interrupt(pf); + set_bit(ICE_FLAG_PTP, pf->flags); err = ice_ptp_init_work(pf, ptp); if (err) @@ -3067,7 +3079,7 @@ void ice_ptp_release(struct ice_pf *pf) return; /* Disable timestamping for both Tx and Rx */ - ice_ptp_cfg_timestamp(pf, false); + ice_ptp_disable_timestamp_mode(pf); ice_ptp_remove_auxbus_device(pf); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 8f6f94392756..06a330867fc9 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -292,7 +292,7 @@ int ice_ptp_clock_index(struct ice_pf *pf); struct ice_pf; int ice_ptp_set_ts_config(struct ice_pf *pf, struct ifreq *ifr); int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr); -void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena); +void ice_ptp_restore_timestamp_mode(struct ice_pf *pf); void ice_ptp_extts_event(struct ice_pf *pf); s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb); @@ -317,8 +317,7 @@ static inline int ice_ptp_get_ts_config(struct ice_pf *pf, struct ifreq *ifr) return -EOPNOTSUPP; } -static inline void ice_ptp_cfg_timestamp(struct ice_pf *pf, bool ena) { } - +static inline void ice_ptp_restore_timestamp_mode(struct ice_pf *pf) { } static inline void ice_ptp_extts_event(struct ice_pf *pf) { } static inline s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 6d573908de7a..a00b55e14aac 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -3961,3 +3961,57 @@ int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num) return ret; } + +/** + * ice_cgu_get_output_pin_state_caps - get output pin state capabilities + * @hw: pointer to the hw struct + * @pin_id: id of a pin + * @caps: capabilities to modify + * + * Return: + * * 0 - success, state capabilities were modified + * * negative - failure, capabilities were not modified + */ +int ice_cgu_get_output_pin_state_caps(struct ice_hw *hw, u8 pin_id, + unsigned long *caps) +{ + bool can_change = true; + + switch (hw->device_id) { + case ICE_DEV_ID_E810C_SFP: + if (pin_id == ZL_OUT2 || pin_id == ZL_OUT3) + can_change = false; + break; + case ICE_DEV_ID_E810C_QSFP: + if (pin_id == ZL_OUT2 || pin_id == ZL_OUT3 || pin_id == ZL_OUT4) + can_change = false; + break; + case ICE_DEV_ID_E823L_10G_BASE_T: + case ICE_DEV_ID_E823L_1GBE: + case ICE_DEV_ID_E823L_BACKPLANE: + case ICE_DEV_ID_E823L_QSFP: + case ICE_DEV_ID_E823L_SFP: + case ICE_DEV_ID_E823C_10G_BASE_T: + case ICE_DEV_ID_E823C_BACKPLANE: + case ICE_DEV_ID_E823C_QSFP: + case ICE_DEV_ID_E823C_SFP: + case ICE_DEV_ID_E823C_SGMII: + if (hw->cgu_part_number == + ICE_AQC_GET_LINK_TOPO_NODE_NR_ZL30632_80032 && + pin_id == ZL_OUT2) + can_change = false; + else if (hw->cgu_part_number == + ICE_AQC_GET_LINK_TOPO_NODE_NR_SI5383_5384 && + pin_id == SI_OUT1) + can_change = false; + break; + default: + return -EINVAL; + } + if (can_change) + *caps |= DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + else + *caps &= ~DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + + return 0; +} diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 36aeeef99ec0..cf76701566c7 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -282,6 +282,8 @@ int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx, int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num); void ice_ptp_init_phy_model(struct ice_hw *hw); +int ice_cgu_get_output_pin_state_caps(struct ice_hw *hw, u8 pin_id, + unsigned long *caps); #define PFTSYN_SEM_BYTES 4 diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 52d0a126eb61..9e97ea863068 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -2306,9 +2306,6 @@ ice_tstamp(struct ice_tx_ring *tx_ring, struct sk_buff *skb, if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) return; - if (!tx_ring->ptp_tx) - return; - /* Tx timestamps cannot be sampled when doing TSO */ if (first->tx_flags & ICE_TX_FLAGS_TSO) return; diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index 166413fc33f4..daf7b9dbb143 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -380,7 +380,6 @@ struct ice_tx_ring { #define ICE_TX_FLAGS_RING_VLAN_L2TAG2 BIT(2) u8 flags; u8 dcb_tc; /* Traffic class of ring */ - u8 ptp_tx; } ____cacheline_internodealigned_in_smp; static inline bool ice_ring_uses_build_skb(struct ice_rx_ring *ring) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 90817136808d..29aac327574d 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4790,14 +4790,17 @@ static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset, u8 *data) { if (sset == ETH_SS_STATS) { + struct mvneta_port *pp = netdev_priv(netdev); int i; for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++) memcpy(data + i * ETH_GSTRING_LEN, mvneta_statistics[i].name, ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN * ARRAY_SIZE(mvneta_statistics); - page_pool_ethtool_stats_get_strings(data); + if (!pp->bm_priv) { + data += ETH_GSTRING_LEN * ARRAY_SIZE(mvneta_statistics); + page_pool_ethtool_stats_get_strings(data); + } } } @@ -4915,8 +4918,10 @@ static void mvneta_ethtool_pp_stats(struct mvneta_port *pp, u64 *data) struct page_pool_stats stats = {}; int i; - for (i = 0; i < rxq_number; i++) - page_pool_get_stats(pp->rxqs[i].page_pool, &stats); + for (i = 0; i < rxq_number; i++) { + if (pp->rxqs[i].page_pool) + page_pool_get_stats(pp->rxqs[i].page_pool, &stats); + } page_pool_ethtool_stats_get(data, &stats); } @@ -4932,14 +4937,21 @@ static void mvneta_ethtool_get_stats(struct net_device *dev, for (i = 0; i < ARRAY_SIZE(mvneta_statistics); i++) *data++ = pp->ethtool_stats[i]; - mvneta_ethtool_pp_stats(pp, data); + if (!pp->bm_priv) + mvneta_ethtool_pp_stats(pp, data); } static int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset) { - if (sset == ETH_SS_STATS) - return ARRAY_SIZE(mvneta_statistics) + - page_pool_ethtool_stats_get_count(); + if (sset == ETH_SS_STATS) { + int count = ARRAY_SIZE(mvneta_statistics); + struct mvneta_port *pp = netdev_priv(dev); + + if (!pp->bm_priv) + count += page_pool_ethtool_stats_get_count(); + + return count; + } return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 4762dbea64a1..97a71e9b8563 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -1088,6 +1088,7 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) struct ethhdr *eth_hdr; bool new = false; int err = 0; + u64 vf_num; u32 ring; if (!flow_cfg->max_flows) { @@ -1100,7 +1101,21 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) if (!(pfvf->flags & OTX2_FLAG_NTUPLE_SUPPORT)) return -ENOMEM; - if (ring >= pfvf->hw.rx_queues && fsp->ring_cookie != RX_CLS_FLOW_DISC) + /* Number of queues on a VF can be greater or less than + * the PF's queue. Hence no need to check for the + * queue count. Hence no need to check queue count if PF + * is installing for its VF. Below is the expected vf_num value + * based on the ethtool commands. + * + * e.g. + * 1. ethtool -U <netdev> ... action -1 ==> vf_num:255 + * 2. ethtool -U <netdev> ... action <queue_num> ==> vf_num:0 + * 3. ethtool -U <netdev> ... vf <vf_idx> queue <queue_num> ==> + * vf_num:vf_idx+1 + */ + vf_num = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie); + if (!is_otx2_vf(pfvf->pcifunc) && !vf_num && + ring >= pfvf->hw.rx_queues && fsp->ring_cookie != RX_CLS_FLOW_DISC) return -EINVAL; if (fsp->location >= otx2_get_maxflows(flow_cfg)) @@ -1182,6 +1197,9 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) flow_cfg->nr_flows++; } + if (flow->is_vf) + netdev_info(pfvf->netdev, + "Make sure that VF's queue number is within its queue limit\n"); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 91b99fd70361..ba95ac913274 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1934,6 +1934,8 @@ int otx2_stop(struct net_device *netdev) /* Clear RSS enable flag */ rss = &pf->hw.rss_info; rss->enable = false; + if (!netif_is_rxfh_configured(netdev)) + kfree(rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP]); /* Cleanup Queue IRQ */ vec = pci_irq_vector(pf->pdev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index bb11e644d24f..af3928eddafd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -177,6 +177,8 @@ static void mlx5e_ptpsq_mark_ts_cqes_undelivered(struct mlx5e_ptpsq *ptpsq, static void mlx5e_ptp_handle_ts_cqe(struct mlx5e_ptpsq *ptpsq, struct mlx5_cqe64 *cqe, + u8 *md_buff, + u8 *md_buff_sz, int budget) { struct mlx5e_ptp_port_ts_cqe_list *pending_cqe_list = ptpsq->ts_cqe_pending_list; @@ -211,19 +213,24 @@ static void mlx5e_ptp_handle_ts_cqe(struct mlx5e_ptpsq *ptpsq, mlx5e_ptpsq_mark_ts_cqes_undelivered(ptpsq, hwtstamp); out: napi_consume_skb(skb, budget); - mlx5e_ptp_metadata_fifo_push(&ptpsq->metadata_freelist, metadata_id); + md_buff[*md_buff_sz++] = metadata_id; if (unlikely(mlx5e_ptp_metadata_map_unhealthy(&ptpsq->metadata_map)) && !test_and_set_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) queue_work(ptpsq->txqsq.priv->wq, &ptpsq->report_unhealthy_work); } -static bool mlx5e_ptp_poll_ts_cq(struct mlx5e_cq *cq, int budget) +static bool mlx5e_ptp_poll_ts_cq(struct mlx5e_cq *cq, int napi_budget) { struct mlx5e_ptpsq *ptpsq = container_of(cq, struct mlx5e_ptpsq, ts_cq); - struct mlx5_cqwq *cqwq = &cq->wq; + int budget = min(napi_budget, MLX5E_TX_CQ_POLL_BUDGET); + u8 metadata_buff[MLX5E_TX_CQ_POLL_BUDGET]; + u8 metadata_buff_sz = 0; + struct mlx5_cqwq *cqwq; struct mlx5_cqe64 *cqe; int work_done = 0; + cqwq = &cq->wq; + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &ptpsq->txqsq.state))) return false; @@ -234,7 +241,8 @@ static bool mlx5e_ptp_poll_ts_cq(struct mlx5e_cq *cq, int budget) do { mlx5_cqwq_pop(cqwq); - mlx5e_ptp_handle_ts_cqe(ptpsq, cqe, budget); + mlx5e_ptp_handle_ts_cqe(ptpsq, cqe, + metadata_buff, &metadata_buff_sz, napi_budget); } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(cqwq))); mlx5_cqwq_update_db_record(cqwq); @@ -242,6 +250,10 @@ static bool mlx5e_ptp_poll_ts_cq(struct mlx5e_cq *cq, int budget) /* ensure cq space is freed before enabling more cqes */ wmb(); + while (metadata_buff_sz > 0) + mlx5e_ptp_metadata_fifo_push(&ptpsq->metadata_freelist, + metadata_buff[--metadata_buff_sz]); + mlx5e_txqsq_wake(&ptpsq->txqsq); return work_done == budget; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c index fea8c0a5fe89..4358798d6ce1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c @@ -492,11 +492,11 @@ static int mlx5e_rx_reporter_dump(struct devlink_health_reporter *reporter, void mlx5e_reporter_rx_timeout(struct mlx5e_rq *rq) { - char icosq_str[MLX5E_REPORTER_PER_Q_MAX_LEN] = {}; char err_str[MLX5E_REPORTER_PER_Q_MAX_LEN]; struct mlx5e_icosq *icosq = rq->icosq; struct mlx5e_priv *priv = rq->priv; struct mlx5e_err_ctx err_ctx = {}; + char icosq_str[32] = {}; err_ctx.ctx = rq; err_ctx.recover = mlx5e_rx_reporter_timeout_recover; @@ -505,7 +505,7 @@ void mlx5e_reporter_rx_timeout(struct mlx5e_rq *rq) if (icosq) snprintf(icosq_str, sizeof(icosq_str), "ICOSQ: 0x%x, ", icosq->sqn); snprintf(err_str, sizeof(err_str), - "RX timeout on channel: %d, %sRQ: 0x%x, CQ: 0x%x", + "RX timeout on channel: %d, %s RQ: 0x%x, CQ: 0x%x", rq->ix, icosq_str, rq->rqn, rq->cq.mcq.cqn); mlx5e_health_report(priv, priv->rx_reporter, err_str, &err_ctx); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c index 00a04fdd756f..668da5c70e63 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c @@ -300,9 +300,6 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, if (err) goto destroy_neigh_entry; - e->encap_size = ipv4_encap_size; - e->encap_header = encap_header; - if (!(nud_state & NUD_VALID)) { neigh_event_send(attr.n, NULL); /* the encap entry will be made valid on neigh update event @@ -322,6 +319,8 @@ int mlx5e_tc_tun_create_header_ipv4(struct mlx5e_priv *priv, goto destroy_neigh_entry; } + e->encap_size = ipv4_encap_size; + e->encap_header = encap_header; e->flags |= MLX5_ENCAP_ENTRY_VALID; mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); mlx5e_route_lookup_ipv4_put(&attr); @@ -404,16 +403,12 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv, if (err) goto free_encap; - e->encap_size = ipv4_encap_size; - kfree(e->encap_header); - e->encap_header = encap_header; - if (!(nud_state & NUD_VALID)) { neigh_event_send(attr.n, NULL); /* the encap entry will be made valid on neigh update event * and not used before that. */ - goto release_neigh; + goto free_encap; } memset(&reformat_params, 0, sizeof(reformat_params)); @@ -427,6 +422,10 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv, goto free_encap; } + e->encap_size = ipv4_encap_size; + kfree(e->encap_header); + e->encap_header = encap_header; + e->flags |= MLX5_ENCAP_ENTRY_VALID; mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); mlx5e_route_lookup_ipv4_put(&attr); @@ -568,9 +567,6 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, if (err) goto destroy_neigh_entry; - e->encap_size = ipv6_encap_size; - e->encap_header = encap_header; - if (!(nud_state & NUD_VALID)) { neigh_event_send(attr.n, NULL); /* the encap entry will be made valid on neigh update event @@ -590,6 +586,8 @@ int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv, goto destroy_neigh_entry; } + e->encap_size = ipv6_encap_size; + e->encap_header = encap_header; e->flags |= MLX5_ENCAP_ENTRY_VALID; mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); mlx5e_route_lookup_ipv6_put(&attr); @@ -671,16 +669,12 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, if (err) goto free_encap; - e->encap_size = ipv6_encap_size; - kfree(e->encap_header); - e->encap_header = encap_header; - if (!(nud_state & NUD_VALID)) { neigh_event_send(attr.n, NULL); /* the encap entry will be made valid on neigh update event * and not used before that. */ - goto release_neigh; + goto free_encap; } memset(&reformat_params, 0, sizeof(reformat_params)); @@ -694,6 +688,10 @@ int mlx5e_tc_tun_update_header_ipv6(struct mlx5e_priv *priv, goto free_encap; } + e->encap_size = ipv6_encap_size; + kfree(e->encap_header); + e->encap_header = encap_header; + e->flags |= MLX5_ENCAP_ENTRY_VALID; mlx5e_rep_queue_neigh_stats_work(netdev_priv(attr.out_dev)); mlx5e_route_lookup_ipv6_put(&attr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 215261a69255..792a0ea544cd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -43,12 +43,17 @@ void mlx5e_ethtool_get_drvinfo(struct mlx5e_priv *priv, struct ethtool_drvinfo *drvinfo) { struct mlx5_core_dev *mdev = priv->mdev; + int count; strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); - snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%04d (%.16s)", - fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev), - mdev->board_id); + count = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d (%.16s)", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev), mdev->board_id); + if (count == sizeof(drvinfo->fw_version)) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev)); + strscpy(drvinfo->bus_info, dev_name(mdev->device), sizeof(drvinfo->bus_info)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 693e55b010d9..3ab682bbcf86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -71,13 +71,17 @@ static void mlx5e_rep_get_drvinfo(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; + int count; strscpy(drvinfo->driver, mlx5e_rep_driver_name, sizeof(drvinfo->driver)); - snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%04d (%.16s)", - fw_rev_maj(mdev), fw_rev_min(mdev), - fw_rev_sub(mdev), mdev->board_id); + count = snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d (%.16s)", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev), mdev->board_id); + if (count == sizeof(drvinfo->fw_version)) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%04d", fw_rev_maj(mdev), + fw_rev_min(mdev), fw_rev_sub(mdev)); } static const struct counter_desc sw_rep_stats_desc[] = { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 9a5a5c2c7da9..7ca9e5b86778 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -3147,7 +3147,7 @@ static struct mlx5_fields fields[] = { OFFLOAD(DIPV6_31_0, 32, U32_MAX, ip6.daddr.s6_addr32[3], 0, dst_ipv4_dst_ipv6.ipv6_layout.ipv6[12]), OFFLOAD(IPV6_HOPLIMIT, 8, U8_MAX, ip6.hop_limit, 0, ttl_hoplimit), - OFFLOAD(IP_DSCP, 16, 0xc00f, ip6, 0, ip_dscp), + OFFLOAD(IP_DSCP, 16, 0x0fc0, ip6, 0, ip_dscp), OFFLOAD(TCP_SPORT, 16, U16_MAX, tcp.source, 0, tcp_sport), OFFLOAD(TCP_DPORT, 16, U16_MAX, tcp.dest, 0, tcp_dport), @@ -3158,21 +3158,31 @@ static struct mlx5_fields fields[] = { OFFLOAD(UDP_DPORT, 16, U16_MAX, udp.dest, 0, udp_dport), }; -static unsigned long mask_to_le(unsigned long mask, int size) +static u32 mask_field_get(void *mask, struct mlx5_fields *f) { - __be32 mask_be32; - __be16 mask_be16; - - if (size == 32) { - mask_be32 = (__force __be32)(mask); - mask = (__force unsigned long)cpu_to_le32(be32_to_cpu(mask_be32)); - } else if (size == 16) { - mask_be32 = (__force __be32)(mask); - mask_be16 = *(__be16 *)&mask_be32; - mask = (__force unsigned long)cpu_to_le16(be16_to_cpu(mask_be16)); + switch (f->field_bsize) { + case 32: + return be32_to_cpu(*(__be32 *)mask) & f->field_mask; + case 16: + return be16_to_cpu(*(__be16 *)mask) & (u16)f->field_mask; + default: + return *(u8 *)mask & (u8)f->field_mask; } +} - return mask; +static void mask_field_clear(void *mask, struct mlx5_fields *f) +{ + switch (f->field_bsize) { + case 32: + *(__be32 *)mask &= ~cpu_to_be32(f->field_mask); + break; + case 16: + *(__be16 *)mask &= ~cpu_to_be16((u16)f->field_mask); + break; + default: + *(u8 *)mask &= ~(u8)f->field_mask; + break; + } } static int offload_pedit_fields(struct mlx5e_priv *priv, @@ -3184,11 +3194,12 @@ static int offload_pedit_fields(struct mlx5e_priv *priv, struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals; struct pedit_headers_action *hdrs = parse_attr->hdrs; void *headers_c, *headers_v, *action, *vals_p; - u32 *s_masks_p, *a_masks_p, s_mask, a_mask; struct mlx5e_tc_mod_hdr_acts *mod_acts; - unsigned long mask, field_mask; + void *s_masks_p, *a_masks_p; int i, first, last, next_z; struct mlx5_fields *f; + unsigned long mask; + u32 s_mask, a_mask; u8 cmd; mod_acts = &parse_attr->mod_hdr_acts; @@ -3204,15 +3215,11 @@ static int offload_pedit_fields(struct mlx5e_priv *priv, bool skip; f = &fields[i]; - /* avoid seeing bits set from previous iterations */ - s_mask = 0; - a_mask = 0; - s_masks_p = (void *)set_masks + f->offset; a_masks_p = (void *)add_masks + f->offset; - s_mask = *s_masks_p & f->field_mask; - a_mask = *a_masks_p & f->field_mask; + s_mask = mask_field_get(s_masks_p, f); + a_mask = mask_field_get(a_masks_p, f); if (!s_mask && !a_mask) /* nothing to offload here */ continue; @@ -3239,22 +3246,20 @@ static int offload_pedit_fields(struct mlx5e_priv *priv, match_mask, f->field_bsize)) skip = true; /* clear to denote we consumed this field */ - *s_masks_p &= ~f->field_mask; + mask_field_clear(s_masks_p, f); } else { cmd = MLX5_ACTION_TYPE_ADD; mask = a_mask; vals_p = (void *)add_vals + f->offset; /* add 0 is no change */ - if ((*(u32 *)vals_p & f->field_mask) == 0) + if (!mask_field_get(vals_p, f)) skip = true; /* clear to denote we consumed this field */ - *a_masks_p &= ~f->field_mask; + mask_field_clear(a_masks_p, f); } if (skip) continue; - mask = mask_to_le(mask, f->field_bsize); - first = find_first_bit(&mask, f->field_bsize); next_z = find_next_zero_bit(&mask, f->field_bsize, first); last = find_last_bit(&mask, f->field_bsize); @@ -3281,10 +3286,9 @@ static int offload_pedit_fields(struct mlx5e_priv *priv, MLX5_SET(set_action_in, action, field, f->field); if (cmd == MLX5_ACTION_TYPE_SET) { + unsigned long field_mask = f->field_mask; int start; - field_mask = mask_to_le(f->field_mask, f->field_bsize); - /* if field is bit sized it can start not from first bit */ start = find_first_bit(&field_mask, f->field_bsize); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index d41435c22ce5..f0b506e562df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -399,9 +399,9 @@ mlx5e_txwqe_complete(struct mlx5e_txqsq *sq, struct sk_buff *skb, u8 metadata_index = be32_to_cpu(eseg->flow_table_metadata); mlx5e_skb_cb_hwtstamp_init(skb); - mlx5e_ptpsq_track_metadata(sq->ptpsq, metadata_index); mlx5e_ptp_metadata_map_put(&sq->ptpsq->metadata_map, skb, metadata_index); + mlx5e_ptpsq_track_metadata(sq->ptpsq, metadata_index); if (!netif_tx_queue_stopped(sq->txq) && mlx5e_ptpsq_metadata_freelist_empty(sq->ptpsq)) { netif_tx_stop_queue(sq->txq); @@ -494,10 +494,10 @@ mlx5e_sq_xmit_wqe(struct mlx5e_txqsq *sq, struct sk_buff *skb, err_drop: stats->dropped++; - dev_kfree_skb_any(skb); if (unlikely(sq->ptpsq && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) mlx5e_ptp_metadata_fifo_push(&sq->ptpsq->metadata_freelist, be32_to_cpu(eseg->flow_table_metadata)); + dev_kfree_skb_any(skb); mlx5e_tx_flush(sq); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index ea0405e0a43f..40a6cb052a2d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -885,11 +885,14 @@ static void comp_irq_release_sf(struct mlx5_core_dev *dev, u16 vecidx) { struct mlx5_eq_table *table = dev->priv.eq_table; struct mlx5_irq *irq; + int cpu; irq = xa_load(&table->comp_irqs, vecidx); if (!irq) return; + cpu = cpumask_first(mlx5_irq_get_affinity_mask(irq)); + cpumask_clear_cpu(cpu, &table->used_cpus); xa_erase(&table->comp_irqs, vecidx); mlx5_irq_affinity_irq_release(dev, irq); } @@ -897,16 +900,26 @@ static void comp_irq_release_sf(struct mlx5_core_dev *dev, u16 vecidx) static int comp_irq_request_sf(struct mlx5_core_dev *dev, u16 vecidx) { struct mlx5_eq_table *table = dev->priv.eq_table; + struct mlx5_irq_pool *pool = mlx5_irq_pool_get(dev); + struct irq_affinity_desc af_desc = {}; struct mlx5_irq *irq; - irq = mlx5_irq_affinity_irq_request_auto(dev, &table->used_cpus, vecidx); - if (IS_ERR(irq)) { - /* In case SF irq pool does not exist, fallback to the PF irqs*/ - if (PTR_ERR(irq) == -ENOENT) - return comp_irq_request_pci(dev, vecidx); + /* In case SF irq pool does not exist, fallback to the PF irqs*/ + if (!mlx5_irq_pool_is_sf_pool(pool)) + return comp_irq_request_pci(dev, vecidx); + af_desc.is_managed = 1; + cpumask_copy(&af_desc.mask, cpu_online_mask); + cpumask_andnot(&af_desc.mask, &af_desc.mask, &table->used_cpus); + irq = mlx5_irq_affinity_request(pool, &af_desc); + if (IS_ERR(irq)) return PTR_ERR(irq); - } + + cpumask_or(&table->used_cpus, &table->used_cpus, mlx5_irq_get_affinity_mask(irq)); + mlx5_core_dbg(pool->dev, "IRQ %u mapped to cpu %*pbl, %u EQs on this irq\n", + pci_irq_vector(dev->pdev, mlx5_irq_get_index(irq)), + cpumask_pr_args(mlx5_irq_get_affinity_mask(irq)), + mlx5_irq_read_locked(irq) / MLX5_EQ_REFS_PER_IRQ); return xa_err(xa_store(&table->comp_irqs, vecidx, irq, GFP_KERNEL)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index b296ac52a439..88236e75fd90 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -984,7 +984,8 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *on_esw, dest.vport.flags |= MLX5_FLOW_DEST_VPORT_VHCA_ID; flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; - if (rep->vport == MLX5_VPORT_UPLINK && on_esw->offloads.ft_ipsec_tx_pol) { + if (rep->vport == MLX5_VPORT_UPLINK && + on_esw == from_esw && on_esw->offloads.ft_ipsec_tx_pol) { dest.ft = on_esw->offloads.ft_ipsec_tx_pol; flow_act.flags = FLOW_ACT_IGNORE_FLOW_LEVEL; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c index 047d5fed5f89..612e666ec263 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/irq_affinity.c @@ -168,45 +168,3 @@ void mlx5_irq_affinity_irq_release(struct mlx5_core_dev *dev, struct mlx5_irq *i if (pool->irqs_per_cpu) cpu_put(pool, cpu); } - -/** - * mlx5_irq_affinity_irq_request_auto - request one IRQ for mlx5 device. - * @dev: mlx5 device that is requesting the IRQ. - * @used_cpus: cpumask of bounded cpus by the device - * @vecidx: vector index to request an IRQ for. - * - * Each IRQ is bounded to at most 1 CPU. - * This function is requesting an IRQ according to the default assignment. - * The default assignment policy is: - * - request the least loaded IRQ which is not bound to any - * CPU of the previous IRQs requested. - * - * On success, this function updates used_cpus mask and returns an irq pointer. - * In case of an error, an appropriate error pointer is returned. - */ -struct mlx5_irq *mlx5_irq_affinity_irq_request_auto(struct mlx5_core_dev *dev, - struct cpumask *used_cpus, u16 vecidx) -{ - struct mlx5_irq_pool *pool = mlx5_irq_pool_get(dev); - struct irq_affinity_desc af_desc = {}; - struct mlx5_irq *irq; - - if (!mlx5_irq_pool_is_sf_pool(pool)) - return ERR_PTR(-ENOENT); - - af_desc.is_managed = 1; - cpumask_copy(&af_desc.mask, cpu_online_mask); - cpumask_andnot(&af_desc.mask, &af_desc.mask, used_cpus); - irq = mlx5_irq_affinity_request(pool, &af_desc); - - if (IS_ERR(irq)) - return irq; - - cpumask_or(used_cpus, used_cpus, mlx5_irq_get_affinity_mask(irq)); - mlx5_core_dbg(pool->dev, "IRQ %u mapped to cpu %*pbl, %u EQs on this irq\n", - pci_irq_vector(dev->pdev, mlx5_irq_get_index(irq)), - cpumask_pr_args(mlx5_irq_get_affinity_mask(irq)), - mlx5_irq_read_locked(irq) / MLX5_EQ_REFS_PER_IRQ); - - return irq; -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index aa29f09e8356..0c83ef174275 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -384,7 +384,12 @@ static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) static int mlx5_ptp_adjphase(struct ptp_clock_info *ptp, s32 delta) { - return mlx5_ptp_adjtime(ptp, delta); + struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, ptp_info); + struct mlx5_core_dev *mdev; + + mdev = container_of(clock, struct mlx5_core_dev, clock); + + return mlx5_ptp_adjtime_real_time(mdev, delta); } static int mlx5_ptp_freq_adj_real_time(struct mlx5_core_dev *mdev, long scaled_ppm) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c index 653648216730..4dcf995cb1a2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c @@ -28,7 +28,7 @@ struct mlx5_irq { struct atomic_notifier_head nh; cpumask_var_t mask; - char name[MLX5_MAX_IRQ_NAME]; + char name[MLX5_MAX_IRQ_FORMATTED_NAME]; struct mlx5_irq_pool *pool; int refcount; struct msi_map map; @@ -292,8 +292,8 @@ struct mlx5_irq *mlx5_irq_alloc(struct mlx5_irq_pool *pool, int i, else irq_sf_set_name(pool, name, i); ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh); - snprintf(irq->name, MLX5_MAX_IRQ_NAME, - "%s@pci:%s", name, pci_name(dev->pdev)); + snprintf(irq->name, MLX5_MAX_IRQ_FORMATTED_NAME, + MLX5_IRQ_NAME_FORMAT_STR, name, pci_name(dev->pdev)); err = request_irq(irq->map.virq, irq_int_handler, 0, irq->name, &irq->nh); if (err) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h index d3a77a0ab848..c4d377f8df30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h @@ -7,6 +7,9 @@ #include <linux/mlx5/driver.h> #define MLX5_MAX_IRQ_NAME (32) +#define MLX5_IRQ_NAME_FORMAT_STR ("%s@pci:%s") +#define MLX5_MAX_IRQ_FORMATTED_NAME \ + (MLX5_MAX_IRQ_NAME + sizeof(MLX5_IRQ_NAME_FORMAT_STR)) /* max irq_index is 2047, so four chars */ #define MLX5_MAX_IRQ_IDX_CHARS (4) #define MLX5_EQ_REFS_PER_IRQ (2) 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 6ea88a581804..e3ec559369fa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c @@ -57,7 +57,8 @@ static const char *dr_action_id_to_str(enum mlx5dr_action_type action_id) static bool mlx5dr_action_supp_fwd_fdb_multi_ft(struct mlx5_core_dev *dev) { - return (MLX5_CAP_ESW_FLOWTABLE(dev, fdb_multi_path_any_table_limit_regc) || + return (MLX5_CAP_GEN(dev, steering_format_version) < MLX5_STEERING_FORMAT_CONNECTX_6DX || + MLX5_CAP_ESW_FLOWTABLE(dev, fdb_multi_path_any_table_limit_regc) || MLX5_CAP_ESW_FLOWTABLE(dev, fdb_multi_path_any_table)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c index 4e8527a724f5..6fa06ba2d346 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c @@ -52,7 +52,6 @@ struct dr_qp_init_attr { u32 cqn; u32 pdn; u32 max_send_wr; - u32 max_send_sge; struct mlx5_uars_page *uar; u8 isolate_vl_tc:1; }; @@ -247,37 +246,6 @@ static int dr_poll_cq(struct mlx5dr_cq *dr_cq, int ne) return err == CQ_POLL_ERR ? err : npolled; } -static int dr_qp_get_args_update_send_wqe_size(struct dr_qp_init_attr *attr) -{ - return roundup_pow_of_two(sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_flow_update_ctrl_seg) + - sizeof(struct mlx5_wqe_header_modify_argument_update_seg)); -} - -/* We calculate for specific RC QP with the required functionality */ -static int dr_qp_calc_rc_send_wqe(struct dr_qp_init_attr *attr) -{ - int update_arg_size; - int inl_size = 0; - int tot_size; - int size; - - update_arg_size = dr_qp_get_args_update_send_wqe_size(attr); - - size = sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_raddr_seg); - inl_size = size + ALIGN(sizeof(struct mlx5_wqe_inline_seg) + - DR_STE_SIZE, 16); - - size += attr->max_send_sge * sizeof(struct mlx5_wqe_data_seg); - - size = max(size, update_arg_size); - - tot_size = max(size, inl_size); - - return ALIGN(tot_size, MLX5_SEND_WQE_BB); -} - static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev, struct dr_qp_init_attr *attr) { @@ -285,7 +253,6 @@ static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev, u32 temp_qpc[MLX5_ST_SZ_DW(qpc)] = {}; struct mlx5_wq_param wqp; struct mlx5dr_qp *dr_qp; - int wqe_size; int inlen; void *qpc; void *in; @@ -365,15 +332,6 @@ static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev, if (err) goto err_in; dr_qp->uar = attr->uar; - wqe_size = dr_qp_calc_rc_send_wqe(attr); - dr_qp->max_inline_data = min(wqe_size - - (sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_raddr_seg) + - sizeof(struct mlx5_wqe_inline_seg)), - (2 * MLX5_SEND_WQE_BB - - (sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_raddr_seg) + - sizeof(struct mlx5_wqe_inline_seg)))); return dr_qp; @@ -437,48 +395,8 @@ dr_rdma_handle_flow_access_arg_segments(struct mlx5_wqe_ctrl_seg *wq_ctrl, MLX5_SEND_WQE_DS; } -static int dr_set_data_inl_seg(struct mlx5dr_qp *dr_qp, - struct dr_data_seg *data_seg, void *wqe) -{ - int inline_header_size = sizeof(struct mlx5_wqe_ctrl_seg) + - sizeof(struct mlx5_wqe_raddr_seg) + - sizeof(struct mlx5_wqe_inline_seg); - struct mlx5_wqe_inline_seg *seg; - int left_space; - int inl = 0; - void *addr; - int len; - int idx; - - seg = wqe; - wqe += sizeof(*seg); - addr = (void *)(unsigned long)(data_seg->addr); - len = data_seg->length; - inl += len; - left_space = MLX5_SEND_WQE_BB - inline_header_size; - - if (likely(len > left_space)) { - memcpy(wqe, addr, left_space); - len -= left_space; - addr += left_space; - idx = (dr_qp->sq.pc + 1) & (dr_qp->sq.wqe_cnt - 1); - wqe = mlx5_wq_cyc_get_wqe(&dr_qp->wq.sq, idx); - } - - memcpy(wqe, addr, len); - - if (likely(inl)) { - seg->byte_count = cpu_to_be32(inl | MLX5_INLINE_SEG); - return DIV_ROUND_UP(inl + sizeof(seg->byte_count), - MLX5_SEND_WQE_DS); - } else { - return 0; - } -} - static void -dr_rdma_handle_icm_write_segments(struct mlx5dr_qp *dr_qp, - struct mlx5_wqe_ctrl_seg *wq_ctrl, +dr_rdma_handle_icm_write_segments(struct mlx5_wqe_ctrl_seg *wq_ctrl, u64 remote_addr, u32 rkey, struct dr_data_seg *data_seg, @@ -494,17 +412,15 @@ dr_rdma_handle_icm_write_segments(struct mlx5dr_qp *dr_qp, wq_raddr->reserved = 0; wq_dseg = (void *)(wq_raddr + 1); - /* WQE ctrl segment + WQE remote addr segment */ - *size = (sizeof(*wq_ctrl) + sizeof(*wq_raddr)) / MLX5_SEND_WQE_DS; - if (data_seg->send_flags & IB_SEND_INLINE) { - *size += dr_set_data_inl_seg(dr_qp, data_seg, wq_dseg); - } else { - wq_dseg->byte_count = cpu_to_be32(data_seg->length); - wq_dseg->lkey = cpu_to_be32(data_seg->lkey); - wq_dseg->addr = cpu_to_be64(data_seg->addr); - *size += sizeof(*wq_dseg) / MLX5_SEND_WQE_DS; /* WQE data segment */ - } + wq_dseg->byte_count = cpu_to_be32(data_seg->length); + wq_dseg->lkey = cpu_to_be32(data_seg->lkey); + wq_dseg->addr = cpu_to_be64(data_seg->addr); + + *size = (sizeof(*wq_ctrl) + /* WQE ctrl segment */ + sizeof(*wq_dseg) + /* WQE data segment */ + sizeof(*wq_raddr)) / /* WQE remote addr segment */ + MLX5_SEND_WQE_DS; } static void dr_set_ctrl_seg(struct mlx5_wqe_ctrl_seg *wq_ctrl, @@ -535,7 +451,7 @@ static void dr_rdma_segments(struct mlx5dr_qp *dr_qp, u64 remote_addr, switch (opcode) { case MLX5_OPCODE_RDMA_READ: case MLX5_OPCODE_RDMA_WRITE: - dr_rdma_handle_icm_write_segments(dr_qp, wq_ctrl, remote_addr, + dr_rdma_handle_icm_write_segments(wq_ctrl, remote_addr, rkey, data_seg, &size); break; case MLX5_OPCODE_FLOW_TBL_ACCESS: @@ -656,7 +572,7 @@ static void dr_fill_write_args_segs(struct mlx5dr_send_ring *send_ring, if (send_ring->pending_wqe % send_ring->signal_th == 0) send_info->write.send_flags |= IB_SEND_SIGNALED; else - send_info->write.send_flags &= ~IB_SEND_SIGNALED; + send_info->write.send_flags = 0; } static void dr_fill_write_icm_segs(struct mlx5dr_domain *dmn, @@ -680,13 +596,9 @@ static void dr_fill_write_icm_segs(struct mlx5dr_domain *dmn, } send_ring->pending_wqe++; - if (!send_info->write.lkey) - send_info->write.send_flags |= IB_SEND_INLINE; if (send_ring->pending_wqe % send_ring->signal_th == 0) send_info->write.send_flags |= IB_SEND_SIGNALED; - else - send_info->write.send_flags &= ~IB_SEND_SIGNALED; send_ring->pending_wqe++; send_info->read.length = send_info->write.length; @@ -696,9 +608,9 @@ static void dr_fill_write_icm_segs(struct mlx5dr_domain *dmn, send_info->read.lkey = send_ring->sync_mr->mkey; if (send_ring->pending_wqe % send_ring->signal_th == 0) - send_info->read.send_flags |= IB_SEND_SIGNALED; + send_info->read.send_flags = IB_SEND_SIGNALED; else - send_info->read.send_flags &= ~IB_SEND_SIGNALED; + send_info->read.send_flags = 0; } static void dr_fill_data_segs(struct mlx5dr_domain *dmn, @@ -1345,7 +1257,6 @@ int mlx5dr_send_ring_alloc(struct mlx5dr_domain *dmn) dmn->send_ring->cq->qp = dmn->send_ring->qp; dmn->info.max_send_wr = QUEUE_SIZE; - init_attr.max_send_sge = 1; dmn->info.max_inline_size = min(dmn->send_ring->qp->max_inline_data, DR_STE_SIZE); diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 0c76c162b8a9..295366a85c63 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -624,6 +624,7 @@ struct rtl8169_private { unsigned supports_gmii:1; unsigned aspm_manageable:1; + unsigned dash_enabled:1; dma_addr_t counters_phys_addr; struct rtl8169_counters *counters; struct rtl8169_tc_offsets tc_offset; @@ -1253,14 +1254,26 @@ static bool r8168ep_check_dash(struct rtl8169_private *tp) return r8168ep_ocp_read(tp, 0x128) & BIT(0); } -static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp) +static bool rtl_dash_is_enabled(struct rtl8169_private *tp) +{ + switch (tp->dash_type) { + case RTL_DASH_DP: + return r8168dp_check_dash(tp); + case RTL_DASH_EP: + return r8168ep_check_dash(tp); + default: + return false; + } +} + +static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp) { switch (tp->mac_version) { case RTL_GIGA_MAC_VER_28: case RTL_GIGA_MAC_VER_31: - return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE; + return RTL_DASH_DP; case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: - return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE; + return RTL_DASH_EP; default: return RTL_DASH_NONE; } @@ -1453,7 +1466,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) device_set_wakeup_enable(tp_to_dev(tp), wolopts); - if (tp->dash_type == RTL_DASH_NONE) { + if (!tp->dash_enabled) { rtl_set_d3_pll_down(tp, !wolopts); tp->dev->wol_enabled = wolopts ? 1 : 0; } @@ -2512,7 +2525,7 @@ static void rtl_wol_enable_rx(struct rtl8169_private *tp) static void rtl_prepare_power_down(struct rtl8169_private *tp) { - if (tp->dash_type != RTL_DASH_NONE) + if (tp->dash_enabled) return; if (tp->mac_version == RTL_GIGA_MAC_VER_32 || @@ -2586,9 +2599,7 @@ static void rtl_set_rx_mode(struct net_device *dev) rx_mode &= ~AcceptMulticast; } else if (netdev_mc_count(dev) > MC_FILTER_LIMIT || dev->flags & IFF_ALLMULTI || - tp->mac_version == RTL_GIGA_MAC_VER_35 || - tp->mac_version == RTL_GIGA_MAC_VER_46 || - tp->mac_version == RTL_GIGA_MAC_VER_48) { + tp->mac_version == RTL_GIGA_MAC_VER_35) { /* accept all multicasts */ } else if (netdev_mc_empty(dev)) { rx_mode &= ~AcceptMulticast; @@ -4648,10 +4659,16 @@ static void rtl8169_down(struct rtl8169_private *tp) rtl8169_cleanup(tp); rtl_disable_exit_l1(tp); rtl_prepare_power_down(tp); + + if (tp->dash_type != RTL_DASH_NONE) + rtl8168_driver_stop(tp); } static void rtl8169_up(struct rtl8169_private *tp) { + if (tp->dash_type != RTL_DASH_NONE) + rtl8168_driver_start(tp); + pci_set_master(tp->pci_dev); phy_init_hw(tp->phydev); phy_resume(tp->phydev); @@ -4869,7 +4886,7 @@ static int rtl8169_runtime_idle(struct device *device) { struct rtl8169_private *tp = dev_get_drvdata(device); - if (tp->dash_type != RTL_DASH_NONE) + if (tp->dash_enabled) return -EBUSY; if (!netif_running(tp->dev) || !netif_carrier_ok(tp->dev)) @@ -4895,8 +4912,7 @@ static void rtl_shutdown(struct pci_dev *pdev) /* Restore original MAC address */ rtl_rar_set(tp, tp->dev->perm_addr); - if (system_state == SYSTEM_POWER_OFF && - tp->dash_type == RTL_DASH_NONE) { + if (system_state == SYSTEM_POWER_OFF && !tp->dash_enabled) { pci_wake_from_d3(pdev, tp->saved_wolopts); pci_set_power_state(pdev, PCI_D3hot); } @@ -5254,7 +5270,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); tp->aspm_manageable = !rc; - tp->dash_type = rtl_check_dash(tp); + tp->dash_type = rtl_get_dash_type(tp); + tp->dash_enabled = rtl_dash_is_enabled(tp); tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; @@ -5325,7 +5342,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* configure chip for default features */ rtl8169_set_features(dev, dev->features); - if (tp->dash_type == RTL_DASH_NONE) { + if (!tp->dash_enabled) { rtl_set_d3_pll_down(tp, true); } else { rtl_set_d3_pll_down(tp, false); @@ -5365,7 +5382,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) "ok" : "ko"); if (tp->dash_type != RTL_DASH_NONE) { - netdev_info(dev, "DASH enabled\n"); + netdev_info(dev, "DASH %s\n", + tp->dash_enabled ? "enabled" : "disabled"); rtl8168_driver_start(tp); } diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index a2b9e289aa36..85dcda51df05 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -280,7 +280,7 @@ config DWMAC_INTEL config DWMAC_LOONGSON tristate "Loongson PCI DWMAC support" default MACH_LOONGSON64 - depends on STMMAC_ETH && PCI + depends on (MACH_LOONGSON64 || COMPILE_TEST) && STMMAC_ETH && PCI depends on COMMON_CLK help This selects the LOONGSON PCI bus support for the stmmac driver, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3e50fd53a617..2afb2bd25977 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -5293,6 +5293,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) dma_dir = page_pool_get_dma_dir(rx_q->page_pool); buf_sz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; + limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit); if (netif_msg_rx_status(priv)) { void *rx_head; @@ -5328,10 +5329,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) len = 0; } +read_again: if (count >= limit) break; -read_again: buf1_len = 0; buf2_len = 0; entry = next_entry; diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c index 6c4b64227ac8..411898a4f38c 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c @@ -2063,7 +2063,7 @@ static int prueth_probe(struct platform_device *pdev) &prueth->shram); if (ret) { dev_err(dev, "unable to get PRUSS SHRD RAM2: %d\n", ret); - pruss_put(prueth->pruss); + goto put_pruss; } prueth->sram_pool = of_gen_pool_get(np, "sram", 0); @@ -2105,10 +2105,7 @@ static int prueth_probe(struct platform_device *pdev) prueth->iep1 = icss_iep_get_idx(np, 1); if (IS_ERR(prueth->iep1)) { ret = dev_err_probe(dev, PTR_ERR(prueth->iep1), "iep1 get failed\n"); - icss_iep_put(prueth->iep0); - prueth->iep0 = NULL; - prueth->iep1 = NULL; - goto free_pool; + goto put_iep0; } if (prueth->pdata.quirk_10m_link_issue) { @@ -2205,6 +2202,12 @@ netdev_exit: exit_iep: if (prueth->pdata.quirk_10m_link_issue) icss_iep_exit_fw(prueth->iep1); + icss_iep_put(prueth->iep1); + +put_iep0: + icss_iep_put(prueth->iep0); + prueth->iep0 = NULL; + prueth->iep1 = NULL; free_pool: gen_pool_free(prueth->sram_pool, @@ -2212,6 +2215,8 @@ free_pool: put_mem: pruss_release_mem_region(prueth->pruss, &prueth->shram); + +put_pruss: pruss_put(prueth->pruss); put_cores: diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index a3c5de9d547a..533e912af089 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -1769,10 +1769,12 @@ int wx_sw_init(struct wx *wx) wx->subsystem_device_id = pdev->subsystem_device; } else { err = wx_flash_read_dword(wx, 0xfffdc, &ssid); - if (!err) - wx->subsystem_device_id = swab16((u16)ssid); + if (err < 0) { + wx_err(wx, "read of internal subsystem device id failed\n"); + return err; + } - return err; + wx->subsystem_device_id = swab16((u16)ssid); } wx->mac_table = kcalloc(wx->mac.num_rar_entries, diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index 3d43f808c86b..8db804543e66 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -121,10 +121,8 @@ static int ngbe_sw_init(struct wx *wx) /* PCI config space info */ err = wx_sw_init(wx); - if (err < 0) { - wx_err(wx, "read of internal subsystem device id failed\n"); + if (err < 0) return err; - } /* mac type, phy type , oem type */ ngbe_init_type_code(wx); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 70f0b5c01dac..526250102db2 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -364,10 +364,8 @@ static int txgbe_sw_init(struct wx *wx) /* PCI config space info */ err = wx_sw_init(wx); - if (err < 0) { - wx_err(wx, "read of internal subsystem device id failed\n"); + if (err < 0) return err; - } txgbe_init_type_code(wx); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 82d0d44b2b02..bf6e33990490 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -822,7 +822,7 @@ axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (lp->features & XAE_FEATURE_FULL_TX_CSUM) { /* Tx Full Checksum Offload Enabled */ cur_p->app0 |= 2; - } else if (lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) { + } else if (lp->features & XAE_FEATURE_PARTIAL_TX_CSUM) { csum_start_off = skb_transport_offset(skb); csum_index_off = csum_start_off + skb->csum_offset; /* Tx Partial Checksum Offload Enabled */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 3ba3c8fb28a5..706ea5263e87 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -2206,9 +2206,6 @@ static int netvsc_vf_join(struct net_device *vf_netdev, goto upper_link_failed; } - /* set slave flag before open to prevent IPv6 addrconf */ - vf_netdev->flags |= IFF_SLAVE; - schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT); call_netdevice_notifiers(NETDEV_JOIN, vf_netdev); @@ -2315,16 +2312,18 @@ static struct net_device *get_netvsc_byslot(const struct net_device *vf_netdev) } - /* Fallback path to check synthetic vf with - * help of mac addr + /* Fallback path to check synthetic vf with help of mac addr. + * Because this function can be called before vf_netdev is + * initialized (NETDEV_POST_INIT) when its perm_addr has not been copied + * from dev_addr, also try to match to its dev_addr. + * Note: On Hyper-V and Azure, it's not possible to set a MAC address + * on a VF that matches to the MAC of a unrelated NETVSC device. */ list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) { ndev = hv_get_drvdata(ndev_ctx->device_ctx); - if (ether_addr_equal(vf_netdev->perm_addr, ndev->perm_addr)) { - netdev_notice(vf_netdev, - "falling back to mac addr based matching\n"); + if (ether_addr_equal(vf_netdev->perm_addr, ndev->perm_addr) || + ether_addr_equal(vf_netdev->dev_addr, ndev->perm_addr)) return ndev; - } } netdev_notice(vf_netdev, @@ -2332,6 +2331,19 @@ static struct net_device *get_netvsc_byslot(const struct net_device *vf_netdev) return NULL; } +static int netvsc_prepare_bonding(struct net_device *vf_netdev) +{ + struct net_device *ndev; + + ndev = get_netvsc_byslot(vf_netdev); + if (!ndev) + return NOTIFY_DONE; + + /* set slave flag before open to prevent IPv6 addrconf */ + vf_netdev->flags |= IFF_SLAVE; + return NOTIFY_DONE; +} + static int netvsc_register_vf(struct net_device *vf_netdev) { struct net_device_context *net_device_ctx; @@ -2531,15 +2543,6 @@ static int netvsc_probe(struct hv_device *dev, goto devinfo_failed; } - nvdev = rndis_filter_device_add(dev, device_info); - if (IS_ERR(nvdev)) { - ret = PTR_ERR(nvdev); - netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); - goto rndis_failed; - } - - eth_hw_addr_set(net, device_info->mac_adr); - /* We must get rtnl lock before scheduling nvdev->subchan_work, * otherwise netvsc_subchan_work() can get rtnl lock first and wait * all subchannels to show up, but that may not happen because @@ -2547,9 +2550,23 @@ static int netvsc_probe(struct hv_device *dev, * -> ... -> device_add() -> ... -> __device_attach() can't get * the device lock, so all the subchannels can't be processed -- * finally netvsc_subchan_work() hangs forever. + * + * The rtnl lock also needs to be held before rndis_filter_device_add() + * which advertises nvsp_2_vsc_capability / sriov bit, and triggers + * VF NIC offering and registering. If VF NIC finished register_netdev() + * earlier it may cause name based config failure. */ rtnl_lock(); + nvdev = rndis_filter_device_add(dev, device_info); + if (IS_ERR(nvdev)) { + ret = PTR_ERR(nvdev); + netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); + goto rndis_failed; + } + + eth_hw_addr_set(net, device_info->mac_adr); + if (nvdev->num_chn > 1) schedule_work(&nvdev->subchan_work); @@ -2586,9 +2603,9 @@ static int netvsc_probe(struct hv_device *dev, return 0; register_failed: - rtnl_unlock(); rndis_filter_device_remove(dev, nvdev); rndis_failed: + rtnl_unlock(); netvsc_devinfo_put(device_info); devinfo_failed: free_percpu(net_device_ctx->vf_stats); @@ -2753,6 +2770,8 @@ static int netvsc_netdev_event(struct notifier_block *this, return NOTIFY_DONE; switch (event) { + case NETDEV_POST_INIT: + return netvsc_prepare_bonding(event_dev); case NETDEV_REGISTER: return netvsc_register_vf(event_dev); case NETDEV_UNREGISTER: @@ -2788,12 +2807,17 @@ static int __init netvsc_drv_init(void) } netvsc_ring_bytes = ring_size * PAGE_SIZE; + register_netdevice_notifier(&netvsc_netdev_notifier); + ret = vmbus_driver_register(&netvsc_drv); if (ret) - return ret; + goto err_vmbus_reg; - register_netdevice_notifier(&netvsc_netdev_notifier); return 0; + +err_vmbus_reg: + unregister_netdevice_notifier(&netvsc_netdev_notifier); + return ret; } MODULE_LICENSE("GPL"); diff --git a/drivers/net/ipa/reg/gsi_reg-v5.0.c b/drivers/net/ipa/reg/gsi_reg-v5.0.c index d7b81a36d673..145eb0bd096d 100644 --- a/drivers/net/ipa/reg/gsi_reg-v5.0.c +++ b/drivers/net/ipa/reg/gsi_reg-v5.0.c @@ -78,7 +78,7 @@ REG_STRIDE_FIELDS(EV_CH_E_CNTXT_0, ev_ch_e_cntxt_0, 0x0001c000 + 0x12000 * GSI_EE_AP, 0x80); static const u32 reg_ev_ch_e_cntxt_1_fmask[] = { - [R_LENGTH] = GENMASK(19, 0), + [R_LENGTH] = GENMASK(23, 0), }; REG_STRIDE_FIELDS(EV_CH_E_CNTXT_1, ev_ch_e_cntxt_1, diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 21e9cac73121..2d5b021b4ea6 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -411,7 +411,7 @@ struct ipvl_addr *ipvlan_addr_lookup(struct ipvl_port *port, void *lyr3h, return addr; } -static int ipvlan_process_v4_outbound(struct sk_buff *skb) +static noinline_for_stack int ipvlan_process_v4_outbound(struct sk_buff *skb) { const struct iphdr *ip4h = ip_hdr(skb); struct net_device *dev = skb->dev; @@ -453,13 +453,11 @@ out: } #if IS_ENABLED(CONFIG_IPV6) -static int ipvlan_process_v6_outbound(struct sk_buff *skb) + +static noinline_for_stack int +ipvlan_route_v6_outbound(struct net_device *dev, struct sk_buff *skb) { const struct ipv6hdr *ip6h = ipv6_hdr(skb); - struct net_device *dev = skb->dev; - struct net *net = dev_net(dev); - struct dst_entry *dst; - int err, ret = NET_XMIT_DROP; struct flowi6 fl6 = { .flowi6_oif = dev->ifindex, .daddr = ip6h->daddr, @@ -469,27 +467,38 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) .flowi6_mark = skb->mark, .flowi6_proto = ip6h->nexthdr, }; + struct dst_entry *dst; + int err; - dst = ip6_route_output(net, NULL, &fl6); - if (dst->error) { - ret = dst->error; + dst = ip6_route_output(dev_net(dev), NULL, &fl6); + err = dst->error; + if (err) { dst_release(dst); - goto err; + return err; } skb_dst_set(skb, dst); + return 0; +} + +static int ipvlan_process_v6_outbound(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + int err, ret = NET_XMIT_DROP; + + err = ipvlan_route_v6_outbound(dev, skb); + if (unlikely(err)) { + DEV_STATS_INC(dev, tx_errors); + kfree_skb(skb); + return err; + } memset(IP6CB(skb), 0, sizeof(*IP6CB(skb))); - err = ip6_local_out(net, skb->sk, skb); + err = ip6_local_out(dev_net(dev), skb->sk, skb); if (unlikely(net_xmit_eval(err))) DEV_STATS_INC(dev, tx_errors); else ret = NET_XMIT_SUCCESS; - goto out; -err: - DEV_STATS_INC(dev, tx_errors); - kfree_skb(skb); -out: return ret; } #else diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 02bd201bc7e5..c8da94af4161 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -780,7 +780,7 @@ static void macvlan_change_rx_flags(struct net_device *dev, int change) if (dev->flags & IFF_UP) { if (change & IFF_ALLMULTI) dev_set_allmulti(lowerdev, dev->flags & IFF_ALLMULTI ? 1 : -1); - if (change & IFF_PROMISC) + if (!macvlan_passthru(vlan->port) && change & IFF_PROMISC) dev_set_promiscuity(lowerdev, dev->flags & IFF_PROMISC ? 1 : -1); diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c index 5a0f86f38f09..97bd6705c241 100644 --- a/drivers/net/netkit.c +++ b/drivers/net/netkit.c @@ -7,6 +7,7 @@ #include <linux/filter.h> #include <linux/netfilter_netdev.h> #include <linux/bpf_mprog.h> +#include <linux/indirect_call_wrapper.h> #include <net/netkit.h> #include <net/dst.h> @@ -68,6 +69,7 @@ static netdev_tx_t netkit_xmit(struct sk_buff *skb, struct net_device *dev) netdev_tx_t ret_dev = NET_XMIT_SUCCESS; const struct bpf_mprog_entry *entry; struct net_device *peer; + int len = skb->len; rcu_read_lock(); peer = rcu_dereference(nk->peer); @@ -85,15 +87,22 @@ static netdev_tx_t netkit_xmit(struct sk_buff *skb, struct net_device *dev) case NETKIT_PASS: skb->protocol = eth_type_trans(skb, skb->dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); - __netif_rx(skb); + if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) { + dev_sw_netstats_tx_add(dev, 1, len); + dev_sw_netstats_rx_add(peer, len); + } else { + goto drop_stats; + } break; case NETKIT_REDIRECT: + dev_sw_netstats_tx_add(dev, 1, len); skb_do_redirect(skb); break; case NETKIT_DROP: default: drop: kfree_skb(skb); +drop_stats: dev_core_stats_tx_dropped_inc(dev); ret_dev = NET_XMIT_DROP; break; @@ -169,11 +178,18 @@ out: rcu_read_unlock(); } -static struct net_device *netkit_peer_dev(struct net_device *dev) +INDIRECT_CALLABLE_SCOPE struct net_device *netkit_peer_dev(struct net_device *dev) { return rcu_dereference(netkit_priv(dev)->peer); } +static void netkit_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + dev_fetch_sw_netstats(stats, dev->tstats); + stats->tx_dropped = DEV_STATS_READ(dev, tx_dropped); +} + static void netkit_uninit(struct net_device *dev); static const struct net_device_ops netkit_netdev_ops = { @@ -184,6 +200,7 @@ static const struct net_device_ops netkit_netdev_ops = { .ndo_set_rx_headroom = netkit_set_headroom, .ndo_get_iflink = netkit_get_iflink, .ndo_get_peer_dev = netkit_peer_dev, + .ndo_get_stats64 = netkit_get_stats, .ndo_uninit = netkit_uninit, .ndo_features_check = passthru_features_check, }; @@ -218,6 +235,7 @@ static void netkit_setup(struct net_device *dev) ether_setup(dev); dev->max_mtu = ETH_MAX_MTU; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; dev->flags |= IFF_NOARP; dev->priv_flags &= ~IFF_TX_SKB_SHARING; diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c index ebcdffdf4f0e..52d05ce4a281 100644 --- a/drivers/net/ppp/ppp_synctty.c +++ b/drivers/net/ppp/ppp_synctty.c @@ -453,6 +453,10 @@ ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) case PPPIOCSMRU: if (get_user(val, (int __user *) argp)) break; + if (val > U16_MAX) { + err = -EINVAL; + break; + } if (val < PPP_MRU) val = PPP_MRU; ap->mru = val; @@ -687,7 +691,7 @@ ppp_sync_input(struct syncppp *ap, const u8 *buf, const u8 *flags, int count) /* strip address/control field if present */ p = skb->data; - if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { + if (skb->len >= 2 && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { /* chop off address/control */ if (skb->len < 3) goto err; diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c index a017e9de2119..7b8afa589a53 100644 --- a/drivers/net/usb/aqc111.c +++ b/drivers/net/usb/aqc111.c @@ -1079,17 +1079,17 @@ static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb) u16 pkt_count = 0; u64 desc_hdr = 0; u16 vlan_tag = 0; - u32 skb_len = 0; + u32 skb_len; if (!skb) goto err; - if (skb->len == 0) + skb_len = skb->len; + if (skb_len < sizeof(desc_hdr)) goto err; - skb_len = skb->len; /* RX Descriptor Header */ - skb_trim(skb, skb->len - sizeof(desc_hdr)); + skb_trim(skb, skb_len - sizeof(desc_hdr)); desc_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb)); /* Check these packets */ diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index aff39bf3161d..4ea0e155bb0d 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1583,11 +1583,11 @@ static int ax88179_reset(struct usbnet *dev) *tmp16 = AX_PHYPWR_RSTCTL_IPRL; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); - msleep(200); + msleep(500); *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); - msleep(100); + msleep(200); /* Ethernet PHY Auto Detach*/ ax88179_auto_detach(dev); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 344af3c5c836..e2e181378f41 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1289,6 +1289,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x0168, 4)}, {QMI_FIXED_INTF(0x19d2, 0x0176, 3)}, {QMI_FIXED_INTF(0x19d2, 0x0178, 3)}, + {QMI_FIXED_INTF(0x19d2, 0x0189, 4)}, /* ZTE MF290 */ {QMI_FIXED_INTF(0x19d2, 0x0191, 4)}, /* ZTE EuFi890 */ {QMI_FIXED_INTF(0x19d2, 0x0199, 1)}, /* ZTE MF820S */ {QMI_FIXED_INTF(0x19d2, 0x0200, 1)}, diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 9980517ed8b0..57efb3454c57 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -236,8 +236,8 @@ static void veth_get_ethtool_stats(struct net_device *dev, data[tx_idx + j] += *(u64 *)(base + offset); } } while (u64_stats_fetch_retry(&rq_stats->syncp, start)); - pp_idx = tx_idx + VETH_TQ_STATS_LEN; } + pp_idx = idx + dev->real_num_tx_queues * VETH_TQ_STATS_LEN; page_pool_stats: veth_get_page_pool_stats(dev, &data[pp_idx]); @@ -373,7 +373,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); if (likely(veth_forward_skb(rcv, skb, rq, use_napi) == NET_RX_SUCCESS)) { if (!use_napi) - dev_lstats_add(dev, length); + dev_sw_netstats_tx_add(dev, 1, length); else __veth_xdp_flush(rq); } else { @@ -387,14 +387,6 @@ drop: return ret; } -static u64 veth_stats_tx(struct net_device *dev, u64 *packets, u64 *bytes) -{ - struct veth_priv *priv = netdev_priv(dev); - - dev_lstats_read(dev, packets, bytes); - return atomic64_read(&priv->dropped); -} - static void veth_stats_rx(struct veth_stats *result, struct net_device *dev) { struct veth_priv *priv = netdev_priv(dev); @@ -432,24 +424,24 @@ static void veth_get_stats64(struct net_device *dev, struct veth_priv *priv = netdev_priv(dev); struct net_device *peer; struct veth_stats rx; - u64 packets, bytes; - tot->tx_dropped = veth_stats_tx(dev, &packets, &bytes); - tot->tx_bytes = bytes; - tot->tx_packets = packets; + tot->tx_dropped = atomic64_read(&priv->dropped); + dev_fetch_sw_netstats(tot, dev->tstats); veth_stats_rx(&rx, dev); tot->tx_dropped += rx.xdp_tx_err; tot->rx_dropped = rx.rx_drops + rx.peer_tq_xdp_xmit_err; - tot->rx_bytes = rx.xdp_bytes; - tot->rx_packets = rx.xdp_packets; + tot->rx_bytes += rx.xdp_bytes; + tot->rx_packets += rx.xdp_packets; rcu_read_lock(); peer = rcu_dereference(priv->peer); if (peer) { - veth_stats_tx(peer, &packets, &bytes); - tot->rx_bytes += bytes; - tot->rx_packets += packets; + struct rtnl_link_stats64 tot_peer = {}; + + dev_fetch_sw_netstats(&tot_peer, peer->tstats); + tot->rx_bytes += tot_peer.tx_bytes; + tot->rx_packets += tot_peer.tx_packets; veth_stats_rx(&rx, peer); tot->tx_dropped += rx.peer_tq_xdp_xmit_err; @@ -1506,25 +1498,12 @@ static void veth_free_queues(struct net_device *dev) static int veth_dev_init(struct net_device *dev) { - int err; - - dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); - if (!dev->lstats) - return -ENOMEM; - - err = veth_alloc_queues(dev); - if (err) { - free_percpu(dev->lstats); - return err; - } - - return 0; + return veth_alloc_queues(dev); } static void veth_dev_free(struct net_device *dev) { veth_free_queues(dev); - free_percpu(dev->lstats); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1796,6 +1775,7 @@ static void veth_setup(struct net_device *dev) NETIF_F_HW_VLAN_STAG_RX); dev->needs_free_netdev = true; dev->priv_destructor = veth_dev_free; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; dev->max_mtu = ETH_MAX_MTU; dev->hw_features = VETH_FEATURES; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index db766941b78f..bb95ce43cd97 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -121,22 +121,12 @@ struct net_vrf { int ifindex; }; -struct pcpu_dstats { - u64 tx_pkts; - u64 tx_bytes; - u64 tx_drps; - u64 rx_pkts; - u64 rx_bytes; - u64 rx_drps; - struct u64_stats_sync syncp; -}; - static void vrf_rx_stats(struct net_device *dev, int len) { struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); u64_stats_update_begin(&dstats->syncp); - dstats->rx_pkts++; + dstats->rx_packets++; dstats->rx_bytes += len; u64_stats_update_end(&dstats->syncp); } @@ -161,10 +151,10 @@ static void vrf_get_stats64(struct net_device *dev, do { start = u64_stats_fetch_begin(&dstats->syncp); tbytes = dstats->tx_bytes; - tpkts = dstats->tx_pkts; - tdrops = dstats->tx_drps; + tpkts = dstats->tx_packets; + tdrops = dstats->tx_drops; rbytes = dstats->rx_bytes; - rpkts = dstats->rx_pkts; + rpkts = dstats->rx_packets; } while (u64_stats_fetch_retry(&dstats->syncp, start)); stats->tx_bytes += tbytes; stats->tx_packets += tpkts; @@ -421,7 +411,7 @@ static int vrf_local_xmit(struct sk_buff *skb, struct net_device *dev, if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) vrf_rx_stats(dev, len); else - this_cpu_inc(dev->dstats->rx_drps); + this_cpu_inc(dev->dstats->rx_drops); return NETDEV_TX_OK; } @@ -616,11 +606,11 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); u64_stats_update_begin(&dstats->syncp); - dstats->tx_pkts++; + dstats->tx_packets++; dstats->tx_bytes += len; u64_stats_update_end(&dstats->syncp); } else { - this_cpu_inc(dev->dstats->tx_drps); + this_cpu_inc(dev->dstats->tx_drops); } return ret; @@ -1174,22 +1164,15 @@ static void vrf_dev_uninit(struct net_device *dev) vrf_rtable_release(dev, vrf); vrf_rt6_release(dev, vrf); - - free_percpu(dev->dstats); - dev->dstats = NULL; } static int vrf_dev_init(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); - if (!dev->dstats) - goto out_nomem; - /* create the default dst which points back to us */ if (vrf_rtable_create(dev) != 0) - goto out_stats; + goto out_nomem; if (vrf_rt6_create(dev) != 0) goto out_rth; @@ -1203,9 +1186,6 @@ static int vrf_dev_init(struct net_device *dev) out_rth: vrf_rtable_release(dev, vrf); -out_stats: - free_percpu(dev->dstats); - dev->dstats = NULL; out_nomem: return -ENOMEM; } @@ -1704,6 +1684,8 @@ static void vrf_setup(struct net_device *dev) dev->min_mtu = IPV6_MIN_MTU; dev->max_mtu = IP6_MAX_MTU; dev->mtu = dev->max_mtu; + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; } static int vrf_validate(struct nlattr *tb[], struct nlattr *data[], diff --git a/drivers/net/wireguard/device.c b/drivers/net/wireguard/device.c index 258dcc103921..deb9636b0ecf 100644 --- a/drivers/net/wireguard/device.c +++ b/drivers/net/wireguard/device.c @@ -210,7 +210,7 @@ static netdev_tx_t wg_xmit(struct sk_buff *skb, struct net_device *dev) */ while (skb_queue_len(&peer->staged_packet_queue) > MAX_STAGED_PACKETS) { dev_kfree_skb(__skb_dequeue(&peer->staged_packet_queue)); - ++dev->stats.tx_dropped; + DEV_STATS_INC(dev, tx_dropped); } skb_queue_splice_tail(&packets, &peer->staged_packet_queue); spin_unlock_bh(&peer->staged_packet_queue.lock); @@ -228,7 +228,7 @@ err_icmp: else if (skb->protocol == htons(ETH_P_IPV6)) icmpv6_ndo_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0); err: - ++dev->stats.tx_errors; + DEV_STATS_INC(dev, tx_errors); kfree_skb(skb); return ret; } diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c index 0b3f0c843550..a176653c8861 100644 --- a/drivers/net/wireguard/receive.c +++ b/drivers/net/wireguard/receive.c @@ -416,20 +416,20 @@ dishonest_packet_peer: net_dbg_skb_ratelimited("%s: Packet has unallowed src IP (%pISc) from peer %llu (%pISpfsc)\n", dev->name, skb, peer->internal_id, &peer->endpoint.addr); - ++dev->stats.rx_errors; - ++dev->stats.rx_frame_errors; + DEV_STATS_INC(dev, rx_errors); + DEV_STATS_INC(dev, rx_frame_errors); goto packet_processed; dishonest_packet_type: net_dbg_ratelimited("%s: Packet is neither ipv4 nor ipv6 from peer %llu (%pISpfsc)\n", dev->name, peer->internal_id, &peer->endpoint.addr); - ++dev->stats.rx_errors; - ++dev->stats.rx_frame_errors; + DEV_STATS_INC(dev, rx_errors); + DEV_STATS_INC(dev, rx_frame_errors); goto packet_processed; dishonest_packet_size: net_dbg_ratelimited("%s: Packet has incorrect size from peer %llu (%pISpfsc)\n", dev->name, peer->internal_id, &peer->endpoint.addr); - ++dev->stats.rx_errors; - ++dev->stats.rx_length_errors; + DEV_STATS_INC(dev, rx_errors); + DEV_STATS_INC(dev, rx_length_errors); goto packet_processed; packet_processed: dev_kfree_skb(skb); diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c index 95c853b59e1d..0d48e0f4a1ba 100644 --- a/drivers/net/wireguard/send.c +++ b/drivers/net/wireguard/send.c @@ -333,7 +333,8 @@ err: void wg_packet_purge_staged_packets(struct wg_peer *peer) { spin_lock_bh(&peer->staged_packet_queue.lock); - peer->device->dev->stats.tx_dropped += peer->staged_packet_queue.qlen; + DEV_STATS_ADD(peer->device->dev, tx_dropped, + peer->staged_packet_queue.qlen); __skb_queue_purge(&peer->staged_packet_queue); spin_unlock_bh(&peer->staged_packet_queue.lock); } diff --git a/drivers/nfc/virtual_ncidev.c b/drivers/nfc/virtual_ncidev.c index b027be0b0b6f..590b038e449e 100644 --- a/drivers/nfc/virtual_ncidev.c +++ b/drivers/nfc/virtual_ncidev.c @@ -26,10 +26,14 @@ struct virtual_nci_dev { struct mutex mtx; struct sk_buff *send_buff; struct wait_queue_head wq; + bool running; }; static int virtual_nci_open(struct nci_dev *ndev) { + struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); + + vdev->running = true; return 0; } @@ -40,6 +44,7 @@ static int virtual_nci_close(struct nci_dev *ndev) mutex_lock(&vdev->mtx); kfree_skb(vdev->send_buff); vdev->send_buff = NULL; + vdev->running = false; mutex_unlock(&vdev->mtx); return 0; @@ -50,7 +55,7 @@ static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb) struct virtual_nci_dev *vdev = nci_get_drvdata(ndev); mutex_lock(&vdev->mtx); - if (vdev->send_buff) { + if (vdev->send_buff || !vdev->running) { mutex_unlock(&vdev->mtx); kfree_skb(skb); return -1; diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c index 48328e36e93b..72c0525c75f5 100644 --- a/drivers/nvme/host/auth.c +++ b/drivers/nvme/host/auth.c @@ -757,12 +757,11 @@ static void nvme_queue_auth_work(struct work_struct *work) __func__, chap->qid); mutex_lock(&ctrl->dhchap_auth_mutex); ret = nvme_auth_dhchap_setup_host_response(ctrl, chap); + mutex_unlock(&ctrl->dhchap_auth_mutex); if (ret) { - mutex_unlock(&ctrl->dhchap_auth_mutex); chap->error = ret; goto fail2; } - mutex_unlock(&ctrl->dhchap_auth_mutex); /* DH-HMAC-CHAP Step 3: send reply */ dev_dbg(ctrl->device, "%s: qid %d send reply\n", @@ -839,6 +838,8 @@ static void nvme_queue_auth_work(struct work_struct *work) } fail2: + if (chap->status == 0) + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; dev_dbg(ctrl->device, "%s: qid %d send failure2, status %x\n", __func__, chap->qid, chap->status); tl = nvme_auth_set_dhchap_failure2_data(ctrl, chap); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 88b54cdcbd68..46a4c9c5ea96 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -482,7 +482,6 @@ EXPORT_SYMBOL_GPL(nvme_cancel_tagset); void nvme_cancel_admin_tagset(struct nvme_ctrl *ctrl) { - nvme_stop_keep_alive(ctrl); if (ctrl->admin_tagset) { blk_mq_tagset_busy_iter(ctrl->admin_tagset, nvme_cancel_request, ctrl); @@ -1814,16 +1813,18 @@ set_pi: return ret; } -static void nvme_configure_metadata(struct nvme_ns *ns, struct nvme_id_ns *id) +static int nvme_configure_metadata(struct nvme_ns *ns, struct nvme_id_ns *id) { struct nvme_ctrl *ctrl = ns->ctrl; + int ret; - if (nvme_init_ms(ns, id)) - return; + ret = nvme_init_ms(ns, id); + if (ret) + return ret; ns->features &= ~(NVME_NS_METADATA_SUPPORTED | NVME_NS_EXT_LBAS); if (!ns->ms || !(ctrl->ops->flags & NVME_F_METADATA_SUPPORTED)) - return; + return 0; if (ctrl->ops->flags & NVME_F_FABRICS) { /* @@ -1832,7 +1833,7 @@ static void nvme_configure_metadata(struct nvme_ns *ns, struct nvme_id_ns *id) * remap the separate metadata buffer from the block layer. */ if (WARN_ON_ONCE(!(id->flbas & NVME_NS_FLBAS_META_EXT))) - return; + return 0; ns->features |= NVME_NS_EXT_LBAS; @@ -1859,6 +1860,7 @@ static void nvme_configure_metadata(struct nvme_ns *ns, struct nvme_id_ns *id) else ns->features |= NVME_NS_METADATA_SUPPORTED; } + return 0; } static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, @@ -2032,7 +2034,11 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns, ns->lba_shift = id->lbaf[lbaf].ds; nvme_set_queue_limits(ns->ctrl, ns->queue); - nvme_configure_metadata(ns, id); + ret = nvme_configure_metadata(ns, id); + if (ret < 0) { + blk_mq_unfreeze_queue(ns->disk->queue); + goto out; + } nvme_set_chunk_sectors(ns, id); nvme_update_disk_info(ns->disk, ns, id); @@ -4348,6 +4354,7 @@ void nvme_stop_ctrl(struct nvme_ctrl *ctrl) { nvme_mpath_stop(ctrl); nvme_auth_stop(ctrl); + nvme_stop_keep_alive(ctrl); nvme_stop_failfast_work(ctrl); flush_work(&ctrl->async_event_work); cancel_work_sync(&ctrl->fw_act_work); diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 4673ead69c5f..aa88606a44c4 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -667,8 +667,10 @@ static const match_table_t opt_tokens = { #endif { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, { NVMF_OPT_DISCOVERY, "discovery" }, +#ifdef CONFIG_NVME_HOST_AUTH { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, { NVMF_OPT_DHCHAP_CTRL_SECRET, "dhchap_ctrl_secret=%s" }, +#endif #ifdef CONFIG_NVME_TCP_TLS { NVMF_OPT_TLS, "tls" }, #endif diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 49c3e46eaa1e..9f9a3b35dc64 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2530,12 +2530,6 @@ __nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) * clean up the admin queue. Same thing as above. */ nvme_quiesce_admin_queue(&ctrl->ctrl); - - /* - * Open-coding nvme_cancel_admin_tagset() as fc - * is not using nvme_cancel_request(). - */ - nvme_stop_keep_alive(&ctrl->ctrl); blk_sync_queue(ctrl->ctrl.admin_q); blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, nvme_fc_terminate_exchange, &ctrl->ctrl); @@ -3138,11 +3132,12 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) nvme_unquiesce_admin_queue(&ctrl->ctrl); ret = nvme_init_ctrl_finish(&ctrl->ctrl, false); - if (!ret && test_bit(ASSOC_FAILED, &ctrl->flags)) - ret = -EIO; if (ret) goto out_disconnect_admin_queue; - + if (test_bit(ASSOC_FAILED, &ctrl->flags)) { + ret = -EIO; + goto out_stop_keep_alive; + } /* sanity checks */ /* FC-NVME does not have other data in the capsule */ @@ -3150,7 +3145,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) dev_err(ctrl->ctrl.device, "icdoff %d is not supported!\n", ctrl->ctrl.icdoff); ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR; - goto out_disconnect_admin_queue; + goto out_stop_keep_alive; } /* FC-NVME supports normal SGL Data Block Descriptors */ @@ -3158,7 +3153,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) dev_err(ctrl->ctrl.device, "Mandatory sgls are not supported!\n"); ret = NVME_SC_INVALID_FIELD | NVME_SC_DNR; - goto out_disconnect_admin_queue; + goto out_stop_keep_alive; } if (opts->queue_size > ctrl->ctrl.maxcmd) { @@ -3205,6 +3200,8 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) out_term_aen_ops: nvme_fc_term_aen_ops(ctrl); +out_stop_keep_alive: + nvme_stop_keep_alive(&ctrl->ctrl); out_disconnect_admin_queue: dev_warn(ctrl->ctrl.device, "NVME-FC{%d}: create_assoc failed, assoc_id %llx ret %d\n", diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index a7fea4cbacd7..6d178d555920 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1080,6 +1080,7 @@ destroy_io: nvme_rdma_free_io_queues(ctrl); } destroy_admin: + nvme_stop_keep_alive(&ctrl->ctrl); nvme_quiesce_admin_queue(&ctrl->ctrl); blk_sync_queue(ctrl->ctrl.admin_q); nvme_rdma_stop_queue(&ctrl->queues[0]); diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 89661a9cf850..d79811cfa0ce 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -36,11 +36,11 @@ static int so_priority; module_param(so_priority, int, 0644); MODULE_PARM_DESC(so_priority, "nvme tcp socket optimize priority"); -#ifdef CONFIG_NVME_TCP_TLS /* * TLS handshake timeout */ static int tls_handshake_timeout = 10; +#ifdef CONFIG_NVME_TCP_TLS module_param(tls_handshake_timeout, int, 0644); MODULE_PARM_DESC(tls_handshake_timeout, "nvme TLS handshake timeout in seconds (default 10)"); @@ -161,10 +161,8 @@ struct nvme_tcp_queue { struct ahash_request *snd_hash; __le32 exp_ddgst; __le32 recv_ddgst; -#ifdef CONFIG_NVME_TCP_TLS struct completion tls_complete; int tls_err; -#endif struct page_frag_cache pf_cache; void (*state_change)(struct sock *); @@ -207,6 +205,14 @@ static inline int nvme_tcp_queue_id(struct nvme_tcp_queue *queue) return queue - queue->ctrl->queues; } +static inline bool nvme_tcp_tls(struct nvme_ctrl *ctrl) +{ + if (!IS_ENABLED(CONFIG_NVME_TCP_TLS)) + return 0; + + return ctrl->opts->tls; +} + static inline struct blk_mq_tags *nvme_tcp_tagset(struct nvme_tcp_queue *queue) { u32 queue_idx = nvme_tcp_queue_id(queue); @@ -1412,7 +1418,7 @@ static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue) memset(&msg, 0, sizeof(msg)); iov.iov_base = icresp; iov.iov_len = sizeof(*icresp); - if (queue->ctrl->ctrl.opts->tls) { + if (nvme_tcp_tls(&queue->ctrl->ctrl)) { msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); } @@ -1424,7 +1430,7 @@ static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue) goto free_icresp; } ret = -ENOTCONN; - if (queue->ctrl->ctrl.opts->tls) { + if (nvme_tcp_tls(&queue->ctrl->ctrl)) { ctype = tls_get_record_type(queue->sock->sk, (struct cmsghdr *)cbuf); if (ctype != TLS_RECORD_TYPE_DATA) { @@ -1548,7 +1554,6 @@ static void nvme_tcp_set_queue_io_cpu(struct nvme_tcp_queue *queue) queue->io_cpu = cpumask_next_wrap(n - 1, cpu_online_mask, -1, false); } -#ifdef CONFIG_NVME_TCP_TLS static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid) { struct nvme_tcp_queue *queue = data; @@ -1625,14 +1630,6 @@ static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl, } return ret; } -#else -static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl, - struct nvme_tcp_queue *queue, - key_serial_t pskid) -{ - return -EPROTONOSUPPORT; -} -#endif static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid, key_serial_t pskid) @@ -1759,7 +1756,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid, } /* If PSKs are configured try to start TLS */ - if (pskid) { + if (IS_ENABLED(CONFIG_NVME_TCP_TLS) && pskid) { ret = nvme_tcp_start_tls(nctrl, queue, pskid); if (ret) goto err_init_connect; @@ -1916,7 +1913,7 @@ static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl) int ret; key_serial_t pskid = 0; - if (ctrl->opts->tls) { + if (nvme_tcp_tls(ctrl)) { if (ctrl->opts->tls_key) pskid = key_serial(ctrl->opts->tls_key); else @@ -1949,7 +1946,7 @@ static int __nvme_tcp_alloc_io_queues(struct nvme_ctrl *ctrl) { int i, ret; - if (ctrl->opts->tls && !ctrl->tls_key) { + if (nvme_tcp_tls(ctrl) && !ctrl->tls_key) { dev_err(ctrl->device, "no PSK negotiated\n"); return -ENOKEY; } @@ -2237,6 +2234,7 @@ destroy_io: nvme_tcp_destroy_io_queues(ctrl, new); } destroy_admin: + nvme_stop_keep_alive(ctrl); nvme_tcp_teardown_admin_queue(ctrl, false); return ret; } diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig index 31633da9427c..e1ebc73f3e5e 100644 --- a/drivers/nvme/target/Kconfig +++ b/drivers/nvme/target/Kconfig @@ -4,6 +4,8 @@ config NVME_TARGET tristate "NVMe Target support" depends on BLOCK depends on CONFIGFS_FS + select NVME_KEYRING if NVME_TARGET_TCP_TLS + select KEYS if NVME_TARGET_TCP_TLS select BLK_DEV_INTEGRITY_T10 if BLK_DEV_INTEGRITY select SGL_ALLOC help @@ -87,9 +89,7 @@ config NVME_TARGET_TCP config NVME_TARGET_TCP_TLS bool "NVMe over Fabrics TCP target TLS encryption support" depends on NVME_TARGET_TCP - select NVME_KEYRING select NET_HANDSHAKE - select KEYS help Enables TLS encryption for the NVMe TCP target using the netlink handshake API. diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index 9eed6e6765ea..e307a044b1a1 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -1893,7 +1893,7 @@ static struct config_group *nvmet_ports_make(struct config_group *group, return ERR_PTR(-ENOMEM); } - if (nvme_keyring_id()) { + if (IS_ENABLED(CONFIG_NVME_TARGET_TCP_TLS) && nvme_keyring_id()) { port->keyring = key_lookup(nvme_keyring_id()); if (IS_ERR(port->keyring)) { pr_warn("NVMe keyring not available, disabling TLS\n"); diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c index 43b5bd8bb6a5..d8da840a1c0e 100644 --- a/drivers/nvme/target/fabrics-cmd.c +++ b/drivers/nvme/target/fabrics-cmd.c @@ -244,6 +244,8 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) goto out; } + d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0'; + d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0'; status = nvmet_alloc_ctrl(d->subsysnqn, d->hostnqn, req, le32_to_cpu(c->kato), &ctrl); if (status) @@ -313,6 +315,8 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) goto out; } + d->subsysnqn[NVMF_NQN_FIELD_LEN - 1] = '\0'; + d->hostnqn[NVMF_NQN_FIELD_LEN - 1] = '\0'; ctrl = nvmet_ctrl_find_get(d->subsysnqn, d->hostnqn, le16_to_cpu(d->cntlid), req); if (!ctrl) { diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 92b74d0b8686..4cc27856aa8f 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1854,6 +1854,8 @@ static int nvmet_tcp_tls_handshake(struct nvmet_tcp_queue *queue) } return ret; } +#else +static void nvmet_tcp_tls_handshake_timeout(struct work_struct *w) {} #endif static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port, @@ -1911,9 +1913,9 @@ static void nvmet_tcp_alloc_queue(struct nvmet_tcp_port *port, list_add_tail(&queue->queue_list, &nvmet_tcp_queue_list); mutex_unlock(&nvmet_tcp_queue_mutex); -#ifdef CONFIG_NVME_TARGET_TCP_TLS INIT_DELAYED_WORK(&queue->tls_handshake_tmo_work, nvmet_tcp_tls_handshake_timeout); +#ifdef CONFIG_NVME_TARGET_TCP_TLS if (queue->state == NVMET_TCP_Q_TLS_HANDSHAKE) { struct sock *sk = queue->sock->sk; diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index 539d8920c202..bb0d92461b08 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -176,7 +176,7 @@ static struct notifier_block parisc_panic_block = { static int qemu_power_off(struct sys_off_data *data) { /* this turns the system off via SeaBIOS */ - *(int *)data->cb_data = 0; + gsc_writel(0, (unsigned long) data->cb_data); pdc_soft_power_button(1); return NOTIFY_DONE; } diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 787354b849c7..4cef568231bf 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -87,7 +87,6 @@ source "drivers/phy/motorola/Kconfig" source "drivers/phy/mscc/Kconfig" source "drivers/phy/qualcomm/Kconfig" source "drivers/phy/ralink/Kconfig" -source "drivers/phy/realtek/Kconfig" source "drivers/phy/renesas/Kconfig" source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 868a220ed0f6..fb3dc9de6111 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -26,7 +26,6 @@ obj-y += allwinner/ \ mscc/ \ qualcomm/ \ ralink/ \ - realtek/ \ renesas/ \ rockchip/ \ samsung/ \ diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig deleted file mode 100644 index 75ac7e7c31ae..000000000000 --- a/drivers/phy/realtek/Kconfig +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Phy drivers for Realtek platforms -# - -if ARCH_REALTEK || COMPILE_TEST - -config PHY_RTK_RTD_USB2PHY - tristate "Realtek RTD USB2 PHY Transceiver Driver" - depends on USB_SUPPORT - select GENERIC_PHY - select USB_PHY - select USB_COMMON - help - Enable this to support Realtek SoC USB2 phy transceiver. - The DHC (digital home center) RTD series SoCs used the Synopsys - DWC3 USB IP. This driver will do the PHY initialization - of the parameters. - -config PHY_RTK_RTD_USB3PHY - tristate "Realtek RTD USB3 PHY Transceiver Driver" - depends on USB_SUPPORT - select GENERIC_PHY - select USB_PHY - select USB_COMMON - help - Enable this to support Realtek SoC USB3 phy transceiver. - The DHC (digital home center) RTD series SoCs used the Synopsys - DWC3 USB IP. This driver will do the PHY initialization - of the parameters. - -endif # ARCH_REALTEK || COMPILE_TEST diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile deleted file mode 100644 index ed7b47ff8a26..000000000000 --- a/drivers/phy/realtek/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_PHY_RTK_RTD_USB2PHY) += phy-rtk-usb2.o -obj-$(CONFIG_PHY_RTK_RTD_USB3PHY) += phy-rtk-usb3.o diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c deleted file mode 100644 index 0a6426285c67..000000000000 --- a/drivers/phy/realtek/phy-rtk-usb2.c +++ /dev/null @@ -1,1325 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * phy-rtk-usb2.c RTK usb2.0 PHY driver - * - * Copyright (C) 2023 Realtek Semiconductor Corporation - * - */ - -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/uaccess.h> -#include <linux/debugfs.h> -#include <linux/nvmem-consumer.h> -#include <linux/regmap.h> -#include <linux/sys_soc.h> -#include <linux/mfd/syscon.h> -#include <linux/phy/phy.h> -#include <linux/usb.h> -#include <linux/usb/phy.h> -#include <linux/usb/hcd.h> - -/* GUSB2PHYACCn register */ -#define PHY_NEW_REG_REQ BIT(25) -#define PHY_VSTS_BUSY BIT(23) -#define PHY_VCTRL_SHIFT 8 -#define PHY_REG_DATA_MASK 0xff - -#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f) -#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4) - -#define EFUS_USB_DC_CAL_RATE 2 -#define EFUS_USB_DC_CAL_MAX 7 - -#define EFUS_USB_DC_DIS_RATE 1 -#define EFUS_USB_DC_DIS_MAX 7 - -#define MAX_PHY_DATA_SIZE 20 -#define OFFEST_PHY_READ 0x20 - -#define MAX_USB_PHY_NUM 4 -#define MAX_USB_PHY_PAGE0_DATA_SIZE 16 -#define MAX_USB_PHY_PAGE1_DATA_SIZE 16 -#define MAX_USB_PHY_PAGE2_DATA_SIZE 8 - -#define SET_PAGE_OFFSET 0xf4 -#define SET_PAGE_0 0x9b -#define SET_PAGE_1 0xbb -#define SET_PAGE_2 0xdb - -#define PAGE_START 0xe0 -#define PAGE0_0XE4 0xe4 -#define PAGE0_0XE6 0xe6 -#define PAGE0_0XE7 0xe7 -#define PAGE1_0XE0 0xe0 -#define PAGE1_0XE2 0xe2 - -#define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6)) -#define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2) -#define DEFAULT_DC_DRIVING_VALUE (0x8) -#define DEFAULT_DC_DISCONNECTION_VALUE (0x6) -#define HS_CLK_SELECT BIT(6) - -struct phy_reg { - void __iomem *reg_wrap_vstatus; - void __iomem *reg_gusb2phyacc0; - int vstatus_index; -}; - -struct phy_data { - u8 addr; - u8 data; -}; - -struct phy_cfg { - int page0_size; - struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE]; - int page1_size; - struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE]; - int page2_size; - struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE]; - - int num_phy; - - bool check_efuse; - int check_efuse_version; -#define CHECK_EFUSE_V1 1 -#define CHECK_EFUSE_V2 2 - int efuse_dc_driving_rate; - int efuse_dc_disconnect_rate; - int dc_driving_mask; - int dc_disconnect_mask; - bool usb_dc_disconnect_at_page0; - int driving_updated_for_dev_dis; - - bool do_toggle; - bool do_toggle_driving; - bool use_default_parameter; - bool is_double_sensitivity_mode; -}; - -struct phy_parameter { - struct phy_reg phy_reg; - - /* Get from efuse */ - s8 efuse_usb_dc_cal; - s8 efuse_usb_dc_dis; - - /* Get from dts */ - bool inverse_hstx_sync_clock; - u32 driving_level; - s32 driving_level_compensate; - s32 disconnection_compensate; -}; - -struct rtk_phy { - struct usb_phy phy; - struct device *dev; - - struct phy_cfg *phy_cfg; - int num_phy; - struct phy_parameter *phy_parameter; - - struct dentry *debug_dir; -}; - -/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */ -static inline int page_addr_to_array_index(u8 addr) -{ - return (int)((((addr) - PAGE_START) & 0x7) + - ((((addr) - PAGE_START) & 0x10) >> 1)); -} - -static inline u8 array_index_to_page_addr(int index) -{ - return ((((index) + PAGE_START) & 0x7) + - ((((index) & 0x8) << 1) + PAGE_START)); -} - -#define PHY_IO_TIMEOUT_USEC (50000) -#define PHY_IO_DELAY_US (100) - -static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) -{ - int ret; - unsigned int val; - - ret = read_poll_timeout(readl, val, ((val & mask) == result), - PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg); - if (ret) { - pr_err("%s can't program USB phy\n", __func__); - return -ETIMEDOUT; - } - - return 0; -} - -static char rtk_phy_read(struct phy_reg *phy_reg, char addr) -{ - void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; - unsigned int val; - int ret = 0; - - addr -= OFFEST_PHY_READ; - - /* polling until VBusy == 0 */ - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return (char)ret; - - /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */ - val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); - writel(val, reg_gusb2phyacc0); - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return (char)ret; - - /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */ - val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); - writel(val, reg_gusb2phyacc0); - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return (char)ret; - - val = readl(reg_gusb2phyacc0); - - return (char)(val & PHY_REG_DATA_MASK); -} - -static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data) -{ - unsigned int val; - void __iomem *reg_wrap_vstatus = phy_reg->reg_wrap_vstatus; - void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0; - int shift_bits = phy_reg->vstatus_index * 8; - int ret = 0; - - /* write data to VStatusOut2 (data output to phy) */ - writel((u32)data << shift_bits, reg_wrap_vstatus); - - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return ret; - - /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */ - val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT); - - writel(val, reg_gusb2phyacc0); - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return ret; - - /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */ - val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT); - - writel(val, reg_gusb2phyacc0); - ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0); - if (ret) - return ret; - - return 0; -} - -static int rtk_phy_set_page(struct phy_reg *phy_reg, int page) -{ - switch (page) { - case 0: - return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_0); - case 1: - return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_1); - case 2: - return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_2); - default: - pr_err("%s error page=%d\n", __func__, page); - } - - return -EINVAL; -} - -static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_cfg *phy_cfg, - struct phy_parameter *phy_parameter, u8 data) -{ - u8 ret; - s32 val; - s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; - int offset = 4; - - val = (s32)((data >> offset) & dc_disconnect_mask) - + phy_parameter->efuse_usb_dc_dis - + phy_parameter->disconnection_compensate; - - if (val > dc_disconnect_mask) - val = dc_disconnect_mask; - else if (val < 0) - val = 0; - - ret = (data & (~(dc_disconnect_mask << offset))) | - (val & dc_disconnect_mask) << offset; - - return ret; -} - -/* updated disconnect level at page0 */ -static void update_dc_disconnect_level_at_page0(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter, bool update) -{ - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - struct phy_data *phy_data_page; - struct phy_data *phy_data; - u8 addr, data; - int offset = 4; - s32 dc_disconnect_mask; - int i; - - phy_cfg = rtk_phy->phy_cfg; - phy_reg = &phy_parameter->phy_reg; - - /* Set page 0 */ - phy_data_page = phy_cfg->page0; - rtk_phy_set_page(phy_reg, 0); - - i = page_addr_to_array_index(PAGE0_0XE4); - phy_data = phy_data_page + i; - if (!phy_data->addr) { - phy_data->addr = PAGE0_0XE4; - phy_data->data = rtk_phy_read(phy_reg, PAGE0_0XE4); - } - - addr = phy_data->addr; - data = phy_data->data; - dc_disconnect_mask = phy_cfg->dc_disconnect_mask; - - if (update) - data = __updated_dc_disconnect_level_page0_0xe4(phy_cfg, phy_parameter, data); - else - data = (data & ~(dc_disconnect_mask << offset)) | - (DEFAULT_DC_DISCONNECTION_VALUE << offset); - - if (rtk_phy_write(phy_reg, addr, data)) - dev_err(rtk_phy->dev, - "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", - __func__, addr, data); -} - -static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_cfg *phy_cfg, - struct phy_parameter *phy_parameter, u8 data) -{ - u8 ret; - s32 val; - s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask; - - if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { - val = (s32)(data & dc_disconnect_mask) - + phy_parameter->efuse_usb_dc_dis - + phy_parameter->disconnection_compensate; - } else { /* for CHECK_EFUSE_V2 or no efuse */ - if (phy_parameter->efuse_usb_dc_dis) - val = (s32)(phy_parameter->efuse_usb_dc_dis + - phy_parameter->disconnection_compensate); - else - val = (s32)((data & dc_disconnect_mask) + - phy_parameter->disconnection_compensate); - } - - if (val > dc_disconnect_mask) - val = dc_disconnect_mask; - else if (val < 0) - val = 0; - - ret = (data & (~dc_disconnect_mask)) | (val & dc_disconnect_mask); - - return ret; -} - -/* updated disconnect level at page1 */ -static void update_dc_disconnect_level_at_page1(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter, bool update) -{ - struct phy_cfg *phy_cfg; - struct phy_data *phy_data_page; - struct phy_data *phy_data; - struct phy_reg *phy_reg; - u8 addr, data; - s32 dc_disconnect_mask; - int i; - - phy_cfg = rtk_phy->phy_cfg; - phy_reg = &phy_parameter->phy_reg; - - /* Set page 1 */ - phy_data_page = phy_cfg->page1; - rtk_phy_set_page(phy_reg, 1); - - i = page_addr_to_array_index(PAGE1_0XE2); - phy_data = phy_data_page + i; - if (!phy_data->addr) { - phy_data->addr = PAGE1_0XE2; - phy_data->data = rtk_phy_read(phy_reg, PAGE1_0XE2); - } - - addr = phy_data->addr; - data = phy_data->data; - dc_disconnect_mask = phy_cfg->dc_disconnect_mask; - - if (update) - data = __updated_dc_disconnect_level_page1_0xe2(phy_cfg, phy_parameter, data); - else - data = (data & ~dc_disconnect_mask) | DEFAULT_DC_DISCONNECTION_VALUE; - - if (rtk_phy_write(phy_reg, addr, data)) - dev_err(rtk_phy->dev, - "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", - __func__, addr, data); -} - -static void update_dc_disconnect_level(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter, bool update) -{ - struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; - - if (phy_cfg->usb_dc_disconnect_at_page0) - update_dc_disconnect_level_at_page0(rtk_phy, phy_parameter, update); - else - update_dc_disconnect_level_at_page1(rtk_phy, phy_parameter, update); -} - -static u8 __update_dc_driving_page0_0xe4(struct phy_cfg *phy_cfg, - struct phy_parameter *phy_parameter, u8 data) -{ - s32 driving_level_compensate = phy_parameter->driving_level_compensate; - s32 dc_driving_mask = phy_cfg->dc_driving_mask; - s32 val; - u8 ret; - - if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { - val = (s32)(data & dc_driving_mask) + driving_level_compensate - + phy_parameter->efuse_usb_dc_cal; - } else { /* for CHECK_EFUSE_V2 or no efuse */ - if (phy_parameter->efuse_usb_dc_cal) - val = (s32)((phy_parameter->efuse_usb_dc_cal & dc_driving_mask) - + driving_level_compensate); - else - val = (s32)(data & dc_driving_mask); - } - - if (val > dc_driving_mask) - val = dc_driving_mask; - else if (val < 0) - val = 0; - - ret = (data & (~dc_driving_mask)) | (val & dc_driving_mask); - - return ret; -} - -static void update_dc_driving_level(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter) -{ - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - - phy_reg = &phy_parameter->phy_reg; - phy_cfg = rtk_phy->phy_cfg; - if (!phy_cfg->page0[4].addr) { - rtk_phy_set_page(phy_reg, 0); - phy_cfg->page0[4].addr = PAGE0_0XE4; - phy_cfg->page0[4].data = rtk_phy_read(phy_reg, PAGE0_0XE4); - } - - if (phy_parameter->driving_level != DEFAULT_DC_DRIVING_VALUE) { - u32 dc_driving_mask; - u8 driving_level; - u8 data; - - data = phy_cfg->page0[4].data; - dc_driving_mask = phy_cfg->dc_driving_mask; - driving_level = data & dc_driving_mask; - - dev_dbg(rtk_phy->dev, "%s driving_level=%d => dts driving_level=%d\n", - __func__, driving_level, phy_parameter->driving_level); - - phy_cfg->page0[4].data = (data & (~dc_driving_mask)) | - (phy_parameter->driving_level & dc_driving_mask); - } - - phy_cfg->page0[4].data = __update_dc_driving_page0_0xe4(phy_cfg, - phy_parameter, - phy_cfg->page0[4].data); -} - -static void update_hs_clk_select(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter) -{ - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - - phy_cfg = rtk_phy->phy_cfg; - phy_reg = &phy_parameter->phy_reg; - - if (phy_parameter->inverse_hstx_sync_clock) { - if (!phy_cfg->page0[6].addr) { - rtk_phy_set_page(phy_reg, 0); - phy_cfg->page0[6].addr = PAGE0_0XE6; - phy_cfg->page0[6].data = rtk_phy_read(phy_reg, PAGE0_0XE6); - } - - phy_cfg->page0[6].data = phy_cfg->page0[6].data | HS_CLK_SELECT; - } -} - -static void do_rtk_phy_toggle(struct rtk_phy *rtk_phy, - int index, bool connect) -{ - struct phy_parameter *phy_parameter; - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - struct phy_data *phy_data_page; - u8 addr, data; - int i; - - phy_cfg = rtk_phy->phy_cfg; - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - if (!phy_cfg->do_toggle) - goto out; - - if (phy_cfg->is_double_sensitivity_mode) - goto do_toggle_driving; - - /* Set page 0 */ - rtk_phy_set_page(phy_reg, 0); - - addr = PAGE0_0XE7; - data = rtk_phy_read(phy_reg, addr); - - if (connect) - rtk_phy_write(phy_reg, addr, data & (~SENSITIVITY_CTRL)); - else - rtk_phy_write(phy_reg, addr, data | (SENSITIVITY_CTRL)); - -do_toggle_driving: - - if (!phy_cfg->do_toggle_driving) - goto do_toggle; - - /* Page 0 addr 0xE4 driving capability */ - - /* Set page 0 */ - phy_data_page = phy_cfg->page0; - rtk_phy_set_page(phy_reg, 0); - - i = page_addr_to_array_index(PAGE0_0XE4); - addr = phy_data_page[i].addr; - data = phy_data_page[i].data; - - if (connect) { - rtk_phy_write(phy_reg, addr, data); - } else { - u8 value; - s32 tmp; - s32 driving_updated = - phy_cfg->driving_updated_for_dev_dis; - s32 dc_driving_mask = phy_cfg->dc_driving_mask; - - tmp = (s32)(data & dc_driving_mask) + driving_updated; - - if (tmp > dc_driving_mask) - tmp = dc_driving_mask; - else if (tmp < 0) - tmp = 0; - - value = (data & (~dc_driving_mask)) | (tmp & dc_driving_mask); - - rtk_phy_write(phy_reg, addr, value); - } - -do_toggle: - /* restore dc disconnect level before toggle */ - update_dc_disconnect_level(rtk_phy, phy_parameter, false); - - /* Set page 1 */ - rtk_phy_set_page(phy_reg, 1); - - addr = PAGE1_0XE0; - data = rtk_phy_read(phy_reg, addr); - - rtk_phy_write(phy_reg, addr, data & - (~ENABLE_AUTO_SENSITIVITY_CALIBRATION)); - mdelay(1); - rtk_phy_write(phy_reg, addr, data | - (ENABLE_AUTO_SENSITIVITY_CALIBRATION)); - - /* update dc disconnect level after toggle */ - update_dc_disconnect_level(rtk_phy, phy_parameter, true); - -out: - return; -} - -static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index) -{ - struct phy_parameter *phy_parameter; - struct phy_cfg *phy_cfg; - struct phy_data *phy_data_page; - struct phy_reg *phy_reg; - int i; - - phy_cfg = rtk_phy->phy_cfg; - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - if (phy_cfg->use_default_parameter) { - dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n", - __func__, index); - goto do_toggle; - } - - /* Set page 0 */ - phy_data_page = phy_cfg->page0; - rtk_phy_set_page(phy_reg, 0); - - for (i = 0; i < phy_cfg->page0_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = phy_data->addr; - u8 data = phy_data->data; - - if (!addr) - continue; - - if (rtk_phy_write(phy_reg, addr, data)) { - dev_err(rtk_phy->dev, - "%s: Error to set page0 parameter addr=0x%x value=0x%x\n", - __func__, addr, data); - return -EINVAL; - } - } - - /* Set page 1 */ - phy_data_page = phy_cfg->page1; - rtk_phy_set_page(phy_reg, 1); - - for (i = 0; i < phy_cfg->page1_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = phy_data->addr; - u8 data = phy_data->data; - - if (!addr) - continue; - - if (rtk_phy_write(phy_reg, addr, data)) { - dev_err(rtk_phy->dev, - "%s: Error to set page1 parameter addr=0x%x value=0x%x\n", - __func__, addr, data); - return -EINVAL; - } - } - - if (phy_cfg->page2_size == 0) - goto do_toggle; - - /* Set page 2 */ - phy_data_page = phy_cfg->page2; - rtk_phy_set_page(phy_reg, 2); - - for (i = 0; i < phy_cfg->page2_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = phy_data->addr; - u8 data = phy_data->data; - - if (!addr) - continue; - - if (rtk_phy_write(phy_reg, addr, data)) { - dev_err(rtk_phy->dev, - "%s: Error to set page2 parameter addr=0x%x value=0x%x\n", - __func__, addr, data); - return -EINVAL; - } - } - -do_toggle: - do_rtk_phy_toggle(rtk_phy, index, false); - - return 0; -} - -static int rtk_phy_init(struct phy *phy) -{ - struct rtk_phy *rtk_phy = phy_get_drvdata(phy); - unsigned long phy_init_time = jiffies; - int i, ret = 0; - - if (!rtk_phy) - return -EINVAL; - - for (i = 0; i < rtk_phy->num_phy; i++) - ret = do_rtk_phy_init(rtk_phy, i); - - dev_dbg(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n", - jiffies_to_msecs(jiffies - phy_init_time)); - return ret; -} - -static int rtk_phy_exit(struct phy *phy) -{ - return 0; -} - -static const struct phy_ops ops = { - .init = rtk_phy_init, - .exit = rtk_phy_exit, - .owner = THIS_MODULE, -}; - -static void rtk_phy_toggle(struct usb_phy *usb2_phy, bool connect, int port) -{ - int index = port; - struct rtk_phy *rtk_phy = NULL; - - rtk_phy = dev_get_drvdata(usb2_phy->dev); - - if (index > rtk_phy->num_phy) { - dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n", - __func__, index, rtk_phy->num_phy); - return; - } - - do_rtk_phy_toggle(rtk_phy, index, connect); -} - -static int rtk_phy_notify_port_status(struct usb_phy *x, int port, - u16 portstatus, u16 portchange) -{ - bool connect = false; - - pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n", - __func__, port, (int)portstatus, (int)portchange); - if (portstatus & USB_PORT_STAT_CONNECTION) - connect = true; - - if (portchange & USB_PORT_STAT_C_CONNECTION) - rtk_phy_toggle(x, connect, port); - - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static struct dentry *create_phy_debug_root(void) -{ - struct dentry *phy_debug_root; - - phy_debug_root = debugfs_lookup("phy", usb_debug_root); - if (!phy_debug_root) - phy_debug_root = debugfs_create_dir("phy", usb_debug_root); - - return phy_debug_root; -} - -static int rtk_usb2_parameter_show(struct seq_file *s, void *unused) -{ - struct rtk_phy *rtk_phy = s->private; - struct phy_cfg *phy_cfg; - int i, index; - - phy_cfg = rtk_phy->phy_cfg; - - seq_puts(s, "Property:\n"); - seq_printf(s, " check_efuse: %s\n", - phy_cfg->check_efuse ? "Enable" : "Disable"); - seq_printf(s, " check_efuse_version: %d\n", - phy_cfg->check_efuse_version); - seq_printf(s, " efuse_dc_driving_rate: %d\n", - phy_cfg->efuse_dc_driving_rate); - seq_printf(s, " dc_driving_mask: 0x%x\n", - phy_cfg->dc_driving_mask); - seq_printf(s, " efuse_dc_disconnect_rate: %d\n", - phy_cfg->efuse_dc_disconnect_rate); - seq_printf(s, " dc_disconnect_mask: 0x%x\n", - phy_cfg->dc_disconnect_mask); - seq_printf(s, " usb_dc_disconnect_at_page0: %s\n", - phy_cfg->usb_dc_disconnect_at_page0 ? "true" : "false"); - seq_printf(s, " do_toggle: %s\n", - phy_cfg->do_toggle ? "Enable" : "Disable"); - seq_printf(s, " do_toggle_driving: %s\n", - phy_cfg->do_toggle_driving ? "Enable" : "Disable"); - seq_printf(s, " driving_updated_for_dev_dis: 0x%x\n", - phy_cfg->driving_updated_for_dev_dis); - seq_printf(s, " use_default_parameter: %s\n", - phy_cfg->use_default_parameter ? "Enable" : "Disable"); - seq_printf(s, " is_double_sensitivity_mode: %s\n", - phy_cfg->is_double_sensitivity_mode ? "Enable" : "Disable"); - - for (index = 0; index < rtk_phy->num_phy; index++) { - struct phy_parameter *phy_parameter; - struct phy_reg *phy_reg; - struct phy_data *phy_data_page; - - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - seq_printf(s, "PHY %d:\n", index); - - seq_puts(s, "Page 0:\n"); - /* Set page 0 */ - phy_data_page = phy_cfg->page0; - rtk_phy_set_page(phy_reg, 0); - - for (i = 0; i < phy_cfg->page0_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = array_index_to_page_addr(i); - u8 data = phy_data->data; - u8 value = rtk_phy_read(phy_reg, addr); - - if (phy_data->addr) - seq_printf(s, " Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n", - addr, data, value); - else - seq_printf(s, " Page 0: addr=0x%x data=none ==> read value=0x%02x\n", - addr, value); - } - - seq_puts(s, "Page 1:\n"); - /* Set page 1 */ - phy_data_page = phy_cfg->page1; - rtk_phy_set_page(phy_reg, 1); - - for (i = 0; i < phy_cfg->page1_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = array_index_to_page_addr(i); - u8 data = phy_data->data; - u8 value = rtk_phy_read(phy_reg, addr); - - if (phy_data->addr) - seq_printf(s, " Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n", - addr, data, value); - else - seq_printf(s, " Page 1: addr=0x%x data=none ==> read value=0x%02x\n", - addr, value); - } - - if (phy_cfg->page2_size == 0) - goto out; - - seq_puts(s, "Page 2:\n"); - /* Set page 2 */ - phy_data_page = phy_cfg->page2; - rtk_phy_set_page(phy_reg, 2); - - for (i = 0; i < phy_cfg->page2_size; i++) { - struct phy_data *phy_data = phy_data_page + i; - u8 addr = array_index_to_page_addr(i); - u8 data = phy_data->data; - u8 value = rtk_phy_read(phy_reg, addr); - - if (phy_data->addr) - seq_printf(s, " Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n", - addr, data, value); - else - seq_printf(s, " Page 2: addr=0x%x data=none ==> read value=0x%02x\n", - addr, value); - } - -out: - seq_puts(s, "PHY Property:\n"); - seq_printf(s, " efuse_usb_dc_cal: %d\n", - (int)phy_parameter->efuse_usb_dc_cal); - seq_printf(s, " efuse_usb_dc_dis: %d\n", - (int)phy_parameter->efuse_usb_dc_dis); - seq_printf(s, " inverse_hstx_sync_clock: %s\n", - phy_parameter->inverse_hstx_sync_clock ? "Enable" : "Disable"); - seq_printf(s, " driving_level: %d\n", - phy_parameter->driving_level); - seq_printf(s, " driving_level_compensate: %d\n", - phy_parameter->driving_level_compensate); - seq_printf(s, " disconnection_compensate: %d\n", - phy_parameter->disconnection_compensate); - } - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(rtk_usb2_parameter); - -static inline void create_debug_files(struct rtk_phy *rtk_phy) -{ - struct dentry *phy_debug_root = NULL; - - phy_debug_root = create_phy_debug_root(); - if (!phy_debug_root) - return; - - rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), - phy_debug_root); - - debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy, - &rtk_usb2_parameter_fops); - - return; -} - -static inline void remove_debug_files(struct rtk_phy *rtk_phy) -{ - debugfs_remove_recursive(rtk_phy->debug_dir); -} -#else -static inline void create_debug_files(struct rtk_phy *rtk_phy) { } -static inline void remove_debug_files(struct rtk_phy *rtk_phy) { } -#endif /* CONFIG_DEBUG_FS */ - -static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter, int index) -{ - struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; - u8 value = 0; - struct nvmem_cell *cell; - struct soc_device_attribute rtk_soc_groot[] = { - { .family = "Realtek Groot",}, - { /* empty */ } }; - - if (!phy_cfg->check_efuse) - goto out; - - /* Read efuse for usb dc cal */ - cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-cal"); - if (IS_ERR(cell)) { - dev_dbg(rtk_phy->dev, "%s no usb-dc-cal: %ld\n", - __func__, PTR_ERR(cell)); - } else { - unsigned char *buf; - size_t buf_size; - - buf = nvmem_cell_read(cell, &buf_size); - if (!IS_ERR(buf)) { - value = buf[0] & phy_cfg->dc_driving_mask; - kfree(buf); - } - nvmem_cell_put(cell); - } - - if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { - int rate = phy_cfg->efuse_dc_driving_rate; - - if (value <= EFUS_USB_DC_CAL_MAX) - phy_parameter->efuse_usb_dc_cal = (int8_t)(value * rate); - else - phy_parameter->efuse_usb_dc_cal = -(int8_t) - ((EFUS_USB_DC_CAL_MAX & value) * rate); - - if (soc_device_match(rtk_soc_groot)) { - dev_dbg(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n"); - - /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */ - if (value <= EFUS_USB_DC_CAL_MAX) - phy_parameter->efuse_usb_dc_cal = (int8_t)(value); - - /* We set max dc cal compensate is 0x8 if otp is 0x7 */ - if (value == 0x7) - phy_parameter->efuse_usb_dc_cal = (int8_t)(value + 1); - } - } else { /* for CHECK_EFUSE_V2 */ - phy_parameter->efuse_usb_dc_cal = value & phy_cfg->dc_driving_mask; - } - - /* Read efuse for usb dc disconnect level */ - value = 0; - cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-dis"); - if (IS_ERR(cell)) { - dev_dbg(rtk_phy->dev, "%s no usb-dc-dis: %ld\n", - __func__, PTR_ERR(cell)); - } else { - unsigned char *buf; - size_t buf_size; - - buf = nvmem_cell_read(cell, &buf_size); - if (!IS_ERR(buf)) { - value = buf[0] & phy_cfg->dc_disconnect_mask; - kfree(buf); - } - nvmem_cell_put(cell); - } - - if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) { - int rate = phy_cfg->efuse_dc_disconnect_rate; - - if (value <= EFUS_USB_DC_DIS_MAX) - phy_parameter->efuse_usb_dc_dis = (int8_t)(value * rate); - else - phy_parameter->efuse_usb_dc_dis = -(int8_t) - ((EFUS_USB_DC_DIS_MAX & value) * rate); - } else { /* for CHECK_EFUSE_V2 */ - phy_parameter->efuse_usb_dc_dis = value & phy_cfg->dc_disconnect_mask; - } - -out: - return 0; -} - -static int parse_phy_data(struct rtk_phy *rtk_phy) -{ - struct device *dev = rtk_phy->dev; - struct device_node *np = dev->of_node; - struct phy_parameter *phy_parameter; - int ret = 0; - int index; - - rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) * - rtk_phy->num_phy, GFP_KERNEL); - if (!rtk_phy->phy_parameter) - return -ENOMEM; - - for (index = 0; index < rtk_phy->num_phy; index++) { - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - - phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(np, 0); - phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(np, 1) + index; - phy_parameter->phy_reg.vstatus_index = index; - - if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock")) - phy_parameter->inverse_hstx_sync_clock = true; - else - phy_parameter->inverse_hstx_sync_clock = false; - - if (of_property_read_u32_index(np, "realtek,driving-level", - index, &phy_parameter->driving_level)) - phy_parameter->driving_level = DEFAULT_DC_DRIVING_VALUE; - - if (of_property_read_u32_index(np, "realtek,driving-level-compensate", - index, &phy_parameter->driving_level_compensate)) - phy_parameter->driving_level_compensate = 0; - - if (of_property_read_u32_index(np, "realtek,disconnection-compensate", - index, &phy_parameter->disconnection_compensate)) - phy_parameter->disconnection_compensate = 0; - - get_phy_data_by_efuse(rtk_phy, phy_parameter, index); - - update_dc_driving_level(rtk_phy, phy_parameter); - - update_hs_clk_select(rtk_phy, phy_parameter); - } - - return ret; -} - -static int rtk_usb2phy_probe(struct platform_device *pdev) -{ - struct rtk_phy *rtk_phy; - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct phy_provider *phy_provider; - const struct phy_cfg *phy_cfg; - int ret = 0; - - phy_cfg = of_device_get_match_data(dev); - if (!phy_cfg) { - dev_err(dev, "phy config are not assigned!\n"); - return -EINVAL; - } - - rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL); - if (!rtk_phy) - return -ENOMEM; - - rtk_phy->dev = &pdev->dev; - rtk_phy->phy.dev = rtk_phy->dev; - rtk_phy->phy.label = "rtk-usb2phy"; - rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status; - - rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); - - memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); - - rtk_phy->num_phy = phy_cfg->num_phy; - - ret = parse_phy_data(rtk_phy); - if (ret) - goto err; - - platform_set_drvdata(pdev, rtk_phy); - - generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); - - phy_set_drvdata(generic_phy, rtk_phy); - - phy_provider = devm_of_phy_provider_register(rtk_phy->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - ret = usb_add_phy_dev(&rtk_phy->phy); - if (ret) - goto err; - - create_debug_files(rtk_phy); - -err: - return ret; -} - -static void rtk_usb2phy_remove(struct platform_device *pdev) -{ - struct rtk_phy *rtk_phy = platform_get_drvdata(pdev); - - remove_debug_files(rtk_phy); - - usb_remove_phy(&rtk_phy->phy); -} - -static const struct phy_cfg rtd1295_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0x90}, - [3] = {0xe3, 0x3a}, - [4] = {0xe4, 0x68}, - [6] = {0xe6, 0x91}, - [13] = {0xf5, 0x81}, - [15] = {0xf7, 0x02}, }, - .page1_size = 8, - .page1 = { /* default parameter */ }, - .page2_size = 0, - .page2 = { /* no parameter */ }, - .num_phy = 1, - .check_efuse = false, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = false, -}; - -static const struct phy_cfg rtd1395_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [4] = {0xe4, 0xac}, - [13] = {0xf5, 0x00}, - [15] = {0xf7, 0x02}, }, - .page1_size = 8, - .page1 = { /* default parameter */ }, - .page2_size = 0, - .page2 = { /* no parameter */ }, - .num_phy = 1, - .check_efuse = false, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = false, -}; - -static const struct phy_cfg rtd1395_phy_cfg_2port = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [4] = {0xe4, 0xac}, - [13] = {0xf5, 0x00}, - [15] = {0xf7, 0x02}, }, - .page1_size = 8, - .page1 = { /* default parameter */ }, - .page2_size = 0, - .page2 = { /* no parameter */ }, - .num_phy = 2, - .check_efuse = false, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = false, -}; - -static const struct phy_cfg rtd1619_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [4] = {0xe4, 0x68}, }, - .page1_size = 8, - .page1 = { /* default parameter */ }, - .page2_size = 0, - .page2 = { /* no parameter */ }, - .num_phy = 1, - .check_efuse = true, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = false, -}; - -static const struct phy_cfg rtd1319_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0x18}, - [4] = {0xe4, 0x6a}, - [7] = {0xe7, 0x71}, - [13] = {0xf5, 0x15}, - [15] = {0xf7, 0x32}, }, - .page1_size = 8, - .page1 = { [3] = {0xe3, 0x44}, }, - .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, - .page2 = { [0] = {0xe0, 0x01}, }, - .num_phy = 1, - .check_efuse = true, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = true, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = true, -}; - -static const struct phy_cfg rtd1312c_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0x14}, - [4] = {0xe4, 0x67}, - [5] = {0xe5, 0x55}, }, - .page1_size = 8, - .page1 = { [3] = {0xe3, 0x23}, - [6] = {0xe6, 0x58}, }, - .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, - .page2 = { /* default parameter */ }, - .num_phy = 1, - .check_efuse = true, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = 1, - .dc_driving_mask = 0xf, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = true, - .do_toggle = true, - .do_toggle_driving = true, - .driving_updated_for_dev_dis = 0xf, - .use_default_parameter = false, - .is_double_sensitivity_mode = true, -}; - -static const struct phy_cfg rtd1619b_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0xa3}, - [4] = {0xe4, 0x88}, - [5] = {0xe5, 0x4f}, - [6] = {0xe6, 0x02}, }, - .page1_size = 8, - .page1 = { [3] = {0xe3, 0x64}, }, - .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, - .page2 = { [7] = {0xe7, 0x45}, }, - .num_phy = 1, - .check_efuse = true, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, - .dc_driving_mask = 0x1f, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = false, - .do_toggle = true, - .do_toggle_driving = true, - .driving_updated_for_dev_dis = 0x8, - .use_default_parameter = false, - .is_double_sensitivity_mode = true, -}; - -static const struct phy_cfg rtd1319d_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0xa3}, - [4] = {0xe4, 0x8e}, - [5] = {0xe5, 0x4f}, - [6] = {0xe6, 0x02}, }, - .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, - .page1 = { [14] = {0xf5, 0x1}, }, - .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, - .page2 = { [7] = {0xe7, 0x44}, }, - .check_efuse = true, - .num_phy = 1, - .check_efuse_version = CHECK_EFUSE_V1, - .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, - .dc_driving_mask = 0x1f, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = false, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0x8, - .use_default_parameter = false, - .is_double_sensitivity_mode = true, -}; - -static const struct phy_cfg rtd1315e_phy_cfg = { - .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE, - .page0 = { [0] = {0xe0, 0xa3}, - [4] = {0xe4, 0x8c}, - [5] = {0xe5, 0x4f}, - [6] = {0xe6, 0x02}, }, - .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE, - .page1 = { [3] = {0xe3, 0x7f}, - [14] = {0xf5, 0x01}, }, - .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE, - .page2 = { [7] = {0xe7, 0x44}, }, - .num_phy = 1, - .check_efuse = true, - .check_efuse_version = CHECK_EFUSE_V2, - .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE, - .dc_driving_mask = 0x1f, - .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE, - .dc_disconnect_mask = 0xf, - .usb_dc_disconnect_at_page0 = false, - .do_toggle = true, - .do_toggle_driving = false, - .driving_updated_for_dev_dis = 0x8, - .use_default_parameter = false, - .is_double_sensitivity_mode = true, -}; - -static const struct of_device_id usbphy_rtk_dt_match[] = { - { .compatible = "realtek,rtd1295-usb2phy", .data = &rtd1295_phy_cfg }, - { .compatible = "realtek,rtd1312c-usb2phy", .data = &rtd1312c_phy_cfg }, - { .compatible = "realtek,rtd1315e-usb2phy", .data = &rtd1315e_phy_cfg }, - { .compatible = "realtek,rtd1319-usb2phy", .data = &rtd1319_phy_cfg }, - { .compatible = "realtek,rtd1319d-usb2phy", .data = &rtd1319d_phy_cfg }, - { .compatible = "realtek,rtd1395-usb2phy", .data = &rtd1395_phy_cfg }, - { .compatible = "realtek,rtd1395-usb2phy-2port", .data = &rtd1395_phy_cfg_2port }, - { .compatible = "realtek,rtd1619-usb2phy", .data = &rtd1619_phy_cfg }, - { .compatible = "realtek,rtd1619b-usb2phy", .data = &rtd1619b_phy_cfg }, - {}, -}; -MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); - -static struct platform_driver rtk_usb2phy_driver = { - .probe = rtk_usb2phy_probe, - .remove_new = rtk_usb2phy_remove, - .driver = { - .name = "rtk-usb2phy", - .of_match_table = usbphy_rtk_dt_match, - }, -}; - -module_platform_driver(rtk_usb2phy_driver); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform: rtk-usb2phy"); -MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>"); -MODULE_DESCRIPTION("Realtek usb 2.0 phy driver"); diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c deleted file mode 100644 index 67446a85e968..000000000000 --- a/drivers/phy/realtek/phy-rtk-usb3.c +++ /dev/null @@ -1,761 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * phy-rtk-usb3.c RTK usb3.0 phy driver - * - * copyright (c) 2023 realtek semiconductor corporation - * - */ - -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/uaccess.h> -#include <linux/debugfs.h> -#include <linux/nvmem-consumer.h> -#include <linux/regmap.h> -#include <linux/sys_soc.h> -#include <linux/mfd/syscon.h> -#include <linux/phy/phy.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/usb/phy.h> - -#define USB_MDIO_CTRL_PHY_BUSY BIT(7) -#define USB_MDIO_CTRL_PHY_WRITE BIT(0) -#define USB_MDIO_CTRL_PHY_ADDR_SHIFT 8 -#define USB_MDIO_CTRL_PHY_DATA_SHIFT 16 - -#define MAX_USB_PHY_DATA_SIZE 0x30 -#define PHY_ADDR_0X09 0x09 -#define PHY_ADDR_0X0B 0x0b -#define PHY_ADDR_0X0D 0x0d -#define PHY_ADDR_0X10 0x10 -#define PHY_ADDR_0X1F 0x1f -#define PHY_ADDR_0X20 0x20 -#define PHY_ADDR_0X21 0x21 -#define PHY_ADDR_0X30 0x30 - -#define REG_0X09_FORCE_CALIBRATION BIT(9) -#define REG_0X0B_RX_OFFSET_RANGE_MASK 0xc -#define REG_0X0D_RX_DEBUG_TEST_EN BIT(6) -#define REG_0X10_DEBUG_MODE_SETTING 0x3c0 -#define REG_0X10_DEBUG_MODE_SETTING_MASK 0x3f8 -#define REG_0X1F_RX_OFFSET_CODE_MASK 0x1e - -#define USB_U3_TX_LFPS_SWING_TRIM_SHIFT 4 -#define USB_U3_TX_LFPS_SWING_TRIM_MASK 0xf -#define AMPLITUDE_CONTROL_COARSE_MASK 0xff -#define AMPLITUDE_CONTROL_FINE_MASK 0xffff -#define AMPLITUDE_CONTROL_COARSE_DEFAULT 0xff -#define AMPLITUDE_CONTROL_FINE_DEFAULT 0xffff - -#define PHY_ADDR_MAP_ARRAY_INDEX(addr) (addr) -#define ARRAY_INDEX_MAP_PHY_ADDR(index) (index) - -struct phy_reg { - void __iomem *reg_mdio_ctl; -}; - -struct phy_data { - u8 addr; - u16 data; -}; - -struct phy_cfg { - int param_size; - struct phy_data param[MAX_USB_PHY_DATA_SIZE]; - - bool check_efuse; - bool do_toggle; - bool do_toggle_once; - bool use_default_parameter; - bool check_rx_front_end_offset; -}; - -struct phy_parameter { - struct phy_reg phy_reg; - - /* Get from efuse */ - u8 efuse_usb_u3_tx_lfps_swing_trim; - - /* Get from dts */ - u32 amplitude_control_coarse; - u32 amplitude_control_fine; -}; - -struct rtk_phy { - struct usb_phy phy; - struct device *dev; - - struct phy_cfg *phy_cfg; - int num_phy; - struct phy_parameter *phy_parameter; - - struct dentry *debug_dir; -}; - -#define PHY_IO_TIMEOUT_USEC (50000) -#define PHY_IO_DELAY_US (100) - -static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) -{ - int ret; - unsigned int val; - - ret = read_poll_timeout(readl, val, ((val & mask) == result), - PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg); - if (ret) { - pr_err("%s can't program USB phy\n", __func__); - return -ETIMEDOUT; - } - - return 0; -} - -static int rtk_phy3_wait_vbusy(struct phy_reg *phy_reg) -{ - return utmi_wait_register(phy_reg->reg_mdio_ctl, USB_MDIO_CTRL_PHY_BUSY, 0); -} - -static u16 rtk_phy_read(struct phy_reg *phy_reg, char addr) -{ - unsigned int tmp; - u32 value; - - tmp = (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT); - - writel(tmp, phy_reg->reg_mdio_ctl); - - rtk_phy3_wait_vbusy(phy_reg); - - value = readl(phy_reg->reg_mdio_ctl); - value = value >> USB_MDIO_CTRL_PHY_DATA_SHIFT; - - return (u16)value; -} - -static int rtk_phy_write(struct phy_reg *phy_reg, char addr, u16 data) -{ - unsigned int val; - - val = USB_MDIO_CTRL_PHY_WRITE | - (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT) | - (data << USB_MDIO_CTRL_PHY_DATA_SHIFT); - - writel(val, phy_reg->reg_mdio_ctl); - - rtk_phy3_wait_vbusy(phy_reg); - - return 0; -} - -static void do_rtk_usb3_phy_toggle(struct rtk_phy *rtk_phy, int index, bool connect) -{ - struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; - struct phy_reg *phy_reg; - struct phy_parameter *phy_parameter; - struct phy_data *phy_data; - u8 addr; - u16 data; - int i; - - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - if (!phy_cfg->do_toggle) - return; - - i = PHY_ADDR_MAP_ARRAY_INDEX(PHY_ADDR_0X09); - phy_data = phy_cfg->param + i; - addr = phy_data->addr; - data = phy_data->data; - - if (!addr && !data) { - addr = PHY_ADDR_0X09; - data = rtk_phy_read(phy_reg, addr); - phy_data->addr = addr; - phy_data->data = data; - } - - rtk_phy_write(phy_reg, addr, data & (~REG_0X09_FORCE_CALIBRATION)); - mdelay(1); - rtk_phy_write(phy_reg, addr, data | REG_0X09_FORCE_CALIBRATION); -} - -static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index) -{ - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - struct phy_parameter *phy_parameter; - int i = 0; - - phy_cfg = rtk_phy->phy_cfg; - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - if (phy_cfg->use_default_parameter) - goto do_toggle; - - for (i = 0; i < phy_cfg->param_size; i++) { - struct phy_data *phy_data = phy_cfg->param + i; - u8 addr = phy_data->addr; - u16 data = phy_data->data; - - if (!addr && !data) - continue; - - rtk_phy_write(phy_reg, addr, data); - } - -do_toggle: - if (phy_cfg->do_toggle_once) - phy_cfg->do_toggle = true; - - do_rtk_usb3_phy_toggle(rtk_phy, index, false); - - if (phy_cfg->do_toggle_once) { - u16 check_value = 0; - int count = 10; - u16 value_0x0d, value_0x10; - - /* Enable Debug mode by set 0x0D and 0x10 */ - value_0x0d = rtk_phy_read(phy_reg, PHY_ADDR_0X0D); - value_0x10 = rtk_phy_read(phy_reg, PHY_ADDR_0X10); - - rtk_phy_write(phy_reg, PHY_ADDR_0X0D, - value_0x0d | REG_0X0D_RX_DEBUG_TEST_EN); - rtk_phy_write(phy_reg, PHY_ADDR_0X10, - (value_0x10 & ~REG_0X10_DEBUG_MODE_SETTING_MASK) | - REG_0X10_DEBUG_MODE_SETTING); - - check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30); - - while (!(check_value & BIT(15))) { - check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30); - mdelay(1); - if (count-- < 0) - break; - } - - if (!(check_value & BIT(15))) - dev_info(rtk_phy->dev, "toggle fail addr=0x%02x, data=0x%04x\n", - PHY_ADDR_0X30, check_value); - - /* Disable Debug mode by set 0x0D and 0x10 to default*/ - rtk_phy_write(phy_reg, PHY_ADDR_0X0D, value_0x0d); - rtk_phy_write(phy_reg, PHY_ADDR_0X10, value_0x10); - - phy_cfg->do_toggle = false; - } - - if (phy_cfg->check_rx_front_end_offset) { - u16 rx_offset_code, rx_offset_range; - u16 code_mask = REG_0X1F_RX_OFFSET_CODE_MASK; - u16 range_mask = REG_0X0B_RX_OFFSET_RANGE_MASK; - bool do_update = false; - - rx_offset_code = rtk_phy_read(phy_reg, PHY_ADDR_0X1F); - if (((rx_offset_code & code_mask) == 0x0) || - ((rx_offset_code & code_mask) == code_mask)) - do_update = true; - - rx_offset_range = rtk_phy_read(phy_reg, PHY_ADDR_0X0B); - if (((rx_offset_range & range_mask) == range_mask) && do_update) { - dev_warn(rtk_phy->dev, "Don't update rx_offset_range (rx_offset_code=0x%x, rx_offset_range=0x%x)\n", - rx_offset_code, rx_offset_range); - do_update = false; - } - - if (do_update) { - u16 tmp1, tmp2; - - tmp1 = rx_offset_range & (~range_mask); - tmp2 = rx_offset_range & range_mask; - tmp2 += (1 << 2); - rx_offset_range = tmp1 | (tmp2 & range_mask); - rtk_phy_write(phy_reg, PHY_ADDR_0X0B, rx_offset_range); - goto do_toggle; - } - } - - return 0; -} - -static int rtk_phy_init(struct phy *phy) -{ - struct rtk_phy *rtk_phy = phy_get_drvdata(phy); - int ret = 0; - int i; - unsigned long phy_init_time = jiffies; - - for (i = 0; i < rtk_phy->num_phy; i++) - ret = do_rtk_phy_init(rtk_phy, i); - - dev_dbg(rtk_phy->dev, "Initialized RTK USB 3.0 PHY (take %dms)\n", - jiffies_to_msecs(jiffies - phy_init_time)); - - return ret; -} - -static int rtk_phy_exit(struct phy *phy) -{ - return 0; -} - -static const struct phy_ops ops = { - .init = rtk_phy_init, - .exit = rtk_phy_exit, - .owner = THIS_MODULE, -}; - -static void rtk_phy_toggle(struct usb_phy *usb3_phy, bool connect, int port) -{ - int index = port; - struct rtk_phy *rtk_phy = NULL; - - rtk_phy = dev_get_drvdata(usb3_phy->dev); - - if (index > rtk_phy->num_phy) { - dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n", - __func__, index, rtk_phy->num_phy); - return; - } - - do_rtk_usb3_phy_toggle(rtk_phy, index, connect); -} - -static int rtk_phy_notify_port_status(struct usb_phy *x, int port, - u16 portstatus, u16 portchange) -{ - bool connect = false; - - pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n", - __func__, port, (int)portstatus, (int)portchange); - if (portstatus & USB_PORT_STAT_CONNECTION) - connect = true; - - if (portchange & USB_PORT_STAT_C_CONNECTION) - rtk_phy_toggle(x, connect, port); - - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static struct dentry *create_phy_debug_root(void) -{ - struct dentry *phy_debug_root; - - phy_debug_root = debugfs_lookup("phy", usb_debug_root); - if (!phy_debug_root) - phy_debug_root = debugfs_create_dir("phy", usb_debug_root); - - return phy_debug_root; -} - -static int rtk_usb3_parameter_show(struct seq_file *s, void *unused) -{ - struct rtk_phy *rtk_phy = s->private; - struct phy_cfg *phy_cfg; - int i, index; - - phy_cfg = rtk_phy->phy_cfg; - - seq_puts(s, "Property:\n"); - seq_printf(s, " check_efuse: %s\n", - phy_cfg->check_efuse ? "Enable" : "Disable"); - seq_printf(s, " do_toggle: %s\n", - phy_cfg->do_toggle ? "Enable" : "Disable"); - seq_printf(s, " do_toggle_once: %s\n", - phy_cfg->do_toggle_once ? "Enable" : "Disable"); - seq_printf(s, " use_default_parameter: %s\n", - phy_cfg->use_default_parameter ? "Enable" : "Disable"); - - for (index = 0; index < rtk_phy->num_phy; index++) { - struct phy_reg *phy_reg; - struct phy_parameter *phy_parameter; - - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - phy_reg = &phy_parameter->phy_reg; - - seq_printf(s, "PHY %d:\n", index); - - for (i = 0; i < phy_cfg->param_size; i++) { - struct phy_data *phy_data = phy_cfg->param + i; - u8 addr = ARRAY_INDEX_MAP_PHY_ADDR(i); - u16 data = phy_data->data; - - if (!phy_data->addr && !data) - seq_printf(s, " addr = 0x%02x, data = none ==> read value = 0x%04x\n", - addr, rtk_phy_read(phy_reg, addr)); - else - seq_printf(s, " addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n", - addr, data, rtk_phy_read(phy_reg, addr)); - } - - seq_puts(s, "PHY Property:\n"); - seq_printf(s, " efuse_usb_u3_tx_lfps_swing_trim: 0x%x\n", - (int)phy_parameter->efuse_usb_u3_tx_lfps_swing_trim); - seq_printf(s, " amplitude_control_coarse: 0x%x\n", - (int)phy_parameter->amplitude_control_coarse); - seq_printf(s, " amplitude_control_fine: 0x%x\n", - (int)phy_parameter->amplitude_control_fine); - } - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(rtk_usb3_parameter); - -static inline void create_debug_files(struct rtk_phy *rtk_phy) -{ - struct dentry *phy_debug_root = NULL; - - phy_debug_root = create_phy_debug_root(); - - if (!phy_debug_root) - return; - - rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), phy_debug_root); - - debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy, - &rtk_usb3_parameter_fops); - - return; -} - -static inline void remove_debug_files(struct rtk_phy *rtk_phy) -{ - debugfs_remove_recursive(rtk_phy->debug_dir); -} -#else -static inline void create_debug_files(struct rtk_phy *rtk_phy) { } -static inline void remove_debug_files(struct rtk_phy *rtk_phy) { } -#endif /* CONFIG_DEBUG_FS */ - -static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter, int index) -{ - struct phy_cfg *phy_cfg = rtk_phy->phy_cfg; - u8 value = 0; - struct nvmem_cell *cell; - - if (!phy_cfg->check_efuse) - goto out; - - cell = nvmem_cell_get(rtk_phy->dev, "usb_u3_tx_lfps_swing_trim"); - if (IS_ERR(cell)) { - dev_dbg(rtk_phy->dev, "%s no usb_u3_tx_lfps_swing_trim: %ld\n", - __func__, PTR_ERR(cell)); - } else { - unsigned char *buf; - size_t buf_size; - - buf = nvmem_cell_read(cell, &buf_size); - if (!IS_ERR(buf)) { - value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK; - kfree(buf); - } - nvmem_cell_put(cell); - } - - if (value > 0 && value < 0x8) - phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = 0x8; - else - phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = (u8)value; - -out: - return 0; -} - -static void update_amplitude_control_value(struct rtk_phy *rtk_phy, - struct phy_parameter *phy_parameter) -{ - struct phy_cfg *phy_cfg; - struct phy_reg *phy_reg; - - phy_reg = &phy_parameter->phy_reg; - phy_cfg = rtk_phy->phy_cfg; - - if (phy_parameter->amplitude_control_coarse != AMPLITUDE_CONTROL_COARSE_DEFAULT) { - u16 val_mask = AMPLITUDE_CONTROL_COARSE_MASK; - u16 data; - - if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) { - phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20; - data = rtk_phy_read(phy_reg, PHY_ADDR_0X20); - } else { - data = phy_cfg->param[PHY_ADDR_0X20].data; - } - - data &= (~val_mask); - data |= (phy_parameter->amplitude_control_coarse & val_mask); - - phy_cfg->param[PHY_ADDR_0X20].data = data; - } - - if (phy_parameter->efuse_usb_u3_tx_lfps_swing_trim) { - u8 efuse_val = phy_parameter->efuse_usb_u3_tx_lfps_swing_trim; - u16 val_mask = USB_U3_TX_LFPS_SWING_TRIM_MASK; - int val_shift = USB_U3_TX_LFPS_SWING_TRIM_SHIFT; - u16 data; - - if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) { - phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20; - data = rtk_phy_read(phy_reg, PHY_ADDR_0X20); - } else { - data = phy_cfg->param[PHY_ADDR_0X20].data; - } - - data &= ~(val_mask << val_shift); - data |= ((efuse_val & val_mask) << val_shift); - - phy_cfg->param[PHY_ADDR_0X20].data = data; - } - - if (phy_parameter->amplitude_control_fine != AMPLITUDE_CONTROL_FINE_DEFAULT) { - u16 val_mask = AMPLITUDE_CONTROL_FINE_MASK; - - if (!phy_cfg->param[PHY_ADDR_0X21].addr && !phy_cfg->param[PHY_ADDR_0X21].data) - phy_cfg->param[PHY_ADDR_0X21].addr = PHY_ADDR_0X21; - - phy_cfg->param[PHY_ADDR_0X21].data = - phy_parameter->amplitude_control_fine & val_mask; - } -} - -static int parse_phy_data(struct rtk_phy *rtk_phy) -{ - struct device *dev = rtk_phy->dev; - struct phy_parameter *phy_parameter; - int ret = 0; - int index; - - rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) * - rtk_phy->num_phy, GFP_KERNEL); - if (!rtk_phy->phy_parameter) - return -ENOMEM; - - for (index = 0; index < rtk_phy->num_phy; index++) { - phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index]; - - phy_parameter->phy_reg.reg_mdio_ctl = of_iomap(dev->of_node, 0) + index; - - /* Amplitude control address 0x20 bit 0 to bit 7 */ - if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-coarse-tuning", - &phy_parameter->amplitude_control_coarse)) - phy_parameter->amplitude_control_coarse = AMPLITUDE_CONTROL_COARSE_DEFAULT; - - /* Amplitude control address 0x21 bit 0 to bit 16 */ - if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-fine-tuning", - &phy_parameter->amplitude_control_fine)) - phy_parameter->amplitude_control_fine = AMPLITUDE_CONTROL_FINE_DEFAULT; - - get_phy_data_by_efuse(rtk_phy, phy_parameter, index); - - update_amplitude_control_value(rtk_phy, phy_parameter); - } - - return ret; -} - -static int rtk_usb3phy_probe(struct platform_device *pdev) -{ - struct rtk_phy *rtk_phy; - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct phy_provider *phy_provider; - const struct phy_cfg *phy_cfg; - int ret; - - phy_cfg = of_device_get_match_data(dev); - if (!phy_cfg) { - dev_err(dev, "phy config are not assigned!\n"); - return -EINVAL; - } - - rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL); - if (!rtk_phy) - return -ENOMEM; - - rtk_phy->dev = &pdev->dev; - rtk_phy->phy.dev = rtk_phy->dev; - rtk_phy->phy.label = "rtk-usb3phy"; - rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status; - - rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL); - - memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg)); - - rtk_phy->num_phy = 1; - - ret = parse_phy_data(rtk_phy); - if (ret) - goto err; - - platform_set_drvdata(pdev, rtk_phy); - - generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); - - phy_set_drvdata(generic_phy, rtk_phy); - - phy_provider = devm_of_phy_provider_register(rtk_phy->dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - ret = usb_add_phy_dev(&rtk_phy->phy); - if (ret) - goto err; - - create_debug_files(rtk_phy); - -err: - return ret; -} - -static void rtk_usb3phy_remove(struct platform_device *pdev) -{ - struct rtk_phy *rtk_phy = platform_get_drvdata(pdev); - - remove_debug_files(rtk_phy); - - usb_remove_phy(&rtk_phy->phy); -} - -static const struct phy_cfg rtd1295_phy_cfg = { - .param_size = MAX_USB_PHY_DATA_SIZE, - .param = { [0] = {0x01, 0x4008}, [1] = {0x01, 0xe046}, - [2] = {0x02, 0x6046}, [3] = {0x03, 0x2779}, - [4] = {0x04, 0x72f5}, [5] = {0x05, 0x2ad3}, - [6] = {0x06, 0x000e}, [7] = {0x07, 0x2e00}, - [8] = {0x08, 0x3591}, [9] = {0x09, 0x525c}, - [10] = {0x0a, 0xa600}, [11] = {0x0b, 0xa904}, - [12] = {0x0c, 0xc000}, [13] = {0x0d, 0xef1c}, - [14] = {0x0e, 0x2000}, [15] = {0x0f, 0x0000}, - [16] = {0x10, 0x000c}, [17] = {0x11, 0x4c00}, - [18] = {0x12, 0xfc00}, [19] = {0x13, 0x0c81}, - [20] = {0x14, 0xde01}, [21] = {0x15, 0x0000}, - [22] = {0x16, 0x0000}, [23] = {0x17, 0x0000}, - [24] = {0x18, 0x0000}, [25] = {0x19, 0x4004}, - [26] = {0x1a, 0x1260}, [27] = {0x1b, 0xff00}, - [28] = {0x1c, 0xcb00}, [29] = {0x1d, 0xa03f}, - [30] = {0x1e, 0xc2e0}, [31] = {0x1f, 0x2807}, - [32] = {0x20, 0x947a}, [33] = {0x21, 0x88aa}, - [34] = {0x22, 0x0057}, [35] = {0x23, 0xab66}, - [36] = {0x24, 0x0800}, [37] = {0x25, 0x0000}, - [38] = {0x26, 0x040a}, [39] = {0x27, 0x01d6}, - [40] = {0x28, 0xf8c2}, [41] = {0x29, 0x3080}, - [42] = {0x2a, 0x3082}, [43] = {0x2b, 0x2078}, - [44] = {0x2c, 0xffff}, [45] = {0x2d, 0xffff}, - [46] = {0x2e, 0x0000}, [47] = {0x2f, 0x0040}, }, - .check_efuse = false, - .do_toggle = true, - .do_toggle_once = false, - .use_default_parameter = false, - .check_rx_front_end_offset = false, -}; - -static const struct phy_cfg rtd1619_phy_cfg = { - .param_size = MAX_USB_PHY_DATA_SIZE, - .param = { [8] = {0x08, 0x3591}, - [38] = {0x26, 0x840b}, - [40] = {0x28, 0xf842}, }, - .check_efuse = false, - .do_toggle = true, - .do_toggle_once = false, - .use_default_parameter = false, - .check_rx_front_end_offset = false, -}; - -static const struct phy_cfg rtd1319_phy_cfg = { - .param_size = MAX_USB_PHY_DATA_SIZE, - .param = { [1] = {0x01, 0xac86}, - [6] = {0x06, 0x0003}, - [9] = {0x09, 0x924c}, - [10] = {0x0a, 0xa608}, - [11] = {0x0b, 0xb905}, - [14] = {0x0e, 0x2010}, - [32] = {0x20, 0x705a}, - [33] = {0x21, 0xf645}, - [34] = {0x22, 0x0013}, - [35] = {0x23, 0xcb66}, - [41] = {0x29, 0xff00}, }, - .check_efuse = true, - .do_toggle = true, - .do_toggle_once = false, - .use_default_parameter = false, - .check_rx_front_end_offset = false, -}; - -static const struct phy_cfg rtd1619b_phy_cfg = { - .param_size = MAX_USB_PHY_DATA_SIZE, - .param = { [1] = {0x01, 0xac8c}, - [6] = {0x06, 0x0017}, - [9] = {0x09, 0x724c}, - [10] = {0x0a, 0xb610}, - [11] = {0x0b, 0xb90d}, - [13] = {0x0d, 0xef2a}, - [15] = {0x0f, 0x9050}, - [16] = {0x10, 0x000c}, - [32] = {0x20, 0x70ff}, - [34] = {0x22, 0x0013}, - [35] = {0x23, 0xdb66}, - [38] = {0x26, 0x8609}, - [41] = {0x29, 0xff13}, - [42] = {0x2a, 0x3070}, }, - .check_efuse = true, - .do_toggle = false, - .do_toggle_once = true, - .use_default_parameter = false, - .check_rx_front_end_offset = false, -}; - -static const struct phy_cfg rtd1319d_phy_cfg = { - .param_size = MAX_USB_PHY_DATA_SIZE, - .param = { [1] = {0x01, 0xac89}, - [4] = {0x04, 0xf2f5}, - [6] = {0x06, 0x0017}, - [9] = {0x09, 0x424c}, - [10] = {0x0a, 0x9610}, - [11] = {0x0b, 0x9901}, - [12] = {0x0c, 0xf000}, - [13] = {0x0d, 0xef2a}, - [14] = {0x0e, 0x1000}, - [15] = {0x0f, 0x9050}, - [32] = {0x20, 0x7077}, - [35] = {0x23, 0x0b62}, - [37] = {0x25, 0x10ec}, - [42] = {0x2a, 0x3070}, }, - .check_efuse = true, - .do_toggle = false, - .do_toggle_once = true, - .use_default_parameter = false, - .check_rx_front_end_offset = true, -}; - -static const struct of_device_id usbphy_rtk_dt_match[] = { - { .compatible = "realtek,rtd1295-usb3phy", .data = &rtd1295_phy_cfg }, - { .compatible = "realtek,rtd1319-usb3phy", .data = &rtd1319_phy_cfg }, - { .compatible = "realtek,rtd1319d-usb3phy", .data = &rtd1319d_phy_cfg }, - { .compatible = "realtek,rtd1619-usb3phy", .data = &rtd1619_phy_cfg }, - { .compatible = "realtek,rtd1619b-usb3phy", .data = &rtd1619b_phy_cfg }, - {}, -}; -MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match); - -static struct platform_driver rtk_usb3phy_driver = { - .probe = rtk_usb3phy_probe, - .remove_new = rtk_usb3phy_remove, - .driver = { - .name = "rtk-usb3phy", - .of_match_table = usbphy_rtk_dt_match, - }, -}; - -module_platform_driver(rtk_usb3phy_driver); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform: rtk-usb3phy"); -MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>"); -MODULE_DESCRIPTION("Realtek usb 3.0 phy driver"); diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index cd6ac04c1468..c3104714b480 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -964,33 +964,6 @@ static const struct pci_device_id pmc_pci_ids[] = { { } }; -static int amd_pmc_get_dram_size(struct amd_pmc_dev *dev) -{ - int ret; - - switch (dev->cpu_id) { - case AMD_CPU_ID_YC: - if (!(dev->major > 90 || (dev->major == 90 && dev->minor > 39))) { - ret = -EINVAL; - goto err_dram_size; - } - break; - default: - ret = -EINVAL; - goto err_dram_size; - } - - ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); - if (ret || !dev->dram_size) - goto err_dram_size; - - return 0; - -err_dram_size: - dev_err(dev->dev, "DRAM size command not supported for this platform\n"); - return ret; -} - static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) { u32 phys_addr_low, phys_addr_hi; @@ -1009,8 +982,8 @@ static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) return -EIO; /* Get DRAM size */ - ret = amd_pmc_get_dram_size(dev); - if (ret) + ret = amd_pmc_send_cmd(dev, S2D_DRAM_SIZE, &dev->dram_size, dev->s2d_msg_id, true); + if (ret || !dev->dram_size) dev->dram_size = S2D_TELEMETRY_DRAMBYTES_MAX; /* Get STB DRAM address */ diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 5798b49ddaba..8c9f4f3227fc 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -588,17 +588,14 @@ static void release_attributes_data(void) static int hp_add_other_attributes(int attr_type) { struct kobject *attr_name_kobj; - union acpi_object *obj = NULL; int ret; char *attr_name; - mutex_lock(&bioscfg_drv.mutex); - attr_name_kobj = kzalloc(sizeof(*attr_name_kobj), GFP_KERNEL); - if (!attr_name_kobj) { - ret = -ENOMEM; - goto err_other_attr_init; - } + if (!attr_name_kobj) + return -ENOMEM; + + mutex_lock(&bioscfg_drv.mutex); /* Check if attribute type is supported */ switch (attr_type) { @@ -615,14 +612,14 @@ static int hp_add_other_attributes(int attr_type) default: pr_err("Error: Unknown attr_type: %d\n", attr_type); ret = -EINVAL; - goto err_other_attr_init; + kfree(attr_name_kobj); + goto unlock_drv_mutex; } ret = kobject_init_and_add(attr_name_kobj, &attr_name_ktype, NULL, "%s", attr_name); if (ret) { pr_err("Error encountered [%d]\n", ret); - kobject_put(attr_name_kobj); goto err_other_attr_init; } @@ -630,27 +627,26 @@ static int hp_add_other_attributes(int attr_type) switch (attr_type) { case HPWMI_SECURE_PLATFORM_TYPE: ret = hp_populate_secure_platform_data(attr_name_kobj); - if (ret) - goto err_other_attr_init; break; case HPWMI_SURE_START_TYPE: ret = hp_populate_sure_start_data(attr_name_kobj); - if (ret) - goto err_other_attr_init; break; default: ret = -EINVAL; - goto err_other_attr_init; } + if (ret) + goto err_other_attr_init; + mutex_unlock(&bioscfg_drv.mutex); return 0; err_other_attr_init: + kobject_put(attr_name_kobj); +unlock_drv_mutex: mutex_unlock(&bioscfg_drv.mutex); - kfree(obj); return ret; } diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index ac037540acfc..88eefccb6ed2 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1425,18 +1425,17 @@ static int ideapad_kbd_bl_init(struct ideapad_private *priv) if (WARN_ON(priv->kbd_bl.initialized)) return -EEXIST; - brightness = ideapad_kbd_bl_brightness_get(priv); - if (brightness < 0) - return brightness; - - priv->kbd_bl.last_brightness = brightness; - if (ideapad_kbd_bl_check_tristate(priv->kbd_bl.type)) { priv->kbd_bl.led.max_brightness = 2; } else { priv->kbd_bl.led.max_brightness = 1; } + brightness = ideapad_kbd_bl_brightness_get(priv); + if (brightness < 0) + return brightness; + + priv->kbd_bl.last_brightness = brightness; priv->kbd_bl.led.name = "platform::" LED_FUNCTION_KBD_BACKLIGHT; priv->kbd_bl.led.brightness_get = ideapad_kbd_bl_led_cdev_brightness_get; priv->kbd_bl.led.brightness_set_blocking = ideapad_kbd_bl_led_cdev_brightness_set; diff --git a/drivers/platform/x86/intel/telemetry/core.c b/drivers/platform/x86/intel/telemetry/core.c index fdf55b5d6948..e4be40f73eeb 100644 --- a/drivers/platform/x86/intel/telemetry/core.c +++ b/drivers/platform/x86/intel/telemetry/core.c @@ -102,7 +102,7 @@ static const struct telemetry_core_ops telm_defpltops = { /** * telemetry_update_events() - Update telemetry Configuration * @pss_evtconfig: PSS related config. No change if num_evts = 0. - * @pss_evtconfig: IOSS related config. No change if num_evts = 0. + * @ioss_evtconfig: IOSS related config. No change if num_evts = 0. * * This API updates the IOSS & PSS Telemetry configuration. Old config * is overwritten. Call telemetry_reset_events when logging is over @@ -176,7 +176,7 @@ EXPORT_SYMBOL_GPL(telemetry_reset_events); /** * telemetry_get_eventconfig() - Returns the pss and ioss events enabled * @pss_evtconfig: Pointer to PSS related configuration. - * @pss_evtconfig: Pointer to IOSS related configuration. + * @ioss_evtconfig: Pointer to IOSS related configuration. * @pss_len: Number of u32 elements allocated for pss_evtconfig array * @ioss_len: Number of u32 elements allocated for ioss_evtconfig array * diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 3f7a74788802..7513018c9f9a 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -572,7 +572,8 @@ ssize_t ptp_read(struct posix_clock_context *pccontext, uint rdflags, for (i = 0; i < cnt; i++) { event[i] = queue->buf[queue->head]; - queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; + /* Paired with READ_ONCE() in queue_cnt() */ + WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS); } spin_unlock_irqrestore(&queue->lock, flags); diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 3134568af622..15b804ba4868 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -57,10 +57,11 @@ static void enqueue_external_timestamp(struct timestamp_event_queue *queue, dst->t.sec = seconds; dst->t.nsec = remainder; + /* Both WRITE_ONCE() are paired with READ_ONCE() in queue_cnt() */ if (!queue_free(queue)) - queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; + WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS); - queue->tail = (queue->tail + 1) % PTP_MAX_TIMESTAMPS; + WRITE_ONCE(queue->tail, (queue->tail + 1) % PTP_MAX_TIMESTAMPS); spin_unlock_irqrestore(&queue->lock, flags); } diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h index 35fde0a05746..45f9002a5dca 100644 --- a/drivers/ptp/ptp_private.h +++ b/drivers/ptp/ptp_private.h @@ -85,9 +85,13 @@ struct ptp_vclock { * that a writer might concurrently increment the tail does not * matter, since the queue remains nonempty nonetheless. */ -static inline int queue_cnt(struct timestamp_event_queue *q) +static inline int queue_cnt(const struct timestamp_event_queue *q) { - int cnt = q->tail - q->head; + /* + * Paired with WRITE_ONCE() in enqueue_external_timestamp(), + * ptp_read(), extts_fifo_show(). + */ + int cnt = READ_ONCE(q->tail) - READ_ONCE(q->head); return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt; } diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c index 7d023d9d0acb..f7a499a1bd39 100644 --- a/drivers/ptp/ptp_sysfs.c +++ b/drivers/ptp/ptp_sysfs.c @@ -94,7 +94,8 @@ static ssize_t extts_fifo_show(struct device *dev, qcnt = queue_cnt(queue); if (qcnt) { event = queue->buf[queue->head]; - queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS; + /* Paired with READ_ONCE() in queue_cnt() */ + WRITE_ONCE(queue->head, (queue->head + 1) % PTP_MAX_TIMESTAMPS); } spin_unlock_irqrestore(&queue->lock, flags); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index d440319a7945..833cfab7d877 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -676,18 +676,20 @@ static void dasd_profile_start(struct dasd_block *block, * we count each request only once. */ device = cqr->startdev; - if (device->profile.data) { - counter = 1; /* request is not yet queued on the start device */ - list_for_each(l, &device->ccw_queue) - if (++counter >= 31) - break; - } + if (!device->profile.data) + return; + + spin_lock(get_ccwdev_lock(device->cdev)); + counter = 1; /* request is not yet queued on the start device */ + list_for_each(l, &device->ccw_queue) + if (++counter >= 31) + break; + spin_unlock(get_ccwdev_lock(device->cdev)); + spin_lock(&device->profile.lock); - if (device->profile.data) { - device->profile.data->dasd_io_nr_req[counter]++; - if (rq_data_dir(req) == READ) - device->profile.data->dasd_read_nr_req[counter]++; - } + device->profile.data->dasd_io_nr_req[counter]++; + if (rq_data_dir(req) == READ) + device->profile.data->dasd_read_nr_req[counter]++; spin_unlock(&device->profile.lock); } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 2e663131adaf..1b1b8a41c4d4 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -283,7 +283,7 @@ struct dasd_pprc_dev_info { __u8 secondary; /* 7 Secondary device address */ __u16 pprc_id; /* 8-9 Peer-to-Peer Remote Copy ID */ __u8 reserved2[12]; /* 10-21 reserved */ - __u16 prim_cu_ssid; /* 22-23 Pimary Control Unit SSID */ + __u16 prim_cu_ssid; /* 22-23 Primary Control Unit SSID */ __u8 reserved3[12]; /* 24-35 reserved */ __u16 sec_cu_ssid; /* 36-37 Secondary Control Unit SSID */ __u8 reserved4[90]; /* 38-127 reserved */ diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 4902d45e929c..c61e6427384c 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -103,10 +103,11 @@ config CCWGROUP config ISM tristate "Support for ISM vPCI Adapter" depends on PCI + imply SMC default n help Select this option if you want to use the Internal Shared Memory - vPCI Adapter. + vPCI Adapter. The adapter can be used with the SMC network protocol. To compile as a module choose M. The module name is ism. If unsure, choose N. diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 6df7f377d2f9..81aabbfbbe2c 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -30,7 +30,6 @@ static const struct pci_device_id ism_device_table[] = { MODULE_DEVICE_TABLE(pci, ism_device_table); static debug_info_t *ism_debug_info; -static const struct smcd_ops ism_ops; #define NO_CLIENT 0xff /* must be >= MAX_CLIENTS */ static struct ism_client *clients[MAX_CLIENTS]; /* use an array rather than */ @@ -289,22 +288,6 @@ out: return ret; } -static int ism_query_rgid(struct ism_dev *ism, u64 rgid, u32 vid_valid, - u32 vid) -{ - union ism_query_rgid cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.request.hdr.cmd = ISM_QUERY_RGID; - cmd.request.hdr.len = sizeof(cmd.request); - - cmd.request.rgid = rgid; - cmd.request.vlan_valid = vid_valid; - cmd.request.vlan_id = vid; - - return ism_cmd(ism, &cmd); -} - static void ism_free_dmb(struct ism_dev *ism, struct ism_dmb *dmb) { clear_bit(dmb->sba_idx, ism->sba_bitmap); @@ -429,23 +412,6 @@ static int ism_del_vlan_id(struct ism_dev *ism, u64 vlan_id) return ism_cmd(ism, &cmd); } -static int ism_signal_ieq(struct ism_dev *ism, u64 rgid, u32 trigger_irq, - u32 event_code, u64 info) -{ - union ism_sig_ieq cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.request.hdr.cmd = ISM_SIGNAL_IEQ; - cmd.request.hdr.len = sizeof(cmd.request); - - cmd.request.rgid = rgid; - cmd.request.trigger_irq = trigger_irq; - cmd.request.event_code = event_code; - cmd.request.info = info; - - return ism_cmd(ism, &cmd); -} - static unsigned int max_bytes(unsigned int start, unsigned int len, unsigned int boundary) { @@ -503,14 +469,6 @@ u8 *ism_get_seid(void) } EXPORT_SYMBOL_GPL(ism_get_seid); -static u16 ism_get_chid(struct ism_dev *ism) -{ - if (!ism || !ism->pdev) - return 0; - - return to_zpci(ism->pdev)->pchid; -} - static void ism_handle_event(struct ism_dev *ism) { struct ism_event *entry; @@ -569,11 +527,6 @@ static irqreturn_t ism_handle_irq(int irq, void *data) return IRQ_HANDLED; } -static u64 ism_get_local_gid(struct ism_dev *ism) -{ - return ism->local_gid; -} - static int ism_dev_init(struct ism_dev *ism) { struct pci_dev *pdev = ism->pdev; @@ -774,6 +727,22 @@ module_exit(ism_exit); /*************************** SMC-D Implementation *****************************/ #if IS_ENABLED(CONFIG_SMC) +static int ism_query_rgid(struct ism_dev *ism, u64 rgid, u32 vid_valid, + u32 vid) +{ + union ism_query_rgid cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_QUERY_RGID; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.rgid = rgid; + cmd.request.vlan_valid = vid_valid; + cmd.request.vlan_id = vid; + + return ism_cmd(ism, &cmd); +} + static int smcd_query_rgid(struct smcd_dev *smcd, u64 rgid, u32 vid_valid, u32 vid) { @@ -811,6 +780,23 @@ static int smcd_reset_vlan_required(struct smcd_dev *smcd) return ism_cmd_simple(smcd->priv, ISM_RESET_VLAN); } +static int ism_signal_ieq(struct ism_dev *ism, u64 rgid, u32 trigger_irq, + u32 event_code, u64 info) +{ + union ism_sig_ieq cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.request.hdr.cmd = ISM_SIGNAL_IEQ; + cmd.request.hdr.len = sizeof(cmd.request); + + cmd.request.rgid = rgid; + cmd.request.trigger_irq = trigger_irq; + cmd.request.event_code = event_code; + cmd.request.info = info; + + return ism_cmd(ism, &cmd); +} + static int smcd_signal_ieq(struct smcd_dev *smcd, u64 rgid, u32 trigger_irq, u32 event_code, u64 info) { @@ -830,11 +816,24 @@ static int smcd_supports_v2(void) SYSTEM_EID.type[0] != '0'; } +static u64 ism_get_local_gid(struct ism_dev *ism) +{ + return ism->local_gid; +} + static u64 smcd_get_local_gid(struct smcd_dev *smcd) { return ism_get_local_gid(smcd->priv); } +static u16 ism_get_chid(struct ism_dev *ism) +{ + if (!ism || !ism->pdev) + return 0; + + return to_zpci(ism->pdev)->pchid; +} + static u16 smcd_get_chid(struct smcd_dev *smcd) { return ism_get_chid(smcd->priv); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 32d1e73e46ee..03348f605c2e 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1837,8 +1837,16 @@ static void qla2x00_abort_srb(struct qla_qpair *qp, srb_t *sp, const int res, } spin_lock_irqsave(qp->qp_lock_ptr, *flags); - if (ret_cmd && blk_mq_request_started(scsi_cmd_to_rq(cmd))) - sp->done(sp, res); + switch (sp->type) { + case SRB_SCSI_CMD: + if (ret_cmd && blk_mq_request_started(scsi_cmd_to_rq(cmd))) + sp->done(sp, res); + break; + default: + if (ret_cmd) + sp->done(sp, res); + break; + } } else { sp->done(sp, res); } diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 67922e2c4c19..6d8218a44122 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1019,7 +1019,7 @@ static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf, struct sdebug_err_inject *inject; struct scsi_device *sdev = (struct scsi_device *)file->f_inode->i_private; - buf = kmalloc(count, GFP_KERNEL); + buf = kzalloc(count + 1, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -1132,7 +1132,6 @@ static const struct file_operations sdebug_target_reset_fail_fops = { static int sdebug_target_alloc(struct scsi_target *starget) { struct sdebug_target_info *targetip; - struct dentry *dentry; targetip = kzalloc(sizeof(struct sdebug_target_info), GFP_KERNEL); if (!targetip) @@ -1140,15 +1139,9 @@ static int sdebug_target_alloc(struct scsi_target *starget) targetip->debugfs_entry = debugfs_create_dir(dev_name(&starget->dev), sdebug_debugfs_root); - if (IS_ERR_OR_NULL(targetip->debugfs_entry)) - pr_info("%s: failed to create debugfs directory for target %s\n", - __func__, dev_name(&starget->dev)); debugfs_create_file("fail_reset", 0600, targetip->debugfs_entry, starget, &sdebug_target_reset_fail_fops); - if (IS_ERR_OR_NULL(dentry)) - pr_info("%s: failed to create fail_reset file for target %s\n", - __func__, dev_name(&starget->dev)); starget->hostdata = targetip; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 530918cbfce2..fa00dd503cbf 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1643,24 +1643,21 @@ out: return disk_changed ? DISK_EVENT_MEDIA_CHANGE : 0; } -static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) +static int sd_sync_cache(struct scsi_disk *sdkp) { int retries, res; struct scsi_device *sdp = sdkp->device; const int timeout = sdp->request_queue->rq_timeout * SD_FLUSH_TIMEOUT_MULTIPLIER; - struct scsi_sense_hdr my_sshdr; + struct scsi_sense_hdr sshdr; const struct scsi_exec_args exec_args = { .req_flags = BLK_MQ_REQ_PM, - /* caller might not be interested in sense, but we need it */ - .sshdr = sshdr ? : &my_sshdr, + .sshdr = &sshdr, }; if (!scsi_device_online(sdp)) return -ENODEV; - sshdr = exec_args.sshdr; - for (retries = 3; retries > 0; --retries) { unsigned char cmd[16] = { 0 }; @@ -1685,15 +1682,23 @@ static int sd_sync_cache(struct scsi_disk *sdkp, struct scsi_sense_hdr *sshdr) return res; if (scsi_status_is_check_condition(res) && - scsi_sense_valid(sshdr)) { - sd_print_sense_hdr(sdkp, sshdr); + scsi_sense_valid(&sshdr)) { + sd_print_sense_hdr(sdkp, &sshdr); /* we need to evaluate the error return */ - if (sshdr->asc == 0x3a || /* medium not present */ - sshdr->asc == 0x20 || /* invalid command */ - (sshdr->asc == 0x74 && sshdr->ascq == 0x71)) /* drive is password locked */ + if (sshdr.asc == 0x3a || /* medium not present */ + sshdr.asc == 0x20 || /* invalid command */ + (sshdr.asc == 0x74 && sshdr.ascq == 0x71)) /* drive is password locked */ /* this is no error here */ return 0; + /* + * This drive doesn't support sync and there's not much + * we can do because this is called during shutdown + * or suspend so just return success so those operations + * can proceed. + */ + if (sshdr.sense_key == ILLEGAL_REQUEST) + return 0; } switch (host_byte(res)) { @@ -3853,7 +3858,7 @@ static void sd_shutdown(struct device *dev) if (sdkp->WCE && sdkp->media_present) { sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); - sd_sync_cache(sdkp, NULL); + sd_sync_cache(sdkp); } if ((system_state != SYSTEM_RESTART && @@ -3874,7 +3879,6 @@ static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime) static int sd_suspend_common(struct device *dev, bool runtime) { struct scsi_disk *sdkp = dev_get_drvdata(dev); - struct scsi_sense_hdr sshdr; int ret = 0; if (!sdkp) /* E.g.: runtime suspend following sd_remove() */ @@ -3883,24 +3887,13 @@ static int sd_suspend_common(struct device *dev, bool runtime) if (sdkp->WCE && sdkp->media_present) { if (!sdkp->device->silence_suspend) sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); - ret = sd_sync_cache(sdkp, &sshdr); - - if (ret) { - /* ignore OFFLINE device */ - if (ret == -ENODEV) - return 0; - - if (!scsi_sense_valid(&sshdr) || - sshdr.sense_key != ILLEGAL_REQUEST) - return ret; + ret = sd_sync_cache(sdkp); + /* ignore OFFLINE device */ + if (ret == -ENODEV) + return 0; - /* - * sshdr.sense_key == ILLEGAL_REQUEST means this drive - * doesn't support sync. There's not much to do and - * suspend shouldn't fail. - */ - ret = 0; - } + if (ret) + return ret; } if (sd_do_start_stop(sdkp->device, runtime)) { diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 1e15ffa79295..44e9b09de47a 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1143,7 +1143,7 @@ int tb_port_lane_bonding_enable(struct tb_port *port) * Only set bonding if the link was not already bonded. This * avoids the lane adapter to re-enter bonding state. */ - if (width == TB_LINK_WIDTH_SINGLE) { + if (width == TB_LINK_WIDTH_SINGLE && !tb_is_upstream_port(port)) { ret = tb_port_set_lane_bonding(port, true); if (ret) goto err_lane1; @@ -2880,6 +2880,7 @@ static int tb_switch_lane_bonding_disable(struct tb_switch *sw) return tb_port_wait_for_link_width(down, TB_LINK_WIDTH_SINGLE, 100); } +/* Note updating sw->link_width done in tb_switch_update_link_attributes() */ static int tb_switch_asym_enable(struct tb_switch *sw, enum tb_link_width width) { struct tb_port *up, *down, *port; @@ -2919,10 +2920,10 @@ static int tb_switch_asym_enable(struct tb_switch *sw, enum tb_link_width width) return ret; } - sw->link_width = width; return 0; } +/* Note updating sw->link_width done in tb_switch_update_link_attributes() */ static int tb_switch_asym_disable(struct tb_switch *sw) { struct tb_port *up, *down; @@ -2957,7 +2958,6 @@ static int tb_switch_asym_disable(struct tb_switch *sw) return ret; } - sw->link_width = TB_LINK_WIDTH_DUAL; return 0; } diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 5acdeb766860..fd49f86e0353 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -213,7 +213,17 @@ static void tb_add_dp_resources(struct tb_switch *sw) if (!tb_switch_query_dp_resource(sw, port)) continue; - list_add(&port->list, &tcm->dp_resources); + /* + * If DP IN on device router exist, position it at the + * beginning of the DP resources list, so that it is used + * before DP IN of the host router. This way external GPU(s) + * will be prioritized when pairing DP IN to a DP OUT. + */ + if (tb_route(sw)) + list_add(&port->list, &tcm->dp_resources); + else + list_add_tail(&port->list, &tcm->dp_resources); + tb_port_dbg(port, "DP IN resource available\n"); } } diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 2ba8ec254dce..0787456c2b89 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -436,7 +436,7 @@ int ufshcd_mcq_init(struct ufs_hba *hba) for (i = 0; i < hba->nr_hw_queues; i++) { hwq = &hba->uhq[i]; - hwq->max_entries = hba->nutrs; + hwq->max_entries = hba->nutrs + 1; spin_lock_init(&hwq->sq_lock); spin_lock_init(&hwq->cq_lock); mutex_init(&hwq->sq_mutex); @@ -630,6 +630,7 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) int tag = scsi_cmd_to_rq(cmd)->tag; struct ufshcd_lrb *lrbp = &hba->lrb[tag]; struct ufs_hw_queue *hwq; + unsigned long flags; int err = FAILED; if (!ufshcd_cmd_inflight(lrbp->cmd)) { @@ -670,8 +671,10 @@ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) } err = SUCCESS; + spin_lock_irqsave(&hwq->cq_lock, flags); if (ufshcd_cmd_inflight(lrbp->cmd)) ufshcd_release_scsi_cmd(hba, lrbp); + spin_unlock_irqrestore(&hwq->cq_lock, flags); out: return err; diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index af981778382d..02f297f5637d 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1529,6 +1529,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data) unsigned long flags; int counter = 0; + local_bh_disable(); spin_lock_irqsave(&pdev->lock, flags); if (pdev->cdnsp_state & (CDNSP_STATE_HALTED | CDNSP_STATE_DYING)) { @@ -1541,6 +1542,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data) cdnsp_died(pdev); spin_unlock_irqrestore(&pdev->lock, flags); + local_bh_enable(); return IRQ_HANDLED; } @@ -1557,6 +1559,7 @@ irqreturn_t cdnsp_thread_irq_handler(int irq, void *data) cdnsp_update_erst_dequeue(pdev, event_ring_deq, 1); spin_unlock_irqrestore(&pdev->lock, flags); + local_bh_enable(); return IRQ_HANDLED; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index b19e38d5fd10..7f8d33f92ddb 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1047,7 +1047,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { dev_notice(ddev, "descriptor type invalid, skip\n"); - continue; + goto skip_to_next_descriptor; } switch (cap_type) { @@ -1078,6 +1078,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) break; } +skip_to_next_descriptor: total_len -= length; buffer += length; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b4584a0cd484..87480a6e6d93 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -622,29 +622,6 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, ret = 0; } mutex_unlock(&hub->status_mutex); - - /* - * There is no need to lock status_mutex here, because status_mutex - * protects hub->status, and the phy driver only checks the port - * status without changing the status. - */ - if (!ret) { - struct usb_device *hdev = hub->hdev; - - /* - * Only roothub will be notified of port state changes, - * since the USB PHY only cares about changes at the next - * level. - */ - if (is_root_hub(hdev)) { - struct usb_hcd *hcd = bus_to_hcd(hdev->bus); - - if (hcd->usb_phy) - usb_phy_notify_port_status(hcd->usb_phy, - port1 - 1, *status, *change); - } - } - return ret; } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 0144ca8350c3..5c7538d498dd 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -2015,15 +2015,17 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) { struct dwc2_qtd *qtd; struct dwc2_host_chan *chan; - u32 hcint, hcintmsk; + u32 hcint, hcintraw, hcintmsk; chan = hsotg->hc_ptr_array[chnum]; - hcint = dwc2_readl(hsotg, HCINT(chnum)); + hcintraw = dwc2_readl(hsotg, HCINT(chnum)); hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum)); + hcint = hcintraw & hcintmsk; + dwc2_writel(hsotg, hcint, HCINT(chnum)); + if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); - dwc2_writel(hsotg, hcint, HCINT(chnum)); return; } @@ -2032,11 +2034,9 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) chnum); dev_vdbg(hsotg->dev, " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", - hcint, hcintmsk, hcint & hcintmsk); + hcintraw, hcintmsk, hcint); } - dwc2_writel(hsotg, hcint, HCINT(chnum)); - /* * If we got an interrupt after someone called * dwc2_hcd_endpoint_disable() we don't want to crash below @@ -2046,8 +2046,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) return; } - chan->hcint = hcint; - hcint &= hcintmsk; + chan->hcint = hcintraw; /* * If the channel was halted due to a dequeue, the qtd list might diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0328c86ef806..b101dbf8c5dc 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2034,6 +2034,8 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_put(dev); + dma_set_max_seg_size(dev, UINT_MAX); + return 0; err_exit_debugfs: diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 039bf241769a..57ddd2e43022 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -505,6 +505,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; mode = DWC3_GCTL_PRTCAP_DEVICE; } + dwc3_set_mode(dwc, mode); dwc3_role_switch.fwnode = dev_fwnode(dwc->dev); dwc3_role_switch.set = dwc3_usb_role_switch_set; @@ -526,7 +527,6 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) } } - dwc3_set_mode(dwc, mode); return 0; } #else diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 3de43df6bbe8..fdf6d5d3c2ad 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -546,10 +546,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) pdata ? pdata->hs_phy_irq_index : -1); if (irq > 0) { /* Keep wakeup interrupts disabled until suspend */ - irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, "qcom_dwc3 HS", qcom); if (ret) { dev_err(qcom->dev, "hs_phy_irq failed: %d\n", ret); @@ -561,10 +560,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) irq = dwc3_qcom_get_irq(pdev, "dp_hs_phy_irq", pdata ? pdata->dp_hs_phy_irq_index : -1); if (irq > 0) { - irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, "qcom_dwc3 DP_HS", qcom); if (ret) { dev_err(qcom->dev, "dp_hs_phy_irq failed: %d\n", ret); @@ -576,10 +574,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) irq = dwc3_qcom_get_irq(pdev, "dm_hs_phy_irq", pdata ? pdata->dm_hs_phy_irq_index : -1); if (irq > 0) { - irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, "qcom_dwc3 DM_HS", qcom); if (ret) { dev_err(qcom->dev, "dm_hs_phy_irq failed: %d\n", ret); @@ -591,10 +588,9 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) irq = dwc3_qcom_get_irq(pdev, "ss_phy_irq", pdata ? pdata->ss_phy_irq_index : -1); if (irq > 0) { - irq_set_status_flags(irq, IRQ_NOAUTOEN); ret = devm_request_threaded_irq(qcom->dev, irq, NULL, qcom_dwc3_resume_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_ONESHOT | IRQF_NO_AUTOEN, "qcom_dwc3 SS", qcom); if (ret) { dev_err(qcom->dev, "ss_phy_irq failed: %d\n", ret); @@ -758,6 +754,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) if (!qcom->dwc3) { ret = -ENODEV; dev_err(dev, "failed to get dwc3 platform device\n"); + of_platform_depopulate(dev); } node_put: @@ -766,9 +763,9 @@ node_put: return ret; } -static struct platform_device * -dwc3_qcom_create_urs_usb_platdev(struct device *dev) +static struct platform_device *dwc3_qcom_create_urs_usb_platdev(struct device *dev) { + struct platform_device *urs_usb = NULL; struct fwnode_handle *fwh; struct acpi_device *adev; char name[8]; @@ -788,9 +785,26 @@ dwc3_qcom_create_urs_usb_platdev(struct device *dev) adev = to_acpi_device_node(fwh); if (!adev) - return NULL; + goto err_put_handle; + + urs_usb = acpi_create_platform_device(adev, NULL); + if (IS_ERR_OR_NULL(urs_usb)) + goto err_put_handle; + + return urs_usb; + +err_put_handle: + fwnode_handle_put(fwh); + + return urs_usb; +} - return acpi_create_platform_device(adev, NULL); +static void dwc3_qcom_destroy_urs_usb_platdev(struct platform_device *urs_usb) +{ + struct fwnode_handle *fwh = urs_usb->dev.fwnode; + + platform_device_unregister(urs_usb); + fwnode_handle_put(fwh); } static int dwc3_qcom_probe(struct platform_device *pdev) @@ -874,13 +888,13 @@ static int dwc3_qcom_probe(struct platform_device *pdev) qcom->qscratch_base = devm_ioremap_resource(dev, parent_res); if (IS_ERR(qcom->qscratch_base)) { ret = PTR_ERR(qcom->qscratch_base); - goto clk_disable; + goto free_urs; } ret = dwc3_qcom_setup_irq(pdev); if (ret) { dev_err(dev, "failed to setup IRQs, err=%d\n", ret); - goto clk_disable; + goto free_urs; } /* @@ -899,7 +913,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); - goto depopulate; + goto free_urs; } ret = dwc3_qcom_interconnect_init(qcom); @@ -931,10 +945,16 @@ static int dwc3_qcom_probe(struct platform_device *pdev) interconnect_exit: dwc3_qcom_interconnect_exit(qcom); depopulate: - if (np) + if (np) { of_platform_depopulate(&pdev->dev); - else - platform_device_put(pdev); + } else { + device_remove_software_node(&qcom->dwc3->dev); + platform_device_del(qcom->dwc3); + } + platform_device_put(qcom->dwc3); +free_urs: + if (qcom->urs_usb) + dwc3_qcom_destroy_urs_usb_platdev(qcom->urs_usb); clk_disable: for (i = qcom->num_clocks - 1; i >= 0; i--) { clk_disable_unprepare(qcom->clks[i]); @@ -953,11 +973,16 @@ static void dwc3_qcom_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; - device_remove_software_node(&qcom->dwc3->dev); - if (np) + if (np) { of_platform_depopulate(&pdev->dev); - else - platform_device_put(pdev); + } else { + device_remove_software_node(&qcom->dwc3->dev); + platform_device_del(qcom->dwc3); + } + platform_device_put(qcom->dwc3); + + if (qcom->urs_usb) + dwc3_qcom_destroy_urs_usb_platdev(qcom->urs_usb); for (i = qcom->num_clocks - 1; i >= 0; i--) { clk_disable_unprepare(qcom->clks[i]); diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index 590028e8fdcb..3cd6b184551c 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -183,10 +183,13 @@ static enum usb_device_speed __get_dwc3_maximum_speed(struct device_node *np) ret = of_property_read_string(dwc3_np, "maximum-speed", &maximum_speed); if (ret < 0) - return USB_SPEED_UNKNOWN; + goto out; ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed); +out: + of_node_put(dwc3_np); + return (ret < 0) ? USB_SPEED_UNKNOWN : ret; } @@ -339,6 +342,9 @@ static int dwc3_rtk_probe_dwc3_core(struct dwc3_rtk *rtk) switch_usb2_role(rtk, rtk->cur_role); + platform_device_put(dwc3_pdev); + of_node_put(dwc3_node); + return 0; err_pdev_put: diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index 5b3cd455adec..61f3f8bbdcea 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -650,9 +650,8 @@ static int check_isoc_ss_overlap(struct mu3h_sch_ep_info *sch_ep, u32 offset) if (sch_ep->ep_type == ISOC_OUT_EP) { for (j = 0; j < sch_ep->num_budget_microframes; j++) { - k = XHCI_MTK_BW_INDEX(base + j + CS_OFFSET); - /* use cs to indicate existence of in-ss @(base+j) */ - if (tt->fs_bus_bw_in[k]) + k = XHCI_MTK_BW_INDEX(base + j); + if (tt->in_ss_cnt[k]) return -ESCH_SS_OVERLAP; } } else if (sch_ep->ep_type == ISOC_IN_EP || sch_ep->ep_type == INT_IN_EP) { @@ -769,6 +768,14 @@ static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used) tt->fs_frame_bw[f] -= (u16)sch_ep->bw_budget_table[j]; } } + + if (sch_ep->ep_type == ISOC_IN_EP || sch_ep->ep_type == INT_IN_EP) { + k = XHCI_MTK_BW_INDEX(base); + if (used) + tt->in_ss_cnt[k]++; + else + tt->in_ss_cnt[k]--; + } } if (used) diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 865b55e23b15..39f7ae7d3087 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -38,6 +38,7 @@ * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes * @ls_bus_bw: save bandwidth used by LS eps in each uframes * @fs_frame_bw: save bandwidth used by FS/LS eps in each FS frames + * @in_ss_cnt: the count of Start-Split for IN eps * @ep_list: Endpoints using this TT */ struct mu3h_sch_tt { @@ -45,6 +46,7 @@ struct mu3h_sch_tt { u16 fs_bus_bw_in[XHCI_MTK_MAX_ESIT]; u8 ls_bus_bw[XHCI_MTK_MAX_ESIT]; u16 fs_frame_bw[XHCI_MTK_FRAMES_CNT]; + u8 in_ss_cnt[XHCI_MTK_MAX_ESIT]; struct list_head ep_list; }; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index b93161374293..732cdeb73920 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/usb/phy.h> #include <linux/slab.h> @@ -148,7 +149,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s int ret; int irq; struct xhci_plat_priv *priv = NULL; - + bool of_match; if (usb_disabled()) return -ENODEV; @@ -253,16 +254,23 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s &xhci->imod_interval); } - hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0); - if (IS_ERR(hcd->usb_phy)) { - ret = PTR_ERR(hcd->usb_phy); - if (ret == -EPROBE_DEFER) - goto disable_clk; - hcd->usb_phy = NULL; - } else { - ret = usb_phy_init(hcd->usb_phy); - if (ret) - goto disable_clk; + /* + * Drivers such as dwc3 manages PHYs themself (and rely on driver name + * matching for the xhci platform device). + */ + of_match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); + if (of_match) { + hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0); + if (IS_ERR(hcd->usb_phy)) { + ret = PTR_ERR(hcd->usb_phy); + if (ret == -EPROBE_DEFER) + goto disable_clk; + hcd->usb_phy = NULL; + } else { + ret = usb_phy_init(hcd->usb_phy); + if (ret) + goto disable_clk; + } } hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); @@ -285,15 +293,17 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s goto dealloc_usb2_hcd; } - xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, - "usb-phy", 1); - if (IS_ERR(xhci->shared_hcd->usb_phy)) { - xhci->shared_hcd->usb_phy = NULL; - } else { - ret = usb_phy_init(xhci->shared_hcd->usb_phy); - if (ret) - dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n", - __func__, ret); + if (of_match) { + xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, + "usb-phy", 1); + if (IS_ERR(xhci->shared_hcd->usb_phy)) { + xhci->shared_hcd->usb_phy = NULL; + } else { + ret = usb_phy_init(xhci->shared_hcd->usb_phy); + if (ret) + dev_err(sysdev, "%s init usb3phy fail (ret=%d)\n", + __func__, ret); + } } xhci->shared_hcd->tpl_support = hcd->tpl_support; diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index a341b2fbb7b4..2b45404e9732 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -432,6 +432,8 @@ static const struct usb_device_id onboard_hub_id_table[] = { { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2412) }, /* USB2412 USB 2.0 */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */ + { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 */ + { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */ diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index c4e24a7b9290..292110e64a1d 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -16,6 +16,11 @@ static const struct onboard_hub_pdata microchip_usb424_data = { .num_supplies = 1, }; +static const struct onboard_hub_pdata microchip_usb5744_data = { + .reset_us = 0, + .num_supplies = 2, +}; + static const struct onboard_hub_pdata realtek_rts5411_data = { .reset_us = 0, .num_supplies = 1, @@ -50,6 +55,8 @@ static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb424,2412", .data = µchip_usb424_data, }, { .compatible = "usb424,2514", .data = µchip_usb424_data, }, { .compatible = "usb424,2517", .data = µchip_usb424_data, }, + { .compatible = "usb424,2744", .data = µchip_usb5744_data, }, + { .compatible = "usb424,5744", .data = µchip_usb5744_data, }, { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index c9decd0396d4..35770e608c64 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -457,8 +457,8 @@ static void ljca_auxdev_acpi_bind(struct ljca_adapter *adap, u64 adr, u8 id) { struct ljca_match_ids_walk_data wd = { 0 }; - struct acpi_device *parent, *adev; struct device *dev = adap->dev; + struct acpi_device *parent; char uid[4]; parent = ACPI_COMPANION(dev); @@ -466,17 +466,7 @@ static void ljca_auxdev_acpi_bind(struct ljca_adapter *adap, return; /* - * get auxdev ACPI handle from the ACPI device directly - * under the parent that matches _ADR. - */ - adev = acpi_find_child_device(parent, adr, false); - if (adev) { - ACPI_COMPANION_SET(&auxdev->dev, adev); - return; - } - - /* - * _ADR is a grey area in the ACPI specification, some + * Currently LJCA hw doesn't use _ADR instead the shipped * platforms use _HID to distinguish children devices. */ switch (adr) { @@ -656,10 +646,11 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap) unsigned int i; int ret; + /* Not all LJCA chips implement SPI, a timeout reading the descriptors is normal */ ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_SPI, NULL, 0, buf, sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS); if (ret < 0) - return ret; + return (ret == -ETIMEDOUT) ? 0 : ret; /* check firmware response */ desc = (struct ljca_spi_descriptor *)buf; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 45dcfaadaf98..4dffcfefd62d 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -203,8 +203,8 @@ static void option_instat_callback(struct urb *urb); #define DELL_PRODUCT_5829E_ESIM 0x81e4 #define DELL_PRODUCT_5829E 0x81e6 -#define DELL_PRODUCT_FM101R 0x8213 -#define DELL_PRODUCT_FM101R_ESIM 0x8215 +#define DELL_PRODUCT_FM101R_ESIM 0x8213 +#define DELL_PRODUCT_FM101R 0x8215 #define KYOCERA_VENDOR_ID 0x0c88 #define KYOCERA_PRODUCT_KPC650 0x17da @@ -609,6 +609,8 @@ static void option_instat_callback(struct urb *urb); #define UNISOC_VENDOR_ID 0x1782 /* TOZED LT70-C based on UNISOC SL8563 uses UNISOC's vendor ID */ #define TOZED_PRODUCT_LT70C 0x4055 +/* Luat Air72*U series based on UNISOC UIS8910 uses UNISOC's vendor ID */ +#define LUAT_PRODUCT_AIR720U 0x4e00 /* Device flags */ @@ -1546,7 +1548,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff), .driver_info = RSVD(4) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff), + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */ .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196, 0xff, 0xff, 0xff) }, @@ -2249,6 +2252,7 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ { USB_DEVICE_INTERFACE_CLASS(0x1782, 0x4d11, 0xff) }, /* Fibocom L610 (ECM/RNDIS mode) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2cb7, 0x0001, 0xff, 0xff, 0xff) }, /* Fibocom L716-EU (ECM/RNDIS mode) */ { USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */ .driver_info = RSVD(4) | RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0105, 0xff), /* Fibocom NL678 series */ @@ -2271,6 +2275,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 058d5b853b57..bfb6f9481e87 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4273,7 +4273,8 @@ static void run_state_machine(struct tcpm_port *port) current_lim = PD_P_SNK_STDBY_MW / 5; tcpm_set_current_limit(port, current_lim, 5000); /* Not sink vbus if operational current is 0mA */ - tcpm_set_charge(port, !!pdo_max_current(port->snk_pdo[0])); + tcpm_set_charge(port, !port->pd_supported || + pdo_max_current(port->snk_pdo[0])); if (!port->pd_supported) tcpm_set_state(port, SNK_READY, 0); @@ -5391,6 +5392,15 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port) if (port->bist_request == BDO_MODE_TESTDATA && port->tcpc->set_bist_data) port->tcpc->set_bist_data(port->tcpc, false); + switch (port->state) { + case ERROR_RECOVERY: + case PORT_RESET: + case PORT_RESET_WAIT_OFF: + return; + default: + break; + } + if (port->ams != NONE_AMS) port->ams = NONE_AMS; if (port->hard_reset_count < PD_N_HARD_RESET_COUNT) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 0e867f531d34..196535ad996d 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -968,16 +968,17 @@ static int tps25750_start_patch_burst_mode(struct tps6598x *tps) ret = of_property_match_string(np, "reg-names", "patch-address"); if (ret < 0) { dev_err(tps->dev, "failed to get patch-address %d\n", ret); - return ret; + goto release_fw; } ret = of_property_read_u32_index(np, "reg", ret, &addr); if (ret) - return ret; + goto release_fw; if (addr == 0 || (addr >= 0x20 && addr <= 0x23)) { dev_err(tps->dev, "wrong patch address %u\n", addr); - return -EINVAL; + ret = -EINVAL; + goto release_fw; } bpms_data.addr = (u8)addr; @@ -1226,7 +1227,10 @@ static int tps6598x_probe(struct i2c_client *client) TPS_REG_INT_PLUG_EVENT; } - tps->data = device_get_match_data(tps->dev); + if (dev_fwnode(tps->dev)) + tps->data = device_get_match_data(tps->dev); + else + tps->data = i2c_get_match_data(client); if (!tps->data) return -EINVAL; @@ -1425,7 +1429,7 @@ static const struct of_device_id tps6598x_of_match[] = { MODULE_DEVICE_TABLE(of, tps6598x_of_match); static const struct i2c_device_id tps6598x_id[] = { - { "tps6598x" }, + { "tps6598x", (kernel_ulong_t)&tps6598x_data }, { } }; MODULE_DEVICE_TABLE(i2c, tps6598x_id); diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c index b3a3cb165795..b137f3679343 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c @@ -437,7 +437,7 @@ static int vdpasim_blk_dev_add(struct vdpa_mgmt_dev *mdev, const char *name, if (blk->shared_backend) { blk->buffer = shared_buffer; } else { - blk->buffer = kvmalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT, + blk->buffer = kvzalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT, GFP_KERNEL); if (!blk->buffer) { ret = -ENOMEM; @@ -495,7 +495,7 @@ static int __init vdpasim_blk_init(void) goto parent_err; if (shared_backend) { - shared_buffer = kvmalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT, + shared_buffer = kvzalloc(VDPASIM_BLK_CAPACITY << SECTOR_SHIFT, GFP_KERNEL); if (!shared_buffer) { ret = -ENOMEM; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 30df5c58db73..da7ec77cdaff 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -1582,7 +1582,6 @@ static int vhost_vdpa_probe(struct vdpa_device *vdpa) err: put_device(&v->dev); - ida_simple_remove(&vhost_vdpa_ida, v->minor); return r; } diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 62f99f6fccd3..6f58ee276ad1 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -21,9 +21,11 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_clk.h> #include <linux/of_platform.h> #include <linux/parser.h> +#include <linux/pm_domain.h> #include <linux/regulator/consumer.h> static const struct fb_fix_screeninfo simplefb_fix = { @@ -77,6 +79,11 @@ struct simplefb_par { unsigned int clk_count; struct clk **clks; #endif +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS + unsigned int num_genpds; + struct device **genpds; + struct device_link **genpd_links; +#endif #if defined CONFIG_OF && defined CONFIG_REGULATOR bool regulators_enabled; u32 regulator_count; @@ -121,12 +128,13 @@ struct simplefb_params { u32 height; u32 stride; struct simplefb_format *format; + struct resource memory; }; static int simplefb_parse_dt(struct platform_device *pdev, struct simplefb_params *params) { - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.of_node, *mem; int ret; const char *format; int i; @@ -166,6 +174,23 @@ static int simplefb_parse_dt(struct platform_device *pdev, return -EINVAL; } + mem = of_parse_phandle(np, "memory-region", 0); + if (mem) { + ret = of_address_to_resource(mem, 0, ¶ms->memory); + if (ret < 0) { + dev_err(&pdev->dev, "failed to parse memory-region\n"); + of_node_put(mem); + return ret; + } + + if (of_property_present(np, "reg")) + dev_warn(&pdev->dev, "preferring \"memory-region\" over \"reg\" property\n"); + + of_node_put(mem); + } else { + memset(¶ms->memory, 0, sizeof(params->memory)); + } + return 0; } @@ -193,6 +218,8 @@ static int simplefb_parse_pd(struct platform_device *pdev, return -EINVAL; } + memset(¶ms->memory, 0, sizeof(params->memory)); + return 0; } @@ -411,6 +438,93 @@ static void simplefb_regulators_enable(struct simplefb_par *par, static void simplefb_regulators_destroy(struct simplefb_par *par) { } #endif +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS +static void simplefb_detach_genpds(void *res) +{ + struct simplefb_par *par = res; + unsigned int i = par->num_genpds; + + if (par->num_genpds <= 1) + return; + + while (i--) { + if (par->genpd_links[i]) + device_link_del(par->genpd_links[i]); + + if (!IS_ERR_OR_NULL(par->genpds[i])) + dev_pm_domain_detach(par->genpds[i], true); + } +} + +static int simplefb_attach_genpds(struct simplefb_par *par, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + unsigned int i; + int err; + + err = of_count_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells"); + if (err < 0) { + /* Nothing wrong if optional PDs are missing */ + if (err == -ENOENT) + return 0; + + dev_info(dev, "failed to parse power-domains: %d\n", err); + return err; + } + + par->num_genpds = err; + + /* + * Single power-domain devices are handled by the driver core, so + * nothing to do here. + */ + if (par->num_genpds <= 1) + return 0; + + par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds), + GFP_KERNEL); + if (!par->genpds) + return -ENOMEM; + + par->genpd_links = devm_kcalloc(dev, par->num_genpds, + sizeof(*par->genpd_links), + GFP_KERNEL); + if (!par->genpd_links) + return -ENOMEM; + + for (i = 0; i < par->num_genpds; i++) { + par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR(par->genpds[i])) { + err = PTR_ERR(par->genpds[i]); + if (err == -EPROBE_DEFER) { + simplefb_detach_genpds(par); + return err; + } + + dev_warn(dev, "failed to attach domain %u: %d\n", i, err); + continue; + } + + par->genpd_links[i] = device_link_add(dev, par->genpds[i], + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!par->genpd_links[i]) + dev_warn(dev, "failed to link power-domain %u\n", i); + } + + return devm_add_action_or_reset(dev, simplefb_detach_genpds, par); +} +#else +static int simplefb_attach_genpds(struct simplefb_par *par, + struct platform_device *pdev) +{ + return 0; +} +#endif + static int simplefb_probe(struct platform_device *pdev) { int ret; @@ -431,10 +545,14 @@ static int simplefb_probe(struct platform_device *pdev) if (ret) return ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "No memory resource\n"); - return -EINVAL; + if (params.memory.start == 0 && params.memory.end == 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No memory resource\n"); + return -EINVAL; + } + } else { + res = ¶ms.memory; } mem = request_mem_region(res->start, resource_size(res), "simplefb"); @@ -493,6 +611,10 @@ static int simplefb_probe(struct platform_device *pdev) if (ret < 0) goto error_clocks; + ret = simplefb_attach_genpds(par, pdev); + if (ret < 0) + goto error_regulators; + simplefb_clocks_enable(par, pdev); simplefb_regulators_enable(par, pdev); diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index c2524a7207cf..7a5593997e0e 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -242,7 +242,7 @@ void vp_del_vqs(struct virtio_device *vdev) if (v != VIRTIO_MSI_NO_VECTOR) { int irq = pci_irq_vector(vp_dev->pci_dev, v); - irq_set_affinity_hint(irq, NULL); + irq_update_affinity_hint(irq, NULL); free_irq(irq, vq); } } @@ -443,10 +443,10 @@ int vp_set_vq_affinity(struct virtqueue *vq, const struct cpumask *cpu_mask) mask = vp_dev->msix_affinity_masks[info->msix_vector]; irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector); if (!cpu_mask) - irq_set_affinity_hint(irq, NULL); + irq_update_affinity_hint(irq, NULL); else { cpumask_copy(mask, cpu_mask); - irq_set_affinity_hint(irq, mask); + irq_set_affinity_and_hint(irq, mask); } } return 0; diff --git a/drivers/virtio/virtio_pci_modern_dev.c b/drivers/virtio/virtio_pci_modern_dev.c index e2a1fe7bb66c..7de8b1ebabac 100644 --- a/drivers/virtio/virtio_pci_modern_dev.c +++ b/drivers/virtio/virtio_pci_modern_dev.c @@ -294,9 +294,10 @@ int vp_modern_probe(struct virtio_pci_modern_device *mdev) err = -EINVAL; mdev->common = vp_modern_map_capability(mdev, common, - sizeof(struct virtio_pci_common_cfg), 4, - 0, sizeof(struct virtio_pci_modern_common_cfg), - &mdev->common_len, NULL); + sizeof(struct virtio_pci_common_cfg), 4, 0, + offsetofend(struct virtio_pci_modern_common_cfg, + queue_reset), + &mdev->common_len, NULL); if (!mdev->common) goto err_map_common; mdev->isr = vp_modern_map_capability(mdev, isr, sizeof(u8), 1, diff --git a/drivers/xen/events/events_2l.c b/drivers/xen/events/events_2l.c index b8f2f971c2f0..e3585330cf98 100644 --- a/drivers/xen/events/events_2l.c +++ b/drivers/xen/events/events_2l.c @@ -171,11 +171,11 @@ static void evtchn_2l_handle_events(unsigned cpu, struct evtchn_loop_ctrl *ctrl) int i; struct shared_info *s = HYPERVISOR_shared_info; struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); + evtchn_port_t evtchn; /* Timer interrupt has highest priority. */ - irq = irq_from_virq(cpu, VIRQ_TIMER); + irq = irq_evtchn_from_virq(cpu, VIRQ_TIMER, &evtchn); if (irq != -1) { - evtchn_port_t evtchn = evtchn_from_irq(irq); word_idx = evtchn / BITS_PER_LONG; bit_idx = evtchn % BITS_PER_LONG; if (active_evtchns(cpu, s, word_idx) & (1ULL << bit_idx)) @@ -328,9 +328,9 @@ irqreturn_t xen_debug_interrupt(int irq, void *dev_id) for (i = 0; i < EVTCHN_2L_NR_CHANNELS; i++) { if (sync_test_bit(i, BM(sh->evtchn_pending))) { int word_idx = i / BITS_PER_EVTCHN_WORD; - printk(" %d: event %d -> irq %d%s%s%s\n", + printk(" %d: event %d -> irq %u%s%s%s\n", cpu_from_evtchn(i), i, - get_evtchn_to_irq(i), + irq_from_evtchn(i), sync_test_bit(word_idx, BM(&v->evtchn_pending_sel)) ? "" : " l2-clear", !sync_test_bit(i, BM(sh->evtchn_mask)) diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 6de6b084ea60..f5edb9e27e3c 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -164,6 +164,8 @@ static DEFINE_PER_CPU(int [NR_VIRQS], virq_to_irq) = {[0 ... NR_VIRQS-1] = -1}; /* IRQ <-> IPI mapping */ static DEFINE_PER_CPU(int [XEN_NR_IPIS], ipi_to_irq) = {[0 ... XEN_NR_IPIS-1] = -1}; +/* Cache for IPI event channels - needed for hot cpu unplug (avoid RCU usage). */ +static DEFINE_PER_CPU(evtchn_port_t [XEN_NR_IPIS], ipi_to_evtchn) = {[0 ... XEN_NR_IPIS-1] = 0}; /* Event channel distribution data */ static atomic_t channels_on_cpu[NR_CPUS]; @@ -172,7 +174,7 @@ static int **evtchn_to_irq; #ifdef CONFIG_X86 static unsigned long *pirq_eoi_map; #endif -static bool (*pirq_needs_eoi)(unsigned irq); +static bool (*pirq_needs_eoi)(struct irq_info *info); #define EVTCHN_ROW(e) (e / (PAGE_SIZE/sizeof(**evtchn_to_irq))) #define EVTCHN_COL(e) (e % (PAGE_SIZE/sizeof(**evtchn_to_irq))) @@ -188,7 +190,6 @@ static struct irq_chip xen_lateeoi_chip; static struct irq_chip xen_percpu_chip; static struct irq_chip xen_pirq_chip; static void enable_dynirq(struct irq_data *data); -static void disable_dynirq(struct irq_data *data); static DEFINE_PER_CPU(unsigned int, irq_epoch); @@ -246,15 +247,6 @@ static int set_evtchn_to_irq(evtchn_port_t evtchn, unsigned int irq) return 0; } -int get_evtchn_to_irq(evtchn_port_t evtchn) -{ - if (evtchn >= xen_evtchn_max_channels()) - return -1; - if (evtchn_to_irq[EVTCHN_ROW(evtchn)] == NULL) - return -1; - return READ_ONCE(evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)]); -} - /* Get info for IRQ */ static struct irq_info *info_for_irq(unsigned irq) { @@ -272,6 +264,19 @@ static void set_info_for_irq(unsigned int irq, struct irq_info *info) irq_set_chip_data(irq, info); } +static struct irq_info *evtchn_to_info(evtchn_port_t evtchn) +{ + int irq; + + if (evtchn >= xen_evtchn_max_channels()) + return NULL; + if (evtchn_to_irq[EVTCHN_ROW(evtchn)] == NULL) + return NULL; + irq = READ_ONCE(evtchn_to_irq[EVTCHN_ROW(evtchn)][EVTCHN_COL(evtchn)]); + + return (irq < 0) ? NULL : info_for_irq(irq); +} + /* Per CPU channel accounting */ static void channels_on_cpu_dec(struct irq_info *info) { @@ -298,6 +303,13 @@ static void channels_on_cpu_inc(struct irq_info *info) info->is_accounted = 1; } +static void xen_irq_free_desc(unsigned int irq) +{ + /* Legacy IRQ descriptors are managed by the arch. */ + if (irq >= nr_legacy_irqs()) + irq_free_desc(irq); +} + static void delayed_free_irq(struct work_struct *work) { struct irq_info *info = container_of(to_rcu_work(work), struct irq_info, @@ -309,14 +321,11 @@ static void delayed_free_irq(struct work_struct *work) kfree(info); - /* Legacy IRQ descriptors are managed by the arch. */ - if (irq >= nr_legacy_irqs()) - irq_free_desc(irq); + xen_irq_free_desc(irq); } /* Constructors for packed IRQ information. */ static int xen_irq_info_common_setup(struct irq_info *info, - unsigned irq, enum xen_irq_type type, evtchn_port_t evtchn, unsigned short cpu) @@ -326,29 +335,27 @@ static int xen_irq_info_common_setup(struct irq_info *info, BUG_ON(info->type != IRQT_UNBOUND && info->type != type); info->type = type; - info->irq = irq; info->evtchn = evtchn; info->cpu = cpu; info->mask_reason = EVT_MASK_REASON_EXPLICIT; raw_spin_lock_init(&info->lock); - ret = set_evtchn_to_irq(evtchn, irq); + ret = set_evtchn_to_irq(evtchn, info->irq); if (ret < 0) return ret; - irq_clear_status_flags(irq, IRQ_NOREQUEST|IRQ_NOAUTOEN); + irq_clear_status_flags(info->irq, IRQ_NOREQUEST | IRQ_NOAUTOEN); return xen_evtchn_port_setup(evtchn); } -static int xen_irq_info_evtchn_setup(unsigned irq, +static int xen_irq_info_evtchn_setup(struct irq_info *info, evtchn_port_t evtchn, struct xenbus_device *dev) { - struct irq_info *info = info_for_irq(irq); int ret; - ret = xen_irq_info_common_setup(info, irq, IRQT_EVTCHN, evtchn, 0); + ret = xen_irq_info_common_setup(info, IRQT_EVTCHN, evtchn, 0); info->u.interdomain = dev; if (dev) atomic_inc(&dev->event_channels); @@ -356,49 +363,37 @@ static int xen_irq_info_evtchn_setup(unsigned irq, return ret; } -static int xen_irq_info_ipi_setup(unsigned cpu, - unsigned irq, - evtchn_port_t evtchn, - enum ipi_vector ipi) +static int xen_irq_info_ipi_setup(struct irq_info *info, unsigned int cpu, + evtchn_port_t evtchn, enum ipi_vector ipi) { - struct irq_info *info = info_for_irq(irq); - info->u.ipi = ipi; - per_cpu(ipi_to_irq, cpu)[ipi] = irq; + per_cpu(ipi_to_irq, cpu)[ipi] = info->irq; + per_cpu(ipi_to_evtchn, cpu)[ipi] = evtchn; - return xen_irq_info_common_setup(info, irq, IRQT_IPI, evtchn, 0); + return xen_irq_info_common_setup(info, IRQT_IPI, evtchn, 0); } -static int xen_irq_info_virq_setup(unsigned cpu, - unsigned irq, - evtchn_port_t evtchn, - unsigned virq) +static int xen_irq_info_virq_setup(struct irq_info *info, unsigned int cpu, + evtchn_port_t evtchn, unsigned int virq) { - struct irq_info *info = info_for_irq(irq); - info->u.virq = virq; - per_cpu(virq_to_irq, cpu)[virq] = irq; + per_cpu(virq_to_irq, cpu)[virq] = info->irq; - return xen_irq_info_common_setup(info, irq, IRQT_VIRQ, evtchn, 0); + return xen_irq_info_common_setup(info, IRQT_VIRQ, evtchn, 0); } -static int xen_irq_info_pirq_setup(unsigned irq, - evtchn_port_t evtchn, - unsigned pirq, - unsigned gsi, - uint16_t domid, - unsigned char flags) +static int xen_irq_info_pirq_setup(struct irq_info *info, evtchn_port_t evtchn, + unsigned int pirq, unsigned int gsi, + uint16_t domid, unsigned char flags) { - struct irq_info *info = info_for_irq(irq); - info->u.pirq.pirq = pirq; info->u.pirq.gsi = gsi; info->u.pirq.domid = domid; info->u.pirq.flags = flags; - return xen_irq_info_common_setup(info, irq, IRQT_PIRQ, evtchn, 0); + return xen_irq_info_common_setup(info, IRQT_PIRQ, evtchn, 0); } static void xen_irq_info_cleanup(struct irq_info *info) @@ -412,7 +407,7 @@ static void xen_irq_info_cleanup(struct irq_info *info) /* * Accessors for packed IRQ information. */ -evtchn_port_t evtchn_from_irq(unsigned irq) +static evtchn_port_t evtchn_from_irq(unsigned int irq) { const struct irq_info *info = NULL; @@ -426,64 +421,51 @@ evtchn_port_t evtchn_from_irq(unsigned irq) unsigned int irq_from_evtchn(evtchn_port_t evtchn) { - return get_evtchn_to_irq(evtchn); + struct irq_info *info = evtchn_to_info(evtchn); + + return info ? info->irq : -1; } EXPORT_SYMBOL_GPL(irq_from_evtchn); -int irq_from_virq(unsigned int cpu, unsigned int virq) +int irq_evtchn_from_virq(unsigned int cpu, unsigned int virq, + evtchn_port_t *evtchn) { - return per_cpu(virq_to_irq, cpu)[virq]; + int irq = per_cpu(virq_to_irq, cpu)[virq]; + + *evtchn = evtchn_from_irq(irq); + + return irq; } -static enum ipi_vector ipi_from_irq(unsigned irq) +static enum ipi_vector ipi_from_irq(struct irq_info *info) { - struct irq_info *info = info_for_irq(irq); - BUG_ON(info == NULL); BUG_ON(info->type != IRQT_IPI); return info->u.ipi; } -static unsigned virq_from_irq(unsigned irq) +static unsigned int virq_from_irq(struct irq_info *info) { - struct irq_info *info = info_for_irq(irq); - BUG_ON(info == NULL); BUG_ON(info->type != IRQT_VIRQ); return info->u.virq; } -static unsigned pirq_from_irq(unsigned irq) +static unsigned int pirq_from_irq(struct irq_info *info) { - struct irq_info *info = info_for_irq(irq); - BUG_ON(info == NULL); BUG_ON(info->type != IRQT_PIRQ); return info->u.pirq.pirq; } -static enum xen_irq_type type_from_irq(unsigned irq) -{ - return info_for_irq(irq)->type; -} - -static unsigned cpu_from_irq(unsigned irq) -{ - return info_for_irq(irq)->cpu; -} - unsigned int cpu_from_evtchn(evtchn_port_t evtchn) { - int irq = get_evtchn_to_irq(evtchn); - unsigned ret = 0; - - if (irq != -1) - ret = cpu_from_irq(irq); + struct irq_info *info = evtchn_to_info(evtchn); - return ret; + return info ? info->cpu : 0; } static void do_mask(struct irq_info *info, u8 reason) @@ -515,36 +497,30 @@ static void do_unmask(struct irq_info *info, u8 reason) } #ifdef CONFIG_X86 -static bool pirq_check_eoi_map(unsigned irq) +static bool pirq_check_eoi_map(struct irq_info *info) { - return test_bit(pirq_from_irq(irq), pirq_eoi_map); + return test_bit(pirq_from_irq(info), pirq_eoi_map); } #endif -static bool pirq_needs_eoi_flag(unsigned irq) +static bool pirq_needs_eoi_flag(struct irq_info *info) { - struct irq_info *info = info_for_irq(irq); BUG_ON(info->type != IRQT_PIRQ); return info->u.pirq.flags & PIRQ_NEEDS_EOI; } -static void bind_evtchn_to_cpu(evtchn_port_t evtchn, unsigned int cpu, +static void bind_evtchn_to_cpu(struct irq_info *info, unsigned int cpu, bool force_affinity) { - int irq = get_evtchn_to_irq(evtchn); - struct irq_info *info = info_for_irq(irq); - - BUG_ON(irq == -1); - if (IS_ENABLED(CONFIG_SMP) && force_affinity) { - struct irq_data *data = irq_get_irq_data(irq); + struct irq_data *data = irq_get_irq_data(info->irq); irq_data_update_affinity(data, cpumask_of(cpu)); irq_data_update_effective_affinity(data, cpumask_of(cpu)); } - xen_evtchn_port_bind_to_cpu(evtchn, cpu, info->cpu); + xen_evtchn_port_bind_to_cpu(info->evtchn, cpu, info->cpu); channels_on_cpu_dec(info); info->cpu = cpu; @@ -601,7 +577,9 @@ static void lateeoi_list_add(struct irq_info *info) spin_lock_irqsave(&eoi->eoi_list_lock, flags); - if (list_empty(&eoi->eoi_list)) { + elem = list_first_entry_or_null(&eoi->eoi_list, struct irq_info, + eoi_list); + if (!elem || info->eoi_time < elem->eoi_time) { list_add(&info->eoi_list, &eoi->eoi_list); mod_delayed_work_on(info->eoi_cpu, system_wq, &eoi->delayed, delay); @@ -732,50 +710,49 @@ void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags) } EXPORT_SYMBOL_GPL(xen_irq_lateeoi); -static void xen_irq_init(unsigned irq) +static struct irq_info *xen_irq_init(unsigned int irq) { struct irq_info *info; info = kzalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) - panic("Unable to allocate metadata for IRQ%d\n", irq); + if (info) { + info->irq = irq; + info->type = IRQT_UNBOUND; + info->refcnt = -1; + INIT_RCU_WORK(&info->rwork, delayed_free_irq); - info->type = IRQT_UNBOUND; - info->refcnt = -1; - INIT_RCU_WORK(&info->rwork, delayed_free_irq); + set_info_for_irq(irq, info); + /* + * Interrupt affinity setting can be immediate. No point + * in delaying it until an interrupt is handled. + */ + irq_set_status_flags(irq, IRQ_MOVE_PCNTXT); - set_info_for_irq(irq, info); - /* - * Interrupt affinity setting can be immediate. No point - * in delaying it until an interrupt is handled. - */ - irq_set_status_flags(irq, IRQ_MOVE_PCNTXT); + INIT_LIST_HEAD(&info->eoi_list); + list_add_tail(&info->list, &xen_irq_list_head); + } - INIT_LIST_HEAD(&info->eoi_list); - list_add_tail(&info->list, &xen_irq_list_head); + return info; } -static int __must_check xen_allocate_irqs_dynamic(int nvec) +static struct irq_info *xen_allocate_irq_dynamic(void) { - int i, irq = irq_alloc_descs(-1, 0, nvec, -1); + int irq = irq_alloc_desc_from(0, -1); + struct irq_info *info = NULL; if (irq >= 0) { - for (i = 0; i < nvec; i++) - xen_irq_init(irq + i); + info = xen_irq_init(irq); + if (!info) + xen_irq_free_desc(irq); } - return irq; -} - -static inline int __must_check xen_allocate_irq_dynamic(void) -{ - - return xen_allocate_irqs_dynamic(1); + return info; } -static int __must_check xen_allocate_irq_gsi(unsigned gsi) +static struct irq_info *xen_allocate_irq_gsi(unsigned int gsi) { int irq; + struct irq_info *info; /* * A PV guest has no concept of a GSI (since it has no ACPI @@ -792,15 +769,15 @@ static int __must_check xen_allocate_irq_gsi(unsigned gsi) else irq = irq_alloc_desc_at(gsi, -1); - xen_irq_init(irq); + info = xen_irq_init(irq); + if (!info) + xen_irq_free_desc(irq); - return irq; + return info; } -static void xen_free_irq(unsigned irq) +static void xen_free_irq(struct irq_info *info) { - struct irq_info *info = info_for_irq(irq); - if (WARN_ON(!info)) return; @@ -821,14 +798,11 @@ static void event_handler_exit(struct irq_info *info) clear_evtchn(info->evtchn); } -static void pirq_query_unmask(int irq) +static void pirq_query_unmask(struct irq_info *info) { struct physdev_irq_status_query irq_status; - struct irq_info *info = info_for_irq(irq); - - BUG_ON(info->type != IRQT_PIRQ); - irq_status.irq = pirq_from_irq(irq); + irq_status.irq = pirq_from_irq(info); if (HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status)) irq_status.flags = 0; @@ -837,61 +811,81 @@ static void pirq_query_unmask(int irq) info->u.pirq.flags |= PIRQ_NEEDS_EOI; } -static void eoi_pirq(struct irq_data *data) +static void do_eoi_pirq(struct irq_info *info) { - struct irq_info *info = info_for_irq(data->irq); - evtchn_port_t evtchn = info ? info->evtchn : 0; - struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) }; + struct physdev_eoi eoi = { .irq = pirq_from_irq(info) }; int rc = 0; - if (!VALID_EVTCHN(evtchn)) + if (!VALID_EVTCHN(info->evtchn)) return; event_handler_exit(info); - if (pirq_needs_eoi(data->irq)) { + if (pirq_needs_eoi(info)) { rc = HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi); WARN_ON(rc); } } +static void eoi_pirq(struct irq_data *data) +{ + struct irq_info *info = info_for_irq(data->irq); + + do_eoi_pirq(info); +} + +static void do_disable_dynirq(struct irq_info *info) +{ + if (VALID_EVTCHN(info->evtchn)) + do_mask(info, EVT_MASK_REASON_EXPLICIT); +} + +static void disable_dynirq(struct irq_data *data) +{ + struct irq_info *info = info_for_irq(data->irq); + + if (info) + do_disable_dynirq(info); +} + static void mask_ack_pirq(struct irq_data *data) { - disable_dynirq(data); - eoi_pirq(data); + struct irq_info *info = info_for_irq(data->irq); + + if (info) { + do_disable_dynirq(info); + do_eoi_pirq(info); + } } -static unsigned int __startup_pirq(unsigned int irq) +static unsigned int __startup_pirq(struct irq_info *info) { struct evtchn_bind_pirq bind_pirq; - struct irq_info *info = info_for_irq(irq); - evtchn_port_t evtchn = evtchn_from_irq(irq); + evtchn_port_t evtchn = info->evtchn; int rc; - BUG_ON(info->type != IRQT_PIRQ); - if (VALID_EVTCHN(evtchn)) goto out; - bind_pirq.pirq = pirq_from_irq(irq); + bind_pirq.pirq = pirq_from_irq(info); /* NB. We are happy to share unless we are probing. */ bind_pirq.flags = info->u.pirq.flags & PIRQ_SHAREABLE ? BIND_PIRQ__WILL_SHARE : 0; rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq); if (rc != 0) { - pr_warn("Failed to obtain physical IRQ %d\n", irq); + pr_warn("Failed to obtain physical IRQ %d\n", info->irq); return 0; } evtchn = bind_pirq.port; - pirq_query_unmask(irq); + pirq_query_unmask(info); - rc = set_evtchn_to_irq(evtchn, irq); + rc = set_evtchn_to_irq(evtchn, info->irq); if (rc) goto err; info->evtchn = evtchn; - bind_evtchn_to_cpu(evtchn, 0, false); + bind_evtchn_to_cpu(info, 0, false); rc = xen_evtchn_port_setup(evtchn); if (rc) @@ -900,26 +894,28 @@ static unsigned int __startup_pirq(unsigned int irq) out: do_unmask(info, EVT_MASK_REASON_EXPLICIT); - eoi_pirq(irq_get_irq_data(irq)); + do_eoi_pirq(info); return 0; err: - pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); + pr_err("irq%d: Failed to set port to irq mapping (%d)\n", info->irq, + rc); xen_evtchn_close(evtchn); return 0; } static unsigned int startup_pirq(struct irq_data *data) { - return __startup_pirq(data->irq); + struct irq_info *info = info_for_irq(data->irq); + + return __startup_pirq(info); } static void shutdown_pirq(struct irq_data *data) { - unsigned int irq = data->irq; - struct irq_info *info = info_for_irq(irq); - evtchn_port_t evtchn = evtchn_from_irq(irq); + struct irq_info *info = info_for_irq(data->irq); + evtchn_port_t evtchn = info->evtchn; BUG_ON(info->type != IRQT_PIRQ); @@ -957,10 +953,14 @@ int xen_irq_from_gsi(unsigned gsi) } EXPORT_SYMBOL_GPL(xen_irq_from_gsi); -static void __unbind_from_irq(unsigned int irq) +static void __unbind_from_irq(struct irq_info *info, unsigned int irq) { - evtchn_port_t evtchn = evtchn_from_irq(irq); - struct irq_info *info = info_for_irq(irq); + evtchn_port_t evtchn; + + if (!info) { + xen_irq_free_desc(irq); + return; + } if (info->refcnt > 0) { info->refcnt--; @@ -968,19 +968,22 @@ static void __unbind_from_irq(unsigned int irq) return; } + evtchn = info->evtchn; + if (VALID_EVTCHN(evtchn)) { - unsigned int cpu = cpu_from_irq(irq); + unsigned int cpu = info->cpu; struct xenbus_device *dev; if (!info->is_static) xen_evtchn_close(evtchn); - switch (type_from_irq(irq)) { + switch (info->type) { case IRQT_VIRQ: - per_cpu(virq_to_irq, cpu)[virq_from_irq(irq)] = -1; + per_cpu(virq_to_irq, cpu)[virq_from_irq(info)] = -1; break; case IRQT_IPI: - per_cpu(ipi_to_irq, cpu)[ipi_from_irq(irq)] = -1; + per_cpu(ipi_to_irq, cpu)[ipi_from_irq(info)] = -1; + per_cpu(ipi_to_evtchn, cpu)[ipi_from_irq(info)] = 0; break; case IRQT_EVTCHN: dev = info->u.interdomain; @@ -994,7 +997,7 @@ static void __unbind_from_irq(unsigned int irq) xen_irq_info_cleanup(info); } - xen_free_irq(irq); + xen_free_irq(info); } /* @@ -1010,24 +1013,24 @@ static void __unbind_from_irq(unsigned int irq) int xen_bind_pirq_gsi_to_irq(unsigned gsi, unsigned pirq, int shareable, char *name) { - int irq; + struct irq_info *info; struct physdev_irq irq_op; int ret; mutex_lock(&irq_mapping_update_lock); - irq = xen_irq_from_gsi(gsi); - if (irq != -1) { + ret = xen_irq_from_gsi(gsi); + if (ret != -1) { pr_info("%s: returning irq %d for gsi %u\n", - __func__, irq, gsi); + __func__, ret, gsi); goto out; } - irq = xen_allocate_irq_gsi(gsi); - if (irq < 0) + info = xen_allocate_irq_gsi(gsi); + if (!info) goto out; - irq_op.irq = irq; + irq_op.irq = info->irq; irq_op.vector = 0; /* Only the privileged domain can do this. For non-priv, the pcifront @@ -1035,20 +1038,19 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi, * this in the priv domain. */ if (xen_initial_domain() && HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &irq_op)) { - xen_free_irq(irq); - irq = -ENOSPC; + xen_free_irq(info); + ret = -ENOSPC; goto out; } - ret = xen_irq_info_pirq_setup(irq, 0, pirq, gsi, DOMID_SELF, + ret = xen_irq_info_pirq_setup(info, 0, pirq, gsi, DOMID_SELF, shareable ? PIRQ_SHAREABLE : 0); if (ret < 0) { - __unbind_from_irq(irq); - irq = ret; + __unbind_from_irq(info, info->irq); goto out; } - pirq_query_unmask(irq); + pirq_query_unmask(info); /* We try to use the handler with the appropriate semantic for the * type of interrupt: if the interrupt is an edge triggered * interrupt we use handle_edge_irq. @@ -1065,16 +1067,18 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi, * is the right choice either way. */ if (shareable) - irq_set_chip_and_handler_name(irq, &xen_pirq_chip, + irq_set_chip_and_handler_name(info->irq, &xen_pirq_chip, handle_fasteoi_irq, name); else - irq_set_chip_and_handler_name(irq, &xen_pirq_chip, + irq_set_chip_and_handler_name(info->irq, &xen_pirq_chip, handle_edge_irq, name); + ret = info->irq; + out: mutex_unlock(&irq_mapping_update_lock); - return irq; + return ret; } #ifdef CONFIG_PCI_MSI @@ -1096,17 +1100,22 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, int pirq, int nvec, const char *name, domid_t domid) { int i, irq, ret; + struct irq_info *info; mutex_lock(&irq_mapping_update_lock); - irq = xen_allocate_irqs_dynamic(nvec); + irq = irq_alloc_descs(-1, 0, nvec, -1); if (irq < 0) goto out; for (i = 0; i < nvec; i++) { + info = xen_irq_init(irq + i); + if (!info) + goto error_irq; + irq_set_chip_and_handler_name(irq + i, &xen_pirq_chip, handle_edge_irq, name); - ret = xen_irq_info_pirq_setup(irq + i, 0, pirq + i, 0, domid, + ret = xen_irq_info_pirq_setup(info, 0, pirq + i, 0, domid, i == 0 ? 0 : PIRQ_MSI_GROUP); if (ret < 0) goto error_irq; @@ -1118,9 +1127,12 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, out: mutex_unlock(&irq_mapping_update_lock); return irq; + error_irq: - while (nvec--) - __unbind_from_irq(irq + nvec); + while (nvec--) { + info = info_for_irq(irq + nvec); + __unbind_from_irq(info, irq + nvec); + } mutex_unlock(&irq_mapping_update_lock); return ret; } @@ -1156,67 +1168,45 @@ int xen_destroy_irq(int irq) } } - xen_free_irq(irq); + xen_free_irq(info); out: mutex_unlock(&irq_mapping_update_lock); return rc; } -int xen_irq_from_pirq(unsigned pirq) -{ - int irq; - - struct irq_info *info; - - mutex_lock(&irq_mapping_update_lock); - - list_for_each_entry(info, &xen_irq_list_head, list) { - if (info->type != IRQT_PIRQ) - continue; - irq = info->irq; - if (info->u.pirq.pirq == pirq) - goto out; - } - irq = -1; -out: - mutex_unlock(&irq_mapping_update_lock); - - return irq; -} - - int xen_pirq_from_irq(unsigned irq) { - return pirq_from_irq(irq); + struct irq_info *info = info_for_irq(irq); + + return pirq_from_irq(info); } EXPORT_SYMBOL_GPL(xen_pirq_from_irq); static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip, struct xenbus_device *dev) { - int irq; - int ret; + int ret = -ENOMEM; + struct irq_info *info; if (evtchn >= xen_evtchn_max_channels()) return -ENOMEM; mutex_lock(&irq_mapping_update_lock); - irq = get_evtchn_to_irq(evtchn); + info = evtchn_to_info(evtchn); - if (irq == -1) { - irq = xen_allocate_irq_dynamic(); - if (irq < 0) + if (!info) { + info = xen_allocate_irq_dynamic(); + if (!info) goto out; - irq_set_chip_and_handler_name(irq, chip, + irq_set_chip_and_handler_name(info->irq, chip, handle_edge_irq, "event"); - ret = xen_irq_info_evtchn_setup(irq, evtchn, dev); + ret = xen_irq_info_evtchn_setup(info, evtchn, dev); if (ret < 0) { - __unbind_from_irq(irq); - irq = ret; + __unbind_from_irq(info, info->irq); goto out; } /* @@ -1226,17 +1216,17 @@ static int bind_evtchn_to_irq_chip(evtchn_port_t evtchn, struct irq_chip *chip, * affinity setting is not invoked on them so nothing would * bind the channel. */ - bind_evtchn_to_cpu(evtchn, 0, false); - } else { - struct irq_info *info = info_for_irq(irq); - if (!WARN_ON(!info || info->type != IRQT_EVTCHN)) - info->refcnt++; + bind_evtchn_to_cpu(info, 0, false); + } else if (!WARN_ON(info->type != IRQT_EVTCHN)) { + info->refcnt++; } + ret = info->irq; + out: mutex_unlock(&irq_mapping_update_lock); - return irq; + return ret; } int bind_evtchn_to_irq(evtchn_port_t evtchn) @@ -1255,18 +1245,19 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) { struct evtchn_bind_ipi bind_ipi; evtchn_port_t evtchn; - int ret, irq; + struct irq_info *info; + int ret; mutex_lock(&irq_mapping_update_lock); - irq = per_cpu(ipi_to_irq, cpu)[ipi]; + ret = per_cpu(ipi_to_irq, cpu)[ipi]; - if (irq == -1) { - irq = xen_allocate_irq_dynamic(); - if (irq < 0) + if (ret == -1) { + info = xen_allocate_irq_dynamic(); + if (!info) goto out; - irq_set_chip_and_handler_name(irq, &xen_percpu_chip, + irq_set_chip_and_handler_name(info->irq, &xen_percpu_chip, handle_percpu_irq, "ipi"); bind_ipi.vcpu = xen_vcpu_nr(cpu); @@ -1275,25 +1266,25 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) BUG(); evtchn = bind_ipi.port; - ret = xen_irq_info_ipi_setup(cpu, irq, evtchn, ipi); + ret = xen_irq_info_ipi_setup(info, cpu, evtchn, ipi); if (ret < 0) { - __unbind_from_irq(irq); - irq = ret; + __unbind_from_irq(info, info->irq); goto out; } /* * Force the affinity mask to the target CPU so proc shows * the correct target. */ - bind_evtchn_to_cpu(evtchn, cpu, true); + bind_evtchn_to_cpu(info, cpu, true); + ret = info->irq; } else { - struct irq_info *info = info_for_irq(irq); + info = info_for_irq(ret); WARN_ON(info == NULL || info->type != IRQT_IPI); } out: mutex_unlock(&irq_mapping_update_lock); - return irq; + return ret; } static int bind_interdomain_evtchn_to_irq_chip(struct xenbus_device *dev, @@ -1361,22 +1352,23 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu) { struct evtchn_bind_virq bind_virq; evtchn_port_t evtchn = 0; - int irq, ret; + struct irq_info *info; + int ret; mutex_lock(&irq_mapping_update_lock); - irq = per_cpu(virq_to_irq, cpu)[virq]; + ret = per_cpu(virq_to_irq, cpu)[virq]; - if (irq == -1) { - irq = xen_allocate_irq_dynamic(); - if (irq < 0) + if (ret == -1) { + info = xen_allocate_irq_dynamic(); + if (!info) goto out; if (percpu) - irq_set_chip_and_handler_name(irq, &xen_percpu_chip, + irq_set_chip_and_handler_name(info->irq, &xen_percpu_chip, handle_percpu_irq, "virq"); else - irq_set_chip_and_handler_name(irq, &xen_dynamic_chip, + irq_set_chip_and_handler_name(info->irq, &xen_dynamic_chip, handle_edge_irq, "virq"); bind_virq.virq = virq; @@ -1391,10 +1383,9 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu) BUG_ON(ret < 0); } - ret = xen_irq_info_virq_setup(cpu, irq, evtchn, virq); + ret = xen_irq_info_virq_setup(info, cpu, evtchn, virq); if (ret < 0) { - __unbind_from_irq(irq); - irq = ret; + __unbind_from_irq(info, info->irq); goto out; } @@ -1402,22 +1393,26 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu) * Force the affinity mask for percpu interrupts so proc * shows the correct target. */ - bind_evtchn_to_cpu(evtchn, cpu, percpu); + bind_evtchn_to_cpu(info, cpu, percpu); + ret = info->irq; } else { - struct irq_info *info = info_for_irq(irq); + info = info_for_irq(ret); WARN_ON(info == NULL || info->type != IRQT_VIRQ); } out: mutex_unlock(&irq_mapping_update_lock); - return irq; + return ret; } static void unbind_from_irq(unsigned int irq) { + struct irq_info *info; + mutex_lock(&irq_mapping_update_lock); - __unbind_from_irq(irq); + info = info_for_irq(irq); + __unbind_from_irq(info, irq); mutex_unlock(&irq_mapping_update_lock); } @@ -1568,13 +1563,7 @@ EXPORT_SYMBOL_GPL(xen_set_irq_priority); int evtchn_make_refcounted(evtchn_port_t evtchn, bool is_static) { - int irq = get_evtchn_to_irq(evtchn); - struct irq_info *info; - - if (irq == -1) - return -ENOENT; - - info = info_for_irq(irq); + struct irq_info *info = evtchn_to_info(evtchn); if (!info) return -ENOENT; @@ -1590,7 +1579,6 @@ EXPORT_SYMBOL_GPL(evtchn_make_refcounted); int evtchn_get(evtchn_port_t evtchn) { - int irq; struct irq_info *info; int err = -ENOENT; @@ -1599,11 +1587,7 @@ int evtchn_get(evtchn_port_t evtchn) mutex_lock(&irq_mapping_update_lock); - irq = get_evtchn_to_irq(evtchn); - if (irq == -1) - goto done; - - info = info_for_irq(irq); + info = evtchn_to_info(evtchn); if (!info) goto done; @@ -1623,16 +1607,17 @@ EXPORT_SYMBOL_GPL(evtchn_get); void evtchn_put(evtchn_port_t evtchn) { - int irq = get_evtchn_to_irq(evtchn); - if (WARN_ON(irq == -1)) + struct irq_info *info = evtchn_to_info(evtchn); + + if (WARN_ON(!info)) return; - unbind_from_irq(irq); + unbind_from_irq(info->irq); } EXPORT_SYMBOL_GPL(evtchn_put); void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) { - int irq; + evtchn_port_t evtchn; #ifdef CONFIG_X86 if (unlikely(vector == XEN_NMI_VECTOR)) { @@ -1643,9 +1628,9 @@ void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) return; } #endif - irq = per_cpu(ipi_to_irq, cpu)[vector]; - BUG_ON(irq < 0); - notify_remote_via_irq(irq); + evtchn = per_cpu(ipi_to_evtchn, cpu)[vector]; + BUG_ON(evtchn == 0); + notify_remote_via_evtchn(evtchn); } struct evtchn_loop_ctrl { @@ -1656,12 +1641,10 @@ struct evtchn_loop_ctrl { void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl) { - int irq; - struct irq_info *info; + struct irq_info *info = evtchn_to_info(port); struct xenbus_device *dev; - irq = get_evtchn_to_irq(port); - if (irq == -1) + if (!info) return; /* @@ -1686,7 +1669,6 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl) } } - info = info_for_irq(irq); if (xchg_acquire(&info->is_active, 1)) return; @@ -1700,7 +1682,7 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl) info->eoi_time = get_jiffies_64() + event_eoi_delay; } - generic_handle_irq(irq); + generic_handle_irq(info->irq); } int xen_evtchn_do_upcall(void) @@ -1758,16 +1740,17 @@ void rebind_evtchn_irq(evtchn_port_t evtchn, int irq) mutex_lock(&irq_mapping_update_lock); /* After resume the irq<->evtchn mappings are all cleared out */ - BUG_ON(get_evtchn_to_irq(evtchn) != -1); + BUG_ON(evtchn_to_info(evtchn)); /* Expect irq to have been bound before, so there should be a proper type */ BUG_ON(info->type == IRQT_UNBOUND); - (void)xen_irq_info_evtchn_setup(irq, evtchn, NULL); + info->irq = irq; + (void)xen_irq_info_evtchn_setup(info, evtchn, NULL); mutex_unlock(&irq_mapping_update_lock); - bind_evtchn_to_cpu(evtchn, info->cpu, false); + bind_evtchn_to_cpu(info, info->cpu, false); /* Unmask the event channel. */ enable_irq(irq); @@ -1801,7 +1784,7 @@ static int xen_rebind_evtchn_to_cpu(struct irq_info *info, unsigned int tcpu) * it, but don't do the xenlinux-level rebind in that case. */ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) - bind_evtchn_to_cpu(evtchn, tcpu, false); + bind_evtchn_to_cpu(info, tcpu, false); do_unmask(info, EVT_MASK_REASON_TEMPORARY); @@ -1858,28 +1841,30 @@ static void enable_dynirq(struct irq_data *data) do_unmask(info, EVT_MASK_REASON_EXPLICIT); } -static void disable_dynirq(struct irq_data *data) +static void do_ack_dynirq(struct irq_info *info) { - struct irq_info *info = info_for_irq(data->irq); - evtchn_port_t evtchn = info ? info->evtchn : 0; + evtchn_port_t evtchn = info->evtchn; if (VALID_EVTCHN(evtchn)) - do_mask(info, EVT_MASK_REASON_EXPLICIT); + event_handler_exit(info); } static void ack_dynirq(struct irq_data *data) { struct irq_info *info = info_for_irq(data->irq); - evtchn_port_t evtchn = info ? info->evtchn : 0; - if (VALID_EVTCHN(evtchn)) - event_handler_exit(info); + if (info) + do_ack_dynirq(info); } static void mask_ack_dynirq(struct irq_data *data) { - disable_dynirq(data); - ack_dynirq(data); + struct irq_info *info = info_for_irq(data->irq); + + if (info) { + do_disable_dynirq(info); + do_ack_dynirq(info); + } } static void lateeoi_ack_dynirq(struct irq_data *data) @@ -1952,13 +1937,13 @@ static void restore_pirqs(void) if (rc) { pr_warn("xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", gsi, irq, pirq, rc); - xen_free_irq(irq); + xen_free_irq(info); continue; } printk(KERN_DEBUG "xen: --> irq=%d, pirq=%d\n", irq, map_irq.pirq); - __startup_pirq(irq); + __startup_pirq(info); } } @@ -1966,13 +1951,15 @@ static void restore_cpu_virqs(unsigned int cpu) { struct evtchn_bind_virq bind_virq; evtchn_port_t evtchn; + struct irq_info *info; int virq, irq; for (virq = 0; virq < NR_VIRQS; virq++) { if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) continue; + info = info_for_irq(irq); - BUG_ON(virq_from_irq(irq) != virq); + BUG_ON(virq_from_irq(info) != virq); /* Get a new binding from Xen. */ bind_virq.virq = virq; @@ -1983,9 +1970,9 @@ static void restore_cpu_virqs(unsigned int cpu) evtchn = bind_virq.port; /* Record the new mapping. */ - (void)xen_irq_info_virq_setup(cpu, irq, evtchn, virq); + xen_irq_info_virq_setup(info, cpu, evtchn, virq); /* The affinity mask is still valid */ - bind_evtchn_to_cpu(evtchn, cpu, false); + bind_evtchn_to_cpu(info, cpu, false); } } @@ -1993,13 +1980,15 @@ static void restore_cpu_ipis(unsigned int cpu) { struct evtchn_bind_ipi bind_ipi; evtchn_port_t evtchn; + struct irq_info *info; int ipi, irq; for (ipi = 0; ipi < XEN_NR_IPIS; ipi++) { if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) continue; + info = info_for_irq(irq); - BUG_ON(ipi_from_irq(irq) != ipi); + BUG_ON(ipi_from_irq(info) != ipi); /* Get a new binding from Xen. */ bind_ipi.vcpu = xen_vcpu_nr(cpu); @@ -2009,9 +1998,9 @@ static void restore_cpu_ipis(unsigned int cpu) evtchn = bind_ipi.port; /* Record the new mapping. */ - (void)xen_irq_info_ipi_setup(cpu, irq, evtchn, ipi); + xen_irq_info_ipi_setup(info, cpu, evtchn, ipi); /* The affinity mask is still valid */ - bind_evtchn_to_cpu(evtchn, cpu, false); + bind_evtchn_to_cpu(info, cpu, false); } } @@ -2025,13 +2014,6 @@ void xen_clear_irq_pending(int irq) event_handler_exit(info); } EXPORT_SYMBOL(xen_clear_irq_pending); -void xen_set_irq_pending(int irq) -{ - evtchn_port_t evtchn = evtchn_from_irq(irq); - - if (VALID_EVTCHN(evtchn)) - set_evtchn(evtchn); -} bool xen_test_irq_pending(int irq) { diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h index 4d3398eff9cd..19ae31695edc 100644 --- a/drivers/xen/events/events_internal.h +++ b/drivers/xen/events/events_internal.h @@ -33,7 +33,6 @@ struct evtchn_ops { extern const struct evtchn_ops *evtchn_ops; -int get_evtchn_to_irq(evtchn_port_t evtchn); void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl); unsigned int cpu_from_evtchn(evtchn_port_t evtchn); diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c index b3e3d1bb37f3..508655273145 100644 --- a/drivers/xen/pcpu.c +++ b/drivers/xen/pcpu.c @@ -47,6 +47,9 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> +#ifdef CONFIG_ACPI +#include <acpi/processor.h> +#endif /* * @cpu_id: Xen physical cpu logic number @@ -400,4 +403,23 @@ bool __init xen_processor_present(uint32_t acpi_id) return online; } + +void xen_sanitize_proc_cap_bits(uint32_t *cap) +{ + struct xen_platform_op op = { + .cmd = XENPF_set_processor_pminfo, + .u.set_pminfo.id = -1, + .u.set_pminfo.type = XEN_PM_PDC, + }; + u32 buf[3] = { ACPI_PDC_REVISION_ID, 1, *cap }; + int ret; + + set_xen_guest_handle(op.u.set_pminfo.pdc, buf); + ret = HYPERVISOR_platform_op(&op); + if (ret) + pr_err("sanitize of _PDC buffer bits from Xen failed: %d\n", + ret); + else + *cap = buf[2]; +} #endif diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 1ce7f3c7a950..0eb337a8ec0f 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -1115,7 +1115,7 @@ struct privcmd_kernel_ioreq { spinlock_t lock; /* Protects ioeventfds list */ struct list_head ioeventfds; struct list_head list; - struct ioreq_port ports[0]; + struct ioreq_port ports[] __counted_by(vcpus); }; static irqreturn_t ioeventfd_interrupt(int irq, void *dev_id) diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 946bd56f0ac5..0e6c6c25d154 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -405,4 +405,5 @@ const struct dma_map_ops xen_swiotlb_dma_ops = { .get_sgtable = dma_common_get_sgtable, .alloc_pages = dma_common_alloc_pages, .free_pages = dma_common_free_pages, + .max_mapping_size = swiotlb_max_mapping_size, }; diff --git a/drivers/xen/xen-front-pgdir-shbuf.c b/drivers/xen/xen-front-pgdir-shbuf.c index b52e0fa595a9..223870a0111b 100644 --- a/drivers/xen/xen-front-pgdir-shbuf.c +++ b/drivers/xen/xen-front-pgdir-shbuf.c @@ -21,7 +21,7 @@ #include <xen/xen-front-pgdir-shbuf.h> -/** +/* * This structure represents the structure of a shared page * that contains grant references to the pages of the shared * buffer. This structure is common to many Xen para-virtualized @@ -33,7 +33,7 @@ struct xen_page_directory { grant_ref_t gref[]; /* Variable length */ }; -/** +/* * Shared buffer ops which are differently implemented * depending on the allocation mode, e.g. if the buffer * is allocated by the corresponding backend or frontend. @@ -61,7 +61,7 @@ struct xen_front_pgdir_shbuf_ops { int (*unmap)(struct xen_front_pgdir_shbuf *buf); }; -/** +/* * Get granted reference to the very first page of the * page directory. Usually this is passed to the backend, * so it can find/fill the grant references to the buffer's @@ -81,7 +81,7 @@ xen_front_pgdir_shbuf_get_dir_start(struct xen_front_pgdir_shbuf *buf) } EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_get_dir_start); -/** +/* * Map granted references of the shared buffer. * * Depending on the shared buffer mode of allocation @@ -102,7 +102,7 @@ int xen_front_pgdir_shbuf_map(struct xen_front_pgdir_shbuf *buf) } EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_map); -/** +/* * Unmap granted references of the shared buffer. * * Depending on the shared buffer mode of allocation @@ -123,7 +123,7 @@ int xen_front_pgdir_shbuf_unmap(struct xen_front_pgdir_shbuf *buf) } EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_unmap); -/** +/* * Free all the resources of the shared buffer. * * \param buf shared buffer which resources to be freed. @@ -150,7 +150,7 @@ EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_free); offsetof(struct xen_page_directory, \ gref)) / sizeof(grant_ref_t)) -/** +/* * Get the number of pages the page directory consumes itself. * * \param buf shared buffer. @@ -160,7 +160,7 @@ static int get_num_pages_dir(struct xen_front_pgdir_shbuf *buf) return DIV_ROUND_UP(buf->num_pages, XEN_NUM_GREFS_PER_PAGE); } -/** +/* * Calculate the number of grant references needed to share the buffer * and its pages when backend allocates the buffer. * @@ -172,7 +172,7 @@ static void backend_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) buf->num_grefs = get_num_pages_dir(buf); } -/** +/* * Calculate the number of grant references needed to share the buffer * and its pages when frontend allocates the buffer. * @@ -190,7 +190,7 @@ static void guest_calc_num_grefs(struct xen_front_pgdir_shbuf *buf) #define xen_page_to_vaddr(page) \ ((uintptr_t)pfn_to_kaddr(page_to_xen_pfn(page))) -/** +/* * Unmap the buffer previously mapped with grant references * provided by the backend. * @@ -238,7 +238,7 @@ static int backend_unmap(struct xen_front_pgdir_shbuf *buf) return ret; } -/** +/* * Map the buffer with grant references provided by the backend. * * \param buf shared buffer. @@ -320,7 +320,7 @@ static int backend_map(struct xen_front_pgdir_shbuf *buf) return ret; } -/** +/* * Fill page directory with grant references to the pages of the * page directory itself. * @@ -350,7 +350,7 @@ static void backend_fill_page_dir(struct xen_front_pgdir_shbuf *buf) page_dir->gref_dir_next_page = XEN_GREF_LIST_END; } -/** +/* * Fill page directory with grant references to the pages of the * page directory and the buffer we share with the backend. * @@ -389,7 +389,7 @@ static void guest_fill_page_dir(struct xen_front_pgdir_shbuf *buf) } } -/** +/* * Grant references to the frontend's buffer pages. * * These will be shared with the backend, so it can @@ -418,7 +418,7 @@ static int guest_grant_refs_for_buffer(struct xen_front_pgdir_shbuf *buf, return 0; } -/** +/* * Grant all the references needed to share the buffer. * * Grant references to the page directory pages and, if @@ -466,7 +466,7 @@ static int grant_references(struct xen_front_pgdir_shbuf *buf) return 0; } -/** +/* * Allocate all required structures to mange shared buffer. * * \param buf shared buffer. @@ -506,7 +506,7 @@ static const struct xen_front_pgdir_shbuf_ops local_ops = { .grant_refs_for_buffer = guest_grant_refs_for_buffer, }; -/** +/* * Allocate a new instance of a shared buffer. * * \param cfg configuration to be used while allocating a new shared buffer. diff --git a/fs/afs/dynroot.c b/fs/afs/dynroot.c index 4d04ef2d3ae7..1fa8cf23bd36 100644 --- a/fs/afs/dynroot.c +++ b/fs/afs/dynroot.c @@ -132,8 +132,8 @@ static int afs_probe_cell_name(struct dentry *dentry) ret = dns_query(net->net, "afsdb", name, len, "srv=1", NULL, NULL, false); - if (ret == -ENODATA) - ret = -EDESTADDRREQ; + if (ret == -ENODATA || ret == -ENOKEY) + ret = -ENOENT; return ret; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index c9cef3782b4a..a812952be1c9 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -553,6 +553,7 @@ struct afs_server_entry { }; struct afs_server_list { + struct rcu_head rcu; afs_volid_t vids[AFS_MAXTYPES]; /* Volume IDs */ refcount_t usage; unsigned char nr_servers; diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index ed9056703505..b59896b1de0a 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) for (i = 0; i < slist->nr_servers; i++) afs_unuse_server(net, slist->servers[i].server, afs_server_trace_put_slist); - kfree(slist); + kfree_rcu(slist, rcu); } } diff --git a/fs/afs/super.c b/fs/afs/super.c index 95d713074dc8..a01a0fb2cdbb 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -407,6 +407,10 @@ static int afs_validate_fc(struct fs_context *fc) return PTR_ERR(volume); ctx->volume = volume; + if (volume->type != AFSVL_RWVOL) { + ctx->flock_mode = afs_flock_mode_local; + fc->sb_flags |= SB_RDONLY; + } } return 0; diff --git a/fs/afs/vl_rotate.c b/fs/afs/vl_rotate.c index 488e58490b16..eb415ce56360 100644 --- a/fs/afs/vl_rotate.c +++ b/fs/afs/vl_rotate.c @@ -58,6 +58,12 @@ static bool afs_start_vl_iteration(struct afs_vl_cursor *vc) } /* Status load is ordered after lookup counter load */ + if (cell->dns_status == DNS_LOOKUP_GOT_NOT_FOUND) { + pr_warn("No record of cell %s\n", cell->name); + vc->error = -ENOENT; + return false; + } + if (cell->dns_source == DNS_RECORD_UNAVAILABLE) { vc->error = -EDESTADDRREQ; return false; @@ -285,6 +291,7 @@ failed: */ static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc) { + struct afs_cell *cell = vc->cell; static int count; int i; @@ -294,6 +301,9 @@ static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc) rcu_read_lock(); pr_notice("EDESTADDR occurred\n"); + pr_notice("CELL: %s err=%d\n", cell->name, cell->error); + pr_notice("DNS: src=%u st=%u lc=%x\n", + cell->dns_source, cell->dns_status, cell->dns_lookup_count); pr_notice("VC: ut=%lx ix=%u ni=%hu fl=%hx err=%hd\n", vc->untried, vc->index, vc->nr_iterations, vc->flags, vc->error); diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index a5083d447a62..1f5db6863663 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -309,9 +309,7 @@ static int autofs_fill_super(struct super_block *s, struct fs_context *fc) struct autofs_fs_context *ctx = fc->fs_private; struct autofs_sb_info *sbi = s->s_fs_info; struct inode *root_inode; - struct dentry *root; struct autofs_info *ino; - int ret = -ENOMEM; pr_debug("starting up, sbi = %p\n", sbi); @@ -328,56 +326,44 @@ static int autofs_fill_super(struct super_block *s, struct fs_context *fc) */ ino = autofs_new_ino(sbi); if (!ino) - goto fail; + return -ENOMEM; root_inode = autofs_get_inode(s, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_uid = ctx->uid; root_inode->i_gid = ctx->gid; + root_inode->i_fop = &autofs_root_operations; + root_inode->i_op = &autofs_dir_inode_operations; - root = d_make_root(root_inode); - if (!root) - goto fail_ino; - - root->d_fsdata = ino; + s->s_root = d_make_root(root_inode); + if (unlikely(!s->s_root)) { + autofs_free_ino(ino); + return -ENOMEM; + } + s->s_root->d_fsdata = ino; if (ctx->pgrp_set) { sbi->oz_pgrp = find_get_pid(ctx->pgrp); - if (!sbi->oz_pgrp) { - ret = invalf(fc, "Could not find process group %d", - ctx->pgrp); - goto fail_dput; - } - } else { + if (!sbi->oz_pgrp) + return invalf(fc, "Could not find process group %d", + ctx->pgrp); + } else sbi->oz_pgrp = get_task_pid(current, PIDTYPE_PGID); - } if (autofs_type_trigger(sbi->type)) - __managed_dentry_set_managed(root); - - root_inode->i_fop = &autofs_root_operations; - root_inode->i_op = &autofs_dir_inode_operations; + /* s->s_root won't be contended so there's little to + * be gained by not taking the d_lock when setting + * d_flags, even when a lot mounts are being done. + */ + managed_dentry_set_managed(s->s_root); pr_debug("pipe fd = %d, pgrp = %u\n", sbi->pipefd, pid_nr(sbi->oz_pgrp)); sbi->flags &= ~AUTOFS_SBI_CATATONIC; - - /* - * Success! Install the root dentry now to indicate completion. - */ - s->s_root = root; return 0; - - /* - * Failure ... clean up. - */ -fail_dput: - dput(root); - goto fail; -fail_ino: - autofs_free_ino(ino); -fail: - return ret; } /* diff --git a/fs/bcachefs/backpointers.c b/fs/bcachefs/backpointers.c index ef02c9bb0354..23c0834a97a4 100644 --- a/fs/bcachefs/backpointers.c +++ b/fs/bcachefs/backpointers.c @@ -313,17 +313,17 @@ struct btree *bch2_backpointer_get_node(struct btree_trans *trans, bp.level - 1, 0); b = bch2_btree_iter_peek_node(iter); - if (IS_ERR(b)) + if (IS_ERR_OR_NULL(b)) goto err; BUG_ON(b->c.level != bp.level - 1); - if (b && extent_matches_bp(c, bp.btree_id, bp.level, - bkey_i_to_s_c(&b->key), - bucket, bp)) + if (extent_matches_bp(c, bp.btree_id, bp.level, + bkey_i_to_s_c(&b->key), + bucket, bp)) return b; - if (b && btree_node_will_make_reachable(b)) { + if (btree_node_will_make_reachable(b)) { b = ERR_PTR(-BCH_ERR_backpointer_to_overwritten_btree_node); } else { backpointer_not_found(trans, bp_pos, bp, bkey_i_to_s_c(&b->key)); diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h index 9cb8684959ee..403aa3389fcc 100644 --- a/fs/bcachefs/bcachefs.h +++ b/fs/bcachefs/bcachefs.h @@ -617,7 +617,7 @@ struct journal_seq_blacklist_table { u64 start; u64 end; bool dirty; - } entries[0]; + } entries[]; }; struct journal_keys { diff --git a/fs/bcachefs/btree_iter.c b/fs/bcachefs/btree_iter.c index c2adf3fbb0b3..6fa90bcd7016 100644 --- a/fs/bcachefs/btree_iter.c +++ b/fs/bcachefs/btree_iter.c @@ -3087,8 +3087,6 @@ void bch2_trans_put(struct btree_trans *trans) srcu_read_unlock(&c->btree_trans_barrier, trans->srcu_idx); } - bch2_journal_preres_put(&c->journal, &trans->journal_preres); - kfree(trans->extra_journal_entries.data); if (trans->fs_usage_deltas) { diff --git a/fs/bcachefs/btree_key_cache.c b/fs/bcachefs/btree_key_cache.c index 9b78f78a75b5..37fbf22de8fc 100644 --- a/fs/bcachefs/btree_key_cache.c +++ b/fs/bcachefs/btree_key_cache.c @@ -89,10 +89,13 @@ static void bkey_cached_free(struct btree_key_cache *bc, ck->btree_trans_barrier_seq = start_poll_synchronize_srcu(&c->btree_trans_barrier); - if (ck->c.lock.readers) + if (ck->c.lock.readers) { list_move_tail(&ck->list, &bc->freed_pcpu); - else + bc->nr_freed_pcpu++; + } else { list_move_tail(&ck->list, &bc->freed_nonpcpu); + bc->nr_freed_nonpcpu++; + } atomic_long_inc(&bc->nr_freed); kfree(ck->k); @@ -109,6 +112,8 @@ static void __bkey_cached_move_to_freelist_ordered(struct btree_key_cache *bc, { struct bkey_cached *pos; + bc->nr_freed_nonpcpu++; + list_for_each_entry_reverse(pos, &bc->freed_nonpcpu, list) { if (ULONG_CMP_GE(ck->btree_trans_barrier_seq, pos->btree_trans_barrier_seq)) { @@ -158,6 +163,7 @@ static void bkey_cached_move_to_freelist(struct btree_key_cache *bc, #else mutex_lock(&bc->lock); list_move_tail(&ck->list, &bc->freed_nonpcpu); + bc->nr_freed_nonpcpu++; mutex_unlock(&bc->lock); #endif } else { @@ -217,6 +223,7 @@ bkey_cached_alloc(struct btree_trans *trans, struct btree_path *path, f->nr < ARRAY_SIZE(f->objs) / 2) { ck = list_last_entry(&bc->freed_nonpcpu, struct bkey_cached, list); list_del_init(&ck->list); + bc->nr_freed_nonpcpu--; f->objs[f->nr++] = ck; } @@ -229,6 +236,7 @@ bkey_cached_alloc(struct btree_trans *trans, struct btree_path *path, if (!list_empty(&bc->freed_nonpcpu)) { ck = list_last_entry(&bc->freed_nonpcpu, struct bkey_cached, list); list_del_init(&ck->list); + bc->nr_freed_nonpcpu--; } mutex_unlock(&bc->lock); #endif @@ -664,7 +672,6 @@ static int btree_key_cache_flush_pos(struct btree_trans *trans, goto out; bch2_journal_pin_drop(j, &ck->journal); - bch2_journal_preres_put(j, &ck->res); BUG_ON(!btree_node_locked(c_iter.path, 0)); @@ -762,18 +769,6 @@ bool bch2_btree_insert_key_cached(struct btree_trans *trans, BUG_ON(insert->k.u64s > ck->u64s); - if (likely(!(flags & BTREE_INSERT_JOURNAL_REPLAY))) { - int difference; - - BUG_ON(jset_u64s(insert->k.u64s) > trans->journal_preres.u64s); - - difference = jset_u64s(insert->k.u64s) - ck->res.u64s; - if (difference > 0) { - trans->journal_preres.u64s -= difference; - ck->res.u64s += difference; - } - } - bkey_copy(ck->k, insert); ck->valid = true; @@ -850,6 +845,8 @@ static unsigned long bch2_btree_key_cache_scan(struct shrinker *shrink, * Newest freed entries are at the end of the list - once we hit one * that's too new to be freed, we can bail out: */ + scanned += bc->nr_freed_nonpcpu; + list_for_each_entry_safe(ck, t, &bc->freed_nonpcpu, list) { if (!poll_state_synchronize_srcu(&c->btree_trans_barrier, ck->btree_trans_barrier_seq)) @@ -859,13 +856,15 @@ static unsigned long bch2_btree_key_cache_scan(struct shrinker *shrink, six_lock_exit(&ck->c.lock); kmem_cache_free(bch2_key_cache, ck); atomic_long_dec(&bc->nr_freed); - scanned++; freed++; + bc->nr_freed_nonpcpu--; } if (scanned >= nr) goto out; + scanned += bc->nr_freed_pcpu; + list_for_each_entry_safe(ck, t, &bc->freed_pcpu, list) { if (!poll_state_synchronize_srcu(&c->btree_trans_barrier, ck->btree_trans_barrier_seq)) @@ -875,8 +874,8 @@ static unsigned long bch2_btree_key_cache_scan(struct shrinker *shrink, six_lock_exit(&ck->c.lock); kmem_cache_free(bch2_key_cache, ck); atomic_long_dec(&bc->nr_freed); - scanned++; freed++; + bc->nr_freed_pcpu--; } if (scanned >= nr) @@ -982,6 +981,9 @@ void bch2_fs_btree_key_cache_exit(struct btree_key_cache *bc) } #endif + BUG_ON(list_count_nodes(&bc->freed_pcpu) != bc->nr_freed_pcpu); + BUG_ON(list_count_nodes(&bc->freed_nonpcpu) != bc->nr_freed_nonpcpu); + list_splice(&bc->freed_pcpu, &items); list_splice(&bc->freed_nonpcpu, &items); @@ -991,7 +993,6 @@ void bch2_fs_btree_key_cache_exit(struct btree_key_cache *bc) cond_resched(); bch2_journal_pin_drop(&c->journal, &ck->journal); - bch2_journal_preres_put(&c->journal, &ck->res); list_del(&ck->list); kfree(ck->k); diff --git a/fs/bcachefs/btree_key_cache_types.h b/fs/bcachefs/btree_key_cache_types.h new file mode 100644 index 000000000000..290e4e57df5b --- /dev/null +++ b/fs/bcachefs/btree_key_cache_types.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _BCACHEFS_BTREE_KEY_CACHE_TYPES_H +#define _BCACHEFS_BTREE_KEY_CACHE_TYPES_H + +struct btree_key_cache_freelist { + struct bkey_cached *objs[16]; + unsigned nr; +}; + +struct btree_key_cache { + struct mutex lock; + struct rhashtable table; + bool table_init_done; + + struct list_head freed_pcpu; + size_t nr_freed_pcpu; + struct list_head freed_nonpcpu; + size_t nr_freed_nonpcpu; + + struct shrinker *shrink; + unsigned shrink_iter; + struct btree_key_cache_freelist __percpu *pcpu_freed; + + atomic_long_t nr_freed; + atomic_long_t nr_keys; + atomic_long_t nr_dirty; +}; + +struct bkey_cached_key { + u32 btree_id; + struct bpos pos; +} __packed __aligned(4); + +#endif /* _BCACHEFS_BTREE_KEY_CACHE_TYPES_H */ diff --git a/fs/bcachefs/btree_trans_commit.c b/fs/bcachefs/btree_trans_commit.c index decad7b66c59..12907beda98c 100644 --- a/fs/bcachefs/btree_trans_commit.c +++ b/fs/bcachefs/btree_trans_commit.c @@ -78,6 +78,53 @@ inline void bch2_btree_node_prep_for_write(struct btree_trans *trans, bch2_btree_init_next(trans, b); } +static noinline int trans_lock_write_fail(struct btree_trans *trans, struct btree_insert_entry *i) +{ + while (--i >= trans->updates) { + if (same_leaf_as_prev(trans, i)) + continue; + + bch2_btree_node_unlock_write(trans, i->path, insert_l(i)->b); + } + + trace_and_count(trans->c, trans_restart_would_deadlock_write, trans); + return btree_trans_restart(trans, BCH_ERR_transaction_restart_would_deadlock_write); +} + +static inline int bch2_trans_lock_write(struct btree_trans *trans) +{ + struct btree_insert_entry *i; + + EBUG_ON(trans->write_locked); + + trans_for_each_update(trans, i) { + if (same_leaf_as_prev(trans, i)) + continue; + + if (bch2_btree_node_lock_write(trans, i->path, &insert_l(i)->b->c)) + return trans_lock_write_fail(trans, i); + + if (!i->cached) + bch2_btree_node_prep_for_write(trans, i->path, insert_l(i)->b); + } + + trans->write_locked = true; + return 0; +} + +static inline void bch2_trans_unlock_write(struct btree_trans *trans) +{ + if (likely(trans->write_locked)) { + struct btree_insert_entry *i; + + trans_for_each_update(trans, i) + if (!same_leaf_as_prev(trans, i)) + bch2_btree_node_unlock_write_inlined(trans, i->path, + insert_l(i)->b); + trans->write_locked = false; + } +} + /* Inserting into a given leaf node (last stage of insert): */ /* Handle overwrites and do insert, for non extents: */ @@ -276,17 +323,6 @@ static inline void btree_insert_entry_checks(struct btree_trans *trans, bch2_snapshot_is_internal_node(trans->c, i->k->k.p.snapshot)); } -static noinline int -bch2_trans_journal_preres_get_cold(struct btree_trans *trans, unsigned flags, - unsigned long trace_ip) -{ - return drop_locks_do(trans, - bch2_journal_preres_get(&trans->c->journal, - &trans->journal_preres, - trans->journal_preres_u64s, - (flags & BCH_WATERMARK_MASK))); -} - static __always_inline int bch2_trans_journal_res_get(struct btree_trans *trans, unsigned flags) { @@ -321,6 +357,45 @@ static inline int btree_key_can_insert(struct btree_trans *trans, return 0; } +noinline static int +btree_key_can_insert_cached_slowpath(struct btree_trans *trans, unsigned flags, + struct btree_path *path, unsigned new_u64s) +{ + struct bch_fs *c = trans->c; + struct btree_insert_entry *i; + struct bkey_cached *ck = (void *) path->l[0].b; + struct bkey_i *new_k; + int ret; + + bch2_trans_unlock_write(trans); + bch2_trans_unlock(trans); + + new_k = kmalloc(new_u64s * sizeof(u64), GFP_KERNEL); + if (!new_k) { + bch_err(c, "error allocating memory for key cache key, btree %s u64s %u", + bch2_btree_id_str(path->btree_id), new_u64s); + return -BCH_ERR_ENOMEM_btree_key_cache_insert; + } + + ret = bch2_trans_relock(trans) ?: + bch2_trans_lock_write(trans); + if (unlikely(ret)) { + kfree(new_k); + return ret; + } + + memcpy(new_k, ck->k, ck->u64s * sizeof(u64)); + + trans_for_each_update(trans, i) + if (i->old_v == &ck->k->v) + i->old_v = &new_k->v; + + kfree(ck->k); + ck->u64s = new_u64s; + ck->k = new_k; + return 0; +} + static int btree_key_can_insert_cached(struct btree_trans *trans, unsigned flags, struct btree_path *path, unsigned u64s) { @@ -347,12 +422,9 @@ static int btree_key_can_insert_cached(struct btree_trans *trans, unsigned flags return 0; new_u64s = roundup_pow_of_two(u64s); - new_k = krealloc(ck->k, new_u64s * sizeof(u64), GFP_NOFS); - if (!new_k) { - bch_err(c, "error allocating memory for key cache key, btree %s u64s %u", - bch2_btree_id_str(path->btree_id), new_u64s); - return -BCH_ERR_ENOMEM_btree_key_cache_insert; - } + new_k = krealloc(ck->k, new_u64s * sizeof(u64), GFP_NOWAIT); + if (unlikely(!new_k)) + return btree_key_can_insert_cached_slowpath(trans, flags, path, new_u64s); trans_for_each_update(trans, i) if (i->old_v == &ck->k->v) @@ -732,37 +804,6 @@ revert_fs_usage: return ret; } -static noinline int trans_lock_write_fail(struct btree_trans *trans, struct btree_insert_entry *i) -{ - while (--i >= trans->updates) { - if (same_leaf_as_prev(trans, i)) - continue; - - bch2_btree_node_unlock_write(trans, i->path, insert_l(i)->b); - } - - trace_and_count(trans->c, trans_restart_would_deadlock_write, trans); - return btree_trans_restart(trans, BCH_ERR_transaction_restart_would_deadlock_write); -} - -static inline int trans_lock_write(struct btree_trans *trans) -{ - struct btree_insert_entry *i; - - trans_for_each_update(trans, i) { - if (same_leaf_as_prev(trans, i)) - continue; - - if (bch2_btree_node_lock_write(trans, i->path, &insert_l(i)->b->c)) - return trans_lock_write_fail(trans, i); - - if (!i->cached) - bch2_btree_node_prep_for_write(trans, i->path, insert_l(i)->b); - } - - return 0; -} - static noinline void bch2_drop_overwrites_from_journal(struct btree_trans *trans) { struct btree_insert_entry *i; @@ -830,15 +871,7 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, unsigned flags } } - ret = bch2_journal_preres_get(&c->journal, - &trans->journal_preres, trans->journal_preres_u64s, - (flags & BCH_WATERMARK_MASK)|JOURNAL_RES_GET_NONBLOCK); - if (unlikely(ret == -BCH_ERR_journal_preres_get_blocked)) - ret = bch2_trans_journal_preres_get_cold(trans, flags, trace_ip); - if (unlikely(ret)) - return ret; - - ret = trans_lock_write(trans); + ret = bch2_trans_lock_write(trans); if (unlikely(ret)) return ret; @@ -847,10 +880,7 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, unsigned flags if (!ret && unlikely(trans->journal_replay_not_finished)) bch2_drop_overwrites_from_journal(trans); - trans_for_each_update(trans, i) - if (!same_leaf_as_prev(trans, i)) - bch2_btree_node_unlock_write_inlined(trans, i->path, - insert_l(i)->b); + bch2_trans_unlock_write(trans); if (!ret && trans->journal_pin) bch2_journal_pin_add(&c->journal, trans->journal_res.seq, @@ -1003,7 +1033,6 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags) struct bch_fs *c = trans->c; struct btree_insert_entry *i = NULL; struct btree_write_buffered_key *wb; - unsigned u64s; int ret = 0; if (!trans->nr_updates && @@ -1063,13 +1092,8 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags) EBUG_ON(test_bit(BCH_FS_CLEAN_SHUTDOWN, &c->flags)); - memset(&trans->journal_preres, 0, sizeof(trans->journal_preres)); - trans->journal_u64s = trans->extra_journal_entries.nr; - trans->journal_preres_u64s = 0; - trans->journal_transaction_names = READ_ONCE(c->opts.journal_transaction_names); - if (trans->journal_transaction_names) trans->journal_u64s += jset_u64s(JSET_ENTRY_LOG_U64s); @@ -1085,16 +1109,11 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags) if (i->key_cache_already_flushed) continue; - /* we're going to journal the key being updated: */ - u64s = jset_u64s(i->k->k.u64s); - if (i->cached && - likely(!(flags & BTREE_INSERT_JOURNAL_REPLAY))) - trans->journal_preres_u64s += u64s; - if (i->flags & BTREE_UPDATE_NOJOURNAL) continue; - trans->journal_u64s += u64s; + /* we're going to journal the key being updated: */ + trans->journal_u64s += jset_u64s(i->k->k.u64s); /* and we're also going to log the overwrite: */ if (trans->journal_transaction_names) @@ -1126,8 +1145,6 @@ retry: trace_and_count(c, transaction_commit, trans, _RET_IP_); out: - bch2_journal_preres_put(&c->journal, &trans->journal_preres); - if (likely(!(flags & BTREE_INSERT_NOCHECK_RW))) bch2_write_ref_put(c, BCH_WRITE_REF_trans); out_reset: diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h index 941841a0c5bf..60453ba86c4b 100644 --- a/fs/bcachefs/btree_types.h +++ b/fs/bcachefs/btree_types.h @@ -5,7 +5,7 @@ #include <linux/list.h> #include <linux/rhashtable.h> -//#include "bkey_methods.h" +#include "btree_key_cache_types.h" #include "buckets_types.h" #include "darray.h" #include "errcode.h" @@ -312,31 +312,6 @@ struct btree_iter { #endif }; -struct btree_key_cache_freelist { - struct bkey_cached *objs[16]; - unsigned nr; -}; - -struct btree_key_cache { - struct mutex lock; - struct rhashtable table; - bool table_init_done; - struct list_head freed_pcpu; - struct list_head freed_nonpcpu; - struct shrinker *shrink; - unsigned shrink_iter; - struct btree_key_cache_freelist __percpu *pcpu_freed; - - atomic_long_t nr_freed; - atomic_long_t nr_keys; - atomic_long_t nr_dirty; -}; - -struct bkey_cached_key { - u32 btree_id; - struct bpos pos; -} __packed __aligned(4); - #define BKEY_CACHED_ACCESSED 0 #define BKEY_CACHED_DIRTY 1 @@ -352,7 +327,6 @@ struct bkey_cached { struct rhash_head hash; struct list_head list; - struct journal_preres res; struct journal_entry_pin journal; u64 seq; @@ -389,11 +363,7 @@ struct btree_insert_entry { unsigned long ip_allocated; }; -#ifndef CONFIG_LOCKDEP #define BTREE_ITER_MAX 64 -#else -#define BTREE_ITER_MAX 32 -#endif struct btree_trans_commit_hook; typedef int (btree_trans_commit_hook_fn)(struct btree_trans *, struct btree_trans_commit_hook *); @@ -434,6 +404,7 @@ struct btree_trans { bool journal_transaction_names:1; bool journal_replay_not_finished:1; bool notrace_relock_fail:1; + bool write_locked:1; enum bch_errcode restarted:16; u32 restart_count; unsigned long last_begin_ip; @@ -465,11 +436,9 @@ struct btree_trans { struct journal_entry_pin *journal_pin; struct journal_res journal_res; - struct journal_preres journal_preres; u64 *journal_seq; struct disk_reservation *disk_res; unsigned journal_u64s; - unsigned journal_preres_u64s; struct replicas_delta_list *fs_usage_deltas; }; diff --git a/fs/bcachefs/btree_update_interior.c b/fs/bcachefs/btree_update_interior.c index 39c2db68123b..76f27bc9fa24 100644 --- a/fs/bcachefs/btree_update_interior.c +++ b/fs/bcachefs/btree_update_interior.c @@ -513,8 +513,6 @@ static void bch2_btree_update_free(struct btree_update *as, struct btree_trans * up_read(&c->gc_lock); as->took_gc_lock = false; - bch2_journal_preres_put(&c->journal, &as->journal_preres); - bch2_journal_pin_drop(&c->journal, &as->journal); bch2_journal_pin_flush(&c->journal, &as->journal); bch2_disk_reservation_put(c, &as->disk_res); @@ -734,8 +732,6 @@ err: bch2_journal_pin_drop(&c->journal, &as->journal); - bch2_journal_preres_put(&c->journal, &as->journal_preres); - mutex_lock(&c->btree_interior_update_lock); for (i = 0; i < as->nr_new_nodes; i++) { b = as->new_nodes[i]; @@ -1047,7 +1043,6 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path, unsigned nr_nodes[2] = { 0, 0 }; unsigned update_level = level; enum bch_watermark watermark = flags & BCH_WATERMARK_MASK; - unsigned journal_flags = 0; int ret = 0; u32 restart_count = trans->restart_count; @@ -1061,10 +1056,6 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path, flags &= ~BCH_WATERMARK_MASK; flags |= watermark; - if (flags & BTREE_INSERT_JOURNAL_RECLAIM) - journal_flags |= JOURNAL_RES_GET_NONBLOCK; - journal_flags |= watermark; - while (1) { nr_nodes[!!update_level] += 1 + split; update_level++; @@ -1129,27 +1120,6 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path, if (ret) goto err; - ret = bch2_journal_preres_get(&c->journal, &as->journal_preres, - BTREE_UPDATE_JOURNAL_RES, - journal_flags|JOURNAL_RES_GET_NONBLOCK); - if (ret) { - if (flags & BTREE_INSERT_JOURNAL_RECLAIM) { - ret = -BCH_ERR_journal_reclaim_would_deadlock; - goto err; - } - - ret = drop_locks_do(trans, - bch2_journal_preres_get(&c->journal, &as->journal_preres, - BTREE_UPDATE_JOURNAL_RES, - journal_flags)); - if (ret == -BCH_ERR_journal_preres_get_blocked) { - trace_and_count(c, trans_restart_journal_preres_get, trans, _RET_IP_, journal_flags); - ret = btree_trans_restart(trans, BCH_ERR_transaction_restart_journal_preres_get); - } - if (ret) - goto err; - } - ret = bch2_disk_reservation_get(c, &as->disk_res, (nr_nodes[0] + nr_nodes[1]) * btree_sectors(c), c->opts.metadata_replicas, diff --git a/fs/bcachefs/btree_update_interior.h b/fs/bcachefs/btree_update_interior.h index 4df21512d640..031076e75fa1 100644 --- a/fs/bcachefs/btree_update_interior.h +++ b/fs/bcachefs/btree_update_interior.h @@ -55,7 +55,6 @@ struct btree_update { unsigned update_level; struct disk_reservation disk_res; - struct journal_preres journal_preres; /* * BTREE_INTERIOR_UPDATING_NODE: diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c index 0771a6d880bf..5ed66202c226 100644 --- a/fs/bcachefs/data_update.c +++ b/fs/bcachefs/data_update.c @@ -239,6 +239,34 @@ restart_drop_extra_replicas: next_pos = insert->k.p; + /* + * Check for nonce offset inconsistency: + * This is debug code - we've been seeing this bug rarely, and + * it's been hard to reproduce, so this should give us some more + * information when it does occur: + */ + struct printbuf err = PRINTBUF; + int invalid = bch2_bkey_invalid(c, bkey_i_to_s_c(insert), __btree_node_type(0, m->btree_id), 0, &err); + printbuf_exit(&err); + + if (invalid) { + struct printbuf buf = PRINTBUF; + + prt_str(&buf, "about to insert invalid key in data update path"); + prt_str(&buf, "\nold: "); + bch2_bkey_val_to_text(&buf, c, old); + prt_str(&buf, "\nk: "); + bch2_bkey_val_to_text(&buf, c, k); + prt_str(&buf, "\nnew: "); + bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(insert)); + + bch2_print_string_as_lines(KERN_ERR, buf.buf); + printbuf_exit(&buf); + + bch2_fatal_error(c); + goto out; + } + ret = bch2_insert_snapshot_whiteouts(trans, m->btree_id, k.k->p, bkey_start_pos(&insert->k)) ?: bch2_insert_snapshot_whiteouts(trans, m->btree_id, diff --git a/fs/bcachefs/disk_groups.c b/fs/bcachefs/disk_groups.c index d613695abf9f..4d0cb0ccff32 100644 --- a/fs/bcachefs/disk_groups.c +++ b/fs/bcachefs/disk_groups.c @@ -555,6 +555,7 @@ void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v) case TARGET_DEV: { struct bch_dev *ca; + out->atomic++; rcu_read_lock(); ca = t.dev < c->sb.nr_devices ? rcu_dereference(c->devs[t.dev]) @@ -570,6 +571,7 @@ void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v) } rcu_read_unlock(); + out->atomic--; break; } case TARGET_GROUP: @@ -580,7 +582,7 @@ void bch2_target_to_text(struct printbuf *out, struct bch_fs *c, unsigned v) } } -void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v) +static void bch2_target_to_text_sb(struct printbuf *out, struct bch_sb *sb, unsigned v) { struct target t = target_decode(v); diff --git a/fs/bcachefs/ec.c b/fs/bcachefs/ec.c index 875f7c5a6fca..2a77de18c004 100644 --- a/fs/bcachefs/ec.c +++ b/fs/bcachefs/ec.c @@ -1373,6 +1373,15 @@ ec_new_stripe_head_alloc(struct bch_fs *c, unsigned target, h->nr_active_devs++; rcu_read_unlock(); + + /* + * If we only have redundancy + 1 devices, we're better off with just + * replication: + */ + if (h->nr_active_devs < h->redundancy + 2) + bch_err(c, "insufficient devices available to create stripe (have %u, need %u) - mismatched bucket sizes?", + h->nr_active_devs, h->redundancy + 2); + list_add(&h->list, &c->ec_stripe_head_list); return h; } @@ -1424,6 +1433,11 @@ __bch2_ec_stripe_head_get(struct btree_trans *trans, h = ec_new_stripe_head_alloc(c, target, algo, redundancy, watermark); found: + if (!IS_ERR_OR_NULL(h) && + h->nr_active_devs < h->redundancy + 2) { + mutex_unlock(&h->lock); + h = NULL; + } mutex_unlock(&c->ec_stripe_head_lock); return h; } @@ -1681,8 +1695,6 @@ struct ec_stripe_head *bch2_ec_stripe_head_get(struct btree_trans *trans, int ret; h = __bch2_ec_stripe_head_get(trans, target, algo, redundancy, watermark); - if (!h) - bch_err(c, "no stripe head"); if (IS_ERR_OR_NULL(h)) return h; diff --git a/fs/bcachefs/fs-io-pagecache.c b/fs/bcachefs/fs-io-pagecache.c index 8bd9bcdd27f7..ff664fd0d8ef 100644 --- a/fs/bcachefs/fs-io-pagecache.c +++ b/fs/bcachefs/fs-io-pagecache.c @@ -13,7 +13,7 @@ int bch2_filemap_get_contig_folios_d(struct address_space *mapping, loff_t start, u64 end, - int fgp_flags, gfp_t gfp, + fgf_t fgp_flags, gfp_t gfp, folios *fs) { struct folio *f; diff --git a/fs/bcachefs/fs-io-pagecache.h b/fs/bcachefs/fs-io-pagecache.h index a2222ad586e9..27f712ae37a6 100644 --- a/fs/bcachefs/fs-io-pagecache.h +++ b/fs/bcachefs/fs-io-pagecache.h @@ -7,7 +7,7 @@ typedef DARRAY(struct folio *) folios; int bch2_filemap_get_contig_folios_d(struct address_space *, loff_t, - u64, int, gfp_t, folios *); + u64, fgf_t, gfp_t, folios *); int bch2_write_invalidate_inode_pages_range(struct address_space *, loff_t, loff_t); /* diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index 166d8d8abe68..8ef817304e4a 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -1922,10 +1922,7 @@ out: return dget(sb->s_root); err_put_super: - sb->s_fs_info = NULL; - c->vfs_sb = NULL; deactivate_locked_super(sb); - bch2_fs_stop(c); return ERR_PTR(bch2_err_class(ret)); } @@ -1933,11 +1930,8 @@ static void bch2_kill_sb(struct super_block *sb) { struct bch_fs *c = sb->s_fs_info; - if (c) - c->vfs_sb = NULL; generic_shutdown_super(sb); - if (c) - bch2_fs_free(c); + bch2_fs_free(c); } static struct file_system_type bcache_fs_type = { diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 9f3e9bd3d767..e0c5cd119acc 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -2220,7 +2220,7 @@ static int nlink_cmp(const void *_l, const void *_r) const struct nlink *l = _l; const struct nlink *r = _r; - return cmp_int(l->inum, r->inum) ?: cmp_int(l->snapshot, r->snapshot); + return cmp_int(l->inum, r->inum); } static void inc_link(struct bch_fs *c, struct snapshots_seen *s, diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c index def77f2d8802..c7849b0753e7 100644 --- a/fs/bcachefs/inode.c +++ b/fs/bcachefs/inode.c @@ -1134,7 +1134,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans, * unlinked inodes in the snapshot leaves: */ *need_another_pass = true; - return 0; + goto out; } ret = 1; @@ -1169,8 +1169,10 @@ again: */ for_each_btree_key(trans, iter, BTREE_ID_deleted_inodes, POS_MIN, BTREE_ITER_PREFETCH|BTREE_ITER_ALL_SNAPSHOTS, k, ret) { - ret = lockrestart_do(trans, may_delete_deleted_inode(trans, &iter, k.k->p, - &need_another_pass)); + ret = commit_do(trans, NULL, NULL, + BTREE_INSERT_NOFAIL| + BTREE_INSERT_LAZY_RW, + may_delete_deleted_inode(trans, &iter, k.k->p, &need_another_pass)); if (ret < 0) break; diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c index f02b3f7d26a0..d704a8f829c8 100644 --- a/fs/bcachefs/io_write.c +++ b/fs/bcachefs/io_write.c @@ -795,7 +795,7 @@ static int bch2_write_decrypt(struct bch_write_op *op) * checksum: */ csum = bch2_checksum_bio(c, op->crc.csum_type, nonce, &op->wbio.bio); - if (bch2_crc_cmp(op->crc.csum, csum)) + if (bch2_crc_cmp(op->crc.csum, csum) && !c->opts.no_data_io) return -EIO; ret = bch2_encrypt_bio(c, op->crc.csum_type, nonce, &op->wbio.bio); diff --git a/fs/bcachefs/journal.c b/fs/bcachefs/journal.c index 5b5d69f2316b..23a9b7845d11 100644 --- a/fs/bcachefs/journal.c +++ b/fs/bcachefs/journal.c @@ -526,36 +526,6 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res, return ret; } -/* journal_preres: */ - -static bool journal_preres_available(struct journal *j, - struct journal_preres *res, - unsigned new_u64s, - unsigned flags) -{ - bool ret = bch2_journal_preres_get_fast(j, res, new_u64s, flags, true); - - if (!ret && mutex_trylock(&j->reclaim_lock)) { - bch2_journal_reclaim(j); - mutex_unlock(&j->reclaim_lock); - } - - return ret; -} - -int __bch2_journal_preres_get(struct journal *j, - struct journal_preres *res, - unsigned new_u64s, - unsigned flags) -{ - int ret; - - closure_wait_event(&j->preres_wait, - (ret = bch2_journal_error(j)) || - journal_preres_available(j, res, new_u64s, flags)); - return ret; -} - /* journal_entry_res: */ void bch2_journal_entry_res_resize(struct journal *j, @@ -1306,7 +1276,6 @@ void __bch2_journal_debug_to_text(struct printbuf *out, struct journal *j) prt_printf(out, "last_seq:\t\t%llu\n", journal_last_seq(j)); prt_printf(out, "last_seq_ondisk:\t%llu\n", j->last_seq_ondisk); prt_printf(out, "flushed_seq_ondisk:\t%llu\n", j->flushed_seq_ondisk); - prt_printf(out, "prereserved:\t\t%u/%u\n", j->prereserved.reserved, j->prereserved.remaining); prt_printf(out, "watermark:\t\t%s\n", bch2_watermarks[j->watermark]); prt_printf(out, "each entry reserved:\t%u\n", j->entry_u64s_reserved); prt_printf(out, "nr flush writes:\t%llu\n", j->nr_flush_writes); diff --git a/fs/bcachefs/journal.h b/fs/bcachefs/journal.h index 011711e99c8d..c85d01cf4948 100644 --- a/fs/bcachefs/journal.h +++ b/fs/bcachefs/journal.h @@ -395,104 +395,6 @@ out: return 0; } -/* journal_preres: */ - -static inline void journal_set_watermark(struct journal *j) -{ - union journal_preres_state s = READ_ONCE(j->prereserved); - unsigned watermark = BCH_WATERMARK_stripe; - - if (fifo_free(&j->pin) < j->pin.size / 4) - watermark = max_t(unsigned, watermark, BCH_WATERMARK_copygc); - if (fifo_free(&j->pin) < j->pin.size / 8) - watermark = max_t(unsigned, watermark, BCH_WATERMARK_reclaim); - - if (s.reserved > s.remaining) - watermark = max_t(unsigned, watermark, BCH_WATERMARK_copygc); - if (!s.remaining) - watermark = max_t(unsigned, watermark, BCH_WATERMARK_reclaim); - - if (watermark == j->watermark) - return; - - swap(watermark, j->watermark); - if (watermark > j->watermark) - journal_wake(j); -} - -static inline void bch2_journal_preres_put(struct journal *j, - struct journal_preres *res) -{ - union journal_preres_state s = { .reserved = res->u64s }; - - if (!res->u64s) - return; - - s.v = atomic64_sub_return(s.v, &j->prereserved.counter); - res->u64s = 0; - - if (unlikely(s.waiting)) { - clear_bit(ilog2((((union journal_preres_state) { .waiting = 1 }).v)), - (unsigned long *) &j->prereserved.v); - closure_wake_up(&j->preres_wait); - } - - if (s.reserved <= s.remaining && j->watermark) - journal_set_watermark(j); -} - -int __bch2_journal_preres_get(struct journal *, - struct journal_preres *, unsigned, unsigned); - -static inline int bch2_journal_preres_get_fast(struct journal *j, - struct journal_preres *res, - unsigned new_u64s, - unsigned flags, - bool set_waiting) -{ - int d = new_u64s - res->u64s; - union journal_preres_state old, new; - u64 v = atomic64_read(&j->prereserved.counter); - enum bch_watermark watermark = flags & BCH_WATERMARK_MASK; - int ret; - - do { - old.v = new.v = v; - ret = 0; - - if (watermark == BCH_WATERMARK_reclaim || - new.reserved + d < new.remaining) { - new.reserved += d; - ret = 1; - } else if (set_waiting && !new.waiting) - new.waiting = true; - else - return 0; - } while ((v = atomic64_cmpxchg(&j->prereserved.counter, - old.v, new.v)) != old.v); - - if (ret) - res->u64s += d; - return ret; -} - -static inline int bch2_journal_preres_get(struct journal *j, - struct journal_preres *res, - unsigned new_u64s, - unsigned flags) -{ - if (new_u64s <= res->u64s) - return 0; - - if (bch2_journal_preres_get_fast(j, res, new_u64s, flags, false)) - return 0; - - if (flags & JOURNAL_RES_GET_NONBLOCK) - return -BCH_ERR_journal_preres_get_blocked; - - return __bch2_journal_preres_get(j, res, new_u64s, flags); -} - /* journal_entry_res: */ void bch2_journal_entry_res_resize(struct journal *, diff --git a/fs/bcachefs/journal_io.c b/fs/bcachefs/journal_io.c index f4bc2cdbfdd7..786a09285509 100644 --- a/fs/bcachefs/journal_io.c +++ b/fs/bcachefs/journal_io.c @@ -1079,6 +1079,12 @@ found: if (ja->bucket_seq[ja->cur_idx] && ja->sectors_free == ca->mi.bucket_size) { +#if 0 + /* + * Debug code for ZNS support, where we (probably) want to be + * correlated where we stopped in the journal to the zone write + * points: + */ bch_err(c, "ja->sectors_free == ca->mi.bucket_size"); bch_err(c, "cur_idx %u/%u", ja->cur_idx, ja->nr); for (i = 0; i < 3; i++) { @@ -1086,6 +1092,7 @@ found: bch_err(c, "bucket_seq[%u] = %llu", idx, ja->bucket_seq[idx]); } +#endif ja->sectors_free = 0; } diff --git a/fs/bcachefs/journal_reclaim.c b/fs/bcachefs/journal_reclaim.c index 9a584aaaa2eb..e63c6eda86af 100644 --- a/fs/bcachefs/journal_reclaim.c +++ b/fs/bcachefs/journal_reclaim.c @@ -50,16 +50,21 @@ unsigned bch2_journal_dev_buckets_available(struct journal *j, return available; } -static void journal_set_remaining(struct journal *j, unsigned u64s_remaining) +static inline void journal_set_watermark(struct journal *j, bool low_on_space) { - union journal_preres_state old, new; - u64 v = atomic64_read(&j->prereserved.counter); + unsigned watermark = BCH_WATERMARK_stripe; - do { - old.v = new.v = v; - new.remaining = u64s_remaining; - } while ((v = atomic64_cmpxchg(&j->prereserved.counter, - old.v, new.v)) != old.v); + if (low_on_space) + watermark = max_t(unsigned, watermark, BCH_WATERMARK_reclaim); + if (fifo_free(&j->pin) < j->pin.size / 4) + watermark = max_t(unsigned, watermark, BCH_WATERMARK_reclaim); + + if (watermark == j->watermark) + return; + + swap(watermark, j->watermark); + if (watermark > j->watermark) + journal_wake(j); } static struct journal_space @@ -162,7 +167,6 @@ void bch2_journal_space_available(struct journal *j) struct bch_fs *c = container_of(j, struct bch_fs, journal); struct bch_dev *ca; unsigned clean, clean_ondisk, total; - s64 u64s_remaining = 0; unsigned max_entry_size = min(j->buf[0].buf_size >> 9, j->buf[1].buf_size >> 9); unsigned i, nr_online = 0, nr_devs_want; @@ -222,16 +226,10 @@ void bch2_journal_space_available(struct journal *j) else clear_bit(JOURNAL_MAY_SKIP_FLUSH, &j->flags); - u64s_remaining = (u64) clean << 6; - u64s_remaining -= (u64) total << 3; - u64s_remaining = max(0LL, u64s_remaining); - u64s_remaining /= 4; - u64s_remaining = min_t(u64, u64s_remaining, U32_MAX); + journal_set_watermark(j, clean * 4 <= total); out: j->cur_entry_sectors = !ret ? j->space[journal_space_discarded].next_entry : 0; j->cur_entry_error = ret; - journal_set_remaining(j, u64s_remaining); - journal_set_watermark(j); if (!ret) journal_wake(j); @@ -555,11 +553,6 @@ static u64 journal_seq_to_flush(struct journal *j) /* Try to keep the journal at most half full: */ nr_buckets = ja->nr / 2; - /* And include pre-reservations: */ - nr_buckets += DIV_ROUND_UP(j->prereserved.reserved, - (ca->mi.bucket_size << 6) - - journal_entry_overhead(j)); - nr_buckets = min(nr_buckets, ja->nr); bucket_to_flush = (ja->cur_idx + nr_buckets) % ja->nr; @@ -638,10 +631,7 @@ static int __bch2_journal_reclaim(struct journal *j, bool direct, bool kicked) msecs_to_jiffies(c->opts.journal_reclaim_delay))) min_nr = 1; - if (j->prereserved.reserved * 4 > j->prereserved.remaining) - min_nr = 1; - - if (fifo_free(&j->pin) <= 32) + if (j->watermark != BCH_WATERMARK_stripe) min_nr = 1; if (atomic_read(&c->btree_cache.dirty) * 2 > c->btree_cache.used) @@ -652,8 +642,6 @@ static int __bch2_journal_reclaim(struct journal *j, bool direct, bool kicked) trace_and_count(c, journal_reclaim_start, c, direct, kicked, min_nr, min_key_cache, - j->prereserved.reserved, - j->prereserved.remaining, atomic_read(&c->btree_cache.dirty), c->btree_cache.used, atomic_long_read(&c->btree_key_cache.nr_dirty), diff --git a/fs/bcachefs/journal_types.h b/fs/bcachefs/journal_types.h index 42504e16acb6..a756b69582e3 100644 --- a/fs/bcachefs/journal_types.h +++ b/fs/bcachefs/journal_types.h @@ -76,14 +76,6 @@ struct journal_res { u64 seq; }; -/* - * For reserving space in the journal prior to getting a reservation on a - * particular journal entry: - */ -struct journal_preres { - unsigned u64s; -}; - union journal_res_state { struct { atomic64_t counter; @@ -104,22 +96,6 @@ union journal_res_state { }; }; -union journal_preres_state { - struct { - atomic64_t counter; - }; - - struct { - u64 v; - }; - - struct { - u64 waiting:1, - reserved:31, - remaining:32; - }; -}; - /* bytes: */ #define JOURNAL_ENTRY_SIZE_MIN (64U << 10) /* 64k */ #define JOURNAL_ENTRY_SIZE_MAX (4U << 20) /* 4M */ @@ -180,8 +156,6 @@ struct journal { union journal_res_state reservations; enum bch_watermark watermark; - union journal_preres_state prereserved; - } __aligned(SMP_CACHE_BYTES); unsigned long flags; diff --git a/fs/bcachefs/six.c b/fs/bcachefs/six.c index b775cf0fb7cb..97790445e67a 100644 --- a/fs/bcachefs/six.c +++ b/fs/bcachefs/six.c @@ -163,8 +163,11 @@ static int __do_six_trylock(struct six_lock *lock, enum six_lock_type type, this_cpu_sub(*lock->readers, !ret); preempt_enable(); - if (!ret && (old & SIX_LOCK_WAITING_write)) - ret = -1 - SIX_LOCK_write; + if (!ret) { + smp_mb(); + if (atomic_read(&lock->state) & SIX_LOCK_WAITING_write) + ret = -1 - SIX_LOCK_write; + } } else if (type == SIX_LOCK_write && lock->readers) { if (try) { atomic_add(SIX_LOCK_HELD_write, &lock->state); diff --git a/fs/bcachefs/subvolume_types.h b/fs/bcachefs/subvolume_types.h index 86833445af20..2d2e66a4e468 100644 --- a/fs/bcachefs/subvolume_types.h +++ b/fs/bcachefs/subvolume_types.h @@ -20,7 +20,7 @@ struct snapshot_t { }; struct snapshot_table { - struct snapshot_t s[0]; + DECLARE_FLEX_ARRAY(struct snapshot_t, s); }; typedef struct { diff --git a/fs/bcachefs/trace.h b/fs/bcachefs/trace.h index 893304a1f06e..7857671159b4 100644 --- a/fs/bcachefs/trace.h +++ b/fs/bcachefs/trace.h @@ -196,10 +196,9 @@ DEFINE_EVENT(bio, journal_write, TRACE_EVENT(journal_reclaim_start, TP_PROTO(struct bch_fs *c, bool direct, bool kicked, u64 min_nr, u64 min_key_cache, - u64 prereserved, u64 prereserved_total, u64 btree_cache_dirty, u64 btree_cache_total, u64 btree_key_cache_dirty, u64 btree_key_cache_total), - TP_ARGS(c, direct, kicked, min_nr, min_key_cache, prereserved, prereserved_total, + TP_ARGS(c, direct, kicked, min_nr, min_key_cache, btree_cache_dirty, btree_cache_total, btree_key_cache_dirty, btree_key_cache_total), @@ -209,8 +208,6 @@ TRACE_EVENT(journal_reclaim_start, __field(bool, kicked ) __field(u64, min_nr ) __field(u64, min_key_cache ) - __field(u64, prereserved ) - __field(u64, prereserved_total ) __field(u64, btree_cache_dirty ) __field(u64, btree_cache_total ) __field(u64, btree_key_cache_dirty ) @@ -223,22 +220,18 @@ TRACE_EVENT(journal_reclaim_start, __entry->kicked = kicked; __entry->min_nr = min_nr; __entry->min_key_cache = min_key_cache; - __entry->prereserved = prereserved; - __entry->prereserved_total = prereserved_total; __entry->btree_cache_dirty = btree_cache_dirty; __entry->btree_cache_total = btree_cache_total; __entry->btree_key_cache_dirty = btree_key_cache_dirty; __entry->btree_key_cache_total = btree_key_cache_total; ), - TP_printk("%d,%d direct %u kicked %u min %llu key cache %llu prereserved %llu/%llu btree cache %llu/%llu key cache %llu/%llu", + TP_printk("%d,%d direct %u kicked %u min %llu key cache %llu btree cache %llu/%llu key cache %llu/%llu", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->direct, __entry->kicked, __entry->min_nr, __entry->min_key_cache, - __entry->prereserved, - __entry->prereserved_total, __entry->btree_cache_dirty, __entry->btree_cache_total, __entry->btree_key_cache_dirty, diff --git a/fs/bcachefs/xattr.c b/fs/bcachefs/xattr.c index a39ff0c296ec..79d982674c18 100644 --- a/fs/bcachefs/xattr.c +++ b/fs/bcachefs/xattr.c @@ -552,6 +552,14 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler, s.v = v + 1; s.defined = true; } else { + /* + * Check if this option was set on the parent - if so, switched + * back to inheriting from the parent: + * + * rename() also has to deal with keeping inherited options up + * to date - see bch2_reinherit_attrs() + */ + spin_lock(&dentry->d_lock); if (!IS_ROOT(dentry)) { struct bch_inode_info *dir = to_bch_ei(d_inode(dentry->d_parent)); @@ -560,6 +568,7 @@ static int bch2_xattr_bcachefs_set(const struct xattr_handler *handler, } else { s.v = 0; } + spin_unlock(&dentry->d_lock); s.defined = false; } diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 2a9344a3fcee..35c1d24d4a78 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -432,7 +432,7 @@ static noinline int update_ref_for_cow(struct btrfs_trans_handle *trans, if (btrfs_block_can_be_shared(trans, root, buf)) { ret = btrfs_lookup_extent_info(trans, fs_info, buf->start, btrfs_header_level(buf), 1, - &refs, &flags); + &refs, &flags, NULL); if (ret) return ret; if (unlikely(refs == 0)) { diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 9223934d95f4..891ea2fa263c 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -1041,7 +1041,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, return -ENOMEM; } - if (btrfs_qgroup_enabled(fs_info) && !generic_ref->skip_qgroup) { + if (btrfs_qgroup_full_accounting(fs_info) && !generic_ref->skip_qgroup) { record = kzalloc(sizeof(*record), GFP_NOFS); if (!record) { kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref); @@ -1144,7 +1144,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, return -ENOMEM; } - if (btrfs_qgroup_enabled(fs_info) && !generic_ref->skip_qgroup) { + if (btrfs_qgroup_full_accounting(fs_info) && !generic_ref->skip_qgroup) { record = kzalloc(sizeof(*record), GFP_NOFS); if (!record) { kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index c8e5b4715b49..0455935ff558 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -102,7 +102,8 @@ int btrfs_lookup_data_extent(struct btrfs_fs_info *fs_info, u64 start, u64 len) */ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, - u64 offset, int metadata, u64 *refs, u64 *flags) + u64 offset, int metadata, u64 *refs, u64 *flags, + u64 *owning_root) { struct btrfs_root *extent_root; struct btrfs_delayed_ref_head *head; @@ -114,6 +115,7 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, u32 item_size; u64 num_refs; u64 extent_flags; + u64 owner = 0; int ret; /* @@ -167,6 +169,8 @@ search_again: struct btrfs_extent_item); num_refs = btrfs_extent_refs(leaf, ei); extent_flags = btrfs_extent_flags(leaf, ei); + owner = btrfs_get_extent_owner_root(fs_info, leaf, + path->slots[0]); } else { ret = -EUCLEAN; btrfs_err(fs_info, @@ -226,6 +230,8 @@ out: *refs = num_refs; if (flags) *flags = extent_flags; + if (owning_root) + *owning_root = owner; out_free: btrfs_free_path(path); return ret; @@ -5234,7 +5240,7 @@ static noinline void reada_walk_down(struct btrfs_trans_handle *trans, /* We don't lock the tree block, it's OK to be racy here */ ret = btrfs_lookup_extent_info(trans, fs_info, bytenr, wc->level - 1, 1, &refs, - &flags); + &flags, NULL); /* We don't care about errors in readahead. */ if (ret < 0) continue; @@ -5301,7 +5307,8 @@ static noinline int walk_down_proc(struct btrfs_trans_handle *trans, ret = btrfs_lookup_extent_info(trans, fs_info, eb->start, level, 1, &wc->refs[level], - &wc->flags[level]); + &wc->flags[level], + NULL); BUG_ON(ret == -ENOMEM); if (ret) return ret; @@ -5391,6 +5398,7 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, u64 bytenr; u64 generation; u64 parent; + u64 owner_root = 0; struct btrfs_tree_parent_check check = { 0 }; struct btrfs_key key; struct btrfs_ref ref = { 0 }; @@ -5434,7 +5442,8 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, ret = btrfs_lookup_extent_info(trans, fs_info, bytenr, level - 1, 1, &wc->refs[level - 1], - &wc->flags[level - 1]); + &wc->flags[level - 1], + &owner_root); if (ret < 0) goto out_unlock; @@ -5567,8 +5576,7 @@ skip: find_next_key(path, level, &wc->drop_progress); btrfs_init_generic_ref(&ref, BTRFS_DROP_DELAYED_REF, bytenr, - fs_info->nodesize, parent, - btrfs_header_owner(next)); + fs_info->nodesize, parent, owner_root); btrfs_init_tree_ref(&ref, level - 1, root->root_key.objectid, 0, false); ret = btrfs_free_extent(trans, &ref); @@ -5635,7 +5643,8 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans, ret = btrfs_lookup_extent_info(trans, fs_info, eb->start, level, 1, &wc->refs[level], - &wc->flags[level]); + &wc->flags[level], + NULL); if (ret < 0) { btrfs_tree_unlock_rw(eb, path->locks[level]); path->locks[level] = 0; @@ -5880,7 +5889,7 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref, int for_reloc) ret = btrfs_lookup_extent_info(trans, fs_info, path->nodes[level]->start, level, 1, &wc->refs[level], - &wc->flags[level]); + &wc->flags[level], NULL); if (ret < 0) { err = ret; goto out_end_trans; diff --git a/fs/btrfs/extent-tree.h b/fs/btrfs/extent-tree.h index 0716f65d9753..2e066035ccee 100644 --- a/fs/btrfs/extent-tree.h +++ b/fs/btrfs/extent-tree.h @@ -99,7 +99,8 @@ u64 btrfs_cleanup_ref_head_accounting(struct btrfs_fs_info *fs_info, int btrfs_lookup_data_extent(struct btrfs_fs_info *fs_info, u64 start, u64 len); int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, u64 bytenr, - u64 offset, int metadata, u64 *refs, u64 *flags); + u64 offset, int metadata, u64 *refs, u64 *flags, + u64 *owner_root); int btrfs_pin_extent(struct btrfs_trans_handle *trans, u64 bytenr, u64 num, int reserved); int btrfs_pin_extent_for_log_replay(struct btrfs_trans_handle *trans, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 5e3fccddde0c..9f5a9894f88f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6983,8 +6983,15 @@ static struct extent_map *btrfs_new_extent_direct(struct btrfs_inode *inode, int ret; alloc_hint = get_extent_allocation_hint(inode, start, len); +again: ret = btrfs_reserve_extent(root, len, len, fs_info->sectorsize, 0, alloc_hint, &ins, 1, 1); + if (ret == -EAGAIN) { + ASSERT(btrfs_is_zoned(fs_info)); + wait_on_bit_io(&inode->root->fs_info->flags, BTRFS_FS_NEED_ZONE_FINISH, + TASK_UNINTERRUPTIBLE); + goto again; + } if (ret) return ERR_PTR(ret); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 752acff2c734..dfe257e1845b 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1528,7 +1528,7 @@ static noinline int key_in_sk(struct btrfs_key *key, static noinline int copy_to_sk(struct btrfs_path *path, struct btrfs_key *key, struct btrfs_ioctl_search_key *sk, - size_t *buf_size, + u64 *buf_size, char __user *ubuf, unsigned long *sk_offset, int *num_found) @@ -1660,7 +1660,7 @@ out: static noinline int search_ioctl(struct inode *inode, struct btrfs_ioctl_search_key *sk, - size_t *buf_size, + u64 *buf_size, char __user *ubuf) { struct btrfs_fs_info *info = btrfs_sb(inode->i_sb); @@ -1733,7 +1733,7 @@ static noinline int btrfs_ioctl_tree_search(struct inode *inode, struct btrfs_ioctl_search_args __user *uargs = argp; struct btrfs_ioctl_search_key sk; int ret; - size_t buf_size; + u64 buf_size; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1763,8 +1763,8 @@ static noinline int btrfs_ioctl_tree_search_v2(struct inode *inode, struct btrfs_ioctl_search_args_v2 __user *uarg = argp; struct btrfs_ioctl_search_args_v2 args; int ret; - size_t buf_size; - const size_t buf_limit = SZ_16M; + u64 buf_size; + const u64 buf_limit = SZ_16M; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index edb84cc03237..ce446d9d7f23 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1888,7 +1888,7 @@ int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info, u64 bytenr = record->bytenr; if (!btrfs_qgroup_full_accounting(fs_info)) - return 0; + return 1; lockdep_assert_held(&delayed_refs->lock); trace_btrfs_qgroup_trace_extent(fs_info, record); @@ -2875,12 +2875,18 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr, num_bytes, seq); /* + * We're done using the iterator, release all its qgroups while holding + * fs_info->qgroup_lock so that we don't race with btrfs_remove_qgroup() + * and trigger use-after-free accesses to qgroups. + */ + qgroup_iterator_nested_clean(&qgroups); + + /* * Bump qgroup_seq to avoid seq overlap */ fs_info->qgroup_seq += max(nr_old_roots, nr_new_roots) + 1; spin_unlock(&fs_info->qgroup_lock); out_free: - qgroup_iterator_nested_clean(&qgroups); ulist_free(old_roots); ulist_free(new_roots); return ret; diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index 944e8f1862aa..9589362acfbf 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -145,7 +145,7 @@ int btrfs_insert_raid_extent(struct btrfs_trans_handle *trans, btrfs_put_bioc(bioc); } - return ret; + return 0; } int btrfs_get_raid_extent_offset(struct btrfs_fs_info *fs_info, diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 9ce5be21b036..f62a408671cb 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -1868,6 +1868,9 @@ static int queue_scrub_stripe(struct scrub_ctx *sctx, struct btrfs_block_group * */ ASSERT(sctx->cur_stripe < SCRUB_TOTAL_STRIPES); + /* @found_logical_ret must be specified. */ + ASSERT(found_logical_ret); + stripe = &sctx->stripes[sctx->cur_stripe]; scrub_reset_stripe(stripe); ret = scrub_find_fill_first_stripe(bg, &sctx->extent_path, @@ -1876,8 +1879,7 @@ static int queue_scrub_stripe(struct scrub_ctx *sctx, struct btrfs_block_group * /* Either >0 as no more extents or <0 for error. */ if (ret) return ret; - if (found_logical_ret) - *found_logical_ret = stripe->logical; + *found_logical_ret = stripe->logical; sctx->cur_stripe++; /* We filled one group, submit it. */ @@ -2080,7 +2082,7 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx, /* Go through each extent items inside the logical range */ while (cur_logical < logical_end) { - u64 found_logical; + u64 found_logical = U64_MAX; u64 cur_physical = physical + cur_logical - logical_start; /* Canceled? */ @@ -2115,6 +2117,8 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx, if (ret < 0) break; + /* queue_scrub_stripe() returned 0, @found_logical must be updated. */ + ASSERT(found_logical != U64_MAX); cur_logical = found_logical + BTRFS_STRIPE_LEN; /* Don't hold CPU for too long time */ diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c87e18827a0a..c6f16625af51 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -748,13 +748,13 @@ static noinline struct btrfs_device *device_list_add(const char *path, if (!fs_devices) { fs_devices = alloc_fs_devices(disk_super->fsid); + if (IS_ERR(fs_devices)) + return ERR_CAST(fs_devices); + if (has_metadata_uuid) memcpy(fs_devices->metadata_uuid, disk_super->metadata_uuid, BTRFS_FSID_SIZE); - if (IS_ERR(fs_devices)) - return ERR_CAST(fs_devices); - if (same_fsid_diff_dev) { generate_random_uuid(fs_devices->fsid); fs_devices->temp_fsid = true; diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 3504ade30cb0..188378ca19c7 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1661,13 +1661,6 @@ int btrfs_load_block_group_zone_info(struct btrfs_block_group *cache, bool new) } out: - if (cache->alloc_offset > fs_info->zone_size) { - btrfs_err(fs_info, - "zoned: invalid write pointer %llu in block group %llu", - cache->alloc_offset, cache->start); - ret = -EIO; - } - if (cache->alloc_offset > cache->zone_capacity) { btrfs_err(fs_info, "zoned: invalid write pointer %llu (larger than zone capacity %llu) in block group %llu", diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index a25dd3d20008..b0e8774c435a 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -998,6 +998,14 @@ static int ecryptfs_getattr_link(struct mnt_idmap *idmap, return rc; } +static int ecryptfs_do_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + if (flags & AT_GETATTR_NOSEC) + return vfs_getattr_nosec(path, stat, request_mask, flags); + return vfs_getattr(path, stat, request_mask, flags); +} + static int ecryptfs_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) @@ -1006,8 +1014,8 @@ static int ecryptfs_getattr(struct mnt_idmap *idmap, struct kstat lower_stat; int rc; - rc = vfs_getattr(ecryptfs_dentry_to_lower_path(dentry), &lower_stat, - request_mask, flags); + rc = ecryptfs_do_getattr(ecryptfs_dentry_to_lower_path(dentry), + &lower_stat, request_mask, flags); if (!rc) { fsstack_copy_attr_all(d_inode(dentry), ecryptfs_inode_to_lower(d_inode(dentry))); diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index e540648dedc2..1d318f85232d 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -21,7 +21,7 @@ config EROFS_FS performance under extremely memory pressure without extra cost. See the documentation at <file:Documentation/filesystems/erofs.rst> - for more details. + and the web pages at <https://erofs.docs.kernel.org> for more details. If unsure, say N. diff --git a/fs/erofs/data.c b/fs/erofs/data.c index 029c761670bf..c98aeda8abb2 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -220,7 +220,7 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map) up_read(&devs->rwsem); return 0; } - map->m_bdev = dif->bdev_handle->bdev; + map->m_bdev = dif->bdev_handle ? dif->bdev_handle->bdev : NULL; map->m_daxdev = dif->dax_dev; map->m_dax_part_off = dif->dax_part_off; map->m_fscache = dif->fscache; @@ -238,7 +238,8 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map) if (map->m_pa >= startoff && map->m_pa < startoff + length) { map->m_pa -= startoff; - map->m_bdev = dif->bdev_handle->bdev; + map->m_bdev = dif->bdev_handle ? + dif->bdev_handle->bdev : NULL; map->m_daxdev = dif->dax_dev; map->m_dax_part_off = dif->dax_part_off; map->m_fscache = dif->fscache; diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index b8ad05b4509d..14a79d3226ab 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -15,11 +15,11 @@ static void *erofs_read_inode(struct erofs_buf *buf, struct erofs_sb_info *sbi = EROFS_SB(sb); struct erofs_inode *vi = EROFS_I(inode); const erofs_off_t inode_loc = erofs_iloc(inode); - erofs_blk_t blkaddr, nblks = 0; void *kaddr; struct erofs_inode_compact *dic; struct erofs_inode_extended *die, *copied = NULL; + union erofs_inode_i_u iu; unsigned int ifmt; int err; @@ -35,9 +35,8 @@ static void *erofs_read_inode(struct erofs_buf *buf, dic = kaddr + *ofs; ifmt = le16_to_cpu(dic->i_format); - if (ifmt & ~EROFS_I_ALL) { - erofs_err(inode->i_sb, "unsupported i_format %u of nid %llu", + erofs_err(sb, "unsupported i_format %u of nid %llu", ifmt, vi->nid); err = -EOPNOTSUPP; goto err_out; @@ -45,7 +44,7 @@ static void *erofs_read_inode(struct erofs_buf *buf, vi->datalayout = erofs_inode_datalayout(ifmt); if (vi->datalayout >= EROFS_INODE_DATALAYOUT_MAX) { - erofs_err(inode->i_sb, "unsupported datalayout %u of nid %llu", + erofs_err(sb, "unsupported datalayout %u of nid %llu", vi->datalayout, vi->nid); err = -EOPNOTSUPP; goto err_out; @@ -82,40 +81,15 @@ static void *erofs_read_inode(struct erofs_buf *buf, vi->xattr_isize = erofs_xattr_ibody_size(die->i_xattr_icount); inode->i_mode = le16_to_cpu(die->i_mode); - switch (inode->i_mode & S_IFMT) { - case S_IFREG: - case S_IFDIR: - case S_IFLNK: - vi->raw_blkaddr = le32_to_cpu(die->i_u.raw_blkaddr); - break; - case S_IFCHR: - case S_IFBLK: - inode->i_rdev = - new_decode_dev(le32_to_cpu(die->i_u.rdev)); - break; - case S_IFIFO: - case S_IFSOCK: - inode->i_rdev = 0; - break; - default: - goto bogusimode; - } + iu = die->i_u; i_uid_write(inode, le32_to_cpu(die->i_uid)); i_gid_write(inode, le32_to_cpu(die->i_gid)); set_nlink(inode, le32_to_cpu(die->i_nlink)); - - /* extended inode has its own timestamp */ + /* each extended inode has its own timestamp */ inode_set_ctime(inode, le64_to_cpu(die->i_mtime), le32_to_cpu(die->i_mtime_nsec)); inode->i_size = le64_to_cpu(die->i_size); - - /* total blocks for compressed files */ - if (erofs_inode_is_data_compressed(vi->datalayout)) - nblks = le32_to_cpu(die->i_u.compressed_blocks); - else if (vi->datalayout == EROFS_INODE_CHUNK_BASED) - /* fill chunked inode summary info */ - vi->chunkformat = le16_to_cpu(die->i_u.c.format); kfree(copied); copied = NULL; break; @@ -125,49 +99,51 @@ static void *erofs_read_inode(struct erofs_buf *buf, vi->xattr_isize = erofs_xattr_ibody_size(dic->i_xattr_icount); inode->i_mode = le16_to_cpu(dic->i_mode); - switch (inode->i_mode & S_IFMT) { - case S_IFREG: - case S_IFDIR: - case S_IFLNK: - vi->raw_blkaddr = le32_to_cpu(dic->i_u.raw_blkaddr); - break; - case S_IFCHR: - case S_IFBLK: - inode->i_rdev = - new_decode_dev(le32_to_cpu(dic->i_u.rdev)); - break; - case S_IFIFO: - case S_IFSOCK: - inode->i_rdev = 0; - break; - default: - goto bogusimode; - } + iu = dic->i_u; i_uid_write(inode, le16_to_cpu(dic->i_uid)); i_gid_write(inode, le16_to_cpu(dic->i_gid)); set_nlink(inode, le16_to_cpu(dic->i_nlink)); - /* use build time for compact inodes */ inode_set_ctime(inode, sbi->build_time, sbi->build_time_nsec); inode->i_size = le32_to_cpu(dic->i_size); - if (erofs_inode_is_data_compressed(vi->datalayout)) - nblks = le32_to_cpu(dic->i_u.compressed_blocks); - else if (vi->datalayout == EROFS_INODE_CHUNK_BASED) - vi->chunkformat = le16_to_cpu(dic->i_u.c.format); break; default: - erofs_err(inode->i_sb, - "unsupported on-disk inode version %u of nid %llu", + erofs_err(sb, "unsupported on-disk inode version %u of nid %llu", erofs_inode_version(ifmt), vi->nid); err = -EOPNOTSUPP; goto err_out; } - if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { + switch (inode->i_mode & S_IFMT) { + case S_IFREG: + case S_IFDIR: + case S_IFLNK: + vi->raw_blkaddr = le32_to_cpu(iu.raw_blkaddr); + break; + case S_IFCHR: + case S_IFBLK: + inode->i_rdev = new_decode_dev(le32_to_cpu(iu.rdev)); + break; + case S_IFIFO: + case S_IFSOCK: + inode->i_rdev = 0; + break; + default: + erofs_err(sb, "bogus i_mode (%o) @ nid %llu", inode->i_mode, + vi->nid); + err = -EFSCORRUPTED; + goto err_out; + } + + /* total blocks for compressed files */ + if (erofs_inode_is_data_compressed(vi->datalayout)) { + nblks = le32_to_cpu(iu.compressed_blocks); + } else if (vi->datalayout == EROFS_INODE_CHUNK_BASED) { + /* fill chunked inode summary info */ + vi->chunkformat = le16_to_cpu(iu.c.format); if (vi->chunkformat & ~EROFS_CHUNK_FORMAT_ALL) { - erofs_err(inode->i_sb, - "unsupported chunk format %x of nid %llu", + erofs_err(sb, "unsupported chunk format %x of nid %llu", vi->chunkformat, vi->nid); err = -EOPNOTSUPP; goto err_out; @@ -191,10 +167,6 @@ static void *erofs_read_inode(struct erofs_buf *buf, inode->i_blocks = nblks << (sb->s_blocksize_bits - 9); return kaddr; -bogusimode: - erofs_err(inode->i_sb, "bogus i_mode (%o) @ nid %llu", - inode->i_mode, vi->nid); - err = -EFSCORRUPTED; err_out: DBG_BUGON(1); kfree(copied); diff --git a/fs/inode.c b/fs/inode.c index edcd8a61975f..f238d987dec9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -215,6 +215,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode) lockdep_set_class_and_name(&mapping->invalidate_lock, &sb->s_type->invalidate_lock_key, "mapping.invalidate_lock"); + if (sb->s_iflags & SB_I_STABLE_WRITES) + mapping_set_stable_writes(mapping); inode->i_private = NULL; inode->i_mapping = mapping; INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */ diff --git a/fs/libfs.c b/fs/libfs.c index e9440d55073c..c2aa6fd4795c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -399,6 +399,8 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence) return -EINVAL; } + /* In this case, ->private_data is protected by f_pos_lock */ + file->private_data = NULL; return vfs_setpos(file, offset, U32_MAX); } @@ -428,7 +430,7 @@ static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry) inode->i_ino, fs_umode_to_dtype(inode->i_mode)); } -static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx) +static void *offset_iterate_dir(struct inode *inode, struct dir_context *ctx) { struct offset_ctx *so_ctx = inode->i_op->get_offset_ctx(inode); XA_STATE(xas, &so_ctx->xa, ctx->pos); @@ -437,7 +439,7 @@ static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx) while (true) { dentry = offset_find_next(&xas); if (!dentry) - break; + return ERR_PTR(-ENOENT); if (!offset_dir_emit(ctx, dentry)) { dput(dentry); @@ -447,6 +449,7 @@ static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx) dput(dentry); ctx->pos = xas.xa_index + 1; } + return NULL; } /** @@ -479,7 +482,12 @@ static int offset_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit_dots(file, ctx)) return 0; - offset_iterate_dir(d_inode(dir), ctx); + /* In this case, ->private_data is protected by f_pos_lock */ + if (ctx->pos == 2) + file->private_data = NULL; + else if (file->private_data == ERR_PTR(-ENOENT)) + return 0; + file->private_data = offset_iterate_dir(d_inode(dir), ctx); return 0; } diff --git a/fs/nfsd/cache.h b/fs/nfsd/cache.h index 929248c6ca84..4cbe0434cbb8 100644 --- a/fs/nfsd/cache.h +++ b/fs/nfsd/cache.h @@ -84,8 +84,8 @@ int nfsd_net_reply_cache_init(struct nfsd_net *nn); void nfsd_net_reply_cache_destroy(struct nfsd_net *nn); int nfsd_reply_cache_init(struct nfsd_net *); void nfsd_reply_cache_shutdown(struct nfsd_net *); -int nfsd_cache_lookup(struct svc_rqst *rqstp, - struct nfsd_cacherep **cacherep); +int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start, + unsigned int len, struct nfsd_cacherep **cacherep); void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp, int cachetype, __be32 *statp); int nfsd_reply_cache_stats_show(struct seq_file *m, void *v); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 4045c852a450..40415929e2ae 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2804,7 +2804,7 @@ static int client_opens_release(struct inode *inode, struct file *file) /* XXX: alternatively, we could get/drop in seq start/stop */ drop_client(clp); - return 0; + return seq_release(inode, file); } static const struct file_operations client_states_fops = { diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index fd56a52aa5fb..d3273a396659 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -369,33 +369,52 @@ nfsd_reply_cache_scan(struct shrinker *shrink, struct shrink_control *sc) return freed; } -/* - * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes +/** + * nfsd_cache_csum - Checksum incoming NFS Call arguments + * @buf: buffer containing a whole RPC Call message + * @start: starting byte of the NFS Call header + * @remaining: size of the NFS Call header, in bytes + * + * Compute a weak checksum of the leading bytes of an NFS procedure + * call header to help verify that a retransmitted Call matches an + * entry in the duplicate reply cache. + * + * To avoid assumptions about how the RPC message is laid out in + * @buf and what else it might contain (eg, a GSS MIC suffix), the + * caller passes us the exact location and length of the NFS Call + * header. + * + * Returns a 32-bit checksum value, as defined in RFC 793. */ -static __wsum -nfsd_cache_csum(struct svc_rqst *rqstp) +static __wsum nfsd_cache_csum(struct xdr_buf *buf, unsigned int start, + unsigned int remaining) { + unsigned int base, len; + struct xdr_buf subbuf; + __wsum csum = 0; + void *p; int idx; - unsigned int base; - __wsum csum; - struct xdr_buf *buf = &rqstp->rq_arg; - const unsigned char *p = buf->head[0].iov_base; - size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len, - RC_CSUMLEN); - size_t len = min(buf->head[0].iov_len, csum_len); + + if (remaining > RC_CSUMLEN) + remaining = RC_CSUMLEN; + if (xdr_buf_subsegment(buf, &subbuf, start, remaining)) + return csum; /* rq_arg.head first */ - csum = csum_partial(p, len, 0); - csum_len -= len; + if (subbuf.head[0].iov_len) { + len = min_t(unsigned int, subbuf.head[0].iov_len, remaining); + csum = csum_partial(subbuf.head[0].iov_base, len, csum); + remaining -= len; + } /* Continue into page array */ - idx = buf->page_base / PAGE_SIZE; - base = buf->page_base & ~PAGE_MASK; - while (csum_len) { - p = page_address(buf->pages[idx]) + base; - len = min_t(size_t, PAGE_SIZE - base, csum_len); + idx = subbuf.page_base / PAGE_SIZE; + base = subbuf.page_base & ~PAGE_MASK; + while (remaining) { + p = page_address(subbuf.pages[idx]) + base; + len = min_t(unsigned int, PAGE_SIZE - base, remaining); csum = csum_partial(p, len, csum); - csum_len -= len; + remaining -= len; base = 0; ++idx; } @@ -466,6 +485,8 @@ out: /** * nfsd_cache_lookup - Find an entry in the duplicate reply cache * @rqstp: Incoming Call to find + * @start: starting byte in @rqstp->rq_arg of the NFS Call header + * @len: size of the NFS Call header, in bytes * @cacherep: OUT: DRC entry for this request * * Try to find an entry matching the current call in the cache. When none @@ -479,7 +500,8 @@ out: * %RC_REPLY: Reply from cache * %RC_DROPIT: Do not process the request further */ -int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep) +int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start, + unsigned int len, struct nfsd_cacherep **cacherep) { struct nfsd_net *nn; struct nfsd_cacherep *rp, *found; @@ -495,7 +517,7 @@ int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep) goto out; } - csum = nfsd_cache_csum(rqstp); + csum = nfsd_cache_csum(&rqstp->rq_arg, start, len); /* * Since the common case is a cache miss followed by an insert, @@ -641,24 +663,17 @@ void nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp, return; } -/* - * Copy cached reply to current reply buffer. Should always fit. - * FIXME as reply is in a page, we should just attach the page, and - * keep a refcount.... - */ static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data) { - struct kvec *vec = &rqstp->rq_res.head[0]; - - if (vec->iov_len + data->iov_len > PAGE_SIZE) { - printk(KERN_WARNING "nfsd: cached reply too large (%zd).\n", - data->iov_len); - return 0; - } - memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len); - vec->iov_len += data->iov_len; - return 1; + __be32 *p; + + p = xdr_reserve_space(&rqstp->rq_res_stream, data->iov_len); + if (unlikely(!p)) + return false; + memcpy(p, data->iov_base, data->iov_len); + xdr_commit_encode(&rqstp->rq_res_stream); + return true; } /* diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index d6122bb2d167..fe61d9bbcc1f 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -981,6 +981,8 @@ int nfsd_dispatch(struct svc_rqst *rqstp) const struct svc_procedure *proc = rqstp->rq_procinfo; __be32 *statp = rqstp->rq_accept_statp; struct nfsd_cacherep *rp; + unsigned int start, len; + __be32 *nfs_reply; /* * Give the xdr decoder a chance to change this if it wants @@ -988,6 +990,13 @@ int nfsd_dispatch(struct svc_rqst *rqstp) */ rqstp->rq_cachetype = proc->pc_cachetype; + /* + * ->pc_decode advances the argument stream past the NFS + * Call header, so grab the header's starting location and + * size now for the call to nfsd_cache_lookup(). + */ + start = xdr_stream_pos(&rqstp->rq_arg_stream); + len = xdr_stream_remaining(&rqstp->rq_arg_stream); if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream)) goto out_decode_err; @@ -1001,7 +1010,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp) smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1); rp = NULL; - switch (nfsd_cache_lookup(rqstp, &rp)) { + switch (nfsd_cache_lookup(rqstp, start, len, &rp)) { case RC_DOIT: break; case RC_REPLY: @@ -1010,6 +1019,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp) goto out_dropit; } + nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0); *statp = proc->pc_func(rqstp); if (test_bit(RQ_DROPME, &rqstp->rq_flags)) goto out_update_drop; @@ -1023,7 +1033,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp) */ smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); - nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1); + nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply); out_cached_reply: return 1; diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 345b8f161ca4..c63b31a460be 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -171,7 +171,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, type = ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); - err = vfs_getattr(&realpath, stat, request_mask, flags); + err = ovl_do_getattr(&realpath, stat, request_mask, flags); if (err) goto out; @@ -196,8 +196,8 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, (!is_dir ? STATX_NLINK : 0); ovl_path_lower(dentry, &realpath); - err = vfs_getattr(&realpath, &lowerstat, - lowermask, flags); + err = ovl_do_getattr(&realpath, &lowerstat, lowermask, + flags); if (err) goto out; @@ -249,8 +249,8 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, ovl_path_lowerdata(dentry, &realpath); if (realpath.dentry) { - err = vfs_getattr(&realpath, &lowerdatastat, - lowermask, flags); + err = ovl_do_getattr(&realpath, &lowerdatastat, + lowermask, flags); if (err) goto out; } else { diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index ca88b2636a57..05c3dd597fa8 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -408,6 +408,14 @@ static inline bool ovl_open_flags_need_copy_up(int flags) return ((OPEN_FMODE(flags) & FMODE_WRITE) || (flags & O_TRUNC)); } +static inline int ovl_do_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) +{ + if (flags & AT_GETATTR_NOSEC) + return vfs_getattr_nosec(path, stat, request_mask, flags); + return vfs_getattr(path, stat, request_mask, flags); +} + /* util.c */ int ovl_get_write_access(struct dentry *dentry); void ovl_put_write_access(struct dentry *dentry); diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c index ddab9ea267d1..3fe2dde1598f 100644 --- a/fs/overlayfs/params.c +++ b/fs/overlayfs/params.c @@ -430,7 +430,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) struct ovl_fs_context *ctx = fc->fs_private; struct ovl_fs_context_layer *l; char *dup = NULL, *iter; - ssize_t nr_lower = 0, nr = 0, nr_data = 0; + ssize_t nr_lower, nr; bool data_layer = false; /* @@ -482,6 +482,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) iter = dup; l = ctx->lower; for (nr = 0; nr < nr_lower; nr++, l++) { + ctx->nr++; memset(l, 0, sizeof(*l)); err = ovl_mount_dir(iter, &l->path); @@ -498,10 +499,10 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) goto out_put; if (data_layer) - nr_data++; + ctx->nr_data++; /* Calling strchr() again would overrun. */ - if ((nr + 1) == nr_lower) + if (ctx->nr == nr_lower) break; err = -EINVAL; @@ -511,7 +512,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) * This is a regular layer so we require that * there are no data layers. */ - if ((ctx->nr_data + nr_data) > 0) { + if (ctx->nr_data > 0) { pr_err("regular lower layers cannot follow data lower layers"); goto out_put; } @@ -524,8 +525,6 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) data_layer = true; iter++; } - ctx->nr = nr_lower; - ctx->nr_data += nr_data; kfree(dup); return 0; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 50a201e9cd39..c3f020ca13a8 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -978,7 +978,7 @@ int ovl_set_protattr(struct inode *inode, struct dentry *upper, return 0; } -/** +/* * Caller must hold a reference to inode to prevent it from being freed while * it is marked inuse. */ diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c index 6f3285f1dfee..af7849e5974f 100644 --- a/fs/smb/client/cifs_spnego.c +++ b/fs/smb/client/cifs_spnego.c @@ -64,8 +64,8 @@ struct key_type cifs_spnego_key_type = { * strlen(";sec=ntlmsspi") */ #define MAX_MECH_STR_LEN 13 -/* strlen of "host=" */ -#define HOST_KEY_LEN 5 +/* strlen of ";host=" */ +#define HOST_KEY_LEN 6 /* strlen of ";ip4=" or ";ip6=" */ #define IP_KEY_LEN 5 diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 6ffbd81bd109..7558167f603c 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -191,7 +191,13 @@ struct cifs_open_info_data { bool reparse_point; bool symlink; }; - __u32 reparse_tag; + struct { + __u32 tag; + union { + struct reparse_data_buffer *buf; + struct reparse_posix_data *posix; + }; + } reparse; char *symlink_target; union { struct smb2_file_all_info fi; @@ -395,8 +401,7 @@ struct smb_version_operations { struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, - struct kvec *rsp_iov); + char **target_path); /* open a file for non-posix mounts */ int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf); @@ -551,6 +556,9 @@ struct smb_version_operations { bool (*is_status_io_timeout)(char *buf); /* Check for STATUS_NETWORK_NAME_DELETED */ bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); + int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb, + struct kvec *rsp_iov, + struct cifs_open_info_data *data); }; struct smb_version_values { diff --git a/fs/smb/client/cifspdu.h b/fs/smb/client/cifspdu.h index a75220db5c1e..83ccc51a54d0 100644 --- a/fs/smb/client/cifspdu.h +++ b/fs/smb/client/cifspdu.h @@ -1356,7 +1356,7 @@ typedef struct smb_com_transaction_ioctl_rsp { __le32 DataDisplacement; __u8 SetupCount; /* 1 */ __le16 ReturnedDataLen; - __u16 ByteCount; + __le16 ByteCount; } __attribute__((packed)) TRANSACT_IOCTL_RSP; #define CIFS_ACL_OWNER 1 @@ -1509,7 +1509,7 @@ struct reparse_posix_data { __le16 ReparseDataLength; __u16 Reserved; __le64 InodeType; /* LNK, FIFO, CHR etc. */ - char PathBuffer[]; + __u8 DataBuffer[]; } __attribute__((packed)); struct cifs_quota_data { diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index d87e2c26cce2..46feaa0880bd 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path, const struct cifs_fid *fid); bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - u32 tag); + struct cifs_open_info_data *data); extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, struct super_block *sb, unsigned int xid); extern int cifs_get_inode_info_unix(struct inode **pinode, @@ -458,6 +458,12 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, const unsigned char *searchName, char **syminfo, const struct nls_table *nls_codepage, int remap); +extern int cifs_query_reparse_point(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + u32 *tag, struct kvec *rsp, + int *rsp_buftype); extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid, char **symlinkinfo, const struct nls_table *nls_codepage); @@ -659,6 +665,12 @@ void cifs_put_tcp_super(struct super_block *sb); int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); char *extract_hostname(const char *unc); char *extract_sharename(const char *unc); +int parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, struct cifs_sb_info *cifs_sb, + bool unicode, struct cifs_open_info_data *data); +int cifs_sfu_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev); #ifdef CONFIG_CIFS_DFS_UPCALL static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 25503f1a4fd2..bad91ba6c3a9 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -2690,136 +2690,97 @@ querySymLinkRetry: return rc; } -/* - * Recent Windows versions now create symlinks more frequently - * and they use the "reparse point" mechanism below. We can of course - * do symlinks nicely to Samba and other servers which support the - * CIFS Unix Extensions and we can also do SFU symlinks and "client only" - * "MF" symlinks optionally, but for recent Windows we really need to - * reenable the code below and fix the cifs_symlink callers to handle this. - * In the interim this code has been moved to its own config option so - * it is not compiled in by default until callers fixed up and more tested. - */ -int -CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, - __u16 fid, char **symlinkinfo, - const struct nls_table *nls_codepage) +int cifs_query_reparse_point(const unsigned int xid, + struct cifs_tcon *tcon, + struct cifs_sb_info *cifs_sb, + const char *full_path, + u32 *tag, struct kvec *rsp, + int *rsp_buftype) { - int rc = 0; - int bytes_returned; - struct smb_com_transaction_ioctl_req *pSMB; - struct smb_com_transaction_ioctl_rsp *pSMBr; - bool is_unicode; - unsigned int sub_len; - char *sub_start; - struct reparse_symlink_data *reparse_buf; - struct reparse_posix_data *posix_buf; + struct cifs_open_parms oparms; + TRANSACT_IOCTL_REQ *io_req = NULL; + TRANSACT_IOCTL_RSP *io_rsp = NULL; + struct cifs_fid fid; __u32 data_offset, data_count; - char *end_of_smb; + __u8 *start, *end; + int io_rsp_len; + int oplock = 0; + int rc; - cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); + cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path); + + if (cap_unix(tcon->ses)) + return -EOPNOTSUPP; + + oparms = (struct cifs_open_parms) { + .tcon = tcon, + .cifs_sb = cifs_sb, + .desired_access = FILE_READ_ATTRIBUTES, + .create_options = cifs_create_options(cifs_sb, + OPEN_REPARSE_POINT), + .disposition = FILE_OPEN, + .path = full_path, + .fid = &fid, + }; + + rc = CIFS_open(xid, &oparms, &oplock, NULL); if (rc) return rc; - pSMB->TotalParameterCount = 0 ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le32(2); - /* BB find exact data count max from sess structure BB */ - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 4; - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); - pSMB->ParameterCount = pSMB->TotalParameterCount; - pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); - pSMB->IsFsctl = 1; /* FSCTL */ - pSMB->IsRootFlag = 0; - pSMB->Fid = fid; /* file handle always le */ - pSMB->ByteCount = 0; + rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, + (void **)&io_req, (void **)&io_rsp); + if (rc) + goto error; - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *) pSMBr, &bytes_returned, 0); - if (rc) { - cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); - goto qreparse_out; - } + io_req->TotalParameterCount = 0; + io_req->TotalDataCount = 0; + io_req->MaxParameterCount = cpu_to_le32(2); + /* BB find exact data count max from sess structure BB */ + io_req->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); + io_req->MaxSetupCount = 4; + io_req->Reserved = 0; + io_req->ParameterOffset = 0; + io_req->DataCount = 0; + io_req->DataOffset = 0; + io_req->SetupCount = 4; + io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL); + io_req->ParameterCount = io_req->TotalParameterCount; + io_req->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT); + io_req->IsFsctl = 1; + io_req->IsRootFlag = 0; + io_req->Fid = fid.netfid; + io_req->ByteCount = 0; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)io_req, + (struct smb_hdr *)io_rsp, &io_rsp_len, 0); + if (rc) + goto error; - data_offset = le32_to_cpu(pSMBr->DataOffset); - data_count = le32_to_cpu(pSMBr->DataCount); - if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { - /* BB also check enough total bytes returned */ - rc = -EIO; /* bad smb */ - goto qreparse_out; - } - if (!data_count || (data_count > 2048)) { + data_offset = le32_to_cpu(io_rsp->DataOffset); + data_count = le32_to_cpu(io_rsp->DataCount); + if (get_bcc(&io_rsp->hdr) < 2 || data_offset > 512 || + !data_count || data_count > 2048) { rc = -EIO; - cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); - goto qreparse_out; - } - end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; - reparse_buf = (struct reparse_symlink_data *) - ((char *)&pSMBr->hdr.Protocol + data_offset); - if ((char *)reparse_buf >= end_of_smb) { - rc = -EIO; - goto qreparse_out; - } - if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { - cifs_dbg(FYI, "NFS style reparse tag\n"); - posix_buf = (struct reparse_posix_data *)reparse_buf; - - if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { - cifs_dbg(FYI, "unsupported file type 0x%llx\n", - le64_to_cpu(posix_buf->InodeType)); - rc = -EOPNOTSUPP; - goto qreparse_out; - } - is_unicode = true; - sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); - if (posix_buf->PathBuffer + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); - rc = -EIO; - goto qreparse_out; - } - *symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, - sub_len, is_unicode, nls_codepage); - goto qreparse_out; - } else if (reparse_buf->ReparseTag != - cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { - rc = -EOPNOTSUPP; - goto qreparse_out; + goto error; } - /* Reparse tag is NTFS symlink */ - sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + - reparse_buf->PathBuffer; - sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); - if (sub_start + sub_len > end_of_smb) { - cifs_dbg(FYI, "reparse buf beyond SMB\n"); + end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount; + start = (__u8 *)&io_rsp->hdr.Protocol + data_offset; + if (start >= end) { rc = -EIO; - goto qreparse_out; + goto error; } - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) - is_unicode = true; - else - is_unicode = false; - - /* BB FIXME investigate remapping reserved chars here */ - *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, - nls_codepage); - if (!*symlinkinfo) - rc = -ENOMEM; -qreparse_out: - cifs_buf_release(pSMB); - /* - * Note: On -EAGAIN error only caller can retry on handle based calls - * since file handle passed in no longer valid. - */ + *tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag); + rsp->iov_base = io_rsp; + rsp->iov_len = io_rsp_len; + *rsp_buftype = CIFS_LARGE_BUFFER; + CIFSSMBClose(xid, tcon, fid.netfid); + return 0; + +error: + cifs_buf_release(io_req); + CIFSSMBClose(xid, tcon, fid.netfid); return rc; } diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 57c2a7df3457..f896f60c924b 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -2065,6 +2065,12 @@ void __cifs_put_smb_ses(struct cifs_ses *ses) ses->chans[i].server = NULL; } + /* we now account for primary channel in iface->refcount */ + if (ses->chans[0].iface) { + kref_put(&ses->chans[0].iface->refcount, release_iface); + ses->chans[0].server = NULL; + } + sesInfoFree(ses); cifs_put_tcp_session(server, 0); } diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 86fbd3f847d6..47f49be69ced 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -459,8 +459,7 @@ static int cifs_get_unix_fattr(const unsigned char *full_path, return -EOPNOTSUPP; rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &fattr->cf_symlink_target, - NULL); + &fattr->cf_symlink_target); cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); } return rc; @@ -722,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); } +static inline dev_t nfs_mkdev(struct reparse_posix_data *buf) +{ + u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); + + return MKDEV(v >> 32, v & 0xffffffff); +} + bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, - u32 tag) + struct cifs_open_info_data *data) { + struct reparse_posix_data *buf = data->reparse.posix; + u32 tag = data->reparse.tag; + + if (tag == IO_REPARSE_TAG_NFS && buf) { + switch (le64_to_cpu(buf->InodeType)) { + case NFS_SPECFILE_CHR: + fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_CHR; + fattr->cf_rdev = nfs_mkdev(buf); + break; + case NFS_SPECFILE_BLK: + fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_BLK; + fattr->cf_rdev = nfs_mkdev(buf); + break; + case NFS_SPECFILE_FIFO: + fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_FIFO; + break; + case NFS_SPECFILE_SOCK: + fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_SOCK; + break; + case NFS_SPECFILE_LNK: + fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode; + fattr->cf_dtype = DT_LNK; + break; + default: + WARN_ON_ONCE(1); + return false; + } + return true; + } + switch (tag) { case IO_REPARSE_TAG_LX_SYMLINK: fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; @@ -791,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); if (cifs_open_data_reparse(data) && - cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag)) + cifs_reparse_point_to_fattr(cifs_sb, fattr, data)) goto out_reparse; if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { @@ -856,7 +896,7 @@ cifs_get_file_info(struct file *filp) data.adjust_tz = false; if (data.symlink_target) { data.symlink = true; - data.reparse_tag = IO_REPARSE_TAG_SYMLINK; + data.reparse.tag = IO_REPARSE_TAG_SYMLINK; } cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); break; @@ -1025,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct kvec rsp_iov, *iov = NULL; int rsp_buftype = CIFS_NO_BUFFER; - u32 tag = data->reparse_tag; + u32 tag = data->reparse.tag; int rc = 0; if (!tag && server->ops->query_reparse_point) { @@ -1035,22 +1075,28 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, if (!rc) iov = &rsp_iov; } - switch ((data->reparse_tag = tag)) { + + rc = -EOPNOTSUPP; + switch ((data->reparse.tag = tag)) { case 0: /* SMB1 symlink */ - iov = NULL; - fallthrough; - case IO_REPARSE_TAG_NFS: - case IO_REPARSE_TAG_SYMLINK: - if (!data->symlink_target && server->ops->query_symlink) { + if (server->ops->query_symlink) { rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, - &data->symlink_target, - iov); + &data->symlink_target); } break; case IO_REPARSE_TAG_MOUNT_POINT: cifs_create_junction_fattr(fattr, sb); + rc = 0; goto out; + default: + if (data->symlink_target) { + rc = 0; + } else if (server->ops->parse_reparse_point) { + rc = server->ops->parse_reparse_point(cifs_sb, + iov, data); + } + break; } cifs_open_info_to_fattr(fattr, data, sb); diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 47fc22de8d20..d30ea2005eb3 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -153,6 +153,10 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) static void cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) { + struct cifs_open_info_data data = { + .reparse = { .tag = fattr->cf_cifstag, }, + }; + fattr->cf_uid = cifs_sb->ctx->linux_uid; fattr->cf_gid = cifs_sb->ctx->linux_gid; @@ -165,7 +169,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) * reasonably map some of them to directories vs. files vs. symlinks */ if ((fattr->cf_cifsattrs & ATTR_REPARSE) && - cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag)) + cifs_reparse_point_to_fattr(cifs_sb, fattr, &data)) goto out_reparse; if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c index 0bb2ac929061..816e01c5589b 100644 --- a/fs/smb/client/sess.c +++ b/fs/smb/client/sess.c @@ -322,28 +322,32 @@ cifs_disable_secondary_channels(struct cifs_ses *ses) iface = ses->chans[i].iface; server = ses->chans[i].server; + /* + * remove these references first, since we need to unlock + * the chan_lock here, since iface_lock is a higher lock + */ + ses->chans[i].iface = NULL; + ses->chans[i].server = NULL; + spin_unlock(&ses->chan_lock); + if (iface) { spin_lock(&ses->iface_lock); - kref_put(&iface->refcount, release_iface); - ses->chans[i].iface = NULL; iface->num_channels--; if (iface->weight_fulfilled) iface->weight_fulfilled--; + kref_put(&iface->refcount, release_iface); spin_unlock(&ses->iface_lock); } - spin_unlock(&ses->chan_lock); - if (server && !server->terminate) { - server->terminate = true; - cifs_signal_cifsd_for_reconnect(server, false); - } - spin_lock(&ses->chan_lock); - if (server) { - ses->chans[i].server = NULL; + if (!server->terminate) { + server->terminate = true; + cifs_signal_cifsd_for_reconnect(server, false); + } cifs_put_tcp_session(server, false); } + spin_lock(&ses->chan_lock); } done: diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c index 9bf8735cdd1e..a9eaba8083b0 100644 --- a/fs/smb/client/smb1ops.c +++ b/fs/smb/client/smb1ops.c @@ -976,64 +976,37 @@ static int cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path, - char **target_path, - struct kvec *rsp_iov) + char **target_path) { int rc; - int oplock = 0; - bool is_reparse_point = !!rsp_iov; - struct cifs_fid fid; - struct cifs_open_parms oparms; - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); + cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path); - if (is_reparse_point) { - cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n"); + if (!cap_unix(tcon->ses)) return -EOPNOTSUPP; - } - - /* Check for unix extensions */ - if (cap_unix(tcon->ses)) { - rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, - cifs_sb->local_nls, - cifs_remap(cifs_sb)); - if (rc == -EREMOTE) - rc = cifs_unix_dfs_readlink(xid, tcon, full_path, - target_path, - cifs_sb->local_nls); - - goto out; - } - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = FILE_READ_ATTRIBUTES, - .create_options = cifs_create_options(cifs_sb, - OPEN_REPARSE_POINT), - .disposition = FILE_OPEN, - .path = full_path, - .fid = &fid, - }; - - rc = CIFS_open(xid, &oparms, &oplock, NULL); - if (rc) - goto out; - - rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path, - cifs_sb->local_nls); - if (rc) - goto out_close; - convert_delimiter(*target_path, '/'); -out_close: - CIFSSMBClose(xid, tcon, fid.netfid); -out: - if (!rc) - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, + cifs_sb->local_nls, cifs_remap(cifs_sb)); + if (rc == -EREMOTE) + rc = cifs_unix_dfs_readlink(xid, tcon, full_path, + target_path, cifs_sb->local_nls); return rc; } +static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb, + struct kvec *rsp_iov, + struct cifs_open_info_data *data) +{ + struct reparse_data_buffer *buf; + TRANSACT_IOCTL_RSP *io = rsp_iov->iov_base; + bool unicode = !!(io->hdr.Flags2 & SMBFLG2_UNICODE); + u32 plen = le16_to_cpu(io->ByteCount); + + buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol + + le32_to_cpu(io->DataOffset)); + return parse_reparse_point(buf, plen, cifs_sb, unicode, data); +} + static bool cifs_is_read_op(__u32 oplock) { @@ -1068,15 +1041,7 @@ cifs_make_node(unsigned int xid, struct inode *inode, { struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct inode *newinode = NULL; - int rc = -EPERM; - struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms; - __u32 oplock = 0; - struct cifs_fid fid; - struct cifs_open_parms oparms; - unsigned int bytes_written; - struct win_dev *pdev; - struct kvec iov[2]; + int rc; if (tcon->unix_ext) { /* @@ -1110,74 +1075,18 @@ cifs_make_node(unsigned int xid, struct inode *inode, d_instantiate(dentry, newinode); return rc; } - /* - * SMB1 SFU emulation: should work with all servers, but only - * support block and char device (no socket & fifo) + * Check if mounted with mount parm 'sfu' mount parm. + * SFU emulation should work with all servers, but only + * supports block and char device (no socket & fifo), + * and was used by default in earlier versions of Windows */ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - if (!S_ISCHR(mode) && !S_ISBLK(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); - - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; - - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); - if (rc) - return rc; - - /* - * BB Do not bother to decode buf since no local inode yet to put - * timestamps in, but we can reuse it safely. - */ - - pdev = (struct win_dev *)&buf.fi; - io_parms.pid = current->tgid; - io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); - } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); - - /* FIXME: add code here to set EAs */ - - cifs_free_open_info(&buf); - return rc; + return -EPERM; + return cifs_sfu_make_node(xid, inode, dentry, tcon, + full_path, mode, dev); } - - struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, @@ -1214,6 +1123,7 @@ struct smb_version_operations smb1_operations = { .is_path_accessible = cifs_is_path_accessible, .can_echo = cifs_can_echo, .query_path_info = cifs_query_path_info, + .query_reparse_point = cifs_query_reparse_point, .query_file_info = cifs_query_file_info, .get_srv_inum = cifs_get_srv_inum, .set_path_size = CIFSSMBSetEOF, @@ -1229,6 +1139,7 @@ struct smb_version_operations smb1_operations = { .rename = CIFSSMBRename, .create_hardlink = CIFSCreateHardLink, .query_symlink = cifs_query_symlink, + .parse_reparse_point = cifs_parse_reparse_point, .open = cifs_open_file, .set_fid = cifs_set_fid, .close = cifs_close_file, diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 0b89f7008ac0..c94940af5d4b 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -555,7 +555,7 @@ static int parse_create_response(struct cifs_open_info_data *data, break; } data->reparse_point = reparse_point; - data->reparse_tag = tag; + data->reparse.tag = tag; return rc; } diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index a959ed2c9b22..82ab62fd0040 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2866,115 +2866,119 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, return rc; } -static int -parse_reparse_posix(struct reparse_posix_data *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) +/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ +static int parse_reparse_posix(struct reparse_posix_data *buf, + struct cifs_sb_info *cifs_sb, + struct cifs_open_info_data *data) { unsigned int len; - - /* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ - len = le16_to_cpu(symlink_buf->ReparseDataLength); - - if (le64_to_cpu(symlink_buf->InodeType) != NFS_SPECFILE_LNK) { - cifs_dbg(VFS, "%lld not a supported symlink type\n", - le64_to_cpu(symlink_buf->InodeType)); + u64 type; + + switch ((type = le64_to_cpu(buf->InodeType))) { + case NFS_SPECFILE_LNK: + len = le16_to_cpu(buf->ReparseDataLength); + data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, + len, true, + cifs_sb->local_nls); + if (!data->symlink_target) + return -ENOMEM; + convert_delimiter(data->symlink_target, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", + __func__, data->symlink_target); + break; + case NFS_SPECFILE_CHR: + case NFS_SPECFILE_BLK: + case NFS_SPECFILE_FIFO: + case NFS_SPECFILE_SOCK: + break; + default: + cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", + __func__, type); return -EOPNOTSUPP; } - - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer, - len, true, cifs_sb->local_nls); - if (!(*target_path)) - return -ENOMEM; - - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); - return 0; } -static int -parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) +static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, + u32 plen, bool unicode, + struct cifs_sb_info *cifs_sb, + struct cifs_open_info_data *data) { - unsigned int sub_len; - unsigned int sub_offset; + unsigned int len; + unsigned int offs; /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ - sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset); - sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength); - if (sub_offset + 20 > plen || - sub_offset + sub_len + 20 > plen) { + offs = le16_to_cpu(sym->SubstituteNameOffset); + len = le16_to_cpu(sym->SubstituteNameLength); + if (offs + 20 > plen || offs + len + 20 > plen) { cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); return -EIO; } - *target_path = cifs_strndup_from_utf16( - symlink_buf->PathBuffer + sub_offset, - sub_len, true, cifs_sb->local_nls); - if (!(*target_path)) + data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, + len, unicode, + cifs_sb->local_nls); + if (!data->symlink_target) return -ENOMEM; - convert_delimiter(*target_path, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); + convert_delimiter(data->symlink_target, '/'); + cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); return 0; } -static int -parse_reparse_point(struct reparse_data_buffer *buf, - u32 plen, char **target_path, - struct cifs_sb_info *cifs_sb) +int parse_reparse_point(struct reparse_data_buffer *buf, + u32 plen, struct cifs_sb_info *cifs_sb, + bool unicode, struct cifs_open_info_data *data) { - if (plen < sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "reparse buffer is too small. Must be at least 8 bytes but was %d\n", - plen); + if (plen < sizeof(*buf)) { + cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n", + __func__, plen); return -EIO; } - if (plen < le16_to_cpu(buf->ReparseDataLength) + - sizeof(struct reparse_data_buffer)) { - cifs_dbg(VFS, "srv returned invalid reparse buf length: %d\n", - plen); + if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) { + cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n", + __func__, plen); return -EIO; } + data->reparse.buf = buf; + /* See MS-FSCC 2.1.2 */ switch (le32_to_cpu(buf->ReparseTag)) { case IO_REPARSE_TAG_NFS: - return parse_reparse_posix( - (struct reparse_posix_data *)buf, - plen, target_path, cifs_sb); + return parse_reparse_posix((struct reparse_posix_data *)buf, + cifs_sb, data); case IO_REPARSE_TAG_SYMLINK: return parse_reparse_symlink( (struct reparse_symlink_data_buffer *)buf, - plen, target_path, cifs_sb); + plen, unicode, cifs_sb, data); + case IO_REPARSE_TAG_LX_SYMLINK: + case IO_REPARSE_TAG_AF_UNIX: + case IO_REPARSE_TAG_LX_FIFO: + case IO_REPARSE_TAG_LX_CHR: + case IO_REPARSE_TAG_LX_BLK: + return 0; default: - cifs_dbg(VFS, "srv returned unknown symlink buffer tag:0x%08x\n", - le32_to_cpu(buf->ReparseTag)); + cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", + __func__, le32_to_cpu(buf->ReparseTag)); return -EOPNOTSUPP; } } -static int smb2_query_symlink(const unsigned int xid, - struct cifs_tcon *tcon, - struct cifs_sb_info *cifs_sb, - const char *full_path, - char **target_path, - struct kvec *rsp_iov) +static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, + struct kvec *rsp_iov, + struct cifs_open_info_data *data) { struct reparse_data_buffer *buf; struct smb2_ioctl_rsp *io = rsp_iov->iov_base; u32 plen = le32_to_cpu(io->OutputCount); - cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); - buf = (struct reparse_data_buffer *)((u8 *)io + le32_to_cpu(io->OutputOffset)); - return parse_reparse_point(buf, plen, target_path, cifs_sb); + return parse_reparse_point(buf, plen, cifs_sb, true, data); } static int smb2_query_reparse_point(const unsigned int xid, @@ -5064,41 +5068,24 @@ smb2_next_header(char *buf) return le32_to_cpu(hdr->NextCommand); } -static int -smb2_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) +int cifs_sfu_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) { - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - int rc = -EPERM; struct cifs_open_info_data buf = {}; - struct cifs_io_parms io_parms = {0}; - __u32 oplock = 0; - struct cifs_fid fid; + struct TCP_Server_Info *server = tcon->ses->server; struct cifs_open_parms oparms; + struct cifs_io_parms io_parms = {}; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_fid fid; unsigned int bytes_written; struct win_dev *pdev; struct kvec iov[2]; - - /* - * Check if mounted with mount parm 'sfu' mount parm. - * SFU emulation should work with all servers, but only - * supports block and char device (no socket & fifo), - * and was used by default in earlier versions of Windows - */ - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) - return rc; - - /* - * TODO: Add ability to create instead via reparse point. Windows (e.g. - * their current NFS server) uses this approach to expose special files - * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions - */ + __u32 oplock = server->oplocks ? REQ_OPLOCK : 0; + int rc; if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) - return rc; - - cifs_dbg(FYI, "sfu compat create special file\n"); + return -EPERM; oparms = (struct cifs_open_parms) { .tcon = tcon, @@ -5111,11 +5098,7 @@ smb2_make_node(unsigned int xid, struct inode *inode, .fid = &fid, }; - if (tcon->ses->server->oplocks) - oplock = REQ_OPLOCK; - else - oplock = 0; - rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf); + rc = server->ops->open(xid, &oparms, &oplock, &buf); if (rc) return rc; @@ -5123,42 +5106,56 @@ smb2_make_node(unsigned int xid, struct inode *inode, * BB Do not bother to decode buf since no local inode yet to put * timestamps in, but we can reuse it safely. */ - pdev = (struct win_dev *)&buf.fi; io_parms.pid = current->tgid; io_parms.tcon = tcon; - io_parms.offset = 0; - io_parms.length = sizeof(struct win_dev); - iov[1].iov_base = &buf.fi; - iov[1].iov_len = sizeof(struct win_dev); + io_parms.length = sizeof(*pdev); + iov[1].iov_base = pdev; + iov[1].iov_len = sizeof(*pdev); if (S_ISCHR(mode)) { memcpy(pdev->type, "IntxCHR", 8); pdev->major = cpu_to_le64(MAJOR(dev)); pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); } else if (S_ISBLK(mode)) { memcpy(pdev->type, "IntxBLK", 8); pdev->major = cpu_to_le64(MAJOR(dev)); pdev->minor = cpu_to_le64(MINOR(dev)); - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); } else if (S_ISFIFO(mode)) { memcpy(pdev->type, "LnxFIFO", 8); - pdev->major = 0; - pdev->minor = 0; - rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms, - &bytes_written, iov, 1); } - tcon->ses->server->ops->close(xid, tcon, &fid); - d_drop(dentry); + rc = server->ops->sync_write(xid, &fid, &io_parms, + &bytes_written, iov, 1); + server->ops->close(xid, tcon, &fid); + d_drop(dentry); /* FIXME: add code here to set EAs */ - cifs_free_open_info(&buf); return rc; } +static int smb2_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + /* + * Check if mounted with mount parm 'sfu' mount parm. + * SFU emulation should work with all servers, but only + * supports block and char device (no socket & fifo), + * and was used by default in earlier versions of Windows + */ + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) + return -EPERM; + /* + * TODO: Add ability to create instead via reparse point. Windows (e.g. + * their current NFS server) uses this approach to expose special files + * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions + */ + return cifs_sfu_make_node(xid, inode, dentry, tcon, + full_path, mode, dev); +} + #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY struct smb_version_operations smb20_operations = { .compare_fids = smb2_compare_fids, @@ -5209,7 +5206,7 @@ struct smb_version_operations smb20_operations = { .unlink = smb2_unlink, .rename = smb2_rename_path, .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, + .parse_reparse_point = smb2_parse_reparse_point, .query_mf_symlink = smb3_query_mf_symlink, .create_mf_symlink = smb3_create_mf_symlink, .open = smb2_open_file, @@ -5311,7 +5308,7 @@ struct smb_version_operations smb21_operations = { .unlink = smb2_unlink, .rename = smb2_rename_path, .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, + .parse_reparse_point = smb2_parse_reparse_point, .query_mf_symlink = smb3_query_mf_symlink, .create_mf_symlink = smb3_create_mf_symlink, .open = smb2_open_file, @@ -5416,7 +5413,7 @@ struct smb_version_operations smb30_operations = { .unlink = smb2_unlink, .rename = smb2_rename_path, .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, + .parse_reparse_point = smb2_parse_reparse_point, .query_mf_symlink = smb3_query_mf_symlink, .create_mf_symlink = smb3_create_mf_symlink, .open = smb2_open_file, @@ -5530,7 +5527,7 @@ struct smb_version_operations smb311_operations = { .unlink = smb2_unlink, .rename = smb2_rename_path, .create_hardlink = smb2_create_hardlink, - .query_symlink = smb2_query_symlink, + .parse_reparse_point = smb2_parse_reparse_point, .query_mf_symlink = smb3_query_mf_symlink, .create_mf_symlink = smb3_create_mf_symlink, .open = smb2_open_file, diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index 84ea67301303..5a3ca62d2f07 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -458,6 +458,8 @@ generate_smb3signingkey(struct cifs_ses *ses, ptriplet->encryption.context, ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; rc = generate_key(ses, ptriplet->decryption.label, ptriplet->decryption.context, ses->smb3decryptionkey, @@ -466,9 +468,6 @@ generate_smb3signingkey(struct cifs_ses *ses, return rc; } - if (rc) - return rc; - #ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__); /* diff --git a/fs/stat.c b/fs/stat.c index 24bb0209e459..f721d26ec3f7 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -133,7 +133,8 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, idmap = mnt_idmap(path->mnt); if (inode->i_op->getattr) return inode->i_op->getattr(idmap, path, stat, - request_mask, query_flags); + request_mask, + query_flags | AT_GETATTR_NOSEC); generic_fillattr(idmap, request_mask, inode, stat); return 0; @@ -166,6 +167,9 @@ int vfs_getattr(const struct path *path, struct kstat *stat, { int retval; + if (WARN_ON_ONCE(query_flags & AT_GETATTR_NOSEC)) + return -EPERM; + retval = security_inode_getattr(path); if (retval) return retval; diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index f8a594a50ae6..0b90869fd805 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -27,16 +27,16 @@ /* * eventfs_mutex protects the eventfs_inode (ei) dentry. Any access * to the ei->dentry must be done under this mutex and after checking - * if ei->is_freed is not set. The ei->dentry is released under the - * mutex at the same time ei->is_freed is set. If ei->is_freed is set - * then the ei->dentry is invalid. + * if ei->is_freed is not set. When ei->is_freed is set, the dentry + * is on its way to being freed after the last dput() is made on it. */ static DEFINE_MUTEX(eventfs_mutex); /* * The eventfs_inode (ei) itself is protected by SRCU. It is released from * its parent's list and will have is_freed set (under eventfs_mutex). - * After the SRCU grace period is over, the ei may be freed. + * After the SRCU grace period is over and the last dput() is called + * the ei is freed. */ DEFINE_STATIC_SRCU(eventfs_srcu); @@ -95,7 +95,7 @@ static int eventfs_set_attr(struct mnt_idmap *idmap, struct dentry *dentry, if (!(dentry->d_inode->i_mode & S_IFDIR)) { if (!ei->entry_attrs) { ei->entry_attrs = kzalloc(sizeof(*ei->entry_attrs) * ei->nr_entries, - GFP_KERNEL); + GFP_NOFS); if (!ei->entry_attrs) { ret = -ENOMEM; goto out; @@ -326,7 +326,8 @@ create_file_dentry(struct eventfs_inode *ei, int idx, struct eventfs_attr *attr = NULL; struct dentry **e_dentry = &ei->d_children[idx]; struct dentry *dentry; - bool invalidate = false; + + WARN_ON_ONCE(!inode_is_locked(parent->d_inode)); mutex_lock(&eventfs_mutex); if (ei->is_freed) { @@ -348,15 +349,8 @@ create_file_dentry(struct eventfs_inode *ei, int idx, mutex_unlock(&eventfs_mutex); - /* The lookup already has the parent->d_inode locked */ - if (!lookup) - inode_lock(parent->d_inode); - dentry = create_file(name, mode, attr, parent, data, fops); - if (!lookup) - inode_unlock(parent->d_inode); - mutex_lock(&eventfs_mutex); if (IS_ERR_OR_NULL(dentry)) { @@ -365,12 +359,14 @@ create_file_dentry(struct eventfs_inode *ei, int idx, * created the dentry for this e_dentry. In which case * use that one. * - * Note, with the mutex held, the e_dentry cannot have content - * and the ei->is_freed be true at the same time. + * If ei->is_freed is set, the e_dentry is currently on its + * way to being freed, don't return it. If e_dentry is NULL + * it means it was already freed. */ - dentry = *e_dentry; - if (WARN_ON_ONCE(dentry && ei->is_freed)) + if (ei->is_freed) dentry = NULL; + else + dentry = *e_dentry; /* The lookup does not need to up the dentry refcount */ if (dentry && !lookup) dget(dentry); @@ -387,17 +383,14 @@ create_file_dentry(struct eventfs_inode *ei, int idx, * Otherwise it means two dentries exist with the same name. */ WARN_ON_ONCE(!ei->is_freed); - invalidate = true; + dentry = NULL; } mutex_unlock(&eventfs_mutex); - if (invalidate) - d_invalidate(dentry); - - if (lookup || invalidate) + if (lookup) dput(dentry); - return invalidate ? NULL : dentry; + return dentry; } /** @@ -437,9 +430,10 @@ static struct dentry * create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, struct dentry *parent, bool lookup) { - bool invalidate = false; struct dentry *dentry = NULL; + WARN_ON_ONCE(!inode_is_locked(parent->d_inode)); + mutex_lock(&eventfs_mutex); if (pei->is_freed || ei->is_freed) { mutex_unlock(&eventfs_mutex); @@ -456,15 +450,8 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, } mutex_unlock(&eventfs_mutex); - /* The lookup already has the parent->d_inode locked */ - if (!lookup) - inode_lock(parent->d_inode); - dentry = create_dir(ei, parent); - if (!lookup) - inode_unlock(parent->d_inode); - mutex_lock(&eventfs_mutex); if (IS_ERR_OR_NULL(dentry) && !ei->is_freed) { @@ -473,8 +460,8 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, * created the dentry for this e_dentry. In which case * use that one. * - * Note, with the mutex held, the e_dentry cannot have content - * and the ei->is_freed be true at the same time. + * If ei->is_freed is set, the e_dentry is currently on its + * way to being freed. */ dentry = ei->dentry; if (dentry && !lookup) @@ -493,16 +480,14 @@ create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei, * Otherwise it means two dentries exist with the same name. */ WARN_ON_ONCE(!ei->is_freed); - invalidate = true; + dentry = NULL; } mutex_unlock(&eventfs_mutex); - if (invalidate) - d_invalidate(dentry); - if (lookup || invalidate) + if (lookup) dput(dentry); - return invalidate ? NULL : dentry; + return dentry; } /** @@ -632,7 +617,7 @@ static int add_dentries(struct dentry ***dentries, struct dentry *d, int cnt) { struct dentry **tmp; - tmp = krealloc(*dentries, sizeof(d) * (cnt + 2), GFP_KERNEL); + tmp = krealloc(*dentries, sizeof(d) * (cnt + 2), GFP_NOFS); if (!tmp) return -1; tmp[cnt] = d; @@ -698,6 +683,7 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) return -ENOMEM; } + inode_lock(parent->d_inode); list_for_each_entry_srcu(ei_child, &ei->children, list, srcu_read_lock_held(&eventfs_srcu)) { d = create_dir_dentry(ei, ei_child, parent, false); @@ -730,6 +716,7 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) cnt++; } } + inode_unlock(parent->d_inode); srcu_read_unlock(&eventfs_srcu, idx); ret = dcache_dir_open(inode, file); diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 5b54948514fe..ae648deed019 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -509,20 +509,15 @@ struct dentry *eventfs_start_creating(const char *name, struct dentry *parent) struct dentry *dentry; int error; + /* Must always have a parent. */ + if (WARN_ON_ONCE(!parent)) + return ERR_PTR(-EINVAL); + error = simple_pin_fs(&trace_fs_type, &tracefs_mount, &tracefs_mount_count); if (error) return ERR_PTR(error); - /* - * If the parent is not specified, we create it in the root. - * We need the root dentry to do this, which is in the super - * block. A pointer to that is in the struct vfsmount that we - * have around. - */ - if (!parent) - parent = tracefs_mount->mnt_root; - if (unlikely(IS_DEADDIR(parent->d_inode))) dentry = ERR_PTR(-ENOENT); else diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig index ed0bc8cbc703..567fb37274d3 100644 --- a/fs/xfs/Kconfig +++ b/fs/xfs/Kconfig @@ -147,7 +147,7 @@ config XFS_ONLINE_SCRUB_STATS bool "XFS online metadata check usage data collection" default y depends on XFS_ONLINE_SCRUB - select XFS_DEBUG + select DEBUG_FS help If you say Y here, the kernel will gather usage data about the online metadata check subsystem. This includes the number diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index 3069194527dd..100ab5931b31 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -2275,16 +2275,37 @@ xfs_alloc_min_freelist( ASSERT(mp->m_alloc_maxlevels > 0); + /* + * For a btree shorter than the maximum height, the worst case is that + * every level gets split and a new level is added, then while inserting + * another entry to refill the AGFL, every level under the old root gets + * split again. This is: + * + * (full height split reservation) + (AGFL refill split height) + * = (current height + 1) + (current height - 1) + * = (new height) + (new height - 2) + * = 2 * new height - 2 + * + * For a btree of maximum height, the worst case is that every level + * under the root gets split, then while inserting another entry to + * refill the AGFL, every level under the root gets split again. This is + * also: + * + * 2 * (current height - 1) + * = 2 * (new height - 1) + * = 2 * new height - 2 + */ + /* space needed by-bno freespace btree */ min_free = min_t(unsigned int, levels[XFS_BTNUM_BNOi] + 1, - mp->m_alloc_maxlevels); + mp->m_alloc_maxlevels) * 2 - 2; /* space needed by-size freespace btree */ min_free += min_t(unsigned int, levels[XFS_BTNUM_CNTi] + 1, - mp->m_alloc_maxlevels); + mp->m_alloc_maxlevels) * 2 - 2; /* space needed reverse mapping used space btree */ if (xfs_has_rmapbt(mp)) min_free += min_t(unsigned int, levels[XFS_BTNUM_RMAPi] + 1, - mp->m_rmap_maxlevels); + mp->m_rmap_maxlevels) * 2 - 2; return min_free; } diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index bcfb6a4203cd..f71679ce23b9 100644 --- a/fs/xfs/libxfs/xfs_defer.c +++ b/fs/xfs/libxfs/xfs_defer.c @@ -245,21 +245,18 @@ xfs_defer_create_intents( return ret; } -/* Abort all the intents that were committed. */ STATIC void -xfs_defer_trans_abort( - struct xfs_trans *tp, - struct list_head *dop_pending) +xfs_defer_pending_abort( + struct xfs_mount *mp, + struct list_head *dop_list) { struct xfs_defer_pending *dfp; const struct xfs_defer_op_type *ops; - trace_xfs_defer_trans_abort(tp, _RET_IP_); - /* Abort intent items that don't have a done item. */ - list_for_each_entry(dfp, dop_pending, dfp_list) { + list_for_each_entry(dfp, dop_list, dfp_list) { ops = defer_op_types[dfp->dfp_type]; - trace_xfs_defer_pending_abort(tp->t_mountp, dfp); + trace_xfs_defer_pending_abort(mp, dfp); if (dfp->dfp_intent && !dfp->dfp_done) { ops->abort_intent(dfp->dfp_intent); dfp->dfp_intent = NULL; @@ -267,6 +264,16 @@ xfs_defer_trans_abort( } } +/* Abort all the intents that were committed. */ +STATIC void +xfs_defer_trans_abort( + struct xfs_trans *tp, + struct list_head *dop_pending) +{ + trace_xfs_defer_trans_abort(tp, _RET_IP_); + xfs_defer_pending_abort(tp->t_mountp, dop_pending); +} + /* * Capture resources that the caller said not to release ("held") when the * transaction commits. Caller is responsible for zero-initializing @dres. @@ -756,12 +763,13 @@ xfs_defer_ops_capture( /* Release all resources that we used to capture deferred ops. */ void -xfs_defer_ops_capture_free( +xfs_defer_ops_capture_abort( struct xfs_mount *mp, struct xfs_defer_capture *dfc) { unsigned short i; + xfs_defer_pending_abort(mp, &dfc->dfc_dfops); xfs_defer_cancel_list(mp, &dfc->dfc_dfops); for (i = 0; i < dfc->dfc_held.dr_bufs; i++) @@ -802,7 +810,7 @@ xfs_defer_ops_capture_and_commit( /* Commit the transaction and add the capture structure to the list. */ error = xfs_trans_commit(tp); if (error) { - xfs_defer_ops_capture_free(mp, dfc); + xfs_defer_ops_capture_abort(mp, dfc); return error; } diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h index 114a3a4930a3..8788ad5f6a73 100644 --- a/fs/xfs/libxfs/xfs_defer.h +++ b/fs/xfs/libxfs/xfs_defer.h @@ -121,7 +121,7 @@ int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp, struct list_head *capture_list); void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp, struct xfs_defer_resources *dres); -void xfs_defer_ops_capture_free(struct xfs_mount *mp, +void xfs_defer_ops_capture_abort(struct xfs_mount *mp, struct xfs_defer_capture *d); void xfs_defer_resources_rele(struct xfs_defer_resources *dres); diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 543f3748c2a3..137a65bda95d 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -510,6 +510,9 @@ xfs_dinode_verify( if (mode && nextents + naextents > nblocks) return __this_address; + if (nextents + naextents == 0 && nblocks != 0) + return __this_address; + if (S_ISDIR(mode) && nextents > mp->m_dir_geo->max_extents) return __this_address; diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index ac6ba646624d..a013b87ab8d5 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -562,7 +562,8 @@ xfs_dquot_from_disk( struct xfs_dquot *dqp, struct xfs_buf *bp) { - struct xfs_disk_dquot *ddqp = bp->b_addr + dqp->q_bufoffset; + struct xfs_dqblk *dqb = xfs_buf_offset(bp, dqp->q_bufoffset); + struct xfs_disk_dquot *ddqp = &dqb->dd_diskdq; /* * Ensure that we got the type and ID we were looking for. @@ -1250,7 +1251,7 @@ xfs_qm_dqflush( } /* Flush the incore dquot to the ondisk buffer. */ - dqblk = bp->b_addr + dqp->q_bufoffset; + dqblk = xfs_buf_offset(bp, dqp->q_bufoffset); xfs_dquot_to_disk(&dqblk->dd_diskdq, dqp); /* diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index 8966ba842395..2c2720ce6923 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -19,6 +19,7 @@ #include "xfs_log.h" #include "xfs_log_priv.h" #include "xfs_log_recover.h" +#include "xfs_error.h" STATIC void xlog_recover_dquot_ra_pass2( @@ -65,6 +66,7 @@ xlog_recover_dquot_commit_pass2( { struct xfs_mount *mp = log->l_mp; struct xfs_buf *bp; + struct xfs_dqblk *dqb; struct xfs_disk_dquot *ddq, *recddq; struct xfs_dq_logformat *dq_f; xfs_failaddr_t fa; @@ -130,14 +132,14 @@ xlog_recover_dquot_commit_pass2( return error; ASSERT(bp); - ddq = xfs_buf_offset(bp, dq_f->qlf_boffset); + dqb = xfs_buf_offset(bp, dq_f->qlf_boffset); + ddq = &dqb->dd_diskdq; /* * If the dquot has an LSN in it, recover the dquot only if it's less * than the lsn of the transaction we are replaying. */ if (xfs_has_crc(mp)) { - struct xfs_dqblk *dqb = (struct xfs_dqblk *)ddq; xfs_lsn_t lsn = be64_to_cpu(dqb->dd_lsn); if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) { @@ -147,10 +149,23 @@ xlog_recover_dquot_commit_pass2( memcpy(ddq, recddq, item->ri_buf[1].i_len); if (xfs_has_crc(mp)) { - xfs_update_cksum((char *)ddq, sizeof(struct xfs_dqblk), + xfs_update_cksum((char *)dqb, sizeof(struct xfs_dqblk), XFS_DQUOT_CRC_OFF); } + /* Validate the recovered dquot. */ + fa = xfs_dqblk_verify(log->l_mp, dqb, dq_f->qlf_id); + if (fa) { + XFS_CORRUPTION_ERROR("Bad dquot after recovery", + XFS_ERRLEVEL_LOW, mp, dqb, + sizeof(struct xfs_dqblk)); + xfs_alert(mp, + "Metadata corruption detected at %pS, dquot 0x%x", + fa, dq_f->qlf_id); + error = -EFSCORRUPTED; + goto out_release; + } + ASSERT(dq_f->qlf_size == 2); ASSERT(bp->b_mount == mp); bp->b_flags |= _XBF_LOGRECOVERY; diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 3dc47937da5d..3beb470f1892 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -569,6 +569,14 @@ extern void xfs_setup_inode(struct xfs_inode *ip); extern void xfs_setup_iops(struct xfs_inode *ip); extern void xfs_diflags_to_iflags(struct xfs_inode *ip, bool init); +static inline void xfs_update_stable_writes(struct xfs_inode *ip) +{ + if (bdev_stable_writes(xfs_inode_buftarg(ip)->bt_bdev)) + mapping_set_stable_writes(VFS_I(ip)->i_mapping); + else + mapping_clear_stable_writes(VFS_I(ip)->i_mapping); +} + /* * When setting up a newly allocated inode, we need to call * xfs_finish_inode_setup() once the inode is fully instantiated at diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c index 0e5dba2343ea..144198a6b270 100644 --- a/fs/xfs/xfs_inode_item_recover.c +++ b/fs/xfs/xfs_inode_item_recover.c @@ -286,6 +286,7 @@ xlog_recover_inode_commit_pass2( struct xfs_log_dinode *ldip; uint isize; int need_free = 0; + xfs_failaddr_t fa; if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) { in_f = item->ri_buf[0].i_addr; @@ -369,24 +370,26 @@ xlog_recover_inode_commit_pass2( * superblock flag to determine whether we need to look at di_flushiter * to skip replay when the on disk inode is newer than the log one */ - if (!xfs_has_v3inodes(mp) && - ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) { - /* - * Deal with the wrap case, DI_MAX_FLUSH is less - * than smaller numbers - */ - if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH && - ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) { - /* do nothing */ - } else { - trace_xfs_log_recover_inode_skip(log, in_f); - error = 0; - goto out_release; + if (!xfs_has_v3inodes(mp)) { + if (ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) { + /* + * Deal with the wrap case, DI_MAX_FLUSH is less + * than smaller numbers + */ + if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH && + ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) { + /* do nothing */ + } else { + trace_xfs_log_recover_inode_skip(log, in_f); + error = 0; + goto out_release; + } } + + /* Take the opportunity to reset the flush iteration count */ + ldip->di_flushiter = 0; } - /* Take the opportunity to reset the flush iteration count */ - ldip->di_flushiter = 0; if (unlikely(S_ISREG(ldip->di_mode))) { if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) && @@ -528,8 +531,19 @@ out_owner_change: (dip->di_mode != 0)) error = xfs_recover_inode_owner_change(mp, dip, in_f, buffer_list); - /* re-generate the checksum. */ + /* re-generate the checksum and validate the recovered inode. */ xfs_dinode_calc_crc(log->l_mp, dip); + fa = xfs_dinode_verify(log->l_mp, in_f->ilf_ino, dip); + if (fa) { + XFS_CORRUPTION_ERROR( + "Bad dinode after recovery", + XFS_ERRLEVEL_LOW, mp, dip, sizeof(*dip)); + xfs_alert(mp, + "Metadata corruption detected at %pS, inode 0x%llx", + fa, in_f->ilf_ino); + error = -EFSCORRUPTED; + goto out_release; + } ASSERT(bp->b_mount == mp); bp->b_flags |= _XBF_LOGRECOVERY; diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index a82470e027f7..6c3919687ea6 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -1121,23 +1121,25 @@ xfs_ioctl_setattr_xflags( struct fileattr *fa) { struct xfs_mount *mp = ip->i_mount; + bool rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME); uint64_t i_flags2; - /* Can't change realtime flag if any extents are allocated. */ - if ((ip->i_df.if_nextents || ip->i_delayed_blks) && - XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME)) - return -EINVAL; + if (rtflag != XFS_IS_REALTIME_INODE(ip)) { + /* Can't change realtime flag if any extents are allocated. */ + if (ip->i_df.if_nextents || ip->i_delayed_blks) + return -EINVAL; + } - /* If realtime flag is set then must have realtime device */ - if (fa->fsx_xflags & FS_XFLAG_REALTIME) { + if (rtflag) { + /* If realtime flag is set then must have realtime device */ if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 || xfs_extlen_to_rtxmod(mp, ip->i_extsize)) return -EINVAL; - } - /* Clear reflink if we are actually able to set the rt flag. */ - if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip)) - ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK; + /* Clear reflink if we are actually able to set the rt flag. */ + if (xfs_is_reflink_inode(ip)) + ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK; + } /* diflags2 only valid for v3 inodes. */ i_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); @@ -1148,6 +1150,14 @@ xfs_ioctl_setattr_xflags( ip->i_diflags2 = i_flags2; xfs_diflags_to_iflags(ip, false); + + /* + * Make the stable writes flag match that of the device the inode + * resides on when flipping the RT flag. + */ + if (rtflag != XFS_IS_REALTIME_INODE(ip) && S_ISREG(VFS_I(ip)->i_mode)) + xfs_update_stable_writes(ip); + xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); XFS_STATS_INC(mp, xs_ig_attrchg); diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index fdfda4fba12b..a0d77f5f512e 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -1299,6 +1299,13 @@ xfs_setup_inode( mapping_set_gfp_mask(inode->i_mapping, (gfp_mask & ~(__GFP_FS))); /* + * For real-time inodes update the stable write flags to that of the RT + * device instead of the data device. + */ + if (S_ISREG(inode->i_mode) && XFS_IS_REALTIME_INODE(ip)) + xfs_update_stable_writes(ip); + + /* * If there is no attribute fork no ACL can exist on this inode, * and it can't have any file capabilities attached to it either. */ diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 51c100c86177..ee206facf0dc 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1893,9 +1893,7 @@ xlog_write_iclog( * the buffer manually, the code needs to be kept in sync * with the I/O completion path. */ - xlog_state_done_syncing(iclog); - up(&iclog->ic_sema); - return; + goto sync; } /* @@ -1925,20 +1923,17 @@ xlog_write_iclog( * avoid shutdown re-entering this path and erroring out again. */ if (log->l_targ != log->l_mp->m_ddev_targp && - blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev)) { - xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); - return; - } + blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev)) + goto shutdown; } if (iclog->ic_flags & XLOG_ICL_NEED_FUA) iclog->ic_bio.bi_opf |= REQ_FUA; iclog->ic_flags &= ~(XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA); - if (xlog_map_iclog_data(&iclog->ic_bio, iclog->ic_data, count)) { - xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); - return; - } + if (xlog_map_iclog_data(&iclog->ic_bio, iclog->ic_data, count)) + goto shutdown; + if (is_vmalloc_addr(iclog->ic_data)) flush_kernel_vmap_range(iclog->ic_data, count); @@ -1959,6 +1954,12 @@ xlog_write_iclog( } submit_bio(&iclog->ic_bio); + return; +shutdown: + xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); +sync: + xlog_state_done_syncing(iclog); + up(&iclog->ic_sema); } /* diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 13b94d2e605b..a1e18b24971a 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2511,7 +2511,7 @@ xlog_abort_defer_ops( list_for_each_entry_safe(dfc, next, capture_list, dfc_list) { list_del_init(&dfc->dfc_list); - xfs_defer_ops_capture_free(mp, dfc); + xfs_defer_ops_capture_abort(mp, dfc); } } diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 658edee8381d..e5b62dc28466 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -784,6 +784,7 @@ xfs_reflink_end_cow_extent( } } del = got; + xfs_trim_extent(&del, *offset_fsb, end_fsb - *offset_fsb); /* Grab the corresponding mapping in the data fork. */ nmaps = 1; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index afeed6e72049..1216d72c650f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -542,6 +542,7 @@ int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_init_power(struct acpi_device *device); int acpi_device_fix_up_power(struct acpi_device *device); void acpi_device_fix_up_power_extended(struct acpi_device *adev); +void acpi_device_fix_up_power_children(struct acpi_device *adev); int acpi_bus_update_power(acpi_handle handle, int *state_p); int acpi_device_update_power(struct acpi_device *device, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h index 995513fa2690..0655aa5b57b2 100644 --- a/include/asm-generic/qspinlock.h +++ b/include/asm-generic/qspinlock.h @@ -70,7 +70,7 @@ static __always_inline int queued_spin_is_locked(struct qspinlock *lock) */ static __always_inline int queued_spin_value_unlocked(struct qspinlock lock) { - return !atomic_read(&lock.val); + return !lock.val.counter; } /** diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index e69cece404b3..83d2039c018b 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -148,6 +148,7 @@ #define DP_RECEIVE_PORT_0_CAP_0 0x008 # define DP_LOCAL_EDID_PRESENT (1 << 1) # define DP_ASSOCIATED_TO_PRECEDING_PORT (1 << 2) +# define DP_HBLANK_EXPANSION_CAPABLE (1 << 3) #define DP_RECEIVE_PORT_0_BUFFER_SIZE 0x009 @@ -543,6 +544,10 @@ /* DFP Capability Extension */ #define DP_DFP_CAPABILITY_EXTENSION_SUPPORT 0x0a3 /* 2.0 */ +#define DP_PANEL_REPLAY_CAP 0x0b0 /* DP 2.0 */ +# define DP_PANEL_REPLAY_SUPPORT (1 << 0) +# define DP_PANEL_REPLAY_SU_SUPPORT (1 << 1) + /* Link Configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_RATE_TABLE 0x00 /* eDP 1.4 */ @@ -699,6 +704,7 @@ #define DP_DSC_ENABLE 0x160 /* DP 1.4 */ # define DP_DECOMPRESSION_EN (1 << 0) +# define DP_DSC_PASSTHROUGH_EN (1 << 1) #define DP_DSC_CONFIGURATION 0x161 /* DP 2.0 */ #define DP_PSR_EN_CFG 0x170 /* XXX 1.2? */ @@ -716,6 +722,13 @@ #define DP_BRANCH_DEVICE_CTRL 0x1a1 # define DP_BRANCH_DEVICE_IRQ_HPD (1 << 0) +#define PANEL_REPLAY_CONFIG 0x1b0 /* DP 2.0 */ +# define DP_PANEL_REPLAY_ENABLE (1 << 0) +# define DP_PANEL_REPLAY_UNRECOVERABLE_ERROR_EN (1 << 3) +# define DP_PANEL_REPLAY_RFB_STORAGE_ERROR_EN (1 << 4) +# define DP_PANEL_REPLAY_ACTIVE_FRAME_CRC_ERROR_EN (1 << 5) +# define DP_PANEL_REPLAY_SU_ENABLE (1 << 6) + #define DP_PAYLOAD_ALLOCATE_SET 0x1c0 #define DP_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x1c1 #define DP_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x1c2 @@ -1105,6 +1118,18 @@ #define DP_LANE_ALIGN_STATUS_UPDATED_ESI 0x200e /* status same as 0x204 */ #define DP_SINK_STATUS_ESI 0x200f /* status same as 0x205 */ +#define DP_PANEL_REPLAY_ERROR_STATUS 0x2020 /* DP 2.1*/ +# define DP_PANEL_REPLAY_LINK_CRC_ERROR (1 << 0) +# define DP_PANEL_REPLAY_RFB_STORAGE_ERROR (1 << 1) +# define DP_PANEL_REPLAY_VSC_SDP_UNCORRECTABLE_ERROR (1 << 2) + +#define DP_SINK_DEVICE_PR_AND_FRAME_LOCK_STATUS 0x2022 /* DP 2.1 */ +# define DP_SINK_DEVICE_PANEL_REPLAY_STATUS_MASK (7 << 0) +# define DP_SINK_FRAME_LOCKED_SHIFT 3 +# define DP_SINK_FRAME_LOCKED_MASK (3 << 3) +# define DP_SINK_FRAME_LOCKED_STATUS_VALID_SHIFT 5 +# define DP_SINK_FRAME_LOCKED_STATUS_VALID_MASK (1 << 5) + /* Extended Receiver Capability: See DP_DPCD_REV for definitions */ #define DP_DP13_DPCD_REV 0x2200 diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 3d74b2cec72f..863b2e7add29 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -164,6 +164,7 @@ drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) } /* DP/eDP DSC support */ +u8 drm_dp_dsc_sink_bpp_incr(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); u8 drm_dp_dsc_sink_max_slice_count(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE], bool is_edp); u8 drm_dp_dsc_sink_line_buf_depth(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]); @@ -251,6 +252,19 @@ drm_edp_backlight_supported(const u8 edp_dpcd[EDP_DISPLAY_CTL_CAP_SIZE]) return !!(edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP); } +/** + * drm_dp_is_uhbr_rate - Determine if a link rate is UHBR + * @link_rate: link rate in 10kbits/s units + * + * Determine if the provided link rate is an UHBR rate. + * + * Returns: %True if @link_rate is an UHBR rate. + */ +static inline bool drm_dp_is_uhbr_rate(int link_rate) +{ + return link_rate >= 1000000; +} + /* * DisplayPort AUX channel */ @@ -632,6 +646,13 @@ enum drm_dp_quirk { * the DP_MAX_LINK_RATE register reporting a lower max multiplier. */ DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS, + /** + * @DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC: + * + * The device applies HBLANK expansion for some modes, but this + * requires enabling DSC. + */ + DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC, }; /** @@ -781,4 +802,15 @@ bool drm_dp_downstream_rgb_to_ycbcr_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZ const u8 port_cap[4], u8 color_spc); int drm_dp_pcon_convert_rgb_to_ycbcr(struct drm_dp_aux *aux, u8 color_spc); +#define DRM_DP_BW_OVERHEAD_MST BIT(0) +#define DRM_DP_BW_OVERHEAD_UHBR BIT(1) +#define DRM_DP_BW_OVERHEAD_SSC_REF_CLK BIT(2) +#define DRM_DP_BW_OVERHEAD_FEC BIT(3) +#define DRM_DP_BW_OVERHEAD_DSC BIT(4) + +int drm_dp_bw_overhead(int lane_count, int hactive, + int dsc_slice_count, + int bpp_x16, unsigned long flags); +int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); + #endif /* _DRM_DP_HELPER_H_ */ diff --git a/include/drm/display/drm_dp_mst_helper.h b/include/drm/display/drm_dp_mst_helper.h index 4429d3b1745b..9b19d8bd520a 100644 --- a/include/drm/display/drm_dp_mst_helper.h +++ b/include/drm/display/drm_dp_mst_helper.h @@ -25,6 +25,7 @@ #include <linux/types.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_atomic.h> +#include <drm/drm_fixed.h> #if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) #include <linux/stackdepot.h> @@ -617,7 +618,7 @@ struct drm_dp_mst_topology_state { * @pbn_div: The current PBN divisor for this topology. The driver is expected to fill this * out itself. */ - int pbn_div; + fixed20_12 pbn_div; }; #define to_dp_mst_topology_mgr(x) container_of(x, struct drm_dp_mst_topology_mgr, base) @@ -839,10 +840,10 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); -int drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr, - int link_rate, int link_lane_count); +fixed20_12 drm_dp_get_vc_payload_bw(const struct drm_dp_mst_topology_mgr *mgr, + int link_rate, int link_lane_count); -int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc); +int drm_dp_calc_pbn_mode(int clock, int bpp); void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap); @@ -892,6 +893,9 @@ drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state, struct drm_dp_mst_atomic_payload * drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state, struct drm_dp_mst_port *port); +bool drm_dp_mst_port_downstream_of_parent(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, + struct drm_dp_mst_port *parent); int __must_check drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr, @@ -913,6 +917,10 @@ int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, struct drm_dp_query_stream_enc_status_ack_reply *status); +int __must_check drm_dp_mst_atomic_check_mgr(struct drm_atomic_state *state, + struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_port **failing_port); int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state); int __must_check drm_dp_mst_root_conn_atomic_check(struct drm_connector_state *new_conn_state, struct drm_dp_mst_topology_mgr *mgr); diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h index 81c298488b0c..54b2b2467bfd 100644 --- a/include/drm/drm_color_mgmt.h +++ b/include/drm/drm_color_mgmt.h @@ -36,20 +36,17 @@ struct drm_plane; * * Extract a degamma/gamma LUT value provided by user (in the form of * &drm_color_lut entries) and round it to the precision supported by the - * hardware. + * hardware, following OpenGL int<->float conversion rules + * (see eg. OpenGL 4.6 specification - 2.3.5 Fixed-Point Data Conversions). */ static inline u32 drm_color_lut_extract(u32 user_input, int bit_precision) { - u32 val = user_input; - u32 max = 0xffff >> (16 - bit_precision); - - /* Round only if we're not using full precision. */ - if (bit_precision < 16) { - val += 1UL << (16 - bit_precision - 1); - val >>= 16 - bit_precision; - } - - return clamp_val(val, 0, max); + if (bit_precision > 16) + return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(user_input, (1 << bit_precision) - 1), + (1 << 16) - 1); + else + return DIV_ROUND_CLOSEST(user_input * ((1 << bit_precision) - 1), + (1 << 16) - 1); } u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 882d2638708e..518d1b8106c7 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -269,64 +269,6 @@ struct detailed_timing { #define DRM_EDID_DSC_MAX_SLICES 0xf #define DRM_EDID_DSC_TOTAL_CHUNK_KBYTES 0x3f -/* ELD Header Block */ -#define DRM_ELD_HEADER_BLOCK_SIZE 4 - -#define DRM_ELD_VER 0 -# define DRM_ELD_VER_SHIFT 3 -# define DRM_ELD_VER_MASK (0x1f << 3) -# define DRM_ELD_VER_CEA861D (2 << 3) /* supports 861D or below */ -# define DRM_ELD_VER_CANNED (0x1f << 3) - -#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ - -/* ELD Baseline Block for ELD_Ver == 2 */ -#define DRM_ELD_CEA_EDID_VER_MNL 4 -# define DRM_ELD_CEA_EDID_VER_SHIFT 5 -# define DRM_ELD_CEA_EDID_VER_MASK (7 << 5) -# define DRM_ELD_CEA_EDID_VER_NONE (0 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861 (1 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861A (2 << 5) -# define DRM_ELD_CEA_EDID_VER_CEA861BCD (3 << 5) -# define DRM_ELD_MNL_SHIFT 0 -# define DRM_ELD_MNL_MASK (0x1f << 0) - -#define DRM_ELD_SAD_COUNT_CONN_TYPE 5 -# define DRM_ELD_SAD_COUNT_SHIFT 4 -# define DRM_ELD_SAD_COUNT_MASK (0xf << 4) -# define DRM_ELD_CONN_TYPE_SHIFT 2 -# define DRM_ELD_CONN_TYPE_MASK (3 << 2) -# define DRM_ELD_CONN_TYPE_HDMI (0 << 2) -# define DRM_ELD_CONN_TYPE_DP (1 << 2) -# define DRM_ELD_SUPPORTS_AI (1 << 1) -# define DRM_ELD_SUPPORTS_HDCP (1 << 0) - -#define DRM_ELD_AUD_SYNCH_DELAY 6 /* in units of 2 ms */ -# define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ - -#define DRM_ELD_SPEAKER 7 -# define DRM_ELD_SPEAKER_MASK 0x7f -# define DRM_ELD_SPEAKER_RLRC (1 << 6) -# define DRM_ELD_SPEAKER_FLRC (1 << 5) -# define DRM_ELD_SPEAKER_RC (1 << 4) -# define DRM_ELD_SPEAKER_RLR (1 << 3) -# define DRM_ELD_SPEAKER_FC (1 << 2) -# define DRM_ELD_SPEAKER_LFE (1 << 1) -# define DRM_ELD_SPEAKER_FLR (1 << 0) - -#define DRM_ELD_PORT_ID 8 /* offsets 8..15 inclusive */ -# define DRM_ELD_PORT_ID_LEN 8 - -#define DRM_ELD_MANUFACTURER_NAME0 16 -#define DRM_ELD_MANUFACTURER_NAME1 17 - -#define DRM_ELD_PRODUCT_CODE0 18 -#define DRM_ELD_PRODUCT_CODE1 19 - -#define DRM_ELD_MONITOR_NAME_STRING 20 /* offsets 20..(20+mnl-1) inclusive */ - -#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad)) - struct edid { u8 header[8]; /* Vendor & product info */ @@ -387,11 +329,6 @@ int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb); int drm_av_sync_delay(struct drm_connector *connector, const struct drm_display_mode *mode); -#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE -int __drm_set_edid_firmware_path(const char *path); -int __drm_get_edid_firmware_path(char *buf, size_t bufsize); -#endif - bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2); int @@ -410,96 +347,6 @@ drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame, enum hdmi_quantization_range rgb_quant_range); /** - * drm_eld_mnl - Get ELD monitor name length in bytes. - * @eld: pointer to an eld memory structure with mnl set - */ -static inline int drm_eld_mnl(const uint8_t *eld) -{ - return (eld[DRM_ELD_CEA_EDID_VER_MNL] & DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; -} - -/** - * drm_eld_sad - Get ELD SAD structures. - * @eld: pointer to an eld memory structure with sad_count set - */ -static inline const uint8_t *drm_eld_sad(const uint8_t *eld) -{ - unsigned int ver, mnl; - - ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT; - if (ver != 2 && ver != 31) - return NULL; - - mnl = drm_eld_mnl(eld); - if (mnl > 16) - return NULL; - - return eld + DRM_ELD_CEA_SAD(mnl, 0); -} - -/** - * drm_eld_sad_count - Get ELD SAD count. - * @eld: pointer to an eld memory structure with sad_count set - */ -static inline int drm_eld_sad_count(const uint8_t *eld) -{ - return (eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_SAD_COUNT_MASK) >> - DRM_ELD_SAD_COUNT_SHIFT; -} - -/** - * drm_eld_calc_baseline_block_size - Calculate baseline block size in bytes - * @eld: pointer to an eld memory structure with mnl and sad_count set - * - * This is a helper for determining the payload size of the baseline block, in - * bytes, for e.g. setting the Baseline_ELD_Len field in the ELD header block. - */ -static inline int drm_eld_calc_baseline_block_size(const uint8_t *eld) -{ - return DRM_ELD_MONITOR_NAME_STRING - DRM_ELD_HEADER_BLOCK_SIZE + - drm_eld_mnl(eld) + drm_eld_sad_count(eld) * 3; -} - -/** - * drm_eld_size - Get ELD size in bytes - * @eld: pointer to a complete eld memory structure - * - * The returned value does not include the vendor block. It's vendor specific, - * and comprises of the remaining bytes in the ELD memory buffer after - * drm_eld_size() bytes of header and baseline block. - * - * The returned value is guaranteed to be a multiple of 4. - */ -static inline int drm_eld_size(const uint8_t *eld) -{ - return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; -} - -/** - * drm_eld_get_spk_alloc - Get speaker allocation - * @eld: pointer to an ELD memory structure - * - * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER - * field definitions to identify speakers. - */ -static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld) -{ - return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; -} - -/** - * drm_eld_get_conn_type - Get device type hdmi/dp connected - * @eld: pointer to an ELD memory structure - * - * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to - * identify the display type connected. - */ -static inline u8 drm_eld_get_conn_type(const uint8_t *eld) -{ - return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK; -} - -/** * drm_edid_decode_mfg_id - Decode the manufacturer ID * @mfg_id: The manufacturer ID * @vend: A 4-byte buffer to store the 3-letter vendor string plus a '\0' diff --git a/include/drm/drm_eld.h b/include/drm/drm_eld.h new file mode 100644 index 000000000000..0a88d10b28b0 --- /dev/null +++ b/include/drm/drm_eld.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __DRM_ELD_H__ +#define __DRM_ELD_H__ + +#include <linux/types.h> + +struct cea_sad; + +/* ELD Header Block */ +#define DRM_ELD_HEADER_BLOCK_SIZE 4 + +#define DRM_ELD_VER 0 +# define DRM_ELD_VER_SHIFT 3 +# define DRM_ELD_VER_MASK (0x1f << 3) +# define DRM_ELD_VER_CEA861D (2 << 3) /* supports 861D or below */ +# define DRM_ELD_VER_CANNED (0x1f << 3) + +#define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ + +/* ELD Baseline Block for ELD_Ver == 2 */ +#define DRM_ELD_CEA_EDID_VER_MNL 4 +# define DRM_ELD_CEA_EDID_VER_SHIFT 5 +# define DRM_ELD_CEA_EDID_VER_MASK (7 << 5) +# define DRM_ELD_CEA_EDID_VER_NONE (0 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861 (1 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861A (2 << 5) +# define DRM_ELD_CEA_EDID_VER_CEA861BCD (3 << 5) +# define DRM_ELD_MNL_SHIFT 0 +# define DRM_ELD_MNL_MASK (0x1f << 0) + +#define DRM_ELD_SAD_COUNT_CONN_TYPE 5 +# define DRM_ELD_SAD_COUNT_SHIFT 4 +# define DRM_ELD_SAD_COUNT_MASK (0xf << 4) +# define DRM_ELD_CONN_TYPE_SHIFT 2 +# define DRM_ELD_CONN_TYPE_MASK (3 << 2) +# define DRM_ELD_CONN_TYPE_HDMI (0 << 2) +# define DRM_ELD_CONN_TYPE_DP (1 << 2) +# define DRM_ELD_SUPPORTS_AI (1 << 1) +# define DRM_ELD_SUPPORTS_HDCP (1 << 0) + +#define DRM_ELD_AUD_SYNCH_DELAY 6 /* in units of 2 ms */ +# define DRM_ELD_AUD_SYNCH_DELAY_MAX 0xfa /* 500 ms */ + +#define DRM_ELD_SPEAKER 7 +# define DRM_ELD_SPEAKER_MASK 0x7f +# define DRM_ELD_SPEAKER_RLRC (1 << 6) +# define DRM_ELD_SPEAKER_FLRC (1 << 5) +# define DRM_ELD_SPEAKER_RC (1 << 4) +# define DRM_ELD_SPEAKER_RLR (1 << 3) +# define DRM_ELD_SPEAKER_FC (1 << 2) +# define DRM_ELD_SPEAKER_LFE (1 << 1) +# define DRM_ELD_SPEAKER_FLR (1 << 0) + +#define DRM_ELD_PORT_ID 8 /* offsets 8..15 inclusive */ +# define DRM_ELD_PORT_ID_LEN 8 + +#define DRM_ELD_MANUFACTURER_NAME0 16 +#define DRM_ELD_MANUFACTURER_NAME1 17 + +#define DRM_ELD_PRODUCT_CODE0 18 +#define DRM_ELD_PRODUCT_CODE1 19 + +#define DRM_ELD_MONITOR_NAME_STRING 20 /* offsets 20..(20+mnl-1) inclusive */ + +#define DRM_ELD_CEA_SAD(mnl, sad) (20 + (mnl) + 3 * (sad)) + +/** + * drm_eld_mnl - Get ELD monitor name length in bytes. + * @eld: pointer to an eld memory structure with mnl set + */ +static inline int drm_eld_mnl(const u8 *eld) +{ + return (eld[DRM_ELD_CEA_EDID_VER_MNL] & DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; +} + +int drm_eld_sad_get(const u8 *eld, int sad_index, struct cea_sad *cta_sad); +int drm_eld_sad_set(u8 *eld, int sad_index, const struct cea_sad *cta_sad); + +/** + * drm_eld_sad - Get ELD SAD structures. + * @eld: pointer to an eld memory structure with sad_count set + */ +static inline const u8 *drm_eld_sad(const u8 *eld) +{ + unsigned int ver, mnl; + + ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT; + if (ver != 2 && ver != 31) + return NULL; + + mnl = drm_eld_mnl(eld); + if (mnl > 16) + return NULL; + + return eld + DRM_ELD_CEA_SAD(mnl, 0); +} + +/** + * drm_eld_sad_count - Get ELD SAD count. + * @eld: pointer to an eld memory structure with sad_count set + */ +static inline int drm_eld_sad_count(const u8 *eld) +{ + return (eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_SAD_COUNT_MASK) >> + DRM_ELD_SAD_COUNT_SHIFT; +} + +/** + * drm_eld_calc_baseline_block_size - Calculate baseline block size in bytes + * @eld: pointer to an eld memory structure with mnl and sad_count set + * + * This is a helper for determining the payload size of the baseline block, in + * bytes, for e.g. setting the Baseline_ELD_Len field in the ELD header block. + */ +static inline int drm_eld_calc_baseline_block_size(const u8 *eld) +{ + return DRM_ELD_MONITOR_NAME_STRING - DRM_ELD_HEADER_BLOCK_SIZE + + drm_eld_mnl(eld) + drm_eld_sad_count(eld) * 3; +} + +/** + * drm_eld_size - Get ELD size in bytes + * @eld: pointer to a complete eld memory structure + * + * The returned value does not include the vendor block. It's vendor specific, + * and comprises of the remaining bytes in the ELD memory buffer after + * drm_eld_size() bytes of header and baseline block. + * + * The returned value is guaranteed to be a multiple of 4. + */ +static inline int drm_eld_size(const u8 *eld) +{ + return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; +} + +/** + * drm_eld_get_spk_alloc - Get speaker allocation + * @eld: pointer to an ELD memory structure + * + * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER + * field definitions to identify speakers. + */ +static inline u8 drm_eld_get_spk_alloc(const u8 *eld) +{ + return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK; +} + +/** + * drm_eld_get_conn_type - Get device type hdmi/dp connected + * @eld: pointer to an ELD memory structure + * + * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to + * identify the display type connected. + */ +static inline u8 drm_eld_get_conn_type(const u8 *eld) +{ + return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK; +} + +#endif /* __DRM_ELD_H__ */ diff --git a/include/drm/drm_flip_work.h b/include/drm/drm_flip_work.h index 21c3d512d25c..1eef3283a109 100644 --- a/include/drm/drm_flip_work.h +++ b/include/drm/drm_flip_work.h @@ -31,11 +31,10 @@ /** * DOC: flip utils * - * Util to queue up work to run from work-queue context after flip/vblank. + * Utility to queue up work to run from work-queue context after flip/vblank. * Typically this can be used to defer unref of framebuffer's, cursor - * bo's, etc until after vblank. The APIs are all thread-safe. - * Moreover, drm_flip_work_queue_task and drm_flip_work_queue can be called - * in atomic context. + * bo's, etc until after vblank. The APIs are all thread-safe. Moreover, + * drm_flip_work_commit() can be called in atomic context. */ struct drm_flip_work; @@ -52,16 +51,6 @@ struct drm_flip_work; typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val); /** - * struct drm_flip_task - flip work task - * @node: list entry element - * @data: data to pass to &drm_flip_work.func - */ -struct drm_flip_task { - struct list_head node; - void *data; -}; - -/** * struct drm_flip_work - flip work queue * @name: debug name * @func: callback fxn called for each committed item @@ -79,9 +68,6 @@ struct drm_flip_work { spinlock_t lock; }; -struct drm_flip_task *drm_flip_work_allocate_task(void *data, gfp_t flags); -void drm_flip_work_queue_task(struct drm_flip_work *work, - struct drm_flip_task *task); void drm_flip_work_queue(struct drm_flip_work *work, void *val); void drm_flip_work_commit(struct drm_flip_work *work, struct workqueue_struct *wq); diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index 291deb09475b..f13b34e0b752 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -15,6 +15,57 @@ struct drm_rect; struct iosys_map; +/** + * struct drm_format_conv_state - Stores format-conversion state + * + * DRM helpers for format conversion store temporary state in + * struct drm_xfrm_buf. The buffer's resources can be reused + * among multiple conversion operations. + * + * All fields are considered private. + */ +struct drm_format_conv_state { + struct { + void *mem; + size_t size; + bool preallocated; + } tmp; +}; + +#define __DRM_FORMAT_CONV_STATE_INIT(_mem, _size, _preallocated) { \ + .tmp = { \ + .mem = (_mem), \ + .size = (_size), \ + .preallocated = (_preallocated), \ + } \ + } + +/** + * DRM_FORMAT_CONV_STATE_INIT - Initializer for struct drm_format_conv_state + * + * Initializes an instance of struct drm_format_conv_state to default values. + */ +#define DRM_FORMAT_CONV_STATE_INIT \ + __DRM_FORMAT_CONV_STATE_INIT(NULL, 0, false) + +/** + * DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED - Initializer for struct drm_format_conv_state + * @_mem: The preallocated memory area + * @_size: The number of bytes in _mem + * + * Initializes an instance of struct drm_format_conv_state to preallocated + * storage. The caller is responsible for releasing the provided memory range. + */ +#define DRM_FORMAT_CONV_STATE_INIT_PREALLOCATED(_mem, _size) \ + __DRM_FORMAT_CONV_STATE_INIT(_mem, _size, true) + +void drm_format_conv_state_init(struct drm_format_conv_state *state); +void drm_format_conv_state_copy(struct drm_format_conv_state *state, + const struct drm_format_conv_state *old_state); +void *drm_format_conv_state_reserve(struct drm_format_conv_state *state, + size_t new_size, gfp_t flags); +void drm_format_conv_state_release(struct drm_format_conv_state *state); + unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format, const struct drm_rect *clip); @@ -23,45 +74,49 @@ void drm_fb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch, const struct drm_rect *clip); void drm_fb_swab(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip, bool cached); + const struct drm_rect *clip, bool cached, + struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_rgb332(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_rgb565(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip, bool swab); + const struct drm_rect *clip, struct drm_format_conv_state *state, + bool swab); void drm_fb_xrgb8888_to_xrgb1555(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_argb1555(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_rgba5551(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_rgb888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_argb8888(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_xrgb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, + struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_argb2101010(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, + struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_gray8(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *rect); + const struct drm_rect *clip, struct drm_format_conv_state *state); void drm_fb_xrgb8888_to_mono(struct iosys_map *dst, const unsigned int *dst_pitch, const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip); + const struct drm_rect *clip, struct drm_format_conv_state *state); size_t drm_fb_build_fourcc_list(struct drm_device *dev, const u32 *native_fourccs, size_t native_nfourccs, diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 16364487fde9..369505447acd 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -580,7 +580,7 @@ int drm_gem_evict(struct drm_gem_object *obj); * drm_gem_gpuva_init() - initialize the gpuva list of a GEM object * @obj: the &drm_gem_object * - * This initializes the &drm_gem_object's &drm_gpuva list. + * This initializes the &drm_gem_object's &drm_gpuvm_bo list. * * Calling this function is only necessary for drivers intending to support the * &drm_driver_feature DRIVER_GEM_GPUVA. @@ -593,28 +593,28 @@ static inline void drm_gem_gpuva_init(struct drm_gem_object *obj) } /** - * drm_gem_for_each_gpuva() - iternator to walk over a list of gpuvas - * @entry__: &drm_gpuva structure to assign to in each iteration step - * @obj__: the &drm_gem_object the &drm_gpuvas to walk are associated with + * drm_gem_for_each_gpuvm_bo() - iterator to walk over a list of &drm_gpuvm_bo + * @entry__: &drm_gpuvm_bo structure to assign to in each iteration step + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with * - * This iterator walks over all &drm_gpuva structures associated with the - * &drm_gpuva_manager. + * This iterator walks over all &drm_gpuvm_bo structures associated with the + * &drm_gem_object. */ -#define drm_gem_for_each_gpuva(entry__, obj__) \ - list_for_each_entry(entry__, &(obj__)->gpuva.list, gem.entry) +#define drm_gem_for_each_gpuvm_bo(entry__, obj__) \ + list_for_each_entry(entry__, &(obj__)->gpuva.list, list.entry.gem) /** - * drm_gem_for_each_gpuva_safe() - iternator to safely walk over a list of - * gpuvas - * @entry__: &drm_gpuva structure to assign to in each iteration step - * @next__: &next &drm_gpuva to store the next step - * @obj__: the &drm_gem_object the &drm_gpuvas to walk are associated with + * drm_gem_for_each_gpuvm_bo_safe() - iterator to safely walk over a list of + * &drm_gpuvm_bo + * @entry__: &drm_gpuvm_bostructure to assign to in each iteration step + * @next__: &next &drm_gpuvm_bo to store the next step + * @obj__: the &drm_gem_object the &drm_gpuvm_bo to walk are associated with * - * This iterator walks over all &drm_gpuva structures associated with the + * This iterator walks over all &drm_gpuvm_bo structures associated with the * &drm_gem_object. It is implemented with list_for_each_entry_safe(), hence * it is save against removal of elements. */ -#define drm_gem_for_each_gpuva_safe(entry__, next__, obj__) \ - list_for_each_entry_safe(entry__, next__, &(obj__)->gpuva.list, gem.entry) +#define drm_gem_for_each_gpuvm_bo_safe(entry__, next__, obj__) \ + list_for_each_entry_safe(entry__, next__, &(obj__)->gpuva.list, list.entry.gem) #endif /* __DRM_GEM_H__ */ diff --git a/include/drm/drm_gem_atomic_helper.h b/include/drm/drm_gem_atomic_helper.h index 40b8b039518e..3e01c619a25e 100644 --- a/include/drm/drm_gem_atomic_helper.h +++ b/include/drm/drm_gem_atomic_helper.h @@ -5,6 +5,7 @@ #include <linux/iosys-map.h> +#include <drm/drm_format_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_plane.h> @@ -49,6 +50,15 @@ struct drm_shadow_plane_state { /** @base: plane state */ struct drm_plane_state base; + /** + * @fmtcnv_state: Format-conversion state + * + * Per-plane state for format conversion. + * Flags for copying shadow buffers into backend storage. Also holds + * temporary storage for format conversion. + */ + struct drm_format_conv_state fmtcnv_state; + /* Transitional state - do not export or duplicate */ /** diff --git a/include/drm/drm_gpuvm.h b/include/drm/drm_gpuvm.h index bdfafc4a7705..f94fec9a8517 100644 --- a/include/drm/drm_gpuvm.h +++ b/include/drm/drm_gpuvm.h @@ -25,13 +25,17 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/dma-resv.h> #include <linux/list.h> #include <linux/rbtree.h> #include <linux/types.h> +#include <drm/drm_device.h> #include <drm/drm_gem.h> +#include <drm/drm_exec.h> struct drm_gpuvm; +struct drm_gpuvm_bo; struct drm_gpuvm_ops; /** @@ -73,6 +77,12 @@ struct drm_gpuva { struct drm_gpuvm *vm; /** + * @vm_bo: the &drm_gpuvm_bo abstraction for the mapped + * &drm_gem_object + */ + struct drm_gpuvm_bo *vm_bo; + + /** * @flags: the &drm_gpuva_flags for this mapping */ enum drm_gpuva_flags flags; @@ -107,7 +117,7 @@ struct drm_gpuva { struct drm_gem_object *obj; /** - * @entry: the &list_head to attach this object to a &drm_gem_object + * @entry: the &list_head to attach this object to a &drm_gpuvm_bo */ struct list_head entry; } gem; @@ -140,7 +150,7 @@ struct drm_gpuva { int drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va); void drm_gpuva_remove(struct drm_gpuva *va); -void drm_gpuva_link(struct drm_gpuva *va); +void drm_gpuva_link(struct drm_gpuva *va, struct drm_gpuvm_bo *vm_bo); void drm_gpuva_unlink(struct drm_gpuva *va); struct drm_gpuva *drm_gpuva_find(struct drm_gpuvm *gpuvm, @@ -184,6 +194,22 @@ static inline bool drm_gpuva_invalidated(struct drm_gpuva *va) } /** + * enum drm_gpuvm_flags - flags for struct drm_gpuvm + */ +enum drm_gpuvm_flags { + /** + * @DRM_GPUVM_RESV_PROTECTED: GPUVM is protected externally by the + * GPUVM's &dma_resv lock + */ + DRM_GPUVM_RESV_PROTECTED = BIT(0), + + /** + * @DRM_GPUVM_USERBITS: user defined bits + */ + DRM_GPUVM_USERBITS = BIT(1), +}; + +/** * struct drm_gpuvm - DRM GPU VA Manager * * The DRM GPU VA Manager keeps track of a GPU's virtual address space by using @@ -202,6 +228,16 @@ struct drm_gpuvm { const char *name; /** + * @flags: the &drm_gpuvm_flags of this GPUVM + */ + enum drm_gpuvm_flags flags; + + /** + * @drm: the &drm_device this VM lives in + */ + struct drm_device *drm; + + /** * @mm_start: start of the VA space */ u64 mm_start; @@ -227,6 +263,11 @@ struct drm_gpuvm { } rb; /** + * @kref: reference count of this object + */ + struct kref kref; + + /** * @kernel_alloc_node: * * &drm_gpuva representing the address space cutout reserved for @@ -238,16 +279,147 @@ struct drm_gpuvm { * @ops: &drm_gpuvm_ops providing the split/merge steps to drivers */ const struct drm_gpuvm_ops *ops; + + /** + * @r_obj: Resv GEM object; representing the GPUVM's common &dma_resv. + */ + struct drm_gem_object *r_obj; + + /** + * @extobj: structure holding the extobj list + */ + struct { + /** + * @list: &list_head storing &drm_gpuvm_bos serving as + * external object + */ + struct list_head list; + + /** + * @local_list: pointer to the local list temporarily storing + * entries from the external object list + */ + struct list_head *local_list; + + /** + * @lock: spinlock to protect the extobj list + */ + spinlock_t lock; + } extobj; + + /** + * @evict: structure holding the evict list and evict list lock + */ + struct { + /** + * @list: &list_head storing &drm_gpuvm_bos currently being + * evicted + */ + struct list_head list; + + /** + * @local_list: pointer to the local list temporarily storing + * entries from the evicted object list + */ + struct list_head *local_list; + + /** + * @lock: spinlock to protect the evict list + */ + spinlock_t lock; + } evict; }; void drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name, + enum drm_gpuvm_flags flags, + struct drm_device *drm, + struct drm_gem_object *r_obj, u64 start_offset, u64 range, u64 reserve_offset, u64 reserve_range, const struct drm_gpuvm_ops *ops); -void drm_gpuvm_destroy(struct drm_gpuvm *gpuvm); +/** + * drm_gpuvm_get() - acquire a struct drm_gpuvm reference + * @gpuvm: the &drm_gpuvm to acquire the reference of + * + * This function acquires an additional reference to @gpuvm. It is illegal to + * call this without already holding a reference. No locks required. + */ +static inline struct drm_gpuvm * +drm_gpuvm_get(struct drm_gpuvm *gpuvm) +{ + kref_get(&gpuvm->kref); + + return gpuvm; +} + +void drm_gpuvm_put(struct drm_gpuvm *gpuvm); + +bool drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range); bool drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range); +struct drm_gem_object * +drm_gpuvm_resv_object_alloc(struct drm_device *drm); + +/** + * drm_gpuvm_resv_protected() - indicates whether &DRM_GPUVM_RESV_PROTECTED is + * set + * @gpuvm: the &drm_gpuvm + * + * Returns: true if &DRM_GPUVM_RESV_PROTECTED is set, false otherwise. + */ +static inline bool +drm_gpuvm_resv_protected(struct drm_gpuvm *gpuvm) +{ + return gpuvm->flags & DRM_GPUVM_RESV_PROTECTED; +} + +/** + * drm_gpuvm_resv() - returns the &drm_gpuvm's &dma_resv + * @gpuvm__: the &drm_gpuvm + * + * Returns: a pointer to the &drm_gpuvm's shared &dma_resv + */ +#define drm_gpuvm_resv(gpuvm__) ((gpuvm__)->r_obj->resv) + +/** + * drm_gpuvm_resv_obj() - returns the &drm_gem_object holding the &drm_gpuvm's + * &dma_resv + * @gpuvm__: the &drm_gpuvm + * + * Returns: a pointer to the &drm_gem_object holding the &drm_gpuvm's shared + * &dma_resv + */ +#define drm_gpuvm_resv_obj(gpuvm__) ((gpuvm__)->r_obj) + +#define drm_gpuvm_resv_held(gpuvm__) \ + dma_resv_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_assert_held(gpuvm__) \ + dma_resv_assert_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_held(gpuvm__) \ + dma_resv_held(drm_gpuvm_resv(gpuvm__)) + +#define drm_gpuvm_resv_assert_held(gpuvm__) \ + dma_resv_assert_held(drm_gpuvm_resv(gpuvm__)) + +/** + * drm_gpuvm_is_extobj() - indicates whether the given &drm_gem_object is an + * external object + * @gpuvm: the &drm_gpuvm to check + * @obj: the &drm_gem_object to check + * + * Returns: true if the &drm_gem_object &dma_resv differs from the + * &drm_gpuvms &dma_resv, false otherwise + */ +static inline bool +drm_gpuvm_is_extobj(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj) +{ + return obj && obj->resv != drm_gpuvm_resv(gpuvm); +} + static inline struct drm_gpuva * __drm_gpuva_next(struct drm_gpuva *va) { @@ -327,6 +499,302 @@ __drm_gpuva_next(struct drm_gpuva *va) list_for_each_entry_safe(va__, next__, &(gpuvm__)->rb.list, rb.entry) /** + * struct drm_gpuvm_exec - &drm_gpuvm abstraction of &drm_exec + * + * This structure should be created on the stack as &drm_exec should be. + * + * Optionally, @extra can be set in order to lock additional &drm_gem_objects. + */ +struct drm_gpuvm_exec { + /** + * @exec: the &drm_exec structure + */ + struct drm_exec exec; + + /** + * @flags: the flags for the struct drm_exec + */ + uint32_t flags; + + /** + * @vm: the &drm_gpuvm to lock its DMA reservations + */ + struct drm_gpuvm *vm; + + /** + * @num_fences: the number of fences to reserve for the &dma_resv of the + * locked &drm_gem_objects + */ + unsigned int num_fences; + + /** + * @extra: Callback and corresponding private data for the driver to + * lock arbitrary additional &drm_gem_objects. + */ + struct { + /** + * @fn: The driver callback to lock additional &drm_gem_objects. + */ + int (*fn)(struct drm_gpuvm_exec *vm_exec); + + /** + * @priv: driver private data for the @fn callback + */ + void *priv; + } extra; +}; + +/** + * drm_gpuvm_prepare_vm() - prepare the GPUVMs common dma-resv + * @gpuvm: the &drm_gpuvm + * @exec: the &drm_exec context + * @num_fences: the amount of &dma_fences to reserve + * + * Calls drm_exec_prepare_obj() for the GPUVMs dummy &drm_gem_object. + * + * Using this function directly, it is the drivers responsibility to call + * drm_exec_init() and drm_exec_fini() accordingly. + * + * Returns: 0 on success, negative error code on failure. + */ +static inline int +drm_gpuvm_prepare_vm(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences) +{ + return drm_exec_prepare_obj(exec, gpuvm->r_obj, num_fences); +} + +int drm_gpuvm_prepare_objects(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + unsigned int num_fences); + +int drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + u64 addr, u64 range, + unsigned int num_fences); + +int drm_gpuvm_exec_lock(struct drm_gpuvm_exec *vm_exec); + +int drm_gpuvm_exec_lock_array(struct drm_gpuvm_exec *vm_exec, + struct drm_gem_object **objs, + unsigned int num_objs); + +int drm_gpuvm_exec_lock_range(struct drm_gpuvm_exec *vm_exec, + u64 addr, u64 range); + +/** + * drm_gpuvm_exec_unlock() - lock all dma-resv of all assoiciated BOs + * @vm_exec: the &drm_gpuvm_exec wrapper + * + * Releases all dma-resv locks of all &drm_gem_objects previously acquired + * through drm_gpuvm_exec_lock() or its variants. + * + * Returns: 0 on success, negative error code on failure. + */ +static inline void +drm_gpuvm_exec_unlock(struct drm_gpuvm_exec *vm_exec) +{ + drm_exec_fini(&vm_exec->exec); +} + +int drm_gpuvm_validate(struct drm_gpuvm *gpuvm, struct drm_exec *exec); +void drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, + struct drm_exec *exec, + struct dma_fence *fence, + enum dma_resv_usage private_usage, + enum dma_resv_usage extobj_usage); + +/** + * drm_gpuvm_exec_resv_add_fence() + * @vm_exec: the &drm_gpuvm_exec wrapper + * @fence: fence to add + * @private_usage: private dma-resv usage + * @extobj_usage: extobj dma-resv usage + * + * See drm_gpuvm_resv_add_fence(). + */ +static inline void +drm_gpuvm_exec_resv_add_fence(struct drm_gpuvm_exec *vm_exec, + struct dma_fence *fence, + enum dma_resv_usage private_usage, + enum dma_resv_usage extobj_usage) +{ + drm_gpuvm_resv_add_fence(vm_exec->vm, &vm_exec->exec, fence, + private_usage, extobj_usage); +} + +/** + * drm_gpuvm_exec_validate() + * @vm_exec: the &drm_gpuvm_exec wrapper + * + * See drm_gpuvm_validate(). + */ +static inline int +drm_gpuvm_exec_validate(struct drm_gpuvm_exec *vm_exec) +{ + return drm_gpuvm_validate(vm_exec->vm, &vm_exec->exec); +} + +/** + * struct drm_gpuvm_bo - structure representing a &drm_gpuvm and + * &drm_gem_object combination + * + * This structure is an abstraction representing a &drm_gpuvm and + * &drm_gem_object combination. It serves as an indirection to accelerate + * iterating all &drm_gpuvas within a &drm_gpuvm backed by the same + * &drm_gem_object. + * + * Furthermore it is used cache evicted GEM objects for a certain GPU-VM to + * accelerate validation. + * + * Typically, drivers want to create an instance of a struct drm_gpuvm_bo once + * a GEM object is mapped first in a GPU-VM and release the instance once the + * last mapping of the GEM object in this GPU-VM is unmapped. + */ +struct drm_gpuvm_bo { + /** + * @vm: The &drm_gpuvm the @obj is mapped in. This is a reference + * counted pointer. + */ + struct drm_gpuvm *vm; + + /** + * @obj: The &drm_gem_object being mapped in @vm. This is a reference + * counted pointer. + */ + struct drm_gem_object *obj; + + /** + * @evicted: Indicates whether the &drm_gem_object is evicted; field + * protected by the &drm_gem_object's dma-resv lock. + */ + bool evicted; + + /** + * @kref: The reference count for this &drm_gpuvm_bo. + */ + struct kref kref; + + /** + * @list: Structure containing all &list_heads. + */ + struct { + /** + * @gpuva: The list of linked &drm_gpuvas. + * + * It is safe to access entries from this list as long as the + * GEM's gpuva lock is held. See also struct drm_gem_object. + */ + struct list_head gpuva; + + /** + * @entry: Structure containing all &list_heads serving as + * entry. + */ + struct { + /** + * @gem: List entry to attach to the &drm_gem_objects + * gpuva list. + */ + struct list_head gem; + + /** + * @evict: List entry to attach to the &drm_gpuvms + * extobj list. + */ + struct list_head extobj; + + /** + * @evict: List entry to attach to the &drm_gpuvms evict + * list. + */ + struct list_head evict; + } entry; + } list; +}; + +struct drm_gpuvm_bo * +drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); + +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); +struct drm_gpuvm_bo * +drm_gpuvm_bo_obtain_prealloc(struct drm_gpuvm_bo *vm_bo); + +/** + * drm_gpuvm_bo_get() - acquire a struct drm_gpuvm_bo reference + * @vm_bo: the &drm_gpuvm_bo to acquire the reference of + * + * This function acquires an additional reference to @vm_bo. It is illegal to + * call this without already holding a reference. No locks required. + */ +static inline struct drm_gpuvm_bo * +drm_gpuvm_bo_get(struct drm_gpuvm_bo *vm_bo) +{ + kref_get(&vm_bo->kref); + return vm_bo; +} + +void drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo); + +struct drm_gpuvm_bo * +drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm, + struct drm_gem_object *obj); + +void drm_gpuvm_bo_evict(struct drm_gpuvm_bo *vm_bo, bool evict); + +/** + * drm_gpuvm_bo_gem_evict() + * @obj: the &drm_gem_object + * @evict: indicates whether @obj is evicted + * + * See drm_gpuvm_bo_evict(). + */ +static inline void +drm_gpuvm_bo_gem_evict(struct drm_gem_object *obj, bool evict) +{ + struct drm_gpuvm_bo *vm_bo; + + drm_gem_gpuva_assert_lock_held(obj); + drm_gem_for_each_gpuvm_bo(vm_bo, obj) + drm_gpuvm_bo_evict(vm_bo, evict); +} + +void drm_gpuvm_bo_extobj_add(struct drm_gpuvm_bo *vm_bo); + +/** + * drm_gpuvm_bo_for_each_va() - iterator to walk over a list of &drm_gpuva + * @va__: &drm_gpuva structure to assign to in each iteration step + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with + * + * This iterator walks over all &drm_gpuva structures associated with the + * &drm_gpuvm_bo. + * + * The caller must hold the GEM's gpuva lock. + */ +#define drm_gpuvm_bo_for_each_va(va__, vm_bo__) \ + list_for_each_entry(va__, &(vm_bo)->list.gpuva, gem.entry) + +/** + * drm_gpuvm_bo_for_each_va_safe() - iterator to safely walk over a list of + * &drm_gpuva + * @va__: &drm_gpuva structure to assign to in each iteration step + * @next__: &next &drm_gpuva to store the next step + * @vm_bo__: the &drm_gpuvm_bo the &drm_gpuva to walk are associated with + * + * This iterator walks over all &drm_gpuva structures associated with the + * &drm_gpuvm_bo. It is implemented with list_for_each_entry_safe(), hence + * it is save against removal of elements. + * + * The caller must hold the GEM's gpuva lock. + */ +#define drm_gpuvm_bo_for_each_va_safe(va__, next__, vm_bo__) \ + list_for_each_entry_safe(va__, next__, &(vm_bo)->list.gpuva, gem.entry) + +/** * enum drm_gpuva_op_type - GPU VA operation type * * Operations to alter the GPU VA mappings tracked by the &drm_gpuvm. @@ -595,8 +1063,7 @@ drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm, u64 addr, u64 range); struct drm_gpuva_ops * -drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, - struct drm_gem_object *obj); +drm_gpuvm_bo_unmap_ops_create(struct drm_gpuvm_bo *vm_bo); void drm_gpuva_ops_free(struct drm_gpuvm *gpuvm, struct drm_gpuva_ops *ops); @@ -617,6 +1084,14 @@ static inline void drm_gpuva_init_from_op(struct drm_gpuva *va, */ struct drm_gpuvm_ops { /** + * @vm_free: called when the last reference of a struct drm_gpuvm is + * dropped + * + * This callback is mandatory. + */ + void (*vm_free)(struct drm_gpuvm *gpuvm); + + /** * @op_alloc: called when the &drm_gpuvm allocates * a struct drm_gpuva_op * @@ -641,6 +1116,42 @@ struct drm_gpuvm_ops { void (*op_free)(struct drm_gpuva_op *op); /** + * @vm_bo_alloc: called when the &drm_gpuvm allocates + * a struct drm_gpuvm_bo + * + * Some drivers may want to embed struct drm_gpuvm_bo into driver + * specific structures. By implementing this callback drivers can + * allocate memory accordingly. + * + * This callback is optional. + */ + struct drm_gpuvm_bo *(*vm_bo_alloc)(void); + + /** + * @vm_bo_free: called when the &drm_gpuvm frees a + * struct drm_gpuvm_bo + * + * Some drivers may want to embed struct drm_gpuvm_bo into driver + * specific structures. By implementing this callback drivers can + * free the previously allocated memory accordingly. + * + * This callback is optional. + */ + void (*vm_bo_free)(struct drm_gpuvm_bo *vm_bo); + + /** + * @vm_bo_validate: called from drm_gpuvm_validate() + * + * Drivers receive this callback for every evicted &drm_gem_object being + * mapped in the corresponding &drm_gpuvm. + * + * Typically, drivers would call their driver specific variant of + * ttm_bo_validate() from within this callback. + */ + int (*vm_bo_validate)(struct drm_gpuvm_bo *vm_bo, + struct drm_exec *exec); + + /** * @sm_step_map: called from &drm_gpuvm_sm_map to finally insert the * mapping once all previous steps were completed * @@ -702,4 +1213,32 @@ void drm_gpuva_remap(struct drm_gpuva *prev, void drm_gpuva_unmap(struct drm_gpuva_op_unmap *op); +/** + * drm_gpuva_op_remap_to_unmap_range() - Helper to get the start and range of + * the unmap stage of a remap op. + * @op: Remap op. + * @start_addr: Output pointer for the start of the required unmap. + * @range: Output pointer for the length of the required unmap. + * + * The given start address and range will be set such that they represent the + * range of the address space that was previously covered by the mapping being + * re-mapped, but is now empty. + */ +static inline void +drm_gpuva_op_remap_to_unmap_range(const struct drm_gpuva_op_remap *op, + u64 *start_addr, u64 *range) +{ + const u64 va_start = op->prev ? + op->prev->va.addr + op->prev->va.range : + op->unmap->va->va.addr; + const u64 va_end = op->next ? + op->next->va.addr : + op->unmap->va->va.addr + op->unmap->va->va.range; + + if (start_addr) + *start_addr = va_start; + if (range) + *range = va_end - va_start; +} + #endif /* __DRM_GPUVM_H__ */ diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 816f196b3d4c..e8e0f8d39f3a 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -12,6 +12,7 @@ #include <drm/drm_device.h> #include <drm/drm_simple_kms_helper.h> +struct drm_format_conv_state; struct drm_rect; struct gpio_desc; struct iosys_map; @@ -192,7 +193,8 @@ int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, size_t len); int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, - struct drm_rect *clip, bool swap); + struct drm_rect *clip, bool swap, + struct drm_format_conv_state *fmtcnv_state); /** * mipi_dbi_command - MIPI DCS command with optional parameter(s) diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index a93a387f8a1a..dd4883df876a 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -453,7 +453,7 @@ void __drm_dev_dbg(struct _ddebug *desc, const struct device *dev, /* Helper for struct drm_device based logging. */ #define __drm_printk(drm, level, type, fmt, ...) \ - dev_##level##type((drm)->dev, "[drm] " fmt, ##__VA_ARGS__) + dev_##level##type((drm) ? (drm)->dev : NULL, "[drm] " fmt, ##__VA_ARGS__) #define drm_info(drm, fmt, ...) \ diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index d2fb81e34174..9a50348bd5c0 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -320,6 +320,7 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); * @sched: the scheduler instance on which this job is scheduled. * @s_fence: contains the fences for the scheduling of job. * @finish_cb: the callback for the finished fence. + * @credits: the number of credits this job contributes to the scheduler * @work: Helper to reschdeule job kill to different context. * @id: a unique id assigned to each job scheduled on the scheduler. * @karma: increment on every hang caused by this job. If this exceeds the hang @@ -339,6 +340,8 @@ struct drm_sched_job { struct drm_gpu_scheduler *sched; struct drm_sched_fence *s_fence; + u32 credits; + /* * work is used only after finish_cb has been used and will not be * accessed anymore. @@ -462,29 +465,42 @@ struct drm_sched_backend_ops { * and it's time to clean it up. */ void (*free_job)(struct drm_sched_job *sched_job); + + /** + * @update_job_credits: Called when the scheduler is considering this + * job for execution. + * + * This callback returns the number of credits the job would take if + * pushed to the hardware. Drivers may use this to dynamically update + * the job's credit count. For instance, deduct the number of credits + * for already signalled native fences. + * + * This callback is optional. + */ + u32 (*update_job_credits)(struct drm_sched_job *sched_job); }; /** * struct drm_gpu_scheduler - scheduler instance-specific data * * @ops: backend operations provided by the driver. - * @hw_submission_limit: the max size of the hardware queue. + * @credit_limit: the credit limit of this scheduler + * @credit_count: the current credit count of this scheduler * @timeout: the time after which a job is removed from the scheduler. * @name: name of the ring for which this scheduler is being used. * @num_rqs: Number of run-queues. This is at most DRM_SCHED_PRIORITY_COUNT, * as there's usually one run-queue per priority, but could be less. * @sched_rq: An allocated array of run-queues of size @num_rqs; - * @wake_up_worker: the wait queue on which the scheduler sleeps until a job - * is ready to be scheduled. * @job_scheduled: once @drm_sched_entity_do_release is called the scheduler * waits on this wait queue until all the scheduled jobs are * finished. - * @hw_rq_count: the number of jobs currently in the hardware queue. * @job_id_count: used to assign unique id to the each job. + * @submit_wq: workqueue used to queue @work_run_job and @work_free_job * @timeout_wq: workqueue used to queue @work_tdr + * @work_run_job: work which calls run_job op of each scheduler. + * @work_free_job: work which calls free_job op of each scheduler. * @work_tdr: schedules a delayed call to @drm_sched_job_timedout after the * timeout interval is over. - * @thread: the kthread on which the scheduler which run. * @pending_list: the list of jobs which are currently in the job queue. * @job_list_lock: lock to protect the pending_list. * @hang_limit: once the hangs by a job crosses this limit then it is marked @@ -493,24 +509,27 @@ struct drm_sched_backend_ops { * @_score: score used when the driver doesn't provide one * @ready: marks if the underlying HW is ready to work * @free_guilty: A hit to time out handler to free the guilty job. + * @pause_submit: pause queuing of @work_run_job on @submit_wq + * @own_submit_wq: scheduler owns allocation of @submit_wq * @dev: system &struct device * * One scheduler is implemented for each hardware ring. */ struct drm_gpu_scheduler { const struct drm_sched_backend_ops *ops; - uint32_t hw_submission_limit; + u32 credit_limit; + atomic_t credit_count; long timeout; const char *name; u32 num_rqs; struct drm_sched_rq **sched_rq; - wait_queue_head_t wake_up_worker; wait_queue_head_t job_scheduled; - atomic_t hw_rq_count; atomic64_t job_id_count; + struct workqueue_struct *submit_wq; struct workqueue_struct *timeout_wq; + struct work_struct work_run_job; + struct work_struct work_free_job; struct delayed_work work_tdr; - struct task_struct *thread; struct list_head pending_list; spinlock_t job_list_lock; int hang_limit; @@ -518,19 +537,22 @@ struct drm_gpu_scheduler { atomic_t _score; bool ready; bool free_guilty; + bool pause_submit; + bool own_submit_wq; struct device *dev; }; int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_backend_ops *ops, - u32 num_rqs, uint32_t hw_submission, unsigned int hang_limit, + struct workqueue_struct *submit_wq, + u32 num_rqs, u32 credit_limit, unsigned int hang_limit, long timeout, struct workqueue_struct *timeout_wq, atomic_t *score, const char *name, struct device *dev); void drm_sched_fini(struct drm_gpu_scheduler *sched); int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, - void *owner); + u32 credits, void *owner); void drm_sched_job_arm(struct drm_sched_job *job); int drm_sched_job_add_dependency(struct drm_sched_job *job, struct dma_fence *fence); @@ -550,8 +572,12 @@ void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, struct drm_gpu_scheduler **sched_list, unsigned int num_sched_list); +void drm_sched_tdr_queue_imm(struct drm_gpu_scheduler *sched); void drm_sched_job_cleanup(struct drm_sched_job *job); -void drm_sched_wakeup_if_can_queue(struct drm_gpu_scheduler *sched); +void drm_sched_wakeup(struct drm_gpu_scheduler *sched, struct drm_sched_entity *entity); +bool drm_sched_wqueue_ready(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_stop(struct drm_gpu_scheduler *sched); +void drm_sched_wqueue_start(struct drm_gpu_scheduler *sched); void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad); void drm_sched_start(struct drm_gpu_scheduler *sched, bool full_recovery); void drm_sched_resubmit_jobs(struct drm_gpu_scheduler *sched); diff --git a/include/linux/blk-pm.h b/include/linux/blk-pm.h index 2580e05a8ab6..004b38a538ff 100644 --- a/include/linux/blk-pm.h +++ b/include/linux/blk-pm.h @@ -15,7 +15,6 @@ extern int blk_pre_runtime_suspend(struct request_queue *q); extern void blk_post_runtime_suspend(struct request_queue *q, int err); extern void blk_pre_runtime_resume(struct request_queue *q); extern void blk_post_runtime_resume(struct request_queue *q); -extern void blk_set_runtime_active(struct request_queue *q); #else static inline void blk_pm_runtime_init(struct request_queue *q, struct device *dev) {} diff --git a/include/linux/bpf.h b/include/linux/bpf.h index b4825d3cdb29..6762dac3ef76 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -56,7 +56,7 @@ extern struct idr btf_idr; extern spinlock_t btf_idr_lock; extern struct kobject *btf_kobj; extern struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma; -extern bool bpf_global_ma_set, bpf_global_percpu_ma_set; +extern bool bpf_global_ma_set; typedef u64 (*bpf_callback_t)(u64, u64, u64, u64, u64); typedef int (*bpf_iter_init_seq_priv_t)(void *private_data, @@ -909,10 +909,14 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size) aux->ctx_field_size = size; } +static bool bpf_is_ldimm64(const struct bpf_insn *insn) +{ + return insn->code == (BPF_LD | BPF_IMM | BPF_DW); +} + static inline bool bpf_pseudo_func(const struct bpf_insn *insn) { - return insn->code == (BPF_LD | BPF_IMM | BPF_DW) && - insn->src_reg == BPF_PSEUDO_FUNC; + return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC; } struct bpf_prog_ops { diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 24213a99cc79..aa4d19d0bc94 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -301,6 +301,17 @@ struct bpf_func_state { struct tnum callback_ret_range; bool in_async_callback_fn; bool in_exception_callback_fn; + /* For callback calling functions that limit number of possible + * callback executions (e.g. bpf_loop) keeps track of current + * simulated iteration number. + * Value in frame N refers to number of times callback with frame + * N+1 was simulated, e.g. for the following call: + * + * bpf_loop(..., fn, ...); | suppose current frame is N + * | fn would be simulated in frame N+1 + * | number of simulations is tracked in frame N + */ + u32 callback_depth; /* The following fields should be last. See copy_func_state() */ int acquired_refs; @@ -400,6 +411,7 @@ struct bpf_verifier_state { struct bpf_idx_pair *jmp_history; u32 jmp_history_cnt; u32 dfs_depth; + u32 callback_unroll_depth; }; #define bpf_get_spilled_reg(slot, frame, mask) \ @@ -511,6 +523,10 @@ struct bpf_insn_aux_data { * this instruction, regardless of any heuristics */ bool force_checkpoint; + /* true if instruction is a call to a helper function that + * accepts callback function as a parameter. + */ + bool calls_callback; }; #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index d305db70674b..efc0c0b07efb 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -195,6 +195,7 @@ enum cpuhp_state { CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, CPUHP_AP_ARM64_ISNDEP_STARTING, CPUHP_AP_SMPCFD_DYING, + CPUHP_AP_HRTIMERS_DYING, CPUHP_AP_X86_TBOOT_DYING, CPUHP_AP_ARM_CACHE_B15_RAC_DYING, CPUHP_AP_ONLINE, diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h index 45fca09b2319..69501e0ec239 100644 --- a/include/linux/export-internal.h +++ b/include/linux/export-internal.h @@ -50,9 +50,7 @@ " .previous" "\n" \ ) -#ifdef CONFIG_IA64 -#define KSYM_FUNC(name) @fptr(name) -#elif defined(CONFIG_PARISC) && defined(CONFIG_64BIT) +#if defined(CONFIG_PARISC) && defined(CONFIG_64BIT) #define KSYM_FUNC(name) P%name #else #define KSYM_FUNC(name) name diff --git a/include/linux/hid.h b/include/linux/hid.h index 5a8387a4a712..bf43f3ff6664 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -679,6 +679,7 @@ struct hid_device { /* device report descriptor */ struct list_head debug_list; spinlock_t debug_list_lock; wait_queue_head_t debug_wait; + struct kref ref; unsigned int id; /* system unique id */ @@ -687,6 +688,8 @@ struct hid_device { /* device report descriptor */ #endif /* CONFIG_BPF */ }; +void hiddev_free(struct kref *ref); + #define to_hid_device(pdev) \ container_of(pdev, struct hid_device, dev) diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 0ee140176f10..f2044d5a652b 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -531,9 +531,9 @@ extern void sysrq_timer_list_show(void); int hrtimers_prepare_cpu(unsigned int cpu); #ifdef CONFIG_HOTPLUG_CPU -int hrtimers_dead_cpu(unsigned int cpu); +int hrtimers_cpu_dying(unsigned int cpu); #else -#define hrtimers_dead_cpu NULL +#define hrtimers_cpu_dying NULL #endif #endif diff --git a/include/linux/iosys-map.h b/include/linux/iosys-map.h index 1b06d074ade0..e3649a6563dd 100644 --- a/include/linux/iosys-map.h +++ b/include/linux/iosys-map.h @@ -168,9 +168,9 @@ struct iosys_map { * about the use of uninitialized variable. */ #define IOSYS_MAP_INIT_OFFSET(map_, offset_) ({ \ - struct iosys_map copy = *map_; \ - iosys_map_incr(©, offset_); \ - copy; \ + struct iosys_map copy_ = *map_; \ + iosys_map_incr(©_, offset_); \ + copy_; \ }) /** @@ -391,14 +391,14 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, * Returns: * The value read from the mapping. */ -#define iosys_map_rd(map__, offset__, type__) ({ \ - type__ val; \ - if ((map__)->is_iomem) { \ - __iosys_map_rd_io(val, (map__)->vaddr_iomem + (offset__), type__);\ - } else { \ - __iosys_map_rd_sys(val, (map__)->vaddr + (offset__), type__); \ - } \ - val; \ +#define iosys_map_rd(map__, offset__, type__) ({ \ + type__ val_; \ + if ((map__)->is_iomem) { \ + __iosys_map_rd_io(val_, (map__)->vaddr_iomem + (offset__), type__); \ + } else { \ + __iosys_map_rd_sys(val_, (map__)->vaddr + (offset__), type__); \ + } \ + val_; \ }) /** @@ -413,13 +413,13 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, * or if pointer may be unaligned (and problematic for the architecture * supported), use iosys_map_memcpy_to() */ -#define iosys_map_wr(map__, offset__, type__, val__) ({ \ - type__ val = (val__); \ - if ((map__)->is_iomem) { \ - __iosys_map_wr_io(val, (map__)->vaddr_iomem + (offset__), type__);\ - } else { \ - __iosys_map_wr_sys(val, (map__)->vaddr + (offset__), type__); \ - } \ +#define iosys_map_wr(map__, offset__, type__, val__) ({ \ + type__ val_ = (val__); \ + if ((map__)->is_iomem) { \ + __iosys_map_wr_io(val_, (map__)->vaddr_iomem + (offset__), type__); \ + } else { \ + __iosys_map_wr_sys(val_, (map__)->vaddr + (offset__), type__); \ + } \ }) /** @@ -485,9 +485,9 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, * The value read from the mapping. */ #define iosys_map_rd_field(map__, struct_offset__, struct_type__, field__) ({ \ - struct_type__ *s; \ + struct_type__ *s_; \ iosys_map_rd(map__, struct_offset__ + offsetof(struct_type__, field__), \ - typeof(s->field__)); \ + typeof(s_->field__)); \ }) /** @@ -508,9 +508,9 @@ static inline void iosys_map_memset(struct iosys_map *dst, size_t offset, * usage and memory layout. */ #define iosys_map_wr_field(map__, struct_offset__, struct_type__, field__, val__) ({ \ - struct_type__ *s; \ + struct_type__ *s_; \ iosys_map_wr(map__, struct_offset__ + offsetof(struct_type__, field__), \ - typeof(s->field__), val__); \ + typeof(s_->field__), val__); \ }) #endif /* __IOSYS_MAP_H__ */ diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 8fa23bdcedbf..007fd9c3e4b6 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -420,7 +420,7 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising) * A function that translates value of following registers to the linkmode: * IEEE 802.3-2018 45.2.3.10 "EEE control and capability 1" register (3.20) * IEEE 802.3-2018 45.2.7.13 "EEE advertisement 1" register (7.60) - * IEEE 802.3-2018 45.2.7.14 "EEE "link partner ability 1 register (7.61) + * IEEE 802.3-2018 45.2.7.14 "EEE link partner ability 1" register (7.61) */ static inline void mii_eee_cap1_mod_linkmode_t(unsigned long *adv, u32 val) { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a16c9cc063fe..2564e209465e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1797,6 +1797,13 @@ enum netdev_ml_priv_type { ML_PRIV_CAN, }; +enum netdev_stat_type { + NETDEV_PCPU_STAT_NONE, + NETDEV_PCPU_STAT_LSTATS, /* struct pcpu_lstats */ + NETDEV_PCPU_STAT_TSTATS, /* struct pcpu_sw_netstats */ + NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */ +}; + /** * struct net_device - The DEVICE structure. * @@ -1991,10 +1998,14 @@ enum netdev_ml_priv_type { * * @ml_priv: Mid-layer private * @ml_priv_type: Mid-layer private type - * @lstats: Loopback statistics - * @tstats: Tunnel statistics - * @dstats: Dummy statistics - * @vstats: Virtual ethernet statistics + * + * @pcpu_stat_type: Type of device statistics which the core should + * allocate/free: none, lstats, tstats, dstats. none + * means the driver is handling statistics allocation/ + * freeing internally. + * @lstats: Loopback statistics: packets, bytes + * @tstats: Tunnel statistics: RX/TX packets, RX/TX bytes + * @dstats: Dummy statistics: RX/TX/drop packets, RX/TX bytes * * @garp_port: GARP * @mrp_port: MRP @@ -2354,6 +2365,7 @@ struct net_device { void *ml_priv; enum netdev_ml_priv_type ml_priv_type; + enum netdev_stat_type pcpu_stat_type:8; union { struct pcpu_lstats __percpu *lstats; struct pcpu_sw_netstats __percpu *tstats; @@ -2755,6 +2767,16 @@ struct pcpu_sw_netstats { struct u64_stats_sync syncp; } __aligned(4 * sizeof(u64)); +struct pcpu_dstats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_drops; + u64 tx_packets; + u64 tx_bytes; + u64 tx_drops; + struct u64_stats_sync syncp; +} __aligned(8 * sizeof(u64)); + struct pcpu_lstats { u64_stats_t packets; u64_stats_t bytes; diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index bcc1ea44b4e8..06142ff7f9ce 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -204,6 +204,8 @@ enum mapping_flags { AS_NO_WRITEBACK_TAGS = 5, AS_LARGE_FOLIO_SUPPORT = 6, AS_RELEASE_ALWAYS, /* Call ->release_folio(), even if no private data */ + AS_STABLE_WRITES, /* must wait for writeback before modifying + folio contents */ }; /** @@ -289,6 +291,21 @@ static inline void mapping_clear_release_always(struct address_space *mapping) clear_bit(AS_RELEASE_ALWAYS, &mapping->flags); } +static inline bool mapping_stable_writes(const struct address_space *mapping) +{ + return test_bit(AS_STABLE_WRITES, &mapping->flags); +} + +static inline void mapping_set_stable_writes(struct address_space *mapping) +{ + set_bit(AS_STABLE_WRITES, &mapping->flags); +} + +static inline void mapping_clear_stable_writes(struct address_space *mapping) +{ + clear_bit(AS_STABLE_WRITES, &mapping->flags); +} + static inline gfp_t mapping_gfp_mask(struct address_space * mapping) { return mapping->gfp_mask; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index afb028c54f33..5547ba68e6e4 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -843,11 +843,11 @@ struct perf_event { }; /* - * ,-----------------------[1:n]----------------------. - * V V - * perf_event_context <-[1:n]-> perf_event_pmu_context <--- perf_event - * ^ ^ | | - * `--------[1:n]---------' `-[n:1]-> pmu <-[1:n]-' + * ,-----------------------[1:n]------------------------. + * V V + * perf_event_context <-[1:n]-> perf_event_pmu_context <-[1:n]- perf_event + * | | + * `--[n:1]-> pmu <-[1:n]--' * * * struct perf_event_pmu_context lifetime is refcount based and RCU freed @@ -865,6 +865,9 @@ struct perf_event { * ctx->mutex pinning the configuration. Since we hold a reference on * group_leader (through the filedesc) it can't go away, therefore it's * associated pmu_ctx must exist and cannot change due to ctx->mutex. + * + * perf_event holds a refcount on perf_event_context + * perf_event holds a refcount on perf_event_pmu_context */ struct perf_event_pmu_context { struct pmu *pmu; diff --git a/include/linux/sizes.h b/include/linux/sizes.h index 84aa448d8bb3..c3a00b967d18 100644 --- a/include/linux/sizes.h +++ b/include/linux/sizes.h @@ -47,8 +47,17 @@ #define SZ_8G _AC(0x200000000, ULL) #define SZ_16G _AC(0x400000000, ULL) #define SZ_32G _AC(0x800000000, ULL) +#define SZ_64G _AC(0x1000000000, ULL) +#define SZ_128G _AC(0x2000000000, ULL) +#define SZ_256G _AC(0x4000000000, ULL) +#define SZ_512G _AC(0x8000000000, ULL) #define SZ_1T _AC(0x10000000000, ULL) +#define SZ_2T _AC(0x20000000000, ULL) +#define SZ_4T _AC(0x40000000000, ULL) +#define SZ_8T _AC(0x80000000000, ULL) +#define SZ_16T _AC(0x100000000000, ULL) +#define SZ_32T _AC(0x200000000000, ULL) #define SZ_64T _AC(0x400000000000, ULL) #endif /* __LINUX_SIZES_H__ */ diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h index c36e7a3b45e7..3be2cb564710 100644 --- a/include/linux/stackleak.h +++ b/include/linux/stackleak.h @@ -14,6 +14,7 @@ #ifdef CONFIG_GCC_PLUGIN_STACKLEAK #include <asm/stacktrace.h> +#include <linux/linkage.h> /* * The lowest address on tsk's stack which we can plausibly erase. @@ -76,6 +77,11 @@ static inline void stackleak_task_init(struct task_struct *t) # endif } +asmlinkage void noinstr stackleak_erase(void); +asmlinkage void noinstr stackleak_erase_on_task_stack(void); +asmlinkage void noinstr stackleak_erase_off_task_stack(void); +void __no_caller_saved_registers noinstr stackleak_track_stack(void); + #else /* !CONFIG_GCC_PLUGIN_STACKLEAK */ static inline void stackleak_task_init(struct task_struct *t) { } #endif diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index b513749582d7..e4de6bc1f69b 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -144,10 +144,6 @@ struct usb_phy { */ int (*set_wakeup)(struct usb_phy *x, bool enabled); - /* notify phy port status change */ - int (*notify_port_status)(struct usb_phy *x, int port, - u16 portstatus, u16 portchange); - /* notify phy connect status change */ int (*notify_connect)(struct usb_phy *x, enum usb_device_speed speed); @@ -321,15 +317,6 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled) } static inline int -usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus, u16 portchange) -{ - if (x && x->notify_port_status) - return x->notify_port_status(x, port, portstatus, portchange); - else - return 0; -} - -static inline int usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed) { if (x && x->notify_connect) diff --git a/include/linux/virtio_pci_modern.h b/include/linux/virtio_pci_modern.h index d0f2797420f7..a09e13a577a9 100644 --- a/include/linux/virtio_pci_modern.h +++ b/include/linux/virtio_pci_modern.h @@ -5,13 +5,6 @@ #include <linux/pci.h> #include <linux/virtio_pci.h> -struct virtio_pci_modern_common_cfg { - struct virtio_pci_common_cfg cfg; - - __le16 queue_notify_data; /* read-write */ - __le16 queue_reset; /* read-write */ -}; - /** * struct virtio_pci_modern_device - info for modern PCI virtio * @pci_dev: Ptr to the PCI device struct diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 3bbd13ab1ecf..b157c5cafd14 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -178,9 +178,9 @@ static inline __be32 nft_reg_load_be32(const u32 *sreg) return *(__force __be32 *)sreg; } -static inline void nft_reg_store64(u32 *dreg, u64 val) +static inline void nft_reg_store64(u64 *dreg, u64 val) { - put_unaligned(val, (u64 *)dreg); + put_unaligned(val, dreg); } static inline u64 nft_reg_load64(const u32 *sreg) diff --git a/include/net/netkit.h b/include/net/netkit.h index 0ba2e6b847ca..9ec0163739f4 100644 --- a/include/net/netkit.h +++ b/include/net/netkit.h @@ -10,6 +10,7 @@ int netkit_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog); int netkit_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); int netkit_prog_detach(const union bpf_attr *attr, struct bpf_prog *prog); int netkit_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr); +INDIRECT_CALLABLE_DECLARE(struct net_device *netkit_peer_dev(struct net_device *dev)); #else static inline int netkit_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog) @@ -34,5 +35,10 @@ static inline int netkit_prog_query(const union bpf_attr *attr, { return -EINVAL; } + +static inline struct net_device *netkit_peer_dev(struct net_device *dev) +{ + return NULL; +} #endif /* CONFIG_NETKIT */ #endif /* __NET_NETKIT_H */ diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index 8a6dbfb23336..77f87c622a2e 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -58,6 +58,11 @@ static inline struct nf_flowtable *tcf_ct_ft(const struct tc_action *a) return to_ct_params(a)->nf_ft; } +static inline struct nf_conntrack_helper *tcf_ct_helper(const struct tc_action *a) +{ + return to_ct_params(a)->helper; +} + #else static inline uint16_t tcf_ct_zone(const struct tc_action *a) { return 0; } static inline int tcf_ct_action(const struct tc_action *a) { return 0; } @@ -65,6 +70,10 @@ static inline struct nf_flowtable *tcf_ct_ft(const struct tc_action *a) { return NULL; } +static inline struct nf_conntrack_helper *tcf_ct_helper(const struct tc_action *a) +{ + return NULL; +} #endif /* CONFIG_NF_CONNTRACK */ #if IS_ENABLED(CONFIG_NET_ACT_CT) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 4c53a5ef6257..f7e537f64db4 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -328,7 +328,7 @@ E_(rxrpc_rtt_tx_ping, "PING") #define rxrpc_rtt_rx_traces \ - EM(rxrpc_rtt_rx_cancel, "CNCL") \ + EM(rxrpc_rtt_rx_other_ack, "OACK") \ EM(rxrpc_rtt_rx_obsolete, "OBSL") \ EM(rxrpc_rtt_rx_lost, "LOST") \ EM(rxrpc_rtt_rx_ping_response, "PONG") \ diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index de723566c5ae..8662b5aeea0c 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -1218,6 +1218,26 @@ extern "C" { #define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd) +/** + * DRM_IOCTL_MODE_CLOSEFB - Close a framebuffer. + * + * This closes a framebuffer previously added via ADDFB/ADDFB2. The IOCTL + * argument is a framebuffer object ID. + * + * This IOCTL is similar to &DRM_IOCTL_MODE_RMFB, except it doesn't disable + * planes and CRTCs. As long as the framebuffer is used by a plane, it's kept + * alive. When the plane no longer uses the framebuffer (because the + * framebuffer is replaced with another one, or the plane is disabled), the + * framebuffer is cleaned up. + * + * This is useful to implement flicker-free transitions between two processes. + * + * Depending on the threat model, user-space may want to ensure that the + * framebuffer doesn't expose any sensitive user information: closed + * framebuffers attached to a plane can be read back by the next DRM master. + */ +#define DRM_IOCTL_MODE_CLOSEFB DRM_IOWR(0xD0, struct drm_mode_closefb) + /* * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x9f. diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 128d09138ceb..09e7a471ee30 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -1323,6 +1323,16 @@ struct drm_mode_rect { __s32 y2; }; +/** + * struct drm_mode_closefb + * @fb_id: Framebuffer ID. + * @pad: Must be zero. + */ +struct drm_mode_closefb { + __u32 fb_id; + __u32 pad; +}; + #if defined(__cplusplus) } #endif diff --git a/include/uapi/drm/ivpu_accel.h b/include/uapi/drm/ivpu_accel.h index 262db0c3beee..de1944e42c65 100644 --- a/include/uapi/drm/ivpu_accel.h +++ b/include/uapi/drm/ivpu_accel.h @@ -196,7 +196,7 @@ struct drm_ivpu_bo_create { * * %DRM_IVPU_BO_UNCACHED: * - * Allocated BO will not be cached on host side nor snooped on the VPU side. + * Not supported. Use DRM_IVPU_BO_WC instead. * * %DRM_IVPU_BO_WC: * diff --git a/include/uapi/drm/pvr_drm.h b/include/uapi/drm/pvr_drm.h new file mode 100644 index 000000000000..1834375390c4 --- /dev/null +++ b/include/uapi/drm/pvr_drm.h @@ -0,0 +1,1297 @@ +/* SPDX-License-Identifier: (GPL-2.0-only WITH Linux-syscall-note) OR MIT */ +/* Copyright (c) 2023 Imagination Technologies Ltd. */ + +#ifndef PVR_DRM_UAPI_H +#define PVR_DRM_UAPI_H + +#include "drm.h" + +#include <linux/const.h> +#include <linux/types.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * DOC: PowerVR UAPI + * + * The PowerVR IOCTL argument structs have a few limitations in place, in + * addition to the standard kernel restrictions: + * + * - All members must be type-aligned. + * - The overall struct must be padded to 64-bit alignment. + * - Explicit padding is almost always required. This takes the form of + * ``_padding_[x]`` members of sufficient size to pad to the next power-of-two + * alignment, where [x] is the offset into the struct in hexadecimal. Arrays + * are never used for alignment. Padding fields must be zeroed; this is + * always checked. + * - Unions may only appear as the last member of a struct. + * - Individual union members may grow in the future. The space between the + * end of a union member and the end of its containing union is considered + * "implicit padding" and must be zeroed. This is always checked. + * + * In addition to the IOCTL argument structs, the PowerVR UAPI makes use of + * DEV_QUERY argument structs. These are used to fetch information about the + * device and runtime. These structs are subject to the same rules set out + * above. + */ + +/** + * struct drm_pvr_obj_array - Container used to pass arrays of objects + * + * It is not unusual to have to extend objects to pass new parameters, and the DRM + * ioctl infrastructure is supporting that by padding ioctl arguments with zeros + * when the data passed by userspace is smaller than the struct defined in the + * drm_ioctl_desc, thus keeping things backward compatible. This type is just + * applying the same concepts to indirect objects passed through arrays referenced + * from the main ioctl arguments structure: the stride basically defines the size + * of the object passed by userspace, which allows the kernel driver to pad with + * zeros when it's smaller than the size of the object it expects. + * + * Use ``DRM_PVR_OBJ_ARRAY()`` to fill object array fields, unless you + * have a very good reason not to. + */ +struct drm_pvr_obj_array { + /** @stride: Stride of object struct. Used for versioning. */ + __u32 stride; + + /** @count: Number of objects in the array. */ + __u32 count; + + /** @array: User pointer to an array of objects. */ + __u64 array; +}; + +/** + * DRM_PVR_OBJ_ARRAY() - Helper macro for filling &struct drm_pvr_obj_array. + * @cnt: Number of elements pointed to py @ptr. + * @ptr: Pointer to start of a C array. + * + * Return: Literal of type &struct drm_pvr_obj_array. + */ +#define DRM_PVR_OBJ_ARRAY(cnt, ptr) \ + { .stride = sizeof((ptr)[0]), .count = (cnt), .array = (__u64)(uintptr_t)(ptr) } + +/** + * DOC: PowerVR IOCTL interface + */ + +/** + * PVR_IOCTL() - Build a PowerVR IOCTL number + * @_ioctl: An incrementing id for this IOCTL. Added to %DRM_COMMAND_BASE. + * @_mode: Must be one of %DRM_IOR, %DRM_IOW or %DRM_IOWR. + * @_data: The type of the args struct passed by this IOCTL. + * + * The struct referred to by @_data must have a ``drm_pvr_ioctl_`` prefix and an + * ``_args suffix``. They are therefore omitted from @_data. + * + * This should only be used to build the constants described below; it should + * never be used to call an IOCTL directly. + * + * Return: An IOCTL number to be passed to ioctl() from userspace. + */ +#define PVR_IOCTL(_ioctl, _mode, _data) \ + _mode(DRM_COMMAND_BASE + (_ioctl), struct drm_pvr_ioctl_##_data##_args) + +#define DRM_IOCTL_PVR_DEV_QUERY PVR_IOCTL(0x00, DRM_IOWR, dev_query) +#define DRM_IOCTL_PVR_CREATE_BO PVR_IOCTL(0x01, DRM_IOWR, create_bo) +#define DRM_IOCTL_PVR_GET_BO_MMAP_OFFSET PVR_IOCTL(0x02, DRM_IOWR, get_bo_mmap_offset) +#define DRM_IOCTL_PVR_CREATE_VM_CONTEXT PVR_IOCTL(0x03, DRM_IOWR, create_vm_context) +#define DRM_IOCTL_PVR_DESTROY_VM_CONTEXT PVR_IOCTL(0x04, DRM_IOW, destroy_vm_context) +#define DRM_IOCTL_PVR_VM_MAP PVR_IOCTL(0x05, DRM_IOW, vm_map) +#define DRM_IOCTL_PVR_VM_UNMAP PVR_IOCTL(0x06, DRM_IOW, vm_unmap) +#define DRM_IOCTL_PVR_CREATE_CONTEXT PVR_IOCTL(0x07, DRM_IOWR, create_context) +#define DRM_IOCTL_PVR_DESTROY_CONTEXT PVR_IOCTL(0x08, DRM_IOW, destroy_context) +#define DRM_IOCTL_PVR_CREATE_FREE_LIST PVR_IOCTL(0x09, DRM_IOWR, create_free_list) +#define DRM_IOCTL_PVR_DESTROY_FREE_LIST PVR_IOCTL(0x0a, DRM_IOW, destroy_free_list) +#define DRM_IOCTL_PVR_CREATE_HWRT_DATASET PVR_IOCTL(0x0b, DRM_IOWR, create_hwrt_dataset) +#define DRM_IOCTL_PVR_DESTROY_HWRT_DATASET PVR_IOCTL(0x0c, DRM_IOW, destroy_hwrt_dataset) +#define DRM_IOCTL_PVR_SUBMIT_JOBS PVR_IOCTL(0x0d, DRM_IOW, submit_jobs) + +/** + * DOC: PowerVR IOCTL DEV_QUERY interface + */ + +/** + * struct drm_pvr_dev_query_gpu_info - Container used to fetch information about + * the graphics processor. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_QUERY_GPU_INFO_GET. + */ +struct drm_pvr_dev_query_gpu_info { + /** + * @gpu_id: GPU identifier. + * + * For all currently supported GPUs this is the BVNC encoded as a 64-bit + * value as follows: + * + * +--------+--------+--------+-------+ + * | 63..48 | 47..32 | 31..16 | 15..0 | + * +========+========+========+=======+ + * | B | V | N | C | + * +--------+--------+--------+-------+ + */ + __u64 gpu_id; + + /** + * @num_phantoms: Number of Phantoms present. + */ + __u32 num_phantoms; + + /** @_padding_c: Reserved. This field must be zeroed. */ + __u32 _padding_c; +}; + +/** + * struct drm_pvr_dev_query_runtime_info - Container used to fetch information + * about the graphics runtime. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_QUERY_RUNTIME_INFO_GET. + */ +struct drm_pvr_dev_query_runtime_info { + /** + * @free_list_min_pages: Minimum allowed free list size, + * in PM physical pages. + */ + __u64 free_list_min_pages; + + /** + * @free_list_max_pages: Maximum allowed free list size, + * in PM physical pages. + */ + __u64 free_list_max_pages; + + /** + * @common_store_alloc_region_size: Size of the Allocation + * Region within the Common Store used for coefficient and shared + * registers, in dwords. + */ + __u32 common_store_alloc_region_size; + + /** + * @common_store_partition_space_size: Size of the + * Partition Space within the Common Store for output buffers, in + * dwords. + */ + __u32 common_store_partition_space_size; + + /** + * @max_coeffs: Maximum coefficients, in dwords. + */ + __u32 max_coeffs; + + /** + * @cdm_max_local_mem_size_regs: Maximum amount of local + * memory available to a compute kernel, in dwords. + */ + __u32 cdm_max_local_mem_size_regs; +}; + +/** + * struct drm_pvr_dev_query_quirks - Container used to fetch information about + * hardware fixes for which the device may require support in the user mode + * driver. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_QUERY_QUIRKS_GET. + */ +struct drm_pvr_dev_query_quirks { + /** + * @quirks: A userspace address for the hardware quirks __u32 array. + * + * The first @musthave_count items in the list are quirks that the + * client must support for this device. If userspace does not support + * all these quirks then functionality is not guaranteed and client + * initialisation must fail. + * The remaining quirks in the list affect userspace and the kernel or + * firmware. They are disabled by default and require userspace to + * opt-in. The opt-in mechanism depends on the quirk. + */ + __u64 quirks; + + /** @count: Length of @quirks (number of __u32). */ + __u16 count; + + /** + * @musthave_count: The number of entries in @quirks that are + * mandatory, starting at index 0. + */ + __u16 musthave_count; + + /** @_padding_c: Reserved. This field must be zeroed. */ + __u32 _padding_c; +}; + +/** + * struct drm_pvr_dev_query_enhancements - Container used to fetch information + * about optional enhancements supported by the device that require support in + * the user mode driver. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_ENHANCEMENTS_GET. + */ +struct drm_pvr_dev_query_enhancements { + /** + * @enhancements: A userspace address for the hardware enhancements + * __u32 array. + * + * These enhancements affect userspace and the kernel or firmware. They + * are disabled by default and require userspace to opt-in. The opt-in + * mechanism depends on the enhancement. + */ + __u64 enhancements; + + /** @count: Length of @enhancements (number of __u32). */ + __u16 count; + + /** @_padding_a: Reserved. This field must be zeroed. */ + __u16 _padding_a; + + /** @_padding_c: Reserved. This field must be zeroed. */ + __u32 _padding_c; +}; + +/** + * enum drm_pvr_heap_id - Array index for heap info data returned by + * %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + * + * For compatibility reasons all indices will be present in the returned array, + * however some heaps may not be present. These are indicated where + * &struct drm_pvr_heap.size is set to zero. + */ +enum drm_pvr_heap_id { + /** @DRM_PVR_HEAP_GENERAL: General purpose heap. */ + DRM_PVR_HEAP_GENERAL = 0, + /** @DRM_PVR_HEAP_PDS_CODE_DATA: PDS code and data heap. */ + DRM_PVR_HEAP_PDS_CODE_DATA, + /** @DRM_PVR_HEAP_USC_CODE: USC code heap. */ + DRM_PVR_HEAP_USC_CODE, + /** @DRM_PVR_HEAP_RGNHDR: Region header heap. Only used if GPU has BRN63142. */ + DRM_PVR_HEAP_RGNHDR, + /** @DRM_PVR_HEAP_VIS_TEST: Visibility test heap. */ + DRM_PVR_HEAP_VIS_TEST, + /** @DRM_PVR_HEAP_TRANSFER_FRAG: Transfer fragment heap. */ + DRM_PVR_HEAP_TRANSFER_FRAG, + + /** + * @DRM_PVR_HEAP_COUNT: The number of heaps returned by + * %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + * + * More heaps may be added, so this also serves as the copy limit when + * sent by the caller. + */ + DRM_PVR_HEAP_COUNT + /* Please only add additional heaps above DRM_PVR_HEAP_COUNT! */ +}; + +/** + * struct drm_pvr_heap - Container holding information about a single heap. + * + * This will always be fetched as an array. + */ +struct drm_pvr_heap { + /** @base: Base address of heap. */ + __u64 base; + + /** @size: Size of heap, in bytes. Will be 0 if the heap is not present. */ + __u64 size; + + /** @flags: Flags for this heap. Currently always 0. */ + __u32 flags; + + /** @page_size_log2: Log2 of page size. */ + __u32 page_size_log2; +}; + +/** + * struct drm_pvr_dev_query_heap_info - Container used to fetch information + * about heaps supported by the device driver. + * + * Please note all driver-supported heaps will be returned up to &heaps.count. + * Some heaps will not be present in all devices, which will be indicated by + * &struct drm_pvr_heap.size being set to zero. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + */ +struct drm_pvr_dev_query_heap_info { + /** + * @heaps: Array of &struct drm_pvr_heap. If pointer is NULL, the count + * and stride will be updated with those known to the driver version, to + * facilitate allocation by the caller. + */ + struct drm_pvr_obj_array heaps; +}; + +/** + * enum drm_pvr_static_data_area_usage - Array index for static data area info + * returned by %DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET. + * + * For compatibility reasons all indices will be present in the returned array, + * however some areas may not be present. These are indicated where + * &struct drm_pvr_static_data_area.size is set to zero. + */ +enum drm_pvr_static_data_area_usage { + /** + * @DRM_PVR_STATIC_DATA_AREA_EOT: End of Tile PDS program code segment. + * + * The End of Tile PDS task runs at completion of a tile during a fragment job, and is + * responsible for emitting the tile to the Pixel Back End. + */ + DRM_PVR_STATIC_DATA_AREA_EOT = 0, + + /** + * @DRM_PVR_STATIC_DATA_AREA_FENCE: MCU fence area, used during cache flush and + * invalidation. + * + * This must point to valid physical memory but the contents otherwise are not used. + */ + DRM_PVR_STATIC_DATA_AREA_FENCE, + + /** + * @DRM_PVR_STATIC_DATA_AREA_VDM_SYNC: VDM sync program. + * + * The VDM sync program is used to synchronise multiple areas of the GPU hardware. + */ + DRM_PVR_STATIC_DATA_AREA_VDM_SYNC, + + /** + * @DRM_PVR_STATIC_DATA_AREA_YUV_CSC: YUV coefficients. + * + * Area contains up to 16 slots with stride of 64 bytes. Each is a 3x4 matrix of u16 fixed + * point numbers, with 1 sign bit, 2 integer bits and 13 fractional bits. + * + * The slots are : + * 0 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR + * 1 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR (full range) + * 2 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR (conformant range) + * 3 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR (full range) + * 4 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR (conformant range) + * 5 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR (full range) + * 6 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR (conformant range) + * 7 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR (full range) + * 8 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR (conformant range) + * 9 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR (conformant range, 10 bit) + * 10 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR (conformant range, 10 bit) + * 11 = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR (conformant range, 10 bit) + * 14 = Identity (biased) + * 15 = Identity + */ + DRM_PVR_STATIC_DATA_AREA_YUV_CSC, +}; + +/** + * struct drm_pvr_static_data_area - Container holding information about a + * single static data area. + * + * This will always be fetched as an array. + */ +struct drm_pvr_static_data_area { + /** + * @area_usage: Usage of static data area. + * See &enum drm_pvr_static_data_area_usage. + */ + __u16 area_usage; + + /** + * @location_heap_id: Array index of heap where this of static data + * area is located. This array is fetched using + * %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + */ + __u16 location_heap_id; + + /** @size: Size of static data area. Not present if set to zero. */ + __u32 size; + + /** @offset: Offset of static data area from start of heap. */ + __u64 offset; +}; + +/** + * struct drm_pvr_dev_query_static_data_areas - Container used to fetch + * information about the static data areas in heaps supported by the device + * driver. + * + * Please note all driver-supported static data areas will be returned up to + * &static_data_areas.count. Some will not be present for all devices which, + * will be indicated by &struct drm_pvr_static_data_area.size being set to zero. + * + * Further, some heaps will not be present either. See &struct + * drm_pvr_dev_query_heap_info. + * + * When fetching this type &struct drm_pvr_ioctl_dev_query_args.type must be set + * to %DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET. + */ +struct drm_pvr_dev_query_static_data_areas { + /** + * @static_data_areas: Array of &struct drm_pvr_static_data_area. If + * pointer is NULL, the count and stride will be updated with those + * known to the driver version, to facilitate allocation by the caller. + */ + struct drm_pvr_obj_array static_data_areas; +}; + +/** + * enum drm_pvr_dev_query - For use with &drm_pvr_ioctl_dev_query_args.type to + * indicate the type of the receiving container. + * + * Append only. Do not reorder. + */ +enum drm_pvr_dev_query { + /** + * @DRM_PVR_DEV_QUERY_GPU_INFO_GET: The dev query args contain a pointer + * to &struct drm_pvr_dev_query_gpu_info. + */ + DRM_PVR_DEV_QUERY_GPU_INFO_GET = 0, + + /** + * @DRM_PVR_DEV_QUERY_RUNTIME_INFO_GET: The dev query args contain a + * pointer to &struct drm_pvr_dev_query_runtime_info. + */ + DRM_PVR_DEV_QUERY_RUNTIME_INFO_GET, + + /** + * @DRM_PVR_DEV_QUERY_QUIRKS_GET: The dev query args contain a pointer + * to &struct drm_pvr_dev_query_quirks. + */ + DRM_PVR_DEV_QUERY_QUIRKS_GET, + + /** + * @DRM_PVR_DEV_QUERY_ENHANCEMENTS_GET: The dev query args contain a + * pointer to &struct drm_pvr_dev_query_enhancements. + */ + DRM_PVR_DEV_QUERY_ENHANCEMENTS_GET, + + /** + * @DRM_PVR_DEV_QUERY_HEAP_INFO_GET: The dev query args contain a + * pointer to &struct drm_pvr_dev_query_heap_info. + */ + DRM_PVR_DEV_QUERY_HEAP_INFO_GET, + + /** + * @DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET: The dev query args contain + * a pointer to &struct drm_pvr_dev_query_static_data_areas. + */ + DRM_PVR_DEV_QUERY_STATIC_DATA_AREAS_GET, +}; + +/** + * struct drm_pvr_ioctl_dev_query_args - Arguments for %DRM_IOCTL_PVR_DEV_QUERY. + */ +struct drm_pvr_ioctl_dev_query_args { + /** + * @type: Type of query and output struct. See &enum drm_pvr_dev_query. + */ + __u32 type; + + /** + * @size: Size of the receiving struct, see @type. + * + * After a successful call this will be updated to the written byte + * length. + * Can also be used to get the minimum byte length (see @pointer). + * This allows additional fields to be appended to the structs in + * future. + */ + __u32 size; + + /** + * @pointer: Pointer to struct @type. + * + * Must be large enough to contain @size bytes. + * If pointer is NULL, the expected size will be returned in the @size + * field, but no other data will be written. + */ + __u64 pointer; +}; + +/** + * DOC: PowerVR IOCTL CREATE_BO interface + */ + +/** + * DOC: Flags for CREATE_BO + * + * We use "device" to refer to the GPU here because of the ambiguity between CPU and GPU in some + * fonts. + * + * Device mapping options + * :DRM_PVR_BO_BYPASS_DEVICE_CACHE: Specify that device accesses to this memory will bypass the + * cache. This is used for buffers that will either be regularly updated by the CPU (eg free + * lists) or will be accessed only once and therefore isn't worth caching (eg partial render + * buffers). + * By default, the device flushes its memory caches after every job, so this is not normally + * required for coherency. + * :DRM_PVR_BO_PM_FW_PROTECT: Specify that only the Parameter Manager (PM) and/or firmware + * processor should be allowed to access this memory when mapped to the device. It is not + * valid to specify this flag with DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS. + * + * CPU mapping options + * :DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS: Allow userspace to map and access the contents of this + * memory. It is not valid to specify this flag with DRM_PVR_BO_PM_FW_PROTECT. + */ +#define DRM_PVR_BO_BYPASS_DEVICE_CACHE _BITULL(0) +#define DRM_PVR_BO_PM_FW_PROTECT _BITULL(1) +#define DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS _BITULL(2) +/* Bits 3..63 are reserved. */ + +#define DRM_PVR_BO_FLAGS_MASK (DRM_PVR_BO_BYPASS_DEVICE_CACHE | DRM_PVR_BO_PM_FW_PROTECT | \ + DRM_PVR_BO_ALLOW_CPU_USERSPACE_ACCESS) + +/** + * struct drm_pvr_ioctl_create_bo_args - Arguments for %DRM_IOCTL_PVR_CREATE_BO + */ +struct drm_pvr_ioctl_create_bo_args { + /** + * @size: [IN] Size of buffer object to create. This must be page size + * aligned. + */ + __u64 size; + + /** + * @handle: [OUT] GEM handle of the new buffer object for use in + * userspace. + */ + __u32 handle; + + /** @_padding_c: Reserved. This field must be zeroed. */ + __u32 _padding_c; + + /** + * @flags: [IN] Options which will affect the behaviour of this + * creation operation and future mapping operations on the created + * object. This field must be a valid combination of ``DRM_PVR_BO_*`` + * values, with all bits marked as reserved set to zero. + */ + __u64 flags; +}; + +/** + * DOC: PowerVR IOCTL GET_BO_MMAP_OFFSET interface + */ + +/** + * struct drm_pvr_ioctl_get_bo_mmap_offset_args - Arguments for + * %DRM_IOCTL_PVR_GET_BO_MMAP_OFFSET + * + * Like other DRM drivers, the "mmap" IOCTL doesn't actually map any memory. + * Instead, it allocates a fake offset which refers to the specified buffer + * object. This offset can be used with a real mmap call on the DRM device + * itself. + */ +struct drm_pvr_ioctl_get_bo_mmap_offset_args { + /** @handle: [IN] GEM handle of the buffer object to be mapped. */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; + + /** @offset: [OUT] Fake offset to use in the real mmap call. */ + __u64 offset; +}; + +/** + * DOC: PowerVR IOCTL CREATE_VM_CONTEXT and DESTROY_VM_CONTEXT interfaces + */ + +/** + * struct drm_pvr_ioctl_create_vm_context_args - Arguments for + * %DRM_IOCTL_PVR_CREATE_VM_CONTEXT + */ +struct drm_pvr_ioctl_create_vm_context_args { + /** @handle: [OUT] Handle for new VM context. */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; +}; + +/** + * struct drm_pvr_ioctl_destroy_vm_context_args - Arguments for + * %DRM_IOCTL_PVR_DESTROY_VM_CONTEXT + */ +struct drm_pvr_ioctl_destroy_vm_context_args { + /** + * @handle: [IN] Handle for VM context to be destroyed. + */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; +}; + +/** + * DOC: PowerVR IOCTL VM_MAP and VM_UNMAP interfaces + * + * The VM UAPI allows userspace to create buffer object mappings in GPU virtual address space. + * + * The client is responsible for managing GPU address space. It should allocate mappings within + * the heaps returned by %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + * + * %DRM_IOCTL_PVR_VM_MAP creates a new mapping. The client provides the target virtual address for + * the mapping. Size and offset within the mapped buffer object can be specified, so the client can + * partially map a buffer. + * + * %DRM_IOCTL_PVR_VM_UNMAP removes a mapping. The entire mapping will be removed from GPU address + * space only if the size of the mapping matches that known to the driver. + */ + +/** + * struct drm_pvr_ioctl_vm_map_args - Arguments for %DRM_IOCTL_PVR_VM_MAP. + */ +struct drm_pvr_ioctl_vm_map_args { + /** + * @vm_context_handle: [IN] Handle for VM context for this mapping to + * exist in. + */ + __u32 vm_context_handle; + + /** @flags: [IN] Flags which affect this mapping. Currently always 0. */ + __u32 flags; + + /** + * @device_addr: [IN] Requested device-virtual address for the mapping. + * This must be non-zero and aligned to the device page size for the + * heap containing the requested address. It is an error to specify an + * address which is not contained within one of the heaps returned by + * %DRM_PVR_DEV_QUERY_HEAP_INFO_GET. + */ + __u64 device_addr; + + /** + * @handle: [IN] Handle of the target buffer object. This must be a + * valid handle returned by %DRM_IOCTL_PVR_CREATE_BO. + */ + __u32 handle; + + /** @_padding_14: Reserved. This field must be zeroed. */ + __u32 _padding_14; + + /** + * @offset: [IN] Offset into the target bo from which to begin the + * mapping. + */ + __u64 offset; + + /** + * @size: [IN] Size of the requested mapping. Must be aligned to + * the device page size for the heap containing the requested address, + * as well as the host page size. When added to @device_addr, the + * result must not overflow the heap which contains @device_addr (i.e. + * the range specified by @device_addr and @size must be completely + * contained within a single heap specified by + * %DRM_PVR_DEV_QUERY_HEAP_INFO_GET). + */ + __u64 size; +}; + +/** + * struct drm_pvr_ioctl_vm_unmap_args - Arguments for %DRM_IOCTL_PVR_VM_UNMAP. + */ +struct drm_pvr_ioctl_vm_unmap_args { + /** + * @vm_context_handle: [IN] Handle for VM context that this mapping + * exists in. + */ + __u32 vm_context_handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; + + /** + * @device_addr: [IN] Device-virtual address at the start of the target + * mapping. This must be non-zero. + */ + __u64 device_addr; + + /** + * @size: Size in bytes of the target mapping. This must be non-zero. + */ + __u64 size; +}; + +/** + * DOC: PowerVR IOCTL CREATE_CONTEXT and DESTROY_CONTEXT interfaces + */ + +/** + * enum drm_pvr_ctx_priority - Arguments for + * &drm_pvr_ioctl_create_context_args.priority + */ +enum drm_pvr_ctx_priority { + /** @DRM_PVR_CTX_PRIORITY_LOW: Priority below normal. */ + DRM_PVR_CTX_PRIORITY_LOW = -512, + + /** @DRM_PVR_CTX_PRIORITY_NORMAL: Normal priority. */ + DRM_PVR_CTX_PRIORITY_NORMAL = 0, + + /** + * @DRM_PVR_CTX_PRIORITY_HIGH: Priority above normal. + * Note this requires ``CAP_SYS_NICE`` or ``DRM_MASTER``. + */ + DRM_PVR_CTX_PRIORITY_HIGH = 512, +}; + +/** + * enum drm_pvr_ctx_type - Arguments for + * &struct drm_pvr_ioctl_create_context_args.type + */ +enum drm_pvr_ctx_type { + /** + * @DRM_PVR_CTX_TYPE_RENDER: Render context. Use &struct + * drm_pvr_ioctl_create_render_context_args for context creation arguments. + */ + DRM_PVR_CTX_TYPE_RENDER = 0, + + /** + * @DRM_PVR_CTX_TYPE_COMPUTE: Compute context. Use &struct + * drm_pvr_ioctl_create_compute_context_args for context creation arguments. + */ + DRM_PVR_CTX_TYPE_COMPUTE, + + /** + * @DRM_PVR_CTX_TYPE_TRANSFER_FRAG: Transfer context for fragment data masters. Use + * &struct drm_pvr_ioctl_create_transfer_context_args for context creation arguments. + */ + DRM_PVR_CTX_TYPE_TRANSFER_FRAG, +}; + +/** + * struct drm_pvr_ioctl_create_context_args - Arguments for + * %DRM_IOCTL_PVR_CREATE_CONTEXT + */ +struct drm_pvr_ioctl_create_context_args { + /** + * @type: [IN] Type of context to create. + * + * This must be one of the values defined by &enum drm_pvr_ctx_type. + */ + __u32 type; + + /** @flags: [IN] Flags for context. */ + __u32 flags; + + /** + * @priority: [IN] Priority of new context. + * + * This must be one of the values defined by &enum drm_pvr_ctx_priority. + */ + __s32 priority; + + /** @handle: [OUT] Handle for new context. */ + __u32 handle; + + /** + * @static_context_state: [IN] Pointer to static context state stream. + */ + __u64 static_context_state; + + /** + * @static_context_state_len: [IN] Length of static context state, in bytes. + */ + __u32 static_context_state_len; + + /** + * @vm_context_handle: [IN] Handle for VM context that this context is + * associated with. + */ + __u32 vm_context_handle; + + /** + * @callstack_addr: [IN] Address for initial call stack pointer. Only valid + * if @type is %DRM_PVR_CTX_TYPE_RENDER, otherwise must be 0. + */ + __u64 callstack_addr; +}; + +/** + * struct drm_pvr_ioctl_destroy_context_args - Arguments for + * %DRM_IOCTL_PVR_DESTROY_CONTEXT + */ +struct drm_pvr_ioctl_destroy_context_args { + /** + * @handle: [IN] Handle for context to be destroyed. + */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; +}; + +/** + * DOC: PowerVR IOCTL CREATE_FREE_LIST and DESTROY_FREE_LIST interfaces + */ + +/** + * struct drm_pvr_ioctl_create_free_list_args - Arguments for + * %DRM_IOCTL_PVR_CREATE_FREE_LIST + * + * Free list arguments have the following constraints : + * + * - @max_num_pages must be greater than zero. + * - @grow_threshold must be between 0 and 100. + * - @grow_num_pages must be less than or equal to &max_num_pages. + * - @initial_num_pages, @max_num_pages and @grow_num_pages must be multiples + * of 4. + * - When &grow_num_pages is 0, @initial_num_pages must be equal to + * @max_num_pages. + * - When &grow_num_pages is non-zero, @initial_num_pages must be less than + * @max_num_pages. + */ +struct drm_pvr_ioctl_create_free_list_args { + /** + * @free_list_gpu_addr: [IN] Address of GPU mapping of buffer object + * containing memory to be used by free list. + * + * The mapped region of the buffer object must be at least + * @max_num_pages * ``sizeof(__u32)``. + * + * The buffer object must have been created with + * %DRM_PVR_BO_DEVICE_PM_FW_PROTECT set and + * %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS not set. + */ + __u64 free_list_gpu_addr; + + /** @initial_num_pages: [IN] Pages initially allocated to free list. */ + __u32 initial_num_pages; + + /** @max_num_pages: [IN] Maximum number of pages in free list. */ + __u32 max_num_pages; + + /** @grow_num_pages: [IN] Pages to grow free list by per request. */ + __u32 grow_num_pages; + + /** + * @grow_threshold: [IN] Percentage of FL memory used that should + * trigger a new grow request. + */ + __u32 grow_threshold; + + /** + * @vm_context_handle: [IN] Handle for VM context that the free list buffer + * object is mapped in. + */ + __u32 vm_context_handle; + + /** + * @handle: [OUT] Handle for created free list. + */ + __u32 handle; +}; + +/** + * struct drm_pvr_ioctl_destroy_free_list_args - Arguments for + * %DRM_IOCTL_PVR_DESTROY_FREE_LIST + */ +struct drm_pvr_ioctl_destroy_free_list_args { + /** + * @handle: [IN] Handle for free list to be destroyed. + */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; +}; + +/** + * DOC: PowerVR IOCTL CREATE_HWRT_DATASET and DESTROY_HWRT_DATASET interfaces + */ + +/** + * struct drm_pvr_create_hwrt_geom_data_args - Geometry data arguments used for + * &struct drm_pvr_ioctl_create_hwrt_dataset_args.geom_data_args. + */ +struct drm_pvr_create_hwrt_geom_data_args { + /** @tpc_dev_addr: [IN] Tail pointer cache GPU virtual address. */ + __u64 tpc_dev_addr; + + /** @tpc_size: [IN] Size of TPC, in bytes. */ + __u32 tpc_size; + + /** @tpc_stride: [IN] Stride between layers in TPC, in pages */ + __u32 tpc_stride; + + /** @vheap_table_dev_addr: [IN] VHEAP table GPU virtual address. */ + __u64 vheap_table_dev_addr; + + /** @rtc_dev_addr: [IN] Render Target Cache virtual address. */ + __u64 rtc_dev_addr; +}; + +/** + * struct drm_pvr_create_hwrt_rt_data_args - Render target arguments used for + * &struct drm_pvr_ioctl_create_hwrt_dataset_args.rt_data_args. + */ +struct drm_pvr_create_hwrt_rt_data_args { + /** @pm_mlist_dev_addr: [IN] PM MLIST GPU virtual address. */ + __u64 pm_mlist_dev_addr; + + /** @macrotile_array_dev_addr: [IN] Macrotile array GPU virtual address. */ + __u64 macrotile_array_dev_addr; + + /** @region_header_dev_addr: [IN] Region header array GPU virtual address. */ + __u64 region_header_dev_addr; +}; + +#define PVR_DRM_HWRT_FREE_LIST_LOCAL 0 +#define PVR_DRM_HWRT_FREE_LIST_GLOBAL 1U + +/** + * struct drm_pvr_ioctl_create_hwrt_dataset_args - Arguments for + * %DRM_IOCTL_PVR_CREATE_HWRT_DATASET + */ +struct drm_pvr_ioctl_create_hwrt_dataset_args { + /** @geom_data_args: [IN] Geometry data arguments. */ + struct drm_pvr_create_hwrt_geom_data_args geom_data_args; + + /** + * @rt_data_args: [IN] Array of render target arguments. + * + * Each entry in this array represents a render target in a double buffered + * setup. + */ + struct drm_pvr_create_hwrt_rt_data_args rt_data_args[2]; + + /** + * @free_list_handles: [IN] Array of free list handles. + * + * free_list_handles[PVR_DRM_HWRT_FREE_LIST_LOCAL] must have initial + * size of at least that reported by + * &drm_pvr_dev_query_runtime_info.free_list_min_pages. + */ + __u32 free_list_handles[2]; + + /** @width: [IN] Width in pixels. */ + __u32 width; + + /** @height: [IN] Height in pixels. */ + __u32 height; + + /** @samples: [IN] Number of samples. */ + __u32 samples; + + /** @layers: [IN] Number of layers. */ + __u32 layers; + + /** @isp_merge_lower_x: [IN] Lower X coefficient for triangle merging. */ + __u32 isp_merge_lower_x; + + /** @isp_merge_lower_y: [IN] Lower Y coefficient for triangle merging. */ + __u32 isp_merge_lower_y; + + /** @isp_merge_scale_x: [IN] Scale X coefficient for triangle merging. */ + __u32 isp_merge_scale_x; + + /** @isp_merge_scale_y: [IN] Scale Y coefficient for triangle merging. */ + __u32 isp_merge_scale_y; + + /** @isp_merge_upper_x: [IN] Upper X coefficient for triangle merging. */ + __u32 isp_merge_upper_x; + + /** @isp_merge_upper_y: [IN] Upper Y coefficient for triangle merging. */ + __u32 isp_merge_upper_y; + + /** + * @region_header_size: [IN] Size of region header array. This common field is used by + * both render targets in this data set. + * + * The units for this field differ depending on what version of the simple internal + * parameter format the device uses. If format 2 is in use then this is interpreted as the + * number of region headers. For other formats it is interpreted as the size in dwords. + */ + __u32 region_header_size; + + /** + * @handle: [OUT] Handle for created HWRT dataset. + */ + __u32 handle; +}; + +/** + * struct drm_pvr_ioctl_destroy_hwrt_dataset_args - Arguments for + * %DRM_IOCTL_PVR_DESTROY_HWRT_DATASET + */ +struct drm_pvr_ioctl_destroy_hwrt_dataset_args { + /** + * @handle: [IN] Handle for HWRT dataset to be destroyed. + */ + __u32 handle; + + /** @_padding_4: Reserved. This field must be zeroed. */ + __u32 _padding_4; +}; + +/** + * DOC: PowerVR IOCTL SUBMIT_JOBS interface + */ + +/** + * DOC: Flags for the drm_pvr_sync_op object. + * + * .. c:macro:: DRM_PVR_SYNC_OP_HANDLE_TYPE_MASK + * + * Handle type mask for the drm_pvr_sync_op::flags field. + * + * .. c:macro:: DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ + * + * Indicates the handle passed in drm_pvr_sync_op::handle is a syncobj handle. + * This is the default type. + * + * .. c:macro:: DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_TIMELINE_SYNCOBJ + * + * Indicates the handle passed in drm_pvr_sync_op::handle is a timeline syncobj handle. + * + * .. c:macro:: DRM_PVR_SYNC_OP_FLAG_SIGNAL + * + * Signal operation requested. The out-fence bound to the job will be attached to + * the syncobj whose handle is passed in drm_pvr_sync_op::handle. + * + * .. c:macro:: DRM_PVR_SYNC_OP_FLAG_WAIT + * + * Wait operation requested. The job will wait for this particular syncobj or syncobj + * point to be signaled before being started. + * This is the default operation. + */ +#define DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_MASK 0xf +#define DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_SYNCOBJ 0 +#define DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_TIMELINE_SYNCOBJ 1 +#define DRM_PVR_SYNC_OP_FLAG_SIGNAL _BITULL(31) +#define DRM_PVR_SYNC_OP_FLAG_WAIT 0 + +#define DRM_PVR_SYNC_OP_FLAGS_MASK (DRM_PVR_SYNC_OP_FLAG_HANDLE_TYPE_MASK | \ + DRM_PVR_SYNC_OP_FLAG_SIGNAL) + +/** + * struct drm_pvr_sync_op - Object describing a sync operation + */ +struct drm_pvr_sync_op { + /** @handle: Handle of sync object. */ + __u32 handle; + + /** @flags: Combination of ``DRM_PVR_SYNC_OP_FLAG_`` flags. */ + __u32 flags; + + /** @value: Timeline value for this drm_syncobj. MBZ for a binary syncobj. */ + __u64 value; +}; + +/** + * DOC: Flags for SUBMIT_JOB ioctl geometry command. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST + * + * Indicates if this the first command to be issued for a render. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST + * + * Indicates if this the last command to be issued for a render. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE + * + * Forces to use single core in a multi core device. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_GEOM_CMD_FLAGS_MASK + * + * Logical OR of all the geometry cmd flags. + */ +#define DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST _BITULL(0) +#define DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST _BITULL(1) +#define DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE _BITULL(2) +#define DRM_PVR_SUBMIT_JOB_GEOM_CMD_FLAGS_MASK \ + (DRM_PVR_SUBMIT_JOB_GEOM_CMD_FIRST | \ + DRM_PVR_SUBMIT_JOB_GEOM_CMD_LAST | \ + DRM_PVR_SUBMIT_JOB_GEOM_CMD_SINGLE_CORE) + +/** + * DOC: Flags for SUBMIT_JOB ioctl fragment command. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE + * + * Use single core in a multi core setup. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER + * + * Indicates whether a depth buffer is present. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER + * + * Indicates whether a stencil buffer is present. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP + * + * Disallow compute overlapped with this render. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS + * + * Indicates whether this render produces visibility results. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER + * + * Indicates whether partial renders write to a scratch buffer instead of + * the final surface. It also forces the full screen copy expected to be + * present on the last render after all partial renders have completed. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE + * + * Disable pixel merging for this render. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_FRAG_CMD_FLAGS_MASK + * + * Logical OR of all the fragment cmd flags. + */ +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE _BITULL(0) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER _BITULL(1) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER _BITULL(2) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP _BITULL(3) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER _BITULL(4) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS _BITULL(5) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_PARTIAL_RENDER _BITULL(6) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE _BITULL(7) +#define DRM_PVR_SUBMIT_JOB_FRAG_CMD_FLAGS_MASK \ + (DRM_PVR_SUBMIT_JOB_FRAG_CMD_SINGLE_CORE | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_DEPTHBUFFER | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_STENCILBUFFER | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_PREVENT_CDM_OVERLAP | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_SCRATCHBUFFER | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_GET_VIS_RESULTS | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_PARTIAL_RENDER | \ + DRM_PVR_SUBMIT_JOB_FRAG_CMD_DISABLE_PIXELMERGE) + +/** + * DOC: Flags for SUBMIT_JOB ioctl compute command. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP + * + * Disallow other jobs overlapped with this compute. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE + * + * Forces to use single core in a multi core device. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_FLAGS_MASK + * + * Logical OR of all the compute cmd flags. + */ +#define DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP _BITULL(0) +#define DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE _BITULL(1) +#define DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_FLAGS_MASK \ + (DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_PREVENT_ALL_OVERLAP | \ + DRM_PVR_SUBMIT_JOB_COMPUTE_CMD_SINGLE_CORE) + +/** + * DOC: Flags for SUBMIT_JOB ioctl transfer command. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE + * + * Forces job to use a single core in a multi core device. + * + * .. c:macro:: DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_FLAGS_MASK + * + * Logical OR of all the transfer cmd flags. + */ +#define DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE _BITULL(0) + +#define DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_FLAGS_MASK \ + DRM_PVR_SUBMIT_JOB_TRANSFER_CMD_SINGLE_CORE + +/** + * enum drm_pvr_job_type - Arguments for &struct drm_pvr_job.job_type + */ +enum drm_pvr_job_type { + /** @DRM_PVR_JOB_TYPE_GEOMETRY: Job type is geometry. */ + DRM_PVR_JOB_TYPE_GEOMETRY = 0, + + /** @DRM_PVR_JOB_TYPE_FRAGMENT: Job type is fragment. */ + DRM_PVR_JOB_TYPE_FRAGMENT, + + /** @DRM_PVR_JOB_TYPE_COMPUTE: Job type is compute. */ + DRM_PVR_JOB_TYPE_COMPUTE, + + /** @DRM_PVR_JOB_TYPE_TRANSFER_FRAG: Job type is a fragment transfer. */ + DRM_PVR_JOB_TYPE_TRANSFER_FRAG, +}; + +/** + * struct drm_pvr_hwrt_data_ref - Reference HWRT data + */ +struct drm_pvr_hwrt_data_ref { + /** @set_handle: HWRT data set handle. */ + __u32 set_handle; + + /** @data_index: Index of the HWRT data inside the data set. */ + __u32 data_index; +}; + +/** + * struct drm_pvr_job - Job arguments passed to the %DRM_IOCTL_PVR_SUBMIT_JOBS ioctl + */ +struct drm_pvr_job { + /** + * @type: [IN] Type of job being submitted + * + * This must be one of the values defined by &enum drm_pvr_job_type. + */ + __u32 type; + + /** + * @context_handle: [IN] Context handle. + * + * When @job_type is %DRM_PVR_JOB_TYPE_RENDER, %DRM_PVR_JOB_TYPE_COMPUTE or + * %DRM_PVR_JOB_TYPE_TRANSFER_FRAG, this must be a valid handle returned by + * %DRM_IOCTL_PVR_CREATE_CONTEXT. The type of context must be compatible + * with the type of job being submitted. + * + * When @job_type is %DRM_PVR_JOB_TYPE_NULL, this must be zero. + */ + __u32 context_handle; + + /** + * @flags: [IN] Flags for command. + * + * Those are job-dependent. See all ``DRM_PVR_SUBMIT_JOB_*``. + */ + __u32 flags; + + /** + * @cmd_stream_len: [IN] Length of command stream, in bytes. + */ + __u32 cmd_stream_len; + + /** + * @cmd_stream: [IN] Pointer to command stream for command. + * + * The command stream must be u64-aligned. + */ + __u64 cmd_stream; + + /** @sync_ops: [IN] Fragment sync operations. */ + struct drm_pvr_obj_array sync_ops; + + /** + * @hwrt: [IN] HWRT data used by render jobs (geometry or fragment). + * + * Must be zero for non-render jobs. + */ + struct drm_pvr_hwrt_data_ref hwrt; +}; + +/** + * struct drm_pvr_ioctl_submit_jobs_args - Arguments for %DRM_IOCTL_PVR_SUBMIT_JOB + * + * If the syscall returns an error it is important to check the value of + * @jobs.count. This indicates the index into @jobs.array where the + * error occurred. + */ +struct drm_pvr_ioctl_submit_jobs_args { + /** @jobs: [IN] Array of jobs to submit. */ + struct drm_pvr_obj_array jobs; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* PVR_DRM_UAPI_H */ diff --git a/include/uapi/drm/qaic_accel.h b/include/uapi/drm/qaic_accel.h index 43ac5d864512..9dab32316aee 100644 --- a/include/uapi/drm/qaic_accel.h +++ b/include/uapi/drm/qaic_accel.h @@ -287,8 +287,9 @@ struct qaic_execute_entry { * struct qaic_partial_execute_entry - Defines a BO to resize and submit. * @handle: In. GEM handle of the BO to commit to the device. * @dir: In. Direction of data. 1 = to device, 2 = from device. - * @resize: In. New size of the BO. Must be <= the original BO size. 0 is - * short for no resize. + * @resize: In. New size of the BO. Must be <= the original BO size. + * @resize as 0 would be interpreted as no DMA transfer is + * involved. */ struct qaic_partial_execute_entry { __u32 handle; diff --git a/include/uapi/drm/v3d_drm.h b/include/uapi/drm/v3d_drm.h index 3dfc0af8756a..1a7d7a689de3 100644 --- a/include/uapi/drm/v3d_drm.h +++ b/include/uapi/drm/v3d_drm.h @@ -319,6 +319,11 @@ struct drm_v3d_submit_tfu { /* Pointer to an array of ioctl extensions*/ __u64 extensions; + + struct { + __u32 ioc; + __u32 pad; + } v71; }; /* Submits a compute shader for dispatch. This job will block on any diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h index b1d0e56565bc..c2ce71987e9b 100644 --- a/include/uapi/drm/virtgpu_drm.h +++ b/include/uapi/drm/virtgpu_drm.h @@ -97,6 +97,7 @@ struct drm_virtgpu_execbuffer { #define VIRTGPU_PARAM_CROSS_DEVICE 5 /* Cross virtio-device resource sharing */ #define VIRTGPU_PARAM_CONTEXT_INIT 6 /* DRM_VIRTGPU_CONTEXT_INIT */ #define VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */ +#define VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME 8 /* Ability to set debug name from userspace */ struct drm_virtgpu_getparam { __u64 param; @@ -198,6 +199,7 @@ struct drm_virtgpu_resource_create_blob { #define VIRTGPU_CONTEXT_PARAM_CAPSET_ID 0x0001 #define VIRTGPU_CONTEXT_PARAM_NUM_RINGS 0x0002 #define VIRTGPU_CONTEXT_PARAM_POLL_RINGS_MASK 0x0003 +#define VIRTGPU_CONTEXT_PARAM_DEBUG_NAME 0x0004 struct drm_virtgpu_context_set_param { __u64 param; __u64 value; diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index c25fc9614594..d24e8e121507 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -219,6 +219,22 @@ */ #define BTRFS_METADATA_ITEM_KEY 169 +/* + * Special inline ref key which stores the id of the subvolume which originally + * created the extent. This subvolume owns the extent permanently from the + * perspective of simple quotas. Needed to know which subvolume to free quota + * usage from when the extent is deleted. + * + * Stored as an inline ref rather to avoid wasting space on a separate item on + * top of the existing extent item. However, unlike the other inline refs, + * there is one one owner ref per extent rather than one per extent. + * + * Because of this, it goes at the front of the list of inline refs, and thus + * must have a lower type value than any other inline ref type (to satisfy the + * disk format rule that inline refs have non-decreasing type). + */ +#define BTRFS_EXTENT_OWNER_REF_KEY 172 + #define BTRFS_TREE_BLOCK_REF_KEY 176 #define BTRFS_EXTENT_DATA_REF_KEY 178 @@ -234,14 +250,6 @@ #define BTRFS_SHARED_DATA_REF_KEY 184 /* - * Special inline ref key which stores the id of the subvolume which originally - * created the extent. This subvolume owns the extent permanently from the - * perspective of simple quotas. Needed to know which subvolume to free quota - * usage from when the extent is deleted. - */ -#define BTRFS_EXTENT_OWNER_REF_KEY 188 - -/* * block groups give us hints into the extent allocation trees. Which * blocks are free etc etc */ diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 6c80f96049bd..282e90aeb163 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -116,5 +116,8 @@ #define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to compare object identity and may not be usable to open_by_handle_at(2) */ +#if defined(__KERNEL__) +#define AT_GETATTR_NOSEC 0x80000000 +#endif #endif /* _UAPI_LINUX_FCNTL_H */ diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index f703afc7ad31..44f4dd2add18 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -166,6 +166,17 @@ struct virtio_pci_common_cfg { __le32 queue_used_hi; /* read-write */ }; +/* + * Warning: do not use sizeof on this: use offsetofend for + * specific fields you need. + */ +struct virtio_pci_modern_common_cfg { + struct virtio_pci_common_cfg cfg; + + __le16 queue_notify_data; /* read-write */ + __le16 queue_reset; /* read-write */ +}; + /* Fields in VIRTIO_PCI_CAP_PCI_CFG: */ struct virtio_pci_cfg_cap { struct virtio_pci_cap cap; diff --git a/include/xen/events.h b/include/xen/events.h index 23932b0673dc..3b07409f8032 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -88,7 +88,6 @@ void xen_irq_resume(void); /* Clear an irq's pending state, in preparation for polling on it */ void xen_clear_irq_pending(int irq); -void xen_set_irq_pending(int irq); bool xen_test_irq_pending(int irq); /* Poll waiting for an irq to become pending. In the usual case, the @@ -101,8 +100,8 @@ void xen_poll_irq_timeout(int irq, u64 timeout); /* Determine the IRQ which is bound to an event channel */ unsigned int irq_from_evtchn(evtchn_port_t evtchn); -int irq_from_virq(unsigned int cpu, unsigned int virq); -evtchn_port_t evtchn_from_irq(unsigned irq); +int irq_evtchn_from_virq(unsigned int cpu, unsigned int virq, + evtchn_port_t *evtchn); int xen_set_callback_via(uint64_t via); int xen_evtchn_do_upcall(void); @@ -122,9 +121,6 @@ int xen_bind_pirq_msi_to_irq(struct pci_dev *dev, struct msi_desc *msidesc, /* De-allocates the above mentioned physical interrupt. */ int xen_destroy_irq(int irq); -/* Return irq from pirq */ -int xen_irq_from_pirq(unsigned pirq); - /* Return the pirq allocated to the irq. */ int xen_pirq_from_irq(unsigned irq); diff --git a/io_uring/fdinfo.c b/io_uring/fdinfo.c index f04a43044d91..976e9500f651 100644 --- a/io_uring/fdinfo.c +++ b/io_uring/fdinfo.c @@ -145,13 +145,8 @@ __cold void io_uring_show_fdinfo(struct seq_file *m, struct file *f) if (has_lock && (ctx->flags & IORING_SETUP_SQPOLL)) { struct io_sq_data *sq = ctx->sq_data; - if (mutex_trylock(&sq->lock)) { - if (sq->thread) { - sq_pid = task_pid_nr(sq->thread); - sq_cpu = task_cpu(sq->thread); - } - mutex_unlock(&sq->lock); - } + sq_pid = sq->task_pid; + sq_cpu = sq->sq_cpu; } seq_printf(m, "SqThread:\t%d\n", sq_pid); diff --git a/io_uring/fs.c b/io_uring/fs.c index 08e3b175469c..eccea851dd5a 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -254,7 +254,7 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) newf = u64_to_user_ptr(READ_ONCE(sqe->addr2)); lnk->flags = READ_ONCE(sqe->hardlink_flags); - lnk->oldpath = getname(oldf); + lnk->oldpath = getname_uflags(oldf, lnk->flags); if (IS_ERR(lnk->oldpath)) return PTR_ERR(lnk->oldpath); diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c index 7034be555334..f521c5965a93 100644 --- a/io_uring/rsrc.c +++ b/io_uring/rsrc.c @@ -1258,7 +1258,7 @@ int io_import_fixed(int ddir, struct iov_iter *iter, */ const struct bio_vec *bvec = imu->bvec; - if (offset <= bvec->bv_len) { + if (offset < bvec->bv_len) { /* * Note, huge pages buffers consists of one large * bvec entry and should always go this way. The other diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c index bd6c2c7959a5..65b5dbe3c850 100644 --- a/io_uring/sqpoll.c +++ b/io_uring/sqpoll.c @@ -214,6 +214,7 @@ static bool io_sqd_handle_event(struct io_sq_data *sqd) did_sig = get_signal(&ksig); cond_resched(); mutex_lock(&sqd->lock); + sqd->sq_cpu = raw_smp_processor_id(); } return did_sig || test_bit(IO_SQ_THREAD_SHOULD_STOP, &sqd->state); } @@ -229,10 +230,15 @@ static int io_sq_thread(void *data) snprintf(buf, sizeof(buf), "iou-sqp-%d", sqd->task_pid); set_task_comm(current, buf); - if (sqd->sq_cpu != -1) + /* reset to our pid after we've set task_comm, for fdinfo */ + sqd->task_pid = current->pid; + + if (sqd->sq_cpu != -1) { set_cpus_allowed_ptr(current, cpumask_of(sqd->sq_cpu)); - else + } else { set_cpus_allowed_ptr(current, cpu_online_mask); + sqd->sq_cpu = raw_smp_processor_id(); + } mutex_lock(&sqd->lock); while (1) { @@ -261,6 +267,7 @@ static int io_sq_thread(void *data) mutex_unlock(&sqd->lock); cond_resched(); mutex_lock(&sqd->lock); + sqd->sq_cpu = raw_smp_processor_id(); } continue; } @@ -294,6 +301,7 @@ static int io_sq_thread(void *data) mutex_unlock(&sqd->lock); schedule(); mutex_lock(&sqd->lock); + sqd->sq_cpu = raw_smp_processor_id(); } list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) atomic_andnot(IORING_SQ_NEED_WAKEUP, diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 91e82e34b51e..7a98cd176a12 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -531,7 +531,7 @@ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark) if (tsk != current) return 0; - if (WARN_ON_ONCE(!current->mm)) + if (!current->mm) return 0; exe_file = get_mm_exe_file(current->mm); if (!exe_file) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 08626b519ce2..cd3afe57ece3 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -64,8 +64,8 @@ #define OFF insn->off #define IMM insn->imm -struct bpf_mem_alloc bpf_global_ma, bpf_global_percpu_ma; -bool bpf_global_ma_set, bpf_global_percpu_ma_set; +struct bpf_mem_alloc bpf_global_ma; +bool bpf_global_ma_set; /* No hurry in this branch * @@ -2934,9 +2934,7 @@ static int __init bpf_global_ma_init(void) ret = bpf_mem_alloc_init(&bpf_global_ma, 0, false); bpf_global_ma_set = !ret; - ret = bpf_mem_alloc_init(&bpf_global_percpu_ma, 0, true); - bpf_global_percpu_ma_set = !ret; - return !bpf_global_ma_set || !bpf_global_percpu_ma_set; + return ret; } late_initcall(bpf_global_ma_init); #endif diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bd1c42eb540f..af2819d5c8ee 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -26,6 +26,7 @@ #include <linux/poison.h> #include <linux/module.h> #include <linux/cpumask.h> +#include <linux/bpf_mem_alloc.h> #include <net/xdp.h> #include "disasm.h" @@ -41,6 +42,9 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = { #undef BPF_LINK_TYPE }; +struct bpf_mem_alloc bpf_global_percpu_ma; +static bool bpf_global_percpu_ma_set; + /* bpf_check() is a static code analyzer that walks eBPF program * instruction by instruction and updates register/stack state. * All paths of conditional branches are analyzed until 'bpf_exit' insn. @@ -336,6 +340,7 @@ struct bpf_kfunc_call_arg_meta { struct btf *btf_vmlinux; static DEFINE_MUTEX(bpf_verifier_lock); +static DEFINE_MUTEX(bpf_percpu_ma_lock); static const struct bpf_line_info * find_linfo(const struct bpf_verifier_env *env, u32 insn_off) @@ -542,13 +547,12 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id) return func_id == BPF_FUNC_dynptr_data; } -static bool is_callback_calling_kfunc(u32 btf_id); +static bool is_sync_callback_calling_kfunc(u32 btf_id); static bool is_bpf_throw_kfunc(struct bpf_insn *insn); -static bool is_callback_calling_function(enum bpf_func_id func_id) +static bool is_sync_callback_calling_function(enum bpf_func_id func_id) { return func_id == BPF_FUNC_for_each_map_elem || - func_id == BPF_FUNC_timer_set_callback || func_id == BPF_FUNC_find_vma || func_id == BPF_FUNC_loop || func_id == BPF_FUNC_user_ringbuf_drain; @@ -559,6 +563,18 @@ static bool is_async_callback_calling_function(enum bpf_func_id func_id) return func_id == BPF_FUNC_timer_set_callback; } +static bool is_callback_calling_function(enum bpf_func_id func_id) +{ + return is_sync_callback_calling_function(func_id) || + is_async_callback_calling_function(func_id); +} + +static bool is_sync_callback_calling_insn(struct bpf_insn *insn) +{ + return (bpf_helper_call(insn) && is_sync_callback_calling_function(insn->imm)) || + (bpf_pseudo_kfunc_call(insn) && is_sync_callback_calling_kfunc(insn->imm)); +} + static bool is_storage_get_function(enum bpf_func_id func_id) { return func_id == BPF_FUNC_sk_storage_get || @@ -1803,6 +1819,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, dst_state->first_insn_idx = src->first_insn_idx; dst_state->last_insn_idx = src->last_insn_idx; dst_state->dfs_depth = src->dfs_depth; + dst_state->callback_unroll_depth = src->callback_unroll_depth; dst_state->used_as_loop_entry = src->used_as_loop_entry; for (i = 0; i <= src->curframe; i++) { dst = dst_state->frame[i]; @@ -3434,13 +3451,11 @@ static void mark_insn_zext(struct bpf_verifier_env *env, reg->subreg_def = DEF_NOT_SUBREG; } -static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, - enum reg_arg_type t) +static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno, + enum reg_arg_type t) { - struct bpf_verifier_state *vstate = env->cur_state; - struct bpf_func_state *state = vstate->frame[vstate->curframe]; struct bpf_insn *insn = env->prog->insnsi + env->insn_idx; - struct bpf_reg_state *reg, *regs = state->regs; + struct bpf_reg_state *reg; bool rw64; if (regno >= MAX_BPF_REG) { @@ -3481,6 +3496,15 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, return 0; } +static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, + enum reg_arg_type t) +{ + struct bpf_verifier_state *vstate = env->cur_state; + struct bpf_func_state *state = vstate->frame[vstate->curframe]; + + return __check_reg_arg(env, state->regs, regno, t); +} + static void mark_jmp_point(struct bpf_verifier_env *env, int idx) { env->insn_aux_data[idx].jmp_point = true; @@ -3516,12 +3540,29 @@ static int push_jmp_history(struct bpf_verifier_env *env, /* Backtrack one insn at a time. If idx is not at the top of recorded * history then previous instruction came from straight line execution. + * Return -ENOENT if we exhausted all instructions within given state. + * + * It's legal to have a bit of a looping with the same starting and ending + * insn index within the same state, e.g.: 3->4->5->3, so just because current + * instruction index is the same as state's first_idx doesn't mean we are + * done. If there is still some jump history left, we should keep going. We + * need to take into account that we might have a jump history between given + * state's parent and itself, due to checkpointing. In this case, we'll have + * history entry recording a jump from last instruction of parent state and + * first instruction of given state. */ static int get_prev_insn_idx(struct bpf_verifier_state *st, int i, u32 *history) { u32 cnt = *history; + if (i == st->first_insn_idx) { + if (cnt == 0) + return -ENOENT; + if (cnt == 1 && st->jmp_history[0].idx == i) + return -ENOENT; + } + if (cnt && st->jmp_history[cnt - 1].idx == i) { i = st->jmp_history[cnt - 1].prev_idx; (*history)--; @@ -3702,6 +3743,8 @@ static void fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask) } } +static bool calls_callback(struct bpf_verifier_env *env, int insn_idx); + /* For given verifier state backtrack_insn() is called from the last insn to * the first insn. Its purpose is to compute a bitmask of registers and * stack slots that needs precision in the parent verifier state. @@ -3877,16 +3920,13 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, return -EFAULT; return 0; } - } else if ((bpf_helper_call(insn) && - is_callback_calling_function(insn->imm) && - !is_async_callback_calling_function(insn->imm)) || - (bpf_pseudo_kfunc_call(insn) && is_callback_calling_kfunc(insn->imm))) { - /* callback-calling helper or kfunc call, which means - * we are exiting from subprog, but unlike the subprog - * call handling above, we shouldn't propagate - * precision of r1-r5 (if any requested), as they are - * not actually arguments passed directly to callback - * subprogs + } else if (is_sync_callback_calling_insn(insn) && idx != subseq_idx - 1) { + /* exit from callback subprog to callback-calling helper or + * kfunc call. Use idx/subseq_idx check to discern it from + * straight line code backtracking. + * Unlike the subprog call handling above, we shouldn't + * propagate precision of r1-r5 (if any requested), as they are + * not actually arguments passed directly to callback subprogs */ if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); @@ -3921,10 +3961,18 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, } else if (opcode == BPF_EXIT) { bool r0_precise; + /* Backtracking to a nested function call, 'idx' is a part of + * the inner frame 'subseq_idx' is a part of the outer frame. + * In case of a regular function call, instructions giving + * precision to registers R1-R5 should have been found already. + * In case of a callback, it is ok to have R1-R5 marked for + * backtracking, as these registers are set by the function + * invoking callback. + */ + if (subseq_idx >= 0 && calls_callback(env, subseq_idx)) + for (i = BPF_REG_1; i <= BPF_REG_5; i++) + bt_clear_reg(bt, i); if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { - /* if backtracing was looking for registers R1-R5 - * they should have been found already. - */ verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); WARN_ONCE(1, "verifier backtracking bug"); return -EFAULT; @@ -4401,10 +4449,10 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) * Nothing to be tracked further in the parent state. */ return 0; - if (i == first_idx) - break; subseq_idx = i; i = get_prev_insn_idx(st, i, &history); + if (i == -ENOENT) + break; if (i >= env->prog->len) { /* This can happen if backtracking reached insn 0 * and there are still reg_mask or stack_mask @@ -9328,7 +9376,7 @@ static void clear_caller_saved_regs(struct bpf_verifier_env *env, /* after the call registers r0 - r5 were scratched */ for (i = 0; i < CALLER_SAVED_REGS; i++) { mark_reg_not_init(env, regs, caller_saved[i]); - check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK); + __check_reg_arg(env, regs, caller_saved[i], DST_OP_NO_MARK); } } @@ -9341,11 +9389,10 @@ static int set_callee_state(struct bpf_verifier_env *env, struct bpf_func_state *caller, struct bpf_func_state *callee, int insn_idx); -static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, - int *insn_idx, int subprog, - set_callee_state_fn set_callee_state_cb) +static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int callsite, + set_callee_state_fn set_callee_state_cb, + struct bpf_verifier_state *state) { - struct bpf_verifier_state *state = env->cur_state; struct bpf_func_state *caller, *callee; int err; @@ -9355,54 +9402,72 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn return -E2BIG; } - caller = state->frame[state->curframe]; if (state->frame[state->curframe + 1]) { verbose(env, "verifier bug. Frame %d already allocated\n", state->curframe + 1); return -EFAULT; } + caller = state->frame[state->curframe]; + callee = kzalloc(sizeof(*callee), GFP_KERNEL); + if (!callee) + return -ENOMEM; + state->frame[state->curframe + 1] = callee; + + /* callee cannot access r0, r6 - r9 for reading and has to write + * into its own stack before reading from it. + * callee can read/write into caller's stack + */ + init_func_state(env, callee, + /* remember the callsite, it will be used by bpf_exit */ + callsite, + state->curframe + 1 /* frameno within this callchain */, + subprog /* subprog number within this prog */); + /* Transfer references to the callee */ + err = copy_reference_state(callee, caller); + err = err ?: set_callee_state_cb(env, caller, callee, callsite); + if (err) + goto err_out; + + /* only increment it after check_reg_arg() finished */ + state->curframe++; + + return 0; + +err_out: + free_func_state(callee); + state->frame[state->curframe + 1] = NULL; + return err; +} + +static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int insn_idx, int subprog, + set_callee_state_fn set_callee_state_cb) +{ + struct bpf_verifier_state *state = env->cur_state, *callback_state; + struct bpf_func_state *caller, *callee; + int err; + + caller = state->frame[state->curframe]; err = btf_check_subprog_call(env, subprog, caller->regs); if (err == -EFAULT) return err; - if (subprog_is_global(env, subprog)) { - if (err) { - verbose(env, "Caller passes invalid args into func#%d\n", - subprog); - return err; - } else { - if (env->log.level & BPF_LOG_LEVEL) - verbose(env, - "Func#%d is global and valid. Skipping.\n", - subprog); - clear_caller_saved_regs(env, caller->regs); - - /* All global functions return a 64-bit SCALAR_VALUE */ - mark_reg_unknown(env, caller->regs, BPF_REG_0); - caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; - - /* continue with next insn after call */ - return 0; - } - } /* set_callee_state is used for direct subprog calls, but we are * interested in validating only BPF helpers that can call subprogs as * callbacks */ - if (set_callee_state_cb != set_callee_state) { - env->subprog_info[subprog].is_cb = true; - if (bpf_pseudo_kfunc_call(insn) && - !is_callback_calling_kfunc(insn->imm)) { - verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n", - func_id_name(insn->imm), insn->imm); - return -EFAULT; - } else if (!bpf_pseudo_kfunc_call(insn) && - !is_callback_calling_function(insn->imm)) { /* helper */ - verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n", - func_id_name(insn->imm), insn->imm); - return -EFAULT; - } + env->subprog_info[subprog].is_cb = true; + if (bpf_pseudo_kfunc_call(insn) && + !is_sync_callback_calling_kfunc(insn->imm)) { + verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; + } else if (!bpf_pseudo_kfunc_call(insn) && + !is_callback_calling_function(insn->imm)) { /* helper */ + verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; } if (insn->code == (BPF_JMP | BPF_CALL) && @@ -9413,53 +9478,83 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn /* there is no real recursion here. timer callbacks are async */ env->subprog_info[subprog].is_async_cb = true; async_cb = push_async_cb(env, env->subprog_info[subprog].start, - *insn_idx, subprog); + insn_idx, subprog); if (!async_cb) return -EFAULT; callee = async_cb->frame[0]; callee->async_entry_cnt = caller->async_entry_cnt + 1; /* Convert bpf_timer_set_callback() args into timer callback args */ - err = set_callee_state_cb(env, caller, callee, *insn_idx); + err = set_callee_state_cb(env, caller, callee, insn_idx); if (err) return err; + return 0; + } + + /* for callback functions enqueue entry to callback and + * proceed with next instruction within current frame. + */ + callback_state = push_stack(env, env->subprog_info[subprog].start, insn_idx, false); + if (!callback_state) + return -ENOMEM; + + err = setup_func_entry(env, subprog, insn_idx, set_callee_state_cb, + callback_state); + if (err) + return err; + + callback_state->callback_unroll_depth++; + callback_state->frame[callback_state->curframe - 1]->callback_depth++; + caller->callback_depth = 0; + return 0; +} + +static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, + int *insn_idx) +{ + struct bpf_verifier_state *state = env->cur_state; + struct bpf_func_state *caller; + int err, subprog, target_insn; + + target_insn = *insn_idx + insn->imm + 1; + subprog = find_subprog(env, target_insn); + if (subprog < 0) { + verbose(env, "verifier bug. No program starts at insn %d\n", target_insn); + return -EFAULT; + } + + caller = state->frame[state->curframe]; + err = btf_check_subprog_call(env, subprog, caller->regs); + if (err == -EFAULT) + return err; + if (subprog_is_global(env, subprog)) { + if (err) { + verbose(env, "Caller passes invalid args into func#%d\n", subprog); + return err; + } + + if (env->log.level & BPF_LOG_LEVEL) + verbose(env, "Func#%d is global and valid. Skipping.\n", subprog); clear_caller_saved_regs(env, caller->regs); + + /* All global functions return a 64-bit SCALAR_VALUE */ mark_reg_unknown(env, caller->regs, BPF_REG_0); caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; + /* continue with next insn after call */ return 0; } - callee = kzalloc(sizeof(*callee), GFP_KERNEL); - if (!callee) - return -ENOMEM; - state->frame[state->curframe + 1] = callee; - - /* callee cannot access r0, r6 - r9 for reading and has to write - * into its own stack before reading from it. - * callee can read/write into caller's stack + /* for regular function entry setup new frame and continue + * from that frame. */ - init_func_state(env, callee, - /* remember the callsite, it will be used by bpf_exit */ - *insn_idx /* callsite */, - state->curframe + 1 /* frameno within this callchain */, - subprog /* subprog number within this prog */); - - /* Transfer references to the callee */ - err = copy_reference_state(callee, caller); - if (err) - goto err_out; - - err = set_callee_state_cb(env, caller, callee, *insn_idx); + err = setup_func_entry(env, subprog, *insn_idx, set_callee_state, state); if (err) - goto err_out; + return err; clear_caller_saved_regs(env, caller->regs); - /* only increment it after check_reg_arg() finished */ - state->curframe++; - /* and go analyze first insn of the callee */ *insn_idx = env->subprog_info[subprog].start - 1; @@ -9467,14 +9562,10 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn verbose(env, "caller:\n"); print_verifier_state(env, caller, true); verbose(env, "callee:\n"); - print_verifier_state(env, callee, true); + print_verifier_state(env, state->frame[state->curframe], true); } - return 0; -err_out: - free_func_state(callee); - state->frame[state->curframe + 1] = NULL; - return err; + return 0; } int map_set_for_each_callback_args(struct bpf_verifier_env *env, @@ -9518,22 +9609,6 @@ static int set_callee_state(struct bpf_verifier_env *env, return 0; } -static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, - int *insn_idx) -{ - int subprog, target_insn; - - target_insn = *insn_idx + insn->imm + 1; - subprog = find_subprog(env, target_insn); - if (subprog < 0) { - verbose(env, "verifier bug. No program starts at insn %d\n", - target_insn); - return -EFAULT; - } - - return __check_func_call(env, insn, insn_idx, subprog, set_callee_state); -} - static int set_map_elem_callback_state(struct bpf_verifier_env *env, struct bpf_func_state *caller, struct bpf_func_state *callee, @@ -9726,9 +9801,10 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env) static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) { - struct bpf_verifier_state *state = env->cur_state; + struct bpf_verifier_state *state = env->cur_state, *prev_st; struct bpf_func_state *caller, *callee; struct bpf_reg_state *r0; + bool in_callback_fn; int err; callee = state->frame[state->curframe]; @@ -9757,6 +9833,11 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) verbose_invalid_scalar(env, r0, &range, "callback return", "R0"); return -EINVAL; } + if (!calls_callback(env, callee->callsite)) { + verbose(env, "BUG: in callback at %d, callsite %d !calls_callback\n", + *insn_idx, callee->callsite); + return -EFAULT; + } } else { /* return to the caller whatever r0 had in the callee */ caller->regs[BPF_REG_0] = *r0; @@ -9774,7 +9855,16 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) return err; } - *insn_idx = callee->callsite + 1; + /* for callbacks like bpf_loop or bpf_for_each_map_elem go back to callsite, + * there function call logic would reschedule callback visit. If iteration + * converges is_state_visited() would prune that visit eventually. + */ + in_callback_fn = callee->in_callback_fn; + if (in_callback_fn) + *insn_idx = callee->callsite; + else + *insn_idx = callee->callsite + 1; + if (env->log.level & BPF_LOG_LEVEL) { verbose(env, "returning from callee:\n"); print_verifier_state(env, callee, true); @@ -9785,6 +9875,24 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) * bpf_throw, this will be done by copy_verifier_state for extra frames. */ free_func_state(callee); state->frame[state->curframe--] = NULL; + + /* for callbacks widen imprecise scalars to make programs like below verify: + * + * struct ctx { int i; } + * void cb(int idx, struct ctx *ctx) { ctx->i++; ... } + * ... + * struct ctx = { .i = 0; } + * bpf_loop(100, cb, &ctx, 0); + * + * This is similar to what is done in process_iter_next_call() for open + * coded iterators. + */ + prev_st = in_callback_fn ? find_prev_entry(env, state, *insn_idx) : NULL; + if (prev_st) { + err = widen_imprecise_scalars(env, prev_st, state); + if (err) + return err; + } return 0; } @@ -10187,24 +10295,37 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn } break; case BPF_FUNC_for_each_map_elem: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_map_elem_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_map_elem_callback_state); break; case BPF_FUNC_timer_set_callback: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_timer_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_timer_callback_state); break; case BPF_FUNC_find_vma: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_find_vma_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_find_vma_callback_state); break; case BPF_FUNC_snprintf: err = check_bpf_snprintf_call(env, regs); break; case BPF_FUNC_loop: update_loop_inline_state(env, meta.subprogno); - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_loop_callback_state); + /* Verifier relies on R1 value to determine if bpf_loop() iteration + * is finished, thus mark it precise. + */ + err = mark_chain_precision(env, BPF_REG_1); + if (err) + return err; + if (cur_func(env)->callback_depth < regs[BPF_REG_1].umax_value) { + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_loop_callback_state); + } else { + cur_func(env)->callback_depth = 0; + if (env->log.level & BPF_LOG_LEVEL2) + verbose(env, "frame%d bpf_loop iteration limit reached\n", + env->cur_state->curframe); + } break; case BPF_FUNC_dynptr_from_mem: if (regs[BPF_REG_1].type != PTR_TO_MAP_VALUE) { @@ -10300,8 +10421,8 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn break; } case BPF_FUNC_user_ringbuf_drain: - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_user_ringbuf_callback_state); + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_user_ringbuf_callback_state); break; } @@ -11189,7 +11310,7 @@ static bool is_bpf_graph_api_kfunc(u32 btf_id) btf_id == special_kfunc_list[KF_bpf_refcount_acquire_impl]; } -static bool is_callback_calling_kfunc(u32 btf_id) +static bool is_sync_callback_calling_kfunc(u32 btf_id) { return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl]; } @@ -11941,6 +12062,21 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EACCES; } + /* Check the arguments */ + err = check_kfunc_args(env, &meta, insn_idx); + if (err < 0) + return err; + + if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) { + err = push_callback_call(env, insn, insn_idx, meta.subprogno, + set_rbtree_add_callback_state); + if (err) { + verbose(env, "kfunc %s#%d failed callback verification\n", + func_name, meta.func_id); + return err; + } + } + rcu_lock = is_kfunc_bpf_rcu_read_lock(&meta); rcu_unlock = is_kfunc_bpf_rcu_read_unlock(&meta); @@ -11976,10 +12112,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return -EINVAL; } - /* Check the arguments */ - err = check_kfunc_args(env, &meta, insn_idx); - if (err < 0) - return err; /* In case of release function, we get register number of refcounted * PTR_TO_BTF_ID in bpf_kfunc_arg_meta, do the release now. */ @@ -12013,16 +12145,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, } } - if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) { - err = __check_func_call(env, insn, insn_idx_p, meta.subprogno, - set_rbtree_add_callback_state); - if (err) { - verbose(env, "kfunc %s#%d failed callback verification\n", - func_name, meta.func_id); - return err; - } - } - if (meta.func_id == special_kfunc_list[KF_bpf_throw]) { if (!bpf_jit_supports_exceptions()) { verbose(env, "JIT does not support calling kfunc %s#%d\n", @@ -12074,8 +12196,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl] && !bpf_global_ma_set) return -ENOMEM; - if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl] && !bpf_global_percpu_ma_set) - return -ENOMEM; + if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { + if (!bpf_global_percpu_ma_set) { + mutex_lock(&bpf_percpu_ma_lock); + if (!bpf_global_percpu_ma_set) { + err = bpf_mem_alloc_init(&bpf_global_percpu_ma, 0, true); + if (!err) + bpf_global_percpu_ma_set = true; + } + mutex_unlock(&bpf_percpu_ma_lock); + if (err) + return err; + } + } if (((u64)(u32)meta.arg_constant.value) != meta.arg_constant.value) { verbose(env, "local type ID argument must be in range [0, U32_MAX]\n"); @@ -15375,6 +15508,15 @@ static bool is_force_checkpoint(struct bpf_verifier_env *env, int insn_idx) return env->insn_aux_data[insn_idx].force_checkpoint; } +static void mark_calls_callback(struct bpf_verifier_env *env, int idx) +{ + env->insn_aux_data[idx].calls_callback = true; +} + +static bool calls_callback(struct bpf_verifier_env *env, int insn_idx) +{ + return env->insn_aux_data[insn_idx].calls_callback; +} enum { DONE_EXPLORING = 0, @@ -15386,8 +15528,7 @@ enum { * w - next instruction * e - edge */ -static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, - bool loop_ok) +static int push_insn(int t, int w, int e, struct bpf_verifier_env *env) { int *insn_stack = env->cfg.insn_stack; int *insn_state = env->cfg.insn_state; @@ -15419,7 +15560,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env, insn_stack[env->cfg.cur_stack++] = w; return KEEP_EXPLORING; } else if ((insn_state[w] & 0xF0) == DISCOVERED) { - if (loop_ok && env->bpf_capable) + if (env->bpf_capable) return DONE_EXPLORING; verbose_linfo(env, t, "%d: ", t); verbose_linfo(env, w, "%d: ", w); @@ -15439,24 +15580,20 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns, struct bpf_verifier_env *env, bool visit_callee) { - int ret; + int ret, insn_sz; - ret = push_insn(t, t + 1, FALLTHROUGH, env, false); + insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1; + ret = push_insn(t, t + insn_sz, FALLTHROUGH, env); if (ret) return ret; - mark_prune_point(env, t + 1); + mark_prune_point(env, t + insn_sz); /* when we exit from subprog, we need to record non-linear history */ - mark_jmp_point(env, t + 1); + mark_jmp_point(env, t + insn_sz); if (visit_callee) { mark_prune_point(env, t); - ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env, - /* It's ok to allow recursion from CFG point of - * view. __check_func_call() will do the actual - * check. - */ - bpf_pseudo_func(insns + t)); + ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env); } return ret; } @@ -15469,15 +15606,17 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns, static int visit_insn(int t, struct bpf_verifier_env *env) { struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t]; - int ret, off; + int ret, off, insn_sz; if (bpf_pseudo_func(insn)) return visit_func_call_insn(t, insns, env, true); /* All non-branch instructions have a single fall-through edge. */ if (BPF_CLASS(insn->code) != BPF_JMP && - BPF_CLASS(insn->code) != BPF_JMP32) - return push_insn(t, t + 1, FALLTHROUGH, env, false); + BPF_CLASS(insn->code) != BPF_JMP32) { + insn_sz = bpf_is_ldimm64(insn) ? 2 : 1; + return push_insn(t, t + insn_sz, FALLTHROUGH, env); + } switch (BPF_OP(insn->code)) { case BPF_EXIT: @@ -15491,6 +15630,21 @@ static int visit_insn(int t, struct bpf_verifier_env *env) * async state will be pushed for further exploration. */ mark_prune_point(env, t); + /* For functions that invoke callbacks it is not known how many times + * callback would be called. Verifier models callback calling functions + * by repeatedly visiting callback bodies and returning to origin call + * instruction. + * In order to stop such iteration verifier needs to identify when a + * state identical some state from a previous iteration is reached. + * Check below forces creation of checkpoint before callback calling + * instruction to allow search for such identical states. + */ + if (is_sync_callback_calling_insn(insn)) { + mark_calls_callback(env, t); + mark_force_checkpoint(env, t); + mark_prune_point(env, t); + mark_jmp_point(env, t); + } if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { struct bpf_kfunc_call_arg_meta meta; @@ -15523,8 +15677,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env) off = insn->imm; /* unconditional jump with single edge */ - ret = push_insn(t, t + off + 1, FALLTHROUGH, env, - true); + ret = push_insn(t, t + off + 1, FALLTHROUGH, env); if (ret) return ret; @@ -15537,11 +15690,11 @@ static int visit_insn(int t, struct bpf_verifier_env *env) /* conditional jump with two edges */ mark_prune_point(env, t); - ret = push_insn(t, t + 1, FALLTHROUGH, env, true); + ret = push_insn(t, t + 1, FALLTHROUGH, env); if (ret) return ret; - return push_insn(t, t + insn->off + 1, BRANCH, env, true); + return push_insn(t, t + insn->off + 1, BRANCH, env); } } @@ -15607,11 +15760,21 @@ walk_cfg: } for (i = 0; i < insn_cnt; i++) { + struct bpf_insn *insn = &env->prog->insnsi[i]; + if (insn_state[i] != EXPLORED) { verbose(env, "unreachable insn %d\n", i); ret = -EINVAL; goto err_free; } + if (bpf_is_ldimm64(insn)) { + if (insn_state[i + 1] != 0) { + verbose(env, "jump into the middle of ldimm64 insn %d\n", i); + ret = -EINVAL; + goto err_free; + } + i++; /* skip second half of ldimm64 */ + } } ret = 0; /* cfg looks good */ @@ -16951,10 +17114,16 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) } goto skip_inf_loop_check; } + if (calls_callback(env, insn_idx)) { + if (states_equal(env, &sl->state, cur, true)) + goto hit; + goto skip_inf_loop_check; + } /* attempt to detect infinite loop to avoid unnecessary doomed work */ if (states_maybe_looping(&sl->state, cur) && states_equal(env, &sl->state, cur, false) && - !iter_active_depths_differ(&sl->state, cur)) { + !iter_active_depths_differ(&sl->state, cur) && + sl->state.callback_unroll_depth == cur->callback_unroll_depth) { verbose_linfo(env, insn_idx, "; "); verbose(env, "infinite loop detected at insn %d\n", insn_idx); verbose(env, "cur state:"); diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 1d5b9de3b1b9..4b9ff41ca603 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -3885,14 +3885,6 @@ static __poll_t cgroup_pressure_poll(struct kernfs_open_file *of, return psi_trigger_poll(&ctx->psi.trigger, of->file, pt); } -static int cgroup_pressure_open(struct kernfs_open_file *of) -{ - if (of->file->f_mode & FMODE_WRITE && !capable(CAP_SYS_RESOURCE)) - return -EPERM; - - return 0; -} - static void cgroup_pressure_release(struct kernfs_open_file *of) { struct cgroup_file_ctx *ctx = of->priv; @@ -5299,7 +5291,6 @@ static struct cftype cgroup_psi_files[] = { { .name = "io.pressure", .file_offset = offsetof(struct cgroup, psi_files[PSI_IO]), - .open = cgroup_pressure_open, .seq_show = cgroup_io_pressure_show, .write = cgroup_io_pressure_write, .poll = cgroup_pressure_poll, @@ -5308,7 +5299,6 @@ static struct cftype cgroup_psi_files[] = { { .name = "memory.pressure", .file_offset = offsetof(struct cgroup, psi_files[PSI_MEM]), - .open = cgroup_pressure_open, .seq_show = cgroup_memory_pressure_show, .write = cgroup_memory_pressure_write, .poll = cgroup_pressure_poll, @@ -5317,7 +5307,6 @@ static struct cftype cgroup_psi_files[] = { { .name = "cpu.pressure", .file_offset = offsetof(struct cgroup, psi_files[PSI_CPU]), - .open = cgroup_pressure_open, .seq_show = cgroup_cpu_pressure_show, .write = cgroup_cpu_pressure_write, .poll = cgroup_pressure_poll, @@ -5327,7 +5316,6 @@ static struct cftype cgroup_psi_files[] = { { .name = "irq.pressure", .file_offset = offsetof(struct cgroup, psi_files[PSI_IRQ]), - .open = cgroup_pressure_open, .seq_show = cgroup_irq_pressure_show, .write = cgroup_irq_pressure_write, .poll = cgroup_pressure_poll, diff --git a/kernel/cpu.c b/kernel/cpu.c index 9e4c6780adde..a86972a91991 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2113,7 +2113,7 @@ static struct cpuhp_step cpuhp_hp_states[] = { [CPUHP_HRTIMERS_PREPARE] = { .name = "hrtimers:prepare", .startup.single = hrtimers_prepare_cpu, - .teardown.single = hrtimers_dead_cpu, + .teardown.single = NULL, }, [CPUHP_SMPCFD_PREPARE] = { .name = "smpcfd:prepare", @@ -2205,6 +2205,12 @@ static struct cpuhp_step cpuhp_hp_states[] = { .startup.single = NULL, .teardown.single = smpcfd_dying_cpu, }, + [CPUHP_AP_HRTIMERS_DYING] = { + .name = "hrtimers:dying", + .startup.single = NULL, + .teardown.single = hrtimers_cpu_dying, + }, + /* Entry state on starting. Interrupts enabled from here on. Transient * state for synchronsization */ [CPUHP_AP_ONLINE] = { diff --git a/kernel/events/core.c b/kernel/events/core.c index 683dc086ef10..b704d83a28b2 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4828,6 +4828,11 @@ find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx, void *task_ctx_data = NULL; if (!ctx->task) { + /* + * perf_pmu_migrate_context() / __perf_pmu_install_event() + * relies on the fact that find_get_pmu_context() cannot fail + * for CPU contexts. + */ struct perf_cpu_pmu_context *cpc; cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu); @@ -12889,6 +12894,9 @@ static void __perf_pmu_install_event(struct pmu *pmu, int cpu, struct perf_event *event) { struct perf_event_pmu_context *epc; + struct perf_event_context *old_ctx = event->ctx; + + get_ctx(ctx); /* normally find_get_context() */ event->cpu = cpu; epc = find_get_pmu_context(pmu, ctx, event); @@ -12897,6 +12905,11 @@ static void __perf_pmu_install_event(struct pmu *pmu, if (event->state >= PERF_EVENT_STATE_OFF) event->state = PERF_EVENT_STATE_INACTIVE; perf_install_in_context(ctx, event, cpu); + + /* + * Now that event->ctx is updated and visible, put the old ctx. + */ + put_ctx(old_ctx); } static void __perf_pmu_install(struct perf_event_context *ctx, @@ -12935,6 +12948,10 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) struct perf_event_context *src_ctx, *dst_ctx; LIST_HEAD(events); + /* + * Since per-cpu context is persistent, no need to grab an extra + * reference. + */ src_ctx = &per_cpu_ptr(&perf_cpu_context, src_cpu)->ctx; dst_ctx = &per_cpu_ptr(&perf_cpu_context, dst_cpu)->ctx; diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 52695c59d041..dad981a865b8 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -700,7 +700,8 @@ retry: owner = uval & FUTEX_TID_MASK; if (pending_op && !pi && !owner) { - futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); + futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, 1, + FUTEX_BITSET_MATCH_ANY); return 0; } @@ -752,8 +753,10 @@ retry: * Wake robust non-PI futexes here. The wakeup of * PI futexes happens in exit_pi_state(): */ - if (!pi && (uval & FUTEX_WAITERS)) - futex_wake(uaddr, 1, 1, FUTEX_BITSET_MATCH_ANY); + if (!pi && (uval & FUTEX_WAITERS)) { + futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, 1, + FUTEX_BITSET_MATCH_ANY); + } return 0; } diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index e85b5ad3e206..151bd3de5936 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -3497,7 +3497,8 @@ static int alloc_chain_hlocks(int req) size = chain_block_size(curr); if (likely(size >= req)) { del_chain_block(0, size, chain_block_next(curr)); - add_chain_block(curr + req, size - req); + if (size > req) + add_chain_block(curr + req, size - req); return curr; } } diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2048138ce54b..d7a3c63a2171 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3666,41 +3666,140 @@ static inline void dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } #endif +static void reweight_eevdf(struct cfs_rq *cfs_rq, struct sched_entity *se, + unsigned long weight) +{ + unsigned long old_weight = se->load.weight; + u64 avruntime = avg_vruntime(cfs_rq); + s64 vlag, vslice; + + /* + * VRUNTIME + * ======== + * + * COROLLARY #1: The virtual runtime of the entity needs to be + * adjusted if re-weight at !0-lag point. + * + * Proof: For contradiction assume this is not true, so we can + * re-weight without changing vruntime at !0-lag point. + * + * Weight VRuntime Avg-VRuntime + * before w v V + * after w' v' V' + * + * Since lag needs to be preserved through re-weight: + * + * lag = (V - v)*w = (V'- v')*w', where v = v' + * ==> V' = (V - v)*w/w' + v (1) + * + * Let W be the total weight of the entities before reweight, + * since V' is the new weighted average of entities: + * + * V' = (WV + w'v - wv) / (W + w' - w) (2) + * + * by using (1) & (2) we obtain: + * + * (WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v + * ==> (WV-Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v + * ==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v + * ==> (V - v)*W/(W + w' - w) = (V - v)*w/w' (3) + * + * Since we are doing at !0-lag point which means V != v, we + * can simplify (3): + * + * ==> W / (W + w' - w) = w / w' + * ==> Ww' = Ww + ww' - ww + * ==> W * (w' - w) = w * (w' - w) + * ==> W = w (re-weight indicates w' != w) + * + * So the cfs_rq contains only one entity, hence vruntime of + * the entity @v should always equal to the cfs_rq's weighted + * average vruntime @V, which means we will always re-weight + * at 0-lag point, thus breach assumption. Proof completed. + * + * + * COROLLARY #2: Re-weight does NOT affect weighted average + * vruntime of all the entities. + * + * Proof: According to corollary #1, Eq. (1) should be: + * + * (V - v)*w = (V' - v')*w' + * ==> v' = V' - (V - v)*w/w' (4) + * + * According to the weighted average formula, we have: + * + * V' = (WV - wv + w'v') / (W - w + w') + * = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w') + * = (WV - wv + w'V' - Vw + wv) / (W - w + w') + * = (WV + w'V' - Vw) / (W - w + w') + * + * ==> V'*(W - w + w') = WV + w'V' - Vw + * ==> V' * (W - w) = (W - w) * V (5) + * + * If the entity is the only one in the cfs_rq, then reweight + * always occurs at 0-lag point, so V won't change. Or else + * there are other entities, hence W != w, then Eq. (5) turns + * into V' = V. So V won't change in either case, proof done. + * + * + * So according to corollary #1 & #2, the effect of re-weight + * on vruntime should be: + * + * v' = V' - (V - v) * w / w' (4) + * = V - (V - v) * w / w' + * = V - vl * w / w' + * = V - vl' + */ + if (avruntime != se->vruntime) { + vlag = (s64)(avruntime - se->vruntime); + vlag = div_s64(vlag * old_weight, weight); + se->vruntime = avruntime - vlag; + } + + /* + * DEADLINE + * ======== + * + * When the weight changes, the virtual time slope changes and + * we should adjust the relative virtual deadline accordingly. + * + * d' = v' + (d - v)*w/w' + * = V' - (V - v)*w/w' + (d - v)*w/w' + * = V - (V - v)*w/w' + (d - v)*w/w' + * = V + (d - V)*w/w' + */ + vslice = (s64)(se->deadline - avruntime); + vslice = div_s64(vslice * old_weight, weight); + se->deadline = avruntime + vslice; +} + static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, unsigned long weight) { - unsigned long old_weight = se->load.weight; + bool curr = cfs_rq->curr == se; if (se->on_rq) { /* commit outstanding execution time */ - if (cfs_rq->curr == se) + if (curr) update_curr(cfs_rq); else - avg_vruntime_sub(cfs_rq, se); + __dequeue_entity(cfs_rq, se); update_load_sub(&cfs_rq->load, se->load.weight); } dequeue_load_avg(cfs_rq, se); - update_load_set(&se->load, weight); - if (!se->on_rq) { /* * Because we keep se->vlag = V - v_i, while: lag_i = w_i*(V - v_i), * we need to scale se->vlag when w_i changes. */ - se->vlag = div_s64(se->vlag * old_weight, weight); + se->vlag = div_s64(se->vlag * se->load.weight, weight); } else { - s64 deadline = se->deadline - se->vruntime; - /* - * When the weight changes, the virtual time slope changes and - * we should adjust the relative virtual deadline accordingly. - */ - deadline = div_s64(deadline * old_weight, weight); - se->deadline = se->vruntime + deadline; - if (se != cfs_rq->curr) - min_deadline_cb_propagate(&se->run_node, NULL); + reweight_eevdf(cfs_rq, se, weight); } + update_load_set(&se->load, weight); + #ifdef CONFIG_SMP do { u32 divider = get_pelt_divider(&se->avg); @@ -3712,8 +3811,17 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, enqueue_load_avg(cfs_rq, se); if (se->on_rq) { update_load_add(&cfs_rq->load, se->load.weight); - if (cfs_rq->curr != se) - avg_vruntime_add(cfs_rq, se); + if (!curr) { + /* + * The entity's vruntime has been adjusted, so let's check + * whether the rq-wide min_vruntime needs updated too. Since + * the calculations above require stable min_vruntime rather + * than up-to-date one, we do the update at the end of the + * reweight process. + */ + __enqueue_entity(cfs_rq, se); + update_min_vruntime(cfs_rq); + } } } @@ -3857,14 +3965,11 @@ static void update_cfs_group(struct sched_entity *se) #ifndef CONFIG_SMP shares = READ_ONCE(gcfs_rq->tg->shares); - - if (likely(se->load.weight == shares)) - return; #else - shares = calc_group_shares(gcfs_rq); + shares = calc_group_shares(gcfs_rq); #endif - - reweight_entity(cfs_rq_of(se), se, shares); + if (unlikely(se->load.weight != shares)) + reweight_entity(cfs_rq_of(se), se, shares); } #else /* CONFIG_FAIR_GROUP_SCHED */ @@ -11079,12 +11184,16 @@ static int should_we_balance(struct lb_env *env) continue; } - /* Are we the first idle CPU? */ + /* + * Are we the first idle core in a non-SMT domain or higher, + * or the first idle CPU in a SMT domain? + */ return cpu == env->dst_cpu; } - if (idle_smt == env->dst_cpu) - return true; + /* Are we the first idle CPU with busy siblings? */ + if (idle_smt != -1) + return idle_smt == env->dst_cpu; /* Are we the first CPU of this group ? */ return group_balance_cpu(sg) == env->dst_cpu; diff --git a/kernel/sys.c b/kernel/sys.c index 420d9cb9cc8e..e219fcfa112d 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2394,6 +2394,10 @@ static inline int prctl_set_mdwe(unsigned long bits, unsigned long arg3, if (bits & PR_MDWE_NO_INHERIT && !(bits & PR_MDWE_REFUSE_EXEC_GAIN)) return -EINVAL; + /* PARISC cannot allow mdwe as it needs writable stacks */ + if (IS_ENABLED(CONFIG_PARISC)) + return -EINVAL; + current_bits = get_current_mdwe(); if (current_bits && current_bits != bits) return -EPERM; /* Cannot unset the flags */ diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 238262e4aba7..760793998cdd 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -2219,29 +2219,22 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, } } -int hrtimers_dead_cpu(unsigned int scpu) +int hrtimers_cpu_dying(unsigned int dying_cpu) { struct hrtimer_cpu_base *old_base, *new_base; - int i; + int i, ncpu = cpumask_first(cpu_active_mask); - BUG_ON(cpu_online(scpu)); - tick_cancel_sched_timer(scpu); + tick_cancel_sched_timer(dying_cpu); + + old_base = this_cpu_ptr(&hrtimer_bases); + new_base = &per_cpu(hrtimer_bases, ncpu); - /* - * this BH disable ensures that raise_softirq_irqoff() does - * not wakeup ksoftirqd (and acquire the pi-lock) while - * holding the cpu_base lock - */ - local_bh_disable(); - local_irq_disable(); - old_base = &per_cpu(hrtimer_bases, scpu); - new_base = this_cpu_ptr(&hrtimer_bases); /* * The caller is globally serialized and nobody else * takes two locks at once, deadlock is not possible. */ - raw_spin_lock(&new_base->lock); - raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); + raw_spin_lock(&old_base->lock); + raw_spin_lock_nested(&new_base->lock, SINGLE_DEPTH_NESTING); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { migrate_hrtimer_list(&old_base->clock_base[i], @@ -2252,15 +2245,13 @@ int hrtimers_dead_cpu(unsigned int scpu) * The migration might have changed the first expiring softirq * timer on this CPU. Update it. */ - hrtimer_update_softirq_timer(new_base, false); + __hrtimer_get_next_event(new_base, HRTIMER_ACTIVE_SOFT); + /* Tell the other CPU to retrigger the next event */ + smp_call_function_single(ncpu, retrigger_next_event, NULL, 0); - raw_spin_unlock(&old_base->lock); raw_spin_unlock(&new_base->lock); + raw_spin_unlock(&old_base->lock); - /* Check, if we got expired work to do */ - __hrtimer_peek_ahead_timers(); - local_irq_enable(); - local_bh_enable(); return 0; } diff --git a/lib/errname.c b/lib/errname.c index dd1b998552cd..4f9112b38f3a 100644 --- a/lib/errname.c +++ b/lib/errname.c @@ -111,9 +111,6 @@ static const char *names_0[] = { E(ENOSPC), E(ENOSR), E(ENOSTR), -#ifdef ENOSYM - E(ENOSYM), -#endif E(ENOSYS), E(ENOTBLK), E(ENOTCONN), @@ -144,9 +141,6 @@ static const char *names_0[] = { #endif E(EREMOTE), E(EREMOTEIO), -#ifdef EREMOTERELEASE - E(EREMOTERELEASE), -#endif E(ERESTART), E(ERFKILL), E(EROFS), diff --git a/lib/iov_iter.c b/lib/iov_iter.c index de7d11cf4c63..8ff6824a1005 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -409,7 +409,7 @@ size_t copy_page_to_iter_nofault(struct page *page, unsigned offset, size_t byte void *kaddr = kmap_local_page(page); size_t n = min(bytes, (size_t)PAGE_SIZE - offset); - n = iterate_and_advance(i, bytes, kaddr, + n = iterate_and_advance(i, n, kaddr + offset, copy_to_user_iter_nofault, memcpy_to_iter); kunmap_local(kaddr); diff --git a/lib/zstd/common/fse_decompress.c b/lib/zstd/common/fse_decompress.c index a0d06095be83..8dcb8ca39767 100644 --- a/lib/zstd/common/fse_decompress.c +++ b/lib/zstd/common/fse_decompress.c @@ -312,7 +312,7 @@ size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size typedef struct { short ncount[FSE_MAX_SYMBOL_VALUE + 1]; - FSE_DTable dtable[1]; /* Dynamically sized */ + FSE_DTable dtable[]; /* Dynamically sized */ } FSE_DecompressWksp; diff --git a/mm/damon/core.c b/mm/damon/core.c index 630077d95dc6..6262d55904e7 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -924,7 +924,7 @@ static bool __damos_filter_out(struct damon_ctx *ctx, struct damon_target *t, matched = true; break; default: - break; + return false; } return matched == filter->matching; diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 45bd0fd4a8b1..be667236b8e6 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -162,6 +162,9 @@ damon_sysfs_scheme_regions_alloc(void) struct damon_sysfs_scheme_regions *regions = kmalloc(sizeof(*regions), GFP_KERNEL); + if (!regions) + return NULL; + regions->kobj = (struct kobject){}; INIT_LIST_HEAD(®ions->regions_list); regions->nr_regions = 0; @@ -1823,6 +1826,8 @@ static int damon_sysfs_before_damos_apply(struct damon_ctx *ctx, return 0; region = damon_sysfs_scheme_region_alloc(r); + if (!region) + return 0; list_add_tail(®ion->list, &sysfs_regions->regions_list); sysfs_regions->nr_regions++; if (kobject_init_and_add(®ion->kobj, diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c index e27846708b5a..7472404456aa 100644 --- a/mm/damon/sysfs.c +++ b/mm/damon/sysfs.c @@ -1172,7 +1172,7 @@ static int damon_sysfs_update_target(struct damon_target *target, struct damon_ctx *ctx, struct damon_sysfs_target *sys_target) { - int err; + int err = 0; if (damon_target_has_pid(ctx)) { err = damon_sysfs_update_target_pid(target, sys_target->pid); @@ -1203,8 +1203,10 @@ static int damon_sysfs_set_targets(struct damon_ctx *ctx, damon_for_each_target_safe(t, next, ctx) { if (i < sysfs_targets->nr) { - damon_sysfs_update_target(t, ctx, + err = damon_sysfs_update_target(t, ctx, sysfs_targets->targets_arr[i]); + if (err) + return err; } else { if (damon_target_has_pid(ctx)) put_pid(t->pid); diff --git a/mm/filemap.c b/mm/filemap.c index 9710f43a89ac..32eedf3afd45 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3443,7 +3443,7 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, * handled in the specific fault path, and it'll prohibit the * fault-around logic. */ - if (!pte_none(vmf->pte[count])) + if (!pte_none(ptep_get(&vmf->pte[count]))) goto skip; count++; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index f31f02472396..4f542444a91f 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2769,13 +2769,15 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) int nr = folio_nr_pages(folio); xas_split(&xas, folio, folio_order(folio)); - if (folio_test_swapbacked(folio)) { - __lruvec_stat_mod_folio(folio, NR_SHMEM_THPS, - -nr); - } else { - __lruvec_stat_mod_folio(folio, NR_FILE_THPS, - -nr); - filemap_nr_thps_dec(mapping); + if (folio_test_pmd_mappable(folio)) { + if (folio_test_swapbacked(folio)) { + __lruvec_stat_mod_folio(folio, + NR_SHMEM_THPS, -nr); + } else { + __lruvec_stat_mod_folio(folio, + NR_FILE_THPS, -nr); + filemap_nr_thps_dec(mapping); + } } } @@ -468,7 +468,7 @@ static int break_ksm_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long nex page = pfn_swap_entry_to_page(entry); } /* return 1 if the page is an normal ksm page or KSM-placed zero page */ - ret = (page && PageKsm(page)) || is_ksm_zero_pte(*pte); + ret = (page && PageKsm(page)) || is_ksm_zero_pte(ptent); pte_unmap_unlock(pte, ptl); return ret; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 774bd6e21e27..1c1061df9cd1 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2936,7 +2936,8 @@ void mem_cgroup_commit_charge(struct folio *folio, struct mem_cgroup *memcg) * Moreover, it should not come from DMA buffer and is not readily * reclaimable. So those GFP bits should be masked off. */ -#define OBJCGS_CLEAR_MASK (__GFP_DMA | __GFP_RECLAIMABLE | __GFP_ACCOUNT) +#define OBJCGS_CLEAR_MASK (__GFP_DMA | __GFP_RECLAIMABLE | \ + __GFP_ACCOUNT | __GFP_NOFAIL) /* * mod_objcg_mlstate() may be called with irq enabled, so diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 46f2f5d3d183..ee2fd6a6af40 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -3107,7 +3107,7 @@ EXPORT_SYMBOL_GPL(folio_wait_writeback_killable); */ void folio_wait_stable(struct folio *folio) { - if (folio_inode(folio)->i_sb->s_iflags & SB_I_STABLE_WRITES) + if (mapping_stable_writes(folio_mapping(folio))) folio_wait_writeback(folio); } EXPORT_SYMBOL_GPL(folio_wait_stable); diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 96d9eae5c7cc..0b6ca553bebe 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -312,7 +312,7 @@ static int mfill_atomic_pte_poison(pmd_t *dst_pmd, ret = -EEXIST; /* Refuse to overwrite any PTE, even a PTE marker (e.g. UFFD WP). */ - if (!pte_none(*dst_pte)) + if (!pte_none(ptep_get(dst_pte))) goto out_unlock; set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); diff --git a/mm/util.c b/mm/util.c index aa01f6ea5a75..744b4d7e3fae 100644 --- a/mm/util.c +++ b/mm/util.c @@ -414,6 +414,15 @@ static int mmap_is_legacy(struct rlimit *rlim_stack) static unsigned long mmap_base(unsigned long rnd, struct rlimit *rlim_stack) { +#ifdef CONFIG_STACK_GROWSUP + /* + * For an upwards growing stack the calculation is much simpler. + * Memory for the maximum stack size is reserved at the top of the + * task. mmap_base starts directly below the stack and grows + * downwards. + */ + return PAGE_ALIGN_DOWN(mmap_upper_limit(rlim_stack) - rnd); +#else unsigned long gap = rlim_stack->rlim_cur; unsigned long pad = stack_guard_gap; @@ -431,6 +440,7 @@ static unsigned long mmap_base(unsigned long rnd, struct rlimit *rlim_stack) gap = MAX_GAP; return PAGE_ALIGN(STACK_TOP - gap - rnd); +#endif } void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack) diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c index b5c406a6e765..abb090f94ed2 100644 --- a/net/bridge/netfilter/nf_conntrack_bridge.c +++ b/net/bridge/netfilter/nf_conntrack_bridge.c @@ -37,7 +37,7 @@ static int nf_br_ip_fragment(struct net *net, struct sock *sk, ktime_t tstamp = skb->tstamp; struct ip_frag_state state; struct iphdr *iph; - int err; + int err = 0; /* for offloaded checksums cleanup checksum before fragmentation */ if (skb->ip_summed == CHECKSUM_PARTIAL && diff --git a/net/core/dev.c b/net/core/dev.c index 0d548431f3fa..c879246be48d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1119,7 +1119,9 @@ static int __dev_alloc_name(struct net *net, const char *name, char *res) if (i == max_netdevices) return -ENFILE; - snprintf(res, IFNAMSIZ, name, i); + /* 'res' and 'name' could overlap, use 'buf' as an intermediate buffer */ + strscpy(buf, name, IFNAMSIZ); + snprintf(res, IFNAMSIZ, buf, i); return i; } @@ -10049,6 +10051,54 @@ void netif_tx_stop_all_queues(struct net_device *dev) } EXPORT_SYMBOL(netif_tx_stop_all_queues); +static int netdev_do_alloc_pcpu_stats(struct net_device *dev) +{ + void __percpu *v; + + /* Drivers implementing ndo_get_peer_dev must support tstat + * accounting, so that skb_do_redirect() can bump the dev's + * RX stats upon network namespace switch. + */ + if (dev->netdev_ops->ndo_get_peer_dev && + dev->pcpu_stat_type != NETDEV_PCPU_STAT_TSTATS) + return -EOPNOTSUPP; + + switch (dev->pcpu_stat_type) { + case NETDEV_PCPU_STAT_NONE: + return 0; + case NETDEV_PCPU_STAT_LSTATS: + v = dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); + break; + case NETDEV_PCPU_STAT_TSTATS: + v = dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + break; + case NETDEV_PCPU_STAT_DSTATS: + v = dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); + break; + default: + return -EINVAL; + } + + return v ? 0 : -ENOMEM; +} + +static void netdev_do_free_pcpu_stats(struct net_device *dev) +{ + switch (dev->pcpu_stat_type) { + case NETDEV_PCPU_STAT_NONE: + return; + case NETDEV_PCPU_STAT_LSTATS: + free_percpu(dev->lstats); + break; + case NETDEV_PCPU_STAT_TSTATS: + free_percpu(dev->tstats); + break; + case NETDEV_PCPU_STAT_DSTATS: + free_percpu(dev->dstats); + break; + } +} + /** * register_netdevice() - register a network device * @dev: device to register @@ -10109,9 +10159,13 @@ int register_netdevice(struct net_device *dev) goto err_uninit; } + ret = netdev_do_alloc_pcpu_stats(dev); + if (ret) + goto err_uninit; + ret = dev_index_reserve(net, dev->ifindex); if (ret < 0) - goto err_uninit; + goto err_free_pcpu; dev->ifindex = ret; /* Transfer changeable features to wanted_features and enable @@ -10217,6 +10271,8 @@ err_uninit_notify: call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev); err_ifindex_release: dev_index_release(net, dev->ifindex); +err_free_pcpu: + netdev_do_free_pcpu_stats(dev); err_uninit: if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); @@ -10469,6 +10525,7 @@ void netdev_run_todo(void) WARN_ON(rcu_access_pointer(dev->ip_ptr)); WARN_ON(rcu_access_pointer(dev->ip6_ptr)); + netdev_do_free_pcpu_stats(dev); if (dev->priv_destructor) dev->priv_destructor(dev); if (dev->needs_free_netdev) diff --git a/net/core/filter.c b/net/core/filter.c index 383f96b0a1c7..7e4d7c3bcc84 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -81,6 +81,7 @@ #include <net/xdp.h> #include <net/mptcp.h> #include <net/netfilter/nf_conntrack_bpf.h> +#include <net/netkit.h> #include <linux/un.h> #include "dev.h" @@ -2468,6 +2469,16 @@ static const struct bpf_func_proto bpf_clone_redirect_proto = { DEFINE_PER_CPU(struct bpf_redirect_info, bpf_redirect_info); EXPORT_PER_CPU_SYMBOL_GPL(bpf_redirect_info); +static struct net_device *skb_get_peer_dev(struct net_device *dev) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (likely(ops->ndo_get_peer_dev)) + return INDIRECT_CALL_1(ops->ndo_get_peer_dev, + netkit_peer_dev, dev); + return NULL; +} + int skb_do_redirect(struct sk_buff *skb) { struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info); @@ -2481,17 +2492,15 @@ int skb_do_redirect(struct sk_buff *skb) if (unlikely(!dev)) goto out_drop; if (flags & BPF_F_PEER) { - const struct net_device_ops *ops = dev->netdev_ops; - - if (unlikely(!ops->ndo_get_peer_dev || - !skb_at_tc_ingress(skb))) + if (unlikely(!skb_at_tc_ingress(skb))) goto out_drop; - dev = ops->ndo_get_peer_dev(dev); + dev = skb_get_peer_dev(dev); if (unlikely(!dev || !(dev->flags & IFF_UP) || net_eq(net, dev_net(dev)))) goto out_drop; skb->dev = dev; + dev_sw_netstats_rx_add(dev, skb->len); return -EAGAIN; } return flags & BPF_F_NEIGH ? diff --git a/net/core/gso_test.c b/net/core/gso_test.c index ceb684be4cbf..4c2e77bd12f4 100644 --- a/net/core/gso_test.c +++ b/net/core/gso_test.c @@ -180,18 +180,17 @@ static void gso_test_func(struct kunit *test) } if (tcase->frag_skbs) { - unsigned int total_size = 0, total_true_size = 0, alloc_size = 0; + unsigned int total_size = 0, total_true_size = 0; struct sk_buff *frag_skb, *prev = NULL; - page = alloc_page(GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, page); - page_ref_add(page, tcase->nr_frag_skbs - 1); - for (i = 0; i < tcase->nr_frag_skbs; i++) { unsigned int frag_size; + page = alloc_page(GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, page); + frag_size = tcase->frag_skbs[i]; - frag_skb = build_skb(page_address(page) + alloc_size, + frag_skb = build_skb(page_address(page), frag_size + shinfo_size); KUNIT_ASSERT_NOT_NULL(test, frag_skb); __skb_put(frag_skb, frag_size); @@ -204,11 +203,8 @@ static void gso_test_func(struct kunit *test) total_size += frag_size; total_true_size += frag_skb->truesize; - alloc_size += frag_size + shinfo_size; } - KUNIT_ASSERT_LE(test, alloc_size, PAGE_SIZE); - skb->len += total_size; skb->data_len += total_size; skb->truesize += total_true_size; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index f01aee832aab..7d0e7aaa71e0 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -1481,5 +1481,6 @@ static void __exit inet_diag_exit(void) module_init(inet_diag_init); module_exit(inet_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("INET/INET6: socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 598c1b114d2c..a532f749e477 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -751,12 +751,12 @@ int __inet_hash(struct sock *sk, struct sock *osk) if (err) goto unlock; } + sock_set_flag(sk, SOCK_RCU_FREE); if (IS_ENABLED(CONFIG_IPV6) && sk->sk_reuseport && sk->sk_family == AF_INET6) __sk_nulls_add_node_tail_rcu(sk, &ilb2->nulls_head); else __sk_nulls_add_node_rcu(sk, &ilb2->nulls_head); - sock_set_flag(sk, SOCK_RCU_FREE); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); unlock: spin_unlock(&ilb2->lock); diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index 63a40e4b678f..fe2140c8375c 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -257,5 +257,6 @@ static void __exit raw_diag_exit(void) module_init(raw_diag_init); module_exit(raw_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RAW socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-255 /* AF_INET - IPPROTO_RAW */); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10-255 /* AF_INET6 - IPPROTO_RAW */); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3290a4442b4a..16615d107cf0 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -780,7 +780,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow goto reject_redirect; } - n = __ipv4_neigh_lookup(rt->dst.dev, new_gw); + n = __ipv4_neigh_lookup(rt->dst.dev, (__force u32)new_gw); if (!n) n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev); if (!IS_ERR(n)) { diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 01b50fa79189..4cbe4b44425a 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -247,4 +247,5 @@ static void __exit tcp_diag_exit(void) module_init(tcp_diag_init); module_exit(tcp_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-6 /* AF_INET - IPPROTO_TCP */); diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index de3f2d31f510..dc41a22ee80e 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -296,5 +296,6 @@ static void __exit udp_diag_exit(void) module_init(udp_diag_init); module_exit(udp_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UDP socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-17 /* AF_INET - IPPROTO_UDP */); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-136 /* AF_INET - IPPROTO_UDPLITE */); diff --git a/net/mptcp/mptcp_diag.c b/net/mptcp/mptcp_diag.c index 8df1bdb647e2..5409c2ea3f57 100644 --- a/net/mptcp/mptcp_diag.c +++ b/net/mptcp/mptcp_diag.c @@ -245,4 +245,5 @@ static void __exit mptcp_diag_exit(void) module_init(mptcp_diag_init); module_exit(mptcp_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MPTCP socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-262 /* AF_INET - IPPROTO_MPTCP */); diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c index 1529ec358815..bf4d96f6f99a 100644 --- a/net/mptcp/pm_netlink.c +++ b/net/mptcp/pm_netlink.c @@ -1515,8 +1515,9 @@ void mptcp_pm_remove_addrs(struct mptcp_sock *msk, struct list_head *rm_list) struct mptcp_pm_addr_entry *entry; list_for_each_entry(entry, rm_list, list) { - remove_anno_list_by_saddr(msk, &entry->addr); - if (alist.nr < MPTCP_RM_IDS_MAX) + if ((remove_anno_list_by_saddr(msk, &entry->addr) || + lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) && + alist.nr < MPTCP_RM_IDS_MAX) alist.ids[alist.nr++] = entry->addr.id; } diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a0b8356cd8c5..bc81ea53a049 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -1230,6 +1230,8 @@ static void mptcp_update_infinite_map(struct mptcp_sock *msk, mptcp_do_fallback(ssk); } +#define MPTCP_MAX_GSO_SIZE (GSO_LEGACY_MAX_SIZE - (MAX_TCP_HEADER + 1)) + static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk, struct mptcp_data_frag *dfrag, struct mptcp_sendmsg_info *info) @@ -1256,6 +1258,8 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk, return -EAGAIN; /* compute send limit */ + if (unlikely(ssk->sk_gso_max_size > MPTCP_MAX_GSO_SIZE)) + ssk->sk_gso_max_size = MPTCP_MAX_GSO_SIZE; info->mss_now = tcp_send_mss(ssk, &info->size_goal, info->flags); copy = info->size_goal; @@ -3398,10 +3402,11 @@ 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)) { - /* be sure to set the current sk state before tacking actions - * depending on sk_state, that is processing MPTCP_ERROR_REPORT + /* be sure to set the current sk state before taking actions + * depending on sk_state (MPTCP_ERROR_REPORT) + * On sk release avoid actions depending on the first subflow */ - if (__test_and_clear_bit(MPTCP_CONNECTED, &msk->cb_flags)) + if (__test_and_clear_bit(MPTCP_CONNECTED, &msk->cb_flags) && msk->first) __mptcp_set_connected(sk); if (__test_and_clear_bit(MPTCP_ERROR_REPORT, &msk->cb_flags)) __mptcp_error_report(sk); diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 77f5e8932abf..353680733700 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -738,8 +738,11 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname, val = READ_ONCE(inet_sk(sk)->tos); mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); + bool slow; + slow = lock_sock_fast(ssk); __ip_sock_set_tos(ssk, val); + unlock_sock_fast(ssk, slow); } release_sock(sk); diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c index f8854bff286c..62fb1031763d 100644 --- a/net/ncsi/ncsi-aen.c +++ b/net/ncsi/ncsi-aen.c @@ -89,11 +89,6 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, if ((had_link == has_link) || chained) return 0; - if (had_link) - netif_carrier_off(ndp->ndev.dev); - else - netif_carrier_on(ndp->ndev.dev); - if (!ndp->multi_package && !nc->package->multi_channel) { if (had_link) { ndp->flags |= NCSI_DEV_RESHUFFLE; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 35d2f9c9ada0..4c133e06be1d 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -61,6 +61,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); ip_set_dereference((inst)->ip_set_list)[id] #define ip_set_ref_netlink(inst,id) \ rcu_dereference_raw((inst)->ip_set_list)[id] +#define ip_set_dereference_nfnl(p) \ + rcu_dereference_check(p, lockdep_nfnl_is_held(NFNL_SUBSYS_IPSET)) /* The set types are implemented in modules and registered set types * can be found in ip_set_type_list. Adding/deleting types is @@ -708,15 +710,10 @@ __ip_set_put_netlink(struct ip_set *set) static struct ip_set * ip_set_rcu_get(struct net *net, ip_set_id_t index) { - struct ip_set *set; struct ip_set_net *inst = ip_set_pernet(net); - rcu_read_lock(); - /* ip_set_list itself needs to be protected */ - set = rcu_dereference(inst->ip_set_list)[index]; - rcu_read_unlock(); - - return set; + /* ip_set_list and the set pointer need to be protected */ + return ip_set_dereference_nfnl(inst->ip_set_list)[index]; } static inline void @@ -1397,6 +1394,9 @@ static int ip_set_swap(struct sk_buff *skb, const struct nfnl_info *info, ip_set(inst, to_id) = from; write_unlock_bh(&ip_set_ref_lock); + /* Make sure all readers of the old set pointers are completed. */ + synchronize_rcu(); + return 0; } diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a761ee6796f6..c0a42989b982 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -7263,10 +7263,11 @@ static int nf_tables_delsetelem(struct sk_buff *skb, if (err < 0) { NL_SET_BAD_ATTR(extack, attr); - break; + return err; } } - return err; + + return 0; } /* @@ -9679,16 +9680,14 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans) call_rcu(&trans->rcu, nft_trans_gc_trans_free); } -static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, - unsigned int gc_seq, - bool sync) +struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc, + unsigned int gc_seq) { - struct nft_set_elem_catchall *catchall, *next; + struct nft_set_elem_catchall *catchall; const struct nft_set *set = gc->set; - struct nft_elem_priv *elem_priv; struct nft_set_ext *ext; - list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { + list_for_each_entry_rcu(catchall, &set->catchall_list, list) { ext = nft_set_elem_ext(set, catchall->elem); if (!nft_set_elem_expired(ext)) @@ -9698,35 +9697,42 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, nft_set_elem_dead(ext); dead_elem: - if (sync) - gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); - else - gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); - + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); if (!gc) return NULL; - elem_priv = catchall->elem; - if (sync) { - nft_setelem_data_deactivate(gc->net, gc->set, elem_priv); - nft_setelem_catchall_destroy(catchall); - } - - nft_trans_gc_elem_add(gc, elem_priv); + nft_trans_gc_elem_add(gc, catchall->elem); } return gc; } -struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc, - unsigned int gc_seq) -{ - return nft_trans_gc_catchall(gc, gc_seq, false); -} - struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc) { - return nft_trans_gc_catchall(gc, 0, true); + struct nft_set_elem_catchall *catchall, *next; + const struct nft_set *set = gc->set; + struct nft_elem_priv *elem_priv; + struct nft_set_ext *ext; + + WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net)); + + 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)) + continue; + + gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL); + if (!gc) + return NULL; + + elem_priv = catchall->elem; + nft_setelem_data_deactivate(gc->net, gc->set, elem_priv); + nft_setelem_catchall_destroy(catchall); + nft_trans_gc_elem_add(gc, elem_priv); + } + + return gc; } static void nf_tables_module_autoload_cleanup(struct net *net) diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index e596d1a842f7..f6e791a68101 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -38,13 +38,14 @@ void nft_byteorder_eval(const struct nft_expr *expr, switch (priv->size) { case 8: { + u64 *dst64 = (void *)dst; u64 src64; switch (priv->op) { case NFT_BYTEORDER_NTOH: for (i = 0; i < priv->len / 8; i++) { src64 = nft_reg_load64(&src[i]); - nft_reg_store64(&dst[i], + nft_reg_store64(&dst64[i], be64_to_cpu((__force __be64)src64)); } break; @@ -52,7 +53,7 @@ void nft_byteorder_eval(const struct nft_expr *expr, for (i = 0; i < priv->len / 8; i++) { src64 = (__force __u64) cpu_to_be64(nft_reg_load64(&src[i])); - nft_reg_store64(&dst[i], src64); + nft_reg_store64(&dst64[i], src64); } break; } diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index f7da7c43333b..ba0d3683a45d 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -63,7 +63,7 @@ nft_meta_get_eval_time(enum nft_meta_keys key, { switch (key) { case NFT_META_TIME_NS: - nft_reg_store64(dest, ktime_get_real_ns()); + nft_reg_store64((u64 *)dest, ktime_get_real_ns()); break; case NFT_META_TIME_DAY: nft_reg_store8(dest, nft_meta_weekday()); diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 6f1186abd47b..baa3fea4fe65 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -624,14 +624,12 @@ static void nft_rbtree_gc(struct nft_set *set) { struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe, *rbe_end = NULL; - struct nftables_pernet *nft_net; struct rb_node *node, *next; struct nft_trans_gc *gc; struct net *net; set = nft_set_container_of(priv); net = read_pnet(&set->net); - nft_net = nft_pernet(net); gc = nft_trans_gc_alloc(set, 0, GFP_KERNEL); if (!gc) diff --git a/net/packet/diag.c b/net/packet/diag.c index f6b200cb3c06..9a7980e3309d 100644 --- a/net/packet/diag.c +++ b/net/packet/diag.c @@ -262,4 +262,5 @@ static void __exit packet_diag_exit(void) module_init(packet_diag_init); module_exit(packet_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PACKET socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 17 /* AF_PACKET */); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 981ca5b98bcb..1d95f8bc769f 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -73,6 +73,7 @@ static void rxrpc_destroy_client_conn_ids(struct rxrpc_local *local) static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_call *call, gfp_t gfp) { + static atomic_t rxrpc_bundle_id; struct rxrpc_bundle *bundle; bundle = kzalloc(sizeof(*bundle), gfp); @@ -85,6 +86,7 @@ static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_call *call, bundle->upgrade = test_bit(RXRPC_CALL_UPGRADE, &call->flags); bundle->service_id = call->dest_srx.srx_service; bundle->security_level = call->security_level; + bundle->debug_id = atomic_inc_return(&rxrpc_bundle_id); refcount_set(&bundle->ref, 1); atomic_set(&bundle->active, 1); INIT_LIST_HEAD(&bundle->waiting_calls); @@ -105,7 +107,8 @@ struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *bundle, static void rxrpc_free_bundle(struct rxrpc_bundle *bundle) { - trace_rxrpc_bundle(bundle->debug_id, 1, rxrpc_bundle_free); + trace_rxrpc_bundle(bundle->debug_id, refcount_read(&bundle->ref), + rxrpc_bundle_free); rxrpc_put_peer(bundle->peer, rxrpc_peer_put_bundle); key_put(bundle->key); kfree(bundle); @@ -239,7 +242,6 @@ dont_reuse: */ int rxrpc_look_up_bundle(struct rxrpc_call *call, gfp_t gfp) { - static atomic_t rxrpc_bundle_id; struct rxrpc_bundle *bundle, *candidate; struct rxrpc_local *local = call->local; struct rb_node *p, **pp, *parent; @@ -306,7 +308,6 @@ int rxrpc_look_up_bundle(struct rxrpc_call *call, gfp_t gfp) } _debug("new bundle"); - candidate->debug_id = atomic_inc_return(&rxrpc_bundle_id); rb_link_node(&candidate->local_node, parent, pp); rb_insert_color(&candidate->local_node, &local->client_bundles); call->bundle = rxrpc_get_bundle(candidate, rxrpc_bundle_get_client_call); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 030d64f282f3..92495e73b869 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -643,12 +643,8 @@ static void rxrpc_complete_rtt_probe(struct rxrpc_call *call, clear_bit(i + RXRPC_CALL_RTT_PEND_SHIFT, &call->rtt_avail); smp_mb(); /* Read data before setting avail bit */ set_bit(i, &call->rtt_avail); - if (type != rxrpc_rtt_rx_cancel) - rxrpc_peer_add_rtt(call, type, i, acked_serial, ack_serial, - sent_at, resp_time); - else - trace_rxrpc_rtt_rx(call, rxrpc_rtt_rx_cancel, i, - orig_serial, acked_serial, 0, 0); + rxrpc_peer_add_rtt(call, type, i, acked_serial, ack_serial, + sent_at, resp_time); matched = true; } @@ -801,28 +797,21 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) summary.ack_reason, nr_acks); rxrpc_inc_stat(call->rxnet, stat_rx_acks[ack.reason]); - switch (ack.reason) { - case RXRPC_ACK_PING_RESPONSE: - rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, - rxrpc_rtt_rx_ping_response); - break; - case RXRPC_ACK_REQUESTED: - rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, - rxrpc_rtt_rx_requested_ack); - break; - default: - if (acked_serial != 0) + if (acked_serial != 0) { + switch (ack.reason) { + case RXRPC_ACK_PING_RESPONSE: rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, - rxrpc_rtt_rx_cancel); - break; - } - - if (ack.reason == RXRPC_ACK_PING) { - rxrpc_send_ACK(call, RXRPC_ACK_PING_RESPONSE, ack_serial, - rxrpc_propose_ack_respond_to_ping); - } else if (sp->hdr.flags & RXRPC_REQUEST_ACK) { - rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, ack_serial, - rxrpc_propose_ack_respond_to_ack); + rxrpc_rtt_rx_ping_response); + break; + case RXRPC_ACK_REQUESTED: + rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, + rxrpc_rtt_rx_requested_ack); + break; + default: + rxrpc_complete_rtt_probe(call, skb->tstamp, acked_serial, ack_serial, + rxrpc_rtt_rx_other_ack); + break; + } } /* If we get an EXCEEDS_WINDOW ACK from the server, it probably @@ -835,7 +824,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - return; + goto send_response; } /* If we get an OUT_OF_SEQUENCE ACK from the server, that can also @@ -849,7 +838,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_is_client_call(call)) { rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, 0, -ENETRESET); - return; + goto send_response; } /* Discard any out-of-order or duplicate ACKs (outside lock). */ @@ -857,7 +846,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) trace_rxrpc_rx_discard_ack(call->debug_id, ack_serial, first_soft_ack, call->acks_first_seq, prev_pkt, call->acks_prev_seq); - return; + goto send_response; } info.rxMTU = 0; @@ -897,7 +886,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) case RXRPC_CALL_SERVER_AWAIT_ACK: break; default: - return; + goto send_response; } if (before(hard_ack, call->acks_hard_ack) || @@ -909,7 +898,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) if (after(hard_ack, call->acks_hard_ack)) { if (rxrpc_rotate_tx_window(call, hard_ack, &summary)) { rxrpc_end_tx_phase(call, false, rxrpc_eproto_unexpected_ack); - return; + goto send_response; } } @@ -927,6 +916,14 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) rxrpc_propose_ack_ping_for_lost_reply); rxrpc_congestion_management(call, skb, &summary, acked_serial); + +send_response: + if (ack.reason == RXRPC_ACK_PING) + rxrpc_send_ACK(call, RXRPC_ACK_PING_RESPONSE, ack_serial, + rxrpc_propose_ack_respond_to_ping); + else if (sp->hdr.flags & RXRPC_REQUEST_ACK) + rxrpc_send_ACK(call, RXRPC_ACK_REQUESTED, ack_serial, + rxrpc_propose_ack_respond_to_ack); } /* diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 0db0ecf1d110..b3f4a503ee2b 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -1549,6 +1549,9 @@ static int tcf_ct_offload_act_setup(struct tc_action *act, void *entry_data, if (bind) { struct flow_action_entry *entry = entry_data; + if (tcf_ct_helper(act)) + return -EOPNOTSUPP; + entry->id = FLOW_ACTION_CT; entry->ct.action = tcf_ct_action(act); entry->ct.zone = tcf_ct_zone(act); diff --git a/net/sctp/diag.c b/net/sctp/diag.c index c3d6b92dd386..eb05131ff1dd 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -527,4 +527,5 @@ static void __exit sctp_diag_exit(void) module_init(sctp_diag_init); module_exit(sctp_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SCTP socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132); diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index da97f946b79b..2a1388841951 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -598,8 +598,12 @@ static int smcr_clnt_conf_first_link(struct smc_sock *smc) struct smc_llc_qentry *qentry; int rc; - /* receive CONFIRM LINK request from server over RoCE fabric */ - qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME, + /* Receive CONFIRM LINK request from server over RoCE fabric. + * Increasing the client's timeout by twice as much as the server's + * timeout by default can temporarily avoid decline messages of + * both sides crossing or colliding + */ + qentry = smc_llc_wait(link->lgr, NULL, 2 * SMC_LLC_WAIT_TIME, SMC_LLC_CONFIRM_LINK); if (!qentry) { struct smc_clc_msg_decline dclc; diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c index 7ff2152971a5..a584613aca12 100644 --- a/net/smc/smc_diag.c +++ b/net/smc/smc_diag.c @@ -268,5 +268,6 @@ static void __exit smc_diag_exit(void) module_init(smc_diag_init); module_exit(smc_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SMC socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 43 /* AF_SMC */); MODULE_ALIAS_GENL_FAMILY(SMCR_GENL_FAMILY_NAME); diff --git a/net/tipc/diag.c b/net/tipc/diag.c index 73137f4aeb68..18733451c9e0 100644 --- a/net/tipc/diag.c +++ b/net/tipc/diag.c @@ -113,4 +113,5 @@ module_init(tipc_diag_init); module_exit(tipc_diag_exit); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TIPC socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_TIPC); diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 5bc076f2fa74..c763008a8adb 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -102,6 +102,7 @@ static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len) return -EMSGSIZE; skb_put(skb, TLV_SPACE(len)); + memset(tlv, 0, TLV_SPACE(len)); tlv->tlv_type = htons(type); tlv->tlv_len = htons(TLV_LENGTH(len)); if (len && data) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index a78e8e722409..316f76187962 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1232,11 +1232,14 @@ void tls_sw_splice_eof(struct socket *sock) lock_sock(sk); retry: + /* same checks as in tls_sw_push_pending_record() */ rec = ctx->open_rec; if (!rec) goto unlock; msg_pl = &rec->msg_plaintext; + if (msg_pl->sg.size == 0) + goto unlock; /* Check the BPF advisor and perform transmission. */ ret = bpf_exec_tx_verdict(msg_pl, sk, false, TLS_RECORD_TYPE_DATA, diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 45506a95b25f..a357dc5f2404 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2581,15 +2581,16 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state) if (!(state->flags & MSG_PEEK)) WRITE_ONCE(u->oob_skb, NULL); - + else + skb_get(oob_skb); unix_state_unlock(sk); chunk = state->recv_actor(oob_skb, 0, chunk, state); - if (!(state->flags & MSG_PEEK)) { + if (!(state->flags & MSG_PEEK)) UNIXCB(oob_skb).consumed += 1; - kfree_skb(oob_skb); - } + + consume_skb(oob_skb); mutex_unlock(&u->iolock); diff --git a/net/unix/diag.c b/net/unix/diag.c index 616b55c5b890..bec09a3a1d44 100644 --- a/net/unix/diag.c +++ b/net/unix/diag.c @@ -339,4 +339,5 @@ static void __exit unix_diag_exit(void) module_init(unix_diag_init); module_exit(unix_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UNIX socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 1 /* AF_LOCAL */); diff --git a/net/vmw_vsock/diag.c b/net/vmw_vsock/diag.c index a2823b1c5e28..2e29994f92ff 100644 --- a/net/vmw_vsock/diag.c +++ b/net/vmw_vsock/diag.c @@ -174,5 +174,6 @@ static void __exit vsock_diag_exit(void) module_init(vsock_diag_init); module_exit(vsock_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VMware Virtual Sockets monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 40 /* AF_VSOCK */); diff --git a/net/xdp/xsk_diag.c b/net/xdp/xsk_diag.c index 22b36c8143cf..9f8955367275 100644 --- a/net/xdp/xsk_diag.c +++ b/net/xdp/xsk_diag.c @@ -211,4 +211,5 @@ static void __exit xsk_diag_exit(void) module_init(xsk_diag_init); module_exit(xsk_diag_exit); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("XDP socket monitoring via SOCK_DIAG"); MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_XDP); diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 68d0134bdbf9..1a965fe68e01 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -487,14 +487,14 @@ UIMAGE_OPTS-y ?= UIMAGE_TYPE ?= kernel UIMAGE_LOADADDR ?= arch_must_set_this UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR) -UIMAGE_NAME ?= 'Linux-$(KERNELRELEASE)' +UIMAGE_NAME ?= Linux-$(KERNELRELEASE) quiet_cmd_uimage = UIMAGE $@ cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \ -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \ -T $(UIMAGE_TYPE) \ -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ - -n $(UIMAGE_NAME) -d $< $@ + -n '$(UIMAGE_NAME)' -d $< $@ # XZ # --------------------------------------------------------------------------- diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index 84f5fb7f1cec..d83ba5d8f3f4 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -97,8 +97,7 @@ my (@stack, $re, $dre, $sub, $x, $xs, $funcre, $min_stack); # 11160: a7 fb ff 60 aghi %r15,-160 # or # 100092: e3 f0 ff c8 ff 71 lay %r15,-56(%r15) - $re = qr/.*(?:lay|ag?hi).*\%r15,-(([0-9]{2}|[3-9])[0-9]{2}) - (?:\(\%r15\))?$/ox; + $re = qr/.*(?:lay|ag?hi).*\%r15,-([0-9]+)(?:\(\%r15\))?$/o; } elsif ($arch eq 'sparc' || $arch eq 'sparc64') { # f0019d10: 9d e3 bf 90 save %sp, -112, %sp $re = qr/.*save.*%sp, -(([0-9]{2}|[3-9])[0-9]{2}), %sp/o; diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c index 39e86be60dd2..ff0b192be91f 100644 --- a/scripts/gcc-plugins/latent_entropy_plugin.c +++ b/scripts/gcc-plugins/latent_entropy_plugin.c @@ -17,7 +17,7 @@ * if (argc <= 1) * printf("%s: no command arguments :(\n", *argv); * else - * printf("%s: %d command arguments!\n", *argv, args - 1); + * printf("%s: %d command arguments!\n", *argv, argc - 1); * } * * after: @@ -47,7 +47,7 @@ * // perturb_local_entropy() * } else { * local_entropy ^= 3896280633962944730; - * printf("%s: %d command arguments!\n", *argv, args - 1); + * printf("%s: %d command arguments!\n", *argv, argc - 1); * } * * // latent_entropy_execute() 4. diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c index 366395cab490..910bd21d08f4 100644 --- a/scripts/gcc-plugins/randomize_layout_plugin.c +++ b/scripts/gcc-plugins/randomize_layout_plugin.c @@ -278,8 +278,6 @@ static bool is_flexible_array(const_tree field) { const_tree fieldtype; const_tree typesize; - const_tree elemtype; - const_tree elemsize; fieldtype = TREE_TYPE(field); typesize = TYPE_SIZE(fieldtype); @@ -287,20 +285,12 @@ static bool is_flexible_array(const_tree field) if (TREE_CODE(fieldtype) != ARRAY_TYPE) return false; - elemtype = TREE_TYPE(fieldtype); - elemsize = TYPE_SIZE(elemtype); - /* size of type is represented in bits */ if (typesize == NULL_TREE && TYPE_DOMAIN(fieldtype) != NULL_TREE && TYPE_MAX_VALUE(TYPE_DOMAIN(fieldtype)) == NULL_TREE) return true; - if (typesize != NULL_TREE && - (TREE_CONSTANT(typesize) && (!tree_to_uhwi(typesize) || - tree_to_uhwi(typesize) == tree_to_uhwi(elemsize)))) - return true; - return false; } diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 0572330bf8a7..a76925b46ce6 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -122,9 +122,9 @@ static long long sym_get_range_val(struct symbol *sym, int base) static void sym_validate_range(struct symbol *sym) { struct property *prop; + struct symbol *range_sym; int base; long long val, val2; - char str[64]; switch (sym->type) { case S_INT: @@ -140,17 +140,15 @@ static void sym_validate_range(struct symbol *sym) if (!prop) return; val = strtoll(sym->curr.val, NULL, base); - val2 = sym_get_range_val(prop->expr->left.sym, base); + range_sym = prop->expr->left.sym; + val2 = sym_get_range_val(range_sym, base); if (val >= val2) { - val2 = sym_get_range_val(prop->expr->right.sym, base); + range_sym = prop->expr->right.sym; + val2 = sym_get_range_val(range_sym, base); if (val <= val2) return; } - if (sym->type == S_INT) - sprintf(str, "%lld", val2); - else - sprintf(str, "0x%llx", val2); - sym->curr.val = xstrdup(str); + sym->curr.val = range_sym->curr.val; } static void sym_set_changed(struct symbol *sym) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 973b5e5ae2dd..cb6406f485a9 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1383,13 +1383,15 @@ static void section_rela(struct module *mod, struct elf_info *elf, const Elf_Rela *rela; for (rela = start; rela < stop; rela++) { + Elf_Sym *tsym; Elf_Addr taddr, r_offset; unsigned int r_type, r_sym; r_offset = TO_NATIVE(rela->r_offset); get_rel_type_and_sym(elf, rela->r_info, &r_type, &r_sym); - taddr = TO_NATIVE(rela->r_addend); + tsym = elf->symtab_start + r_sym; + taddr = tsym->st_value + TO_NATIVE(rela->r_addend); switch (elf->hdr->e_machine) { case EM_RISCV: @@ -1404,7 +1406,7 @@ static void section_rela(struct module *mod, struct elf_info *elf, break; } - check_section_mismatch(mod, elf, elf->symtab_start + r_sym, + check_section_mismatch(mod, elf, tsym, fsecndx, fromsec, r_offset, taddr); } } diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index 07075071972d..1cdca4d4fc9c 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -6,6 +6,7 @@ #include <linux/export.h> #include <linux/hdmi.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <sound/pcm.h> #include <sound/pcm_drm_eld.h> diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c index 757a4d193e0f..a9ef6d86de83 100644 --- a/sound/pci/hda/cs35l56_hda_i2c.c +++ b/sound/pci/hda/cs35l56_hda_i2c.c @@ -21,6 +21,10 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) return -ENOMEM; cs35l56->base.dev = &clt->dev; + +#ifdef CS35L56_WAKE_HOLD_TIME_US + cs35l56->base.can_hibernate = true; +#endif cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 03264915c618..db90feb49c16 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2135,6 +2135,9 @@ static int azx_probe(struct pci_dev *pci, if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) { err = snd_hdac_i915_init(azx_bus(chip)); if (err < 0) { + if (err == -EPROBE_DEFER) + goto out_free; + /* if the controller is bound only with HDMI/DP * (for HSW and BDW), we need to abort the probe; * for other chips, still continue probing as other diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 669ae3d6e447..5618b1d9bfd1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9832,6 +9832,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9867,6 +9868,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), @@ -9900,12 +9902,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), @@ -9944,13 +9950,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS), SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JI", ALC285_FIXUP_ASUS_HEADSET_MIC), @@ -10821,22 +10831,6 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x17, 0x90170110}, {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x21, 0x04211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, - {0x14, 0x90170110}, - {0x21, 0x04211030}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC295_STANDARD_PINS, - {0x17, 0x21014020}, - {0x18, 0x21a19030}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC295_STANDARD_PINS, - {0x17, 0x21014040}, - {0x18, 0x21a19050}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, - ALC295_STANDARD_PINS), SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170110}), @@ -10880,6 +10874,9 @@ static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1b, 0x40000000}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + {0x19, 0x40000000}, + {0x1b, 0x40000000}), SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x19, 0x40000000}, {0x1a, 0x40000000}), diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index b9c5ffbfb5ba..a188a89e40d9 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -16,6 +16,7 @@ #include <linux/pm_runtime.h> #include <linux/hdmi.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <sound/pcm_params.h> #include <sound/jack.h> #include <sound/soc.h> diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 20da1eaa4f1c..a7bd1796aed6 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -17,6 +17,7 @@ #include <sound/pcm_iec958.h> #include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */ +#include <drm/drm_eld.h> #define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index ab95fb34a635..02f5a7f9b728 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -30,6 +30,7 @@ #include <sound/control.h> #include <sound/jack.h> #include <drm/drm_edid.h> +#include <drm/drm_eld.h> #include <drm/intel_lpe_audio.h> #include "intel_hdmi_audio.h" diff --git a/tools/arch/parisc/include/uapi/asm/errno.h b/tools/arch/parisc/include/uapi/asm/errno.h index 87245c584784..8d94739d75c6 100644 --- a/tools/arch/parisc/include/uapi/asm/errno.h +++ b/tools/arch/parisc/include/uapi/asm/errno.h @@ -75,7 +75,6 @@ /* We now return you to your regularly scheduled HPUX. */ -#define ENOSYM 215 /* symbol does not exist in executable */ #define ENOTSOCK 216 /* Socket operation on non-socket */ #define EDESTADDRREQ 217 /* Destination address required */ #define EMSGSIZE 218 /* Message too long */ @@ -101,7 +100,6 @@ #define ETIMEDOUT 238 /* Connection timed out */ #define ECONNREFUSED 239 /* Connection refused */ #define EREFUSED ECONNREFUSED /* for HP's NFS apparently */ -#define EREMOTERELEASE 240 /* Remote peer released connection */ #define EHOSTDOWN 241 /* Host is down */ #define EHOSTUNREACH 242 /* No route to host */ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 264eeb9c46a9..318e2dad27e0 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1421,7 +1421,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) if (error) goto setval_error; - if (new_val->addr_family == ADDR_FAMILY_IPV6) { + if (new_val->addr_family & ADDR_FAMILY_IPV6) { error = fprintf(nmfile, "\n[ipv6]\n"); if (error < 0) goto setval_error; @@ -1455,14 +1455,18 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) if (error < 0) goto setval_error; - error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); - if (error < 0) - goto setval_error; - - error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); - if (error < 0) - goto setval_error; + /* we do not want ipv4 addresses in ipv6 section and vice versa */ + if (is_ipv6 != is_ipv4((char *)new_val->gate_way)) { + error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); + if (error < 0) + goto setval_error; + } + if (is_ipv6 != is_ipv4((char *)new_val->dns_addr)) { + error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); + if (error < 0) + goto setval_error; + } fclose(nmfile); fclose(ifcfg_file); diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh index ae5a7a8249a2..440a91b35823 100755 --- a/tools/hv/hv_set_ifconfig.sh +++ b/tools/hv/hv_set_ifconfig.sh @@ -53,7 +53,7 @@ # or "manual" if no boot-time protocol should be used) # # address1=ipaddr1/plen -# address=ipaddr2/plen +# address2=ipaddr2/plen # # gateway=gateway1;gateway2 # @@ -61,7 +61,7 @@ # # [ipv6] # address1=ipaddr1/plen -# address2=ipaddr1/plen +# address2=ipaddr2/plen # # gateway=gateway1;gateway2 # diff --git a/tools/net/ynl/Makefile.deps b/tools/net/ynl/Makefile.deps index 64d139400db1..3110f84dd029 100644 --- a/tools/net/ynl/Makefile.deps +++ b/tools/net/ynl/Makefile.deps @@ -18,4 +18,4 @@ CFLAGS_devlink:=$(call get_hdr_inc,_LINUX_DEVLINK_H_,devlink.h) CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_H_,ethtool_netlink.h) CFLAGS_handshake:=$(call get_hdr_inc,_LINUX_HANDSHAKE_H,handshake.h) CFLAGS_netdev:=$(call get_hdr_inc,_LINUX_NETDEV_H,netdev.h) -CFLAGS_nfsd:=$(call get_hdr_inc,_LINUX_NFSD_H,nfsd.h) +CFLAGS_nfsd:=$(call get_hdr_inc,_LINUX_NFSD_NETLINK_H,nfsd_netlink.h) diff --git a/tools/net/ynl/generated/devlink-user.c b/tools/net/ynl/generated/devlink-user.c index bc5065bd99b2..c12ca87ca2bb 100644 --- a/tools/net/ynl/generated/devlink-user.c +++ b/tools/net/ynl/generated/devlink-user.c @@ -15,7 +15,7 @@ /* Enums */ static const char * const devlink_op_strmap[] = { [3] = "get", - [7] = "port-get", + // skip "port-get", duplicate reply value [DEVLINK_CMD_PORT_NEW] = "port-new", [13] = "sb-get", [17] = "sb-pool-get", diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py index c4003a83cd5d..3bd6b928c14f 100755 --- a/tools/net/ynl/ynl-gen-c.py +++ b/tools/net/ynl/ynl-gen-c.py @@ -1505,6 +1505,12 @@ def put_op_name(family, cw): cw.block_start(line=f"static const char * const {map_name}[] =") for op_name, op in family.msgs.items(): if op.rsp_value: + # Make sure we don't add duplicated entries, if multiple commands + # produce the same response in legacy families. + if family.rsp_by_value[op.rsp_value] != op: + cw.p(f'// skip "{op_name}", duplicate reply value') + continue + if op.req_value == op.rsp_value: cw.p(f'[{op.enum_name}] = "{op_name}",') else: diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py index 4a356a706785..40ad221e8881 100755 --- a/tools/power/pm-graph/sleepgraph.py +++ b/tools/power/pm-graph/sleepgraph.py @@ -4151,7 +4151,7 @@ def parseKernelLog(data): elif(re.match('Enabling non-boot CPUs .*', msg)): # start of first cpu resume cpu_start = ktime - elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg)) \ + elif(re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg) \ or re.match('psci: CPU(?P<cpu>[0-9]*) killed.*', msg)): # end of a cpu suspend, start of the next m = re.match('smpboot: CPU (?P<cpu>[0-9]*) is now offline', msg) diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 9a10512e3407..7a334377f92b 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -211,9 +211,6 @@ int *fd_instr_count_percpu; struct timeval interval_tv = { 5, 0 }; struct timespec interval_ts = { 5, 0 }; -/* Save original CPU model */ -unsigned int model_orig; - unsigned int num_iterations; unsigned int header_iterations; unsigned int debug; @@ -224,24 +221,16 @@ unsigned int rapl_joules; unsigned int summary_only; unsigned int list_header_only; unsigned int dump_only; -unsigned int do_snb_cstates; -unsigned int do_knl_cstates; -unsigned int do_slm_cstates; -unsigned int use_c1_residency_msr; unsigned int has_aperf; unsigned int has_epb; unsigned int has_turbo; unsigned int is_hybrid; -unsigned int do_irtl_snb; -unsigned int do_irtl_hsw; unsigned int units = 1000000; /* MHz etc */ unsigned int genuine_intel; unsigned int authentic_amd; unsigned int hygon_genuine; unsigned int max_level, max_extended_level; unsigned int has_invariant_tsc; -unsigned int do_nhm_platform_info; -unsigned int no_MSR_MISC_PWR_MGMT; unsigned int aperf_mperf_multiplier = 1; double bclk; double base_hz; @@ -250,7 +239,6 @@ double tsc_tweak = 1.0; unsigned int show_pkg_only; unsigned int show_core_only; char *output_buffer, *outp; -unsigned int do_rapl; unsigned int do_dts; unsigned int do_ptm; unsigned int do_ipc; @@ -261,65 +249,686 @@ unsigned int gfx_cur_mhz; unsigned int gfx_act_mhz; unsigned int tj_max; unsigned int tj_max_override; -int tcc_offset_bits; double rapl_power_units, rapl_time_units; double rapl_dram_energy_units, rapl_energy_units; double rapl_joule_counter_range; -unsigned int do_core_perf_limit_reasons; -unsigned int has_automatic_cstate_conversion; -unsigned int dis_cstate_prewake; -unsigned int do_gfx_perf_limit_reasons; -unsigned int do_ring_perf_limit_reasons; unsigned int crystal_hz; unsigned long long tsc_hz; int base_cpu; -double discover_bclk(unsigned int family, unsigned int model); unsigned int has_hwp; /* IA32_PM_ENABLE, IA32_HWP_CAPABILITIES */ /* IA32_HWP_REQUEST, IA32_HWP_STATUS */ unsigned int has_hwp_notify; /* IA32_HWP_INTERRUPT */ unsigned int has_hwp_activity_window; /* IA32_HWP_REQUEST[bits 41:32] */ unsigned int has_hwp_epp; /* IA32_HWP_REQUEST[bits 31:24] */ unsigned int has_hwp_pkg; /* IA32_HWP_REQUEST_PKG */ -unsigned int has_misc_feature_control; unsigned int first_counter_read = 1; int ignore_stdin; -#define RAPL_PKG (1 << 0) - /* 0x610 MSR_PKG_POWER_LIMIT */ - /* 0x611 MSR_PKG_ENERGY_STATUS */ -#define RAPL_PKG_PERF_STATUS (1 << 1) - /* 0x613 MSR_PKG_PERF_STATUS */ -#define RAPL_PKG_POWER_INFO (1 << 2) - /* 0x614 MSR_PKG_POWER_INFO */ - -#define RAPL_DRAM (1 << 3) - /* 0x618 MSR_DRAM_POWER_LIMIT */ - /* 0x619 MSR_DRAM_ENERGY_STATUS */ -#define RAPL_DRAM_PERF_STATUS (1 << 4) - /* 0x61b MSR_DRAM_PERF_STATUS */ -#define RAPL_DRAM_POWER_INFO (1 << 5) - /* 0x61c MSR_DRAM_POWER_INFO */ - -#define RAPL_CORES_POWER_LIMIT (1 << 6) - /* 0x638 MSR_PP0_POWER_LIMIT */ -#define RAPL_CORE_POLICY (1 << 7) - /* 0x63a MSR_PP0_POLICY */ - -#define RAPL_GFX (1 << 8) - /* 0x640 MSR_PP1_POWER_LIMIT */ - /* 0x641 MSR_PP1_ENERGY_STATUS */ - /* 0x642 MSR_PP1_POLICY */ - -#define RAPL_CORES_ENERGY_STATUS (1 << 9) - /* 0x639 MSR_PP0_ENERGY_STATUS */ -#define RAPL_PER_CORE_ENERGY (1 << 10) - /* Indicates cores energy collection is per-core, - * not per-package. */ -#define RAPL_AMD_F17H (1 << 11) - /* 0xc0010299 MSR_RAPL_PWR_UNIT */ - /* 0xc001029a MSR_CORE_ENERGY_STAT */ - /* 0xc001029b MSR_PKG_ENERGY_STAT */ -#define RAPL_CORES (RAPL_CORES_ENERGY_STATUS | RAPL_CORES_POWER_LIMIT) +int get_msr(int cpu, off_t offset, unsigned long long *msr); + +/* Model specific support Start */ + +/* List of features that may diverge among different platforms */ +struct platform_features { + bool has_msr_misc_feature_control; /* MSR_MISC_FEATURE_CONTROL */ + bool has_msr_misc_pwr_mgmt; /* MSR_MISC_PWR_MGMT */ + bool has_nhm_msrs; /* MSR_PLATFORM_INFO, MSR_IA32_TEMPERATURE_TARGET, MSR_SMI_COUNT, MSR_PKG_CST_CONFIG_CONTROL, MSR_IA32_POWER_CTL, TRL MSRs */ + bool has_config_tdp; /* MSR_CONFIG_TDP_NOMINAL/LEVEL_1/LEVEL_2/CONTROL, MSR_TURBO_ACTIVATION_RATIO */ + int bclk_freq; /* CPU base clock */ + int crystal_freq; /* Crystal clock to use when not available from CPUID.15 */ + int supported_cstates; /* Core cstates and Package cstates supported */ + int cst_limit; /* MSR_PKG_CST_CONFIG_CONTROL */ + bool has_cst_auto_convension; /* AUTOMATIC_CSTATE_CONVERSION bit in MSR_PKG_CST_CONFIG_CONTROL */ + bool has_irtl_msrs; /* MSR_PKGC3/PKGC6/PKGC7/PKGC8/PKGC9/PKGC10_IRTL */ + bool has_msr_core_c1_res; /* MSR_CORE_C1_RES */ + bool has_msr_module_c6_res_ms; /* MSR_MODULE_C6_RES_MS */ + bool has_msr_c6_demotion_policy_config; /* MSR_CC6_DEMOTION_POLICY_CONFIG/MSR_MC6_DEMOTION_POLICY_CONFIG */ + bool has_msr_atom_pkg_c6_residency; /* MSR_ATOM_PKG_C6_RESIDENCY */ + bool has_msr_knl_core_c6_residency; /* MSR_KNL_CORE_C6_RESIDENCY */ + bool has_ext_cst_msrs; /* MSR_PKG_WEIGHTED_CORE_C0_RES/MSR_PKG_ANY_CORE_C0_RES/MSR_PKG_ANY_GFXE_C0_RES/MSR_PKG_BOTH_CORE_GFXE_C0_RES */ + bool has_cst_prewake_bit; /* Cstate prewake bit in MSR_IA32_POWER_CTL */ + int trl_msrs; /* MSR_TURBO_RATIO_LIMIT/LIMIT1/LIMIT2/SECONDARY, Atom TRL MSRs */ + int plr_msrs; /* MSR_CORE/GFX/RING_PERF_LIMIT_REASONS */ + int rapl_msrs; /* RAPL PKG/DRAM/CORE/GFX MSRs, AMD RAPL MSRs */ + bool has_per_core_rapl; /* Indicates cores energy collection is per-core, not per-package. AMD specific for now */ + bool has_rapl_divisor; /* Divisor for Energy unit raw value from MSR_RAPL_POWER_UNIT */ + bool has_fixed_rapl_unit; /* Fixed Energy Unit used for DRAM RAPL Domain */ + int rapl_quirk_tdp; /* Hardcoded TDP value when cannot be retrieved from hardware */ + int tcc_offset_bits; /* TCC Offset bits in MSR_IA32_TEMPERATURE_TARGET */ + bool enable_tsc_tweak; /* Use CPU Base freq instead of TSC freq for aperf/mperf counter */ + bool need_perf_multiplier; /* mperf/aperf multiplier */ +}; + +struct platform_data { + unsigned int model; + const struct platform_features *features; +}; + +/* For BCLK */ +enum bclk_freq { + BCLK_100MHZ = 1, + BCLK_133MHZ, + BCLK_SLV, +}; + +#define SLM_BCLK_FREQS 5 +double slm_freq_table[SLM_BCLK_FREQS] = { 83.3, 100.0, 133.3, 116.7, 80.0 }; + +double slm_bclk(void) +{ + unsigned long long msr = 3; + unsigned int i; + double freq; + + if (get_msr(base_cpu, MSR_FSB_FREQ, &msr)) + fprintf(outf, "SLM BCLK: unknown\n"); + + i = msr & 0xf; + if (i >= SLM_BCLK_FREQS) { + fprintf(outf, "SLM BCLK[%d] invalid\n", i); + i = 3; + } + freq = slm_freq_table[i]; + + if (!quiet) + fprintf(outf, "SLM BCLK: %.1f Mhz\n", freq); + + return freq; +} + +/* For Package cstate limit */ +enum package_cstate_limit { + CST_LIMIT_NHM = 1, + CST_LIMIT_SNB, + CST_LIMIT_HSW, + CST_LIMIT_SKX, + CST_LIMIT_ICX, + CST_LIMIT_SLV, + CST_LIMIT_AMT, + CST_LIMIT_KNL, + CST_LIMIT_GMT, +}; + +/* For Turbo Ratio Limit MSRs */ +enum turbo_ratio_limit_msrs { + TRL_BASE = BIT(0), + TRL_LIMIT1 = BIT(1), + TRL_LIMIT2 = BIT(2), + TRL_ATOM = BIT(3), + TRL_KNL = BIT(4), + TRL_CORECOUNT = BIT(5), +}; + +/* For Perf Limit Reason MSRs */ +enum perf_limit_reason_msrs { + PLR_CORE = BIT(0), + PLR_GFX = BIT(1), + PLR_RING = BIT(2), +}; + +/* For RAPL MSRs */ +enum rapl_msrs { + RAPL_PKG_POWER_LIMIT = BIT(0), /* 0x610 MSR_PKG_POWER_LIMIT */ + RAPL_PKG_ENERGY_STATUS = BIT(1), /* 0x611 MSR_PKG_ENERGY_STATUS */ + RAPL_PKG_PERF_STATUS = BIT(2), /* 0x613 MSR_PKG_PERF_STATUS */ + RAPL_PKG_POWER_INFO = BIT(3), /* 0x614 MSR_PKG_POWER_INFO */ + RAPL_DRAM_POWER_LIMIT = BIT(4), /* 0x618 MSR_DRAM_POWER_LIMIT */ + RAPL_DRAM_ENERGY_STATUS = BIT(5), /* 0x619 MSR_DRAM_ENERGY_STATUS */ + RAPL_DRAM_PERF_STATUS = BIT(6), /* 0x61b MSR_DRAM_PERF_STATUS */ + RAPL_DRAM_POWER_INFO = BIT(7), /* 0x61c MSR_DRAM_POWER_INFO */ + RAPL_CORE_POWER_LIMIT = BIT(8), /* 0x638 MSR_PP0_POWER_LIMIT */ + RAPL_CORE_ENERGY_STATUS = BIT(9), /* 0x639 MSR_PP0_ENERGY_STATUS */ + RAPL_CORE_POLICY = BIT(10), /* 0x63a MSR_PP0_POLICY */ + RAPL_GFX_POWER_LIMIT = BIT(11), /* 0x640 MSR_PP1_POWER_LIMIT */ + RAPL_GFX_ENERGY_STATUS = BIT(12), /* 0x641 MSR_PP1_ENERGY_STATUS */ + RAPL_GFX_POLICY = BIT(13), /* 0x642 MSR_PP1_POLICY */ + RAPL_AMD_PWR_UNIT = BIT(14), /* 0xc0010299 MSR_AMD_RAPL_POWER_UNIT */ + RAPL_AMD_CORE_ENERGY_STAT = BIT(15), /* 0xc001029a MSR_AMD_CORE_ENERGY_STATUS */ + RAPL_AMD_PKG_ENERGY_STAT = BIT(16), /* 0xc001029b MSR_AMD_PKG_ENERGY_STATUS */ +}; + +#define RAPL_PKG (RAPL_PKG_ENERGY_STATUS | RAPL_PKG_POWER_LIMIT) +#define RAPL_DRAM (RAPL_DRAM_ENERGY_STATUS | RAPL_DRAM_POWER_LIMIT) +#define RAPL_CORE (RAPL_CORE_ENERGY_STATUS | RAPL_CORE_POWER_LIMIT) +#define RAPL_GFX (RAPL_GFX_POWER_LIMIT | RAPL_GFX_ENERGY_STATUS) + +#define RAPL_PKG_ALL (RAPL_PKG | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO) +#define RAPL_DRAM_ALL (RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_DRAM_POWER_INFO) +#define RAPL_CORE_ALL (RAPL_CORE | RAPL_CORE_POLICY) +#define RAPL_GFX_ALL (RAPL_GFX | RAPL_GFX_POLIGY) + +#define RAPL_AMD_F17H (RAPL_AMD_PWR_UNIT | RAPL_AMD_CORE_ENERGY_STAT | RAPL_AMD_PKG_ENERGY_STAT) + +/* For Cstates */ +enum cstates { + CC1 = BIT(0), + CC3 = BIT(1), + CC6 = BIT(2), + CC7 = BIT(3), + PC2 = BIT(4), + PC3 = BIT(5), + PC6 = BIT(6), + PC7 = BIT(7), + PC8 = BIT(8), + PC9 = BIT(9), + PC10 = BIT(10), +}; + +static const struct platform_features nhm_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_133MHZ, + .supported_cstates = CC1 | CC3 | CC6 | PC3 | PC6, + .cst_limit = CST_LIMIT_NHM, + .trl_msrs = TRL_BASE, +}; + +static const struct platform_features nhx_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_133MHZ, + .supported_cstates = CC1 | CC3 | CC6 | PC3 | PC6, + .cst_limit = CST_LIMIT_NHM, +}; + +static const struct platform_features snb_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features snx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, +}; + +static const struct platform_features ivb_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features ivx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, +}; + +static const struct platform_features hsw_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features hsx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1 | TRL_LIMIT2, + .plr_msrs = PLR_CORE | PLR_RING, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, +}; + +static const struct platform_features hswl_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features hswg_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features bdw_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features bdwg_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features bdx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC3 | CC6 | PC2 | PC3 | PC6, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, +}; + +static const struct platform_features skl_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .crystal_freq = 24000000, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, +}; + +static const struct platform_features cnl_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .has_msr_core_c1_res = 1, + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, +}; + +static const struct platform_features adl_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC3 | PC6 | PC8 | PC10, + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .has_msr_core_c1_res = 1, + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, +}; + +static const struct platform_features skx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | PC2 | PC6, + .cst_limit = CST_LIMIT_SKX, + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, +}; + +static const struct platform_features icx_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | PC2 | PC6, + .cst_limit = CST_LIMIT_ICX, + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, +}; + +static const struct platform_features spr_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | PC2 | PC6, + .cst_limit = CST_LIMIT_SKX, + .has_msr_core_c1_res = 1, + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, +}; + +static const struct platform_features srf_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | PC2 | PC6, + .cst_limit = CST_LIMIT_SKX, + .has_msr_core_c1_res = 1, + .has_msr_module_c6_res_ms = 1, + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, +}; + +static const struct platform_features grr_features = { + .has_msr_misc_feature_control = 1, + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6, + .cst_limit = CST_LIMIT_SKX, + .has_msr_core_c1_res = 1, + .has_msr_module_c6_res_ms = 1, + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, +}; + +static const struct platform_features slv_features = { + .has_nhm_msrs = 1, + .bclk_freq = BCLK_SLV, + .supported_cstates = CC1 | CC6 | PC6, + .cst_limit = CST_LIMIT_SLV, + .has_msr_core_c1_res = 1, + .has_msr_module_c6_res_ms = 1, + .has_msr_c6_demotion_policy_config = 1, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_ATOM, + .rapl_msrs = RAPL_PKG | RAPL_CORE, + .has_rapl_divisor = 1, + .rapl_quirk_tdp = 30, +}; + +static const struct platform_features slvd_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_SLV, + .supported_cstates = CC1 | CC6 | PC3 | PC6, + .cst_limit = CST_LIMIT_SLV, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_CORE, + .rapl_quirk_tdp = 30, +}; + +static const struct platform_features amt_features = { + .has_nhm_msrs = 1, + .bclk_freq = BCLK_133MHZ, + .supported_cstates = CC1 | CC3 | CC6 | PC3 | PC6, + .cst_limit = CST_LIMIT_AMT, + .trl_msrs = TRL_BASE, +}; + +static const struct platform_features gmt_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .crystal_freq = 19200000, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features gmtd_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .crystal_freq = 25000000, + .supported_cstates = CC1 | CC6 | PC2 | PC6, + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .has_msr_core_c1_res = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_CORE_ENERGY_STATUS, +}; + +static const struct platform_features gmtp_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .crystal_freq = 19200000, + .supported_cstates = CC1 | CC3 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, +}; + +static const struct platform_features tmt_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | CC7 | PC2 | PC3 | PC6 | PC7 | PC8 | PC9 | PC10, + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, +}; + +static const struct platform_features tmtd_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6, + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, + .rapl_msrs = RAPL_PKG_ALL, +}; + +static const struct platform_features knl_features = { + .has_msr_misc_pwr_mgmt = 1, + .has_nhm_msrs = 1, + .has_config_tdp = 1, + .bclk_freq = BCLK_100MHZ, + .supported_cstates = CC1 | CC6 | PC3 | PC6, + .cst_limit = CST_LIMIT_KNL, + .has_msr_knl_core_c6_residency = 1, + .trl_msrs = TRL_KNL, + .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + .need_perf_multiplier = 1, +}; + +static const struct platform_features default_features = { +}; + +static const struct platform_features amd_features_with_rapl = { + .rapl_msrs = RAPL_AMD_F17H, + .has_per_core_rapl = 1, + .rapl_quirk_tdp = 280, /* This is the max stock TDP of HEDT/Server Fam17h+ chips */ +}; + +static const struct platform_data turbostat_pdata[] = { + { INTEL_FAM6_NEHALEM, &nhm_features }, + { INTEL_FAM6_NEHALEM_G, &nhm_features }, + { INTEL_FAM6_NEHALEM_EP, &nhm_features }, + { INTEL_FAM6_NEHALEM_EX, &nhx_features }, + { INTEL_FAM6_WESTMERE, &nhm_features }, + { INTEL_FAM6_WESTMERE_EP, &nhm_features }, + { INTEL_FAM6_WESTMERE_EX, &nhx_features }, + { INTEL_FAM6_SANDYBRIDGE, &snb_features }, + { INTEL_FAM6_SANDYBRIDGE_X, &snx_features }, + { INTEL_FAM6_IVYBRIDGE, &ivb_features }, + { INTEL_FAM6_IVYBRIDGE_X, &ivx_features }, + { INTEL_FAM6_HASWELL, &hsw_features }, + { INTEL_FAM6_HASWELL_X, &hsx_features }, + { INTEL_FAM6_HASWELL_L, &hswl_features }, + { INTEL_FAM6_HASWELL_G, &hswg_features }, + { INTEL_FAM6_BROADWELL, &bdw_features }, + { INTEL_FAM6_BROADWELL_G, &bdwg_features }, + { INTEL_FAM6_BROADWELL_X, &bdx_features }, + { INTEL_FAM6_BROADWELL_D, &bdx_features }, + { INTEL_FAM6_SKYLAKE_L, &skl_features }, + { INTEL_FAM6_SKYLAKE, &skl_features }, + { INTEL_FAM6_SKYLAKE_X, &skx_features }, + { INTEL_FAM6_KABYLAKE_L, &skl_features }, + { INTEL_FAM6_KABYLAKE, &skl_features }, + { INTEL_FAM6_COMETLAKE, &skl_features }, + { INTEL_FAM6_COMETLAKE_L, &skl_features }, + { INTEL_FAM6_CANNONLAKE_L, &cnl_features }, + { INTEL_FAM6_ICELAKE_X, &icx_features }, + { INTEL_FAM6_ICELAKE_D, &icx_features }, + { INTEL_FAM6_ICELAKE_L, &cnl_features }, + { INTEL_FAM6_ICELAKE_NNPI, &cnl_features }, + { INTEL_FAM6_ROCKETLAKE, &cnl_features }, + { INTEL_FAM6_TIGERLAKE_L, &cnl_features }, + { INTEL_FAM6_TIGERLAKE, &cnl_features }, + { INTEL_FAM6_SAPPHIRERAPIDS_X, &spr_features }, + { INTEL_FAM6_EMERALDRAPIDS_X, &spr_features }, + { INTEL_FAM6_GRANITERAPIDS_X, &spr_features }, + { INTEL_FAM6_LAKEFIELD, &cnl_features }, + { INTEL_FAM6_ALDERLAKE, &adl_features }, + { INTEL_FAM6_ALDERLAKE_L, &adl_features }, + { INTEL_FAM6_RAPTORLAKE, &adl_features }, + { INTEL_FAM6_RAPTORLAKE_P, &adl_features }, + { INTEL_FAM6_RAPTORLAKE_S, &adl_features }, + { INTEL_FAM6_METEORLAKE, &cnl_features }, + { INTEL_FAM6_METEORLAKE_L, &cnl_features }, + { INTEL_FAM6_ARROWLAKE, &cnl_features }, + { INTEL_FAM6_LUNARLAKE_M, &cnl_features }, + { INTEL_FAM6_ATOM_SILVERMONT, &slv_features }, + { INTEL_FAM6_ATOM_SILVERMONT_D, &slvd_features }, + { INTEL_FAM6_ATOM_AIRMONT, &amt_features }, + { INTEL_FAM6_ATOM_GOLDMONT, &gmt_features }, + { INTEL_FAM6_ATOM_GOLDMONT_D, &gmtd_features }, + { INTEL_FAM6_ATOM_GOLDMONT_PLUS, &gmtp_features }, + { INTEL_FAM6_ATOM_TREMONT_D, &tmtd_features }, + { INTEL_FAM6_ATOM_TREMONT, &tmt_features }, + { INTEL_FAM6_ATOM_TREMONT_L, &tmt_features }, + { INTEL_FAM6_ATOM_GRACEMONT, &adl_features }, + { INTEL_FAM6_ATOM_CRESTMONT_X, &srf_features }, + { INTEL_FAM6_ATOM_CRESTMONT, &grr_features }, + { INTEL_FAM6_XEON_PHI_KNL, &knl_features }, + { INTEL_FAM6_XEON_PHI_KNM, &knl_features }, + /* + * Missing support for + * INTEL_FAM6_ICELAKE + * INTEL_FAM6_ATOM_SILVERMONT_MID + * INTEL_FAM6_ATOM_AIRMONT_MID + * INTEL_FAM6_ATOM_AIRMONT_NP + */ + { 0, NULL }, +}; + +static const struct platform_features *platform; + +void probe_platform_features(unsigned int family, unsigned int model) +{ + int i; + + platform = &default_features; + + if (authentic_amd || hygon_genuine) { + if (max_extended_level >= 0x80000007) { + unsigned int eax, ebx, ecx, edx; + + __cpuid(0x80000007, eax, ebx, ecx, edx); + /* RAPL (Fam 17h+) */ + if ((edx & (1 << 14)) && family >= 0x17) + platform = &amd_features_with_rapl; + } + return; + } + + if (!genuine_intel || family != 6) + return; + + for (i = 0; turbostat_pdata[i].features; i++) { + if (turbostat_pdata[i].model == model) { + platform = turbostat_pdata[i].features; + return; + } + } +} + +/* Model specific support End */ + #define TJMAX_DEFAULT 100 /* MSRs that are not yet in the kernel-provided header. */ @@ -333,8 +942,8 @@ int backwards_count; char *progname; #define CPU_SUBSET_MAXCPUS 1024 /* need to use before probe... */ -cpu_set_t *cpu_present_set, *cpu_affinity_set, *cpu_subset; -size_t cpu_present_setsize, cpu_affinity_setsize, cpu_subset_size; +cpu_set_t *cpu_present_set, *cpu_effective_set, *cpu_allowed_set, *cpu_affinity_set, *cpu_subset; +size_t cpu_present_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, cpu_subset_size; #define MAX_ADDED_COUNTERS 8 #define MAX_ADDED_THREAD_COUNTERS 24 #define BITMASK_SIZE 32 @@ -355,12 +964,11 @@ struct thread_data { unsigned int x2apic_id; unsigned int flags; bool is_atom; -#define CPU_IS_FIRST_THREAD_IN_CORE 0x2 -#define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4 unsigned long long counter[MAX_ADDED_THREAD_COUNTERS]; } *thread_even, *thread_odd; struct core_data { + int base_cpu; unsigned long long c3; unsigned long long c6; unsigned long long c7; @@ -373,6 +981,7 @@ struct core_data { } *core_even, *core_odd; struct pkg_data { + int base_cpu; unsigned long long pc2; unsigned long long pc3; unsigned long long pc6; @@ -456,7 +1065,7 @@ off_t idx_to_offset(int idx) switch (idx) { case IDX_PKG_ENERGY: - if (do_rapl & RAPL_AMD_F17H) + if (platform->rapl_msrs & RAPL_AMD_F17H) offset = MSR_PKG_ENERGY_STAT; else offset = MSR_PKG_ENERGY_STATUS; @@ -516,17 +1125,17 @@ int idx_valid(int idx) { switch (idx) { case IDX_PKG_ENERGY: - return do_rapl & (RAPL_PKG | RAPL_AMD_F17H); + return platform->rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H); case IDX_DRAM_ENERGY: - return do_rapl & RAPL_DRAM; + return platform->rapl_msrs & RAPL_DRAM; case IDX_PP0_ENERGY: - return do_rapl & RAPL_CORES_ENERGY_STATUS; + return platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS; case IDX_PP1_ENERGY: - return do_rapl & RAPL_GFX; + return platform->rapl_msrs & RAPL_GFX; case IDX_PKG_PERF: - return do_rapl & RAPL_PKG_PERF_STATUS; + return platform->rapl_msrs & RAPL_PKG_PERF_STATUS; case IDX_DRAM_PERF: - return do_rapl & RAPL_DRAM_PERF_STATUS; + return platform->rapl_msrs & RAPL_DRAM_PERF_STATUS; default: return 0; } @@ -563,6 +1172,9 @@ struct topo_params { int num_die; int num_cpus; int num_cores; + int allowed_packages; + int allowed_cpus; + int allowed_cores; int max_cpu_num; int max_node_num; int nodes_per_pkg; @@ -575,7 +1187,7 @@ struct timeval tv_even, tv_odd, tv_delta; int *irq_column_2_cpu; /* /proc/interrupts column numbers */ int *irqs_per_cpu; /* indexed by cpu_num */ -void setup_all_buffers(void); +void setup_all_buffers(bool startup); char *sys_lpi_file; char *sys_lpi_file_sysfs = "/sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us"; @@ -586,6 +1198,11 @@ int cpu_is_not_present(int cpu) return !CPU_ISSET_S(cpu, cpu_present_setsize, cpu_present_set); } +int cpu_is_not_allowed(int cpu) +{ + return !CPU_ISSET_S(cpu, cpu_allowed_setsize, cpu_allowed_set); +} + /* * run func(thread, core, package) in topology order * skip non-present cpus @@ -603,10 +1220,9 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk struct thread_data *t; struct core_data *c; struct pkg_data *p; - t = GET_THREAD(thread_base, thread_no, core_no, node_no, pkg_no); - if (cpu_is_not_present(t->cpu_id)) + if (cpu_is_not_allowed(t->cpu_id)) continue; c = GET_CORE(core_base, core_no, node_no, pkg_no); @@ -622,6 +1238,25 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk return 0; } +int is_cpu_first_thread_in_core(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + UNUSED(p); + + return ((int)t->cpu_id == c->base_cpu || c->base_cpu < 0); +} + +int is_cpu_first_core_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + UNUSED(c); + + return ((int)t->cpu_id == p->base_cpu || p->base_cpu < 0); +} + +int is_cpu_first_thread_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + return is_cpu_first_thread_in_core(t, c, p) && is_cpu_first_core_in_package(t, c, p); +} + int cpu_migrate(int cpu) { CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set); @@ -904,11 +1539,11 @@ void print_header(char *delim) if (DO_BIC(BIC_CORE_THROT_CNT)) outp += sprintf(outp, "%sCoreThr", (printed++ ? delim : "")); - if (do_rapl && !rapl_joules) { - if (DO_BIC(BIC_CorWatt) && (do_rapl & RAPL_PER_CORE_ENERGY)) + if (platform->rapl_msrs && !rapl_joules) { + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) outp += sprintf(outp, "%sCorWatt", (printed++ ? delim : "")); - } else if (do_rapl && rapl_joules) { - if (DO_BIC(BIC_Cor_J) && (do_rapl & RAPL_PER_CORE_ENERGY)) + } else if (platform->rapl_msrs && rapl_joules) { + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); } @@ -966,10 +1601,10 @@ void print_header(char *delim) if (DO_BIC(BIC_SYS_LPI)) outp += sprintf(outp, "%sSYS%%LPI", (printed++ ? delim : "")); - if (do_rapl && !rapl_joules) { + if (platform->rapl_msrs && !rapl_joules) { if (DO_BIC(BIC_PkgWatt)) outp += sprintf(outp, "%sPkgWatt", (printed++ ? delim : "")); - if (DO_BIC(BIC_CorWatt) && !(do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_CorWatt) && !platform->has_per_core_rapl) outp += sprintf(outp, "%sCorWatt", (printed++ ? delim : "")); if (DO_BIC(BIC_GFXWatt)) outp += sprintf(outp, "%sGFXWatt", (printed++ ? delim : "")); @@ -979,10 +1614,10 @@ void print_header(char *delim) outp += sprintf(outp, "%sPKG_%%", (printed++ ? delim : "")); if (DO_BIC(BIC_RAM__)) outp += sprintf(outp, "%sRAM_%%", (printed++ ? delim : "")); - } else if (do_rapl && rapl_joules) { + } else if (platform->rapl_msrs && rapl_joules) { if (DO_BIC(BIC_Pkg_J)) outp += sprintf(outp, "%sPkg_J", (printed++ ? delim : "")); - if (DO_BIC(BIC_Cor_J) && !(do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_Cor_J) && !platform->has_per_core_rapl) outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); if (DO_BIC(BIC_GFX_J)) outp += sprintf(outp, "%sGFX_J", (printed++ ? delim : "")); @@ -1106,11 +1741,11 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data int printed = 0; /* if showing only 1st thread in core and this isn't one, bail out */ - if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) + if (show_core_only && !is_cpu_first_thread_in_core(t, c, p)) return 0; /* if showing only 1st thread in pkg and this isn't one, bail out */ - if (show_pkg_only && !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (show_pkg_only && !is_cpu_first_core_in_package(t, c, p)) return 0; /*if not summary line and --cpu is used */ @@ -1244,7 +1879,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc); /* print per-core data only for 1st thread in core */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) + if (!is_cpu_first_thread_in_core(t, c, p)) goto done; if (DO_BIC(BIC_CPU_c3)) @@ -1284,14 +1919,14 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data fmt8 = "%s%.2f"; - if (DO_BIC(BIC_CorWatt) && (do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), c->core_energy * rapl_energy_units / interval_float); - if (DO_BIC(BIC_Cor_J) && (do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), c->core_energy * rapl_energy_units); /* print per-package data only for 1st core in package */ - if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_core_in_package(t, c, p)) goto done; /* PkgTmp */ @@ -1352,7 +1987,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data outp += sprintf(outp, fmt8, (printed++ ? delim : ""), p->energy_pkg * rapl_energy_units / interval_float); - if (DO_BIC(BIC_CorWatt) && !(do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_CorWatt) && !platform->has_per_core_rapl) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), p->energy_cores * rapl_energy_units / interval_float); if (DO_BIC(BIC_GFXWatt)) @@ -1364,7 +1999,7 @@ int format_counters(struct thread_data *t, struct core_data *c, struct pkg_data p->energy_dram * rapl_dram_energy_units / interval_float); if (DO_BIC(BIC_Pkg_J)) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), p->energy_pkg * rapl_energy_units); - if (DO_BIC(BIC_Cor_J) && !(do_rapl & RAPL_PER_CORE_ENERGY)) + if (DO_BIC(BIC_Cor_J) && !platform->has_per_core_rapl) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), p->energy_cores * rapl_energy_units); if (DO_BIC(BIC_GFX_J)) outp += sprintf(outp, fmt8, (printed++ ? delim : ""), p->energy_gfx * rapl_energy_units); @@ -1527,7 +2162,7 @@ void delta_core(struct core_data *new, struct core_data *old) int soft_c1_residency_display(int bic) { - if (!DO_BIC(BIC_CPU_c1) || use_c1_residency_msr) + if (!DO_BIC(BIC_CPU_c1) || platform->has_msr_core_c1_res) return 0; return DO_BIC_READ(bic); @@ -1567,7 +2202,8 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d old->c1 = new->c1 - old->c1; - if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) || soft_c1_residency_display(BIC_Avg_MHz)) { + if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) || DO_BIC(BIC_IPC) + || soft_c1_residency_display(BIC_Avg_MHz)) { if ((new->aperf > old->aperf) && (new->mperf > old->mperf)) { old->aperf = new->aperf - old->aperf; old->mperf = new->mperf - old->mperf; @@ -1576,7 +2212,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d } } - if (use_c1_residency_msr) { + if (platform->has_msr_core_c1_res) { /* * Some models have a dedicated C1 residency MSR, * which should be more accurate than the derivation below. @@ -1626,7 +2262,7 @@ int delta_cpu(struct thread_data *t, struct core_data *c, int retval = 0; /* calculate core delta only for 1st thread in core */ - if (t->flags & CPU_IS_FIRST_THREAD_IN_CORE) + if (is_cpu_first_thread_in_core(t, c, p)) delta_core(c, c2); /* always calculate thread delta */ @@ -1635,7 +2271,7 @@ int delta_cpu(struct thread_data *t, struct core_data *c, return retval; /* calculate package delta only for 1st core in package */ - if (t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE) + if (is_cpu_first_core_in_package(t, c, p)) retval = delta_package(p, p2); return retval; @@ -1663,9 +2299,6 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data t->irq_count = 0; t->smi_count = 0; - /* tells format_counters to dump all fields from this set */ - t->flags = CPU_IS_FIRST_THREAD_IN_CORE | CPU_IS_FIRST_CORE_IN_PACKAGE; - c->c3 = 0; c->c6 = 0; c->c7 = 0; @@ -1749,7 +2382,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) } /* sum per-core values only for 1st thread in core */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) + if (!is_cpu_first_thread_in_core(t, c, p)) return 0; average.cores.c3 += c->c3; @@ -1769,7 +2402,7 @@ int sum_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) } /* sum per-pkg values only for 1st core in pkg */ - if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_core_in_package(t, c, p)) return 0; if (DO_BIC(BIC_Totl_c0)) @@ -1834,40 +2467,40 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data /* Use the global time delta for the average. */ average.threads.tv_delta = tv_delta; - average.threads.tsc /= topo.num_cpus; - average.threads.aperf /= topo.num_cpus; - average.threads.mperf /= topo.num_cpus; - average.threads.instr_count /= topo.num_cpus; - average.threads.c1 /= topo.num_cpus; + average.threads.tsc /= topo.allowed_cpus; + average.threads.aperf /= topo.allowed_cpus; + average.threads.mperf /= topo.allowed_cpus; + average.threads.instr_count /= topo.allowed_cpus; + average.threads.c1 /= topo.allowed_cpus; if (average.threads.irq_count > 9999999) sums_need_wide_columns = 1; - average.cores.c3 /= topo.num_cores; - average.cores.c6 /= topo.num_cores; - average.cores.c7 /= topo.num_cores; - average.cores.mc6_us /= topo.num_cores; + average.cores.c3 /= topo.allowed_cores; + average.cores.c6 /= topo.allowed_cores; + average.cores.c7 /= topo.allowed_cores; + average.cores.mc6_us /= topo.allowed_cores; if (DO_BIC(BIC_Totl_c0)) - average.packages.pkg_wtd_core_c0 /= topo.num_packages; + average.packages.pkg_wtd_core_c0 /= topo.allowed_packages; if (DO_BIC(BIC_Any_c0)) - average.packages.pkg_any_core_c0 /= topo.num_packages; + average.packages.pkg_any_core_c0 /= topo.allowed_packages; if (DO_BIC(BIC_GFX_c0)) - average.packages.pkg_any_gfxe_c0 /= topo.num_packages; + average.packages.pkg_any_gfxe_c0 /= topo.allowed_packages; if (DO_BIC(BIC_CPUGFX)) - average.packages.pkg_both_core_gfxe_c0 /= topo.num_packages; + average.packages.pkg_both_core_gfxe_c0 /= topo.allowed_packages; - average.packages.pc2 /= topo.num_packages; + average.packages.pc2 /= topo.allowed_packages; if (DO_BIC(BIC_Pkgpc3)) - average.packages.pc3 /= topo.num_packages; + average.packages.pc3 /= topo.allowed_packages; if (DO_BIC(BIC_Pkgpc6)) - average.packages.pc6 /= topo.num_packages; + average.packages.pc6 /= topo.allowed_packages; if (DO_BIC(BIC_Pkgpc7)) - average.packages.pc7 /= topo.num_packages; + average.packages.pc7 /= topo.allowed_packages; - average.packages.pc8 /= topo.num_packages; - average.packages.pc9 /= topo.num_packages; - average.packages.pc10 /= topo.num_packages; + average.packages.pc8 /= topo.allowed_packages; + average.packages.pc9 /= topo.allowed_packages; + average.packages.pc10 /= topo.allowed_packages; for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { if (mp->format == FORMAT_RAW) @@ -1877,7 +2510,7 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data sums_need_wide_columns = 1; continue; } - average.threads.counter[i] /= topo.num_cpus; + average.threads.counter[i] /= topo.allowed_cpus; } for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { if (mp->format == FORMAT_RAW) @@ -1886,7 +2519,7 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data if (average.cores.counter[i] > 9999999) sums_need_wide_columns = 1; } - average.cores.counter[i] /= topo.num_cores; + average.cores.counter[i] /= topo.allowed_cores; } for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { if (mp->format == FORMAT_RAW) @@ -1895,7 +2528,7 @@ void compute_average(struct thread_data *t, struct core_data *c, struct pkg_data if (average.packages.counter[i] > 9999999) sums_need_wide_columns = 1; } - average.packages.counter[i] /= topo.num_packages; + average.packages.counter[i] /= topo.allowed_packages; } } @@ -2092,7 +2725,8 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) retry: t->tsc = rdtsc(); /* we are running on local CPU of interest */ - if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) || soft_c1_residency_display(BIC_Avg_MHz)) { + if (DO_BIC(BIC_Avg_MHz) || DO_BIC(BIC_Busy) || DO_BIC(BIC_Bzy_MHz) || DO_BIC(BIC_IPC) + || soft_c1_residency_display(BIC_Avg_MHz)) { unsigned long long tsc_before, tsc_between, tsc_after, aperf_time, mperf_time; /* @@ -2158,7 +2792,7 @@ retry: return -5; t->smi_count = msr & 0xFFFFFFFF; } - if (DO_BIC(BIC_CPU_c1) && use_c1_residency_msr) { + if (DO_BIC(BIC_CPU_c1) && platform->has_msr_core_c1_res) { if (get_msr(cpu, MSR_CORE_C1_RES, &t->c1)) return -6; } @@ -2169,7 +2803,7 @@ retry: } /* collect core counters only for 1st thread in core */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) + if (!is_cpu_first_thread_in_core(t, c, p)) goto done; if (DO_BIC(BIC_CPU_c3) || soft_c1_residency_display(BIC_CPU_c3)) { @@ -2177,10 +2811,10 @@ retry: return -6; } - if ((DO_BIC(BIC_CPU_c6) || soft_c1_residency_display(BIC_CPU_c6)) && !do_knl_cstates) { + if ((DO_BIC(BIC_CPU_c6) || soft_c1_residency_display(BIC_CPU_c6)) && !platform->has_msr_knl_core_c6_residency) { if (get_msr(cpu, MSR_CORE_C6_RESIDENCY, &c->c6)) return -7; - } else if (do_knl_cstates || soft_c1_residency_display(BIC_CPU_c6)) { + } else if (platform->has_msr_knl_core_c6_residency && soft_c1_residency_display(BIC_CPU_c6)) { if (get_msr(cpu, MSR_KNL_CORE_C6_RESIDENCY, &c->c6)) return -7; } @@ -2212,7 +2846,7 @@ retry: if (DO_BIC(BIC_CORE_THROT_CNT)) get_core_throt_cnt(cpu, &c->core_throt_cnt); - if (do_rapl & RAPL_AMD_F17H) { + if (platform->rapl_msrs & RAPL_AMD_F17H) { if (get_msr(cpu, MSR_CORE_ENERGY_STAT, &msr)) return -14; c->core_energy = msr & 0xFFFFFFFF; @@ -2224,7 +2858,7 @@ retry: } /* collect package counters only for 1st core in package */ - if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_core_in_package(t, c, p)) goto done; if (DO_BIC(BIC_Totl_c0)) { @@ -2247,7 +2881,7 @@ retry: if (get_msr(cpu, MSR_PKG_C3_RESIDENCY, &p->pc3)) return -9; if (DO_BIC(BIC_Pkgpc6)) { - if (do_slm_cstates) { + if (platform->has_msr_atom_pkg_c6_residency) { if (get_msr(cpu, MSR_ATOM_PKG_C6_RESIDENCY, &p->pc6)) return -10; } else { @@ -2277,37 +2911,37 @@ retry: if (DO_BIC(BIC_SYS_LPI)) p->sys_lpi = cpuidle_cur_sys_lpi_us; - if (do_rapl & RAPL_PKG) { + if (platform->rapl_msrs & RAPL_PKG) { if (get_msr_sum(cpu, MSR_PKG_ENERGY_STATUS, &msr)) return -13; p->energy_pkg = msr; } - if (do_rapl & RAPL_CORES_ENERGY_STATUS) { + if (platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS) { if (get_msr_sum(cpu, MSR_PP0_ENERGY_STATUS, &msr)) return -14; p->energy_cores = msr; } - if (do_rapl & RAPL_DRAM) { + if (platform->rapl_msrs & RAPL_DRAM) { if (get_msr_sum(cpu, MSR_DRAM_ENERGY_STATUS, &msr)) return -15; p->energy_dram = msr; } - if (do_rapl & RAPL_GFX) { + if (platform->rapl_msrs & RAPL_GFX) { if (get_msr_sum(cpu, MSR_PP1_ENERGY_STATUS, &msr)) return -16; p->energy_gfx = msr; } - if (do_rapl & RAPL_PKG_PERF_STATUS) { + if (platform->rapl_msrs & RAPL_PKG_PERF_STATUS) { if (get_msr_sum(cpu, MSR_PKG_PERF_STATUS, &msr)) return -16; p->rapl_pkg_perf_status = msr; } - if (do_rapl & RAPL_DRAM_PERF_STATUS) { + if (platform->rapl_msrs & RAPL_DRAM_PERF_STATUS) { if (get_msr_sum(cpu, MSR_DRAM_PERF_STATUS, &msr)) return -16; p->rapl_dram_perf_status = msr; } - if (do_rapl & RAPL_AMD_F17H) { + if (platform->rapl_msrs & RAPL_AMD_F17H) { if (get_msr_sum(cpu, MSR_PKG_ENERGY_STAT, &msr)) return -13; p->energy_pkg = msr; @@ -2414,18 +3048,58 @@ int icx_pkg_cstate_limits[16] = PCLRSV, PCLRSV }; -static void calculate_tsc_tweak() +void probe_cst_limit(void) { - tsc_tweak = base_hz / tsc_hz; -} + unsigned long long msr; + int *pkg_cstate_limits; + + if (!platform->has_nhm_msrs) + return; + + switch (platform->cst_limit) { + case CST_LIMIT_NHM: + pkg_cstate_limits = nhm_pkg_cstate_limits; + break; + case CST_LIMIT_SNB: + pkg_cstate_limits = snb_pkg_cstate_limits; + break; + case CST_LIMIT_HSW: + pkg_cstate_limits = hsw_pkg_cstate_limits; + break; + case CST_LIMIT_SKX: + pkg_cstate_limits = skx_pkg_cstate_limits; + break; + case CST_LIMIT_ICX: + pkg_cstate_limits = icx_pkg_cstate_limits; + break; + case CST_LIMIT_SLV: + pkg_cstate_limits = slv_pkg_cstate_limits; + break; + case CST_LIMIT_AMT: + pkg_cstate_limits = amt_pkg_cstate_limits; + break; + case CST_LIMIT_KNL: + pkg_cstate_limits = phi_pkg_cstate_limits; + break; + case CST_LIMIT_GMT: + pkg_cstate_limits = glm_pkg_cstate_limits; + break; + default: + return; + } -void prewake_cstate_probe(unsigned int family, unsigned int model); + get_msr(base_cpu, MSR_PKG_CST_CONFIG_CONTROL, &msr); + pkg_cstate_limit = pkg_cstate_limits[msr & 0xF]; +} -static void dump_nhm_platform_info(void) +static void dump_platform_info(void) { unsigned long long msr; unsigned int ratio; + if (!platform->has_nhm_msrs) + return; + get_msr(base_cpu, MSR_PLATFORM_INFO, &msr); fprintf(outf, "cpu%d: MSR_PLATFORM_INFO: 0x%08llx\n", base_cpu, msr); @@ -2435,19 +3109,27 @@ static void dump_nhm_platform_info(void) ratio = (msr >> 8) & 0xFF; fprintf(outf, "%d * %.1f = %.1f MHz base frequency\n", ratio, bclk, ratio * bclk); +} + +static void dump_power_ctl(void) +{ + unsigned long long msr; + + if (!platform->has_nhm_msrs) + return; get_msr(base_cpu, MSR_IA32_POWER_CTL, &msr); fprintf(outf, "cpu%d: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n", base_cpu, msr, msr & 0x2 ? "EN" : "DIS"); /* C-state Pre-wake Disable (CSTATE_PREWAKE_DISABLE) */ - if (dis_cstate_prewake) + if (platform->has_cst_prewake_bit) fprintf(outf, "C-state Pre-wake: %sabled\n", msr & 0x40000000 ? "DIS" : "EN"); return; } -static void dump_hsw_turbo_ratio_limits(void) +static void dump_turbo_ratio_limit2(void) { unsigned long long msr; unsigned int ratio; @@ -2466,7 +3148,7 @@ static void dump_hsw_turbo_ratio_limits(void) return; } -static void dump_ivt_turbo_ratio_limits(void) +static void dump_turbo_ratio_limit1(void) { unsigned long long msr; unsigned int ratio; @@ -2509,29 +3191,7 @@ static void dump_ivt_turbo_ratio_limits(void) return; } -int has_turbo_ratio_group_limits(int family, int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_GOLDMONT: - case INTEL_FAM6_SKYLAKE_X: - case INTEL_FAM6_ICELAKE_X: - case INTEL_FAM6_SAPPHIRERAPIDS_X: - case INTEL_FAM6_ATOM_GOLDMONT_D: - case INTEL_FAM6_ATOM_TREMONT_D: - return 1; - default: - return 0; - } -} - -static void dump_turbo_ratio_limits(int trl_msr_offset, int family, int model) +static void dump_turbo_ratio_limits(int trl_msr_offset) { unsigned long long msr, core_counts; int shift; @@ -2540,7 +3200,7 @@ static void dump_turbo_ratio_limits(int trl_msr_offset, int family, int model) fprintf(outf, "cpu%d: MSR_%sTURBO_RATIO_LIMIT: 0x%08llx\n", base_cpu, trl_msr_offset == MSR_SECONDARY_TURBO_RATIO_LIMIT ? "SECONDARY_" : "", msr); - if (has_turbo_ratio_group_limits(family, model)) { + if (platform->trl_msrs & TRL_CORECOUNT) { get_msr(base_cpu, MSR_TURBO_RATIO_LIMIT1, &core_counts); fprintf(outf, "cpu%d: MSR_TURBO_RATIO_LIMIT1: 0x%08llx\n", base_cpu, core_counts); } else { @@ -2657,10 +3317,13 @@ static void dump_knl_turbo_ratio_limits(void) ratio[i], bclk, ratio[i] * bclk, cores[i]); } -static void dump_nhm_cst_cfg(void) +static void dump_cst_cfg(void) { unsigned long long msr; + if (!platform->has_nhm_msrs) + return; + get_msr(base_cpu, MSR_PKG_CST_CONFIG_CONTROL, &msr); fprintf(outf, "cpu%d: MSR_PKG_CST_CONFIG_CONTROL: 0x%08llx", base_cpu, msr); @@ -2673,7 +3336,7 @@ static void dump_nhm_cst_cfg(void) (msr & (1 << 15)) ? "" : "UN", (unsigned int)msr & 0xF, pkg_cstate_limit_strings[pkg_cstate_limit]); #define AUTOMATIC_CSTATE_CONVERSION (1UL << 16) - if (has_automatic_cstate_conversion) { + if (platform->has_cst_auto_convension) { fprintf(outf, ", automatic c-state conversion=%s", (msr & AUTOMATIC_CSTATE_CONVERSION) ? "on" : "off"); } @@ -2730,39 +3393,50 @@ void print_irtl(void) { unsigned long long msr; - get_msr(base_cpu, MSR_PKGC3_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC3_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); - - get_msr(base_cpu, MSR_PKGC6_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC6_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + if (!platform->has_irtl_msrs) + return; - get_msr(base_cpu, MSR_PKGC7_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC7_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + if (platform->supported_cstates & PC3) { + get_msr(base_cpu, MSR_PKGC3_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC3_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } - if (!do_irtl_hsw) - return; + if (platform->supported_cstates & PC6) { + get_msr(base_cpu, MSR_PKGC6_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC6_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } - get_msr(base_cpu, MSR_PKGC8_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC8_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + if (platform->supported_cstates & PC7) { + get_msr(base_cpu, MSR_PKGC7_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC7_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } - get_msr(base_cpu, MSR_PKGC9_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC9_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + if (platform->supported_cstates & PC8) { + get_msr(base_cpu, MSR_PKGC8_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC8_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } - get_msr(base_cpu, MSR_PKGC10_IRTL, &msr); - fprintf(outf, "cpu%d: MSR_PKGC10_IRTL: 0x%08llx (", base_cpu, msr); - fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", - (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + if (platform->supported_cstates & PC9) { + get_msr(base_cpu, MSR_PKGC9_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC9_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + if (platform->supported_cstates & PC10) { + get_msr(base_cpu, MSR_PKGC10_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC10_IRTL: 0x%08llx (", base_cpu, msr); + fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", + (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } } void free_fd_percpu(void) @@ -2785,6 +3459,14 @@ void free_all_buffers(void) cpu_present_set = NULL; cpu_present_setsize = 0; + CPU_FREE(cpu_effective_set); + cpu_effective_set = NULL; + cpu_effective_setsize = 0; + + CPU_FREE(cpu_allowed_set); + cpu_allowed_set = NULL; + cpu_allowed_setsize = 0; + CPU_FREE(cpu_affinity_set); cpu_affinity_set = NULL; cpu_affinity_setsize = 0; @@ -2927,6 +3609,59 @@ int get_physical_node_id(struct cpu_topology *thiscpu) return -1; } +static int parse_cpu_str(char *cpu_str, cpu_set_t *cpu_set, int cpu_set_size) +{ + unsigned int start, end; + char *next = cpu_str; + + while (next && *next) { + + if (*next == '-') /* no negative cpu numbers */ + return 1; + + start = strtoul(next, &next, 10); + + if (start >= CPU_SUBSET_MAXCPUS) + return 1; + CPU_SET_S(start, cpu_set_size, cpu_set); + + if (*next == '\0' || *next == '\n') + break; + + if (*next == ',') { + next += 1; + continue; + } + + if (*next == '-') { + next += 1; /* start range */ + } else if (*next == '.') { + next += 1; + if (*next == '.') + next += 1; /* start range */ + else + return 1; + } + + end = strtoul(next, &next, 10); + if (end <= start) + return 1; + + while (++start <= end) { + if (start >= CPU_SUBSET_MAXCPUS) + return 1; + CPU_SET_S(start, cpu_set_size, cpu_set); + } + + if (*next == ',') + next += 1; + else if (*next != '\0' && *next != '\n') + return 1; + } + + return 0; +} + int get_thread_siblings(struct cpu_topology *thiscpu) { char path[80], character; @@ -2998,7 +3733,7 @@ int for_all_cpus_2(int (func) (struct thread_data *, struct core_data *, t = GET_THREAD(thread_base, thread_no, core_no, node_no, pkg_no); - if (cpu_is_not_present(t->cpu_id)) + if (cpu_is_not_allowed(t->cpu_id)) continue; t2 = GET_THREAD(thread_base2, thread_no, core_no, node_no, pkg_no); @@ -3050,11 +3785,51 @@ int for_all_proc_cpus(int (func) (int)) return 0; } +#define PATH_EFFECTIVE_CPUS "/sys/fs/cgroup/cpuset.cpus.effective" + +static char cpu_effective_str[1024]; + +static int update_effective_str(bool startup) +{ + FILE *fp; + char *pos; + char buf[1024]; + int ret; + + if (cpu_effective_str[0] == '\0' && !startup) + return 0; + + fp = fopen(PATH_EFFECTIVE_CPUS, "r"); + if (!fp) + return 0; + + pos = fgets(buf, 1024, fp); + if (!pos) + err(1, "%s: file read failed\n", PATH_EFFECTIVE_CPUS); + + fclose(fp); + + ret = strncmp(cpu_effective_str, buf, 1024); + if (!ret) + return 0; + + strncpy(cpu_effective_str, buf, 1024); + return 1; +} + +static void update_effective_set(bool startup) +{ + update_effective_str(startup); + + if (parse_cpu_str(cpu_effective_str, cpu_effective_set, cpu_effective_setsize)) + err(1, "%s: cpu str malformat %s\n", PATH_EFFECTIVE_CPUS, cpu_effective_str); +} + void re_initialize(void) { free_all_buffers(); - setup_all_buffers(); - fprintf(outf, "turbostat: re-initialized with num_cpus %d\n", topo.num_cpus); + setup_all_buffers(false); + fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, topo.allowed_cpus); } void set_max_cpu_num(void) @@ -3191,8 +3966,8 @@ int snapshot_gfx_rc6_ms(void) /* * snapshot_gfx_mhz() * - * record snapshot of - * /sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz + * fall back to /sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz + * when /sys/class/drm/card0/gt_cur_freq_mhz is not available. * * return 1 if config change requires a restart, else return 0 */ @@ -3201,9 +3976,11 @@ int snapshot_gfx_mhz(void) static FILE *fp; int retval; - if (fp == NULL) - fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", "r"); - else { + if (fp == NULL) { + fp = fopen("/sys/class/drm/card0/gt_cur_freq_mhz", "r"); + if (!fp) + fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", "r"); + } else { rewind(fp); fflush(fp); } @@ -3218,8 +3995,8 @@ int snapshot_gfx_mhz(void) /* * snapshot_gfx_cur_mhz() * - * record snapshot of - * /sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz + * fall back to /sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz + * when /sys/class/drm/card0/gt_act_freq_mhz is not available. * * return 1 if config change requires a restart, else return 0 */ @@ -3228,9 +4005,11 @@ int snapshot_gfx_act_mhz(void) static FILE *fp; int retval; - if (fp == NULL) - fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", "r"); - else { + if (fp == NULL) { + fp = fopen("/sys/class/drm/card0/gt_act_freq_mhz", "r"); + if (!fp) + fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", "r"); + } else { rewind(fp); fflush(fp); } @@ -3562,6 +4341,10 @@ restart: re_initialize(); goto restart; } + if (update_effective_str(false)) { + re_initialize(); + goto restart; + } do_sleep(); if (snapshot_proc_sysfs_files()) goto restart; @@ -3674,395 +4457,31 @@ void check_permissions(void) exit(-6); } -/* - * NHM adds support for additional MSRs: - * - * MSR_SMI_COUNT 0x00000034 - * - * MSR_PLATFORM_INFO 0x000000ce - * MSR_PKG_CST_CONFIG_CONTROL 0x000000e2 - * - * MSR_MISC_PWR_MGMT 0x000001aa - * - * MSR_PKG_C3_RESIDENCY 0x000003f8 - * MSR_PKG_C6_RESIDENCY 0x000003f9 - * MSR_CORE_C3_RESIDENCY 0x000003fc - * MSR_CORE_C6_RESIDENCY 0x000003fd - * - * Side effect: - * sets global pkg_cstate_limit to decode MSR_PKG_CST_CONFIG_CONTROL - * sets has_misc_feature_control - */ -int probe_nhm_msrs(unsigned int family, unsigned int model) +void probe_bclk(void) { unsigned long long msr; unsigned int base_ratio; - int *pkg_cstate_limits; - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - bclk = discover_bclk(family, model); + if (!platform->has_nhm_msrs) + return; - switch (model) { - case INTEL_FAM6_NEHALEM: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ - case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */ - pkg_cstate_limits = nhm_pkg_cstate_limits; - break; - case INTEL_FAM6_SANDYBRIDGE: /* SNB */ - case INTEL_FAM6_SANDYBRIDGE_X: /* SNB Xeon */ - case INTEL_FAM6_IVYBRIDGE: /* IVB */ - case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */ - pkg_cstate_limits = snb_pkg_cstate_limits; - has_misc_feature_control = 1; - break; - case INTEL_FAM6_HASWELL: /* HSW */ - case INTEL_FAM6_HASWELL_G: /* HSW */ - case INTEL_FAM6_HASWELL_X: /* HSX */ - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_BROADWELL: /* BDW */ - case INTEL_FAM6_BROADWELL_G: /* BDW */ - case INTEL_FAM6_BROADWELL_X: /* BDX */ - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - pkg_cstate_limits = hsw_pkg_cstate_limits; - has_misc_feature_control = 1; - break; - case INTEL_FAM6_SKYLAKE_X: /* SKX */ - case INTEL_FAM6_SAPPHIRERAPIDS_X: /* SPR */ - pkg_cstate_limits = skx_pkg_cstate_limits; - has_misc_feature_control = 1; - break; - case INTEL_FAM6_ICELAKE_X: /* ICX */ - pkg_cstate_limits = icx_pkg_cstate_limits; - has_misc_feature_control = 1; - break; - case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */ - no_MSR_MISC_PWR_MGMT = 1; - /* FALLTHRU */ - case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */ - pkg_cstate_limits = slv_pkg_cstate_limits; - break; - case INTEL_FAM6_ATOM_AIRMONT: /* AMT */ - pkg_cstate_limits = amt_pkg_cstate_limits; - no_MSR_MISC_PWR_MGMT = 1; - break; - case INTEL_FAM6_XEON_PHI_KNL: /* PHI */ - pkg_cstate_limits = phi_pkg_cstate_limits; - break; - case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ - case INTEL_FAM6_ATOM_GOLDMONT_PLUS: - case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */ - case INTEL_FAM6_ATOM_TREMONT: /* EHL */ - case INTEL_FAM6_ATOM_TREMONT_D: /* JVL */ - pkg_cstate_limits = glm_pkg_cstate_limits; - break; - default: - return 0; - } - get_msr(base_cpu, MSR_PKG_CST_CONFIG_CONTROL, &msr); - pkg_cstate_limit = pkg_cstate_limits[msr & 0xF]; + if (platform->bclk_freq == BCLK_100MHZ) + bclk = 100.00; + else if (platform->bclk_freq == BCLK_133MHZ) + bclk = 133.33; + else if (platform->bclk_freq == BCLK_SLV) + bclk = slm_bclk(); + else + return; get_msr(base_cpu, MSR_PLATFORM_INFO, &msr); base_ratio = (msr >> 8) & 0xFF; base_hz = base_ratio * bclk * 1000000; has_base_hz = 1; - return 1; -} - -/* - * SLV client has support for unique MSRs: - * - * MSR_CC6_DEMOTION_POLICY_CONFIG - * MSR_MC6_DEMOTION_POLICY_CONFIG - */ - -int has_slv_msrs(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_SILVERMONT: - case INTEL_FAM6_ATOM_SILVERMONT_MID: - case INTEL_FAM6_ATOM_AIRMONT_MID: - return 1; - } - return 0; -} - -int is_dnv(unsigned int family, unsigned int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_GOLDMONT_D: - return 1; - } - return 0; -} - -int is_bdx(unsigned int family, unsigned int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_BROADWELL_X: - return 1; - } - return 0; -} - -int is_skx(unsigned int family, unsigned int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_SKYLAKE_X: - return 1; - } - return 0; -} - -int is_icx(unsigned int family, unsigned int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ICELAKE_X: - return 1; - } - return 0; -} - -int is_spr(unsigned int family, unsigned int model) -{ - - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_SAPPHIRERAPIDS_X: - return 1; - } - return 0; -} - -int is_ehl(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_TREMONT: - return 1; - } - return 0; -} - -int is_jvl(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_TREMONT_D: - return 1; - } - return 0; -} - -int has_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (has_slv_msrs(family, model)) - return 0; - - if (family != 6) - return 0; - - switch (model) { - /* Nehalem compatible, but do not include turbo-ratio limit support */ - case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */ - case INTEL_FAM6_XEON_PHI_KNL: /* PHI - Knights Landing (different MSR definition) */ - return 0; - default: - return 1; - } -} - -int has_atom_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (has_slv_msrs(family, model)) - return 1; - - return 0; -} - -int has_ivt_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */ - case INTEL_FAM6_HASWELL_X: /* HSW Xeon */ - return 1; - default: - return 0; - } -} - -int has_hsw_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_HASWELL_X: /* HSW Xeon */ - return 1; - default: - return 0; - } -} - -int has_knl_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */ - return 1; - default: - return 0; - } -} - -int has_glm_turbo_ratio_limit(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_GOLDMONT: - case INTEL_FAM6_SKYLAKE_X: - case INTEL_FAM6_ICELAKE_X: - case INTEL_FAM6_SAPPHIRERAPIDS_X: - return 1; - default: - return 0; - } -} - -int has_config_tdp(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_IVYBRIDGE: /* IVB */ - case INTEL_FAM6_HASWELL: /* HSW */ - case INTEL_FAM6_HASWELL_X: /* HSX */ - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_HASWELL_G: /* HSW */ - case INTEL_FAM6_BROADWELL: /* BDW */ - case INTEL_FAM6_BROADWELL_G: /* BDW */ - case INTEL_FAM6_BROADWELL_X: /* BDX */ - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - case INTEL_FAM6_SKYLAKE_X: /* SKX */ - case INTEL_FAM6_ICELAKE_X: /* ICX */ - case INTEL_FAM6_SAPPHIRERAPIDS_X: /* SPR */ - case INTEL_FAM6_XEON_PHI_KNL: /* Knights Landing */ - return 1; - default: - return 0; - } -} - -/* - * tcc_offset_bits: - * 0: Tcc Offset not supported (Default) - * 6: Bit 29:24 of MSR_PLATFORM_INFO - * 4: Bit 27:24 of MSR_PLATFORM_INFO - */ -void check_tcc_offset(int model) -{ - unsigned long long msr; - - if (!genuine_intel) - return; - - switch (model) { - case INTEL_FAM6_SKYLAKE_L: - case INTEL_FAM6_SKYLAKE: - case INTEL_FAM6_KABYLAKE_L: - case INTEL_FAM6_KABYLAKE: - case INTEL_FAM6_ICELAKE_L: - case INTEL_FAM6_ICELAKE: - case INTEL_FAM6_TIGERLAKE_L: - case INTEL_FAM6_TIGERLAKE: - case INTEL_FAM6_COMETLAKE: - if (!get_msr(base_cpu, MSR_PLATFORM_INFO, &msr)) { - msr = (msr >> 30) & 1; - if (msr) - tcc_offset_bits = 6; - } - return; - default: - return; - } + if (platform->enable_tsc_tweak) + tsc_tweak = base_hz / tsc_hz; } static void remove_underbar(char *s) @@ -4078,44 +4497,37 @@ static void remove_underbar(char *s) *to = 0; } -static void dump_turbo_ratio_info(unsigned int family, unsigned int model) +static void dump_turbo_ratio_info(void) { if (!has_turbo) return; - if (has_hsw_turbo_ratio_limit(family, model)) - dump_hsw_turbo_ratio_limits(); + if (!platform->has_nhm_msrs) + return; + + if (platform->trl_msrs & TRL_LIMIT2) + dump_turbo_ratio_limit2(); - if (has_ivt_turbo_ratio_limit(family, model)) - dump_ivt_turbo_ratio_limits(); + if (platform->trl_msrs & TRL_LIMIT1) + dump_turbo_ratio_limit1(); - if (has_turbo_ratio_limit(family, model)) { - dump_turbo_ratio_limits(MSR_TURBO_RATIO_LIMIT, family, model); + if (platform->trl_msrs & TRL_BASE) { + dump_turbo_ratio_limits(MSR_TURBO_RATIO_LIMIT); if (is_hybrid) - dump_turbo_ratio_limits(MSR_SECONDARY_TURBO_RATIO_LIMIT, family, model); + dump_turbo_ratio_limits(MSR_SECONDARY_TURBO_RATIO_LIMIT); } - if (has_atom_turbo_ratio_limit(family, model)) + if (platform->trl_msrs & TRL_ATOM) dump_atom_turbo_ratio_limits(); - if (has_knl_turbo_ratio_limit(family, model)) + if (platform->trl_msrs & TRL_KNL) dump_knl_turbo_ratio_limits(); - if (has_config_tdp(family, model)) + if (platform->has_config_tdp) dump_config_tdp(); } -static void dump_cstate_pstate_config_info(unsigned int family, unsigned int model) -{ - if (!do_nhm_platform_info) - return; - - dump_nhm_platform_info(); - dump_turbo_ratio_info(family, model); - dump_nhm_cst_cfg(); -} - static int read_sysfs_int(char *path) { FILE *input; @@ -4152,7 +4564,7 @@ static void dump_sysfs_file(char *path) fprintf(outf, "%s: %s", strrchr(path, '/') + 1, cpuidle_buf); } -static void intel_uncore_frequency_probe(void) +static void probe_intel_uncore_frequency(void) { int i, j; char path[128]; @@ -4163,6 +4575,10 @@ static void intel_uncore_frequency_probe(void) if (access("/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00", R_OK)) return; + /* Cluster level sysfs not supported yet. */ + if (!access("/sys/devices/system/cpu/intel_uncore_frequency/uncore00", R_OK)) + return; + if (!access("/sys/devices/system/cpu/intel_uncore_frequency/package_00_die_00/current_freq_khz", R_OK)) BIC_PRESENT(BIC_UNCORE_MHZ); @@ -4194,6 +4610,20 @@ static void intel_uncore_frequency_probe(void) } } +static void probe_graphics(void) +{ + if (!access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK)) + BIC_PRESENT(BIC_GFX_rc6); + + if (!access("/sys/class/drm/card0/gt_cur_freq_mhz", R_OK) || + !access("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", R_OK)) + BIC_PRESENT(BIC_GFXMHz); + + if (!access("/sys/class/drm/card0/gt_act_freq_mhz", R_OK) || + !access("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", R_OK)) + BIC_PRESENT(BIC_GFXACTMHz); +} + static void dump_sysfs_cstate_config(void) { char path[64]; @@ -4310,7 +4740,7 @@ int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) cpu = t->cpu_id; /* EPB is per-package */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_thread_in_package(t, c, p)) return 0; if (cpu_migrate(cpu)) { @@ -4359,7 +4789,7 @@ int print_hwp(struct thread_data *t, struct core_data *c, struct pkg_data *p) cpu = t->cpu_id; /* MSR_HWP_CAPABILITIES is per-package */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_thread_in_package(t, c, p)) return 0; if (cpu_migrate(cpu)) { @@ -4442,7 +4872,7 @@ int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data cpu = t->cpu_id; /* per-package */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_thread_in_package(t, c, p)) return 0; if (cpu_migrate(cpu)) { @@ -4450,7 +4880,7 @@ int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data return -1; } - if (do_core_perf_limit_reasons) { + if (platform->plr_msrs & PLR_CORE) { get_msr(cpu, MSR_CORE_PERF_LIMIT_REASONS, &msr); fprintf(outf, "cpu%d: MSR_CORE_PERF_LIMIT_REASONS, 0x%08llx", cpu, msr); fprintf(outf, " (Active: %s%s%s%s%s%s%s%s%s%s%s%s%s%s)", @@ -4483,7 +4913,7 @@ int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data (msr & 1 << 17) ? "ThermStatus, " : "", (msr & 1 << 16) ? "PROCHOT, " : ""); } - if (do_gfx_perf_limit_reasons) { + if (platform->plr_msrs & PLR_GFX) { get_msr(cpu, MSR_GFX_PERF_LIMIT_REASONS, &msr); fprintf(outf, "cpu%d: MSR_GFX_PERF_LIMIT_REASONS, 0x%08llx", cpu, msr); fprintf(outf, " (Active: %s%s%s%s%s%s%s%s)", @@ -4503,7 +4933,7 @@ int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data (msr & 1 << 25) ? "GFXPwr, " : "", (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); } - if (do_ring_perf_limit_reasons) { + if (platform->plr_msrs & PLR_RING) { get_msr(cpu, MSR_RING_PERF_LIMIT_REASONS, &msr); fprintf(outf, "cpu%d: MSR_RING_PERF_LIMIT_REASONS, 0x%08llx", cpu, msr); fprintf(outf, " (Active: %s%s%s%s%s%s)", @@ -4525,208 +4955,74 @@ int print_perf_limit(struct thread_data *t, struct core_data *c, struct pkg_data #define RAPL_POWER_GRANULARITY 0x7FFF /* 15 bit power granularity */ #define RAPL_TIME_GRANULARITY 0x3F /* 6 bit time granularity */ -double get_tdp_intel(unsigned int model) +double get_quirk_tdp(void) { - unsigned long long msr; + if (platform->rapl_quirk_tdp) + return platform->rapl_quirk_tdp; - if (do_rapl & RAPL_PKG_POWER_INFO) - if (!get_msr(base_cpu, MSR_PKG_POWER_INFO, &msr)) - return ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units; - - switch (model) { - case INTEL_FAM6_ATOM_SILVERMONT: - case INTEL_FAM6_ATOM_SILVERMONT_D: - return 30.0; - default: - return 135.0; - } + return 135.0; } -double get_tdp_amd(unsigned int family) +double get_tdp_intel(void) { - UNUSED(family); + unsigned long long msr; - /* This is the max stock TDP of HEDT/Server Fam17h+ chips */ - return 280.0; + if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) + if (!get_msr(base_cpu, MSR_PKG_POWER_INFO, &msr)) + return ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units; + return get_quirk_tdp(); } -/* - * rapl_dram_energy_units_probe() - * Energy units are either hard-coded, or come from RAPL Energy Unit MSR. - */ -static double rapl_dram_energy_units_probe(int model, double rapl_energy_units) +double get_tdp_amd(void) { - /* only called for genuine_intel, family 6 */ - - switch (model) { - case INTEL_FAM6_HASWELL_X: /* HSX */ - case INTEL_FAM6_BROADWELL_X: /* BDX */ - case INTEL_FAM6_SKYLAKE_X: /* SKX */ - case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ - case INTEL_FAM6_ICELAKE_X: /* ICX */ - return (rapl_dram_energy_units = 15.3 / 1000000); - default: - return (rapl_energy_units); - } + return get_quirk_tdp(); } -void rapl_probe_intel(unsigned int family, unsigned int model) +void rapl_probe_intel(void) { unsigned long long msr; unsigned int time_unit; double tdp; - if (family != 6) - return; - - switch (model) { - case INTEL_FAM6_SANDYBRIDGE: - case INTEL_FAM6_IVYBRIDGE: - case INTEL_FAM6_HASWELL: /* HSW */ - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_HASWELL_G: /* HSW */ - case INTEL_FAM6_BROADWELL: /* BDW */ - case INTEL_FAM6_BROADWELL_G: /* BDW */ - do_rapl = RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_GFX | RAPL_PKG_POWER_INFO; - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_Cor_J); - BIC_PRESENT(BIC_GFX_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_CorWatt); - BIC_PRESENT(BIC_GFXWatt); - } - break; - case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ - case INTEL_FAM6_ATOM_GOLDMONT_PLUS: - do_rapl = RAPL_PKG | RAPL_PKG_POWER_INFO; - if (rapl_joules) - BIC_PRESENT(BIC_Pkg_J); - else - BIC_PRESENT(BIC_PkgWatt); - break; - case INTEL_FAM6_ATOM_TREMONT: /* EHL */ - do_rapl = - RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS - | RAPL_GFX | RAPL_PKG_POWER_INFO; - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_Cor_J); - BIC_PRESENT(BIC_RAM_J); - BIC_PRESENT(BIC_GFX_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_CorWatt); - BIC_PRESENT(BIC_RAMWatt); - BIC_PRESENT(BIC_GFXWatt); - } - break; - case INTEL_FAM6_ATOM_TREMONT_D: /* JVL */ - do_rapl = RAPL_PKG | RAPL_PKG_PERF_STATUS | RAPL_PKG_POWER_INFO; - BIC_PRESENT(BIC_PKG__); - if (rapl_joules) - BIC_PRESENT(BIC_Pkg_J); - else - BIC_PRESENT(BIC_PkgWatt); - break; - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - do_rapl = - RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS - | RAPL_GFX | RAPL_PKG_POWER_INFO; - BIC_PRESENT(BIC_PKG__); - BIC_PRESENT(BIC_RAM__); - if (rapl_joules) { + if (rapl_joules) { + if (platform->rapl_msrs & RAPL_PKG_ENERGY_STATUS) BIC_PRESENT(BIC_Pkg_J); + if (platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS) BIC_PRESENT(BIC_Cor_J); + if (platform->rapl_msrs & RAPL_DRAM_ENERGY_STATUS) BIC_PRESENT(BIC_RAM_J); + if (platform->rapl_msrs & RAPL_GFX_ENERGY_STATUS) BIC_PRESENT(BIC_GFX_J); - } else { + } else { + if (platform->rapl_msrs & RAPL_PKG_ENERGY_STATUS) BIC_PRESENT(BIC_PkgWatt); + if (platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS) BIC_PRESENT(BIC_CorWatt); + if (platform->rapl_msrs & RAPL_DRAM_ENERGY_STATUS) BIC_PRESENT(BIC_RAMWatt); + if (platform->rapl_msrs & RAPL_GFX_ENERGY_STATUS) BIC_PRESENT(BIC_GFXWatt); - } - break; - case INTEL_FAM6_HASWELL_X: /* HSX */ - case INTEL_FAM6_BROADWELL_X: /* BDX */ - case INTEL_FAM6_SKYLAKE_X: /* SKX */ - case INTEL_FAM6_ICELAKE_X: /* ICX */ - case INTEL_FAM6_SAPPHIRERAPIDS_X: /* SPR */ - case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ - do_rapl = - RAPL_PKG | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | - RAPL_PKG_POWER_INFO; - BIC_PRESENT(BIC_PKG__); - BIC_PRESENT(BIC_RAM__); - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_RAM_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_RAMWatt); - } - break; - case INTEL_FAM6_SANDYBRIDGE_X: - case INTEL_FAM6_IVYBRIDGE_X: - do_rapl = - RAPL_PKG | RAPL_CORES | RAPL_CORE_POLICY | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_PKG_PERF_STATUS | - RAPL_DRAM_PERF_STATUS | RAPL_PKG_POWER_INFO; - BIC_PRESENT(BIC_PKG__); - BIC_PRESENT(BIC_RAM__); - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_Cor_J); - BIC_PRESENT(BIC_RAM_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_CorWatt); - BIC_PRESENT(BIC_RAMWatt); - } - break; - case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */ - case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */ - do_rapl = RAPL_PKG | RAPL_CORES; - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_Cor_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_CorWatt); - } - break; - case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */ - do_rapl = - RAPL_PKG | RAPL_DRAM | RAPL_DRAM_POWER_INFO | RAPL_DRAM_PERF_STATUS | RAPL_PKG_PERF_STATUS | - RAPL_PKG_POWER_INFO | RAPL_CORES_ENERGY_STATUS; + } + + if (platform->rapl_msrs & RAPL_PKG_PERF_STATUS) BIC_PRESENT(BIC_PKG__); + if (platform->rapl_msrs & RAPL_DRAM_PERF_STATUS) BIC_PRESENT(BIC_RAM__); - if (rapl_joules) { - BIC_PRESENT(BIC_Pkg_J); - BIC_PRESENT(BIC_Cor_J); - BIC_PRESENT(BIC_RAM_J); - } else { - BIC_PRESENT(BIC_PkgWatt); - BIC_PRESENT(BIC_CorWatt); - BIC_PRESENT(BIC_RAMWatt); - } - break; - default: - return; - } /* units on package 0, verify later other packages match */ if (get_msr(base_cpu, MSR_RAPL_POWER_UNIT, &msr)) return; rapl_power_units = 1.0 / (1 << (msr & 0xF)); - if (model == INTEL_FAM6_ATOM_SILVERMONT) + if (platform->has_rapl_divisor) rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000; else rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F)); - rapl_dram_energy_units = rapl_dram_energy_units_probe(model, rapl_energy_units); + if (platform->has_fixed_rapl_unit) + rapl_dram_energy_units = (15.3 / 1000000); + else + rapl_dram_energy_units = rapl_energy_units; time_unit = msr >> 16 & 0xF; if (time_unit == 0) @@ -4734,32 +5030,18 @@ void rapl_probe_intel(unsigned int family, unsigned int model) rapl_time_units = 1.0 / (1 << (time_unit)); - tdp = get_tdp_intel(model); + tdp = get_tdp_intel(); rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp; if (!quiet) fprintf(outf, "RAPL: %.0f sec. Joule Counter Range, at %.0f Watts\n", rapl_joule_counter_range, tdp); } -void rapl_probe_amd(unsigned int family, unsigned int model) +void rapl_probe_amd(void) { unsigned long long msr; - unsigned int eax, ebx, ecx, edx; - unsigned int has_rapl = 0; double tdp; - UNUSED(model); - - if (max_extended_level >= 0x80000007) { - __cpuid(0x80000007, eax, ebx, ecx, edx); - /* RAPL (Fam 17h+) */ - has_rapl = edx & (1 << 14); - } - - if (!has_rapl || family < 0x17) - return; - - do_rapl = RAPL_AMD_F17H | RAPL_PER_CORE_ENERGY; if (rapl_joules) { BIC_PRESENT(BIC_Pkg_J); BIC_PRESENT(BIC_Cor_J); @@ -4775,128 +5057,13 @@ void rapl_probe_amd(unsigned int family, unsigned int model) rapl_energy_units = ldexp(1.0, -(msr >> 8 & 0x1f)); rapl_power_units = ldexp(1.0, -(msr & 0xf)); - tdp = get_tdp_amd(family); + tdp = get_tdp_amd(); rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp; if (!quiet) fprintf(outf, "RAPL: %.0f sec. Joule Counter Range, at %.0f Watts\n", rapl_joule_counter_range, tdp); } -/* - * rapl_probe() - * - * sets do_rapl, rapl_power_units, rapl_energy_units, rapl_time_units - */ -void rapl_probe(unsigned int family, unsigned int model) -{ - if (genuine_intel) - rapl_probe_intel(family, model); - if (authentic_amd || hygon_genuine) - rapl_probe_amd(family, model); -} - -void perf_limit_reasons_probe(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return; - - if (family != 6) - return; - - switch (model) { - case INTEL_FAM6_HASWELL: /* HSW */ - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_HASWELL_G: /* HSW */ - do_gfx_perf_limit_reasons = 1; - /* FALLTHRU */ - case INTEL_FAM6_HASWELL_X: /* HSX */ - do_core_perf_limit_reasons = 1; - do_ring_perf_limit_reasons = 1; - default: - return; - } -} - -void automatic_cstate_conversion_probe(unsigned int family, unsigned int model) -{ - if (family != 6) - return; - - switch (model) { - case INTEL_FAM6_BROADWELL_X: - case INTEL_FAM6_SKYLAKE_X: - has_automatic_cstate_conversion = 1; - } -} - -void prewake_cstate_probe(unsigned int family, unsigned int model) -{ - if (is_icx(family, model) || is_spr(family, model)) - dis_cstate_prewake = 1; -} - -int print_thermal(struct thread_data *t, struct core_data *c, struct pkg_data *p) -{ - unsigned long long msr; - unsigned int dts, dts2; - int cpu; - - UNUSED(c); - UNUSED(p); - - if (!(do_dts || do_ptm)) - return 0; - - cpu = t->cpu_id; - - /* DTS is per-core, no need to print for each thread */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) - return 0; - - if (cpu_migrate(cpu)) { - fprintf(outf, "print_thermal: Could not migrate to CPU %d\n", cpu); - return -1; - } - - if (do_ptm && (t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) { - if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) - return 0; - - dts = (msr >> 16) & 0x7F; - fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_STATUS: 0x%08llx (%d C)\n", cpu, msr, tj_max - dts); - - if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &msr)) - return 0; - - dts = (msr >> 16) & 0x7F; - dts2 = (msr >> 8) & 0x7F; - fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", - cpu, msr, tj_max - dts, tj_max - dts2); - } - - if (do_dts && debug) { - unsigned int resolution; - - if (get_msr(cpu, MSR_IA32_THERM_STATUS, &msr)) - return 0; - - dts = (msr >> 16) & 0x7F; - resolution = (msr >> 27) & 0xF; - fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", - cpu, msr, tj_max - dts, resolution); - - if (get_msr(cpu, MSR_IA32_THERM_INTERRUPT, &msr)) - return 0; - - dts = (msr >> 16) & 0x7F; - dts2 = (msr >> 8) & 0x7F; - fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", - cpu, msr, tj_max - dts, tj_max - dts2); - } - - return 0; -} - void print_power_limit_msr(int cpu, unsigned long long msr, char *label) { fprintf(outf, "cpu%d: %s: %sabled (%0.3f Watts, %f sec, clamp %sabled)\n", @@ -4918,11 +5085,11 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) UNUSED(c); UNUSED(p); - if (!do_rapl) + if (!platform->rapl_msrs) return 0; /* RAPL counters are per package, so print only for 1st thread/package */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_thread_in_package(t, c, p)) return 0; cpu = t->cpu_id; @@ -4931,7 +5098,7 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) return -1; } - if (do_rapl & RAPL_AMD_F17H) { + if (platform->rapl_msrs & RAPL_AMD_F17H) { msr_name = "MSR_RAPL_PWR_UNIT"; if (get_msr(cpu, MSR_RAPL_PWR_UNIT, &msr)) return -1; @@ -4944,7 +5111,7 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) fprintf(outf, "cpu%d: %s: 0x%08llx (%f Watts, %f Joules, %f sec.)\n", cpu, msr_name, msr, rapl_power_units, rapl_energy_units, rapl_time_units); - if (do_rapl & RAPL_PKG_POWER_INFO) { + if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) { if (get_msr(cpu, MSR_PKG_POWER_INFO, &msr)) return -5; @@ -4957,7 +5124,7 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); } - if (do_rapl & RAPL_PKG) { + if (platform->rapl_msrs & RAPL_PKG) { if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) return -9; @@ -4981,7 +5148,7 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) cpu, ((msr >> 0) & 0x1FFF) * rapl_power_units, (msr >> 31) & 1 ? "" : "UN"); } - if (do_rapl & RAPL_DRAM_POWER_INFO) { + if (platform->rapl_msrs & RAPL_DRAM_POWER_INFO) { if (get_msr(cpu, MSR_DRAM_POWER_INFO, &msr)) return -6; @@ -4992,7 +5159,7 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); } - if (do_rapl & RAPL_DRAM) { + if (platform->rapl_msrs & RAPL_DRAM) { if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) return -9; fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", @@ -5000,20 +5167,20 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) print_power_limit_msr(cpu, msr, "DRAM Limit"); } - if (do_rapl & RAPL_CORE_POLICY) { + if (platform->rapl_msrs & RAPL_CORE_POLICY) { if (get_msr(cpu, MSR_PP0_POLICY, &msr)) return -7; fprintf(outf, "cpu%d: MSR_PP0_POLICY: %lld\n", cpu, msr & 0xF); } - if (do_rapl & RAPL_CORES_POWER_LIMIT) { + if (platform->rapl_msrs & RAPL_CORE_POWER_LIMIT) { if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) return -9; fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); print_power_limit_msr(cpu, msr, "Cores Limit"); } - if (do_rapl & RAPL_GFX) { + if (platform->rapl_msrs & RAPL_GFX) { if (get_msr(cpu, MSR_PP1_POLICY, &msr)) return -8; @@ -5029,217 +5196,24 @@ int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) } /* - * SNB adds support for additional MSRs: + * probe_rapl() * - * MSR_PKG_C7_RESIDENCY 0x000003fa - * MSR_CORE_C7_RESIDENCY 0x000003fe - * MSR_PKG_C2_RESIDENCY 0x0000060d + * sets rapl_power_units, rapl_energy_units, rapl_time_units */ - -int has_snb_msrs(unsigned int family, unsigned int model) +void probe_rapl(void) { - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_SANDYBRIDGE: - case INTEL_FAM6_SANDYBRIDGE_X: - case INTEL_FAM6_IVYBRIDGE: /* IVB */ - case INTEL_FAM6_IVYBRIDGE_X: /* IVB Xeon */ - case INTEL_FAM6_HASWELL: /* HSW */ - case INTEL_FAM6_HASWELL_X: /* HSW */ - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_HASWELL_G: /* HSW */ - case INTEL_FAM6_BROADWELL: /* BDW */ - case INTEL_FAM6_BROADWELL_G: /* BDW */ - case INTEL_FAM6_BROADWELL_X: /* BDX */ - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - case INTEL_FAM6_SKYLAKE_X: /* SKX */ - case INTEL_FAM6_ICELAKE_X: /* ICX */ - case INTEL_FAM6_SAPPHIRERAPIDS_X: /* SPR */ - case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ - case INTEL_FAM6_ATOM_GOLDMONT_PLUS: - case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */ - case INTEL_FAM6_ATOM_TREMONT: /* EHL */ - case INTEL_FAM6_ATOM_TREMONT_D: /* JVL */ - return 1; - } - return 0; -} - -/* - * HSW ULT added support for C8/C9/C10 MSRs: - * - * MSR_PKG_C8_RESIDENCY 0x00000630 - * MSR_PKG_C9_RESIDENCY 0x00000631 - * MSR_PKG_C10_RESIDENCY 0x00000632 - * - * MSR_PKGC8_IRTL 0x00000633 - * MSR_PKGC9_IRTL 0x00000634 - * MSR_PKGC10_IRTL 0x00000635 - * - */ -int has_c8910_msrs(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_HASWELL_L: /* HSW */ - case INTEL_FAM6_BROADWELL: /* BDW */ - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ - case INTEL_FAM6_ATOM_GOLDMONT_PLUS: - case INTEL_FAM6_ATOM_TREMONT: /* EHL */ - return 1; - } - return 0; -} - -/* - * SKL adds support for additional MSRS: - * - * MSR_PKG_WEIGHTED_CORE_C0_RES 0x00000658 - * MSR_PKG_ANY_CORE_C0_RES 0x00000659 - * MSR_PKG_ANY_GFXE_C0_RES 0x0000065A - * MSR_PKG_BOTH_CORE_GFXE_C0_RES 0x0000065B - */ -int has_skl_msrs(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - return 1; - } - return 0; -} - -int is_slm(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_ATOM_SILVERMONT: /* BYT */ - case INTEL_FAM6_ATOM_SILVERMONT_D: /* AVN */ - return 1; - } - return 0; -} - -int is_knl(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_XEON_PHI_KNL: /* KNL */ - return 1; - } - return 0; -} - -int is_cnl(unsigned int family, unsigned int model) -{ - if (!genuine_intel) - return 0; - - if (family != 6) - return 0; - - switch (model) { - case INTEL_FAM6_CANNONLAKE_L: /* CNL */ - return 1; - } - - return 0; -} - -unsigned int get_aperf_mperf_multiplier(unsigned int family, unsigned int model) -{ - if (is_knl(family, model)) - return 1024; - return 1; -} - -#define SLM_BCLK_FREQS 5 -double slm_freq_table[SLM_BCLK_FREQS] = { 83.3, 100.0, 133.3, 116.7, 80.0 }; - -double slm_bclk(void) -{ - unsigned long long msr = 3; - unsigned int i; - double freq; - - if (get_msr(base_cpu, MSR_FSB_FREQ, &msr)) - fprintf(outf, "SLM BCLK: unknown\n"); - - i = msr & 0xf; - if (i >= SLM_BCLK_FREQS) { - fprintf(outf, "SLM BCLK[%d] invalid\n", i); - i = 3; - } - freq = slm_freq_table[i]; - - if (!quiet) - fprintf(outf, "SLM BCLK: %.1f Mhz\n", freq); - - return freq; -} - -double discover_bclk(unsigned int family, unsigned int model) -{ - if (has_snb_msrs(family, model) || is_knl(family, model)) - return 100.00; - else if (is_slm(family, model)) - return slm_bclk(); - else - return 133.33; -} - -int get_cpu_type(struct thread_data *t, struct core_data *c, struct pkg_data *p) -{ - unsigned int eax, ebx, ecx, edx; - - UNUSED(c); - UNUSED(p); - - if (!genuine_intel) - return 0; + if (!platform->rapl_msrs) + return; - if (cpu_migrate(t->cpu_id)) { - fprintf(outf, "Could not migrate to CPU %d\n", t->cpu_id); - return -1; - } + if (genuine_intel) + rapl_probe_intel(); + if (authentic_amd || hygon_genuine) + rapl_probe_amd(); - if (max_level < 0x1a) - return 0; + if (quiet) + return; - __cpuid(0x1a, eax, ebx, ecx, edx); - eax = (eax >> 24) & 0xFF; - if (eax == 0x20) - t->is_atom = true; - return 0; + for_all_cpus(print_rapl, ODD_COUNTERS); } /* @@ -5268,7 +5242,7 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk return 0; /* this is a per-package concept */ - if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + if (!is_cpu_first_thread_in_package(t, c, p)) return 0; cpu = t->cpu_id; @@ -5284,7 +5258,7 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk } /* Temperature Target MSR is Nehalem and newer only */ - if (!do_nhm_platform_info) + if (!platform->has_nhm_msrs) goto guess; if (get_msr(base_cpu, MSR_IA32_TEMPERATURE_TARGET, &msr)) @@ -5293,20 +5267,18 @@ int set_temperature_target(struct thread_data *t, struct core_data *c, struct pk tcc_default = (msr >> 16) & 0xFF; if (!quiet) { - switch (tcc_offset_bits) { - case 4: - tcc_offset = (msr >> 24) & 0xF; - fprintf(outf, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C) (%d default - %d offset)\n", - cpu, msr, tcc_default - tcc_offset, tcc_default, tcc_offset); - break; - case 6: - tcc_offset = (msr >> 24) & 0x3F; + int bits = platform->tcc_offset_bits; + unsigned long long enabled = 0; + + if (bits && !get_msr(base_cpu, MSR_PLATFORM_INFO, &enabled)) + enabled = (enabled >> 30) & 1; + + if (bits && enabled) { + tcc_offset = (msr >> 24) & GENMASK(bits - 1, 0); fprintf(outf, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C) (%d default - %d offset)\n", cpu, msr, tcc_default - tcc_offset, tcc_default, tcc_offset); - break; - default: + } else { fprintf(outf, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n", cpu, msr, tcc_default); - break; } } @@ -5324,6 +5296,108 @@ guess: return 0; } +int print_thermal(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + unsigned long long msr; + unsigned int dts, dts2; + int cpu; + + UNUSED(c); + UNUSED(p); + + if (!(do_dts || do_ptm)) + return 0; + + cpu = t->cpu_id; + + /* DTS is per-core, no need to print for each thread */ + if (!is_cpu_first_thread_in_core(t, c, p)) + return 0; + + if (cpu_migrate(cpu)) { + fprintf(outf, "print_thermal: Could not migrate to CPU %d\n", cpu); + return -1; + } + + if (do_ptm && is_cpu_first_core_in_package(t, c, p)) { + if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_STATUS: 0x%08llx (%d C)\n", cpu, msr, tj_max - dts); + + if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; + fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", + cpu, msr, tj_max - dts, tj_max - dts2); + } + + if (do_dts && debug) { + unsigned int resolution; + + if (get_msr(cpu, MSR_IA32_THERM_STATUS, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + resolution = (msr >> 27) & 0xF; + fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", + cpu, msr, tj_max - dts, resolution); + + if (get_msr(cpu, MSR_IA32_THERM_INTERRUPT, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; + fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", + cpu, msr, tj_max - dts, tj_max - dts2); + } + + return 0; +} + +void probe_thermal(void) +{ + if (!access("/sys/devices/system/cpu/cpu0/thermal_throttle/core_throttle_count", R_OK)) + BIC_PRESENT(BIC_CORE_THROT_CNT); + else + BIC_NOT_PRESENT(BIC_CORE_THROT_CNT); + + for_all_cpus(set_temperature_target, ODD_COUNTERS); + + if (quiet) + return; + + for_all_cpus(print_thermal, ODD_COUNTERS); +} + +int get_cpu_type(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + unsigned int eax, ebx, ecx, edx; + + UNUSED(c); + UNUSED(p); + + if (!genuine_intel) + return 0; + + if (cpu_migrate(t->cpu_id)) { + fprintf(outf, "Could not migrate to CPU %d\n", t->cpu_id); + return -1; + } + + if (max_level < 0x1a) + return 0; + + __cpuid(0x1a, eax, ebx, ecx, edx); + eax = (eax >> 24) & 0xFF; + if (eax == 0x20) + t->is_atom = true; + return 0; +} + void decode_feature_control_msr(void) { unsigned long long msr; @@ -5354,7 +5428,7 @@ void decode_misc_feature_control(void) { unsigned long long msr; - if (!has_misc_feature_control) + if (!platform->has_msr_misc_feature_control) return; if (!get_msr(base_cpu, MSR_MISC_FEATURE_CONTROL, &msr)) @@ -5375,10 +5449,7 @@ void decode_misc_pwr_mgmt_msr(void) { unsigned long long msr; - if (!do_nhm_platform_info) - return; - - if (no_MSR_MISC_PWR_MGMT) + if (!platform->has_msr_misc_pwr_mgmt) return; if (!get_msr(base_cpu, MSR_MISC_PWR_MGMT, &msr)) @@ -5397,6 +5468,9 @@ void decode_c6_demotion_policy_msr(void) { unsigned long long msr; + if (!platform->has_msr_c6_demotion_policy_config) + return; + if (!get_msr(base_cpu, MSR_CC6_DEMOTION_POLICY_CONFIG, &msr)) fprintf(outf, "cpu%d: MSR_CC6_DEMOTION_POLICY_CONFIG: 0x%08llx (%sable-CC6-Demotion)\n", base_cpu, msr, msr & (1 << 0) ? "EN" : "DIS"); @@ -5406,67 +5480,6 @@ void decode_c6_demotion_policy_msr(void) base_cpu, msr, msr & (1 << 0) ? "EN" : "DIS"); } -/* - * When models are the same, for the purpose of turbostat, reuse - */ -unsigned int intel_model_duplicates(unsigned int model) -{ - - switch (model) { - case INTEL_FAM6_NEHALEM_EP: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */ - case INTEL_FAM6_NEHALEM: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ - case 0x1F: /* Core i7 and i5 Processor - Nehalem */ - case INTEL_FAM6_WESTMERE: /* Westmere Client - Clarkdale, Arrandale */ - case INTEL_FAM6_WESTMERE_EP: /* Westmere EP - Gulftown */ - return INTEL_FAM6_NEHALEM; - - case INTEL_FAM6_NEHALEM_EX: /* Nehalem-EX Xeon - Beckton */ - case INTEL_FAM6_WESTMERE_EX: /* Westmere-EX Xeon - Eagleton */ - return INTEL_FAM6_NEHALEM_EX; - - case INTEL_FAM6_XEON_PHI_KNM: - return INTEL_FAM6_XEON_PHI_KNL; - - case INTEL_FAM6_BROADWELL_X: - case INTEL_FAM6_BROADWELL_D: /* BDX-DE */ - return INTEL_FAM6_BROADWELL_X; - - case INTEL_FAM6_SKYLAKE_L: - case INTEL_FAM6_SKYLAKE: - case INTEL_FAM6_KABYLAKE_L: - case INTEL_FAM6_KABYLAKE: - case INTEL_FAM6_COMETLAKE_L: - case INTEL_FAM6_COMETLAKE: - return INTEL_FAM6_SKYLAKE_L; - - case INTEL_FAM6_ICELAKE_L: - case INTEL_FAM6_ICELAKE_NNPI: - case INTEL_FAM6_TIGERLAKE_L: - case INTEL_FAM6_TIGERLAKE: - case INTEL_FAM6_ROCKETLAKE: - case INTEL_FAM6_LAKEFIELD: - case INTEL_FAM6_ALDERLAKE: - case INTEL_FAM6_ALDERLAKE_L: - case INTEL_FAM6_ATOM_GRACEMONT: - case INTEL_FAM6_RAPTORLAKE: - case INTEL_FAM6_RAPTORLAKE_P: - case INTEL_FAM6_RAPTORLAKE_S: - case INTEL_FAM6_METEORLAKE: - case INTEL_FAM6_METEORLAKE_L: - return INTEL_FAM6_CANNONLAKE_L; - - case INTEL_FAM6_ATOM_TREMONT_L: - return INTEL_FAM6_ATOM_TREMONT; - - case INTEL_FAM6_ICELAKE_D: - return INTEL_FAM6_ICELAKE_X; - - case INTEL_FAM6_EMERALDRAPIDS_X: - return INTEL_FAM6_SAPPHIRERAPIDS_X; - } - return model; -} - void print_dev_latency(void) { char *path = "/dev/cpu_dma_latency"; @@ -5510,6 +5523,101 @@ void linux_perf_init(void) BIC_PRESENT(BIC_IPC); } +void probe_cstates(void) +{ + probe_cst_limit(); + + if (platform->supported_cstates & CC1) + BIC_PRESENT(BIC_CPU_c1); + + if (platform->supported_cstates & CC3) + BIC_PRESENT(BIC_CPU_c3); + + if (platform->supported_cstates & CC6) + BIC_PRESENT(BIC_CPU_c6); + + if (platform->supported_cstates & CC7) + BIC_PRESENT(BIC_CPU_c7); + + if (platform->supported_cstates & PC2 && (pkg_cstate_limit >= PCL__2)) + BIC_PRESENT(BIC_Pkgpc2); + + if (platform->supported_cstates & PC3 && (pkg_cstate_limit >= PCL__3)) + BIC_PRESENT(BIC_Pkgpc3); + + if (platform->supported_cstates & PC6 && (pkg_cstate_limit >= PCL__6)) + BIC_PRESENT(BIC_Pkgpc6); + + if (platform->supported_cstates & PC7 && (pkg_cstate_limit >= PCL__7)) + BIC_PRESENT(BIC_Pkgpc7); + + if (platform->supported_cstates & PC8 && (pkg_cstate_limit >= PCL__8)) + BIC_PRESENT(BIC_Pkgpc8); + + if (platform->supported_cstates & PC9 && (pkg_cstate_limit >= PCL__9)) + BIC_PRESENT(BIC_Pkgpc9); + + if (platform->supported_cstates & PC10 && (pkg_cstate_limit >= PCL_10)) + BIC_PRESENT(BIC_Pkgpc10); + + if (platform->has_msr_module_c6_res_ms) + BIC_PRESENT(BIC_Mod_c6); + + if (platform->has_ext_cst_msrs) { + BIC_PRESENT(BIC_Totl_c0); + BIC_PRESENT(BIC_Any_c0); + BIC_PRESENT(BIC_GFX_c0); + BIC_PRESENT(BIC_CPUGFX); + } + + if (quiet) + return; + + dump_power_ctl(); + dump_cst_cfg(); + decode_c6_demotion_policy_msr(); + print_dev_latency(); + dump_sysfs_cstate_config(); + print_irtl(); +} + +void probe_lpi(void) +{ + if (!access("/sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us", R_OK)) + BIC_PRESENT(BIC_CPU_LPI); + else + BIC_NOT_PRESENT(BIC_CPU_LPI); + + if (!access(sys_lpi_file_sysfs, R_OK)) { + sys_lpi_file = sys_lpi_file_sysfs; + BIC_PRESENT(BIC_SYS_LPI); + } else if (!access(sys_lpi_file_debugfs, R_OK)) { + sys_lpi_file = sys_lpi_file_debugfs; + BIC_PRESENT(BIC_SYS_LPI); + } else { + sys_lpi_file_sysfs = NULL; + BIC_NOT_PRESENT(BIC_SYS_LPI); + } + +} + +void probe_pstates(void) +{ + probe_bclk(); + + if (quiet) + return; + + dump_platform_info(); + dump_turbo_ratio_info(); + dump_sysfs_pstate_config(); + decode_misc_pwr_mgmt_msr(); + + for_all_cpus(print_hwp, ODD_COUNTERS); + for_all_cpus(print_epb, ODD_COUNTERS); + for_all_cpus(print_perf_limit, ODD_COUNTERS); +} + void process_cpuid() { unsigned int eax, ebx, ecx, edx; @@ -5569,10 +5677,8 @@ void process_cpuid() edx_flags & (1 << 22) ? "ACPI-TM" : "-", edx_flags & (1 << 28) ? "HT" : "-", edx_flags & (1 << 29) ? "TM" : "-"); } - if (genuine_intel) { - model_orig = model; - model = intel_model_duplicates(model); - } + + probe_platform_features(family, model); if (!(edx_flags & (1 << 5))) errx(1, "CPUID: no MSR"); @@ -5656,26 +5762,12 @@ void process_cpuid() __cpuid(0x15, eax_crystal, ebx_tsc, crystal_hz, edx); if (ebx_tsc != 0) { - if (!quiet && (ebx != 0)) fprintf(outf, "CPUID(0x15): eax_crystal: %d ebx_tsc: %d ecx_crystal_hz: %d\n", eax_crystal, ebx_tsc, crystal_hz); if (crystal_hz == 0) - switch (model) { - case INTEL_FAM6_SKYLAKE_L: /* SKL */ - crystal_hz = 24000000; /* 24.0 MHz */ - break; - case INTEL_FAM6_ATOM_GOLDMONT_D: /* DNV */ - crystal_hz = 25000000; /* 25.0 MHz */ - break; - case INTEL_FAM6_ATOM_GOLDMONT: /* BXT */ - case INTEL_FAM6_ATOM_GOLDMONT_PLUS: - crystal_hz = 19200000; /* 19.2 MHz */ - break; - default: - crystal_hz = 0; - } + crystal_hz = platform->crystal_freq; if (crystal_hz) { tsc_hz = (unsigned long long)crystal_hz *ebx_tsc / eax_crystal; @@ -5700,147 +5792,33 @@ void process_cpuid() } if (has_aperf) - aperf_mperf_multiplier = get_aperf_mperf_multiplier(family, model); + aperf_mperf_multiplier = platform->need_perf_multiplier ? 1024 : 1; BIC_PRESENT(BIC_IRQ); BIC_PRESENT(BIC_TSC_MHz); +} - if (probe_nhm_msrs(family, model)) { - do_nhm_platform_info = 1; - BIC_PRESENT(BIC_CPU_c1); - BIC_PRESENT(BIC_CPU_c3); - BIC_PRESENT(BIC_CPU_c6); - BIC_PRESENT(BIC_SMI); - } - do_snb_cstates = has_snb_msrs(family, model); - - if (do_snb_cstates) - BIC_PRESENT(BIC_CPU_c7); - - do_irtl_snb = has_snb_msrs(family, model); - if (do_snb_cstates && (pkg_cstate_limit >= PCL__2)) - BIC_PRESENT(BIC_Pkgpc2); - if (pkg_cstate_limit >= PCL__3) - BIC_PRESENT(BIC_Pkgpc3); - if (pkg_cstate_limit >= PCL__6) - BIC_PRESENT(BIC_Pkgpc6); - if (do_snb_cstates && (pkg_cstate_limit >= PCL__7)) - BIC_PRESENT(BIC_Pkgpc7); - if (has_slv_msrs(family, model)) { - BIC_NOT_PRESENT(BIC_Pkgpc2); - BIC_NOT_PRESENT(BIC_Pkgpc3); - BIC_PRESENT(BIC_Pkgpc6); - BIC_NOT_PRESENT(BIC_Pkgpc7); - BIC_PRESENT(BIC_Mod_c6); - use_c1_residency_msr = 1; - } - if (is_jvl(family, model)) { - BIC_NOT_PRESENT(BIC_CPU_c3); - BIC_NOT_PRESENT(BIC_CPU_c7); - BIC_NOT_PRESENT(BIC_Pkgpc2); - BIC_NOT_PRESENT(BIC_Pkgpc3); - BIC_NOT_PRESENT(BIC_Pkgpc6); - BIC_NOT_PRESENT(BIC_Pkgpc7); - } - if (is_dnv(family, model)) { - BIC_PRESENT(BIC_CPU_c1); - BIC_NOT_PRESENT(BIC_CPU_c3); - BIC_NOT_PRESENT(BIC_Pkgpc3); - BIC_NOT_PRESENT(BIC_CPU_c7); - BIC_NOT_PRESENT(BIC_Pkgpc7); - use_c1_residency_msr = 1; - } - if (is_skx(family, model) || is_icx(family, model) || is_spr(family, model)) { - BIC_NOT_PRESENT(BIC_CPU_c3); - BIC_NOT_PRESENT(BIC_Pkgpc3); - BIC_NOT_PRESENT(BIC_CPU_c7); - BIC_NOT_PRESENT(BIC_Pkgpc7); - } - if (is_bdx(family, model)) { - BIC_NOT_PRESENT(BIC_CPU_c7); - BIC_NOT_PRESENT(BIC_Pkgpc7); - } - if (has_c8910_msrs(family, model)) { - if (pkg_cstate_limit >= PCL__8) - BIC_PRESENT(BIC_Pkgpc8); - if (pkg_cstate_limit >= PCL__9) - BIC_PRESENT(BIC_Pkgpc9); - if (pkg_cstate_limit >= PCL_10) - BIC_PRESENT(BIC_Pkgpc10); - } - do_irtl_hsw = has_c8910_msrs(family, model); - if (has_skl_msrs(family, model)) { - BIC_PRESENT(BIC_Totl_c0); - BIC_PRESENT(BIC_Any_c0); - BIC_PRESENT(BIC_GFX_c0); - BIC_PRESENT(BIC_CPUGFX); - } - do_slm_cstates = is_slm(family, model); - do_knl_cstates = is_knl(family, model); - - if (do_slm_cstates || do_knl_cstates || is_cnl(family, model) || is_ehl(family, model)) - BIC_NOT_PRESENT(BIC_CPU_c3); - - if (!quiet) - decode_misc_pwr_mgmt_msr(); - - if (!quiet && has_slv_msrs(family, model)) - decode_c6_demotion_policy_msr(); - - rapl_probe(family, model); - perf_limit_reasons_probe(family, model); - automatic_cstate_conversion_probe(family, model); - - check_tcc_offset(model_orig); - - if (!quiet) - dump_cstate_pstate_config_info(family, model); - intel_uncore_frequency_probe(); - - if (!quiet) - print_dev_latency(); - if (!quiet) - dump_sysfs_cstate_config(); - if (!quiet) - dump_sysfs_pstate_config(); +void probe_pm_features(void) +{ + probe_pstates(); - if (has_skl_msrs(family, model) || is_ehl(family, model)) - calculate_tsc_tweak(); + probe_cstates(); - if (!access("/sys/class/drm/card0/power/rc6_residency_ms", R_OK)) - BIC_PRESENT(BIC_GFX_rc6); + probe_lpi(); - if (!access("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", R_OK)) - BIC_PRESENT(BIC_GFXMHz); + probe_intel_uncore_frequency(); - if (!access("/sys/class/graphics/fb0/device/drm/card0/gt_act_freq_mhz", R_OK)) - BIC_PRESENT(BIC_GFXACTMHz); + probe_graphics(); - if (!access("/sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us", R_OK)) - BIC_PRESENT(BIC_CPU_LPI); - else - BIC_NOT_PRESENT(BIC_CPU_LPI); + probe_rapl(); - if (!access("/sys/devices/system/cpu/cpu0/thermal_throttle/core_throttle_count", R_OK)) - BIC_PRESENT(BIC_CORE_THROT_CNT); - else - BIC_NOT_PRESENT(BIC_CORE_THROT_CNT); + probe_thermal(); - if (!access(sys_lpi_file_sysfs, R_OK)) { - sys_lpi_file = sys_lpi_file_sysfs; - BIC_PRESENT(BIC_SYS_LPI); - } else if (!access(sys_lpi_file_debugfs, R_OK)) { - sys_lpi_file = sys_lpi_file_debugfs; - BIC_PRESENT(BIC_SYS_LPI); - } else { - sys_lpi_file_sysfs = NULL; - BIC_NOT_PRESENT(BIC_SYS_LPI); - } + if (platform->has_nhm_msrs) + BIC_PRESENT(BIC_SMI); if (!quiet) decode_misc_feature_control(); - - return; } /* @@ -5855,7 +5833,7 @@ int dir_filter(const struct dirent *dirp) return 0; } -void topology_probe() +void topology_probe(bool startup) { int i; int max_core_id = 0; @@ -5888,14 +5866,62 @@ void topology_probe() for_all_proc_cpus(mark_cpu_present); /* - * Validate that all cpus in cpu_subset are also in cpu_present_set + * Allocate and initialize cpu_effective_set + */ + cpu_effective_set = CPU_ALLOC((topo.max_cpu_num + 1)); + if (cpu_effective_set == NULL) + err(3, "CPU_ALLOC"); + cpu_effective_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); + CPU_ZERO_S(cpu_effective_setsize, cpu_effective_set); + update_effective_set(startup); + + /* + * Allocate and initialize cpu_allowed_set + */ + cpu_allowed_set = CPU_ALLOC((topo.max_cpu_num + 1)); + if (cpu_allowed_set == NULL) + err(3, "CPU_ALLOC"); + cpu_allowed_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); + CPU_ZERO_S(cpu_allowed_setsize, cpu_allowed_set); + + /* + * Validate and update cpu_allowed_set. + * + * Make sure all cpus in cpu_subset are also in cpu_present_set during startup. + * Give a warning when cpus in cpu_subset become unavailable at runtime. + * Give a warning when cpus are not effective because of cgroup setting. + * + * cpu_allowed_set is the intersection of cpu_present_set/cpu_effective_set/cpu_subset. */ for (i = 0; i < CPU_SUBSET_MAXCPUS; ++i) { - if (CPU_ISSET_S(i, cpu_subset_size, cpu_subset)) - if (!CPU_ISSET_S(i, cpu_present_setsize, cpu_present_set)) - err(1, "cpu%d not present", i); + if (cpu_subset && !CPU_ISSET_S(i, cpu_subset_size, cpu_subset)) + continue; + + if (!CPU_ISSET_S(i, cpu_present_setsize, cpu_present_set)) { + if (cpu_subset) { + /* cpus in cpu_subset must be in cpu_present_set during startup */ + if (startup) + err(1, "cpu%d not present", i); + else + fprintf(stderr, "cpu%d not present\n", i); + } + continue; + } + + if (CPU_COUNT_S(cpu_effective_setsize, cpu_effective_set)) { + if (!CPU_ISSET_S(i, cpu_effective_setsize, cpu_effective_set)) { + fprintf(stderr, "cpu%d not effective\n", i); + continue; + } + } + + CPU_SET_S(i, cpu_allowed_setsize, cpu_allowed_set); } + if (!CPU_COUNT_S(cpu_allowed_setsize, cpu_allowed_set)) + err(-ENODEV, "No valid cpus found"); + sched_setaffinity(0, cpu_allowed_setsize, cpu_allowed_set); + /* * Allocate and initialize cpu_affinity_set */ @@ -6009,15 +6035,19 @@ void allocate_counters(struct thread_data **t, struct core_data **c, struct pkg_ if (*c == NULL) goto error; - for (i = 0; i < num_cores; i++) + for (i = 0; i < num_cores; i++) { (*c)[i].core_id = -1; + (*c)[i].base_cpu = -1; + } *p = calloc(topo.num_packages, sizeof(struct pkg_data)); if (*p == NULL) goto error; - for (i = 0; i < topo.num_packages; i++) + for (i = 0; i < topo.num_packages; i++) { (*p)[i].package_id = i; + (*p)[i].base_cpu = -1; + } return; error: @@ -6050,10 +6080,11 @@ void init_counter(struct thread_data *thread_base, struct core_data *core_base, p = GET_PKG(pkg_base, pkg_id); t->cpu_id = cpu_id; - if (thread_id == 0) { - t->flags |= CPU_IS_FIRST_THREAD_IN_CORE; - if (cpu_is_first_core_in_package(cpu_id)) - t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE; + if (!cpu_is_not_allowed(cpu_id)) { + if (c->base_cpu < 0) + c->base_cpu = t->cpu_id; + if (p->base_cpu < 0) + p->base_cpu = t->cpu_id; } c->core_id = core_id; @@ -6093,59 +6124,64 @@ void allocate_irq_buffers(void) err(-1, "calloc %d", topo.max_cpu_num + 1); } -void setup_all_buffers(void) +int update_topo(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + topo.allowed_cpus++; + if ((int)t->cpu_id == c->base_cpu) + topo.allowed_cores++; + if ((int)t->cpu_id == p->base_cpu) + topo.allowed_packages++; + + return 0; +} + +void topology_update(void) { - topology_probe(); + topo.allowed_cpus = 0; + topo.allowed_cores = 0; + topo.allowed_packages = 0; + for_all_cpus(update_topo, ODD_COUNTERS); +} +void setup_all_buffers(bool startup) +{ + topology_probe(startup); allocate_irq_buffers(); allocate_fd_percpu(); allocate_counters(&thread_even, &core_even, &package_even); allocate_counters(&thread_odd, &core_odd, &package_odd); allocate_output_buffer(); for_all_proc_cpus(initialize_counters); + topology_update(); } void set_base_cpu(void) { - base_cpu = sched_getcpu(); - if (base_cpu < 0) - err(-ENODEV, "No valid cpus found"); + int i; - if (debug > 1) - fprintf(outf, "base_cpu = %d\n", base_cpu); + for (i = 0; i < topo.max_cpu_num + 1; ++i) { + if (cpu_is_not_allowed(i)) + continue; + base_cpu = i; + if (debug > 1) + fprintf(outf, "base_cpu = %d\n", base_cpu); + return; + } + err(-ENODEV, "No valid cpus found"); } void turbostat_init() { - setup_all_buffers(); + setup_all_buffers(true); set_base_cpu(); check_dev_msr(); check_permissions(); process_cpuid(); + probe_pm_features(); linux_perf_init(); - if (!quiet) - for_all_cpus(print_hwp, ODD_COUNTERS); - - if (!quiet) - for_all_cpus(print_epb, ODD_COUNTERS); - - if (!quiet) - for_all_cpus(print_perf_limit, ODD_COUNTERS); - - if (!quiet) - for_all_cpus(print_rapl, ODD_COUNTERS); - - for_all_cpus(set_temperature_target, ODD_COUNTERS); - for_all_cpus(get_cpu_type, ODD_COUNTERS); for_all_cpus(get_cpu_type, EVEN_COUNTERS); - if (!quiet) - for_all_cpus(print_thermal, ODD_COUNTERS); - - if (!quiet && do_irtl_snb) - print_irtl(); - if (DO_BIC(BIC_IPC)) (void)get_instr_count_fd(base_cpu); } @@ -6160,8 +6196,6 @@ int fork_it(char **argv) first_counter_read = 0; if (status) exit(status); - /* clear affinity side-effect of get_counters() */ - sched_setaffinity(0, cpu_present_setsize, cpu_present_set); gettimeofday(&tv_even, (struct timezone *)NULL); child_pid = fork(); @@ -6225,7 +6259,7 @@ int get_and_dump_counters(void) void print_version() { - fprintf(outf, "turbostat version 2023.03.17 - Len Brown <lenb@kernel.org>\n"); + fprintf(outf, "turbostat version 2023.11.07 - Len Brown <lenb@kernel.org>\n"); } #define COMMAND_LINE_SIZE 2048 @@ -6508,9 +6542,6 @@ void probe_sysfs(void) */ void parse_cpu_command(char *optarg) { - unsigned int start, end; - char *next; - if (!strcmp(optarg, "core")) { if (cpu_subset) goto error; @@ -6533,52 +6564,8 @@ void parse_cpu_command(char *optarg) CPU_ZERO_S(cpu_subset_size, cpu_subset); - next = optarg; - - while (next && *next) { - - if (*next == '-') /* no negative cpu numbers */ - goto error; - - start = strtoul(next, &next, 10); - - if (start >= CPU_SUBSET_MAXCPUS) - goto error; - CPU_SET_S(start, cpu_subset_size, cpu_subset); - - if (*next == '\0') - break; - - if (*next == ',') { - next += 1; - continue; - } - - if (*next == '-') { - next += 1; /* start range */ - } else if (*next == '.') { - next += 1; - if (*next == '.') - next += 1; /* start range */ - else - goto error; - } - - end = strtoul(next, &next, 10); - if (end <= start) - goto error; - - while (++start <= end) { - if (start >= CPU_SUBSET_MAXCPUS) - goto error; - CPU_SET_S(start, cpu_subset_size, cpu_subset); - } - - if (*next == ',') - next += 1; - else if (*next != '\0') - goto error; - } + if (parse_cpu_str(optarg, cpu_subset, cpu_subset_size)) + goto error; return; @@ -6719,6 +6706,19 @@ void cmdline(int argc, char **argv) int main(int argc, char **argv) { + int fd, ret; + + fd = open("/sys/fs/cgroup/cgroup.procs", O_WRONLY); + if (fd < 0) + goto skip_cgroup_setting; + + ret = write(fd, "0\n", 2); + if (ret == -1) + perror("Can't update cgroup\n"); + + close(fd); + +skip_cgroup_setting: outf = stderr; cmdline(argc, argv); diff --git a/tools/testing/selftests/arm64/fp/za-fork.c b/tools/testing/selftests/arm64/fp/za-fork.c index b86cb1049497..587b94648222 100644 --- a/tools/testing/selftests/arm64/fp/za-fork.c +++ b/tools/testing/selftests/arm64/fp/za-fork.c @@ -85,7 +85,7 @@ int main(int argc, char **argv) */ ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0); if (ret >= 0) { - ksft_test_result(fork_test(), "fork_test"); + ksft_test_result(fork_test(), "fork_test\n"); } else { ksft_print_msg("SME not supported\n"); diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index 6ee22c3b251a..518f143c5b0f 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -24,6 +24,7 @@ #include "test_progs.h" #include "network_helpers.h" +#include "netlink_helpers.h" #include "test_tc_neigh_fib.skel.h" #include "test_tc_neigh.skel.h" #include "test_tc_peer.skel.h" @@ -110,11 +111,17 @@ static void netns_setup_namespaces_nofail(const char *verb) } } +enum dev_mode { + MODE_VETH, + MODE_NETKIT, +}; + struct netns_setup_result { - int ifindex_veth_src; - int ifindex_veth_src_fwd; - int ifindex_veth_dst; - int ifindex_veth_dst_fwd; + enum dev_mode dev_mode; + int ifindex_src; + int ifindex_src_fwd; + int ifindex_dst; + int ifindex_dst_fwd; }; static int get_ifaddr(const char *name, char *ifaddr) @@ -137,58 +144,110 @@ static int get_ifaddr(const char *name, char *ifaddr) return 0; } +static int create_netkit(int mode, char *prim, char *peer) +{ + struct rtattr *linkinfo, *data, *peer_info; + struct rtnl_handle rth = { .fd = -1 }; + const char *type = "netkit"; + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = {}; + int err; + + err = rtnl_open(&rth, 0); + if (!ASSERT_OK(err, "open_rtnetlink")) + return err; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = AF_UNSPEC; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, prim, strlen(prim)); + linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, strlen(type)); + data = addattr_nest(&req.n, sizeof(req), IFLA_INFO_DATA); + addattr32(&req.n, sizeof(req), IFLA_NETKIT_MODE, mode); + peer_info = addattr_nest(&req.n, sizeof(req), IFLA_NETKIT_PEER_INFO); + req.n.nlmsg_len += sizeof(struct ifinfomsg); + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, peer, strlen(peer)); + addattr_nest_end(&req.n, peer_info); + addattr_nest_end(&req.n, data); + addattr_nest_end(&req.n, linkinfo); + + err = rtnl_talk(&rth, &req.n, NULL); + ASSERT_OK(err, "talk_rtnetlink"); + rtnl_close(&rth); + return err; +} + static int netns_setup_links_and_routes(struct netns_setup_result *result) { struct nstoken *nstoken = NULL; - char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {}; - - SYS(fail, "ip link add veth_src type veth peer name veth_src_fwd"); - SYS(fail, "ip link add veth_dst type veth peer name veth_dst_fwd"); + char src_fwd_addr[IFADDR_STR_LEN+1] = {}; + int err; - SYS(fail, "ip link set veth_dst_fwd address " MAC_DST_FWD); - SYS(fail, "ip link set veth_dst address " MAC_DST); + if (result->dev_mode == MODE_VETH) { + SYS(fail, "ip link add src type veth peer name src_fwd"); + SYS(fail, "ip link add dst type veth peer name dst_fwd"); + + SYS(fail, "ip link set dst_fwd address " MAC_DST_FWD); + SYS(fail, "ip link set dst address " MAC_DST); + } else if (result->dev_mode == MODE_NETKIT) { + err = create_netkit(NETKIT_L3, "src", "src_fwd"); + if (!ASSERT_OK(err, "create_ifindex_src")) + goto fail; + err = create_netkit(NETKIT_L3, "dst", "dst_fwd"); + if (!ASSERT_OK(err, "create_ifindex_dst")) + goto fail; + } - if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr)) + if (get_ifaddr("src_fwd", src_fwd_addr)) goto fail; - result->ifindex_veth_src = if_nametoindex("veth_src"); - if (!ASSERT_GT(result->ifindex_veth_src, 0, "ifindex_veth_src")) + result->ifindex_src = if_nametoindex("src"); + if (!ASSERT_GT(result->ifindex_src, 0, "ifindex_src")) goto fail; - result->ifindex_veth_src_fwd = if_nametoindex("veth_src_fwd"); - if (!ASSERT_GT(result->ifindex_veth_src_fwd, 0, "ifindex_veth_src_fwd")) + result->ifindex_src_fwd = if_nametoindex("src_fwd"); + if (!ASSERT_GT(result->ifindex_src_fwd, 0, "ifindex_src_fwd")) goto fail; - result->ifindex_veth_dst = if_nametoindex("veth_dst"); - if (!ASSERT_GT(result->ifindex_veth_dst, 0, "ifindex_veth_dst")) + result->ifindex_dst = if_nametoindex("dst"); + if (!ASSERT_GT(result->ifindex_dst, 0, "ifindex_dst")) goto fail; - result->ifindex_veth_dst_fwd = if_nametoindex("veth_dst_fwd"); - if (!ASSERT_GT(result->ifindex_veth_dst_fwd, 0, "ifindex_veth_dst_fwd")) + result->ifindex_dst_fwd = if_nametoindex("dst_fwd"); + if (!ASSERT_GT(result->ifindex_dst_fwd, 0, "ifindex_dst_fwd")) goto fail; - SYS(fail, "ip link set veth_src netns " NS_SRC); - SYS(fail, "ip link set veth_src_fwd netns " NS_FWD); - SYS(fail, "ip link set veth_dst_fwd netns " NS_FWD); - SYS(fail, "ip link set veth_dst netns " NS_DST); + SYS(fail, "ip link set src netns " NS_SRC); + SYS(fail, "ip link set src_fwd netns " NS_FWD); + SYS(fail, "ip link set dst_fwd netns " NS_FWD); + SYS(fail, "ip link set dst netns " NS_DST); /** setup in 'src' namespace */ nstoken = open_netns(NS_SRC); if (!ASSERT_OK_PTR(nstoken, "setns src")) goto fail; - SYS(fail, "ip addr add " IP4_SRC "/32 dev veth_src"); - SYS(fail, "ip addr add " IP6_SRC "/128 dev veth_src nodad"); - SYS(fail, "ip link set dev veth_src up"); + SYS(fail, "ip addr add " IP4_SRC "/32 dev src"); + SYS(fail, "ip addr add " IP6_SRC "/128 dev src nodad"); + SYS(fail, "ip link set dev src up"); - SYS(fail, "ip route add " IP4_DST "/32 dev veth_src scope global"); - SYS(fail, "ip route add " IP4_NET "/16 dev veth_src scope global"); - SYS(fail, "ip route add " IP6_DST "/128 dev veth_src scope global"); + SYS(fail, "ip route add " IP4_DST "/32 dev src scope global"); + SYS(fail, "ip route add " IP4_NET "/16 dev src scope global"); + SYS(fail, "ip route add " IP6_DST "/128 dev src scope global"); - SYS(fail, "ip neigh add " IP4_DST " dev veth_src lladdr %s", - veth_src_fwd_addr); - SYS(fail, "ip neigh add " IP6_DST " dev veth_src lladdr %s", - veth_src_fwd_addr); + if (result->dev_mode == MODE_VETH) { + SYS(fail, "ip neigh add " IP4_DST " dev src lladdr %s", + src_fwd_addr); + SYS(fail, "ip neigh add " IP6_DST " dev src lladdr %s", + src_fwd_addr); + } close_netns(nstoken); @@ -201,15 +260,15 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result) * needs v4 one in order to start ARP probing. IP4_NET route is added * to the endpoints so that the ARP processing will reply. */ - SYS(fail, "ip addr add " IP4_SLL "/32 dev veth_src_fwd"); - SYS(fail, "ip addr add " IP4_DLL "/32 dev veth_dst_fwd"); - SYS(fail, "ip link set dev veth_src_fwd up"); - SYS(fail, "ip link set dev veth_dst_fwd up"); + SYS(fail, "ip addr add " IP4_SLL "/32 dev src_fwd"); + SYS(fail, "ip addr add " IP4_DLL "/32 dev dst_fwd"); + SYS(fail, "ip link set dev src_fwd up"); + SYS(fail, "ip link set dev dst_fwd up"); - SYS(fail, "ip route add " IP4_SRC "/32 dev veth_src_fwd scope global"); - SYS(fail, "ip route add " IP6_SRC "/128 dev veth_src_fwd scope global"); - SYS(fail, "ip route add " IP4_DST "/32 dev veth_dst_fwd scope global"); - SYS(fail, "ip route add " IP6_DST "/128 dev veth_dst_fwd scope global"); + SYS(fail, "ip route add " IP4_SRC "/32 dev src_fwd scope global"); + SYS(fail, "ip route add " IP6_SRC "/128 dev src_fwd scope global"); + SYS(fail, "ip route add " IP4_DST "/32 dev dst_fwd scope global"); + SYS(fail, "ip route add " IP6_DST "/128 dev dst_fwd scope global"); close_netns(nstoken); @@ -218,16 +277,18 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result) if (!ASSERT_OK_PTR(nstoken, "setns dst")) goto fail; - SYS(fail, "ip addr add " IP4_DST "/32 dev veth_dst"); - SYS(fail, "ip addr add " IP6_DST "/128 dev veth_dst nodad"); - SYS(fail, "ip link set dev veth_dst up"); + SYS(fail, "ip addr add " IP4_DST "/32 dev dst"); + SYS(fail, "ip addr add " IP6_DST "/128 dev dst nodad"); + SYS(fail, "ip link set dev dst up"); - SYS(fail, "ip route add " IP4_SRC "/32 dev veth_dst scope global"); - SYS(fail, "ip route add " IP4_NET "/16 dev veth_dst scope global"); - SYS(fail, "ip route add " IP6_SRC "/128 dev veth_dst scope global"); + SYS(fail, "ip route add " IP4_SRC "/32 dev dst scope global"); + SYS(fail, "ip route add " IP4_NET "/16 dev dst scope global"); + SYS(fail, "ip route add " IP6_SRC "/128 dev dst scope global"); - SYS(fail, "ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD); - SYS(fail, "ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD); + if (result->dev_mode == MODE_VETH) { + SYS(fail, "ip neigh add " IP4_SRC " dev dst lladdr " MAC_DST_FWD); + SYS(fail, "ip neigh add " IP6_SRC " dev dst lladdr " MAC_DST_FWD); + } close_netns(nstoken); @@ -293,23 +354,23 @@ static int netns_load_bpf(const struct bpf_program *src_prog, const struct bpf_program *chk_prog, const struct netns_setup_result *setup_result) { - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd); - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_src_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd); int err; - /* tc qdisc add dev veth_src_fwd clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd); - /* tc filter add dev veth_src_fwd ingress bpf da src_prog */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, src_prog, 0); - /* tc filter add dev veth_src_fwd egress bpf da chk_prog */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, chk_prog, 0); + /* tc qdisc add dev src_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_src_fwd, setup_result->ifindex_src_fwd); + /* tc filter add dev src_fwd ingress bpf da src_prog */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS, src_prog, 0); + /* tc filter add dev src_fwd egress bpf da chk_prog */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS, chk_prog, 0); - /* tc qdisc add dev veth_dst_fwd clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); - /* tc filter add dev veth_dst_fwd ingress bpf da dst_prog */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, dst_prog, 0); - /* tc filter add dev veth_dst_fwd egress bpf da chk_prog */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, chk_prog, 0); + /* tc qdisc add dev dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd); + /* tc filter add dev dst_fwd ingress bpf da dst_prog */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, dst_prog, 0); + /* tc filter add dev dst_fwd egress bpf da chk_prog */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, chk_prog, 0); return 0; fail: @@ -539,10 +600,10 @@ done: static int netns_load_dtime_bpf(struct test_tc_dtime *skel, const struct netns_setup_result *setup_result) { - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src_fwd); - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_src); - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst); + LIBBPF_OPTS(bpf_tc_hook, qdisc_src_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_src); + LIBBPF_OPTS(bpf_tc_hook, qdisc_dst); struct nstoken *nstoken; int err; @@ -550,58 +611,58 @@ static int netns_load_dtime_bpf(struct test_tc_dtime *skel, nstoken = open_netns(NS_SRC); if (!ASSERT_OK_PTR(nstoken, "setns " NS_SRC)) return -1; - /* tc qdisc add dev veth_src clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_src, setup_result->ifindex_veth_src); - /* tc filter add dev veth_src ingress bpf da ingress_host */ - XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_INGRESS, skel->progs.ingress_host, 0); - /* tc filter add dev veth_src egress bpf da egress_host */ - XGRESS_FILTER_ADD(&qdisc_veth_src, BPF_TC_EGRESS, skel->progs.egress_host, 0); + /* tc qdisc add dev src clsact */ + QDISC_CLSACT_CREATE(&qdisc_src, setup_result->ifindex_src); + /* tc filter add dev src ingress bpf da ingress_host */ + XGRESS_FILTER_ADD(&qdisc_src, BPF_TC_INGRESS, skel->progs.ingress_host, 0); + /* tc filter add dev src egress bpf da egress_host */ + XGRESS_FILTER_ADD(&qdisc_src, BPF_TC_EGRESS, skel->progs.egress_host, 0); close_netns(nstoken); /* setup ns_dst tc progs */ nstoken = open_netns(NS_DST); if (!ASSERT_OK_PTR(nstoken, "setns " NS_DST)) return -1; - /* tc qdisc add dev veth_dst clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_dst, setup_result->ifindex_veth_dst); - /* tc filter add dev veth_dst ingress bpf da ingress_host */ - XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_INGRESS, skel->progs.ingress_host, 0); - /* tc filter add dev veth_dst egress bpf da egress_host */ - XGRESS_FILTER_ADD(&qdisc_veth_dst, BPF_TC_EGRESS, skel->progs.egress_host, 0); + /* tc qdisc add dev dst clsact */ + QDISC_CLSACT_CREATE(&qdisc_dst, setup_result->ifindex_dst); + /* tc filter add dev dst ingress bpf da ingress_host */ + XGRESS_FILTER_ADD(&qdisc_dst, BPF_TC_INGRESS, skel->progs.ingress_host, 0); + /* tc filter add dev dst egress bpf da egress_host */ + XGRESS_FILTER_ADD(&qdisc_dst, BPF_TC_EGRESS, skel->progs.egress_host, 0); close_netns(nstoken); /* setup ns_fwd tc progs */ nstoken = open_netns(NS_FWD); if (!ASSERT_OK_PTR(nstoken, "setns " NS_FWD)) return -1; - /* tc qdisc add dev veth_dst_fwd clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); - /* tc filter add dev veth_dst_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, + /* tc qdisc add dev dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd); + /* tc filter add dev dst_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, skel->progs.ingress_fwdns_prio100, 100); - /* tc filter add dev veth_dst_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, + /* tc filter add dev dst_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, skel->progs.ingress_fwdns_prio101, 101); - /* tc filter add dev veth_dst_fwd egress prio 100 bpf da egress_fwdns_prio100 */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, + /* tc filter add dev dst_fwd egress prio 100 bpf da egress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, skel->progs.egress_fwdns_prio100, 100); - /* tc filter add dev veth_dst_fwd egress prio 101 bpf da egress_fwdns_prio101 */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, + /* tc filter add dev dst_fwd egress prio 101 bpf da egress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, skel->progs.egress_fwdns_prio101, 101); - /* tc qdisc add dev veth_src_fwd clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_src_fwd, setup_result->ifindex_veth_src_fwd); - /* tc filter add dev veth_src_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, + /* tc qdisc add dev src_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_src_fwd, setup_result->ifindex_src_fwd); + /* tc filter add dev src_fwd ingress prio 100 bpf da ingress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS, skel->progs.ingress_fwdns_prio100, 100); - /* tc filter add dev veth_src_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_INGRESS, + /* tc filter add dev src_fwd ingress prio 101 bpf da ingress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_INGRESS, skel->progs.ingress_fwdns_prio101, 101); - /* tc filter add dev veth_src_fwd egress prio 100 bpf da egress_fwdns_prio100 */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, + /* tc filter add dev src_fwd egress prio 100 bpf da egress_fwdns_prio100 */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS, skel->progs.egress_fwdns_prio100, 100); - /* tc filter add dev veth_src_fwd egress prio 101 bpf da egress_fwdns_prio101 */ - XGRESS_FILTER_ADD(&qdisc_veth_src_fwd, BPF_TC_EGRESS, + /* tc filter add dev src_fwd egress prio 101 bpf da egress_fwdns_prio101 */ + XGRESS_FILTER_ADD(&qdisc_src_fwd, BPF_TC_EGRESS, skel->progs.egress_fwdns_prio101, 101); close_netns(nstoken); return 0; @@ -777,8 +838,8 @@ static void test_tc_redirect_dtime(struct netns_setup_result *setup_result) if (!ASSERT_OK_PTR(skel, "test_tc_dtime__open")) return; - skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd; - skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd; + skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd; + skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd; err = test_tc_dtime__load(skel); if (!ASSERT_OK(err, "test_tc_dtime__load")) @@ -868,8 +929,8 @@ static void test_tc_redirect_neigh(struct netns_setup_result *setup_result) if (!ASSERT_OK_PTR(skel, "test_tc_neigh__open")) goto done; - skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd; - skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd; + skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd; + skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd; err = test_tc_neigh__load(skel); if (!ASSERT_OK(err, "test_tc_neigh__load")) @@ -904,8 +965,8 @@ static void test_tc_redirect_peer(struct netns_setup_result *setup_result) if (!ASSERT_OK_PTR(skel, "test_tc_peer__open")) goto done; - skel->rodata->IFINDEX_SRC = setup_result->ifindex_veth_src_fwd; - skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd; + skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd; + skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd; err = test_tc_peer__load(skel); if (!ASSERT_OK(err, "test_tc_peer__load")) @@ -996,7 +1057,7 @@ static int tun_relay_loop(int src_fd, int target_fd) static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) { LIBBPF_OPTS(bpf_tc_hook, qdisc_tun_fwd); - LIBBPF_OPTS(bpf_tc_hook, qdisc_veth_dst_fwd); + LIBBPF_OPTS(bpf_tc_hook, qdisc_dst_fwd); struct test_tc_peer *skel = NULL; struct nstoken *nstoken = NULL; int err; @@ -1045,7 +1106,7 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) goto fail; skel->rodata->IFINDEX_SRC = ifindex; - skel->rodata->IFINDEX_DST = setup_result->ifindex_veth_dst_fwd; + skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd; err = test_tc_peer__load(skel); if (!ASSERT_OK(err, "test_tc_peer__load")) @@ -1053,19 +1114,19 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) /* Load "tc_src_l3" to the tun_fwd interface to redirect packets * towards dst, and "tc_dst" to redirect packets - * and "tc_chk" on veth_dst_fwd to drop non-redirected packets. + * and "tc_chk" on dst_fwd to drop non-redirected packets. */ /* tc qdisc add dev tun_fwd clsact */ QDISC_CLSACT_CREATE(&qdisc_tun_fwd, ifindex); /* tc filter add dev tun_fwd ingress bpf da tc_src_l3 */ XGRESS_FILTER_ADD(&qdisc_tun_fwd, BPF_TC_INGRESS, skel->progs.tc_src_l3, 0); - /* tc qdisc add dev veth_dst_fwd clsact */ - QDISC_CLSACT_CREATE(&qdisc_veth_dst_fwd, setup_result->ifindex_veth_dst_fwd); - /* tc filter add dev veth_dst_fwd ingress bpf da tc_dst_l3 */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_INGRESS, skel->progs.tc_dst_l3, 0); - /* tc filter add dev veth_dst_fwd egress bpf da tc_chk */ - XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0); + /* tc qdisc add dev dst_fwd clsact */ + QDISC_CLSACT_CREATE(&qdisc_dst_fwd, setup_result->ifindex_dst_fwd); + /* tc filter add dev dst_fwd ingress bpf da tc_dst_l3 */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_INGRESS, skel->progs.tc_dst_l3, 0); + /* tc filter add dev dst_fwd egress bpf da tc_chk */ + XGRESS_FILTER_ADD(&qdisc_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0); /* Setup route and neigh tables */ SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24"); @@ -1074,17 +1135,17 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result) SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad"); SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad"); - SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global"); + SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev src scope global"); SYS(fail, "ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD " dev tun_src scope global"); - SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global"); - SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global"); + SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev dst scope global"); + SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev src scope global"); SYS(fail, "ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD " dev tun_src scope global"); - SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global"); + SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev dst scope global"); - SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD); - SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD); + SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev dst lladdr " MAC_DST_FWD); + SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev dst lladdr " MAC_DST_FWD); if (!ASSERT_OK(set_forwarding(false), "disable forwarding")) goto fail; @@ -1106,9 +1167,9 @@ fail: close_netns(nstoken); } -#define RUN_TEST(name) \ +#define RUN_TEST(name, mode) \ ({ \ - struct netns_setup_result setup_result; \ + struct netns_setup_result setup_result = { .dev_mode = mode, }; \ if (test__start_subtest(#name)) \ if (ASSERT_OK(netns_setup_namespaces("add"), "setup namespaces")) { \ if (ASSERT_OK(netns_setup_links_and_routes(&setup_result), \ @@ -1122,11 +1183,13 @@ static void *test_tc_redirect_run_tests(void *arg) { netns_setup_namespaces_nofail("delete"); - RUN_TEST(tc_redirect_peer); - RUN_TEST(tc_redirect_peer_l3); - RUN_TEST(tc_redirect_neigh); - RUN_TEST(tc_redirect_neigh_fib); - RUN_TEST(tc_redirect_dtime); + RUN_TEST(tc_redirect_peer, MODE_VETH); + RUN_TEST(tc_redirect_peer, MODE_NETKIT); + RUN_TEST(tc_redirect_peer_l3, MODE_VETH); + RUN_TEST(tc_redirect_peer_l3, MODE_NETKIT); + RUN_TEST(tc_redirect_neigh, MODE_VETH); + RUN_TEST(tc_redirect_neigh_fib, MODE_VETH); + RUN_TEST(tc_redirect_dtime, MODE_VETH); return NULL; } diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index e5c61aa6604a..5cfa7a6316b6 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -31,6 +31,7 @@ #include "verifier_helper_restricted.skel.h" #include "verifier_helper_value_access.skel.h" #include "verifier_int_ptr.skel.h" +#include "verifier_iterating_callbacks.skel.h" #include "verifier_jeq_infer_not_null.skel.h" #include "verifier_ld_ind.skel.h" #include "verifier_ldsx.skel.h" @@ -139,6 +140,7 @@ void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_acces void test_verifier_helper_restricted(void) { RUN(verifier_helper_restricted); } void test_verifier_helper_value_access(void) { RUN(verifier_helper_value_access); } void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); } +void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); } void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); } void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); } void test_verifier_ldsx(void) { RUN(verifier_ldsx); } diff --git a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c index 4ce76eb064c4..d461746fd3c1 100644 --- a/tools/testing/selftests/bpf/progs/bpf_loop_bench.c +++ b/tools/testing/selftests/bpf/progs/bpf_loop_bench.c @@ -15,13 +15,16 @@ static int empty_callback(__u32 index, void *data) return 0; } +static int outer_loop(__u32 index, void *data) +{ + bpf_loop(nr_loops, empty_callback, NULL, 0); + __sync_add_and_fetch(&hits, nr_loops); + return 0; +} + SEC("fentry/" SYS_PREFIX "sys_getpgid") int benchmark(void *ctx) { - for (int i = 0; i < 1000; i++) { - bpf_loop(nr_loops, empty_callback, NULL, 0); - - __sync_add_and_fetch(&hits, nr_loops); - } + bpf_loop(1000, outer_loop, NULL, 0); return 0; } diff --git a/tools/testing/selftests/bpf/progs/cb_refs.c b/tools/testing/selftests/bpf/progs/cb_refs.c index 76d661b20e87..56c764df8196 100644 --- a/tools/testing/selftests/bpf/progs/cb_refs.c +++ b/tools/testing/selftests/bpf/progs/cb_refs.c @@ -33,6 +33,7 @@ int underflow_prog(void *ctx) if (!p) return 0; bpf_for_each_map_elem(&array_map, cb1, &p, 0); + bpf_kfunc_call_test_release(p); return 0; } diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c index 4c39e920dac2..8c0ef2742208 100644 --- a/tools/testing/selftests/bpf/progs/exceptions_fail.c +++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c @@ -171,6 +171,7 @@ int reject_with_rbtree_add_throw(void *ctx) return 0; bpf_spin_lock(&lock); bpf_rbtree_add(&rbtree, &f->node, rbless); + bpf_spin_unlock(&lock); return 0; } @@ -214,6 +215,7 @@ int reject_with_cb_reference(void *ctx) if (!f) return 0; bpf_loop(5, subprog_cb_ref, NULL, 0); + bpf_obj_drop(f); return 0; } diff --git a/tools/testing/selftests/bpf/progs/strobemeta.h b/tools/testing/selftests/bpf/progs/strobemeta.h index e02cfd380746..40df2cc26eaf 100644 --- a/tools/testing/selftests/bpf/progs/strobemeta.h +++ b/tools/testing/selftests/bpf/progs/strobemeta.h @@ -24,9 +24,11 @@ struct task_struct {}; #define STACK_TABLE_EPOCH_SHIFT 20 #define STROBE_MAX_STR_LEN 1 #define STROBE_MAX_CFGS 32 +#define READ_MAP_VAR_PAYLOAD_CAP \ + ((1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) #define STROBE_MAX_PAYLOAD \ (STROBE_MAX_STRS * STROBE_MAX_STR_LEN + \ - STROBE_MAX_MAPS * (1 + STROBE_MAX_MAP_ENTRIES * 2) * STROBE_MAX_STR_LEN) + STROBE_MAX_MAPS * READ_MAP_VAR_PAYLOAD_CAP) struct strobe_value_header { /* @@ -355,7 +357,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, size_t idx, void *tls_base, struct strobe_value_generic *value, struct strobemeta_payload *data, - void *payload) + size_t off) { void *location; uint64_t len; @@ -366,7 +368,7 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, return 0; bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr); + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, value->ptr); /* * if bpf_probe_read_user_str returns error (<0), due to casting to * unsinged int, it will become big number, so next check is @@ -378,14 +380,14 @@ static __always_inline uint64_t read_str_var(struct strobemeta_cfg *cfg, return 0; data->str_lens[idx] = len; - return len; + return off + len; } -static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, - size_t idx, void *tls_base, - struct strobe_value_generic *value, - struct strobemeta_payload *data, - void *payload) +static __always_inline uint64_t read_map_var(struct strobemeta_cfg *cfg, + size_t idx, void *tls_base, + struct strobe_value_generic *value, + struct strobemeta_payload *data, + size_t off) { struct strobe_map_descr* descr = &data->map_descrs[idx]; struct strobe_map_raw map; @@ -397,11 +399,11 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, location = calc_location(&cfg->map_locs[idx], tls_base); if (!location) - return payload; + return off; bpf_probe_read_user(value, sizeof(struct strobe_value_generic), location); if (bpf_probe_read_user(&map, sizeof(struct strobe_map_raw), value->ptr)) - return payload; + return off; descr->id = map.id; descr->cnt = map.cnt; @@ -410,10 +412,10 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, data->req_meta_valid = 1; } - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, map.tag); + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.tag); if (len <= STROBE_MAX_STR_LEN) { descr->tag_len = len; - payload += len; + off += len; } #ifdef NO_UNROLL @@ -426,22 +428,22 @@ static __always_inline void *read_map_var(struct strobemeta_cfg *cfg, break; descr->key_lens[i] = 0; - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.entries[i].key); if (len <= STROBE_MAX_STR_LEN) { descr->key_lens[i] = len; - payload += len; + off += len; } descr->val_lens[i] = 0; - len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, + len = bpf_probe_read_user_str(&data->payload[off], STROBE_MAX_STR_LEN, map.entries[i].val); if (len <= STROBE_MAX_STR_LEN) { descr->val_lens[i] = len; - payload += len; + off += len; } } - return payload; + return off; } #ifdef USE_BPF_LOOP @@ -455,14 +457,20 @@ struct read_var_ctx { struct strobemeta_payload *data; void *tls_base; struct strobemeta_cfg *cfg; - void *payload; + size_t payload_off; /* value gets mutated */ struct strobe_value_generic *value; enum read_type type; }; -static int read_var_callback(__u32 index, struct read_var_ctx *ctx) +static int read_var_callback(__u64 index, struct read_var_ctx *ctx) { + /* lose precision info for ctx->payload_off, verifier won't track + * double xor, barrier_var() is needed to force clang keep both xors. + */ + ctx->payload_off ^= index; + barrier_var(ctx->payload_off); + ctx->payload_off ^= index; switch (ctx->type) { case READ_INT_VAR: if (index >= STROBE_MAX_INTS) @@ -472,14 +480,18 @@ static int read_var_callback(__u32 index, struct read_var_ctx *ctx) case READ_MAP_VAR: if (index >= STROBE_MAX_MAPS) return 1; - ctx->payload = read_map_var(ctx->cfg, index, ctx->tls_base, - ctx->value, ctx->data, ctx->payload); + if (ctx->payload_off > sizeof(ctx->data->payload) - READ_MAP_VAR_PAYLOAD_CAP) + return 1; + ctx->payload_off = read_map_var(ctx->cfg, index, ctx->tls_base, + ctx->value, ctx->data, ctx->payload_off); break; case READ_STR_VAR: if (index >= STROBE_MAX_STRS) return 1; - ctx->payload += read_str_var(ctx->cfg, index, ctx->tls_base, - ctx->value, ctx->data, ctx->payload); + if (ctx->payload_off > sizeof(ctx->data->payload) - STROBE_MAX_STR_LEN) + return 1; + ctx->payload_off = read_str_var(ctx->cfg, index, ctx->tls_base, + ctx->value, ctx->data, ctx->payload_off); break; } return 0; @@ -501,7 +513,8 @@ static void *read_strobe_meta(struct task_struct *task, pid_t pid = bpf_get_current_pid_tgid() >> 32; struct strobe_value_generic value = {0}; struct strobemeta_cfg *cfg; - void *tls_base, *payload; + size_t payload_off; + void *tls_base; cfg = bpf_map_lookup_elem(&strobemeta_cfgs, &pid); if (!cfg) @@ -509,7 +522,7 @@ static void *read_strobe_meta(struct task_struct *task, data->int_vals_set_mask = 0; data->req_meta_valid = 0; - payload = data->payload; + payload_off = 0; /* * we don't have struct task_struct definition, it should be: * tls_base = (void *)task->thread.fsbase; @@ -522,7 +535,7 @@ static void *read_strobe_meta(struct task_struct *task, .tls_base = tls_base, .value = &value, .data = data, - .payload = payload, + .payload_off = 0, }; int err; @@ -540,6 +553,11 @@ static void *read_strobe_meta(struct task_struct *task, err = bpf_loop(STROBE_MAX_MAPS, read_var_callback, &ctx, 0); if (err != STROBE_MAX_MAPS) return NULL; + + payload_off = ctx.payload_off; + /* this should not really happen, here only to satisfy verifer */ + if (payload_off > sizeof(data->payload)) + payload_off = sizeof(data->payload); #else #ifdef NO_UNROLL #pragma clang loop unroll(disable) @@ -555,7 +573,7 @@ static void *read_strobe_meta(struct task_struct *task, #pragma unroll #endif /* NO_UNROLL */ for (int i = 0; i < STROBE_MAX_STRS; ++i) { - payload += read_str_var(cfg, i, tls_base, &value, data, payload); + payload_off = read_str_var(cfg, i, tls_base, &value, data, payload_off); } #ifdef NO_UNROLL #pragma clang loop unroll(disable) @@ -563,7 +581,7 @@ static void *read_strobe_meta(struct task_struct *task, #pragma unroll #endif /* NO_UNROLL */ for (int i = 0; i < STROBE_MAX_MAPS; ++i) { - payload = read_map_var(cfg, i, tls_base, &value, data, payload); + payload_off = read_map_var(cfg, i, tls_base, &value, data, payload_off); } #endif /* USE_BPF_LOOP */ @@ -571,7 +589,7 @@ static void *read_strobe_meta(struct task_struct *task, * return pointer right after end of payload, so it's possible to * calculate exact amount of useful data that needs to be sent */ - return payload; + return &data->payload[payload_off]; } SEC("raw_tracepoint/kfree_skb") diff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c index df7697b94007..c1f55e1d80a4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_cfg.c +++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c @@ -97,4 +97,66 @@ l0_%=: r2 = r0; \ " ::: __clobber_all); } +SEC("socket") +__description("conditional loop (2)") +__success +__failure_unpriv __msg_unpriv("back-edge from insn 10 to 11") +__naked void conditional_loop2(void) +{ + asm volatile (" \ + r9 = 2 ll; \ + r3 = 0x20 ll; \ + r4 = 0x35 ll; \ + r8 = r4; \ + goto l1_%=; \ +l0_%=: r9 -= r3; \ + r9 -= r4; \ + r9 -= r8; \ +l1_%=: r8 += r4; \ + if r8 < 0x64 goto l0_%=; \ + r0 = r9; \ + exit; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("unconditional loop after conditional jump") +__failure __msg("infinite loop detected") +__failure_unpriv __msg_unpriv("back-edge from insn 3 to 2") +__naked void uncond_loop_after_cond_jmp(void) +{ + asm volatile (" \ + r0 = 0; \ + if r0 > 0 goto l1_%=; \ +l0_%=: r0 = 1; \ + goto l0_%=; \ +l1_%=: exit; \ +" ::: __clobber_all); +} + + +__naked __noinline __used +static unsigned long never_ending_subprog() +{ + asm volatile (" \ + r0 = r1; \ + goto -1; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("unconditional loop after conditional jump") +/* infinite loop is detected *after* check_cfg() */ +__failure __msg("infinite loop detected") +__naked void uncond_loop_in_subprog_after_cond_jmp(void) +{ + asm volatile (" \ + r0 = 0; \ + if r0 > 0 goto l1_%=; \ +l0_%=: r0 += 1; \ + call never_ending_subprog; \ +l1_%=: exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c new file mode 100644 index 000000000000..5905e036e0ea --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> +#include "bpf_misc.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 8); + __type(key, __u32); + __type(value, __u64); +} map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_USER_RINGBUF); + __uint(max_entries, 8); +} ringbuf SEC(".maps"); + +struct vm_area_struct; +struct bpf_map; + +struct buf_context { + char *buf; +}; + +struct num_context { + __u64 i; + __u64 j; +}; + +__u8 choice_arr[2] = { 0, 1 }; + +static int unsafe_on_2nd_iter_cb(__u32 idx, struct buf_context *ctx) +{ + if (idx == 0) { + ctx->buf = (char *)(0xDEAD); + return 0; + } + + if (bpf_probe_read_user(ctx->buf, 8, (void *)(0xBADC0FFEE))) + return 1; + + return 0; +} + +SEC("?raw_tp") +__failure __msg("R1 type=scalar expected=fp") +int unsafe_on_2nd_iter(void *unused) +{ + char buf[4]; + struct buf_context loop_ctx = { .buf = buf }; + + bpf_loop(100, unsafe_on_2nd_iter_cb, &loop_ctx, 0); + return 0; +} + +static int unsafe_on_zero_iter_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i = 0; + return 0; +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_on_zero_iter(void *unused) +{ + struct num_context loop_ctx = { .i = 32 }; + + bpf_loop(100, unsafe_on_zero_iter_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static int widening_cb(__u32 idx, struct num_context *ctx) +{ + ++ctx->i; + return 0; +} + +SEC("?raw_tp") +__success +int widening(void *unused) +{ + struct num_context loop_ctx = { .i = 0, .j = 1 }; + + bpf_loop(100, widening_cb, &loop_ctx, 0); + /* loop_ctx.j is not changed during callback iteration, + * verifier should not apply widening to it. + */ + return choice_arr[loop_ctx.j]; +} + +static int loop_detection_cb(__u32 idx, struct num_context *ctx) +{ + for (;;) {} + return 0; +} + +SEC("?raw_tp") +__failure __msg("infinite loop detected") +int loop_detection(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_loop(100, loop_detection_cb, &loop_ctx, 0); + return 0; +} + +static __always_inline __u64 oob_state_machine(struct num_context *ctx) +{ + switch (ctx->i) { + case 0: + ctx->i = 1; + break; + case 1: + ctx->i = 32; + break; + } + return 0; +} + +static __u64 for_each_map_elem_cb(struct bpf_map *map, __u32 *key, __u64 *val, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_for_each_map_elem(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_for_each_map_elem(&map, for_each_map_elem_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static __u64 ringbuf_drain_cb(struct bpf_dynptr *dynptr, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_ringbuf_drain(void *unused) +{ + struct num_context loop_ctx = { .i = 0 }; + + bpf_user_ringbuf_drain(&ringbuf, ringbuf_drain_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static __u64 find_vma_cb(struct task_struct *task, struct vm_area_struct *vma, void *data) +{ + return oob_state_machine(data); +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=32 size=1") +int unsafe_find_vma(void *unused) +{ + struct task_struct *task = bpf_get_current_task_btf(); + struct num_context loop_ctx = { .i = 0 }; + + bpf_find_vma(task, 0, find_vma_cb, &loop_ctx, 0); + return choice_arr[loop_ctx.i]; +} + +static int iter_limit_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i++; + return 0; +} + +SEC("?raw_tp") +__success +int bpf_loop_iter_limit_ok(void *unused) +{ + struct num_context ctx = { .i = 0 }; + + bpf_loop(1, iter_limit_cb, &ctx, 0); + return choice_arr[ctx.i]; +} + +SEC("?raw_tp") +__failure __msg("invalid access to map value, value_size=2 off=2 size=1") +int bpf_loop_iter_limit_overflow(void *unused) +{ + struct num_context ctx = { .i = 0 }; + + bpf_loop(2, iter_limit_cb, &ctx, 0); + return choice_arr[ctx.i]; +} + +static int iter_limit_level2a_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 100; + return 0; +} + +static int iter_limit_level2b_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 10; + return 0; +} + +static int iter_limit_level1_cb(__u32 idx, struct num_context *ctx) +{ + ctx->i += 1; + bpf_loop(1, iter_limit_level2a_cb, ctx, 0); + bpf_loop(1, iter_limit_level2b_cb, ctx, 0); + return 0; +} + +/* Check that path visiting every callback function once had been + * reached by verifier. Variables 'ctx{1,2}i' below serve as flags, + * with each decimal digit corresponding to a callback visit marker. + */ +SEC("socket") +__success __retval(111111) +int bpf_loop_iter_limit_nested(void *unused) +{ + struct num_context ctx1 = { .i = 0 }; + struct num_context ctx2 = { .i = 0 }; + __u64 a, b, c; + + bpf_loop(1, iter_limit_level1_cb, &ctx1, 0); + bpf_loop(1, iter_limit_level1_cb, &ctx2, 0); + a = ctx1.i; + b = ctx2.i; + /* Force 'ctx1.i' and 'ctx2.i' precise. */ + c = choice_arr[(a + b) % 2]; + /* This makes 'c' zero, but neither clang nor verifier know it. */ + c /= 10; + /* Make sure that verifier does not visit 'impossible' states: + * enumerate all possible callback visit masks. + */ + if (a != 0 && a != 1 && a != 11 && a != 101 && a != 111 && + b != 0 && b != 1 && b != 11 && b != 101 && b != 111) + asm volatile ("r0 /= 0;" ::: "r0"); + return 1000 * a + b + c; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_loops1.c b/tools/testing/selftests/bpf/progs/verifier_loops1.c index 5bc86af80a9a..71735dbf33d4 100644 --- a/tools/testing/selftests/bpf/progs/verifier_loops1.c +++ b/tools/testing/selftests/bpf/progs/verifier_loops1.c @@ -75,9 +75,10 @@ l0_%=: r0 += 1; \ " ::: __clobber_all); } -SEC("tracepoint") +SEC("socket") __description("bounded loop, start in the middle") -__failure __msg("back-edge") +__success +__failure_unpriv __msg_unpriv("back-edge") __naked void loop_start_in_the_middle(void) { asm volatile (" \ @@ -136,7 +137,9 @@ l0_%=: exit; \ SEC("tracepoint") __description("bounded recursion") -__failure __msg("back-edge") +__failure +/* verifier limitation in detecting max stack depth */ +__msg("the call stack of 8 frames is too deep !") __naked void bounded_recursion(void) { asm volatile (" \ diff --git a/tools/testing/selftests/bpf/progs/verifier_precision.c b/tools/testing/selftests/bpf/progs/verifier_precision.c index 193c0f8272d0..6b564d4c0986 100644 --- a/tools/testing/selftests/bpf/progs/verifier_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_precision.c @@ -91,3 +91,43 @@ __naked int bpf_end_bswap(void) } #endif /* v4 instruction */ + +SEC("?raw_tp") +__success __log_level(2) +/* + * Without the bug fix there will be no history between "last_idx 3 first_idx 3" + * and "parent state regs=" lines. "R0_w=6" parts are here to help anchor + * expected log messages to the one specific mark_chain_precision operation. + * + * This is quite fragile: if verifier checkpointing heuristic changes, this + * might need adjusting. + */ +__msg("2: (07) r0 += 1 ; R0_w=6") +__msg("3: (35) if r0 >= 0xa goto pc+1") +__msg("mark_precise: frame0: last_idx 3 first_idx 3 subseq_idx -1") +__msg("mark_precise: frame0: regs=r0 stack= before 2: (07) r0 += 1") +__msg("mark_precise: frame0: regs=r0 stack= before 1: (07) r0 += 1") +__msg("mark_precise: frame0: regs=r0 stack= before 4: (05) goto pc-4") +__msg("mark_precise: frame0: regs=r0 stack= before 3: (35) if r0 >= 0xa goto pc+1") +__msg("mark_precise: frame0: parent state regs= stack=: R0_rw=P4") +__msg("3: R0_w=6") +__naked int state_loop_first_last_equal(void) +{ + asm volatile ( + "r0 = 0;" + "l0_%=:" + "r0 += 1;" + "r0 += 1;" + /* every few iterations we'll have a checkpoint here with + * first_idx == last_idx, potentially confusing precision + * backtracking logic + */ + "if r0 >= 10 goto l1_%=;" /* checkpoint + mark_precise */ + "goto l0_%=;" + "l1_%=:" + "exit;" + ::: __clobber_common + ); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c index db6b3143338b..f61d623b1ce8 100644 --- a/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c +++ b/tools/testing/selftests/bpf/progs/verifier_subprog_precision.c @@ -119,15 +119,41 @@ __naked int global_subprog_result_precise(void) SEC("?raw_tp") __success __log_level(2) +/* First simulated path does not include callback body, + * r1 and r4 are always precise for bpf_loop() calls. + */ +__msg("9: (85) call bpf_loop#181") +__msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") +__msg("mark_precise: frame0: parent state regs=r4 stack=:") +__msg("mark_precise: frame0: last_idx 8 first_idx 0 subseq_idx 9") +__msg("mark_precise: frame0: regs=r4 stack= before 8: (b7) r4 = 0") +__msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") +__msg("mark_precise: frame0: parent state regs=r1 stack=:") +__msg("mark_precise: frame0: last_idx 8 first_idx 0 subseq_idx 9") +__msg("mark_precise: frame0: regs=r1 stack= before 8: (b7) r4 = 0") +__msg("mark_precise: frame0: regs=r1 stack= before 7: (b7) r3 = 0") +__msg("mark_precise: frame0: regs=r1 stack= before 6: (bf) r2 = r8") +__msg("mark_precise: frame0: regs=r1 stack= before 5: (bf) r1 = r6") +__msg("mark_precise: frame0: regs=r6 stack= before 4: (b7) r6 = 3") +/* r6 precision propagation */ __msg("14: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 14 first_idx 10") +__msg("mark_precise: frame0: last_idx 14 first_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4") __msg("mark_precise: frame0: regs=r6 stack= before 11: (25) if r6 > 0x3 goto pc+4") __msg("mark_precise: frame0: regs=r6 stack= before 10: (bf) r6 = r0") -__msg("mark_precise: frame0: parent state regs=r0 stack=:") -__msg("mark_precise: frame0: last_idx 18 first_idx 0") -__msg("mark_precise: frame0: regs=r0 stack= before 18: (95) exit") +__msg("mark_precise: frame0: regs=r0 stack= before 9: (85) call bpf_loop") +/* State entering callback body popped from states stack */ +__msg("from 9 to 17: frame1:") +__msg("17: frame1: R1=scalar() R2=0 R10=fp0 cb") +__msg("17: (b7) r0 = 0") +__msg("18: (95) exit") +__msg("returning from callee:") +__msg("to caller at 9:") +__msg("frame 0: propagating r1,r4") +__msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") +__msg("mark_precise: frame0: regs=r1,r4 stack= before 18: (95) exit") +__msg("from 18 to 9: safe") __naked int callback_result_precise(void) { asm volatile ( @@ -233,20 +259,36 @@ __naked int parent_callee_saved_reg_precise_global(void) SEC("?raw_tp") __success __log_level(2) +/* First simulated path does not include callback body */ __msg("12: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 12 first_idx 10") +__msg("mark_precise: frame0: last_idx 12 first_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 11: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 10: (27) r6 *= 4") +__msg("mark_precise: frame0: regs=r6 stack= before 9: (85) call bpf_loop") __msg("mark_precise: frame0: parent state regs=r6 stack=:") -__msg("mark_precise: frame0: last_idx 16 first_idx 0") -__msg("mark_precise: frame0: regs=r6 stack= before 16: (95) exit") -__msg("mark_precise: frame1: regs= stack= before 15: (b7) r0 = 0") -__msg("mark_precise: frame1: regs= stack= before 9: (85) call bpf_loop#181") +__msg("mark_precise: frame0: last_idx 8 first_idx 0 subseq_idx 9") __msg("mark_precise: frame0: regs=r6 stack= before 8: (b7) r4 = 0") __msg("mark_precise: frame0: regs=r6 stack= before 7: (b7) r3 = 0") __msg("mark_precise: frame0: regs=r6 stack= before 6: (bf) r2 = r8") __msg("mark_precise: frame0: regs=r6 stack= before 5: (b7) r1 = 1") __msg("mark_precise: frame0: regs=r6 stack= before 4: (b7) r6 = 3") +/* State entering callback body popped from states stack */ +__msg("from 9 to 15: frame1:") +__msg("15: frame1: R1=scalar() R2=0 R10=fp0 cb") +__msg("15: (b7) r0 = 0") +__msg("16: (95) exit") +__msg("returning from callee:") +__msg("to caller at 9:") +/* r1, r4 are always precise for bpf_loop(), + * r6 was marked before backtracking to callback body. + */ +__msg("frame 0: propagating r1,r4,r6") +__msg("mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1") +__msg("mark_precise: frame0: regs=r1,r4,r6 stack= before 16: (95) exit") +__msg("mark_precise: frame1: regs= stack= before 15: (b7) r0 = 0") +__msg("mark_precise: frame1: regs= stack= before 9: (85) call bpf_loop") +__msg("mark_precise: frame0: parent state regs= stack=:") +__msg("from 16 to 9: safe") __naked int parent_callee_saved_reg_precise_with_callback(void) { asm volatile ( @@ -373,22 +415,38 @@ __naked int parent_stack_slot_precise_global(void) SEC("?raw_tp") __success __log_level(2) +/* First simulated path does not include callback body */ __msg("14: (0f) r1 += r6") -__msg("mark_precise: frame0: last_idx 14 first_idx 11") +__msg("mark_precise: frame0: last_idx 14 first_idx 10") __msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7") __msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4") __msg("mark_precise: frame0: regs=r6 stack= before 11: (79) r6 = *(u64 *)(r10 -8)") +__msg("mark_precise: frame0: regs= stack=-8 before 10: (85) call bpf_loop") __msg("mark_precise: frame0: parent state regs= stack=-8:") -__msg("mark_precise: frame0: last_idx 18 first_idx 0") -__msg("mark_precise: frame0: regs= stack=-8 before 18: (95) exit") -__msg("mark_precise: frame1: regs= stack= before 17: (b7) r0 = 0") -__msg("mark_precise: frame1: regs= stack= before 10: (85) call bpf_loop#181") +__msg("mark_precise: frame0: last_idx 9 first_idx 0 subseq_idx 10") __msg("mark_precise: frame0: regs= stack=-8 before 9: (b7) r4 = 0") __msg("mark_precise: frame0: regs= stack=-8 before 8: (b7) r3 = 0") __msg("mark_precise: frame0: regs= stack=-8 before 7: (bf) r2 = r8") __msg("mark_precise: frame0: regs= stack=-8 before 6: (bf) r1 = r6") __msg("mark_precise: frame0: regs= stack=-8 before 5: (7b) *(u64 *)(r10 -8) = r6") __msg("mark_precise: frame0: regs=r6 stack= before 4: (b7) r6 = 3") +/* State entering callback body popped from states stack */ +__msg("from 10 to 17: frame1:") +__msg("17: frame1: R1=scalar() R2=0 R10=fp0 cb") +__msg("17: (b7) r0 = 0") +__msg("18: (95) exit") +__msg("returning from callee:") +__msg("to caller at 10:") +/* r1, r4 are always precise for bpf_loop(), + * fp-8 was marked before backtracking to callback body. + */ +__msg("frame 0: propagating r1,r4,fp-8") +__msg("mark_precise: frame0: last_idx 10 first_idx 10 subseq_idx -1") +__msg("mark_precise: frame0: regs=r1,r4 stack=-8 before 18: (95) exit") +__msg("mark_precise: frame1: regs= stack= before 17: (b7) r0 = 0") +__msg("mark_precise: frame1: regs= stack= before 10: (85) call bpf_loop#181") +__msg("mark_precise: frame0: parent state regs= stack=:") +__msg("from 18 to 10: safe") __naked int parent_stack_slot_precise_with_callback(void) { asm volatile ( diff --git a/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c b/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c index e959336c7a73..80f620602d50 100644 --- a/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c +++ b/tools/testing/selftests/bpf/progs/xdp_synproxy_kern.c @@ -53,6 +53,8 @@ #define DEFAULT_TTL 64 #define MAX_ALLOWED_PORTS 8 +#define MAX_PACKET_OFF 0xffff + #define swap(a, b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) @@ -183,63 +185,76 @@ static __always_inline __u32 tcp_clock_ms(void) } struct tcpopt_context { - __u8 *ptr; - __u8 *end; + void *data; void *data_end; __be32 *tsecr; __u8 wscale; bool option_timestamp; bool option_sack; + __u32 off; }; -static int tscookie_tcpopt_parse(struct tcpopt_context *ctx) +static __always_inline u8 *next(struct tcpopt_context *ctx, __u32 sz) { - __u8 opcode, opsize; + __u64 off = ctx->off; + __u8 *data; - if (ctx->ptr >= ctx->end) - return 1; - if (ctx->ptr >= ctx->data_end) - return 1; + /* Verifier forbids access to packet when offset exceeds MAX_PACKET_OFF */ + if (off > MAX_PACKET_OFF - sz) + return NULL; - opcode = ctx->ptr[0]; + data = ctx->data + off; + barrier_var(data); + if (data + sz >= ctx->data_end) + return NULL; - if (opcode == TCPOPT_EOL) - return 1; - if (opcode == TCPOPT_NOP) { - ++ctx->ptr; - return 0; - } + ctx->off += sz; + return data; +} - if (ctx->ptr + 1 >= ctx->end) - return 1; - if (ctx->ptr + 1 >= ctx->data_end) +static int tscookie_tcpopt_parse(struct tcpopt_context *ctx) +{ + __u8 *opcode, *opsize, *wscale, *tsecr; + __u32 off = ctx->off; + + opcode = next(ctx, 1); + if (!opcode) return 1; - opsize = ctx->ptr[1]; - if (opsize < 2) + + if (*opcode == TCPOPT_EOL) return 1; + if (*opcode == TCPOPT_NOP) + return 0; - if (ctx->ptr + opsize > ctx->end) + opsize = next(ctx, 1); + if (!opsize || *opsize < 2) return 1; - switch (opcode) { + switch (*opcode) { case TCPOPT_WINDOW: - if (opsize == TCPOLEN_WINDOW && ctx->ptr + TCPOLEN_WINDOW <= ctx->data_end) - ctx->wscale = ctx->ptr[2] < TCP_MAX_WSCALE ? ctx->ptr[2] : TCP_MAX_WSCALE; + wscale = next(ctx, 1); + if (!wscale) + return 1; + if (*opsize == TCPOLEN_WINDOW) + ctx->wscale = *wscale < TCP_MAX_WSCALE ? *wscale : TCP_MAX_WSCALE; break; case TCPOPT_TIMESTAMP: - if (opsize == TCPOLEN_TIMESTAMP && ctx->ptr + TCPOLEN_TIMESTAMP <= ctx->data_end) { + tsecr = next(ctx, 4); + if (!tsecr) + return 1; + if (*opsize == TCPOLEN_TIMESTAMP) { ctx->option_timestamp = true; /* Client's tsval becomes our tsecr. */ - *ctx->tsecr = get_unaligned((__be32 *)(ctx->ptr + 2)); + *ctx->tsecr = get_unaligned((__be32 *)tsecr); } break; case TCPOPT_SACK_PERM: - if (opsize == TCPOLEN_SACK_PERM) + if (*opsize == TCPOLEN_SACK_PERM) ctx->option_sack = true; break; } - ctx->ptr += opsize; + ctx->off = off + *opsize; return 0; } @@ -256,16 +271,21 @@ static int tscookie_tcpopt_parse_batch(__u32 index, void *context) static __always_inline bool tscookie_init(struct tcphdr *tcp_header, __u16 tcp_len, __be32 *tsval, - __be32 *tsecr, void *data_end) + __be32 *tsecr, void *data, void *data_end) { struct tcpopt_context loop_ctx = { - .ptr = (__u8 *)(tcp_header + 1), - .end = (__u8 *)tcp_header + tcp_len, + .data = data, .data_end = data_end, .tsecr = tsecr, .wscale = TS_OPT_WSCALE_MASK, .option_timestamp = false, .option_sack = false, + /* Note: currently verifier would track .off as unbound scalar. + * In case if verifier would at some point get smarter and + * compute bounded value for this var, beware that it might + * hinder bpf_loop() convergence validation. + */ + .off = (__u8 *)(tcp_header + 1) - (__u8 *)data, }; u32 cookie; @@ -635,7 +655,7 @@ static __always_inline int syncookie_handle_syn(struct header_pointers *hdr, cookie = (__u32)value; if (tscookie_init((void *)hdr->tcp, hdr->tcp_len, - &tsopt_buf[0], &tsopt_buf[1], data_end)) + &tsopt_buf[0], &tsopt_buf[1], data, data_end)) tsopt = tsopt_buf; /* Check that there is enough space for a SYNACK. It also covers diff --git a/tools/testing/selftests/bpf/verifier/calls.c b/tools/testing/selftests/bpf/verifier/calls.c index 1bdf2b43e49e..3d5cd51071f0 100644 --- a/tools/testing/selftests/bpf/verifier/calls.c +++ b/tools/testing/selftests/bpf/verifier/calls.c @@ -442,7 +442,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_TRACEPOINT, - .errstr = "back-edge from insn 0 to 0", + .errstr = "the call stack of 9 frames is too deep", .result = REJECT, }, { @@ -799,7 +799,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_TRACEPOINT, - .errstr = "back-edge", + .errstr = "the call stack of 9 frames is too deep", .result = REJECT, }, { @@ -811,7 +811,7 @@ BPF_EXIT_INSN(), }, .prog_type = BPF_PROG_TYPE_TRACEPOINT, - .errstr = "back-edge", + .errstr = "the call stack of 9 frames is too deep", .result = REJECT, }, { diff --git a/tools/testing/selftests/bpf/verifier/ld_imm64.c b/tools/testing/selftests/bpf/verifier/ld_imm64.c index f9297900cea6..78f19c255f20 100644 --- a/tools/testing/selftests/bpf/verifier/ld_imm64.c +++ b/tools/testing/selftests/bpf/verifier/ld_imm64.c @@ -9,8 +9,8 @@ BPF_MOV64_IMM(BPF_REG_0, 2), BPF_EXIT_INSN(), }, - .errstr = "invalid BPF_LD_IMM insn", - .errstr_unpriv = "R1 pointer comparison", + .errstr = "jump into the middle of ldimm64 insn 1", + .errstr_unpriv = "jump into the middle of ldimm64 insn 1", .result = REJECT, }, { @@ -23,8 +23,8 @@ BPF_LD_IMM64(BPF_REG_0, 1), BPF_EXIT_INSN(), }, - .errstr = "invalid BPF_LD_IMM insn", - .errstr_unpriv = "R1 pointer comparison", + .errstr = "jump into the middle of ldimm64 insn 1", + .errstr_unpriv = "jump into the middle of ldimm64 insn 1", .result = REJECT, }, { diff --git a/tools/testing/selftests/bpf/xskxceiver.c b/tools/testing/selftests/bpf/xskxceiver.c index 591ca9637b23..b604c570309a 100644 --- a/tools/testing/selftests/bpf/xskxceiver.c +++ b/tools/testing/selftests/bpf/xskxceiver.c @@ -908,8 +908,9 @@ static bool is_metadata_correct(struct pkt *pkt, void *buffer, u64 addr) struct xdp_info *meta = data - sizeof(struct xdp_info); if (meta->count != pkt->pkt_nb) { - ksft_print_msg("[%s] expected meta_count [%d], got meta_count [%d]\n", - __func__, pkt->pkt_nb, meta->count); + ksft_print_msg("[%s] expected meta_count [%d], got meta_count [%llu]\n", + __func__, pkt->pkt_nb, + (unsigned long long)meta->count); return false; } @@ -926,11 +927,13 @@ static bool is_frag_valid(struct xsk_umem_info *umem, u64 addr, u32 len, u32 exp if (addr >= umem->num_frames * umem->frame_size || addr + len > umem->num_frames * umem->frame_size) { - ksft_print_msg("Frag invalid addr: %llx len: %u\n", addr, len); + ksft_print_msg("Frag invalid addr: %llx len: %u\n", + (unsigned long long)addr, len); return false; } if (!umem->unaligned_mode && addr % umem->frame_size + len > umem->frame_size) { - ksft_print_msg("Frag crosses frame boundary addr: %llx len: %u\n", addr, len); + ksft_print_msg("Frag crosses frame boundary addr: %llx len: %u\n", + (unsigned long long)addr, len); return false; } @@ -1029,7 +1032,8 @@ static int complete_pkts(struct xsk_socket_info *xsk, int batch_size) u64 addr = *xsk_ring_cons__comp_addr(&xsk->umem->cq, idx + rcvd - 1); ksft_print_msg("[%s] Too many packets completed\n", __func__); - ksft_print_msg("Last completion address: %llx\n", addr); + ksft_print_msg("Last completion address: %llx\n", + (unsigned long long)addr); return TEST_FAILURE; } @@ -1513,8 +1517,9 @@ static int validate_tx_invalid_descs(struct ifobject *ifobject) } if (stats.tx_invalid_descs != ifobject->xsk->pkt_stream->nb_pkts / 2) { - ksft_print_msg("[%s] tx_invalid_descs incorrect. Got [%u] expected [%u]\n", - __func__, stats.tx_invalid_descs, + ksft_print_msg("[%s] tx_invalid_descs incorrect. Got [%llu] expected [%u]\n", + __func__, + (unsigned long long)stats.tx_invalid_descs, ifobject->xsk->pkt_stream->nb_pkts); return TEST_FAILURE; } diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore index cc920c79ff1c..4ff10ea61461 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -45,3 +45,4 @@ mdwe_test gup_longterm mkdirty va_high_addr_switch +hugetlb_fault_after_madv diff --git a/tools/testing/selftests/mm/pagemap_ioctl.c b/tools/testing/selftests/mm/pagemap_ioctl.c index 0161fb49fc6e..befab43719ba 100644 --- a/tools/testing/selftests/mm/pagemap_ioctl.c +++ b/tools/testing/selftests/mm/pagemap_ioctl.c @@ -94,19 +94,19 @@ int init_uffd(void) uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); if (uffd == -1) - ksft_exit_fail_msg("uffd syscall failed\n"); + return uffd; uffdio_api.api = UFFD_API; uffdio_api.features = UFFD_FEATURE_WP_UNPOPULATED | UFFD_FEATURE_WP_ASYNC | UFFD_FEATURE_WP_HUGETLBFS_SHMEM; if (ioctl(uffd, UFFDIO_API, &uffdio_api)) - ksft_exit_fail_msg("UFFDIO_API\n"); + return -1; if (!(uffdio_api.api & UFFDIO_REGISTER_MODE_WP) || !(uffdio_api.features & UFFD_FEATURE_WP_UNPOPULATED) || !(uffdio_api.features & UFFD_FEATURE_WP_ASYNC) || !(uffdio_api.features & UFFD_FEATURE_WP_HUGETLBFS_SHMEM)) - ksft_exit_fail_msg("UFFDIO_API error %llu\n", uffdio_api.api); + return -1; return 0; } @@ -1151,7 +1151,7 @@ int sanity_tests(void) /* 9. Memory mapped file */ fd = open(__FILE__, O_RDONLY); if (fd < 0) - ksft_exit_fail_msg("%s Memory mapped file\n"); + ksft_exit_fail_msg("%s Memory mapped file\n", __func__); ret = stat(__FILE__, &sbuf); if (ret < 0) @@ -1159,7 +1159,7 @@ int sanity_tests(void) fmem = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (fmem == MAP_FAILED) - ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); + ksft_exit_fail_msg("error nomem %d %s\n", errno, strerror(errno)); tmp_buf = malloc(sbuf.st_size); memcpy(tmp_buf, fmem, sbuf.st_size); @@ -1189,7 +1189,7 @@ int sanity_tests(void) fmem = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fmem == MAP_FAILED) - ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); + ksft_exit_fail_msg("error nomem %d %s\n", errno, strerror(errno)); wp_init(fmem, buf_size); wp_addr_range(fmem, buf_size); @@ -1479,6 +1479,10 @@ int main(void) struct stat sbuf; ksft_print_header(); + + if (init_uffd()) + return ksft_exit_pass(); + ksft_set_plan(115); page_size = getpagesize(); @@ -1488,9 +1492,6 @@ int main(void) if (pagemap_fd < 0) return -EINVAL; - if (init_uffd()) - ksft_exit_fail_msg("uffd init failed\n"); - /* 1. Sanity testing */ sanity_tests_sd(); @@ -1595,7 +1596,7 @@ int main(void) fmem = mmap(NULL, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fmem == MAP_FAILED) - ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); + ksft_exit_fail_msg("error nomem %d %s\n", errno, strerror(errno)); wp_init(fmem, sbuf.st_size); wp_addr_range(fmem, sbuf.st_size); @@ -1623,7 +1624,7 @@ int main(void) fmem = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (fmem == MAP_FAILED) - ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); + ksft_exit_fail_msg("error nomem %d %s\n", errno, strerror(errno)); wp_init(fmem, buf_size); wp_addr_range(fmem, buf_size); diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index cc16f6ca8533..00757445278e 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -223,9 +223,12 @@ CATEGORY="hugetlb" run_test ./hugepage-mremap CATEGORY="hugetlb" run_test ./hugepage-vmemmap CATEGORY="hugetlb" run_test ./hugetlb-madvise +nr_hugepages_tmp=$(cat /proc/sys/vm/nr_hugepages) # For this test, we need one and just one huge page echo 1 > /proc/sys/vm/nr_hugepages CATEGORY="hugetlb" run_test ./hugetlb_fault_after_madv +# Restore the previous number of huge pages, since further tests rely on it +echo "$nr_hugepages_tmp" > /proc/sys/vm/nr_hugepages if test_selected "hugetlb"; then echo "NOTE: These hugetlb tests provide minimal coverage. Use" diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index 75a2438efdf3..3c94f2f194d6 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -3240,7 +3240,7 @@ fastclose_tests() if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then test_linkfail=1024 fastclose=server \ run_tests $ns1 $ns2 10.0.1.1 - chk_join_nr 0 0 0 + chk_join_nr 0 0 0 0 0 0 1 chk_fclose_nr 1 1 invert chk_rst_nr 1 1 fi diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 5f2b3f6c0d74..38be9706c45f 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -859,7 +859,7 @@ kci_test_gretap() run_cmd ip -netns "$testns" addr add dev "$DEV_NS" 10.1.1.100/24 - run_cmd ip -netns "$testns" link set dev $DEV_NS ups + run_cmd ip -netns "$testns" link set dev $DEV_NS up run_cmd ip -netns "$testns" link del "$DEV_NS" # test external mode diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 5b0e93f9996c..01fa816868bc 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -353,11 +353,12 @@ static void test_stream_msg_peek_server(const struct test_opts *opts) } #define SOCK_BUF_SIZE (2 * 1024 * 1024) -#define MAX_MSG_SIZE (32 * 1024) +#define MAX_MSG_PAGES 4 static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) { unsigned long curr_hash; + size_t max_msg_size; int page_size; int msg_count; int fd; @@ -373,7 +374,8 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) curr_hash = 0; page_size = getpagesize(); - msg_count = SOCK_BUF_SIZE / MAX_MSG_SIZE; + max_msg_size = MAX_MSG_PAGES * page_size; + msg_count = SOCK_BUF_SIZE / max_msg_size; for (int i = 0; i < msg_count; i++) { size_t buf_size; @@ -383,7 +385,7 @@ static void test_seqpacket_msg_bounds_client(const struct test_opts *opts) /* Use "small" buffers and "big" buffers. */ if (i & 1) buf_size = page_size + - (rand() % (MAX_MSG_SIZE - page_size)); + (rand() % (max_msg_size - page_size)); else buf_size = 1 + (rand() % page_size); @@ -429,7 +431,6 @@ static void test_seqpacket_msg_bounds_server(const struct test_opts *opts) unsigned long remote_hash; unsigned long curr_hash; int fd; - char buf[MAX_MSG_SIZE]; struct msghdr msg = {0}; struct iovec iov = {0}; @@ -457,8 +458,13 @@ static void test_seqpacket_msg_bounds_server(const struct test_opts *opts) control_writeln("SRVREADY"); /* Wait, until peer sends whole data. */ control_expectln("SENDDONE"); - iov.iov_base = buf; - iov.iov_len = sizeof(buf); + iov.iov_len = MAX_MSG_PAGES * getpagesize(); + iov.iov_base = malloc(iov.iov_len); + if (!iov.iov_base) { + perror("malloc"); + exit(EXIT_FAILURE); + } + msg.msg_iov = &iov; msg.msg_iovlen = 1; @@ -483,6 +489,7 @@ static void test_seqpacket_msg_bounds_server(const struct test_opts *opts) curr_hash += hash_djb2(msg.msg_iov[0].iov_base, recv_size); } + free(iov.iov_base); close(fd); remote_hash = control_readulong(); |