bcm27xx: update 5.4 patches from RPi foundation
authorÁlvaro Fernández Rojas <noltari@gmail.com>
Tue, 31 Mar 2020 07:26:30 +0000 (09:26 +0200)
committerÁlvaro Fernández Rojas <noltari@gmail.com>
Tue, 31 Mar 2020 11:18:08 +0000 (13:18 +0200)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
140 files changed:
target/linux/bcm27xx/bcm2708/config-5.4
target/linux/bcm27xx/bcm2709/config-5.4
target/linux/bcm27xx/bcm2710/config-5.4
target/linux/bcm27xx/bcm2711/config-5.4
target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0436-ARM-dts-overlays-Create-custom-clocks-in.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0437-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0438-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0439-of-overlay-Correct-symbol-path-fixups.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0440-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0441-of-address-Introduce-of_get_next_dma_parent-helper.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0442-of-address-Follow-DMA-parent-for-dma-coherent.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0443-of-Factor-out-addr-size-cells-parsing.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0444-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0445-of-Make-of_dma_get_range-work-on-bus-nodes.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0446-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0447-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0448-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0449-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0450-resource-Add-a-resource_list_first_type-helper.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0452-x86-PCI-sta2x11-use-default-DMA-address-translation.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0453-PCI-of-Add-inbound-resource-parsing-to-helpers.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-unify-the-dma_capable-definitions.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0456-dma-direct-exclude-dma_direct_map_resource-from-the-.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0457-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0458-ARM-dts-bcm2711-Enable-PCIe-controller.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Add-MSI-support.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0461-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0462-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0463-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0464-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0465-overlays-imx219-Correct-link-frequency-to-match-the-.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0466-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-Add-extra-10-bit-sensor-modes.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0471-media-ov5647-change-defaults-to-better-match-raw-cam.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0472-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0473-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0474-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0475-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0476-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0478-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0479-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Accept-extras-directly-after.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0490-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0491-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch [deleted file]
target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch [new file with mode: 0644]
target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch [new file with mode: 0644]

index 6e7d91e3f41d09b461d296353ef90911b5f7e9ee..1f12a36ee72c8e61ea1b19555e8cca01a2c1cbfd 100644 (file)
@@ -243,6 +243,8 @@ CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
 CONFIG_HAVE_UID16=y
 CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
 CONFIG_HW_CONSOLE=y
+CONFIG_HZ=100
+CONFIG_HZ_100=y
 CONFIG_HZ_FIXED=0
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
index e6ce625d1be143b4694df4d1b1e6a1555bbb1a27..c1630c599d8d49dba7c61729023ad9d5c0b5645a 100644 (file)
@@ -311,6 +311,8 @@ CONFIG_HIGHMEM=y
 CONFIG_HIGHPTE=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_HW_CONSOLE=y
+CONFIG_HZ=100
+CONFIG_HZ_100=y
 CONFIG_HZ_FIXED=0
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
index a04d519b137cf61614c7bafb4b7ad34e0d723180..4a5b491b0fcdfa8b09661d8f5e1beb13843c08ef 100644 (file)
@@ -367,6 +367,8 @@ CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
 CONFIG_HOLES_IN_ZONE=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_HW_CONSOLE=y
+CONFIG_HZ=250
+CONFIG_HZ_250=y
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
index 0b0e49ad62dd14ae8581a2f5f97f28ce390c30b6..abf6e8844a1048ae6c4dca32f9bc2204a50044a5 100644 (file)
@@ -373,6 +373,8 @@ CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
 CONFIG_HOLES_IN_ZONE=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_HW_CONSOLE=y
+CONFIG_HZ=250
+CONFIG_HZ_250=y
 CONFIG_I2C=y
 # CONFIG_I2C_BCM2708 is not set
 CONFIG_I2C_BOARDINFO=y
diff --git a/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch b/target/linux/bcm27xx/patches-5.4/950-0435-ARM-dts-overlays-Create-custom-clocks-in.patch
new file mode 100644 (file)
index 0000000..ac50884
--- /dev/null
@@ -0,0 +1,79 @@
+From c182949e33dc3ac4d718386f97c75583bae0e46b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 28 Feb 2020 11:22:40 +0000
+Subject: [PATCH] ARM: dts: overlays: Create custom clocks in /
+
+Change [1] removes the simple-bus compatible string from the "/clocks"
+node, preventing any custom clocks placed there from being initialised.
+Rather than reinstate the compatible string and trigger DT warnings at
+kernel build time, change the overlays to instantiate those clocks under
+the root node ("/").
+
+See: https://github.com/raspberrypi/linux/issues/3481
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+[1] 4b2d24662126 ("ARM: dts: bcm283x: Remove simple-bus from fixed clocks")
+---
+ .../boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts   | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts        | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts     | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts  | 2 +-
+ arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts      | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
+@@ -9,7 +9,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target-path = "/clocks";
++              target-path = "/";
+               __overlay__ {
+                       boss_osc: boss_osc {
+                               compatible = "allo,dac-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
+@@ -6,7 +6,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target-path = "/clocks";
++              target-path = "/";
+               __overlay__ {
+                       dacpro_osc: dacpro_osc {
+                               compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
+@@ -6,7 +6,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target-path = "/clocks";
++              target-path = "/";
+               __overlay__ {
+                       dacpro_osc: dacpro_osc {
+                               compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
+@@ -6,7 +6,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target-path = "/clocks";
++              target-path = "/";
+               __overlay__ {
+                       dacpro_osc: dacpro_osc {
+                               compatible = "hifiberry,dacpro-clk";
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
+@@ -8,7 +8,7 @@
+       compatible = "brcm,bcm2835";
+       fragment@0 {
+-              target-path = "/clocks";
++              target-path = "/";
+               __overlay__ {
+                       dachd_osc: pll_dachd_osc {
+                               compatible = "hifiberry,dachd-clk";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0436-ARM-dts-overlays-Create-custom-clocks-in.patch b/target/linux/bcm27xx/patches-5.4/950-0436-ARM-dts-overlays-Create-custom-clocks-in.patch
deleted file mode 100644 (file)
index ac50884..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-From c182949e33dc3ac4d718386f97c75583bae0e46b Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Fri, 28 Feb 2020 11:22:40 +0000
-Subject: [PATCH] ARM: dts: overlays: Create custom clocks in /
-
-Change [1] removes the simple-bus compatible string from the "/clocks"
-node, preventing any custom clocks placed there from being initialised.
-Rather than reinstate the compatible string and trigger DT warnings at
-kernel build time, change the overlays to instantiate those clocks under
-the root node ("/").
-
-See: https://github.com/raspberrypi/linux/issues/3481
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
-
-[1] 4b2d24662126 ("ARM: dts: bcm283x: Remove simple-bus from fixed clocks")
----
- .../boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts   | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts        | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts     | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts  | 2 +-
- arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts      | 2 +-
- 5 files changed, 5 insertions(+), 5 deletions(-)
-
---- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
-@@ -9,7 +9,7 @@
-       compatible = "brcm,bcm2835";
-       fragment@0 {
--              target-path = "/clocks";
-+              target-path = "/";
-               __overlay__ {
-                       boss_osc: boss_osc {
-                               compatible = "allo,dac-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
-@@ -6,7 +6,7 @@
-       compatible = "brcm,bcm2835";
-       fragment@0 {
--              target-path = "/clocks";
-+              target-path = "/";
-               __overlay__ {
-                       dacpro_osc: dacpro_osc {
-                               compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
-@@ -6,7 +6,7 @@
-       compatible = "brcm,bcm2835";
-       fragment@0 {
--              target-path = "/clocks";
-+              target-path = "/";
-               __overlay__ {
-                       dacpro_osc: dacpro_osc {
-                               compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
-@@ -6,7 +6,7 @@
-       compatible = "brcm,bcm2835";
-       fragment@0 {
--              target-path = "/clocks";
-+              target-path = "/";
-               __overlay__ {
-                       dacpro_osc: dacpro_osc {
-                               compatible = "hifiberry,dacpro-clk";
---- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
-@@ -8,7 +8,7 @@
-       compatible = "brcm,bcm2835";
-       fragment@0 {
--              target-path = "/clocks";
-+              target-path = "/";
-               __overlay__ {
-                       dachd_osc: pll_dachd_osc {
-                               compatible = "hifiberry,dachd-clk";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch b/target/linux/bcm27xx/patches-5.4/950-0436-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
new file mode 100644 (file)
index 0000000..4774fd2
--- /dev/null
@@ -0,0 +1,26 @@
+From 38e906c77467bf83ec130bea6859b46ea1e0d4b8 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 30 Jan 2020 12:35:44 +0000
+Subject: [PATCH] staging: vc04_services: Fix vcsm overflow bug when
+ counting transactions
+
+The response block and local state were using u16 and u32 respectively
+to represent transaction id.  When the former would wrap, there is a
+mismatch and subsequent transactions will be marked as failures.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
+@@ -34,7 +34,7 @@ struct sm_cmd_rsp_blk {
+       /* To be signaled when the response is there */
+       struct completion cmplt;
+-      u16 id;
++      u32 id;
+       u16 length;
+       u8 msg[VC_SM_MAX_MSG_LEN];
diff --git a/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch b/target/linux/bcm27xx/patches-5.4/950-0437-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
new file mode 100644 (file)
index 0000000..213e8b8
--- /dev/null
@@ -0,0 +1,34 @@
+From 04f569021b0d24ec9f5c3671447b77157c859d16 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 7 Feb 2020 09:51:31 +0000
+Subject: [PATCH] overlays: Add timeout_ms parameter to gpio-poweroff
+
+The timeout_ms parameter specifies in milliseconds how long the kernel
+waits for power-down before issuing a WARN. The default value is 3000 ms.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README                    | 2 ++
+ arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts | 1 +
+ 2 files changed, 3 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -821,6 +821,8 @@ Params: gpiopin                 GPIO for
+         input                   Set if the gpio pin should be configured as
+                                 an input.
+         export                  Set to export the configured pin to sysfs
++        timeout_ms              Specify (in ms) how long the kernel waits for
++                                power-down before issuing a WARN (default 3000).
+ Name:   gpio-shutdown
+--- a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
++++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
+@@ -32,5 +32,6 @@
+               active_low =    <&power_ctrl>,"gpios:8";
+               input =         <&power_ctrl>,"input?";
+               export =        <&power_ctrl>,"export?";
++              timeout_ms =    <&power_ctrl>,"timeout-ms:0";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0437-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch b/target/linux/bcm27xx/patches-5.4/950-0437-staging-vc04_services-Fix-vcsm-overflow-bug-when-cou.patch
deleted file mode 100644 (file)
index 4774fd2..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-From 38e906c77467bf83ec130bea6859b46ea1e0d4b8 Mon Sep 17 00:00:00 2001
-From: Naushir Patuck <naush@raspberrypi.com>
-Date: Thu, 30 Jan 2020 12:35:44 +0000
-Subject: [PATCH] staging: vc04_services: Fix vcsm overflow bug when
- counting transactions
-
-The response block and local state were using u16 and u32 respectively
-to represent transaction id.  When the former would wrap, there is a
-mismatch and subsequent transactions will be marked as failures.
-
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
-+++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c
-@@ -34,7 +34,7 @@ struct sm_cmd_rsp_blk {
-       /* To be signaled when the response is there */
-       struct completion cmplt;
--      u16 id;
-+      u32 id;
-       u16 length;
-       u8 msg[VC_SM_MAX_MSG_LEN];
diff --git a/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch b/target/linux/bcm27xx/patches-5.4/950-0438-of-overlay-Correct-symbol-path-fixups.patch
new file mode 100644 (file)
index 0000000..4b00587
--- /dev/null
@@ -0,0 +1,37 @@
+From 8f22c4228bbb91697ab3510f5a6176e530c0d639 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 6 Feb 2020 12:23:15 +0000
+Subject: [PATCH] of: overlay: Correct symbol path fixups
+
+When symbols from overlays are added to the live tree their paths must
+be rebased. The translated symbol is normally the result of joining
+the fragment-relative path (with a leading "/") to the target path
+(either copied directly from the "target-path" property or resolved
+from the phandle). This translation fails when the target is the root
+node (a common case for Raspberry Pi overlays) because the resulting
+path starts with a double slash. For example, if target-path is "/" and
+the fragment adds a node called "newnode", the label associated with
+that node will be assigned the path "//newnode", which can't be found
+in the tree.
+
+Fix the failure case by explicitly replacing a target path of "/" with
+an empty string.
+
+Fixes: d1651b03c2df ("of: overlay: add overlay symbols to live device tree")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/of/overlay.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/of/overlay.c
++++ b/drivers/of/overlay.c
+@@ -245,6 +245,8 @@ static struct property *dup_and_fixup_sy
+       if (!target_path)
+               return NULL;
+       target_path_len = strlen(target_path);
++      if (!strcmp(target_path, "/"))
++              target_path_len = 0;
+       new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
+       if (!new_prop)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0438-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch b/target/linux/bcm27xx/patches-5.4/950-0438-overlays-Add-timeout_ms-parameter-to-gpio-poweroff.patch
deleted file mode 100644 (file)
index 213e8b8..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-From 04f569021b0d24ec9f5c3671447b77157c859d16 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Fri, 7 Feb 2020 09:51:31 +0000
-Subject: [PATCH] overlays: Add timeout_ms parameter to gpio-poweroff
-
-The timeout_ms parameter specifies in milliseconds how long the kernel
-waits for power-down before issuing a WARN. The default value is 3000 ms.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/README                    | 2 ++
- arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts | 1 +
- 2 files changed, 3 insertions(+)
-
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -821,6 +821,8 @@ Params: gpiopin                 GPIO for
-         input                   Set if the gpio pin should be configured as
-                                 an input.
-         export                  Set to export the configured pin to sysfs
-+        timeout_ms              Specify (in ms) how long the kernel waits for
-+                                power-down before issuing a WARN (default 3000).
- Name:   gpio-shutdown
---- a/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/gpio-poweroff-overlay.dts
-@@ -32,5 +32,6 @@
-               active_low =    <&power_ctrl>,"gpios:8";
-               input =         <&power_ctrl>,"input?";
-               export =        <&power_ctrl>,"export?";
-+              timeout_ms =    <&power_ctrl>,"timeout-ms:0";
-       };
- };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0439-of-overlay-Correct-symbol-path-fixups.patch b/target/linux/bcm27xx/patches-5.4/950-0439-of-overlay-Correct-symbol-path-fixups.patch
deleted file mode 100644 (file)
index 4b00587..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-From 8f22c4228bbb91697ab3510f5a6176e530c0d639 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 6 Feb 2020 12:23:15 +0000
-Subject: [PATCH] of: overlay: Correct symbol path fixups
-
-When symbols from overlays are added to the live tree their paths must
-be rebased. The translated symbol is normally the result of joining
-the fragment-relative path (with a leading "/") to the target path
-(either copied directly from the "target-path" property or resolved
-from the phandle). This translation fails when the target is the root
-node (a common case for Raspberry Pi overlays) because the resulting
-path starts with a double slash. For example, if target-path is "/" and
-the fragment adds a node called "newnode", the label associated with
-that node will be assigned the path "//newnode", which can't be found
-in the tree.
-
-Fix the failure case by explicitly replacing a target path of "/" with
-an empty string.
-
-Fixes: d1651b03c2df ("of: overlay: add overlay symbols to live device tree")
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/of/overlay.c | 2 ++
- 1 file changed, 2 insertions(+)
-
---- a/drivers/of/overlay.c
-+++ b/drivers/of/overlay.c
-@@ -245,6 +245,8 @@ static struct property *dup_and_fixup_sy
-       if (!target_path)
-               return NULL;
-       target_path_len = strlen(target_path);
-+      if (!strcmp(target_path, "/"))
-+              target_path_len = 0;
-       new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
-       if (!new_prop)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0439-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
new file mode 100644 (file)
index 0000000..636ad26
--- /dev/null
@@ -0,0 +1,25 @@
+From 65318cd76f4523acf8ffe8fe7448fb7d913f8c66 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 3 Mar 2020 09:43:41 +0000
+Subject: [PATCH] overlays: sc16ic750-i2c: Fix xtal parameter
+
+The xtal parameter is targetting the wrong node - fix it.
+
+See: https://github.com/raspberrypi/linux/issues/3156
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
++++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
+@@ -32,7 +32,7 @@
+       __overrides__ {
+               int_pin = <&sc16is750>,"interrupts:0";
+               addr = <&sc16is750>,"reg:0",<&sc16is750_clk>,"name";
+-              xtal = <&sc16is750>,"clock-frequency:0";
++              xtal = <&sc16is750_clk>,"clock-frequency:0";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0440-of-address-Introduce-of_get_next_dma_parent-helper.patch
new file mode 100644 (file)
index 0000000..1056cfc
--- /dev/null
@@ -0,0 +1,39 @@
+From 25ab98ceb9844642c994b5766de1033552d1aef2 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 18:23:01 +0100
+Subject: [PATCH] of/address: Introduce of_get_next_dma_parent() helper
+
+commit 862ab5578f754117742c8b8c8e5ddf98bdb190ba upstream.
+
+Add of_get_next_dma_parent() helper which is similar to
+__of_get_dma_parent(), but can be used in iterators and decrements the
+ref count on the prior parent.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -695,6 +695,16 @@ static struct device_node *__of_get_dma_
+       return of_node_get(args.np);
+ }
++static struct device_node *of_get_next_dma_parent(struct device_node *np)
++{
++      struct device_node *parent;
++
++      parent = __of_get_dma_parent(np);
++      of_node_put(np);
++
++      return parent;
++}
++
+ u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
+ {
+       struct device_node *host;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0440-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch b/target/linux/bcm27xx/patches-5.4/950-0440-overlays-sc16ic750-i2c-Fix-xtal-parameter.patch
deleted file mode 100644 (file)
index 636ad26..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 65318cd76f4523acf8ffe8fe7448fb7d913f8c66 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Tue, 3 Mar 2020 09:43:41 +0000
-Subject: [PATCH] overlays: sc16ic750-i2c: Fix xtal parameter
-
-The xtal parameter is targetting the wrong node - fix it.
-
-See: https://github.com/raspberrypi/linux/issues/3156
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/sc16is750-i2c-overlay.dts
-@@ -32,7 +32,7 @@
-       __overrides__ {
-               int_pin = <&sc16is750>,"interrupts:0";
-               addr = <&sc16is750>,"reg:0",<&sc16is750_clk>,"name";
--              xtal = <&sc16is750>,"clock-frequency:0";
-+              xtal = <&sc16is750_clk>,"clock-frequency:0";
-       };
- };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch b/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Follow-DMA-parent-for-dma-coherent.patch
new file mode 100644 (file)
index 0000000..dbfb102
--- /dev/null
@@ -0,0 +1,30 @@
+From e4a649779ff6857240fe691cdf147a3b4896e71b Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 14:47:31 +0100
+Subject: [PATCH] of: address: Follow DMA parent for "dma-coherent"
+
+commit c60bf3eb888a362100aa1bdbea351dab681e262a upstream.
+
+Much like for address translation, when checking for DMA coherence we
+should be sure to walk up the DMA hierarchy, rather than the MMIO one,
+now that we can accommodate them being different.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -1023,7 +1023,7 @@ bool of_dma_is_coherent(struct device_no
+                       of_node_put(node);
+                       return true;
+               }
+-              node = of_get_next_parent(node);
++              node = of_get_next_dma_parent(node);
+       }
+       of_node_put(node);
+       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Introduce-of_get_next_dma_parent-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0441-of-address-Introduce-of_get_next_dma_parent-helper.patch
deleted file mode 100644 (file)
index 1056cfc..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-From 25ab98ceb9844642c994b5766de1033552d1aef2 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 18:23:01 +0100
-Subject: [PATCH] of/address: Introduce of_get_next_dma_parent() helper
-
-commit 862ab5578f754117742c8b8c8e5ddf98bdb190ba upstream.
-
-Add of_get_next_dma_parent() helper which is similar to
-__of_get_dma_parent(), but can be used in iterators and decrements the
-ref count on the prior parent.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 10 ++++++++++
- 1 file changed, 10 insertions(+)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -695,6 +695,16 @@ static struct device_node *__of_get_dma_
-       return of_node_get(args.np);
- }
-+static struct device_node *of_get_next_dma_parent(struct device_node *np)
-+{
-+      struct device_node *parent;
-+
-+      parent = __of_get_dma_parent(np);
-+      of_node_put(np);
-+
-+      return parent;
-+}
-+
- u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
- {
-       struct device_node *host;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch b/target/linux/bcm27xx/patches-5.4/950-0442-of-Factor-out-addr-size-cells-parsing.patch
new file mode 100644 (file)
index 0000000..045ee98
--- /dev/null
@@ -0,0 +1,117 @@
+From 839aeedc908eb729b9014e7d1d38e109778a52d2 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Tue, 2 Jul 2019 18:42:39 +0100
+Subject: [PATCH] of: Factor out #{addr,size}-cells parsing
+
+In some cases such as PCI host controllers, we may have a "parent bus"
+which is an OF leaf node, but still need to correctly parse ranges from
+the point of view of that bus. For that, factor out variants of the
+"#addr-cells" and "#size-cells" parsers which do not assume they have a
+device node and thus immediately traverse upwards before reading the
+relevant property.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+[robh: don't make of_bus_n_{addr,size}_cells() public]
+Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+
+(cherry picked from commit b68ac8dc22ebbf003e26e44bf4dd3030c076df5a)
+---
+ drivers/of/address.c    |  2 ++
+ drivers/of/base.c       | 32 ++++++++++++++++++++++----------
+ drivers/of/of_private.h | 14 ++++++++++++++
+ 3 files changed, 38 insertions(+), 10 deletions(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -14,6 +14,8 @@
+ #include <linux/slab.h>
+ #include <linux/string.h>
++#include "of_private.h"
++
+ /* Max address size we deal with */
+ #define OF_MAX_ADDR_CELLS     4
+ #define OF_CHECK_ADDR_COUNT(na)       ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
+--- a/drivers/of/base.c
++++ b/drivers/of/base.c
+@@ -86,34 +86,46 @@ static bool __of_node_is_type(const stru
+       return np && match && type && !strcmp(match, type);
+ }
+-int of_n_addr_cells(struct device_node *np)
++int of_bus_n_addr_cells(struct device_node *np)
+ {
+       u32 cells;
+-      do {
+-              if (np->parent)
+-                      np = np->parent;
++      for (; np; np = np->parent)
+               if (!of_property_read_u32(np, "#address-cells", &cells))
+                       return cells;
+-      } while (np->parent);
++
+       /* No #address-cells property for the root node */
+       return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
+ }
++
++int of_n_addr_cells(struct device_node *np)
++{
++      if (np->parent)
++              np = np->parent;
++
++      return of_bus_n_addr_cells(np);
++}
+ EXPORT_SYMBOL(of_n_addr_cells);
+-int of_n_size_cells(struct device_node *np)
++int of_bus_n_size_cells(struct device_node *np)
+ {
+       u32 cells;
+-      do {
+-              if (np->parent)
+-                      np = np->parent;
++      for (; np; np = np->parent)
+               if (!of_property_read_u32(np, "#size-cells", &cells))
+                       return cells;
+-      } while (np->parent);
++
+       /* No #size-cells property for the root node */
+       return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+ }
++
++int of_n_size_cells(struct device_node *np)
++{
++      if (np->parent)
++              np = np->parent;
++
++      return of_bus_n_size_cells(np);
++}
+ EXPORT_SYMBOL(of_n_size_cells);
+ #ifdef CONFIG_NUMA
+--- a/drivers/of/of_private.h
++++ b/drivers/of/of_private.h
+@@ -158,4 +158,18 @@ extern void __of_sysfs_remove_bin_file(s
+ #define for_each_transaction_entry_reverse(_oft, _te) \
+       list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
++extern int of_bus_n_addr_cells(struct device_node *np);
++extern int of_bus_n_size_cells(struct device_node *np);
++
++#ifdef CONFIG_OF_ADDRESS
++extern int of_dma_get_range(struct device_node *np, u64 *dma_addr,
++                          u64 *paddr, u64 *size);
++#else
++static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr,
++                                 u64 *paddr, u64 *size)
++{
++      return -ENODEV;
++}
++#endif
++
+ #endif /* _LINUX_OF_PRIVATE_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0442-of-address-Follow-DMA-parent-for-dma-coherent.patch b/target/linux/bcm27xx/patches-5.4/950-0442-of-address-Follow-DMA-parent-for-dma-coherent.patch
deleted file mode 100644 (file)
index dbfb102..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From e4a649779ff6857240fe691cdf147a3b4896e71b Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 14:47:31 +0100
-Subject: [PATCH] of: address: Follow DMA parent for "dma-coherent"
-
-commit c60bf3eb888a362100aa1bdbea351dab681e262a upstream.
-
-Much like for address translation, when checking for DMA coherence we
-should be sure to walk up the DMA hierarchy, rather than the MMIO one,
-now that we can accommodate them being different.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -1023,7 +1023,7 @@ bool of_dma_is_coherent(struct device_no
-                       of_node_put(node);
-                       return true;
-               }
--              node = of_get_next_parent(node);
-+              node = of_get_next_dma_parent(node);
-       }
-       of_node_put(node);
-       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0443-of-Factor-out-addr-size-cells-parsing.patch b/target/linux/bcm27xx/patches-5.4/950-0443-of-Factor-out-addr-size-cells-parsing.patch
deleted file mode 100644 (file)
index 045ee98..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-From 839aeedc908eb729b9014e7d1d38e109778a52d2 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Tue, 2 Jul 2019 18:42:39 +0100
-Subject: [PATCH] of: Factor out #{addr,size}-cells parsing
-
-In some cases such as PCI host controllers, we may have a "parent bus"
-which is an OF leaf node, but still need to correctly parse ranges from
-the point of view of that bus. For that, factor out variants of the
-"#addr-cells" and "#size-cells" parsers which do not assume they have a
-device node and thus immediately traverse upwards before reading the
-relevant property.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-[robh: don't make of_bus_n_{addr,size}_cells() public]
-Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
-
-(cherry picked from commit b68ac8dc22ebbf003e26e44bf4dd3030c076df5a)
----
- drivers/of/address.c    |  2 ++
- drivers/of/base.c       | 32 ++++++++++++++++++++++----------
- drivers/of/of_private.h | 14 ++++++++++++++
- 3 files changed, 38 insertions(+), 10 deletions(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -14,6 +14,8 @@
- #include <linux/slab.h>
- #include <linux/string.h>
-+#include "of_private.h"
-+
- /* Max address size we deal with */
- #define OF_MAX_ADDR_CELLS     4
- #define OF_CHECK_ADDR_COUNT(na)       ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS)
---- a/drivers/of/base.c
-+++ b/drivers/of/base.c
-@@ -86,34 +86,46 @@ static bool __of_node_is_type(const stru
-       return np && match && type && !strcmp(match, type);
- }
--int of_n_addr_cells(struct device_node *np)
-+int of_bus_n_addr_cells(struct device_node *np)
- {
-       u32 cells;
--      do {
--              if (np->parent)
--                      np = np->parent;
-+      for (; np; np = np->parent)
-               if (!of_property_read_u32(np, "#address-cells", &cells))
-                       return cells;
--      } while (np->parent);
-+
-       /* No #address-cells property for the root node */
-       return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
- }
-+
-+int of_n_addr_cells(struct device_node *np)
-+{
-+      if (np->parent)
-+              np = np->parent;
-+
-+      return of_bus_n_addr_cells(np);
-+}
- EXPORT_SYMBOL(of_n_addr_cells);
--int of_n_size_cells(struct device_node *np)
-+int of_bus_n_size_cells(struct device_node *np)
- {
-       u32 cells;
--      do {
--              if (np->parent)
--                      np = np->parent;
-+      for (; np; np = np->parent)
-               if (!of_property_read_u32(np, "#size-cells", &cells))
-                       return cells;
--      } while (np->parent);
-+
-       /* No #size-cells property for the root node */
-       return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
- }
-+
-+int of_n_size_cells(struct device_node *np)
-+{
-+      if (np->parent)
-+              np = np->parent;
-+
-+      return of_bus_n_size_cells(np);
-+}
- EXPORT_SYMBOL(of_n_size_cells);
- #ifdef CONFIG_NUMA
---- a/drivers/of/of_private.h
-+++ b/drivers/of/of_private.h
-@@ -158,4 +158,18 @@ extern void __of_sysfs_remove_bin_file(s
- #define for_each_transaction_entry_reverse(_oft, _te) \
-       list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
-+extern int of_bus_n_addr_cells(struct device_node *np);
-+extern int of_bus_n_size_cells(struct device_node *np);
-+
-+#ifdef CONFIG_OF_ADDRESS
-+extern int of_dma_get_range(struct device_node *np, u64 *dma_addr,
-+                          u64 *paddr, u64 *size);
-+#else
-+static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr,
-+                                 u64 *paddr, u64 *size)
-+{
-+      return -ENODEV;
-+}
-+#endif
-+
- #endif /* _LINUX_OF_PRIVATE_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch b/target/linux/bcm27xx/patches-5.4/950-0443-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
new file mode 100644 (file)
index 0000000..28d1987
--- /dev/null
@@ -0,0 +1,40 @@
+From 39f5d9e883393e32938eac45b564f74afde8a942 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Wed, 4 Sep 2019 11:43:30 +0100
+Subject: [PATCH] of/address: Translate 'dma-ranges' for parent nodes
+ missing 'dma-ranges'
+
+commit 81db12ee15cb83926e290a8a3654a2dfebc80935 upstream.
+
+'dma-ranges' frequently exists without parent nodes having 'dma-ranges'.
+While this is an error for 'ranges', this is fine because DMA capable
+devices always have a translatable DMA address. Also, with no
+'dma-ranges' at all, the assumption is that DMA addresses are 1:1 with
+no restrictions unless perhaps the device itself has implicit
+restrictions.
+
+Cc: Robin Murphy <robin.murphy@arm.com>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Rob Herring <robh@kernel.org>
+---
+ drivers/of/address.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -519,9 +519,13 @@ static int of_translate_one(struct devic
+        *
+        * As far as we know, this damage only exists on Apple machines, so
+        * This code is only enabled on powerpc. --gcl
++       *
++       * This quirk also applies for 'dma-ranges' which frequently exist in
++       * child nodes without 'dma-ranges' in the parent nodes. --RobH
+        */
+       ranges = of_get_property(parent, rprop, &rlen);
+-      if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
++      if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
++          strcmp(rprop, "dma-ranges")) {
+               pr_debug("no ranges; cannot translate\n");
+               return 1;
+       }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0444-of-Make-of_dma_get_range-work-on-bus-nodes.patch
new file mode 100644 (file)
index 0000000..1cac2df
--- /dev/null
@@ -0,0 +1,107 @@
+From 7631cb95056f03136c9e0a35484e8bebe7b52650 Mon Sep 17 00:00:00 2001
+From: Robin Murphy <robin.murphy@arm.com>
+Date: Wed, 3 Jul 2019 18:42:20 +0100
+Subject: [PATCH] of: Make of_dma_get_range() work on bus nodes
+
+commit 951d48855d86e72e0d6de73440fe09d363168064 upstream.
+
+Since the "dma-ranges" property is only valid for a node representing a
+bus, of_dma_get_range() currently assumes the node passed in is a leaf
+representing a device, and starts the walk from its parent. In cases
+like PCI host controllers on typical FDT systems, however, where the PCI
+endpoints are probed dynamically the initial leaf node represents the
+'bus' itself, and this logic means we fail to consider any "dma-ranges"
+describing the host bridge itself. Rework the logic such that
+of_dma_get_range() also works correctly starting from a bus node
+containing "dma-ranges".
+
+While this does mean "dma-ranges" could incorrectly be in a device leaf
+node, there isn't really any way in this function to ensure that a leaf
+node is or isn't a bus node.
+
+Signed-off-by: Robin Murphy <robin.murphy@arm.com>
+[robh: Allow for the bus child node to still be passed in]
+Signed-off-by: Rob Herring <robh@kernel.org>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ drivers/of/address.c | 44 ++++++++++++++++++--------------------------
+ 1 file changed, 18 insertions(+), 26 deletions(-)
+
+--- a/drivers/of/address.c
++++ b/drivers/of/address.c
+@@ -940,47 +940,39 @@ int of_dma_get_range(struct device_node
+       const __be32 *ranges = NULL;
+       int len, naddr, nsize, pna;
+       int ret = 0;
++      bool found_dma_ranges = false;
+       u64 dmaaddr;
+-      if (!node)
+-              return -EINVAL;
+-
+-      while (1) {
+-              struct device_node *parent;
+-
+-              naddr = of_n_addr_cells(node);
+-              nsize = of_n_size_cells(node);
+-
+-              parent = __of_get_dma_parent(node);
+-              of_node_put(node);
+-
+-              node = parent;
+-              if (!node)
+-                      break;
+-
++      while (node) {
+               ranges = of_get_property(node, "dma-ranges", &len);
+               /* Ignore empty ranges, they imply no translation required */
+               if (ranges && len > 0)
+                       break;
+-              /*
+-               * At least empty ranges has to be defined for parent node if
+-               * DMA is supported
+-               */
+-              if (!ranges)
+-                      break;
++              /* Once we find 'dma-ranges', then a missing one is an error */
++              if (found_dma_ranges && !ranges) {
++                      ret = -ENODEV;
++                      goto out;
++              }
++              found_dma_ranges = true;
++
++              node = of_get_next_dma_parent(node);
+       }
+-      if (!ranges) {
++      if (!node || !ranges) {
+               pr_debug("no dma-ranges found for node(%pOF)\n", np);
+               ret = -ENODEV;
+               goto out;
+       }
+-      len /= sizeof(u32);
+-
++      naddr = of_bus_n_addr_cells(node);
++      nsize = of_bus_n_size_cells(node);
+       pna = of_n_addr_cells(node);
++      if ((len / sizeof(__be32)) % (pna + naddr + nsize)) {
++              ret = -EINVAL;
++              goto out;
++      }
+       /* dma-ranges format:
+        * DMA addr     : naddr cells
+@@ -988,7 +980,7 @@ int of_dma_get_range(struct device_node
+        * size         : nsize cells
+        */
+       dmaaddr = of_read_number(ranges, naddr);
+-      *paddr = of_translate_dma_address(np, ranges);
++      *paddr = of_translate_dma_address(node, ranges + naddr);
+       if (*paddr == OF_BAD_ADDR) {
+               pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n",
+                      dma_addr, np);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0444-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch b/target/linux/bcm27xx/patches-5.4/950-0444-of-address-Translate-dma-ranges-for-parent-nodes-mis.patch
deleted file mode 100644 (file)
index 28d1987..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-From 39f5d9e883393e32938eac45b564f74afde8a942 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 4 Sep 2019 11:43:30 +0100
-Subject: [PATCH] of/address: Translate 'dma-ranges' for parent nodes
- missing 'dma-ranges'
-
-commit 81db12ee15cb83926e290a8a3654a2dfebc80935 upstream.
-
-'dma-ranges' frequently exists without parent nodes having 'dma-ranges'.
-While this is an error for 'ranges', this is fine because DMA capable
-devices always have a translatable DMA address. Also, with no
-'dma-ranges' at all, the assumption is that DMA addresses are 1:1 with
-no restrictions unless perhaps the device itself has implicit
-restrictions.
-
-Cc: Robin Murphy <robin.murphy@arm.com>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Rob Herring <robh@kernel.org>
----
- drivers/of/address.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -519,9 +519,13 @@ static int of_translate_one(struct devic
-        *
-        * As far as we know, this damage only exists on Apple machines, so
-        * This code is only enabled on powerpc. --gcl
-+       *
-+       * This quirk also applies for 'dma-ranges' which frequently exist in
-+       * child nodes without 'dma-ranges' in the parent nodes. --RobH
-        */
-       ranges = of_get_property(parent, rprop, &rlen);
--      if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
-+      if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
-+          strcmp(rprop, "dma-ranges")) {
-               pr_debug("no ranges; cannot translate\n");
-               return 1;
-       }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch b/target/linux/bcm27xx/patches-5.4/950-0445-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
new file mode 100644 (file)
index 0000000..a90e7f8
--- /dev/null
@@ -0,0 +1,30 @@
+From c17f622cbb33332a305ef383506740d3d01aa831 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:43 +0200
+Subject: [PATCH] arm64: mm: use arm64_dma_phys_limit instead of
+ calling max_zone_dma_phys()
+
+commit ae970dc096b2d39f65f2e18d142e3978dc9ee1c7 upstream.
+
+By the time we call zones_sizes_init() arm64_dma_phys_limit already
+contains the result of max_zone_dma_phys(). We use the variable instead
+of calling the function directly to save some precious cpu time.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
+       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
+ #ifdef CONFIG_ZONE_DMA32
+-      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(max_zone_dma_phys());
++      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
+ #endif
+       max_zone_pfns[ZONE_NORMAL] = max;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0445-of-Make-of_dma_get_range-work-on-bus-nodes.patch b/target/linux/bcm27xx/patches-5.4/950-0445-of-Make-of_dma_get_range-work-on-bus-nodes.patch
deleted file mode 100644 (file)
index 1cac2df..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-From 7631cb95056f03136c9e0a35484e8bebe7b52650 Mon Sep 17 00:00:00 2001
-From: Robin Murphy <robin.murphy@arm.com>
-Date: Wed, 3 Jul 2019 18:42:20 +0100
-Subject: [PATCH] of: Make of_dma_get_range() work on bus nodes
-
-commit 951d48855d86e72e0d6de73440fe09d363168064 upstream.
-
-Since the "dma-ranges" property is only valid for a node representing a
-bus, of_dma_get_range() currently assumes the node passed in is a leaf
-representing a device, and starts the walk from its parent. In cases
-like PCI host controllers on typical FDT systems, however, where the PCI
-endpoints are probed dynamically the initial leaf node represents the
-'bus' itself, and this logic means we fail to consider any "dma-ranges"
-describing the host bridge itself. Rework the logic such that
-of_dma_get_range() also works correctly starting from a bus node
-containing "dma-ranges".
-
-While this does mean "dma-ranges" could incorrectly be in a device leaf
-node, there isn't really any way in this function to ensure that a leaf
-node is or isn't a bus node.
-
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-[robh: Allow for the bus child node to still be passed in]
-Signed-off-by: Rob Herring <robh@kernel.org>
-Reviewed-by: Robin Murphy <robin.murphy@arm.com>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
----
- drivers/of/address.c | 44 ++++++++++++++++++--------------------------
- 1 file changed, 18 insertions(+), 26 deletions(-)
-
---- a/drivers/of/address.c
-+++ b/drivers/of/address.c
-@@ -940,47 +940,39 @@ int of_dma_get_range(struct device_node
-       const __be32 *ranges = NULL;
-       int len, naddr, nsize, pna;
-       int ret = 0;
-+      bool found_dma_ranges = false;
-       u64 dmaaddr;
--      if (!node)
--              return -EINVAL;
--
--      while (1) {
--              struct device_node *parent;
--
--              naddr = of_n_addr_cells(node);
--              nsize = of_n_size_cells(node);
--
--              parent = __of_get_dma_parent(node);
--              of_node_put(node);
--
--              node = parent;
--              if (!node)
--                      break;
--
-+      while (node) {
-               ranges = of_get_property(node, "dma-ranges", &len);
-               /* Ignore empty ranges, they imply no translation required */
-               if (ranges && len > 0)
-                       break;
--              /*
--               * At least empty ranges has to be defined for parent node if
--               * DMA is supported
--               */
--              if (!ranges)
--                      break;
-+              /* Once we find 'dma-ranges', then a missing one is an error */
-+              if (found_dma_ranges && !ranges) {
-+                      ret = -ENODEV;
-+                      goto out;
-+              }
-+              found_dma_ranges = true;
-+
-+              node = of_get_next_dma_parent(node);
-       }
--      if (!ranges) {
-+      if (!node || !ranges) {
-               pr_debug("no dma-ranges found for node(%pOF)\n", np);
-               ret = -ENODEV;
-               goto out;
-       }
--      len /= sizeof(u32);
--
-+      naddr = of_bus_n_addr_cells(node);
-+      nsize = of_bus_n_size_cells(node);
-       pna = of_n_addr_cells(node);
-+      if ((len / sizeof(__be32)) % (pna + naddr + nsize)) {
-+              ret = -EINVAL;
-+              goto out;
-+      }
-       /* dma-ranges format:
-        * DMA addr     : naddr cells
-@@ -988,7 +980,7 @@ int of_dma_get_range(struct device_node
-        * size         : nsize cells
-        */
-       dmaaddr = of_read_number(ranges, naddr);
--      *paddr = of_translate_dma_address(np, ranges);
-+      *paddr = of_translate_dma_address(node, ranges + naddr);
-       if (*paddr == OF_BAD_ADDR) {
-               pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n",
-                      dma_addr, np);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch b/target/linux/bcm27xx/patches-5.4/950-0446-arm64-mm-use-arm64_dma_phys_limit-instead-of-calling.patch
deleted file mode 100644 (file)
index a90e7f8..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From c17f622cbb33332a305ef383506740d3d01aa831 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:43 +0200
-Subject: [PATCH] arm64: mm: use arm64_dma_phys_limit instead of
- calling max_zone_dma_phys()
-
-commit ae970dc096b2d39f65f2e18d142e3978dc9ee1c7 upstream.
-
-By the time we call zones_sizes_init() arm64_dma_phys_limit already
-contains the result of max_zone_dma_phys(). We use the variable instead
-of calling the function directly to save some precious cpu time.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/mm/init.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
-       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
- #ifdef CONFIG_ZONE_DMA32
--      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(max_zone_dma_phys());
-+      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
- #endif
-       max_zone_pfns[ZONE_NORMAL] = max;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch b/target/linux/bcm27xx/patches-5.4/950-0446-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
new file mode 100644 (file)
index 0000000..3039bfe
--- /dev/null
@@ -0,0 +1,117 @@
+From 4d2bd7f66bac81b042afc2a6e742bd776a5a3938 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:44 +0200
+Subject: [PATCH] arm64: rename variables used to calculate
+ ZONE_DMA32's size
+
+commit a573cdd7973dedd87e62196c400332896bb236c8 upstream.
+
+Let the name indicate that they are used to calculate ZONE_DMA32's size
+as opposed to ZONE_DMA.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/mm/init.c | 30 +++++++++++++++---------------
+ 1 file changed, 15 insertions(+), 15 deletions(-)
+
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -56,7 +56,7 @@ EXPORT_SYMBOL(physvirt_offset);
+ struct page *vmemmap __ro_after_init;
+ EXPORT_SYMBOL(vmemmap);
+-phys_addr_t arm64_dma_phys_limit __ro_after_init;
++phys_addr_t arm64_dma32_phys_limit __ro_after_init;
+ #ifdef CONFIG_KEXEC_CORE
+ /*
+@@ -174,7 +174,7 @@ static void __init reserve_elfcorehdr(vo
+  * currently assumes that for memory starting above 4G, 32-bit devices will
+  * use a DMA offset.
+  */
+-static phys_addr_t __init max_zone_dma_phys(void)
++static phys_addr_t __init max_zone_dma32_phys(void)
+ {
+       phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
+       return min(offset + (1ULL << 32), memblock_end_of_DRAM());
+@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
+       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
+ #ifdef CONFIG_ZONE_DMA32
+-      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
++      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
+ #endif
+       max_zone_pfns[ZONE_NORMAL] = max;
+@@ -200,16 +200,16 @@ static void __init zone_sizes_init(unsig
+ {
+       struct memblock_region *reg;
+       unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+-      unsigned long max_dma = min;
++      unsigned long max_dma32 = min;
+       memset(zone_size, 0, sizeof(zone_size));
+       /* 4GB maximum for 32-bit only capable devices */
+ #ifdef CONFIG_ZONE_DMA32
+-      max_dma = PFN_DOWN(arm64_dma_phys_limit);
+-      zone_size[ZONE_DMA32] = max_dma - min;
++      max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
++      zone_size[ZONE_DMA32] = max_dma32 - min;
+ #endif
+-      zone_size[ZONE_NORMAL] = max - max_dma;
++      zone_size[ZONE_NORMAL] = max - max_dma32;
+       memcpy(zhole_size, zone_size, sizeof(zhole_size));
+@@ -221,14 +221,14 @@ static void __init zone_sizes_init(unsig
+                       continue;
+ #ifdef CONFIG_ZONE_DMA32
+-              if (start < max_dma) {
+-                      unsigned long dma_end = min(end, max_dma);
++              if (start < max_dma32) {
++                      unsigned long dma_end = min(end, max_dma32);
+                       zhole_size[ZONE_DMA32] -= dma_end - start;
+               }
+ #endif
+-              if (end > max_dma) {
++              if (end > max_dma32) {
+                       unsigned long normal_end = min(end, max);
+-                      unsigned long normal_start = max(start, max_dma);
++                      unsigned long normal_start = max(start, max_dma32);
+                       zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
+               }
+       }
+@@ -420,9 +420,9 @@ void __init arm64_memblock_init(void)
+       /* 4GB maximum for 32-bit only capable devices */
+       if (IS_ENABLED(CONFIG_ZONE_DMA32))
+-              arm64_dma_phys_limit = max_zone_dma_phys();
++              arm64_dma32_phys_limit = max_zone_dma32_phys();
+       else
+-              arm64_dma_phys_limit = PHYS_MASK + 1;
++              arm64_dma32_phys_limit = PHYS_MASK + 1;
+       reserve_crashkernel();
+@@ -430,7 +430,7 @@ void __init arm64_memblock_init(void)
+       high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
+-      dma_contiguous_reserve(arm64_dma_phys_limit);
++      dma_contiguous_reserve(arm64_dma32_phys_limit);
+ }
+ void __init bootmem_init(void)
+@@ -534,7 +534,7 @@ static void __init free_unused_memmap(vo
+ void __init mem_init(void)
+ {
+       if (swiotlb_force == SWIOTLB_FORCE ||
+-          max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
++          max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
+               swiotlb_init(1);
+       else
+               swiotlb_force = SWIOTLB_NO_FORCE;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch b/target/linux/bcm27xx/patches-5.4/950-0447-arm64-rename-variables-used-to-calculate-ZONE_DMA32-.patch
deleted file mode 100644 (file)
index 3039bfe..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-From 4d2bd7f66bac81b042afc2a6e742bd776a5a3938 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:44 +0200
-Subject: [PATCH] arm64: rename variables used to calculate
- ZONE_DMA32's size
-
-commit a573cdd7973dedd87e62196c400332896bb236c8 upstream.
-
-Let the name indicate that they are used to calculate ZONE_DMA32's size
-as opposed to ZONE_DMA.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/mm/init.c | 30 +++++++++++++++---------------
- 1 file changed, 15 insertions(+), 15 deletions(-)
-
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -56,7 +56,7 @@ EXPORT_SYMBOL(physvirt_offset);
- struct page *vmemmap __ro_after_init;
- EXPORT_SYMBOL(vmemmap);
--phys_addr_t arm64_dma_phys_limit __ro_after_init;
-+phys_addr_t arm64_dma32_phys_limit __ro_after_init;
- #ifdef CONFIG_KEXEC_CORE
- /*
-@@ -174,7 +174,7 @@ static void __init reserve_elfcorehdr(vo
-  * currently assumes that for memory starting above 4G, 32-bit devices will
-  * use a DMA offset.
-  */
--static phys_addr_t __init max_zone_dma_phys(void)
-+static phys_addr_t __init max_zone_dma32_phys(void)
- {
-       phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
-       return min(offset + (1ULL << 32), memblock_end_of_DRAM());
-@@ -187,7 +187,7 @@ static void __init zone_sizes_init(unsig
-       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
- #ifdef CONFIG_ZONE_DMA32
--      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma_phys_limit);
-+      max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
- #endif
-       max_zone_pfns[ZONE_NORMAL] = max;
-@@ -200,16 +200,16 @@ static void __init zone_sizes_init(unsig
- {
-       struct memblock_region *reg;
-       unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
--      unsigned long max_dma = min;
-+      unsigned long max_dma32 = min;
-       memset(zone_size, 0, sizeof(zone_size));
-       /* 4GB maximum for 32-bit only capable devices */
- #ifdef CONFIG_ZONE_DMA32
--      max_dma = PFN_DOWN(arm64_dma_phys_limit);
--      zone_size[ZONE_DMA32] = max_dma - min;
-+      max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
-+      zone_size[ZONE_DMA32] = max_dma32 - min;
- #endif
--      zone_size[ZONE_NORMAL] = max - max_dma;
-+      zone_size[ZONE_NORMAL] = max - max_dma32;
-       memcpy(zhole_size, zone_size, sizeof(zhole_size));
-@@ -221,14 +221,14 @@ static void __init zone_sizes_init(unsig
-                       continue;
- #ifdef CONFIG_ZONE_DMA32
--              if (start < max_dma) {
--                      unsigned long dma_end = min(end, max_dma);
-+              if (start < max_dma32) {
-+                      unsigned long dma_end = min(end, max_dma32);
-                       zhole_size[ZONE_DMA32] -= dma_end - start;
-               }
- #endif
--              if (end > max_dma) {
-+              if (end > max_dma32) {
-                       unsigned long normal_end = min(end, max);
--                      unsigned long normal_start = max(start, max_dma);
-+                      unsigned long normal_start = max(start, max_dma32);
-                       zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
-               }
-       }
-@@ -420,9 +420,9 @@ void __init arm64_memblock_init(void)
-       /* 4GB maximum for 32-bit only capable devices */
-       if (IS_ENABLED(CONFIG_ZONE_DMA32))
--              arm64_dma_phys_limit = max_zone_dma_phys();
-+              arm64_dma32_phys_limit = max_zone_dma32_phys();
-       else
--              arm64_dma_phys_limit = PHYS_MASK + 1;
-+              arm64_dma32_phys_limit = PHYS_MASK + 1;
-       reserve_crashkernel();
-@@ -430,7 +430,7 @@ void __init arm64_memblock_init(void)
-       high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
--      dma_contiguous_reserve(arm64_dma_phys_limit);
-+      dma_contiguous_reserve(arm64_dma32_phys_limit);
- }
- void __init bootmem_init(void)
-@@ -534,7 +534,7 @@ static void __init free_unused_memmap(vo
- void __init mem_init(void)
- {
-       if (swiotlb_force == SWIOTLB_FORCE ||
--          max_pfn > (arm64_dma_phys_limit >> PAGE_SHIFT))
-+          max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
-               swiotlb_init(1);
-       else
-               swiotlb_force = SWIOTLB_NO_FORCE;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0447-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
new file mode 100644 (file)
index 0000000..2397c71
--- /dev/null
@@ -0,0 +1,174 @@
+From 1fb65f4bc30fbadd0c89521985ff8142693c9631 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:45 +0200
+Subject: [PATCH] arm64: use both ZONE_DMA and ZONE_DMA32
+
+commit 1a8e1cef7603e218339ac63cb3178b25554524e5 upstream.
+
+So far all arm64 devices have supported 32 bit DMA masks for their
+peripherals. This is not true anymore for the Raspberry Pi 4 as most of
+it's peripherals can only address the first GB of memory on a total of
+up to 4 GB.
+
+This goes against ZONE_DMA32's intent, as it's expected for ZONE_DMA32
+to be addressable with a 32 bit mask. So it was decided to re-introduce
+ZONE_DMA in arm64.
+
+ZONE_DMA will contain the lower 1G of memory, which is currently the
+memory area addressable by any peripheral on an arm64 device.
+ZONE_DMA32 will contain the rest of the 32 bit addressable memory.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/Kconfig            |  4 +++
+ arch/arm64/include/asm/page.h |  2 ++
+ arch/arm64/mm/init.c          | 54 +++++++++++++++++++++++++----------
+ 3 files changed, 45 insertions(+), 15 deletions(-)
+
+--- a/arch/arm64/Kconfig
++++ b/arch/arm64/Kconfig
+@@ -267,6 +267,10 @@ config GENERIC_CSUM
+ config GENERIC_CALIBRATE_DELAY
+       def_bool y
++config ZONE_DMA
++      bool "Support DMA zone" if EXPERT
++      default y
++
+ config ZONE_DMA32
+       bool "Support DMA32 zone" if EXPERT
+       default y
+--- a/arch/arm64/include/asm/page.h
++++ b/arch/arm64/include/asm/page.h
+@@ -38,4 +38,6 @@ extern int pfn_valid(unsigned long);
+ #include <asm-generic/getorder.h>
++#define ARCH_ZONE_DMA_BITS 30
++
+ #endif
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -56,6 +56,13 @@ EXPORT_SYMBOL(physvirt_offset);
+ struct page *vmemmap __ro_after_init;
+ EXPORT_SYMBOL(vmemmap);
++/*
++ * We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
++ * memory as some devices, namely the Raspberry Pi 4, have peripherals with
++ * this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
++ * bit addressable memory area.
++ */
++phys_addr_t arm64_dma_phys_limit __ro_after_init;
+ phys_addr_t arm64_dma32_phys_limit __ro_after_init;
+ #ifdef CONFIG_KEXEC_CORE
+@@ -169,15 +176,16 @@ static void __init reserve_elfcorehdr(vo
+ {
+ }
+ #endif /* CONFIG_CRASH_DUMP */
++
+ /*
+- * Return the maximum physical address for ZONE_DMA32 (DMA_BIT_MASK(32)). It
+- * currently assumes that for memory starting above 4G, 32-bit devices will
+- * use a DMA offset.
++ * Return the maximum physical address for a zone with a given address size
++ * limit. It currently assumes that for memory starting above 4G, 32-bit
++ * devices will use a DMA offset.
+  */
+-static phys_addr_t __init max_zone_dma32_phys(void)
++static phys_addr_t __init max_zone_phys(unsigned int zone_bits)
+ {
+-      phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
+-      return min(offset + (1ULL << 32), memblock_end_of_DRAM());
++      phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits);
++      return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM());
+ }
+ #ifdef CONFIG_NUMA
+@@ -186,6 +194,9 @@ static void __init zone_sizes_init(unsig
+ {
+       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
++#ifdef CONFIG_ZONE_DMA
++      max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
+ #endif
+@@ -201,13 +212,18 @@ static void __init zone_sizes_init(unsig
+       struct memblock_region *reg;
+       unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+       unsigned long max_dma32 = min;
++      unsigned long max_dma = min;
+       memset(zone_size, 0, sizeof(zone_size));
+-      /* 4GB maximum for 32-bit only capable devices */
++#ifdef CONFIG_ZONE_DMA
++      max_dma = PFN_DOWN(arm64_dma_phys_limit);
++      zone_size[ZONE_DMA] = max_dma - min;
++      max_dma32 = max_dma;
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+       max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
+-      zone_size[ZONE_DMA32] = max_dma32 - min;
++      zone_size[ZONE_DMA32] = max_dma32 - max_dma;
+ #endif
+       zone_size[ZONE_NORMAL] = max - max_dma32;
+@@ -219,11 +235,17 @@ static void __init zone_sizes_init(unsig
+               if (start >= max)
+                       continue;
+-
++#ifdef CONFIG_ZONE_DMA
++              if (start < max_dma) {
++                      unsigned long dma_end = min_not_zero(end, max_dma);
++                      zhole_size[ZONE_DMA] -= dma_end - start;
++              }
++#endif
+ #ifdef CONFIG_ZONE_DMA32
+               if (start < max_dma32) {
+-                      unsigned long dma_end = min(end, max_dma32);
+-                      zhole_size[ZONE_DMA32] -= dma_end - start;
++                      unsigned long dma32_end = min(end, max_dma32);
++                      unsigned long dma32_start = max(start, max_dma);
++                      zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
+               }
+ #endif
+               if (end > max_dma32) {
+@@ -418,9 +440,11 @@ void __init arm64_memblock_init(void)
+       early_init_fdt_scan_reserved_mem();
+-      /* 4GB maximum for 32-bit only capable devices */
++      if (IS_ENABLED(CONFIG_ZONE_DMA))
++              arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
++
+       if (IS_ENABLED(CONFIG_ZONE_DMA32))
+-              arm64_dma32_phys_limit = max_zone_dma32_phys();
++              arm64_dma32_phys_limit = max_zone_phys(32);
+       else
+               arm64_dma32_phys_limit = PHYS_MASK + 1;
+@@ -430,7 +454,7 @@ void __init arm64_memblock_init(void)
+       high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
+-      dma_contiguous_reserve(arm64_dma32_phys_limit);
++      dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
+ }
+ void __init bootmem_init(void)
+@@ -534,7 +558,7 @@ static void __init free_unused_memmap(vo
+ void __init mem_init(void)
+ {
+       if (swiotlb_force == SWIOTLB_FORCE ||
+-          max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
++          max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
+               swiotlb_init(1);
+       else
+               swiotlb_force = SWIOTLB_NO_FORCE;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0448-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch b/target/linux/bcm27xx/patches-5.4/950-0448-arm64-use-both-ZONE_DMA-and-ZONE_DMA32.patch
deleted file mode 100644 (file)
index 2397c71..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-From 1fb65f4bc30fbadd0c89521985ff8142693c9631 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:45 +0200
-Subject: [PATCH] arm64: use both ZONE_DMA and ZONE_DMA32
-
-commit 1a8e1cef7603e218339ac63cb3178b25554524e5 upstream.
-
-So far all arm64 devices have supported 32 bit DMA masks for their
-peripherals. This is not true anymore for the Raspberry Pi 4 as most of
-it's peripherals can only address the first GB of memory on a total of
-up to 4 GB.
-
-This goes against ZONE_DMA32's intent, as it's expected for ZONE_DMA32
-to be addressable with a 32 bit mask. So it was decided to re-introduce
-ZONE_DMA in arm64.
-
-ZONE_DMA will contain the lower 1G of memory, which is currently the
-memory area addressable by any peripheral on an arm64 device.
-ZONE_DMA32 will contain the rest of the 32 bit addressable memory.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/Kconfig            |  4 +++
- arch/arm64/include/asm/page.h |  2 ++
- arch/arm64/mm/init.c          | 54 +++++++++++++++++++++++++----------
- 3 files changed, 45 insertions(+), 15 deletions(-)
-
---- a/arch/arm64/Kconfig
-+++ b/arch/arm64/Kconfig
-@@ -267,6 +267,10 @@ config GENERIC_CSUM
- config GENERIC_CALIBRATE_DELAY
-       def_bool y
-+config ZONE_DMA
-+      bool "Support DMA zone" if EXPERT
-+      default y
-+
- config ZONE_DMA32
-       bool "Support DMA32 zone" if EXPERT
-       default y
---- a/arch/arm64/include/asm/page.h
-+++ b/arch/arm64/include/asm/page.h
-@@ -38,4 +38,6 @@ extern int pfn_valid(unsigned long);
- #include <asm-generic/getorder.h>
-+#define ARCH_ZONE_DMA_BITS 30
-+
- #endif
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -56,6 +56,13 @@ EXPORT_SYMBOL(physvirt_offset);
- struct page *vmemmap __ro_after_init;
- EXPORT_SYMBOL(vmemmap);
-+/*
-+ * We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
-+ * memory as some devices, namely the Raspberry Pi 4, have peripherals with
-+ * this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
-+ * bit addressable memory area.
-+ */
-+phys_addr_t arm64_dma_phys_limit __ro_after_init;
- phys_addr_t arm64_dma32_phys_limit __ro_after_init;
- #ifdef CONFIG_KEXEC_CORE
-@@ -169,15 +176,16 @@ static void __init reserve_elfcorehdr(vo
- {
- }
- #endif /* CONFIG_CRASH_DUMP */
-+
- /*
-- * Return the maximum physical address for ZONE_DMA32 (DMA_BIT_MASK(32)). It
-- * currently assumes that for memory starting above 4G, 32-bit devices will
-- * use a DMA offset.
-+ * Return the maximum physical address for a zone with a given address size
-+ * limit. It currently assumes that for memory starting above 4G, 32-bit
-+ * devices will use a DMA offset.
-  */
--static phys_addr_t __init max_zone_dma32_phys(void)
-+static phys_addr_t __init max_zone_phys(unsigned int zone_bits)
- {
--      phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
--      return min(offset + (1ULL << 32), memblock_end_of_DRAM());
-+      phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, zone_bits);
-+      return min(offset + (1ULL << zone_bits), memblock_end_of_DRAM());
- }
- #ifdef CONFIG_NUMA
-@@ -186,6 +194,9 @@ static void __init zone_sizes_init(unsig
- {
-       unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
-+#ifdef CONFIG_ZONE_DMA
-+      max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
-+#endif
- #ifdef CONFIG_ZONE_DMA32
-       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
- #endif
-@@ -201,13 +212,18 @@ static void __init zone_sizes_init(unsig
-       struct memblock_region *reg;
-       unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
-       unsigned long max_dma32 = min;
-+      unsigned long max_dma = min;
-       memset(zone_size, 0, sizeof(zone_size));
--      /* 4GB maximum for 32-bit only capable devices */
-+#ifdef CONFIG_ZONE_DMA
-+      max_dma = PFN_DOWN(arm64_dma_phys_limit);
-+      zone_size[ZONE_DMA] = max_dma - min;
-+      max_dma32 = max_dma;
-+#endif
- #ifdef CONFIG_ZONE_DMA32
-       max_dma32 = PFN_DOWN(arm64_dma32_phys_limit);
--      zone_size[ZONE_DMA32] = max_dma32 - min;
-+      zone_size[ZONE_DMA32] = max_dma32 - max_dma;
- #endif
-       zone_size[ZONE_NORMAL] = max - max_dma32;
-@@ -219,11 +235,17 @@ static void __init zone_sizes_init(unsig
-               if (start >= max)
-                       continue;
--
-+#ifdef CONFIG_ZONE_DMA
-+              if (start < max_dma) {
-+                      unsigned long dma_end = min_not_zero(end, max_dma);
-+                      zhole_size[ZONE_DMA] -= dma_end - start;
-+              }
-+#endif
- #ifdef CONFIG_ZONE_DMA32
-               if (start < max_dma32) {
--                      unsigned long dma_end = min(end, max_dma32);
--                      zhole_size[ZONE_DMA32] -= dma_end - start;
-+                      unsigned long dma32_end = min(end, max_dma32);
-+                      unsigned long dma32_start = max(start, max_dma);
-+                      zhole_size[ZONE_DMA32] -= dma32_end - dma32_start;
-               }
- #endif
-               if (end > max_dma32) {
-@@ -418,9 +440,11 @@ void __init arm64_memblock_init(void)
-       early_init_fdt_scan_reserved_mem();
--      /* 4GB maximum for 32-bit only capable devices */
-+      if (IS_ENABLED(CONFIG_ZONE_DMA))
-+              arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
-+
-       if (IS_ENABLED(CONFIG_ZONE_DMA32))
--              arm64_dma32_phys_limit = max_zone_dma32_phys();
-+              arm64_dma32_phys_limit = max_zone_phys(32);
-       else
-               arm64_dma32_phys_limit = PHYS_MASK + 1;
-@@ -430,7 +454,7 @@ void __init arm64_memblock_init(void)
-       high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
--      dma_contiguous_reserve(arm64_dma32_phys_limit);
-+      dma_contiguous_reserve(arm64_dma_phys_limit ? : arm64_dma32_phys_limit);
- }
- void __init bootmem_init(void)
-@@ -534,7 +558,7 @@ static void __init free_unused_memmap(vo
- void __init mem_init(void)
- {
-       if (swiotlb_force == SWIOTLB_FORCE ||
--          max_pfn > (arm64_dma32_phys_limit >> PAGE_SHIFT))
-+          max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
-               swiotlb_init(1);
-       else
-               swiotlb_force = SWIOTLB_NO_FORCE;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch b/target/linux/bcm27xx/patches-5.4/950-0448-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
new file mode 100644 (file)
index 0000000..23811e0
--- /dev/null
@@ -0,0 +1,84 @@
+From 1c108eaeae73a504ac1b2d882bc1fefb91eecf17 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Wed, 11 Sep 2019 20:25:46 +0200
+Subject: [PATCH] mm: refresh ZONE_DMA and ZONE_DMA32 comments in 'enum
+ zone_type'
+
+commit 734f9246e791d8da278957b2c326d7709b2a97c0 upstream.
+
+These zones usage has evolved with time and the comments were outdated.
+This joins both ZONE_DMA and ZONE_DMA32 explanation and gives up to date
+examples on how they are used on different architectures.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ include/linux/mmzone.h | 45 ++++++++++++++++++++++++------------------
+ 1 file changed, 26 insertions(+), 19 deletions(-)
+
+--- a/include/linux/mmzone.h
++++ b/include/linux/mmzone.h
+@@ -358,33 +358,40 @@ struct per_cpu_nodestat {
+ #endif /* !__GENERATING_BOUNDS.H */
+ enum zone_type {
+-#ifdef CONFIG_ZONE_DMA
+       /*
+-       * ZONE_DMA is used when there are devices that are not able
+-       * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
+-       * carve out the portion of memory that is needed for these devices.
+-       * The range is arch specific.
+-       *
+-       * Some examples
+-       *
+-       * Architecture         Limit
+-       * ---------------------------
+-       * parisc, ia64, sparc  <4G
+-       * s390, powerpc        <2G
+-       * arm                  Various
+-       * alpha                Unlimited or 0-16MB.
++       * ZONE_DMA and ZONE_DMA32 are used when there are peripherals not able
++       * to DMA to all of the addressable memory (ZONE_NORMAL).
++       * On architectures where this area covers the whole 32 bit address
++       * space ZONE_DMA32 is used. ZONE_DMA is left for the ones with smaller
++       * DMA addressing constraints. This distinction is important as a 32bit
++       * DMA mask is assumed when ZONE_DMA32 is defined. Some 64-bit
++       * platforms may need both zones as they support peripherals with
++       * different DMA addressing limitations.
++       *
++       * Some examples:
++       *
++       *  - i386 and x86_64 have a fixed 16M ZONE_DMA and ZONE_DMA32 for the
++       *    rest of the lower 4G.
++       *
++       *  - arm only uses ZONE_DMA, the size, up to 4G, may vary depending on
++       *    the specific device.
++       *
++       *  - arm64 has a fixed 1G ZONE_DMA and ZONE_DMA32 for the rest of the
++       *    lower 4G.
++       *
++       *  - powerpc only uses ZONE_DMA, the size, up to 2G, may vary
++       *    depending on the specific device.
+        *
+-       * i386, x86_64 and multiple other arches
+-       *                      <16M.
++       *  - s390 uses ZONE_DMA fixed to the lower 2G.
++       *
++       *  - ia64 and riscv only use ZONE_DMA32.
++       *
++       *  - parisc uses neither.
+        */
++#ifdef CONFIG_ZONE_DMA
+       ZONE_DMA,
+ #endif
+ #ifdef CONFIG_ZONE_DMA32
+-      /*
+-       * x86_64 needs two ZONE_DMAs because it supports devices that are
+-       * only able to do DMA to the lower 16M but also 32 bit devices that
+-       * can only do DMA areas below 4G.
+-       */
+       ZONE_DMA32,
+ #endif
+       /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0449-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch b/target/linux/bcm27xx/patches-5.4/950-0449-mm-refresh-ZONE_DMA-and-ZONE_DMA32-comments-in-enum-.patch
deleted file mode 100644 (file)
index 23811e0..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-From 1c108eaeae73a504ac1b2d882bc1fefb91eecf17 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Wed, 11 Sep 2019 20:25:46 +0200
-Subject: [PATCH] mm: refresh ZONE_DMA and ZONE_DMA32 comments in 'enum
- zone_type'
-
-commit 734f9246e791d8da278957b2c326d7709b2a97c0 upstream.
-
-These zones usage has evolved with time and the comments were outdated.
-This joins both ZONE_DMA and ZONE_DMA32 explanation and gives up to date
-examples on how they are used on different architectures.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Christoph Hellwig <hch@lst.de>
-Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- include/linux/mmzone.h | 45 ++++++++++++++++++++++++------------------
- 1 file changed, 26 insertions(+), 19 deletions(-)
-
---- a/include/linux/mmzone.h
-+++ b/include/linux/mmzone.h
-@@ -358,33 +358,40 @@ struct per_cpu_nodestat {
- #endif /* !__GENERATING_BOUNDS.H */
- enum zone_type {
--#ifdef CONFIG_ZONE_DMA
-       /*
--       * ZONE_DMA is used when there are devices that are not able
--       * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
--       * carve out the portion of memory that is needed for these devices.
--       * The range is arch specific.
--       *
--       * Some examples
--       *
--       * Architecture         Limit
--       * ---------------------------
--       * parisc, ia64, sparc  <4G
--       * s390, powerpc        <2G
--       * arm                  Various
--       * alpha                Unlimited or 0-16MB.
-+       * ZONE_DMA and ZONE_DMA32 are used when there are peripherals not able
-+       * to DMA to all of the addressable memory (ZONE_NORMAL).
-+       * On architectures where this area covers the whole 32 bit address
-+       * space ZONE_DMA32 is used. ZONE_DMA is left for the ones with smaller
-+       * DMA addressing constraints. This distinction is important as a 32bit
-+       * DMA mask is assumed when ZONE_DMA32 is defined. Some 64-bit
-+       * platforms may need both zones as they support peripherals with
-+       * different DMA addressing limitations.
-+       *
-+       * Some examples:
-+       *
-+       *  - i386 and x86_64 have a fixed 16M ZONE_DMA and ZONE_DMA32 for the
-+       *    rest of the lower 4G.
-+       *
-+       *  - arm only uses ZONE_DMA, the size, up to 4G, may vary depending on
-+       *    the specific device.
-+       *
-+       *  - arm64 has a fixed 1G ZONE_DMA and ZONE_DMA32 for the rest of the
-+       *    lower 4G.
-+       *
-+       *  - powerpc only uses ZONE_DMA, the size, up to 2G, may vary
-+       *    depending on the specific device.
-        *
--       * i386, x86_64 and multiple other arches
--       *                      <16M.
-+       *  - s390 uses ZONE_DMA fixed to the lower 2G.
-+       *
-+       *  - ia64 and riscv only use ZONE_DMA32.
-+       *
-+       *  - parisc uses neither.
-        */
-+#ifdef CONFIG_ZONE_DMA
-       ZONE_DMA,
- #endif
- #ifdef CONFIG_ZONE_DMA32
--      /*
--       * x86_64 needs two ZONE_DMAs because it supports devices that are
--       * only able to do DMA to the lower 16M but also 32 bit devices that
--       * can only do DMA areas below 4G.
--       */
-       ZONE_DMA32,
- #endif
-       /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0449-resource-Add-a-resource_list_first_type-helper.patch
new file mode 100644 (file)
index 0000000..c2c959a
--- /dev/null
@@ -0,0 +1,36 @@
+From dacb1a46835914b8c3862db15726bcc0a68af8f5 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Mon, 28 Oct 2019 11:32:32 -0500
+Subject: [PATCH] resource: Add a resource_list_first_type helper
+
+commit 494f8b10d832456a96be4ee7317425f6936cabc8 upstream.
+
+A common pattern is looping over a resource_list just to get a matching
+entry with a specific type. Add resource_list_first_type() helper which
+implements this.
+
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ include/linux/resource_ext.h | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/include/linux/resource_ext.h
++++ b/include/linux/resource_ext.h
+@@ -66,4 +66,16 @@ resource_list_destroy_entry(struct resou
+ #define resource_list_for_each_entry_safe(entry, tmp, list)   \
+       list_for_each_entry_safe((entry), (tmp), (list), node)
++static inline struct resource_entry *
++resource_list_first_type(struct list_head *list, unsigned long type)
++{
++      struct resource_entry *entry;
++
++      resource_list_for_each_entry(entry, list) {
++              if (resource_type(entry->res) == type)
++                      return entry;
++      }
++      return NULL;
++}
++
+ #endif /* _LINUX_RESOURCE_EXT_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0450-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
new file mode 100644 (file)
index 0000000..5de73d3
--- /dev/null
@@ -0,0 +1,195 @@
+From 78b03f0aef9f67c4db700ba5dc56e2c8f562d181 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Mon, 14 Oct 2019 20:31:03 +0200
+Subject: [PATCH] dma/direct: turn ARCH_ZONE_DMA_BITS into a variable
+
+commit 8b5369ea580964dbc982781bfb9fb93459fc5e8d upstream.
+
+Some architectures, notably ARM, are interested in tweaking this
+depending on their runtime DMA addressing limitations.
+
+Acked-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+---
+ arch/arm64/include/asm/page.h   |  2 --
+ arch/arm64/mm/init.c            |  9 +++++++--
+ arch/powerpc/include/asm/page.h |  9 ---------
+ arch/powerpc/mm/mem.c           | 20 +++++++++++++++-----
+ arch/s390/include/asm/page.h    |  2 --
+ arch/s390/mm/init.c             |  1 +
+ include/linux/dma-direct.h      |  2 ++
+ kernel/dma/direct.c             | 13 ++++++-------
+ 8 files changed, 31 insertions(+), 27 deletions(-)
+
+--- a/arch/arm64/include/asm/page.h
++++ b/arch/arm64/include/asm/page.h
+@@ -38,6 +38,4 @@ extern int pfn_valid(unsigned long);
+ #include <asm-generic/getorder.h>
+-#define ARCH_ZONE_DMA_BITS 30
+-
+ #endif
+--- a/arch/arm64/mm/init.c
++++ b/arch/arm64/mm/init.c
+@@ -20,6 +20,7 @@
+ #include <linux/sort.h>
+ #include <linux/of.h>
+ #include <linux/of_fdt.h>
++#include <linux/dma-direct.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/dma-contiguous.h>
+ #include <linux/efi.h>
+@@ -41,6 +42,8 @@
+ #include <asm/tlb.h>
+ #include <asm/alternative.h>
++#define ARM64_ZONE_DMA_BITS   30
++
+ /*
+  * We need to be able to catch inadvertent references to memstart_addr
+  * that occur (potentially in generic code) before arm64_memblock_init()
+@@ -440,8 +443,10 @@ void __init arm64_memblock_init(void)
+       early_init_fdt_scan_reserved_mem();
+-      if (IS_ENABLED(CONFIG_ZONE_DMA))
+-              arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
++      if (IS_ENABLED(CONFIG_ZONE_DMA)) {
++              zone_dma_bits = ARM64_ZONE_DMA_BITS;
++              arm64_dma_phys_limit = max_zone_phys(ARM64_ZONE_DMA_BITS);
++      }
+       if (IS_ENABLED(CONFIG_ZONE_DMA32))
+               arm64_dma32_phys_limit = max_zone_phys(32);
+--- a/arch/powerpc/include/asm/page.h
++++ b/arch/powerpc/include/asm/page.h
+@@ -334,13 +334,4 @@ struct vm_area_struct;
+ #endif /* __ASSEMBLY__ */
+ #include <asm/slice.h>
+-/*
+- * Allow 30-bit DMA for very limited Broadcom wifi chips on many powerbooks.
+- */
+-#ifdef CONFIG_PPC32
+-#define ARCH_ZONE_DMA_BITS 30
+-#else
+-#define ARCH_ZONE_DMA_BITS 31
+-#endif
+-
+ #endif /* _ASM_POWERPC_PAGE_H */
+--- a/arch/powerpc/mm/mem.c
++++ b/arch/powerpc/mm/mem.c
+@@ -31,6 +31,7 @@
+ #include <linux/slab.h>
+ #include <linux/vmalloc.h>
+ #include <linux/memremap.h>
++#include <linux/dma-direct.h>
+ #include <asm/pgalloc.h>
+ #include <asm/prom.h>
+@@ -223,10 +224,10 @@ static int __init mark_nonram_nosave(voi
+  * everything else. GFP_DMA32 page allocations automatically fall back to
+  * ZONE_DMA.
+  *
+- * By using 31-bit unconditionally, we can exploit ARCH_ZONE_DMA_BITS to
+- * inform the generic DMA mapping code.  32-bit only devices (if not handled
+- * by an IOMMU anyway) will take a first dip into ZONE_NORMAL and get
+- * otherwise served by ZONE_DMA.
++ * By using 31-bit unconditionally, we can exploit zone_dma_bits to inform the
++ * generic DMA mapping code.  32-bit only devices (if not handled by an IOMMU
++ * anyway) will take a first dip into ZONE_NORMAL and get otherwise served by
++ * ZONE_DMA.
+  */
+ static unsigned long max_zone_pfns[MAX_NR_ZONES];
+@@ -259,9 +260,18 @@ void __init paging_init(void)
+       printk(KERN_DEBUG "Memory hole size: %ldMB\n",
+              (long int)((top_of_ram - total_ram) >> 20));
++      /*
++       * Allow 30-bit DMA for very limited Broadcom wifi chips on many
++       * powerbooks.
++       */
++      if (IS_ENABLED(CONFIG_PPC32))
++              zone_dma_bits = 30;
++      else
++              zone_dma_bits = 31;
++
+ #ifdef CONFIG_ZONE_DMA
+       max_zone_pfns[ZONE_DMA] = min(max_low_pfn,
+-                                    1UL << (ARCH_ZONE_DMA_BITS - PAGE_SHIFT));
++                                    1UL << (zone_dma_bits - PAGE_SHIFT));
+ #endif
+       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+ #ifdef CONFIG_HIGHMEM
+--- a/arch/s390/include/asm/page.h
++++ b/arch/s390/include/asm/page.h
+@@ -179,8 +179,6 @@ static inline int devmem_is_allowed(unsi
+ #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
+                                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+-#define ARCH_ZONE_DMA_BITS    31
+-
+ #include <asm-generic/memory_model.h>
+ #include <asm-generic/getorder.h>
+--- a/arch/s390/mm/init.c
++++ b/arch/s390/mm/init.c
+@@ -118,6 +118,7 @@ void __init paging_init(void)
+       sparse_memory_present_with_active_regions(MAX_NUMNODES);
+       sparse_init();
++      zone_dma_bits = 31;
+       memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+       max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
+       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -8,6 +8,8 @@
+ static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
++extern unsigned int zone_dma_bits;
++
+ #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
+ #include <asm/dma-direct.h>
+ #else
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -16,12 +16,11 @@
+ #include <linux/swiotlb.h>
+ /*
+- * Most architectures use ZONE_DMA for the first 16 Megabytes, but
+- * some use it for entirely different regions:
++ * Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
++ * it for entirely different regions. In that case the arch code needs to
++ * override the variable below for dma-direct to work properly.
+  */
+-#ifndef ARCH_ZONE_DMA_BITS
+-#define ARCH_ZONE_DMA_BITS 24
+-#endif
++unsigned int zone_dma_bits __ro_after_init = 24;
+ static void report_addr(struct device *dev, dma_addr_t dma_addr, size_t size)
+ {
+@@ -69,7 +68,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
+        * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
+        * zones.
+        */
+-      if (*phys_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
++      if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
+               return GFP_DMA;
+       if (*phys_mask <= DMA_BIT_MASK(32))
+               return GFP_DMA32;
+@@ -395,7 +394,7 @@ int dma_direct_supported(struct device *
+       u64 min_mask;
+       if (IS_ENABLED(CONFIG_ZONE_DMA))
+-              min_mask = DMA_BIT_MASK(ARCH_ZONE_DMA_BITS);
++              min_mask = DMA_BIT_MASK(zone_dma_bits);
+       else
+               min_mask = DMA_BIT_MASK(30);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0450-resource-Add-a-resource_list_first_type-helper.patch b/target/linux/bcm27xx/patches-5.4/950-0450-resource-Add-a-resource_list_first_type-helper.patch
deleted file mode 100644 (file)
index c2c959a..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-From dacb1a46835914b8c3862db15726bcc0a68af8f5 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Mon, 28 Oct 2019 11:32:32 -0500
-Subject: [PATCH] resource: Add a resource_list_first_type helper
-
-commit 494f8b10d832456a96be4ee7317425f6936cabc8 upstream.
-
-A common pattern is looping over a resource_list just to get a matching
-entry with a specific type. Add resource_list_first_type() helper which
-implements this.
-
-Signed-off-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
----
- include/linux/resource_ext.h | 12 ++++++++++++
- 1 file changed, 12 insertions(+)
-
---- a/include/linux/resource_ext.h
-+++ b/include/linux/resource_ext.h
-@@ -66,4 +66,16 @@ resource_list_destroy_entry(struct resou
- #define resource_list_for_each_entry_safe(entry, tmp, list)   \
-       list_for_each_entry_safe((entry), (tmp), (list), node)
-+static inline struct resource_entry *
-+resource_list_first_type(struct list_head *list, unsigned long type)
-+{
-+      struct resource_entry *entry;
-+
-+      resource_list_for_each_entry(entry, list) {
-+              if (resource_type(entry->res) == type)
-+                      return entry;
-+      }
-+      return NULL;
-+}
-+
- #endif /* _LINUX_RESOURCE_EXT_H */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch b/target/linux/bcm27xx/patches-5.4/950-0451-dma-direct-turn-ARCH_ZONE_DMA_BITS-into-a-variable.patch
deleted file mode 100644 (file)
index 5de73d3..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-From 78b03f0aef9f67c4db700ba5dc56e2c8f562d181 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Mon, 14 Oct 2019 20:31:03 +0200
-Subject: [PATCH] dma/direct: turn ARCH_ZONE_DMA_BITS into a variable
-
-commit 8b5369ea580964dbc982781bfb9fb93459fc5e8d upstream.
-
-Some architectures, notably ARM, are interested in tweaking this
-depending on their runtime DMA addressing limitations.
-
-Acked-by: Christoph Hellwig <hch@lst.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
----
- arch/arm64/include/asm/page.h   |  2 --
- arch/arm64/mm/init.c            |  9 +++++++--
- arch/powerpc/include/asm/page.h |  9 ---------
- arch/powerpc/mm/mem.c           | 20 +++++++++++++++-----
- arch/s390/include/asm/page.h    |  2 --
- arch/s390/mm/init.c             |  1 +
- include/linux/dma-direct.h      |  2 ++
- kernel/dma/direct.c             | 13 ++++++-------
- 8 files changed, 31 insertions(+), 27 deletions(-)
-
---- a/arch/arm64/include/asm/page.h
-+++ b/arch/arm64/include/asm/page.h
-@@ -38,6 +38,4 @@ extern int pfn_valid(unsigned long);
- #include <asm-generic/getorder.h>
--#define ARCH_ZONE_DMA_BITS 30
--
- #endif
---- a/arch/arm64/mm/init.c
-+++ b/arch/arm64/mm/init.c
-@@ -20,6 +20,7 @@
- #include <linux/sort.h>
- #include <linux/of.h>
- #include <linux/of_fdt.h>
-+#include <linux/dma-direct.h>
- #include <linux/dma-mapping.h>
- #include <linux/dma-contiguous.h>
- #include <linux/efi.h>
-@@ -41,6 +42,8 @@
- #include <asm/tlb.h>
- #include <asm/alternative.h>
-+#define ARM64_ZONE_DMA_BITS   30
-+
- /*
-  * We need to be able to catch inadvertent references to memstart_addr
-  * that occur (potentially in generic code) before arm64_memblock_init()
-@@ -440,8 +443,10 @@ void __init arm64_memblock_init(void)
-       early_init_fdt_scan_reserved_mem();
--      if (IS_ENABLED(CONFIG_ZONE_DMA))
--              arm64_dma_phys_limit = max_zone_phys(ARCH_ZONE_DMA_BITS);
-+      if (IS_ENABLED(CONFIG_ZONE_DMA)) {
-+              zone_dma_bits = ARM64_ZONE_DMA_BITS;
-+              arm64_dma_phys_limit = max_zone_phys(ARM64_ZONE_DMA_BITS);
-+      }
-       if (IS_ENABLED(CONFIG_ZONE_DMA32))
-               arm64_dma32_phys_limit = max_zone_phys(32);
---- a/arch/powerpc/include/asm/page.h
-+++ b/arch/powerpc/include/asm/page.h
-@@ -334,13 +334,4 @@ struct vm_area_struct;
- #endif /* __ASSEMBLY__ */
- #include <asm/slice.h>
--/*
-- * Allow 30-bit DMA for very limited Broadcom wifi chips on many powerbooks.
-- */
--#ifdef CONFIG_PPC32
--#define ARCH_ZONE_DMA_BITS 30
--#else
--#define ARCH_ZONE_DMA_BITS 31
--#endif
--
- #endif /* _ASM_POWERPC_PAGE_H */
---- a/arch/powerpc/mm/mem.c
-+++ b/arch/powerpc/mm/mem.c
-@@ -31,6 +31,7 @@
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
- #include <linux/memremap.h>
-+#include <linux/dma-direct.h>
- #include <asm/pgalloc.h>
- #include <asm/prom.h>
-@@ -223,10 +224,10 @@ static int __init mark_nonram_nosave(voi
-  * everything else. GFP_DMA32 page allocations automatically fall back to
-  * ZONE_DMA.
-  *
-- * By using 31-bit unconditionally, we can exploit ARCH_ZONE_DMA_BITS to
-- * inform the generic DMA mapping code.  32-bit only devices (if not handled
-- * by an IOMMU anyway) will take a first dip into ZONE_NORMAL and get
-- * otherwise served by ZONE_DMA.
-+ * By using 31-bit unconditionally, we can exploit zone_dma_bits to inform the
-+ * generic DMA mapping code.  32-bit only devices (if not handled by an IOMMU
-+ * anyway) will take a first dip into ZONE_NORMAL and get otherwise served by
-+ * ZONE_DMA.
-  */
- static unsigned long max_zone_pfns[MAX_NR_ZONES];
-@@ -259,9 +260,18 @@ void __init paging_init(void)
-       printk(KERN_DEBUG "Memory hole size: %ldMB\n",
-              (long int)((top_of_ram - total_ram) >> 20));
-+      /*
-+       * Allow 30-bit DMA for very limited Broadcom wifi chips on many
-+       * powerbooks.
-+       */
-+      if (IS_ENABLED(CONFIG_PPC32))
-+              zone_dma_bits = 30;
-+      else
-+              zone_dma_bits = 31;
-+
- #ifdef CONFIG_ZONE_DMA
-       max_zone_pfns[ZONE_DMA] = min(max_low_pfn,
--                                    1UL << (ARCH_ZONE_DMA_BITS - PAGE_SHIFT));
-+                                    1UL << (zone_dma_bits - PAGE_SHIFT));
- #endif
-       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
- #ifdef CONFIG_HIGHMEM
---- a/arch/s390/include/asm/page.h
-+++ b/arch/s390/include/asm/page.h
-@@ -179,8 +179,6 @@ static inline int devmem_is_allowed(unsi
- #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
-                                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
--#define ARCH_ZONE_DMA_BITS    31
--
- #include <asm-generic/memory_model.h>
- #include <asm-generic/getorder.h>
---- a/arch/s390/mm/init.c
-+++ b/arch/s390/mm/init.c
-@@ -118,6 +118,7 @@ void __init paging_init(void)
-       sparse_memory_present_with_active_regions(MAX_NUMNODES);
-       sparse_init();
-+      zone_dma_bits = 31;
-       memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
-       max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
-       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -8,6 +8,8 @@
- static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
-+extern unsigned int zone_dma_bits;
-+
- #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
- #include <asm/dma-direct.h>
- #else
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -16,12 +16,11 @@
- #include <linux/swiotlb.h>
- /*
-- * Most architectures use ZONE_DMA for the first 16 Megabytes, but
-- * some use it for entirely different regions:
-+ * Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
-+ * it for entirely different regions. In that case the arch code needs to
-+ * override the variable below for dma-direct to work properly.
-  */
--#ifndef ARCH_ZONE_DMA_BITS
--#define ARCH_ZONE_DMA_BITS 24
--#endif
-+unsigned int zone_dma_bits __ro_after_init = 24;
- static void report_addr(struct device *dev, dma_addr_t dma_addr, size_t size)
- {
-@@ -69,7 +68,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
-        * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
-        * zones.
-        */
--      if (*phys_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
-+      if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
-               return GFP_DMA;
-       if (*phys_mask <= DMA_BIT_MASK(32))
-               return GFP_DMA32;
-@@ -395,7 +394,7 @@ int dma_direct_supported(struct device *
-       u64 min_mask;
-       if (IS_ENABLED(CONFIG_ZONE_DMA))
--              min_mask = DMA_BIT_MASK(ARCH_ZONE_DMA_BITS);
-+              min_mask = DMA_BIT_MASK(zone_dma_bits);
-       else
-               min_mask = DMA_BIT_MASK(30);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch b/target/linux/bcm27xx/patches-5.4/950-0451-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
new file mode 100644 (file)
index 0000000..51fd4be
--- /dev/null
@@ -0,0 +1,259 @@
+From 97a48106d1698038720495fdd49c491b283bf110 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 7 Nov 2019 16:06:45 +0100
+Subject: [PATCH] x86/PCI: sta2x11: use default DMA address translation
+
+commit e380a0394c36a3a878c858418d5dd7f5f195b6fc upstream.
+
+The devices found behind this PCIe chip have unusual DMA mapping
+constraints as there is an AMBA interconnect placed in between them and
+the different PCI endpoints. The offset between physical memory
+addresses and AMBA's view is provided by reading a PCI config register,
+which is saved and used whenever DMA mapping is needed.
+
+It turns out that this DMA setup can be represented by properly setting
+'dma_pfn_offset', 'dma_bus_mask' and 'dma_mask' during the PCI device
+enable fixup. And ultimately allows us to get rid of this device's
+custom DMA functions.
+
+Aside from the code deletion and DMA setup, sta2x11_pdev_to_mapping() is
+moved to avoid warnings whenever CONFIG_PM is not enabled.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+---
+ arch/x86/Kconfig                  |   1 -
+ arch/x86/include/asm/device.h     |   3 -
+ arch/x86/include/asm/dma-direct.h |   9 --
+ arch/x86/pci/sta2x11-fixup.c      | 135 ++++++------------------------
+ 4 files changed, 26 insertions(+), 122 deletions(-)
+ delete mode 100644 arch/x86/include/asm/dma-direct.h
+
+--- a/arch/x86/Kconfig
++++ b/arch/x86/Kconfig
+@@ -708,7 +708,6 @@ config X86_SUPPORTS_MEMORY_FAILURE
+ config STA2X11
+       bool "STA2X11 Companion Chip Support"
+       depends on X86_32_NON_STANDARD && PCI
+-      select ARCH_HAS_PHYS_TO_DMA
+       select SWIOTLB
+       select MFD_STA2X11
+       select GPIOLIB
+--- a/arch/x86/include/asm/device.h
++++ b/arch/x86/include/asm/device.h
+@@ -6,9 +6,6 @@ struct dev_archdata {
+ #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
+       void *iommu; /* hook for IOMMU specific extension */
+ #endif
+-#ifdef CONFIG_STA2X11
+-      bool is_sta2x11;
+-#endif
+ };
+ #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
+--- a/arch/x86/include/asm/dma-direct.h
++++ /dev/null
+@@ -1,9 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef ASM_X86_DMA_DIRECT_H
+-#define ASM_X86_DMA_DIRECT_H 1
+-
+-bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
+-dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
+-phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
+-
+-#endif /* ASM_X86_DMA_DIRECT_H */
+--- a/arch/x86/pci/sta2x11-fixup.c
++++ b/arch/x86/pci/sta2x11-fixup.c
+@@ -30,7 +30,6 @@ struct sta2x11_ahb_regs { /* saved durin
+ };
+ struct sta2x11_mapping {
+-      u32 amba_base;
+       int is_suspended;
+       struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS];
+ };
+@@ -92,18 +91,6 @@ static int sta2x11_pdev_to_ep(struct pci
+       return pdev->bus->number - instance->bus0;
+ }
+-static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
+-{
+-      struct sta2x11_instance *instance;
+-      int ep;
+-
+-      instance = sta2x11_pdev_to_instance(pdev);
+-      if (!instance)
+-              return NULL;
+-      ep = sta2x11_pdev_to_ep(pdev);
+-      return instance->map + ep;
+-}
+-
+ /* This is exported, as some devices need to access the MFD registers */
+ struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
+ {
+@@ -111,39 +98,6 @@ struct sta2x11_instance *sta2x11_get_ins
+ }
+ EXPORT_SYMBOL(sta2x11_get_instance);
+-
+-/**
+- * p2a - Translate physical address to STA2x11 AMBA address,
+- *       used for DMA transfers to STA2x11
+- * @p: Physical address
+- * @pdev: PCI device (must be hosted within the connext)
+- */
+-static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev)
+-{
+-      struct sta2x11_mapping *map;
+-      dma_addr_t a;
+-
+-      map = sta2x11_pdev_to_mapping(pdev);
+-      a = p + map->amba_base;
+-      return a;
+-}
+-
+-/**
+- * a2p - Translate STA2x11 AMBA address to physical address
+- *       used for DMA transfers from STA2x11
+- * @a: STA2x11 AMBA address
+- * @pdev: PCI device (must be hosted within the connext)
+- */
+-static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev)
+-{
+-      struct sta2x11_mapping *map;
+-      dma_addr_t p;
+-
+-      map = sta2x11_pdev_to_mapping(pdev);
+-      p = a - map->amba_base;
+-      return p;
+-}
+-
+ /* At setup time, we use our own ops if the device is a ConneXt one */
+ static void sta2x11_setup_pdev(struct pci_dev *pdev)
+ {
+@@ -151,9 +105,6 @@ static void sta2x11_setup_pdev(struct pc
+       if (!instance) /* either a sta2x11 bridge or another ST device */
+               return;
+-      pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+-      pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
+-      pdev->dev.archdata.is_sta2x11 = true;
+       /* We must enable all devices as master, for audio DMA to work */
+       pci_set_master(pdev);
+@@ -161,61 +112,6 @@ static void sta2x11_setup_pdev(struct pc
+ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev);
+ /*
+- * The following three functions are exported (used in swiotlb: FIXME)
+- */
+-/**
+- * dma_capable - Check if device can manage DMA transfers (FIXME: kill it)
+- * @dev: device for a PCI device
+- * @addr: DMA address
+- * @size: DMA size
+- */
+-bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+-      struct sta2x11_mapping *map;
+-
+-      if (!dev->archdata.is_sta2x11) {
+-              if (!dev->dma_mask)
+-                      return false;
+-              return addr + size - 1 <= *dev->dma_mask;
+-      }
+-
+-      map = sta2x11_pdev_to_mapping(to_pci_dev(dev));
+-
+-      if (!map || (addr < map->amba_base))
+-              return false;
+-      if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) {
+-              return false;
+-      }
+-
+-      return true;
+-}
+-
+-/**
+- * __phys_to_dma - Return the DMA AMBA address used for this STA2x11 device
+- * @dev: device for a PCI device
+- * @paddr: Physical address
+- */
+-dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
+-{
+-      if (!dev->archdata.is_sta2x11)
+-              return paddr;
+-      return p2a(paddr, to_pci_dev(dev));
+-}
+-
+-/**
+- * dma_to_phys - Return the physical address used for this STA2x11 DMA address
+- * @dev: device for a PCI device
+- * @daddr: STA2x11 AMBA DMA address
+- */
+-phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr)
+-{
+-      if (!dev->archdata.is_sta2x11)
+-              return daddr;
+-      return a2p(daddr, to_pci_dev(dev));
+-}
+-
+-
+-/*
+  * At boot we must set up the mappings for the pcie-to-amba bridge.
+  * It involves device access, and the same happens at suspend/resume time
+  */
+@@ -234,12 +130,22 @@ phys_addr_t __dma_to_phys(struct device
+ /* At probe time, enable mapping for each endpoint, using the pdev */
+ static void sta2x11_map_ep(struct pci_dev *pdev)
+ {
+-      struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
++      struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
++      struct device *dev = &pdev->dev;
++      u32 amba_base, max_amba_addr;
+       int i;
+-      if (!map)
++      if (!instance)
+               return;
+-      pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base);
++
++      pci_read_config_dword(pdev, AHB_BASE(0), &amba_base);
++      max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1;
++
++      dev->dma_pfn_offset = PFN_DOWN(-amba_base);
++
++      dev->bus_dma_mask = max_amba_addr;
++      pci_set_consistent_dma_mask(pdev, max_amba_addr);
++      pci_set_dma_mask(pdev, max_amba_addr);
+       /* Configure AHB mapping */
+       pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0);
+@@ -253,13 +159,24 @@ static void sta2x11_map_ep(struct pci_de
+       dev_info(&pdev->dev,
+                "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n",
+-               sta2x11_pdev_to_ep(pdev),  map->amba_base,
+-               map->amba_base + STA2X11_AMBA_SIZE - 1);
++               sta2x11_pdev_to_ep(pdev), amba_base, max_amba_addr);
+ }
+ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep);
+ #ifdef CONFIG_PM /* Some register values must be saved and restored */
++static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
++{
++      struct sta2x11_instance *instance;
++      int ep;
++
++      instance = sta2x11_pdev_to_instance(pdev);
++      if (!instance)
++              return NULL;
++      ep = sta2x11_pdev_to_ep(pdev);
++      return instance->map + ep;
++}
++
+ static void suspend_mapping(struct pci_dev *pdev)
+ {
+       struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0452-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
new file mode 100644 (file)
index 0000000..0d74bc5
--- /dev/null
@@ -0,0 +1,427 @@
+From 125a18144253e3a3f4bcad24484ee9b590dc47c6 Mon Sep 17 00:00:00 2001
+From: Rob Herring <robh@kernel.org>
+Date: Wed, 30 Oct 2019 17:30:57 -0500
+Subject: [PATCH] PCI: of: Add inbound resource parsing to helpers
+
+Extend devm_of_pci_get_host_bridge_resources() and
+pci_parse_request_of_pci_ranges() helpers to also parse the inbound
+addresses from DT 'dma-ranges' and populate a resource list with the
+translated addresses. This will help ensure 'dma-ranges' is always
+parsed in a consistent way.
+
+Tested-by: Srinath Mannam <srinath.mannam@broadcom.com>
+Tested-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> # for AArdvark
+Signed-off-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Srinath Mannam <srinath.mannam@broadcom.com>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+Cc: Jingoo Han <jingoohan1@gmail.com>
+Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
+Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Cc: Bjorn Helgaas <bhelgaas@google.com>
+Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: Linus Walleij <linus.walleij@linaro.org>
+Cc: Toan Le <toan@os.amperecomputing.com>
+Cc: Ley Foon Tan <lftan@altera.com>
+Cc: Tom Joseph <tjoseph@cadence.com>
+Cc: Ray Jui <rjui@broadcom.com>
+Cc: Scott Branden <sbranden@broadcom.com>
+Cc: bcm-kernel-feedback-list@broadcom.com
+Cc: Ryder Lee <ryder.lee@mediatek.com>
+Cc: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
+Cc: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
+Cc: Simon Horman <horms@verge.net.au>
+Cc: Shawn Lin <shawn.lin@rock-chips.com>
+Cc: Heiko Stuebner <heiko@sntech.de>
+Cc: Michal Simek <michal.simek@xilinx.com>
+Cc: rfi@lists.rocketboards.org
+Cc: linux-mediatek@lists.infradead.org
+Cc: linux-renesas-soc@vger.kernel.org
+Cc: linux-rockchip@lists.infradead.org
+(cherry picked from commit 331f63457165a30c708280de2c77f1742c6351dc)
+---
+ .../pci/controller/dwc/pcie-designware-host.c |  8 +--
+ drivers/pci/controller/pci-aardvark.c         |  3 +-
+ drivers/pci/controller/pci-ftpci100.c         |  4 +-
+ drivers/pci/controller/pci-host-common.c      |  2 +-
+ drivers/pci/controller/pci-v3-semi.c          |  8 +--
+ drivers/pci/controller/pci-versatile.c        |  3 +-
+ drivers/pci/controller/pci-xgene.c            |  4 +-
+ drivers/pci/controller/pcie-altera.c          |  5 +-
+ drivers/pci/controller/pcie-cadence-host.c    |  2 +-
+ drivers/pci/controller/pcie-iproc-platform.c  |  4 +-
+ drivers/pci/controller/pcie-mediatek.c        |  4 +-
+ drivers/pci/controller/pcie-mobiveil.c        |  4 +-
+ drivers/pci/controller/pcie-rcar.c            |  3 +-
+ drivers/pci/controller/pcie-rockchip-host.c   |  4 +-
+ drivers/pci/controller/pcie-xilinx-nwl.c      |  4 +-
+ drivers/pci/controller/pcie-xilinx.c          |  4 +-
+ drivers/pci/of.c                              | 61 ++++++++++++++++---
+ drivers/pci/pci.h                             |  8 ++-
+ include/linux/pci.h                           |  9 ++-
+ 19 files changed, 96 insertions(+), 48 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pcie-designware-host.c
++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
+@@ -343,12 +343,8 @@ int dw_pcie_host_init(struct pcie_port *
+       if (!bridge)
+               return -ENOMEM;
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+-                                      &bridge->windows, &pp->io_base);
+-      if (ret)
+-              return ret;
+-
+-      ret = devm_request_pci_bus_resources(dev, &bridge->windows);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/pci-aardvark.c
++++ b/drivers/pci/controller/pci-aardvark.c
+@@ -1023,7 +1023,8 @@ static int advk_pcie_probe(struct platfo
+               return ret;
+       }
+-      ret = advk_pcie_parse_request_of_pci_ranges(pcie);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, &bus);
+       if (ret) {
+               dev_err(dev, "Failed to parse resources\n");
+               return ret;
+--- a/drivers/pci/controller/pci-ftpci100.c
++++ b/drivers/pci/controller/pci-ftpci100.c
+@@ -480,8 +480,8 @@ static int faraday_pci_probe(struct plat
+       if (IS_ERR(p->base))
+               return PTR_ERR(p->base);
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+-                                                  &res, &io_base);
++      ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
++                                            &host->dma_ranges, NULL);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/pci-host-common.c
++++ b/drivers/pci/controller/pci-host-common.c
+@@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci
+       struct pci_config_window *cfg;
+       /* Parse our PCI ranges and request their resources */
+-      err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
++      err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
+       if (err)
+               return ERR_PTR(err);
+--- a/drivers/pci/controller/pci-v3-semi.c
++++ b/drivers/pci/controller/pci-v3-semi.c
+@@ -793,12 +793,8 @@ static int v3_pci_probe(struct platform_
+       if (IS_ERR(v3->config_base))
+               return PTR_ERR(v3->config_base);
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+-                                                  &io_base);
+-      if (ret)
+-              return ret;
+-
+-      ret = devm_request_pci_bus_resources(dev, &res);
++      ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
++                                            &host->dma_ranges, NULL);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/pci-versatile.c
++++ b/drivers/pci/controller/pci-versatile.c
+@@ -141,7 +141,8 @@ static int versatile_pci_probe(struct pl
+       if (IS_ERR(versatile_cfg_base[1]))
+               return PTR_ERR(versatile_cfg_base[1]);
+-      ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            NULL, NULL);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/pci-xgene.c
++++ b/drivers/pci/controller/pci-xgene.c
+@@ -634,8 +634,8 @@ static int xgene_pcie_probe(struct platf
+       if (ret)
+               return ret;
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+-                                                  &iobase);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (ret)
+               return ret;
+--- a/drivers/pci/controller/pcie-altera.c
++++ b/drivers/pci/controller/pcie-altera.c
+@@ -833,9 +833,8 @@ static int altera_pcie_probe(struct plat
+               return ret;
+       }
+-      INIT_LIST_HEAD(&pcie->resources);
+-
+-      ret = altera_pcie_parse_request_of_pci_ranges(pcie);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (ret) {
+               dev_err(dev, "Failed add resources\n");
+               return ret;
+--- a/drivers/pci/controller/pcie-cadence-host.c
++++ b/drivers/pci/controller/pcie-cadence-host.c
+@@ -211,7 +211,7 @@ static int cdns_pcie_host_init(struct de
+       int err;
+       /* Parse our PCI ranges and request their resources */
+-      err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
++      err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
+       if (err)
+               return err;
+--- a/drivers/pci/controller/pcie-iproc-platform.c
++++ b/drivers/pci/controller/pcie-iproc-platform.c
+@@ -97,8 +97,8 @@ static int iproc_pcie_pltfm_probe(struct
+       if (IS_ERR(pcie->phy))
+               return PTR_ERR(pcie->phy);
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources,
+-                                                  &iobase);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (ret) {
+               dev_err(dev, "unable to get PCI host bridge resources\n");
+               return ret;
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -1027,8 +1027,8 @@ static int mtk_pcie_setup(struct mtk_pci
+       resource_size_t io_base;
+       int err;
+-      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+-                                                  windows, &io_base);
++      err = pci_parse_request_of_pci_ranges(dev, windows,
++                                            &host->dma_ranges, &bus);
+       if (err)
+               return err;
+--- a/drivers/pci/controller/pcie-mobiveil.c
++++ b/drivers/pci/controller/pcie-mobiveil.c
+@@ -883,8 +883,8 @@ static int mobiveil_pcie_probe(struct pl
+       INIT_LIST_HEAD(&pcie->resources);
+       /* parse the host bridge base addresses from the device tree file */
+-      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+-                                                  &pcie->resources, &iobase);
++      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (ret) {
+               dev_err(dev, "Getting bridge resources failed\n");
+               return ret;
+--- a/drivers/pci/controller/pcie-rcar.c
++++ b/drivers/pci/controller/pcie-rcar.c
+@@ -1143,7 +1143,8 @@ static int rcar_pcie_probe(struct platfo
+       pcie->dev = dev;
+       platform_set_drvdata(pdev, pcie);
+-      err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL);
++      err = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
++                                            &bridge->dma_ranges, NULL);
+       if (err)
+               goto err_free_bridge;
+--- a/drivers/pci/controller/pcie-rockchip-host.c
++++ b/drivers/pci/controller/pcie-rockchip-host.c
+@@ -995,8 +995,8 @@ static int rockchip_pcie_probe(struct pl
+       if (err < 0)
+               goto err_deinit_port;
+-      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
+-                                                  &res, &io_base);
++      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, &bus_res);
+       if (err)
+               goto err_remove_irq_domain;
+--- a/drivers/pci/controller/pcie-xilinx-nwl.c
++++ b/drivers/pci/controller/pcie-xilinx-nwl.c
+@@ -845,8 +845,8 @@ static int nwl_pcie_probe(struct platfor
+               return err;
+       }
+-      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+-                                                  &iobase);
++      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (err) {
+               dev_err(dev, "Getting bridge resources failed\n");
+               return err;
+--- a/drivers/pci/controller/pcie-xilinx.c
++++ b/drivers/pci/controller/pcie-xilinx.c
+@@ -647,8 +647,8 @@ static int xilinx_pcie_probe(struct plat
+               return err;
+       }
+-      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
+-                                                  &iobase);
++      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
+       if (err) {
+               dev_err(dev, "Getting bridge resources failed\n");
+               return err;
+--- a/drivers/pci/of.c
++++ b/drivers/pci/of.c
+@@ -257,14 +257,16 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_onl
+  */
+ int devm_of_pci_get_host_bridge_resources(struct device *dev,
+                       unsigned char busno, unsigned char bus_max,
+-                      struct list_head *resources, resource_size_t *io_base)
++                      struct list_head *resources,
++                      struct list_head *ib_resources,
++                      resource_size_t *io_base)
+ {
+       struct device_node *dev_node = dev->of_node;
+       struct resource *res, tmp_res;
+       struct resource *bus_range;
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+-      char range_type[4];
++      const char *range_type;
+       int err;
+       if (io_base)
+@@ -298,12 +300,12 @@ int devm_of_pci_get_host_bridge_resource
+       for_each_of_pci_range(&parser, &range) {
+               /* Read next ranges element */
+               if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+-                      snprintf(range_type, 4, " IO");
++                      range_type = "IO";
+               else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+-                      snprintf(range_type, 4, "MEM");
++                      range_type = "MEM";
+               else
+-                      snprintf(range_type, 4, "err");
+-              dev_info(dev, "  %s %#010llx..%#010llx -> %#010llx\n",
++                      range_type = "err";
++              dev_info(dev, "  %6s %#012llx..%#012llx -> %#012llx\n",
+                        range_type, range.cpu_addr,
+                        range.cpu_addr + range.size - 1, range.pci_addr);
+@@ -340,6 +342,48 @@ int devm_of_pci_get_host_bridge_resource
+               pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+       }
++      /* Check for dma-ranges property */
++      if (!ib_resources)
++              return 0;
++      err = of_pci_dma_range_parser_init(&parser, dev_node);
++      if (err)
++              return 0;
++
++      dev_dbg(dev, "Parsing dma-ranges property...\n");
++      for_each_of_pci_range(&parser, &range) {
++              struct resource_entry *entry;
++              /*
++               * If we failed translation or got a zero-sized region
++               * then skip this range
++               */
++              if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) ||
++                  range.cpu_addr == OF_BAD_ADDR || range.size == 0)
++                      continue;
++
++              dev_info(dev, "  %6s %#012llx..%#012llx -> %#012llx\n",
++                       "IB MEM", range.cpu_addr,
++                       range.cpu_addr + range.size - 1, range.pci_addr);
++
++
++              err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
++              if (err)
++                      continue;
++
++              res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
++              if (!res) {
++                      err = -ENOMEM;
++                      goto failed;
++              }
++
++              /* Keep the resource list sorted */
++              resource_list_for_each_entry(entry, ib_resources)
++                      if (entry->res->start > res->start)
++                              break;
++
++              pci_add_resource_offset(&entry->node, res,
++                                      res->start - range.pci_addr);
++      }
++
+       return 0;
+ failed:
+@@ -482,6 +526,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_and_map_p
+ int pci_parse_request_of_pci_ranges(struct device *dev,
+                                   struct list_head *resources,
++                                  struct list_head *ib_resources,
+                                   struct resource **bus_range)
+ {
+       int err, res_valid = 0;
+@@ -489,8 +534,10 @@ int pci_parse_request_of_pci_ranges(stru
+       struct resource_entry *win, *tmp;
+       INIT_LIST_HEAD(resources);
++      if (ib_resources)
++              INIT_LIST_HEAD(ib_resources);
+       err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources,
+-                                                  &iobase);
++                                                  ib_resources, &iobase);
+       if (err)
+               return err;
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -636,11 +636,15 @@ static inline void pci_release_bus_of_no
+ #if defined(CONFIG_OF_ADDRESS)
+ int devm_of_pci_get_host_bridge_resources(struct device *dev,
+                       unsigned char busno, unsigned char bus_max,
+-                      struct list_head *resources, resource_size_t *io_base);
++                      struct list_head *resources,
++                      struct list_head *ib_resources,
++                      resource_size_t *io_base);
+ #else
+ static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
+                       unsigned char busno, unsigned char bus_max,
+-                      struct list_head *resources, resource_size_t *io_base)
++                      struct list_head *resources,
++                      struct list_head *ib_resources,
++                      resource_size_t *io_base)
+ {
+       return -EINVAL;
+ }
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -2278,6 +2278,7 @@ struct irq_domain;
+ struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus);
+ int pci_parse_request_of_pci_ranges(struct device *dev,
+                                   struct list_head *resources,
++                                  struct list_head *ib_resources,
+                                   struct resource **bus_range);
+ /* Arch may override this (weak) */
+@@ -2286,9 +2287,11 @@ struct device_node *pcibios_get_phb_of_n
+ #else /* CONFIG_OF */
+ static inline struct irq_domain *
+ pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
+-static inline int pci_parse_request_of_pci_ranges(struct device *dev,
+-                                                struct list_head *resources,
+-                                                struct resource **bus_range)
++static inline int
++pci_parse_request_of_pci_ranges(struct device *dev,
++                              struct list_head *resources,
++                              struct list_head *ib_resources,
++                              struct resource **bus_range)
+ {
+       return -EINVAL;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0452-x86-PCI-sta2x11-use-default-DMA-address-translation.patch b/target/linux/bcm27xx/patches-5.4/950-0452-x86-PCI-sta2x11-use-default-DMA-address-translation.patch
deleted file mode 100644 (file)
index 51fd4be..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-From 97a48106d1698038720495fdd49c491b283bf110 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Thu, 7 Nov 2019 16:06:45 +0100
-Subject: [PATCH] x86/PCI: sta2x11: use default DMA address translation
-
-commit e380a0394c36a3a878c858418d5dd7f5f195b6fc upstream.
-
-The devices found behind this PCIe chip have unusual DMA mapping
-constraints as there is an AMBA interconnect placed in between them and
-the different PCI endpoints. The offset between physical memory
-addresses and AMBA's view is provided by reading a PCI config register,
-which is saved and used whenever DMA mapping is needed.
-
-It turns out that this DMA setup can be represented by properly setting
-'dma_pfn_offset', 'dma_bus_mask' and 'dma_mask' during the PCI device
-enable fixup. And ultimately allows us to get rid of this device's
-custom DMA functions.
-
-Aside from the code deletion and DMA setup, sta2x11_pdev_to_mapping() is
-moved to avoid warnings whenever CONFIG_PM is not enabled.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
----
- arch/x86/Kconfig                  |   1 -
- arch/x86/include/asm/device.h     |   3 -
- arch/x86/include/asm/dma-direct.h |   9 --
- arch/x86/pci/sta2x11-fixup.c      | 135 ++++++------------------------
- 4 files changed, 26 insertions(+), 122 deletions(-)
- delete mode 100644 arch/x86/include/asm/dma-direct.h
-
---- a/arch/x86/Kconfig
-+++ b/arch/x86/Kconfig
-@@ -708,7 +708,6 @@ config X86_SUPPORTS_MEMORY_FAILURE
- config STA2X11
-       bool "STA2X11 Companion Chip Support"
-       depends on X86_32_NON_STANDARD && PCI
--      select ARCH_HAS_PHYS_TO_DMA
-       select SWIOTLB
-       select MFD_STA2X11
-       select GPIOLIB
---- a/arch/x86/include/asm/device.h
-+++ b/arch/x86/include/asm/device.h
-@@ -6,9 +6,6 @@ struct dev_archdata {
- #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU)
-       void *iommu; /* hook for IOMMU specific extension */
- #endif
--#ifdef CONFIG_STA2X11
--      bool is_sta2x11;
--#endif
- };
- #if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
---- a/arch/x86/include/asm/dma-direct.h
-+++ /dev/null
-@@ -1,9 +0,0 @@
--/* SPDX-License-Identifier: GPL-2.0 */
--#ifndef ASM_X86_DMA_DIRECT_H
--#define ASM_X86_DMA_DIRECT_H 1
--
--bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
--dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
--phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
--
--#endif /* ASM_X86_DMA_DIRECT_H */
---- a/arch/x86/pci/sta2x11-fixup.c
-+++ b/arch/x86/pci/sta2x11-fixup.c
-@@ -30,7 +30,6 @@ struct sta2x11_ahb_regs { /* saved durin
- };
- struct sta2x11_mapping {
--      u32 amba_base;
-       int is_suspended;
-       struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS];
- };
-@@ -92,18 +91,6 @@ static int sta2x11_pdev_to_ep(struct pci
-       return pdev->bus->number - instance->bus0;
- }
--static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
--{
--      struct sta2x11_instance *instance;
--      int ep;
--
--      instance = sta2x11_pdev_to_instance(pdev);
--      if (!instance)
--              return NULL;
--      ep = sta2x11_pdev_to_ep(pdev);
--      return instance->map + ep;
--}
--
- /* This is exported, as some devices need to access the MFD registers */
- struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
- {
-@@ -111,39 +98,6 @@ struct sta2x11_instance *sta2x11_get_ins
- }
- EXPORT_SYMBOL(sta2x11_get_instance);
--
--/**
-- * p2a - Translate physical address to STA2x11 AMBA address,
-- *       used for DMA transfers to STA2x11
-- * @p: Physical address
-- * @pdev: PCI device (must be hosted within the connext)
-- */
--static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev)
--{
--      struct sta2x11_mapping *map;
--      dma_addr_t a;
--
--      map = sta2x11_pdev_to_mapping(pdev);
--      a = p + map->amba_base;
--      return a;
--}
--
--/**
-- * a2p - Translate STA2x11 AMBA address to physical address
-- *       used for DMA transfers from STA2x11
-- * @a: STA2x11 AMBA address
-- * @pdev: PCI device (must be hosted within the connext)
-- */
--static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev)
--{
--      struct sta2x11_mapping *map;
--      dma_addr_t p;
--
--      map = sta2x11_pdev_to_mapping(pdev);
--      p = a - map->amba_base;
--      return p;
--}
--
- /* At setup time, we use our own ops if the device is a ConneXt one */
- static void sta2x11_setup_pdev(struct pci_dev *pdev)
- {
-@@ -151,9 +105,6 @@ static void sta2x11_setup_pdev(struct pc
-       if (!instance) /* either a sta2x11 bridge or another ST device */
-               return;
--      pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
--      pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1);
--      pdev->dev.archdata.is_sta2x11 = true;
-       /* We must enable all devices as master, for audio DMA to work */
-       pci_set_master(pdev);
-@@ -161,61 +112,6 @@ static void sta2x11_setup_pdev(struct pc
- DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev);
- /*
-- * The following three functions are exported (used in swiotlb: FIXME)
-- */
--/**
-- * dma_capable - Check if device can manage DMA transfers (FIXME: kill it)
-- * @dev: device for a PCI device
-- * @addr: DMA address
-- * @size: DMA size
-- */
--bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
--      struct sta2x11_mapping *map;
--
--      if (!dev->archdata.is_sta2x11) {
--              if (!dev->dma_mask)
--                      return false;
--              return addr + size - 1 <= *dev->dma_mask;
--      }
--
--      map = sta2x11_pdev_to_mapping(to_pci_dev(dev));
--
--      if (!map || (addr < map->amba_base))
--              return false;
--      if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) {
--              return false;
--      }
--
--      return true;
--}
--
--/**
-- * __phys_to_dma - Return the DMA AMBA address used for this STA2x11 device
-- * @dev: device for a PCI device
-- * @paddr: Physical address
-- */
--dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
--{
--      if (!dev->archdata.is_sta2x11)
--              return paddr;
--      return p2a(paddr, to_pci_dev(dev));
--}
--
--/**
-- * dma_to_phys - Return the physical address used for this STA2x11 DMA address
-- * @dev: device for a PCI device
-- * @daddr: STA2x11 AMBA DMA address
-- */
--phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr)
--{
--      if (!dev->archdata.is_sta2x11)
--              return daddr;
--      return a2p(daddr, to_pci_dev(dev));
--}
--
--
--/*
-  * At boot we must set up the mappings for the pcie-to-amba bridge.
-  * It involves device access, and the same happens at suspend/resume time
-  */
-@@ -234,12 +130,22 @@ phys_addr_t __dma_to_phys(struct device
- /* At probe time, enable mapping for each endpoint, using the pdev */
- static void sta2x11_map_ep(struct pci_dev *pdev)
- {
--      struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
-+      struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
-+      struct device *dev = &pdev->dev;
-+      u32 amba_base, max_amba_addr;
-       int i;
--      if (!map)
-+      if (!instance)
-               return;
--      pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base);
-+
-+      pci_read_config_dword(pdev, AHB_BASE(0), &amba_base);
-+      max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1;
-+
-+      dev->dma_pfn_offset = PFN_DOWN(-amba_base);
-+
-+      dev->bus_dma_mask = max_amba_addr;
-+      pci_set_consistent_dma_mask(pdev, max_amba_addr);
-+      pci_set_dma_mask(pdev, max_amba_addr);
-       /* Configure AHB mapping */
-       pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0);
-@@ -253,13 +159,24 @@ static void sta2x11_map_ep(struct pci_de
-       dev_info(&pdev->dev,
-                "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n",
--               sta2x11_pdev_to_ep(pdev),  map->amba_base,
--               map->amba_base + STA2X11_AMBA_SIZE - 1);
-+               sta2x11_pdev_to_ep(pdev), amba_base, max_amba_addr);
- }
- DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep);
- #ifdef CONFIG_PM /* Some register values must be saved and restored */
-+static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev)
-+{
-+      struct sta2x11_instance *instance;
-+      int ep;
-+
-+      instance = sta2x11_pdev_to_instance(pdev);
-+      if (!instance)
-+              return NULL;
-+      ep = sta2x11_pdev_to_ep(pdev);
-+      return instance->map + ep;
-+}
-+
- static void suspend_mapping(struct pci_dev *pdev)
- {
-       struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0453-PCI-of-Add-inbound-resource-parsing-to-helpers.patch b/target/linux/bcm27xx/patches-5.4/950-0453-PCI-of-Add-inbound-resource-parsing-to-helpers.patch
deleted file mode 100644 (file)
index 0d74bc5..0000000
+++ /dev/null
@@ -1,427 +0,0 @@
-From 125a18144253e3a3f4bcad24484ee9b590dc47c6 Mon Sep 17 00:00:00 2001
-From: Rob Herring <robh@kernel.org>
-Date: Wed, 30 Oct 2019 17:30:57 -0500
-Subject: [PATCH] PCI: of: Add inbound resource parsing to helpers
-
-Extend devm_of_pci_get_host_bridge_resources() and
-pci_parse_request_of_pci_ranges() helpers to also parse the inbound
-addresses from DT 'dma-ranges' and populate a resource list with the
-translated addresses. This will help ensure 'dma-ranges' is always
-parsed in a consistent way.
-
-Tested-by: Srinath Mannam <srinath.mannam@broadcom.com>
-Tested-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> # for AArdvark
-Signed-off-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Srinath Mannam <srinath.mannam@broadcom.com>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
-Acked-by: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
-Cc: Jingoo Han <jingoohan1@gmail.com>
-Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
-Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Cc: Bjorn Helgaas <bhelgaas@google.com>
-Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
-Cc: Will Deacon <will@kernel.org>
-Cc: Linus Walleij <linus.walleij@linaro.org>
-Cc: Toan Le <toan@os.amperecomputing.com>
-Cc: Ley Foon Tan <lftan@altera.com>
-Cc: Tom Joseph <tjoseph@cadence.com>
-Cc: Ray Jui <rjui@broadcom.com>
-Cc: Scott Branden <sbranden@broadcom.com>
-Cc: bcm-kernel-feedback-list@broadcom.com
-Cc: Ryder Lee <ryder.lee@mediatek.com>
-Cc: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
-Cc: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
-Cc: Simon Horman <horms@verge.net.au>
-Cc: Shawn Lin <shawn.lin@rock-chips.com>
-Cc: Heiko Stuebner <heiko@sntech.de>
-Cc: Michal Simek <michal.simek@xilinx.com>
-Cc: rfi@lists.rocketboards.org
-Cc: linux-mediatek@lists.infradead.org
-Cc: linux-renesas-soc@vger.kernel.org
-Cc: linux-rockchip@lists.infradead.org
-(cherry picked from commit 331f63457165a30c708280de2c77f1742c6351dc)
----
- .../pci/controller/dwc/pcie-designware-host.c |  8 +--
- drivers/pci/controller/pci-aardvark.c         |  3 +-
- drivers/pci/controller/pci-ftpci100.c         |  4 +-
- drivers/pci/controller/pci-host-common.c      |  2 +-
- drivers/pci/controller/pci-v3-semi.c          |  8 +--
- drivers/pci/controller/pci-versatile.c        |  3 +-
- drivers/pci/controller/pci-xgene.c            |  4 +-
- drivers/pci/controller/pcie-altera.c          |  5 +-
- drivers/pci/controller/pcie-cadence-host.c    |  2 +-
- drivers/pci/controller/pcie-iproc-platform.c  |  4 +-
- drivers/pci/controller/pcie-mediatek.c        |  4 +-
- drivers/pci/controller/pcie-mobiveil.c        |  4 +-
- drivers/pci/controller/pcie-rcar.c            |  3 +-
- drivers/pci/controller/pcie-rockchip-host.c   |  4 +-
- drivers/pci/controller/pcie-xilinx-nwl.c      |  4 +-
- drivers/pci/controller/pcie-xilinx.c          |  4 +-
- drivers/pci/of.c                              | 61 ++++++++++++++++---
- drivers/pci/pci.h                             |  8 ++-
- include/linux/pci.h                           |  9 ++-
- 19 files changed, 96 insertions(+), 48 deletions(-)
-
---- a/drivers/pci/controller/dwc/pcie-designware-host.c
-+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
-@@ -343,12 +343,8 @@ int dw_pcie_host_init(struct pcie_port *
-       if (!bridge)
-               return -ENOMEM;
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
--                                      &bridge->windows, &pp->io_base);
--      if (ret)
--              return ret;
--
--      ret = devm_request_pci_bus_resources(dev, &bridge->windows);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (ret)
-               return ret;
---- a/drivers/pci/controller/pci-aardvark.c
-+++ b/drivers/pci/controller/pci-aardvark.c
-@@ -1023,7 +1023,8 @@ static int advk_pcie_probe(struct platfo
-               return ret;
-       }
--      ret = advk_pcie_parse_request_of_pci_ranges(pcie);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, &bus);
-       if (ret) {
-               dev_err(dev, "Failed to parse resources\n");
-               return ret;
---- a/drivers/pci/controller/pci-ftpci100.c
-+++ b/drivers/pci/controller/pci-ftpci100.c
-@@ -480,8 +480,8 @@ static int faraday_pci_probe(struct plat
-       if (IS_ERR(p->base))
-               return PTR_ERR(p->base);
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
--                                                  &res, &io_base);
-+      ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
-+                                            &host->dma_ranges, NULL);
-       if (ret)
-               return ret;
---- a/drivers/pci/controller/pci-host-common.c
-+++ b/drivers/pci/controller/pci-host-common.c
-@@ -27,7 +27,7 @@ static struct pci_config_window *gen_pci
-       struct pci_config_window *cfg;
-       /* Parse our PCI ranges and request their resources */
--      err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
-+      err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
-       if (err)
-               return ERR_PTR(err);
---- a/drivers/pci/controller/pci-v3-semi.c
-+++ b/drivers/pci/controller/pci-v3-semi.c
-@@ -793,12 +793,8 @@ static int v3_pci_probe(struct platform_
-       if (IS_ERR(v3->config_base))
-               return PTR_ERR(v3->config_base);
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
--                                                  &io_base);
--      if (ret)
--              return ret;
--
--      ret = devm_request_pci_bus_resources(dev, &res);
-+      ret = pci_parse_request_of_pci_ranges(dev, &host->windows,
-+                                            &host->dma_ranges, NULL);
-       if (ret)
-               return ret;
---- a/drivers/pci/controller/pci-versatile.c
-+++ b/drivers/pci/controller/pci-versatile.c
-@@ -141,7 +141,8 @@ static int versatile_pci_probe(struct pl
-       if (IS_ERR(versatile_cfg_base[1]))
-               return PTR_ERR(versatile_cfg_base[1]);
--      ret = versatile_pci_parse_request_of_pci_ranges(dev, &pci_res);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            NULL, NULL);
-       if (ret)
-               return ret;
---- a/drivers/pci/controller/pci-xgene.c
-+++ b/drivers/pci/controller/pci-xgene.c
-@@ -634,8 +634,8 @@ static int xgene_pcie_probe(struct platf
-       if (ret)
-               return ret;
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
--                                                  &iobase);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (ret)
-               return ret;
---- a/drivers/pci/controller/pcie-altera.c
-+++ b/drivers/pci/controller/pcie-altera.c
-@@ -833,9 +833,8 @@ static int altera_pcie_probe(struct plat
-               return ret;
-       }
--      INIT_LIST_HEAD(&pcie->resources);
--
--      ret = altera_pcie_parse_request_of_pci_ranges(pcie);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (ret) {
-               dev_err(dev, "Failed add resources\n");
-               return ret;
---- a/drivers/pci/controller/pcie-cadence-host.c
-+++ b/drivers/pci/controller/pcie-cadence-host.c
-@@ -211,7 +211,7 @@ static int cdns_pcie_host_init(struct de
-       int err;
-       /* Parse our PCI ranges and request their resources */
--      err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
-+      err = pci_parse_request_of_pci_ranges(dev, resources, NULL, &bus_range);
-       if (err)
-               return err;
---- a/drivers/pci/controller/pcie-iproc-platform.c
-+++ b/drivers/pci/controller/pcie-iproc-platform.c
-@@ -97,8 +97,8 @@ static int iproc_pcie_pltfm_probe(struct
-       if (IS_ERR(pcie->phy))
-               return PTR_ERR(pcie->phy);
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &resources,
--                                                  &iobase);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (ret) {
-               dev_err(dev, "unable to get PCI host bridge resources\n");
-               return ret;
---- a/drivers/pci/controller/pcie-mediatek.c
-+++ b/drivers/pci/controller/pcie-mediatek.c
-@@ -1027,8 +1027,8 @@ static int mtk_pcie_setup(struct mtk_pci
-       resource_size_t io_base;
-       int err;
--      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
--                                                  windows, &io_base);
-+      err = pci_parse_request_of_pci_ranges(dev, windows,
-+                                            &host->dma_ranges, &bus);
-       if (err)
-               return err;
---- a/drivers/pci/controller/pcie-mobiveil.c
-+++ b/drivers/pci/controller/pcie-mobiveil.c
-@@ -883,8 +883,8 @@ static int mobiveil_pcie_probe(struct pl
-       INIT_LIST_HEAD(&pcie->resources);
-       /* parse the host bridge base addresses from the device tree file */
--      ret = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
--                                                  &pcie->resources, &iobase);
-+      ret = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (ret) {
-               dev_err(dev, "Getting bridge resources failed\n");
-               return ret;
---- a/drivers/pci/controller/pcie-rcar.c
-+++ b/drivers/pci/controller/pcie-rcar.c
-@@ -1143,7 +1143,8 @@ static int rcar_pcie_probe(struct platfo
-       pcie->dev = dev;
-       platform_set_drvdata(pdev, pcie);
--      err = pci_parse_request_of_pci_ranges(dev, &pcie->resources, NULL);
-+      err = pci_parse_request_of_pci_ranges(dev, &pcie->resources,
-+                                            &bridge->dma_ranges, NULL);
-       if (err)
-               goto err_free_bridge;
---- a/drivers/pci/controller/pcie-rockchip-host.c
-+++ b/drivers/pci/controller/pcie-rockchip-host.c
-@@ -995,8 +995,8 @@ static int rockchip_pcie_probe(struct pl
-       if (err < 0)
-               goto err_deinit_port;
--      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff,
--                                                  &res, &io_base);
-+      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, &bus_res);
-       if (err)
-               goto err_remove_irq_domain;
---- a/drivers/pci/controller/pcie-xilinx-nwl.c
-+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
-@@ -845,8 +845,8 @@ static int nwl_pcie_probe(struct platfor
-               return err;
-       }
--      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
--                                                  &iobase);
-+      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (err) {
-               dev_err(dev, "Getting bridge resources failed\n");
-               return err;
---- a/drivers/pci/controller/pcie-xilinx.c
-+++ b/drivers/pci/controller/pcie-xilinx.c
-@@ -647,8 +647,8 @@ static int xilinx_pcie_probe(struct plat
-               return err;
-       }
--      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
--                                                  &iobase);
-+      err = pci_parse_request_of_pci_ranges(dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-       if (err) {
-               dev_err(dev, "Getting bridge resources failed\n");
-               return err;
---- a/drivers/pci/of.c
-+++ b/drivers/pci/of.c
-@@ -257,14 +257,16 @@ EXPORT_SYMBOL_GPL(of_pci_check_probe_onl
-  */
- int devm_of_pci_get_host_bridge_resources(struct device *dev,
-                       unsigned char busno, unsigned char bus_max,
--                      struct list_head *resources, resource_size_t *io_base)
-+                      struct list_head *resources,
-+                      struct list_head *ib_resources,
-+                      resource_size_t *io_base)
- {
-       struct device_node *dev_node = dev->of_node;
-       struct resource *res, tmp_res;
-       struct resource *bus_range;
-       struct of_pci_range range;
-       struct of_pci_range_parser parser;
--      char range_type[4];
-+      const char *range_type;
-       int err;
-       if (io_base)
-@@ -298,12 +300,12 @@ int devm_of_pci_get_host_bridge_resource
-       for_each_of_pci_range(&parser, &range) {
-               /* Read next ranges element */
-               if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
--                      snprintf(range_type, 4, " IO");
-+                      range_type = "IO";
-               else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
--                      snprintf(range_type, 4, "MEM");
-+                      range_type = "MEM";
-               else
--                      snprintf(range_type, 4, "err");
--              dev_info(dev, "  %s %#010llx..%#010llx -> %#010llx\n",
-+                      range_type = "err";
-+              dev_info(dev, "  %6s %#012llx..%#012llx -> %#012llx\n",
-                        range_type, range.cpu_addr,
-                        range.cpu_addr + range.size - 1, range.pci_addr);
-@@ -340,6 +342,48 @@ int devm_of_pci_get_host_bridge_resource
-               pci_add_resource_offset(resources, res, res->start - range.pci_addr);
-       }
-+      /* Check for dma-ranges property */
-+      if (!ib_resources)
-+              return 0;
-+      err = of_pci_dma_range_parser_init(&parser, dev_node);
-+      if (err)
-+              return 0;
-+
-+      dev_dbg(dev, "Parsing dma-ranges property...\n");
-+      for_each_of_pci_range(&parser, &range) {
-+              struct resource_entry *entry;
-+              /*
-+               * If we failed translation or got a zero-sized region
-+               * then skip this range
-+               */
-+              if (((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM) ||
-+                  range.cpu_addr == OF_BAD_ADDR || range.size == 0)
-+                      continue;
-+
-+              dev_info(dev, "  %6s %#012llx..%#012llx -> %#012llx\n",
-+                       "IB MEM", range.cpu_addr,
-+                       range.cpu_addr + range.size - 1, range.pci_addr);
-+
-+
-+              err = of_pci_range_to_resource(&range, dev_node, &tmp_res);
-+              if (err)
-+                      continue;
-+
-+              res = devm_kmemdup(dev, &tmp_res, sizeof(tmp_res), GFP_KERNEL);
-+              if (!res) {
-+                      err = -ENOMEM;
-+                      goto failed;
-+              }
-+
-+              /* Keep the resource list sorted */
-+              resource_list_for_each_entry(entry, ib_resources)
-+                      if (entry->res->start > res->start)
-+                              break;
-+
-+              pci_add_resource_offset(&entry->node, res,
-+                                      res->start - range.pci_addr);
-+      }
-+
-       return 0;
- failed:
-@@ -482,6 +526,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_and_map_p
- int pci_parse_request_of_pci_ranges(struct device *dev,
-                                   struct list_head *resources,
-+                                  struct list_head *ib_resources,
-                                   struct resource **bus_range)
- {
-       int err, res_valid = 0;
-@@ -489,8 +534,10 @@ int pci_parse_request_of_pci_ranges(stru
-       struct resource_entry *win, *tmp;
-       INIT_LIST_HEAD(resources);
-+      if (ib_resources)
-+              INIT_LIST_HEAD(ib_resources);
-       err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, resources,
--                                                  &iobase);
-+                                                  ib_resources, &iobase);
-       if (err)
-               return err;
---- a/drivers/pci/pci.h
-+++ b/drivers/pci/pci.h
-@@ -636,11 +636,15 @@ static inline void pci_release_bus_of_no
- #if defined(CONFIG_OF_ADDRESS)
- int devm_of_pci_get_host_bridge_resources(struct device *dev,
-                       unsigned char busno, unsigned char bus_max,
--                      struct list_head *resources, resource_size_t *io_base);
-+                      struct list_head *resources,
-+                      struct list_head *ib_resources,
-+                      resource_size_t *io_base);
- #else
- static inline int devm_of_pci_get_host_bridge_resources(struct device *dev,
-                       unsigned char busno, unsigned char bus_max,
--                      struct list_head *resources, resource_size_t *io_base)
-+                      struct list_head *resources,
-+                      struct list_head *ib_resources,
-+                      resource_size_t *io_base)
- {
-       return -EINVAL;
- }
---- a/include/linux/pci.h
-+++ b/include/linux/pci.h
-@@ -2278,6 +2278,7 @@ struct irq_domain;
- struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus);
- int pci_parse_request_of_pci_ranges(struct device *dev,
-                                   struct list_head *resources,
-+                                  struct list_head *ib_resources,
-                                   struct resource **bus_range);
- /* Arch may override this (weak) */
-@@ -2286,9 +2287,11 @@ struct device_node *pcibios_get_phb_of_n
- #else /* CONFIG_OF */
- static inline struct irq_domain *
- pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
--static inline int pci_parse_request_of_pci_ranges(struct device *dev,
--                                                struct list_head *resources,
--                                                struct resource **bus_range)
-+static inline int
-+pci_parse_request_of_pci_ranges(struct device *dev,
-+                              struct list_head *resources,
-+                              struct list_head *ib_resources,
-+                              struct resource **bus_range)
- {
-       return -EINVAL;
- }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch b/target/linux/bcm27xx/patches-5.4/950-0453-dma-direct-unify-the-dma_capable-definitions.patch
new file mode 100644 (file)
index 0000000..d115f0e
--- /dev/null
@@ -0,0 +1,103 @@
+From 203e0c39b262fc1da6f976495c32ec38ea93a137 Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 12 Nov 2019 17:06:04 +0100
+Subject: [PATCH] dma-direct: unify the dma_capable definitions
+
+commit 130c1ccbf55330b55e82612a6e54eebb82c9d746 upstream.
+
+Currently each architectures that wants to override dma_to_phys and
+phys_to_dma also has to provide dma_capable.  But there isn't really
+any good reason for that.  powerpc and mips just have copies of the
+generic one minus the latests fix, and the arm one was the inspiration
+for said fix, but misses the bus_dma_mask handling.
+Make all architectures use the generic version instead.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+---
+ arch/arm/include/asm/dma-direct.h     | 19 -------------------
+ arch/mips/include/asm/dma-direct.h    |  8 --------
+ arch/powerpc/include/asm/dma-direct.h |  9 ---------
+ include/linux/dma-direct.h            |  2 +-
+ 4 files changed, 1 insertion(+), 37 deletions(-)
+
+--- a/arch/arm/include/asm/dma-direct.h
++++ b/arch/arm/include/asm/dma-direct.h
+@@ -14,23 +14,4 @@ static inline phys_addr_t __dma_to_phys(
+       return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
+ }
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+-      u64 limit, mask;
+-
+-      if (!dev->dma_mask)
+-              return 0;
+-
+-      mask = *dev->dma_mask;
+-
+-      limit = (mask + 1) & ~mask;
+-      if (limit && size > limit)
+-              return 0;
+-
+-      if ((addr | (addr + size - 1)) & ~mask)
+-              return 0;
+-
+-      return 1;
+-}
+-
+ #endif /* ASM_ARM_DMA_DIRECT_H */
+--- a/arch/mips/include/asm/dma-direct.h
++++ b/arch/mips/include/asm/dma-direct.h
+@@ -2,14 +2,6 @@
+ #ifndef _MIPS_DMA_DIRECT_H
+ #define _MIPS_DMA_DIRECT_H 1
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+-      if (!dev->dma_mask)
+-              return false;
+-
+-      return addr + size - 1 <= *dev->dma_mask;
+-}
+-
+ dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
+ phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
+--- a/arch/powerpc/include/asm/dma-direct.h
++++ b/arch/powerpc/include/asm/dma-direct.h
+@@ -2,15 +2,6 @@
+ #ifndef ASM_POWERPC_DMA_DIRECT_H
+ #define ASM_POWERPC_DMA_DIRECT_H 1
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+-      if (!dev->dma_mask)
+-              return false;
+-
+-      return addr + size - 1 <=
+-              min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+-}
+-
+ static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
+ {
+       if (!dev)
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -26,6 +26,7 @@ static inline phys_addr_t __dma_to_phys(
+       return paddr + ((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
+ }
++#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+ {
+@@ -40,7 +41,6 @@ static inline bool dma_capable(struct de
+       return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+ }
+-#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+ #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
+ bool force_dma_unencrypted(struct device *dev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch b/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
new file mode 100644 (file)
index 0000000..a98f1d3
--- /dev/null
@@ -0,0 +1,69 @@
+From a3794022e928547de664abd03b61280163c7f13a Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 12 Nov 2019 17:07:43 +0100
+Subject: [PATCH] dma-direct: avoid a forward declaration for
+ phys_to_dma
+
+Move dma_capable down a bit so that we don't need a forward declaration
+for phys_to_dma.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+(cherry picked from commit c7345159f7db6fb69ec1c3b3f8f28cd05c731be2)
+---
+ include/linux/dma-direct.h | 30 ++++++++++++++----------------
+ 1 file changed, 14 insertions(+), 16 deletions(-)
+
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -6,8 +6,6 @@
+ #include <linux/memblock.h> /* for min_low_pfn */
+ #include <linux/mem_encrypt.h>
+-static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
+-
+ extern unsigned int zone_dma_bits;
+ #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
+@@ -28,20 +26,6 @@ static inline phys_addr_t __dma_to_phys(
+ }
+ #endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+-{
+-      dma_addr_t end = addr + size - 1;
+-
+-      if (!dev->dma_mask)
+-              return false;
+-
+-      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+-          min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+-              return false;
+-
+-      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
+-}
+-
+ #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
+ bool force_dma_unencrypted(struct device *dev);
+ #else
+@@ -67,6 +51,20 @@ static inline phys_addr_t dma_to_phys(st
+       return __sme_clr(__dma_to_phys(dev, daddr));
+ }
++static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
++{
++      dma_addr_t end = addr + size - 1;
++
++      if (!dev->dma_mask)
++              return false;
++
++      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
++          min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
++              return false;
++
++      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
++}
++
+ u64 dma_direct_get_required_mask(struct device *dev);
+ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
+               gfp_t gfp, unsigned long attrs);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-unify-the-dma_capable-definitions.patch b/target/linux/bcm27xx/patches-5.4/950-0454-dma-direct-unify-the-dma_capable-definitions.patch
deleted file mode 100644 (file)
index d115f0e..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-From 203e0c39b262fc1da6f976495c32ec38ea93a137 Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 12 Nov 2019 17:06:04 +0100
-Subject: [PATCH] dma-direct: unify the dma_capable definitions
-
-commit 130c1ccbf55330b55e82612a6e54eebb82c9d746 upstream.
-
-Currently each architectures that wants to override dma_to_phys and
-phys_to_dma also has to provide dma_capable.  But there isn't really
-any good reason for that.  powerpc and mips just have copies of the
-generic one minus the latests fix, and the arm one was the inspiration
-for said fix, but misses the bus_dma_mask handling.
-Make all architectures use the generic version instead.
-
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Acked-by: Michael Ellerman <mpe@ellerman.id.au> (powerpc)
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
----
- arch/arm/include/asm/dma-direct.h     | 19 -------------------
- arch/mips/include/asm/dma-direct.h    |  8 --------
- arch/powerpc/include/asm/dma-direct.h |  9 ---------
- include/linux/dma-direct.h            |  2 +-
- 4 files changed, 1 insertion(+), 37 deletions(-)
-
---- a/arch/arm/include/asm/dma-direct.h
-+++ b/arch/arm/include/asm/dma-direct.h
-@@ -14,23 +14,4 @@ static inline phys_addr_t __dma_to_phys(
-       return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
- }
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
--      u64 limit, mask;
--
--      if (!dev->dma_mask)
--              return 0;
--
--      mask = *dev->dma_mask;
--
--      limit = (mask + 1) & ~mask;
--      if (limit && size > limit)
--              return 0;
--
--      if ((addr | (addr + size - 1)) & ~mask)
--              return 0;
--
--      return 1;
--}
--
- #endif /* ASM_ARM_DMA_DIRECT_H */
---- a/arch/mips/include/asm/dma-direct.h
-+++ b/arch/mips/include/asm/dma-direct.h
-@@ -2,14 +2,6 @@
- #ifndef _MIPS_DMA_DIRECT_H
- #define _MIPS_DMA_DIRECT_H 1
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
--      if (!dev->dma_mask)
--              return false;
--
--      return addr + size - 1 <= *dev->dma_mask;
--}
--
- dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr);
- phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr);
---- a/arch/powerpc/include/asm/dma-direct.h
-+++ b/arch/powerpc/include/asm/dma-direct.h
-@@ -2,15 +2,6 @@
- #ifndef ASM_POWERPC_DMA_DIRECT_H
- #define ASM_POWERPC_DMA_DIRECT_H 1
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
--      if (!dev->dma_mask)
--              return false;
--
--      return addr + size - 1 <=
--              min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
--}
--
- static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
- {
-       if (!dev)
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -26,6 +26,7 @@ static inline phys_addr_t __dma_to_phys(
-       return paddr + ((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
- }
-+#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
- static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
- {
-@@ -40,7 +41,6 @@ static inline bool dma_capable(struct de
-       return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
- }
--#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
- #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
- bool force_dma_unencrypted(struct device *dev);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch b/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-avoid-a-forward-declaration-for-phys_to_d.patch
deleted file mode 100644 (file)
index a98f1d3..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-From a3794022e928547de664abd03b61280163c7f13a Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 12 Nov 2019 17:07:43 +0100
-Subject: [PATCH] dma-direct: avoid a forward declaration for
- phys_to_dma
-
-Move dma_capable down a bit so that we don't need a forward declaration
-for phys_to_dma.
-
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-(cherry picked from commit c7345159f7db6fb69ec1c3b3f8f28cd05c731be2)
----
- include/linux/dma-direct.h | 30 ++++++++++++++----------------
- 1 file changed, 14 insertions(+), 16 deletions(-)
-
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -6,8 +6,6 @@
- #include <linux/memblock.h> /* for min_low_pfn */
- #include <linux/mem_encrypt.h>
--static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr);
--
- extern unsigned int zone_dma_bits;
- #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
-@@ -28,20 +26,6 @@ static inline phys_addr_t __dma_to_phys(
- }
- #endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
--{
--      dma_addr_t end = addr + size - 1;
--
--      if (!dev->dma_mask)
--              return false;
--
--      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
--          min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
--              return false;
--
--      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
--}
--
- #ifdef CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED
- bool force_dma_unencrypted(struct device *dev);
- #else
-@@ -67,6 +51,20 @@ static inline phys_addr_t dma_to_phys(st
-       return __sme_clr(__dma_to_phys(dev, daddr));
- }
-+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
-+{
-+      dma_addr_t end = addr + size - 1;
-+
-+      if (!dev->dma_mask)
-+              return false;
-+
-+      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-+          min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
-+              return false;
-+
-+      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
-+}
-+
- u64 dma_direct_get_required_mask(struct device *dev);
- void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
-               gfp_t gfp, unsigned long attrs);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0455-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
new file mode 100644 (file)
index 0000000..94a1329
--- /dev/null
@@ -0,0 +1,115 @@
+From b763f24aed409296eb76d085c279b2c40462f8a1 Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 19 Nov 2019 17:38:58 +0100
+Subject: [PATCH] dma-direct: exclude dma_direct_map_resource from the
+ min_low_pfn check
+
+commit 68a33b1794665ba8a1d1ef1d3bfcc7c587d380a6 upstream.
+
+The valid memory address check in dma_capable only makes sense when mapping
+normal memory, not when using dma_map_resource to map a device resource.
+Add a new boolean argument to dma_capable to exclude that check for the
+dma_map_resource case.
+
+Fixes: b12d66278dd6 ("dma-direct: check for overflows on 32 bit DMA addresses")
+Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
+---
+ arch/x86/kernel/amd_gart_64.c | 4 ++--
+ drivers/xen/swiotlb-xen.c     | 4 ++--
+ include/linux/dma-direct.h    | 5 +++--
+ kernel/dma/direct.c           | 4 ++--
+ kernel/dma/swiotlb.c          | 2 +-
+ 5 files changed, 10 insertions(+), 9 deletions(-)
+
+--- a/arch/x86/kernel/amd_gart_64.c
++++ b/arch/x86/kernel/amd_gart_64.c
+@@ -185,13 +185,13 @@ static void iommu_full(struct device *de
+ static inline int
+ need_iommu(struct device *dev, unsigned long addr, size_t size)
+ {
+-      return force_iommu || !dma_capable(dev, addr, size);
++      return force_iommu || !dma_capable(dev, addr, size, true);
+ }
+ static inline int
+ nonforced_iommu(struct device *dev, unsigned long addr, size_t size)
+ {
+-      return !dma_capable(dev, addr, size);
++      return !dma_capable(dev, addr, size, true);
+ }
+ /* Map a single continuous physical area into the IOMMU.
+--- a/drivers/xen/swiotlb-xen.c
++++ b/drivers/xen/swiotlb-xen.c
+@@ -375,7 +375,7 @@ static dma_addr_t xen_swiotlb_map_page(s
+        * we can safely return the device addr and not worry about bounce
+        * buffering it.
+        */
+-      if (dma_capable(dev, dev_addr, size) &&
++      if (dma_capable(dev, dev_addr, size, true) &&
+           !range_straddles_page_boundary(phys, size) &&
+               !xen_arch_need_swiotlb(dev, phys, dev_addr) &&
+               swiotlb_force != SWIOTLB_FORCE)
+@@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(s
+       /*
+        * Ensure that the address returned is DMA'ble
+        */
+-      if (unlikely(!dma_capable(dev, dev_addr, size))) {
++      if (unlikely(!dma_capable(dev, dev_addr, size, true))) {
+               swiotlb_tbl_unmap_single(dev, map, size, size, dir,
+                               attrs | DMA_ATTR_SKIP_CPU_SYNC);
+               return DMA_MAPPING_ERROR;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -51,14 +51,15 @@ static inline phys_addr_t dma_to_phys(st
+       return __sme_clr(__dma_to_phys(dev, daddr));
+ }
+-static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
++static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
++              bool is_ram)
+ {
+       dma_addr_t end = addr + size - 1;
+       if (!dev->dma_mask)
+               return false;
+-      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
++      if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+           min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+               return false;
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -325,7 +325,7 @@ static inline bool dma_direct_possible(s
+               size_t size)
+ {
+       return swiotlb_force != SWIOTLB_FORCE &&
+-              dma_capable(dev, dma_addr, size);
++              dma_capable(dev, dma_addr, size, true);
+ }
+ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
+@@ -374,7 +374,7 @@ dma_addr_t dma_direct_map_resource(struc
+ {
+       dma_addr_t dma_addr = paddr;
+-      if (unlikely(!dma_capable(dev, dma_addr, size))) {
++      if (unlikely(!dma_capable(dev, dma_addr, size, false))) {
+               report_addr(dev, dma_addr, size);
+               return DMA_MAPPING_ERROR;
+       }
+--- a/kernel/dma/swiotlb.c
++++ b/kernel/dma/swiotlb.c
+@@ -678,7 +678,7 @@ bool swiotlb_map(struct device *dev, phy
+       /* Ensure that the address returned is DMA'ble */
+       *dma_addr = __phys_to_dma(dev, *phys);
+-      if (unlikely(!dma_capable(dev, *dma_addr, size))) {
++      if (unlikely(!dma_capable(dev, *dma_addr, size, true))) {
+               swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
+                       attrs | DMA_ATTR_SKIP_CPU_SYNC);
+               return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0456-dma-direct-exclude-dma_direct_map_resource-from-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0456-dma-direct-exclude-dma_direct_map_resource-from-the-.patch
deleted file mode 100644 (file)
index 94a1329..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-From b763f24aed409296eb76d085c279b2c40462f8a1 Mon Sep 17 00:00:00 2001
-From: Christoph Hellwig <hch@lst.de>
-Date: Tue, 19 Nov 2019 17:38:58 +0100
-Subject: [PATCH] dma-direct: exclude dma_direct_map_resource from the
- min_low_pfn check
-
-commit 68a33b1794665ba8a1d1ef1d3bfcc7c587d380a6 upstream.
-
-The valid memory address check in dma_capable only makes sense when mapping
-normal memory, not when using dma_map_resource to map a device resource.
-Add a new boolean argument to dma_capable to exclude that check for the
-dma_map_resource case.
-
-Fixes: b12d66278dd6 ("dma-direct: check for overflows on 32 bit DMA addresses")
-Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
-Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
----
- arch/x86/kernel/amd_gart_64.c | 4 ++--
- drivers/xen/swiotlb-xen.c     | 4 ++--
- include/linux/dma-direct.h    | 5 +++--
- kernel/dma/direct.c           | 4 ++--
- kernel/dma/swiotlb.c          | 2 +-
- 5 files changed, 10 insertions(+), 9 deletions(-)
-
---- a/arch/x86/kernel/amd_gart_64.c
-+++ b/arch/x86/kernel/amd_gart_64.c
-@@ -185,13 +185,13 @@ static void iommu_full(struct device *de
- static inline int
- need_iommu(struct device *dev, unsigned long addr, size_t size)
- {
--      return force_iommu || !dma_capable(dev, addr, size);
-+      return force_iommu || !dma_capable(dev, addr, size, true);
- }
- static inline int
- nonforced_iommu(struct device *dev, unsigned long addr, size_t size)
- {
--      return !dma_capable(dev, addr, size);
-+      return !dma_capable(dev, addr, size, true);
- }
- /* Map a single continuous physical area into the IOMMU.
---- a/drivers/xen/swiotlb-xen.c
-+++ b/drivers/xen/swiotlb-xen.c
-@@ -375,7 +375,7 @@ static dma_addr_t xen_swiotlb_map_page(s
-        * we can safely return the device addr and not worry about bounce
-        * buffering it.
-        */
--      if (dma_capable(dev, dev_addr, size) &&
-+      if (dma_capable(dev, dev_addr, size, true) &&
-           !range_straddles_page_boundary(phys, size) &&
-               !xen_arch_need_swiotlb(dev, phys, dev_addr) &&
-               swiotlb_force != SWIOTLB_FORCE)
-@@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(s
-       /*
-        * Ensure that the address returned is DMA'ble
-        */
--      if (unlikely(!dma_capable(dev, dev_addr, size))) {
-+      if (unlikely(!dma_capable(dev, dev_addr, size, true))) {
-               swiotlb_tbl_unmap_single(dev, map, size, size, dir,
-                               attrs | DMA_ATTR_SKIP_CPU_SYNC);
-               return DMA_MAPPING_ERROR;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -51,14 +51,15 @@ static inline phys_addr_t dma_to_phys(st
-       return __sme_clr(__dma_to_phys(dev, daddr));
- }
--static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
-+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
-+              bool is_ram)
- {
-       dma_addr_t end = addr + size - 1;
-       if (!dev->dma_mask)
-               return false;
--      if (!IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-+      if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
-           min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
-               return false;
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -325,7 +325,7 @@ static inline bool dma_direct_possible(s
-               size_t size)
- {
-       return swiotlb_force != SWIOTLB_FORCE &&
--              dma_capable(dev, dma_addr, size);
-+              dma_capable(dev, dma_addr, size, true);
- }
- dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
-@@ -374,7 +374,7 @@ dma_addr_t dma_direct_map_resource(struc
- {
-       dma_addr_t dma_addr = paddr;
--      if (unlikely(!dma_capable(dev, dma_addr, size))) {
-+      if (unlikely(!dma_capable(dev, dma_addr, size, false))) {
-               report_addr(dev, dma_addr, size);
-               return DMA_MAPPING_ERROR;
-       }
---- a/kernel/dma/swiotlb.c
-+++ b/kernel/dma/swiotlb.c
-@@ -678,7 +678,7 @@ bool swiotlb_map(struct device *dev, phy
-       /* Ensure that the address returned is DMA'ble */
-       *dma_addr = __phys_to_dma(dev, *phys);
--      if (unlikely(!dma_capable(dev, *dma_addr, size))) {
-+      if (unlikely(!dma_capable(dev, *dma_addr, size, true))) {
-               swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
-                       attrs | DMA_ATTR_SKIP_CPU_SYNC);
-               return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch b/target/linux/bcm27xx/patches-5.4/950-0456-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
new file mode 100644 (file)
index 0000000..05dad5d
--- /dev/null
@@ -0,0 +1,366 @@
+From d5430c466b3c3b5f631ee37be333a40924575b72 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Thu, 21 Nov 2019 10:26:44 +0100
+Subject: [PATCH] dma-mapping: treat dev->bus_dma_mask as a DMA limit
+
+commit a7ba70f1787f977f970cd116076c6fce4b9e01cc upstream.
+
+Using a mask to represent bus DMA constraints has a set of limitations.
+The biggest one being it can only hold a power of two (minus one). The
+DMA mapping code is already aware of this and treats dev->bus_dma_mask
+as a limit. This quirk is already used by some architectures although
+still rare.
+
+With the introduction of the Raspberry Pi 4 we've found a new contender
+for the use of bus DMA limits, as its PCIe bus can only address the
+lower 3GB of memory (of a total of 4GB). This is impossible to represent
+with a mask. To make things worse the device-tree code rounds non power
+of two bus DMA limits to the next power of two, which is unacceptable in
+this case.
+
+In the light of this, rename dev->bus_dma_mask to dev->bus_dma_limit all
+over the tree and treat it as such. Note that dev->bus_dma_limit should
+contain the higher accessible DMA address.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+---
+ arch/mips/pci/fixup-sb1250.c  | 16 ++++++++--------
+ arch/powerpc/sysdev/fsl_pci.c |  6 +++---
+ arch/x86/kernel/pci-dma.c     |  2 +-
+ arch/x86/mm/mem_encrypt.c     |  2 +-
+ arch/x86/pci/sta2x11-fixup.c  |  2 +-
+ drivers/acpi/arm64/iort.c     | 20 +++++++-------------
+ drivers/ata/ahci.c            |  2 +-
+ drivers/iommu/dma-iommu.c     |  3 +--
+ drivers/of/device.c           |  9 +++++----
+ include/linux/device.h        |  6 +++---
+ include/linux/dma-direct.h    |  2 +-
+ include/linux/dma-mapping.h   |  2 +-
+ kernel/dma/direct.c           | 27 +++++++++++++--------------
+ 13 files changed, 46 insertions(+), 53 deletions(-)
+
+--- a/arch/mips/pci/fixup-sb1250.c
++++ b/arch/mips/pci/fixup-sb1250.c
+@@ -21,22 +21,22 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SI
+ /*
+  * The BCM1250, etc. PCI host bridge does not support DAC on its 32-bit
+- * bus, so we set the bus's DMA mask accordingly.  However the HT link
++ * bus, so we set the bus's DMA limit accordingly.  However the HT link
+  * down the artificial PCI-HT bridge supports 40-bit addressing and the
+  * SP1011 HT-PCI bridge downstream supports both DAC and a 64-bit bus
+  * width, so we record the PCI-HT bridge's secondary and subordinate bus
+- * numbers and do not set the mask for devices present in the inclusive
++ * numbers and do not set the limit for devices present in the inclusive
+  * range of those.
+  */
+-struct sb1250_bus_dma_mask_exclude {
++struct sb1250_bus_dma_limit_exclude {
+       bool set;
+       unsigned char start;
+       unsigned char end;
+ };
+-static int sb1250_bus_dma_mask(struct pci_dev *dev, void *data)
++static int sb1250_bus_dma_limit(struct pci_dev *dev, void *data)
+ {
+-      struct sb1250_bus_dma_mask_exclude *exclude = data;
++      struct sb1250_bus_dma_limit_exclude *exclude = data;
+       bool exclude_this;
+       bool ht_bridge;
+@@ -55,7 +55,7 @@ static int sb1250_bus_dma_mask(struct pc
+                       exclude->start, exclude->end);
+       } else {
+               dev_dbg(&dev->dev, "disabling DAC for device");
+-              dev->dev.bus_dma_mask = DMA_BIT_MASK(32);
++              dev->dev.bus_dma_limit = DMA_BIT_MASK(32);
+       }
+       return 0;
+@@ -63,9 +63,9 @@ static int sb1250_bus_dma_mask(struct pc
+ static void quirk_sb1250_pci_dac(struct pci_dev *dev)
+ {
+-      struct sb1250_bus_dma_mask_exclude exclude = { .set = false };
++      struct sb1250_bus_dma_limit_exclude exclude = { .set = false };
+-      pci_walk_bus(dev->bus, sb1250_bus_dma_mask, &exclude);
++      pci_walk_bus(dev->bus, sb1250_bus_dma_limit, &exclude);
+ }
+ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_PCI,
+                       quirk_sb1250_pci_dac);
+--- a/arch/powerpc/sysdev/fsl_pci.c
++++ b/arch/powerpc/sysdev/fsl_pci.c
+@@ -115,8 +115,8 @@ static void pci_dma_dev_setup_swiotlb(st
+ {
+       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+-      pdev->dev.bus_dma_mask =
+-              hose->dma_window_base_cur + hose->dma_window_size;
++      pdev->dev.bus_dma_limit =
++              hose->dma_window_base_cur + hose->dma_window_size - 1;
+ }
+ static void setup_swiotlb_ops(struct pci_controller *hose)
+@@ -135,7 +135,7 @@ static void fsl_pci_dma_set_mask(struct
+        * mapping that allows addressing any RAM address from across PCI.
+        */
+       if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
+-              dev->bus_dma_mask = 0;
++              dev->bus_dma_limit = 0;
+               dev->archdata.dma_offset = pci64_dma_offset;
+       }
+ }
+--- a/arch/x86/kernel/pci-dma.c
++++ b/arch/x86/kernel/pci-dma.c
+@@ -146,7 +146,7 @@ rootfs_initcall(pci_iommu_init);
+ static int via_no_dac_cb(struct pci_dev *pdev, void *data)
+ {
+-      pdev->dev.bus_dma_mask = DMA_BIT_MASK(32);
++      pdev->dev.bus_dma_limit = DMA_BIT_MASK(32);
+       return 0;
+ }
+--- a/arch/x86/mm/mem_encrypt.c
++++ b/arch/x86/mm/mem_encrypt.c
+@@ -367,7 +367,7 @@ bool force_dma_unencrypted(struct device
+       if (sme_active()) {
+               u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));
+               u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask,
+-                                              dev->bus_dma_mask);
++                                              dev->bus_dma_limit);
+               if (dma_dev_mask <= dma_enc_mask)
+                       return true;
+--- a/arch/x86/pci/sta2x11-fixup.c
++++ b/arch/x86/pci/sta2x11-fixup.c
+@@ -143,7 +143,7 @@ static void sta2x11_map_ep(struct pci_de
+       dev->dma_pfn_offset = PFN_DOWN(-amba_base);
+-      dev->bus_dma_mask = max_amba_addr;
++      dev->bus_dma_limit = max_amba_addr;
+       pci_set_consistent_dma_mask(pdev, max_amba_addr);
+       pci_set_dma_mask(pdev, max_amba_addr);
+--- a/drivers/acpi/arm64/iort.c
++++ b/drivers/acpi/arm64/iort.c
+@@ -1057,8 +1057,8 @@ static int rc_dma_get_range(struct devic
+  */
+ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
+ {
+-      u64 mask, dmaaddr = 0, size = 0, offset = 0;
+-      int ret, msb;
++      u64 end, mask, dmaaddr = 0, size = 0, offset = 0;
++      int ret;
+       /*
+        * If @dev is expected to be DMA-capable then the bus code that created
+@@ -1085,19 +1085,13 @@ void iort_dma_setup(struct device *dev,
+       }
+       if (!ret) {
+-              msb = fls64(dmaaddr + size - 1);
+               /*
+-               * Round-up to the power-of-two mask or set
+-               * the mask to the whole 64-bit address space
+-               * in case the DMA region covers the full
+-               * memory window.
++               * Limit coherent and dma mask based on size retrieved from
++               * firmware.
+                */
+-              mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1;
+-              /*
+-               * Limit coherent and dma mask based on size
+-               * retrieved from firmware.
+-               */
+-              dev->bus_dma_mask = mask;
++              end = dmaaddr + size - 1;
++              mask = DMA_BIT_MASK(ilog2(end) + 1);
++              dev->bus_dma_limit = end;
+               dev->coherent_dma_mask = mask;
+               *dev->dma_mask = mask;
+       }
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -899,7 +899,7 @@ static int ahci_configure_dma_masks(stru
+        * value, don't extend it here. This happens on STA2X11, for example.
+        *
+        * XXX: manipulating the DMA mask from platform code is completely
+-       * bogus, platform code should use dev->bus_dma_mask instead..
++       * bogus, platform code should use dev->bus_dma_limit instead..
+        */
+       if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
+               return 0;
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -404,8 +404,7 @@ static dma_addr_t iommu_dma_alloc_iova(s
+       if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
+               iova_len = roundup_pow_of_two(iova_len);
+-      if (dev->bus_dma_mask)
+-              dma_limit &= dev->bus_dma_mask;
++      dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
+       if (domain->geometry.force_aperture)
+               dma_limit = min(dma_limit, domain->geometry.aperture_end);
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -93,7 +93,7 @@ int of_dma_configure(struct device *dev,
+       bool coherent;
+       unsigned long offset;
+       const struct iommu_ops *iommu;
+-      u64 mask;
++      u64 mask, end;
+       ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
+       if (ret < 0) {
+@@ -148,12 +148,13 @@ int of_dma_configure(struct device *dev,
+        * Limit coherent and dma mask based on size and default mask
+        * set by the driver.
+        */
+-      mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1);
++      end = dma_addr + size - 1;
++      mask = DMA_BIT_MASK(ilog2(end) + 1);
+       dev->coherent_dma_mask &= mask;
+       *dev->dma_mask &= mask;
+-      /* ...but only set bus mask if we found valid dma-ranges earlier */
++      /* ...but only set bus limit if we found valid dma-ranges earlier */
+       if (!ret)
+-              dev->bus_dma_mask = mask;
++              dev->bus_dma_limit = end;
+       coherent = of_dma_is_coherent(np);
+       dev_dbg(dev, "device is%sdma coherent\n",
+--- a/include/linux/device.h
++++ b/include/linux/device.h
+@@ -1186,8 +1186,8 @@ struct dev_links_info {
+  * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
+  *            hardware supports 64-bit addresses for consistent allocations
+  *            such descriptors.
+- * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
+- *            limit than the device itself supports.
++ * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
++ *            DMA limit than the device itself supports.
+  * @dma_pfn_offset: offset of DMA memory range relatively of RAM
+  * @dma_parms:        A low level driver may set these to teach IOMMU code about
+  *            segment limitations.
+@@ -1270,7 +1270,7 @@ struct device {
+                                            not all hardware supports
+                                            64 bit addresses for consistent
+                                            allocations such descriptors. */
+-      u64             bus_dma_mask;   /* upstream dma_mask constraint */
++      u64             bus_dma_limit;  /* upstream dma constraint */
+       unsigned long   dma_pfn_offset;
+       struct device_dma_parameters *dma_parms;
+--- a/include/linux/dma-direct.h
++++ b/include/linux/dma-direct.h
+@@ -63,7 +63,7 @@ static inline bool dma_capable(struct de
+           min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
+               return false;
+-      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
++      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_limit);
+ }
+ u64 dma_direct_get_required_mask(struct device *dev);
+--- a/include/linux/dma-mapping.h
++++ b/include/linux/dma-mapping.h
+@@ -697,7 +697,7 @@ static inline int dma_coerce_mask_and_co
+  */
+ static inline bool dma_addressing_limited(struct device *dev)
+ {
+-      return min_not_zero(dma_get_mask(dev), dev->bus_dma_mask) <
++      return min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
+                           dma_get_required_mask(dev);
+ }
+--- a/kernel/dma/direct.c
++++ b/kernel/dma/direct.c
+@@ -26,10 +26,10 @@ static void report_addr(struct device *d
+ {
+       if (!dev->dma_mask) {
+               dev_err_once(dev, "DMA map on device without dma_mask\n");
+-      } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
++      } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_limit) {
+               dev_err_once(dev,
+-                      "overflow %pad+%zu of DMA mask %llx bus mask %llx\n",
+-                      &dma_addr, size, *dev->dma_mask, dev->bus_dma_mask);
++                      "overflow %pad+%zu of DMA mask %llx bus limit %llx\n",
++                      &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
+       }
+       WARN_ON_ONCE(1);
+ }
+@@ -50,15 +50,14 @@ u64 dma_direct_get_required_mask(struct
+ }
+ static gfp_t __dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
+-              u64 *phys_mask)
++              u64 *phys_limit)
+ {
+-      if (dev->bus_dma_mask && dev->bus_dma_mask < dma_mask)
+-              dma_mask = dev->bus_dma_mask;
++      u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
+       if (force_dma_unencrypted(dev))
+-              *phys_mask = __dma_to_phys(dev, dma_mask);
++              *phys_limit = __dma_to_phys(dev, dma_limit);
+       else
+-              *phys_mask = dma_to_phys(dev, dma_mask);
++              *phys_limit = dma_to_phys(dev, dma_limit);
+       /*
+        * Optimistically try the zone that the physical address mask falls
+@@ -68,9 +67,9 @@ static gfp_t __dma_direct_optimal_gfp_ma
+        * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
+        * zones.
+        */
+-      if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
++      if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
+               return GFP_DMA;
+-      if (*phys_mask <= DMA_BIT_MASK(32))
++      if (*phys_limit <= DMA_BIT_MASK(32))
+               return GFP_DMA32;
+       return 0;
+ }
+@@ -78,7 +77,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
+ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
+ {
+       return phys_to_dma_direct(dev, phys) + size - 1 <=
+-                      min_not_zero(dev->coherent_dma_mask, dev->bus_dma_mask);
++                      min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
+ }
+ struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
+@@ -87,7 +86,7 @@ struct page *__dma_direct_alloc_pages(st
+       size_t alloc_size = PAGE_ALIGN(size);
+       int node = dev_to_node(dev);
+       struct page *page = NULL;
+-      u64 phys_mask;
++      u64 phys_limit;
+       if (attrs & DMA_ATTR_NO_WARN)
+               gfp |= __GFP_NOWARN;
+@@ -95,7 +94,7 @@ struct page *__dma_direct_alloc_pages(st
+       /* we always manually zero the memory once we are done: */
+       gfp &= ~__GFP_ZERO;
+       gfp |= __dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
+-                      &phys_mask);
++                      &phys_limit);
+       page = dma_alloc_contiguous(dev, alloc_size, gfp);
+       if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
+               dma_free_contiguous(dev, page, alloc_size);
+@@ -109,7 +108,7 @@ again:
+               page = NULL;
+               if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
+-                  phys_mask < DMA_BIT_MASK(64) &&
++                  phys_limit < DMA_BIT_MASK(64) &&
+                   !(gfp & (GFP_DMA32 | GFP_DMA))) {
+                       gfp |= GFP_DMA32;
+                       goto again;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch b/target/linux/bcm27xx/patches-5.4/950-0457-ARM-dts-bcm2711-Enable-PCIe-controller.patch
new file mode 100644 (file)
index 0000000..9f114c1
--- /dev/null
@@ -0,0 +1,56 @@
+From 0ec0bc884f6cf1ec9775c750f78ce28be7da4340 Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Mon, 16 Dec 2019 12:01:08 +0100
+Subject: [PATCH] ARM: dts: bcm2711: Enable PCIe controller
+
+commit d5c8dc0d4c880fbde5293cc186b1ab23466254c4 upstream.
+
+This enables bcm2711's PCIe bus, which is hardwired to a VIA
+Technologies XHCI USB 3.0 controller.
+
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ arch/arm/boot/dts/bcm2711.dtsi | 31 ++++++++++++++++++++++++++++++-
+ 1 file changed, 30 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2711.dtsi
++++ b/arch/arm/boot/dts/bcm2711.dtsi
+@@ -331,7 +331,36 @@
+               #address-cells = <2>;
+               #size-cells = <1>;
+-              ranges = <0x0 0x7c000000  0x0 0xfc000000  0x03800000>;
++              ranges = <0x0 0x7c000000  0x0 0xfc000000  0x03800000>,
++                       <0x6 0x00000000  0x6 0x00000000  0x40000000>;
++
++              pcie0: pcie@7d500000 {
++                      compatible = "brcm,bcm2711-pcie";
++                      reg = <0x0 0x7d500000 0x9310>;
++                      device_type = "pci";
++                      #address-cells = <3>;
++                      #interrupt-cells = <1>;
++                      #size-cells = <2>;
++                      interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
++                      interrupt-names = "pcie", "msi";
++                      interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++                      interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
++                                                      IRQ_TYPE_LEVEL_HIGH>;
++                      msi-controller;
++                      msi-parent = <&pcie0>;
++
++                      ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
++                                0x0 0x04000000>;
++                      /*
++                       * The wrapper around the PCIe block has a bug
++                       * preventing it from accessing beyond the first 3GB of
++                       * memory.
++                       */
++                      dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
++                                    0x0 0xc0000000>;
++                      brcm,enable-ssc;
++              };
+               genet: ethernet@7d580000 {
+                       compatible = "brcm,bcm2711-genet-v5";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0457-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch b/target/linux/bcm27xx/patches-5.4/950-0457-dma-mapping-treat-dev-bus_dma_mask-as-a-DMA-limit.patch
deleted file mode 100644 (file)
index 05dad5d..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-From d5430c466b3c3b5f631ee37be333a40924575b72 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Thu, 21 Nov 2019 10:26:44 +0100
-Subject: [PATCH] dma-mapping: treat dev->bus_dma_mask as a DMA limit
-
-commit a7ba70f1787f977f970cd116076c6fce4b9e01cc upstream.
-
-Using a mask to represent bus DMA constraints has a set of limitations.
-The biggest one being it can only hold a power of two (minus one). The
-DMA mapping code is already aware of this and treats dev->bus_dma_mask
-as a limit. This quirk is already used by some architectures although
-still rare.
-
-With the introduction of the Raspberry Pi 4 we've found a new contender
-for the use of bus DMA limits, as its PCIe bus can only address the
-lower 3GB of memory (of a total of 4GB). This is impossible to represent
-with a mask. To make things worse the device-tree code rounds non power
-of two bus DMA limits to the next power of two, which is unacceptable in
-this case.
-
-In the light of this, rename dev->bus_dma_mask to dev->bus_dma_limit all
-over the tree and treat it as such. Note that dev->bus_dma_limit should
-contain the higher accessible DMA address.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Reviewed-by: Robin Murphy <robin.murphy@arm.com>
-Signed-off-by: Christoph Hellwig <hch@lst.de>
----
- arch/mips/pci/fixup-sb1250.c  | 16 ++++++++--------
- arch/powerpc/sysdev/fsl_pci.c |  6 +++---
- arch/x86/kernel/pci-dma.c     |  2 +-
- arch/x86/mm/mem_encrypt.c     |  2 +-
- arch/x86/pci/sta2x11-fixup.c  |  2 +-
- drivers/acpi/arm64/iort.c     | 20 +++++++-------------
- drivers/ata/ahci.c            |  2 +-
- drivers/iommu/dma-iommu.c     |  3 +--
- drivers/of/device.c           |  9 +++++----
- include/linux/device.h        |  6 +++---
- include/linux/dma-direct.h    |  2 +-
- include/linux/dma-mapping.h   |  2 +-
- kernel/dma/direct.c           | 27 +++++++++++++--------------
- 13 files changed, 46 insertions(+), 53 deletions(-)
-
---- a/arch/mips/pci/fixup-sb1250.c
-+++ b/arch/mips/pci/fixup-sb1250.c
-@@ -21,22 +21,22 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SI
- /*
-  * The BCM1250, etc. PCI host bridge does not support DAC on its 32-bit
-- * bus, so we set the bus's DMA mask accordingly.  However the HT link
-+ * bus, so we set the bus's DMA limit accordingly.  However the HT link
-  * down the artificial PCI-HT bridge supports 40-bit addressing and the
-  * SP1011 HT-PCI bridge downstream supports both DAC and a 64-bit bus
-  * width, so we record the PCI-HT bridge's secondary and subordinate bus
-- * numbers and do not set the mask for devices present in the inclusive
-+ * numbers and do not set the limit for devices present in the inclusive
-  * range of those.
-  */
--struct sb1250_bus_dma_mask_exclude {
-+struct sb1250_bus_dma_limit_exclude {
-       bool set;
-       unsigned char start;
-       unsigned char end;
- };
--static int sb1250_bus_dma_mask(struct pci_dev *dev, void *data)
-+static int sb1250_bus_dma_limit(struct pci_dev *dev, void *data)
- {
--      struct sb1250_bus_dma_mask_exclude *exclude = data;
-+      struct sb1250_bus_dma_limit_exclude *exclude = data;
-       bool exclude_this;
-       bool ht_bridge;
-@@ -55,7 +55,7 @@ static int sb1250_bus_dma_mask(struct pc
-                       exclude->start, exclude->end);
-       } else {
-               dev_dbg(&dev->dev, "disabling DAC for device");
--              dev->dev.bus_dma_mask = DMA_BIT_MASK(32);
-+              dev->dev.bus_dma_limit = DMA_BIT_MASK(32);
-       }
-       return 0;
-@@ -63,9 +63,9 @@ static int sb1250_bus_dma_mask(struct pc
- static void quirk_sb1250_pci_dac(struct pci_dev *dev)
- {
--      struct sb1250_bus_dma_mask_exclude exclude = { .set = false };
-+      struct sb1250_bus_dma_limit_exclude exclude = { .set = false };
--      pci_walk_bus(dev->bus, sb1250_bus_dma_mask, &exclude);
-+      pci_walk_bus(dev->bus, sb1250_bus_dma_limit, &exclude);
- }
- DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_PCI,
-                       quirk_sb1250_pci_dac);
---- a/arch/powerpc/sysdev/fsl_pci.c
-+++ b/arch/powerpc/sysdev/fsl_pci.c
-@@ -115,8 +115,8 @@ static void pci_dma_dev_setup_swiotlb(st
- {
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
--      pdev->dev.bus_dma_mask =
--              hose->dma_window_base_cur + hose->dma_window_size;
-+      pdev->dev.bus_dma_limit =
-+              hose->dma_window_base_cur + hose->dma_window_size - 1;
- }
- static void setup_swiotlb_ops(struct pci_controller *hose)
-@@ -135,7 +135,7 @@ static void fsl_pci_dma_set_mask(struct
-        * mapping that allows addressing any RAM address from across PCI.
-        */
-       if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
--              dev->bus_dma_mask = 0;
-+              dev->bus_dma_limit = 0;
-               dev->archdata.dma_offset = pci64_dma_offset;
-       }
- }
---- a/arch/x86/kernel/pci-dma.c
-+++ b/arch/x86/kernel/pci-dma.c
-@@ -146,7 +146,7 @@ rootfs_initcall(pci_iommu_init);
- static int via_no_dac_cb(struct pci_dev *pdev, void *data)
- {
--      pdev->dev.bus_dma_mask = DMA_BIT_MASK(32);
-+      pdev->dev.bus_dma_limit = DMA_BIT_MASK(32);
-       return 0;
- }
---- a/arch/x86/mm/mem_encrypt.c
-+++ b/arch/x86/mm/mem_encrypt.c
-@@ -367,7 +367,7 @@ bool force_dma_unencrypted(struct device
-       if (sme_active()) {
-               u64 dma_enc_mask = DMA_BIT_MASK(__ffs64(sme_me_mask));
-               u64 dma_dev_mask = min_not_zero(dev->coherent_dma_mask,
--                                              dev->bus_dma_mask);
-+                                              dev->bus_dma_limit);
-               if (dma_dev_mask <= dma_enc_mask)
-                       return true;
---- a/arch/x86/pci/sta2x11-fixup.c
-+++ b/arch/x86/pci/sta2x11-fixup.c
-@@ -143,7 +143,7 @@ static void sta2x11_map_ep(struct pci_de
-       dev->dma_pfn_offset = PFN_DOWN(-amba_base);
--      dev->bus_dma_mask = max_amba_addr;
-+      dev->bus_dma_limit = max_amba_addr;
-       pci_set_consistent_dma_mask(pdev, max_amba_addr);
-       pci_set_dma_mask(pdev, max_amba_addr);
---- a/drivers/acpi/arm64/iort.c
-+++ b/drivers/acpi/arm64/iort.c
-@@ -1057,8 +1057,8 @@ static int rc_dma_get_range(struct devic
-  */
- void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
- {
--      u64 mask, dmaaddr = 0, size = 0, offset = 0;
--      int ret, msb;
-+      u64 end, mask, dmaaddr = 0, size = 0, offset = 0;
-+      int ret;
-       /*
-        * If @dev is expected to be DMA-capable then the bus code that created
-@@ -1085,19 +1085,13 @@ void iort_dma_setup(struct device *dev,
-       }
-       if (!ret) {
--              msb = fls64(dmaaddr + size - 1);
-               /*
--               * Round-up to the power-of-two mask or set
--               * the mask to the whole 64-bit address space
--               * in case the DMA region covers the full
--               * memory window.
-+               * Limit coherent and dma mask based on size retrieved from
-+               * firmware.
-                */
--              mask = msb == 64 ? U64_MAX : (1ULL << msb) - 1;
--              /*
--               * Limit coherent and dma mask based on size
--               * retrieved from firmware.
--               */
--              dev->bus_dma_mask = mask;
-+              end = dmaaddr + size - 1;
-+              mask = DMA_BIT_MASK(ilog2(end) + 1);
-+              dev->bus_dma_limit = end;
-               dev->coherent_dma_mask = mask;
-               *dev->dma_mask = mask;
-       }
---- a/drivers/ata/ahci.c
-+++ b/drivers/ata/ahci.c
-@@ -899,7 +899,7 @@ static int ahci_configure_dma_masks(stru
-        * value, don't extend it here. This happens on STA2X11, for example.
-        *
-        * XXX: manipulating the DMA mask from platform code is completely
--       * bogus, platform code should use dev->bus_dma_mask instead..
-+       * bogus, platform code should use dev->bus_dma_limit instead..
-        */
-       if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32))
-               return 0;
---- a/drivers/iommu/dma-iommu.c
-+++ b/drivers/iommu/dma-iommu.c
-@@ -404,8 +404,7 @@ static dma_addr_t iommu_dma_alloc_iova(s
-       if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
-               iova_len = roundup_pow_of_two(iova_len);
--      if (dev->bus_dma_mask)
--              dma_limit &= dev->bus_dma_mask;
-+      dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
-       if (domain->geometry.force_aperture)
-               dma_limit = min(dma_limit, domain->geometry.aperture_end);
---- a/drivers/of/device.c
-+++ b/drivers/of/device.c
-@@ -93,7 +93,7 @@ int of_dma_configure(struct device *dev,
-       bool coherent;
-       unsigned long offset;
-       const struct iommu_ops *iommu;
--      u64 mask;
-+      u64 mask, end;
-       ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
-       if (ret < 0) {
-@@ -148,12 +148,13 @@ int of_dma_configure(struct device *dev,
-        * Limit coherent and dma mask based on size and default mask
-        * set by the driver.
-        */
--      mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1);
-+      end = dma_addr + size - 1;
-+      mask = DMA_BIT_MASK(ilog2(end) + 1);
-       dev->coherent_dma_mask &= mask;
-       *dev->dma_mask &= mask;
--      /* ...but only set bus mask if we found valid dma-ranges earlier */
-+      /* ...but only set bus limit if we found valid dma-ranges earlier */
-       if (!ret)
--              dev->bus_dma_mask = mask;
-+              dev->bus_dma_limit = end;
-       coherent = of_dma_is_coherent(np);
-       dev_dbg(dev, "device is%sdma coherent\n",
---- a/include/linux/device.h
-+++ b/include/linux/device.h
-@@ -1186,8 +1186,8 @@ struct dev_links_info {
-  * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
-  *            hardware supports 64-bit addresses for consistent allocations
-  *            such descriptors.
-- * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
-- *            limit than the device itself supports.
-+ * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
-+ *            DMA limit than the device itself supports.
-  * @dma_pfn_offset: offset of DMA memory range relatively of RAM
-  * @dma_parms:        A low level driver may set these to teach IOMMU code about
-  *            segment limitations.
-@@ -1270,7 +1270,7 @@ struct device {
-                                            not all hardware supports
-                                            64 bit addresses for consistent
-                                            allocations such descriptors. */
--      u64             bus_dma_mask;   /* upstream dma_mask constraint */
-+      u64             bus_dma_limit;  /* upstream dma constraint */
-       unsigned long   dma_pfn_offset;
-       struct device_dma_parameters *dma_parms;
---- a/include/linux/dma-direct.h
-+++ b/include/linux/dma-direct.h
-@@ -63,7 +63,7 @@ static inline bool dma_capable(struct de
-           min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
-               return false;
--      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_mask);
-+      return end <= min_not_zero(*dev->dma_mask, dev->bus_dma_limit);
- }
- u64 dma_direct_get_required_mask(struct device *dev);
---- a/include/linux/dma-mapping.h
-+++ b/include/linux/dma-mapping.h
-@@ -697,7 +697,7 @@ static inline int dma_coerce_mask_and_co
-  */
- static inline bool dma_addressing_limited(struct device *dev)
- {
--      return min_not_zero(dma_get_mask(dev), dev->bus_dma_mask) <
-+      return min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
-                           dma_get_required_mask(dev);
- }
---- a/kernel/dma/direct.c
-+++ b/kernel/dma/direct.c
-@@ -26,10 +26,10 @@ static void report_addr(struct device *d
- {
-       if (!dev->dma_mask) {
-               dev_err_once(dev, "DMA map on device without dma_mask\n");
--      } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_mask) {
-+      } else if (*dev->dma_mask >= DMA_BIT_MASK(32) || dev->bus_dma_limit) {
-               dev_err_once(dev,
--                      "overflow %pad+%zu of DMA mask %llx bus mask %llx\n",
--                      &dma_addr, size, *dev->dma_mask, dev->bus_dma_mask);
-+                      "overflow %pad+%zu of DMA mask %llx bus limit %llx\n",
-+                      &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
-       }
-       WARN_ON_ONCE(1);
- }
-@@ -50,15 +50,14 @@ u64 dma_direct_get_required_mask(struct
- }
- static gfp_t __dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
--              u64 *phys_mask)
-+              u64 *phys_limit)
- {
--      if (dev->bus_dma_mask && dev->bus_dma_mask < dma_mask)
--              dma_mask = dev->bus_dma_mask;
-+      u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
-       if (force_dma_unencrypted(dev))
--              *phys_mask = __dma_to_phys(dev, dma_mask);
-+              *phys_limit = __dma_to_phys(dev, dma_limit);
-       else
--              *phys_mask = dma_to_phys(dev, dma_mask);
-+              *phys_limit = dma_to_phys(dev, dma_limit);
-       /*
-        * Optimistically try the zone that the physical address mask falls
-@@ -68,9 +67,9 @@ static gfp_t __dma_direct_optimal_gfp_ma
-        * Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
-        * zones.
-        */
--      if (*phys_mask <= DMA_BIT_MASK(zone_dma_bits))
-+      if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
-               return GFP_DMA;
--      if (*phys_mask <= DMA_BIT_MASK(32))
-+      if (*phys_limit <= DMA_BIT_MASK(32))
-               return GFP_DMA32;
-       return 0;
- }
-@@ -78,7 +77,7 @@ static gfp_t __dma_direct_optimal_gfp_ma
- static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
- {
-       return phys_to_dma_direct(dev, phys) + size - 1 <=
--                      min_not_zero(dev->coherent_dma_mask, dev->bus_dma_mask);
-+                      min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
- }
- struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
-@@ -87,7 +86,7 @@ struct page *__dma_direct_alloc_pages(st
-       size_t alloc_size = PAGE_ALIGN(size);
-       int node = dev_to_node(dev);
-       struct page *page = NULL;
--      u64 phys_mask;
-+      u64 phys_limit;
-       if (attrs & DMA_ATTR_NO_WARN)
-               gfp |= __GFP_NOWARN;
-@@ -95,7 +94,7 @@ struct page *__dma_direct_alloc_pages(st
-       /* we always manually zero the memory once we are done: */
-       gfp &= ~__GFP_ZERO;
-       gfp |= __dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
--                      &phys_mask);
-+                      &phys_limit);
-       page = dma_alloc_contiguous(dev, alloc_size, gfp);
-       if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
-               dma_free_contiguous(dev, page, alloc_size);
-@@ -109,7 +108,7 @@ again:
-               page = NULL;
-               if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
--                  phys_mask < DMA_BIT_MASK(64) &&
-+                  phys_limit < DMA_BIT_MASK(64) &&
-                   !(gfp & (GFP_DMA32 | GFP_DMA))) {
-                       gfp |= GFP_DMA32;
-                       goto again;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0458-ARM-dts-bcm2711-Enable-PCIe-controller.patch b/target/linux/bcm27xx/patches-5.4/950-0458-ARM-dts-bcm2711-Enable-PCIe-controller.patch
deleted file mode 100644 (file)
index 9f114c1..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-From 0ec0bc884f6cf1ec9775c750f78ce28be7da4340 Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Mon, 16 Dec 2019 12:01:08 +0100
-Subject: [PATCH] ARM: dts: bcm2711: Enable PCIe controller
-
-commit d5c8dc0d4c880fbde5293cc186b1ab23466254c4 upstream.
-
-This enables bcm2711's PCIe bus, which is hardwired to a VIA
-Technologies XHCI USB 3.0 controller.
-
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
----
- arch/arm/boot/dts/bcm2711.dtsi | 31 ++++++++++++++++++++++++++++++-
- 1 file changed, 30 insertions(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/bcm2711.dtsi
-+++ b/arch/arm/boot/dts/bcm2711.dtsi
-@@ -331,7 +331,36 @@
-               #address-cells = <2>;
-               #size-cells = <1>;
--              ranges = <0x0 0x7c000000  0x0 0xfc000000  0x03800000>;
-+              ranges = <0x0 0x7c000000  0x0 0xfc000000  0x03800000>,
-+                       <0x6 0x00000000  0x6 0x00000000  0x40000000>;
-+
-+              pcie0: pcie@7d500000 {
-+                      compatible = "brcm,bcm2711-pcie";
-+                      reg = <0x0 0x7d500000 0x9310>;
-+                      device_type = "pci";
-+                      #address-cells = <3>;
-+                      #interrupt-cells = <1>;
-+                      #size-cells = <2>;
-+                      interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
-+                                   <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
-+                      interrupt-names = "pcie", "msi";
-+                      interrupt-map-mask = <0x0 0x0 0x0 0x7>;
-+                      interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
-+                                                      IRQ_TYPE_LEVEL_HIGH>;
-+                      msi-controller;
-+                      msi-parent = <&pcie0>;
-+
-+                      ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000
-+                                0x0 0x04000000>;
-+                      /*
-+                       * The wrapper around the PCIe block has a bug
-+                       * preventing it from accessing beyond the first 3GB of
-+                       * memory.
-+                       */
-+                      dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
-+                                    0x0 0xc0000000>;
-+                      brcm,enable-ssc;
-+              };
-               genet: ethernet@7d580000 {
-                       compatible = "brcm,bcm2711-genet-v5";
diff --git a/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch b/target/linux/bcm27xx/patches-5.4/950-0458-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
new file mode 100644 (file)
index 0000000..ca97a19
--- /dev/null
@@ -0,0 +1,810 @@
+From 4d9470c29736bf81bdb0d21da24cf350b1e99402 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+Date: Mon, 16 Dec 2019 12:01:09 +0100
+Subject: [PATCH] PCI: brcmstb: Add Broadcom STB PCIe host controller
+ driver
+
+commit c0452137034bda8f686dd9a2e167949bfffd6776 upstream.
+
+This adds a basic driver for Broadcom's STB PCIe controller, for now
+aimed at Raspberry Pi 4's SoC, bcm2711.
+
+Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
+Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+[lorenzo.pieralisi@arm.com: updated brcm_pcie_get_rc_bar2_size_and_offset()according to https://lore.kernel.org/linux-pci/be8ddb33a7360af1815cf686f77f3f0913d02be3.camel@suse.de]
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+Reviewed-by: Jeremy Linton <jeremy.linton@arm.com>
+---
+ drivers/pci/controller/Kconfig        |   8 +
+ drivers/pci/controller/Makefile       |   1 +
+ drivers/pci/controller/pcie-brcmstb.c | 755 ++++++++++++++++++++++++++
+ 3 files changed, 764 insertions(+)
+ create mode 100644 drivers/pci/controller/pcie-brcmstb.c
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -281,6 +281,14 @@ config VMD
+         To compile this driver as a module, choose M here: the
+         module will be called vmd.
++config PCIE_BRCMSTB
++      tristate "Broadcom Brcmstb PCIe host controller"
++      depends on ARCH_BCM2835 || COMPILE_TEST
++      depends on OF
++      help
++        Say Y here to enable PCIe host controller support for
++        Broadcom STB based SoCs, like the Raspberry Pi 4.
++
+ config PCI_HYPERV_INTERFACE
+       tristate "Hyper-V PCI Interface"
+       depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-medi
+ obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
+ obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
+ obj-$(CONFIG_VMD) += vmd.o
++obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
+ # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
+ obj-y                         += dwc/
+--- /dev/null
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -0,0 +1,755 @@
++// SPDX-License-Identifier: GPL-2.0+
++/* Copyright (C) 2009 - 2019 Broadcom */
++
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/compiler.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/irqdomain.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/log2.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/of_platform.h>
++#include <linux/pci.h>
++#include <linux/printk.h>
++#include <linux/sizes.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++#include <linux/types.h>
++
++#include "../pci.h"
++
++/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
++#define BRCM_PCIE_CAP_REGS                            0x00ac
++
++/* Broadcom STB PCIe Register Offsets */
++#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1                               0x0188
++#define  PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK        0xc
++#define  PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN                        0x0
++
++#define PCIE_RC_CFG_PRIV1_ID_VAL3                     0x043c
++#define  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK    0xffffff
++
++#define PCIE_RC_DL_MDIO_ADDR                          0x1100
++#define PCIE_RC_DL_MDIO_WR_DATA                               0x1104
++#define PCIE_RC_DL_MDIO_RD_DATA                               0x1108
++
++#define PCIE_MISC_MISC_CTRL                           0x4008
++#define  PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK               0x1000
++#define  PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK    0x2000
++#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK      0x300000
++#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128               0x0
++#define  PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK           0xf8000000
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO              0x400c
++#define PCIE_MEM_WIN0_LO(win) \
++              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI              0x4010
++#define PCIE_MEM_WIN0_HI(win) \
++              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
++
++#define PCIE_MISC_RC_BAR1_CONFIG_LO                   0x402c
++#define  PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK                0x1f
++
++#define PCIE_MISC_RC_BAR2_CONFIG_LO                   0x4034
++#define  PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK                0x1f
++#define PCIE_MISC_RC_BAR2_CONFIG_HI                   0x4038
++
++#define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
++#define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK                0x1f
++
++#define PCIE_MISC_PCIE_CTRL                           0x4064
++#define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK    0x1
++
++#define PCIE_MISC_PCIE_STATUS                         0x4068
++#define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK         0x80
++#define  PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK    0x20
++#define  PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK    0x10
++#define  PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK  0x40
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT              0x4070
++#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK  0xfff00000
++#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK   0xfff0
++#define PCIE_MEM_WIN0_BASE_LIMIT(win) \
++              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI                 0x4080
++#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK      0xff
++#define PCIE_MEM_WIN0_BASE_HI(win)    \
++              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
++
++#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI                        0x4084
++#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK    0xff
++#define PCIE_MEM_WIN0_LIMIT_HI(win)   \
++              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
++
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG                                        0x4204
++#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK      0x2
++#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK              0x08000000
++
++#define PCIE_MSI_INTR2_STATUS                         0x4500
++#define PCIE_MSI_INTR2_CLR                            0x4508
++#define PCIE_MSI_INTR2_MASK_SET                               0x4510
++#define PCIE_MSI_INTR2_MASK_CLR                               0x4514
++
++#define PCIE_EXT_CFG_DATA                             0x8000
++
++#define PCIE_EXT_CFG_INDEX                            0x9000
++#define  PCIE_EXT_BUSNUM_SHIFT                                20
++#define  PCIE_EXT_SLOT_SHIFT                          15
++#define  PCIE_EXT_FUNC_SHIFT                          12
++
++#define PCIE_RGR1_SW_INIT_1                           0x9210
++#define  PCIE_RGR1_SW_INIT_1_PERST_MASK                       0x1
++#define  PCIE_RGR1_SW_INIT_1_INIT_MASK                        0x2
++
++/* PCIe parameters */
++#define BRCM_NUM_PCIE_OUT_WINS                0x4
++
++/* MDIO registers */
++#define MDIO_PORT0                    0x0
++#define MDIO_DATA_MASK                        0x7fffffff
++#define MDIO_PORT_MASK                        0xf0000
++#define MDIO_REGAD_MASK                       0xffff
++#define MDIO_CMD_MASK                 0xfff00000
++#define MDIO_CMD_READ                 0x1
++#define MDIO_CMD_WRITE                        0x0
++#define MDIO_DATA_DONE_MASK           0x80000000
++#define MDIO_RD_DONE(x)                       (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0)
++#define MDIO_WT_DONE(x)                       (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1)
++#define SSC_REGS_ADDR                 0x1100
++#define SET_ADDR_OFFSET                       0x1f
++#define SSC_CNTL_OFFSET                       0x2
++#define SSC_CNTL_OVRD_EN_MASK         0x8000
++#define SSC_CNTL_OVRD_VAL_MASK                0x4000
++#define SSC_STATUS_OFFSET             0x1
++#define SSC_STATUS_SSC_MASK           0x400
++#define SSC_STATUS_PLL_LOCK_MASK      0x800
++
++/* Internal PCIe Host Controller Information.*/
++struct brcm_pcie {
++      struct device           *dev;
++      void __iomem            *base;
++      struct clk              *clk;
++      struct pci_bus          *root_bus;
++      struct device_node      *np;
++      bool                    ssc;
++      int                     gen;
++};
++
++/*
++ * This is to convert the size of the inbound "BAR" region to the
++ * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
++ */
++static int brcm_pcie_encode_ibar_size(u64 size)
++{
++      int log2_in = ilog2(size);
++
++      if (log2_in >= 12 && log2_in <= 15)
++              /* Covers 4KB to 32KB (inclusive) */
++              return (log2_in - 12) + 0x1c;
++      else if (log2_in >= 16 && log2_in <= 35)
++              /* Covers 64KB to 32GB, (inclusive) */
++              return log2_in - 15;
++      /* Something is awry so disable */
++      return 0;
++}
++
++static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
++{
++      u32 pkt = 0;
++
++      pkt |= FIELD_PREP(MDIO_PORT_MASK, port);
++      pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad);
++      pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd);
++
++      return pkt;
++}
++
++/* negative return value indicates error */
++static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
++{
++      int tries;
++      u32 data;
++
++      writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
++                 base + PCIE_RC_DL_MDIO_ADDR);
++      readl(base + PCIE_RC_DL_MDIO_ADDR);
++
++      data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
++      for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) {
++              udelay(10);
++              data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
++      }
++
++      *val = FIELD_GET(MDIO_DATA_MASK, data);
++      return MDIO_RD_DONE(data) ? 0 : -EIO;
++}
++
++/* negative return value indicates error */
++static int brcm_pcie_mdio_write(void __iomem *base, u8 port,
++                              u8 regad, u16 wrdata)
++{
++      int tries;
++      u32 data;
++
++      writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
++                 base + PCIE_RC_DL_MDIO_ADDR);
++      readl(base + PCIE_RC_DL_MDIO_ADDR);
++      writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
++
++      data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
++      for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) {
++              udelay(10);
++              data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
++      }
++
++      return MDIO_WT_DONE(data) ? 0 : -EIO;
++}
++
++/*
++ * Configures device for Spread Spectrum Clocking (SSC) mode; a negative
++ * return value indicates error.
++ */
++static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
++{
++      int pll, ssc;
++      int ret;
++      u32 tmp;
++
++      ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
++                                 SSC_REGS_ADDR);
++      if (ret < 0)
++              return ret;
++
++      ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
++                                SSC_CNTL_OFFSET, &tmp);
++      if (ret < 0)
++              return ret;
++
++      u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK);
++      u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK);
++      ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0,
++                                 SSC_CNTL_OFFSET, tmp);
++      if (ret < 0)
++              return ret;
++
++      usleep_range(1000, 2000);
++      ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
++                                SSC_STATUS_OFFSET, &tmp);
++      if (ret < 0)
++              return ret;
++
++      ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp);
++      pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp);
++
++      return ssc && pll ? 0 : -EIO;
++}
++
++/* Limits operation to a specific generation (1, 2, or 3) */
++static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
++{
++      u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
++      u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
++
++      lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen;
++      writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
++
++      lnkctl2 = (lnkctl2 & ~0xf) | gen;
++      writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
++}
++
++static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
++                                     unsigned int win, u64 cpu_addr,
++                                     u64 pcie_addr, u64 size)
++{
++      u32 cpu_addr_mb_high, limit_addr_mb_high;
++      phys_addr_t cpu_addr_mb, limit_addr_mb;
++      int high_addr_shift;
++      u32 tmp;
++
++      /* Set the base of the pcie_addr window */
++      writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win));
++      writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win));
++
++      /* Write the addr base & limit lower bits (in MBs) */
++      cpu_addr_mb = cpu_addr / SZ_1M;
++      limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
++
++      tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
++      u32p_replace_bits(&tmp, cpu_addr_mb,
++                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
++      u32p_replace_bits(&tmp, limit_addr_mb,
++                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
++      writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
++
++      /* Write the cpu & limit addr upper bits */
++      high_addr_shift =
++              HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
++
++      cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift;
++      tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
++      u32p_replace_bits(&tmp, cpu_addr_mb_high,
++                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK);
++      writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
++
++      limit_addr_mb_high = limit_addr_mb >> high_addr_shift;
++      tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
++      u32p_replace_bits(&tmp, limit_addr_mb_high,
++                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK);
++      writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
++}
++
++/* The controller is capable of serving in both RC and EP roles */
++static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
++{
++      void __iomem *base = pcie->base;
++      u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
++
++      return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
++}
++
++static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
++{
++      u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
++      u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val);
++      u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val);
++
++      return dla && plu;
++}
++
++/* Configuration space read/write support */
++static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg)
++{
++      return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT)
++              | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT)
++              | (busnr << PCIE_EXT_BUSNUM_SHIFT)
++              | (reg & ~3);
++}
++
++static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
++                                      int where)
++{
++      struct brcm_pcie *pcie = bus->sysdata;
++      void __iomem *base = pcie->base;
++      int idx;
++
++      /* Accesses to the RC go right to the RC registers if slot==0 */
++      if (pci_is_root_bus(bus))
++              return PCI_SLOT(devfn) ? NULL : base + where;
++
++      /* For devices, write to the config space index register */
++      idx = brcm_pcie_cfg_index(bus->number, devfn, 0);
++      writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
++      return base + PCIE_EXT_CFG_DATA + where;
++}
++
++static struct pci_ops brcm_pcie_ops = {
++      .map_bus = brcm_pcie_map_conf,
++      .read = pci_generic_config_read,
++      .write = pci_generic_config_write,
++};
++
++static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
++{
++      u32 tmp;
++
++      tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
++      u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK);
++      writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
++}
++
++static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val)
++{
++      u32 tmp;
++
++      tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
++      u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
++      writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
++}
++
++static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
++                                                      u64 *rc_bar2_size,
++                                                      u64 *rc_bar2_offset)
++{
++      struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++      struct device *dev = pcie->dev;
++      struct resource_entry *entry;
++
++      entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
++      if (!entry)
++              return -ENODEV;
++
++
++      /*
++       * The controller expects the inbound window offset to be calculated as
++       * the difference between PCIe's address space and CPU's. The offset
++       * provided by the firmware is calculated the opposite way, so we
++       * negate it.
++       */
++      *rc_bar2_offset = -entry->offset;
++      *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start);
++
++      /*
++       * We validate the inbound memory view even though we should trust
++       * whatever the device-tree provides. This is because of an HW issue on
++       * early Raspberry Pi 4's revisions (bcm2711). It turns out its
++       * firmware has to dynamically edit dma-ranges due to a bug on the
++       * PCIe controller integration, which prohibits any access above the
++       * lower 3GB of memory. Given this, we decided to keep the dma-ranges
++       * in check, avoiding hard to debug device-tree related issues in the
++       * future:
++       *
++       * The PCIe host controller by design must set the inbound viewport to
++       * be a contiguous arrangement of all of the system's memory.  In
++       * addition, its size mut be a power of two.  To further complicate
++       * matters, the viewport must start on a pcie-address that is aligned
++       * on a multiple of its size.  If a portion of the viewport does not
++       * represent system memory -- e.g. 3GB of memory requires a 4GB
++       * viewport -- we can map the outbound memory in or after 3GB and even
++       * though the viewport will overlap the outbound memory the controller
++       * will know to send outbound memory downstream and everything else
++       * upstream.
++       *
++       * For example:
++       *
++       * - The best-case scenario, memory up to 3GB, is to place the inbound
++       *   region in the first 4GB of pcie-space, as some legacy devices can
++       *   only address 32bits. We would also like to put the MSI under 4GB
++       *   as well, since some devices require a 32bit MSI target address.
++       *
++       * - If the system memory is 4GB or larger we cannot start the inbound
++       *   region at location 0 (since we have to allow some space for
++       *   outbound memory @ 3GB). So instead it will  start at the 1x
++       *   multiple of its size
++       */
++      if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
++          (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
++              dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
++                      *rc_bar2_size, *rc_bar2_offset);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int brcm_pcie_setup(struct brcm_pcie *pcie)
++{
++      struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++      u64 rc_bar2_offset, rc_bar2_size;
++      void __iomem *base = pcie->base;
++      struct device *dev = pcie->dev;
++      struct resource_entry *entry;
++      unsigned int scb_size_val;
++      bool ssc_good = false;
++      struct resource *res;
++      int num_out_wins = 0;
++      u16 nlw, cls, lnksta;
++      int i, ret;
++      u32 tmp;
++
++      /* Reset the bridge */
++      brcm_pcie_bridge_sw_init_set(pcie, 1);
++
++      usleep_range(100, 200);
++
++      /* Take the bridge out of reset */
++      brcm_pcie_bridge_sw_init_set(pcie, 0);
++
++      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++      tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
++      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++      /* Wait for SerDes to be stable */
++      usleep_range(100, 200);
++
++      /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
++      u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
++      u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
++      u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128,
++                        PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
++      writel(tmp, base + PCIE_MISC_MISC_CTRL);
++
++      ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
++                                                  &rc_bar2_offset);
++      if (ret)
++              return ret;
++
++      tmp = lower_32_bits(rc_bar2_offset);
++      u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
++                        PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
++      writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
++      writel(upper_32_bits(rc_bar2_offset),
++             base + PCIE_MISC_RC_BAR2_CONFIG_HI);
++
++      scb_size_val = rc_bar2_size ?
++                     ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
++      tmp = readl(base + PCIE_MISC_MISC_CTRL);
++      u32p_replace_bits(&tmp, scb_size_val,
++                        PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
++      writel(tmp, base + PCIE_MISC_MISC_CTRL);
++
++      /* disable the PCIe->GISB memory window (RC_BAR1) */
++      tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++      tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
++      writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++
++      /* disable the PCIe->SCB memory window (RC_BAR3) */
++      tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
++      tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
++      writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
++
++      /* Mask all interrupts since we are not handling any yet */
++      writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET);
++
++      /* clear any interrupts we find on boot */
++      writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR);
++
++      if (pcie->gen)
++              brcm_pcie_set_gen(pcie, pcie->gen);
++
++      /* Unassert the fundamental reset */
++      brcm_pcie_perst_set(pcie, 0);
++
++      /*
++       * Give the RC/EP time to wake up, before trying to configure RC.
++       * Intermittently check status for link-up, up to a total of 100ms.
++       */
++      for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
++              msleep(5);
++
++      if (!brcm_pcie_link_up(pcie)) {
++              dev_err(dev, "link down\n");
++              return -ENODEV;
++      }
++
++      if (!brcm_pcie_rc_mode(pcie)) {
++              dev_err(dev, "PCIe misconfigured; is in EP mode\n");
++              return -EINVAL;
++      }
++
++      resource_list_for_each_entry(entry, &bridge->windows) {
++              res = entry->res;
++
++              if (resource_type(res) != IORESOURCE_MEM)
++                      continue;
++
++              if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) {
++                      dev_err(pcie->dev, "too many outbound wins\n");
++                      return -EINVAL;
++              }
++
++              brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,
++                                         res->start - entry->offset,
++                                         resource_size(res));
++              num_out_wins++;
++      }
++
++      /*
++       * For config space accesses on the RC, show the right class for
++       * a PCIe-PCIe bridge (the default setting is to be EP mode).
++       */
++      tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
++      u32p_replace_bits(&tmp, 0x060400,
++                        PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
++      writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
++
++      if (pcie->ssc) {
++              ret = brcm_pcie_set_ssc(pcie);
++              if (ret == 0)
++                      ssc_good = true;
++              else
++                      dev_err(dev, "failed attempt to enter ssc mode\n");
++      }
++
++      lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
++      cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
++      nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
++      dev_info(dev, "link up, %s x%u %s\n",
++               PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533),
++               nlw, ssc_good ? "(SSC)" : "(!SSC)");
++
++      /* PCIe->SCB endian mode for BAR */
++      tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
++      u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN,
++              PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
++      writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
++
++      /*
++       * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
++       * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
++       */
++      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++      tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++      return 0;
++}
++
++/* L23 is a low-power PCIe link state */
++static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
++{
++      void __iomem *base = pcie->base;
++      int l23, i;
++      u32 tmp;
++
++      /* Assert request for L23 */
++      tmp = readl(base + PCIE_MISC_PCIE_CTRL);
++      u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
++      writel(tmp, base + PCIE_MISC_PCIE_CTRL);
++
++      /* Wait up to 36 msec for L23 */
++      tmp = readl(base + PCIE_MISC_PCIE_STATUS);
++      l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp);
++      for (i = 0; i < 15 && !l23; i++) {
++              usleep_range(2000, 2400);
++              tmp = readl(base + PCIE_MISC_PCIE_STATUS);
++              l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK,
++                              tmp);
++      }
++
++      if (!l23)
++              dev_err(pcie->dev, "failed to enter low-power link state\n");
++}
++
++static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
++{
++      void __iomem *base = pcie->base;
++      int tmp;
++
++      if (brcm_pcie_link_up(pcie))
++              brcm_pcie_enter_l23(pcie);
++      /* Assert fundamental reset */
++      brcm_pcie_perst_set(pcie, 1);
++
++      /* Deassert request for L23 in case it was asserted */
++      tmp = readl(base + PCIE_MISC_PCIE_CTRL);
++      u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
++      writel(tmp, base + PCIE_MISC_PCIE_CTRL);
++
++      /* Turn off SerDes */
++      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++      u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
++      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++      /* Shutdown PCIe bridge */
++      brcm_pcie_bridge_sw_init_set(pcie, 1);
++}
++
++static void __brcm_pcie_remove(struct brcm_pcie *pcie)
++{
++      brcm_pcie_turn_off(pcie);
++      clk_disable_unprepare(pcie->clk);
++      clk_put(pcie->clk);
++}
++
++static int brcm_pcie_remove(struct platform_device *pdev)
++{
++      struct brcm_pcie *pcie = platform_get_drvdata(pdev);
++
++      pci_stop_root_bus(pcie->root_bus);
++      pci_remove_root_bus(pcie->root_bus);
++      __brcm_pcie_remove(pcie);
++
++      return 0;
++}
++
++static int brcm_pcie_probe(struct platform_device *pdev)
++{
++      struct device_node *np = pdev->dev.of_node;
++      struct pci_host_bridge *bridge;
++      struct brcm_pcie *pcie;
++      struct pci_bus *child;
++      struct resource *res;
++      int ret;
++
++      bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
++      if (!bridge)
++              return -ENOMEM;
++
++      pcie = pci_host_bridge_priv(bridge);
++      pcie->dev = &pdev->dev;
++      pcie->np = np;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      pcie->base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(pcie->base))
++              return PTR_ERR(pcie->base);
++
++      pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie");
++      if (IS_ERR(pcie->clk))
++              return PTR_ERR(pcie->clk);
++
++      ret = of_pci_get_max_link_speed(np);
++      pcie->gen = (ret < 0) ? 0 : ret;
++
++      pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
++
++      ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows,
++                                            &bridge->dma_ranges, NULL);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(pcie->clk);
++      if (ret) {
++              dev_err(&pdev->dev, "could not enable clock\n");
++              return ret;
++      }
++
++      ret = brcm_pcie_setup(pcie);
++      if (ret)
++              goto fail;
++
++      bridge->dev.parent = &pdev->dev;
++      bridge->busnr = 0;
++      bridge->ops = &brcm_pcie_ops;
++      bridge->sysdata = pcie;
++      bridge->map_irq = of_irq_parse_and_map_pci;
++      bridge->swizzle_irq = pci_common_swizzle;
++
++      ret = pci_scan_root_bus_bridge(bridge);
++      if (ret < 0) {
++              dev_err(pcie->dev, "Scanning root bridge failed\n");
++              goto fail;
++      }
++
++      pci_assign_unassigned_bus_resources(bridge->bus);
++      list_for_each_entry(child, &bridge->bus->children, node)
++              pcie_bus_configure_settings(child);
++      pci_bus_add_devices(bridge->bus);
++      platform_set_drvdata(pdev, pcie);
++      pcie->root_bus = bridge->bus;
++
++      return 0;
++fail:
++      __brcm_pcie_remove(pcie);
++      return ret;
++}
++
++static const struct of_device_id brcm_pcie_match[] = {
++      { .compatible = "brcm,bcm2711-pcie" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, brcm_pcie_match);
++
++static struct platform_driver brcm_pcie_driver = {
++      .probe = brcm_pcie_probe,
++      .remove = brcm_pcie_remove,
++      .driver = {
++              .name = "brcm-pcie",
++              .of_match_table = brcm_pcie_match,
++      },
++};
++module_platform_driver(brcm_pcie_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Broadcom STB PCIe RC driver");
++MODULE_AUTHOR("Broadcom");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch b/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-Broadcom-STB-PCIe-host-controller-dr.patch
deleted file mode 100644 (file)
index ca97a19..0000000
+++ /dev/null
@@ -1,810 +0,0 @@
-From 4d9470c29736bf81bdb0d21da24cf350b1e99402 Mon Sep 17 00:00:00 2001
-From: Jim Quinlan <james.quinlan@broadcom.com>
-Date: Mon, 16 Dec 2019 12:01:09 +0100
-Subject: [PATCH] PCI: brcmstb: Add Broadcom STB PCIe host controller
- driver
-
-commit c0452137034bda8f686dd9a2e167949bfffd6776 upstream.
-
-This adds a basic driver for Broadcom's STB PCIe controller, for now
-aimed at Raspberry Pi 4's SoC, bcm2711.
-
-Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
-Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-[lorenzo.pieralisi@arm.com: updated brcm_pcie_get_rc_bar2_size_and_offset()according to https://lore.kernel.org/linux-pci/be8ddb33a7360af1815cf686f77f3f0913d02be3.camel@suse.de]
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
-Reviewed-by: Jeremy Linton <jeremy.linton@arm.com>
----
- drivers/pci/controller/Kconfig        |   8 +
- drivers/pci/controller/Makefile       |   1 +
- drivers/pci/controller/pcie-brcmstb.c | 755 ++++++++++++++++++++++++++
- 3 files changed, 764 insertions(+)
- create mode 100644 drivers/pci/controller/pcie-brcmstb.c
-
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -281,6 +281,14 @@ config VMD
-         To compile this driver as a module, choose M here: the
-         module will be called vmd.
-+config PCIE_BRCMSTB
-+      tristate "Broadcom Brcmstb PCIe host controller"
-+      depends on ARCH_BCM2835 || COMPILE_TEST
-+      depends on OF
-+      help
-+        Say Y here to enable PCIe host controller support for
-+        Broadcom STB based SoCs, like the Raspberry Pi 4.
-+
- config PCI_HYPERV_INTERFACE
-       tristate "Hyper-V PCI Interface"
-       depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64
---- a/drivers/pci/controller/Makefile
-+++ b/drivers/pci/controller/Makefile
-@@ -30,6 +30,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-medi
- obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
- obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
- obj-$(CONFIG_VMD) += vmd.o
-+obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
- # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
- obj-y                         += dwc/
---- /dev/null
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -0,0 +1,755 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/* Copyright (C) 2009 - 2019 Broadcom */
-+
-+#include <linux/bitfield.h>
-+#include <linux/clk.h>
-+#include <linux/compiler.h>
-+#include <linux/delay.h>
-+#include <linux/init.h>
-+#include <linux/interrupt.h>
-+#include <linux/io.h>
-+#include <linux/ioport.h>
-+#include <linux/irqdomain.h>
-+#include <linux/kernel.h>
-+#include <linux/list.h>
-+#include <linux/log2.h>
-+#include <linux/module.h>
-+#include <linux/of_address.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_pci.h>
-+#include <linux/of_platform.h>
-+#include <linux/pci.h>
-+#include <linux/printk.h>
-+#include <linux/sizes.h>
-+#include <linux/slab.h>
-+#include <linux/string.h>
-+#include <linux/types.h>
-+
-+#include "../pci.h"
-+
-+/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
-+#define BRCM_PCIE_CAP_REGS                            0x00ac
-+
-+/* Broadcom STB PCIe Register Offsets */
-+#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1                               0x0188
-+#define  PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK        0xc
-+#define  PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN                        0x0
-+
-+#define PCIE_RC_CFG_PRIV1_ID_VAL3                     0x043c
-+#define  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK    0xffffff
-+
-+#define PCIE_RC_DL_MDIO_ADDR                          0x1100
-+#define PCIE_RC_DL_MDIO_WR_DATA                               0x1104
-+#define PCIE_RC_DL_MDIO_RD_DATA                               0x1108
-+
-+#define PCIE_MISC_MISC_CTRL                           0x4008
-+#define  PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK               0x1000
-+#define  PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK    0x2000
-+#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK      0x300000
-+#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128               0x0
-+#define  PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK           0xf8000000
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO              0x400c
-+#define PCIE_MEM_WIN0_LO(win) \
-+              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 4)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI              0x4010
-+#define PCIE_MEM_WIN0_HI(win) \
-+              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 4)
-+
-+#define PCIE_MISC_RC_BAR1_CONFIG_LO                   0x402c
-+#define  PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK                0x1f
-+
-+#define PCIE_MISC_RC_BAR2_CONFIG_LO                   0x4034
-+#define  PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK                0x1f
-+#define PCIE_MISC_RC_BAR2_CONFIG_HI                   0x4038
-+
-+#define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
-+#define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK                0x1f
-+
-+#define PCIE_MISC_PCIE_CTRL                           0x4064
-+#define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK    0x1
-+
-+#define PCIE_MISC_PCIE_STATUS                         0x4068
-+#define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK         0x80
-+#define  PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK    0x20
-+#define  PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK    0x10
-+#define  PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK  0x40
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT              0x4070
-+#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK  0xfff00000
-+#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK   0xfff0
-+#define PCIE_MEM_WIN0_BASE_LIMIT(win) \
-+              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI                 0x4080
-+#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK      0xff
-+#define PCIE_MEM_WIN0_BASE_HI(win)    \
-+              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
-+
-+#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI                        0x4084
-+#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK    0xff
-+#define PCIE_MEM_WIN0_LIMIT_HI(win)   \
-+              PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
-+
-+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG                                        0x4204
-+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK      0x2
-+#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK              0x08000000
-+
-+#define PCIE_MSI_INTR2_STATUS                         0x4500
-+#define PCIE_MSI_INTR2_CLR                            0x4508
-+#define PCIE_MSI_INTR2_MASK_SET                               0x4510
-+#define PCIE_MSI_INTR2_MASK_CLR                               0x4514
-+
-+#define PCIE_EXT_CFG_DATA                             0x8000
-+
-+#define PCIE_EXT_CFG_INDEX                            0x9000
-+#define  PCIE_EXT_BUSNUM_SHIFT                                20
-+#define  PCIE_EXT_SLOT_SHIFT                          15
-+#define  PCIE_EXT_FUNC_SHIFT                          12
-+
-+#define PCIE_RGR1_SW_INIT_1                           0x9210
-+#define  PCIE_RGR1_SW_INIT_1_PERST_MASK                       0x1
-+#define  PCIE_RGR1_SW_INIT_1_INIT_MASK                        0x2
-+
-+/* PCIe parameters */
-+#define BRCM_NUM_PCIE_OUT_WINS                0x4
-+
-+/* MDIO registers */
-+#define MDIO_PORT0                    0x0
-+#define MDIO_DATA_MASK                        0x7fffffff
-+#define MDIO_PORT_MASK                        0xf0000
-+#define MDIO_REGAD_MASK                       0xffff
-+#define MDIO_CMD_MASK                 0xfff00000
-+#define MDIO_CMD_READ                 0x1
-+#define MDIO_CMD_WRITE                        0x0
-+#define MDIO_DATA_DONE_MASK           0x80000000
-+#define MDIO_RD_DONE(x)                       (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0)
-+#define MDIO_WT_DONE(x)                       (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1)
-+#define SSC_REGS_ADDR                 0x1100
-+#define SET_ADDR_OFFSET                       0x1f
-+#define SSC_CNTL_OFFSET                       0x2
-+#define SSC_CNTL_OVRD_EN_MASK         0x8000
-+#define SSC_CNTL_OVRD_VAL_MASK                0x4000
-+#define SSC_STATUS_OFFSET             0x1
-+#define SSC_STATUS_SSC_MASK           0x400
-+#define SSC_STATUS_PLL_LOCK_MASK      0x800
-+
-+/* Internal PCIe Host Controller Information.*/
-+struct brcm_pcie {
-+      struct device           *dev;
-+      void __iomem            *base;
-+      struct clk              *clk;
-+      struct pci_bus          *root_bus;
-+      struct device_node      *np;
-+      bool                    ssc;
-+      int                     gen;
-+};
-+
-+/*
-+ * This is to convert the size of the inbound "BAR" region to the
-+ * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
-+ */
-+static int brcm_pcie_encode_ibar_size(u64 size)
-+{
-+      int log2_in = ilog2(size);
-+
-+      if (log2_in >= 12 && log2_in <= 15)
-+              /* Covers 4KB to 32KB (inclusive) */
-+              return (log2_in - 12) + 0x1c;
-+      else if (log2_in >= 16 && log2_in <= 35)
-+              /* Covers 64KB to 32GB, (inclusive) */
-+              return log2_in - 15;
-+      /* Something is awry so disable */
-+      return 0;
-+}
-+
-+static u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
-+{
-+      u32 pkt = 0;
-+
-+      pkt |= FIELD_PREP(MDIO_PORT_MASK, port);
-+      pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad);
-+      pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd);
-+
-+      return pkt;
-+}
-+
-+/* negative return value indicates error */
-+static int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
-+{
-+      int tries;
-+      u32 data;
-+
-+      writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
-+                 base + PCIE_RC_DL_MDIO_ADDR);
-+      readl(base + PCIE_RC_DL_MDIO_ADDR);
-+
-+      data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
-+      for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) {
-+              udelay(10);
-+              data = readl(base + PCIE_RC_DL_MDIO_RD_DATA);
-+      }
-+
-+      *val = FIELD_GET(MDIO_DATA_MASK, data);
-+      return MDIO_RD_DONE(data) ? 0 : -EIO;
-+}
-+
-+/* negative return value indicates error */
-+static int brcm_pcie_mdio_write(void __iomem *base, u8 port,
-+                              u8 regad, u16 wrdata)
-+{
-+      int tries;
-+      u32 data;
-+
-+      writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
-+                 base + PCIE_RC_DL_MDIO_ADDR);
-+      readl(base + PCIE_RC_DL_MDIO_ADDR);
-+      writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
-+
-+      data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
-+      for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) {
-+              udelay(10);
-+              data = readl(base + PCIE_RC_DL_MDIO_WR_DATA);
-+      }
-+
-+      return MDIO_WT_DONE(data) ? 0 : -EIO;
-+}
-+
-+/*
-+ * Configures device for Spread Spectrum Clocking (SSC) mode; a negative
-+ * return value indicates error.
-+ */
-+static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
-+{
-+      int pll, ssc;
-+      int ret;
-+      u32 tmp;
-+
-+      ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
-+                                 SSC_REGS_ADDR);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
-+                                SSC_CNTL_OFFSET, &tmp);
-+      if (ret < 0)
-+              return ret;
-+
-+      u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK);
-+      u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK);
-+      ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0,
-+                                 SSC_CNTL_OFFSET, tmp);
-+      if (ret < 0)
-+              return ret;
-+
-+      usleep_range(1000, 2000);
-+      ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
-+                                SSC_STATUS_OFFSET, &tmp);
-+      if (ret < 0)
-+              return ret;
-+
-+      ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp);
-+      pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp);
-+
-+      return ssc && pll ? 0 : -EIO;
-+}
-+
-+/* Limits operation to a specific generation (1, 2, or 3) */
-+static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
-+{
-+      u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
-+      u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
-+
-+      lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen;
-+      writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
-+
-+      lnkctl2 = (lnkctl2 & ~0xf) | gen;
-+      writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
-+}
-+
-+static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
-+                                     unsigned int win, u64 cpu_addr,
-+                                     u64 pcie_addr, u64 size)
-+{
-+      u32 cpu_addr_mb_high, limit_addr_mb_high;
-+      phys_addr_t cpu_addr_mb, limit_addr_mb;
-+      int high_addr_shift;
-+      u32 tmp;
-+
-+      /* Set the base of the pcie_addr window */
-+      writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win));
-+      writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win));
-+
-+      /* Write the addr base & limit lower bits (in MBs) */
-+      cpu_addr_mb = cpu_addr / SZ_1M;
-+      limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
-+
-+      tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
-+      u32p_replace_bits(&tmp, cpu_addr_mb,
-+                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
-+      u32p_replace_bits(&tmp, limit_addr_mb,
-+                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
-+      writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
-+
-+      /* Write the cpu & limit addr upper bits */
-+      high_addr_shift =
-+              HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
-+
-+      cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift;
-+      tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
-+      u32p_replace_bits(&tmp, cpu_addr_mb_high,
-+                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK);
-+      writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
-+
-+      limit_addr_mb_high = limit_addr_mb >> high_addr_shift;
-+      tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
-+      u32p_replace_bits(&tmp, limit_addr_mb_high,
-+                        PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK);
-+      writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
-+}
-+
-+/* The controller is capable of serving in both RC and EP roles */
-+static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
-+{
-+      void __iomem *base = pcie->base;
-+      u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
-+
-+      return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
-+}
-+
-+static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
-+{
-+      u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
-+      u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val);
-+      u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val);
-+
-+      return dla && plu;
-+}
-+
-+/* Configuration space read/write support */
-+static inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg)
-+{
-+      return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT)
-+              | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT)
-+              | (busnr << PCIE_EXT_BUSNUM_SHIFT)
-+              | (reg & ~3);
-+}
-+
-+static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
-+                                      int where)
-+{
-+      struct brcm_pcie *pcie = bus->sysdata;
-+      void __iomem *base = pcie->base;
-+      int idx;
-+
-+      /* Accesses to the RC go right to the RC registers if slot==0 */
-+      if (pci_is_root_bus(bus))
-+              return PCI_SLOT(devfn) ? NULL : base + where;
-+
-+      /* For devices, write to the config space index register */
-+      idx = brcm_pcie_cfg_index(bus->number, devfn, 0);
-+      writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
-+      return base + PCIE_EXT_CFG_DATA + where;
-+}
-+
-+static struct pci_ops brcm_pcie_ops = {
-+      .map_bus = brcm_pcie_map_conf,
-+      .read = pci_generic_config_read,
-+      .write = pci_generic_config_write,
-+};
-+
-+static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
-+{
-+      u32 tmp;
-+
-+      tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
-+      u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK);
-+      writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
-+}
-+
-+static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val)
-+{
-+      u32 tmp;
-+
-+      tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
-+      u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
-+      writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
-+}
-+
-+static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
-+                                                      u64 *rc_bar2_size,
-+                                                      u64 *rc_bar2_offset)
-+{
-+      struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
-+      struct device *dev = pcie->dev;
-+      struct resource_entry *entry;
-+
-+      entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
-+      if (!entry)
-+              return -ENODEV;
-+
-+
-+      /*
-+       * The controller expects the inbound window offset to be calculated as
-+       * the difference between PCIe's address space and CPU's. The offset
-+       * provided by the firmware is calculated the opposite way, so we
-+       * negate it.
-+       */
-+      *rc_bar2_offset = -entry->offset;
-+      *rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start);
-+
-+      /*
-+       * We validate the inbound memory view even though we should trust
-+       * whatever the device-tree provides. This is because of an HW issue on
-+       * early Raspberry Pi 4's revisions (bcm2711). It turns out its
-+       * firmware has to dynamically edit dma-ranges due to a bug on the
-+       * PCIe controller integration, which prohibits any access above the
-+       * lower 3GB of memory. Given this, we decided to keep the dma-ranges
-+       * in check, avoiding hard to debug device-tree related issues in the
-+       * future:
-+       *
-+       * The PCIe host controller by design must set the inbound viewport to
-+       * be a contiguous arrangement of all of the system's memory.  In
-+       * addition, its size mut be a power of two.  To further complicate
-+       * matters, the viewport must start on a pcie-address that is aligned
-+       * on a multiple of its size.  If a portion of the viewport does not
-+       * represent system memory -- e.g. 3GB of memory requires a 4GB
-+       * viewport -- we can map the outbound memory in or after 3GB and even
-+       * though the viewport will overlap the outbound memory the controller
-+       * will know to send outbound memory downstream and everything else
-+       * upstream.
-+       *
-+       * For example:
-+       *
-+       * - The best-case scenario, memory up to 3GB, is to place the inbound
-+       *   region in the first 4GB of pcie-space, as some legacy devices can
-+       *   only address 32bits. We would also like to put the MSI under 4GB
-+       *   as well, since some devices require a 32bit MSI target address.
-+       *
-+       * - If the system memory is 4GB or larger we cannot start the inbound
-+       *   region at location 0 (since we have to allow some space for
-+       *   outbound memory @ 3GB). So instead it will  start at the 1x
-+       *   multiple of its size
-+       */
-+      if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
-+          (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
-+              dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
-+                      *rc_bar2_size, *rc_bar2_offset);
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static int brcm_pcie_setup(struct brcm_pcie *pcie)
-+{
-+      struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
-+      u64 rc_bar2_offset, rc_bar2_size;
-+      void __iomem *base = pcie->base;
-+      struct device *dev = pcie->dev;
-+      struct resource_entry *entry;
-+      unsigned int scb_size_val;
-+      bool ssc_good = false;
-+      struct resource *res;
-+      int num_out_wins = 0;
-+      u16 nlw, cls, lnksta;
-+      int i, ret;
-+      u32 tmp;
-+
-+      /* Reset the bridge */
-+      brcm_pcie_bridge_sw_init_set(pcie, 1);
-+
-+      usleep_range(100, 200);
-+
-+      /* Take the bridge out of reset */
-+      brcm_pcie_bridge_sw_init_set(pcie, 0);
-+
-+      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+      tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
-+      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+      /* Wait for SerDes to be stable */
-+      usleep_range(100, 200);
-+
-+      /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
-+      u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
-+      u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
-+      u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128,
-+                        PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
-+      writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+
-+      ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
-+                                                  &rc_bar2_offset);
-+      if (ret)
-+              return ret;
-+
-+      tmp = lower_32_bits(rc_bar2_offset);
-+      u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
-+                        PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
-+      writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
-+      writel(upper_32_bits(rc_bar2_offset),
-+             base + PCIE_MISC_RC_BAR2_CONFIG_HI);
-+
-+      scb_size_val = rc_bar2_size ?
-+                     ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
-+      tmp = readl(base + PCIE_MISC_MISC_CTRL);
-+      u32p_replace_bits(&tmp, scb_size_val,
-+                        PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
-+      writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+
-+      /* disable the PCIe->GISB memory window (RC_BAR1) */
-+      tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-+      tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
-+      writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-+
-+      /* disable the PCIe->SCB memory window (RC_BAR3) */
-+      tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
-+      tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
-+      writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
-+
-+      /* Mask all interrupts since we are not handling any yet */
-+      writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET);
-+
-+      /* clear any interrupts we find on boot */
-+      writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR);
-+
-+      if (pcie->gen)
-+              brcm_pcie_set_gen(pcie, pcie->gen);
-+
-+      /* Unassert the fundamental reset */
-+      brcm_pcie_perst_set(pcie, 0);
-+
-+      /*
-+       * Give the RC/EP time to wake up, before trying to configure RC.
-+       * Intermittently check status for link-up, up to a total of 100ms.
-+       */
-+      for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
-+              msleep(5);
-+
-+      if (!brcm_pcie_link_up(pcie)) {
-+              dev_err(dev, "link down\n");
-+              return -ENODEV;
-+      }
-+
-+      if (!brcm_pcie_rc_mode(pcie)) {
-+              dev_err(dev, "PCIe misconfigured; is in EP mode\n");
-+              return -EINVAL;
-+      }
-+
-+      resource_list_for_each_entry(entry, &bridge->windows) {
-+              res = entry->res;
-+
-+              if (resource_type(res) != IORESOURCE_MEM)
-+                      continue;
-+
-+              if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) {
-+                      dev_err(pcie->dev, "too many outbound wins\n");
-+                      return -EINVAL;
-+              }
-+
-+              brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,
-+                                         res->start - entry->offset,
-+                                         resource_size(res));
-+              num_out_wins++;
-+      }
-+
-+      /*
-+       * For config space accesses on the RC, show the right class for
-+       * a PCIe-PCIe bridge (the default setting is to be EP mode).
-+       */
-+      tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-+      u32p_replace_bits(&tmp, 0x060400,
-+                        PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
-+      writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
-+
-+      if (pcie->ssc) {
-+              ret = brcm_pcie_set_ssc(pcie);
-+              if (ret == 0)
-+                      ssc_good = true;
-+              else
-+                      dev_err(dev, "failed attempt to enter ssc mode\n");
-+      }
-+
-+      lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
-+      cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
-+      nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
-+      dev_info(dev, "link up, %s x%u %s\n",
-+               PCIE_SPEED2STR(cls + PCI_SPEED_133MHz_PCIX_533),
-+               nlw, ssc_good ? "(SSC)" : "(!SSC)");
-+
-+      /* PCIe->SCB endian mode for BAR */
-+      tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
-+      u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN,
-+              PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
-+      writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
-+
-+      /*
-+       * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
-+       * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
-+       */
-+      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+      tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
-+      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+
-+      return 0;
-+}
-+
-+/* L23 is a low-power PCIe link state */
-+static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
-+{
-+      void __iomem *base = pcie->base;
-+      int l23, i;
-+      u32 tmp;
-+
-+      /* Assert request for L23 */
-+      tmp = readl(base + PCIE_MISC_PCIE_CTRL);
-+      u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
-+      writel(tmp, base + PCIE_MISC_PCIE_CTRL);
-+
-+      /* Wait up to 36 msec for L23 */
-+      tmp = readl(base + PCIE_MISC_PCIE_STATUS);
-+      l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp);
-+      for (i = 0; i < 15 && !l23; i++) {
-+              usleep_range(2000, 2400);
-+              tmp = readl(base + PCIE_MISC_PCIE_STATUS);
-+              l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK,
-+                              tmp);
-+      }
-+
-+      if (!l23)
-+              dev_err(pcie->dev, "failed to enter low-power link state\n");
-+}
-+
-+static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
-+{
-+      void __iomem *base = pcie->base;
-+      int tmp;
-+
-+      if (brcm_pcie_link_up(pcie))
-+              brcm_pcie_enter_l23(pcie);
-+      /* Assert fundamental reset */
-+      brcm_pcie_perst_set(pcie, 1);
-+
-+      /* Deassert request for L23 in case it was asserted */
-+      tmp = readl(base + PCIE_MISC_PCIE_CTRL);
-+      u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
-+      writel(tmp, base + PCIE_MISC_PCIE_CTRL);
-+
-+      /* Turn off SerDes */
-+      tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+      u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
-+      writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
-+
-+      /* Shutdown PCIe bridge */
-+      brcm_pcie_bridge_sw_init_set(pcie, 1);
-+}
-+
-+static void __brcm_pcie_remove(struct brcm_pcie *pcie)
-+{
-+      brcm_pcie_turn_off(pcie);
-+      clk_disable_unprepare(pcie->clk);
-+      clk_put(pcie->clk);
-+}
-+
-+static int brcm_pcie_remove(struct platform_device *pdev)
-+{
-+      struct brcm_pcie *pcie = platform_get_drvdata(pdev);
-+
-+      pci_stop_root_bus(pcie->root_bus);
-+      pci_remove_root_bus(pcie->root_bus);
-+      __brcm_pcie_remove(pcie);
-+
-+      return 0;
-+}
-+
-+static int brcm_pcie_probe(struct platform_device *pdev)
-+{
-+      struct device_node *np = pdev->dev.of_node;
-+      struct pci_host_bridge *bridge;
-+      struct brcm_pcie *pcie;
-+      struct pci_bus *child;
-+      struct resource *res;
-+      int ret;
-+
-+      bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
-+      if (!bridge)
-+              return -ENOMEM;
-+
-+      pcie = pci_host_bridge_priv(bridge);
-+      pcie->dev = &pdev->dev;
-+      pcie->np = np;
-+
-+      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      pcie->base = devm_ioremap_resource(&pdev->dev, res);
-+      if (IS_ERR(pcie->base))
-+              return PTR_ERR(pcie->base);
-+
-+      pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie");
-+      if (IS_ERR(pcie->clk))
-+              return PTR_ERR(pcie->clk);
-+
-+      ret = of_pci_get_max_link_speed(np);
-+      pcie->gen = (ret < 0) ? 0 : ret;
-+
-+      pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
-+
-+      ret = pci_parse_request_of_pci_ranges(pcie->dev, &bridge->windows,
-+                                            &bridge->dma_ranges, NULL);
-+      if (ret)
-+              return ret;
-+
-+      ret = clk_prepare_enable(pcie->clk);
-+      if (ret) {
-+              dev_err(&pdev->dev, "could not enable clock\n");
-+              return ret;
-+      }
-+
-+      ret = brcm_pcie_setup(pcie);
-+      if (ret)
-+              goto fail;
-+
-+      bridge->dev.parent = &pdev->dev;
-+      bridge->busnr = 0;
-+      bridge->ops = &brcm_pcie_ops;
-+      bridge->sysdata = pcie;
-+      bridge->map_irq = of_irq_parse_and_map_pci;
-+      bridge->swizzle_irq = pci_common_swizzle;
-+
-+      ret = pci_scan_root_bus_bridge(bridge);
-+      if (ret < 0) {
-+              dev_err(pcie->dev, "Scanning root bridge failed\n");
-+              goto fail;
-+      }
-+
-+      pci_assign_unassigned_bus_resources(bridge->bus);
-+      list_for_each_entry(child, &bridge->bus->children, node)
-+              pcie_bus_configure_settings(child);
-+      pci_bus_add_devices(bridge->bus);
-+      platform_set_drvdata(pdev, pcie);
-+      pcie->root_bus = bridge->bus;
-+
-+      return 0;
-+fail:
-+      __brcm_pcie_remove(pcie);
-+      return ret;
-+}
-+
-+static const struct of_device_id brcm_pcie_match[] = {
-+      { .compatible = "brcm,bcm2711-pcie" },
-+      {},
-+};
-+MODULE_DEVICE_TABLE(of, brcm_pcie_match);
-+
-+static struct platform_driver brcm_pcie_driver = {
-+      .probe = brcm_pcie_probe,
-+      .remove = brcm_pcie_remove,
-+      .driver = {
-+              .name = "brcm-pcie",
-+              .of_match_table = brcm_pcie_match,
-+      },
-+};
-+module_platform_driver(brcm_pcie_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("Broadcom STB PCIe RC driver");
-+MODULE_AUTHOR("Broadcom");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0459-PCI-brcmstb-Add-MSI-support.patch
new file mode 100644 (file)
index 0000000..a27259b
--- /dev/null
@@ -0,0 +1,383 @@
+From 1a90ecdfae1c0cf1b242276f6f0e3d98b5877f14 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+Date: Mon, 16 Dec 2019 12:01:10 +0100
+Subject: [PATCH] PCI: brcmstb: Add MSI support
+
+commit 40ca1bf580ef24df30702032ba5e40dfdcaa200b upstream.
+
+This adds MSI support to the Broadcom STB PCIe host controller. The MSI
+controller is physically located within the PCIe block, however, there
+is no reason why the MSI controller could not be moved elsewhere in the
+future. MSIX is not supported by the HW.
+
+Since the internal Brcmstb MSI controller is intertwined with the PCIe
+controller, it is not its own platform device but rather part of the
+PCIe platform device.
+
+Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
+Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Reviewed-by: Andrew Murray <andrew.murray@arm.com>
+---
+ drivers/pci/controller/Kconfig        |   1 +
+ drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++-
+ 2 files changed, 262 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -285,6 +285,7 @@ config PCIE_BRCMSTB
+       tristate "Broadcom Brcmstb PCIe host controller"
+       depends on ARCH_BCM2835 || COMPILE_TEST
+       depends on OF
++      depends on PCI_MSI_IRQ_DOMAIN
+       help
+         Say Y here to enable PCIe host controller support for
+         Broadcom STB based SoCs, like the Raspberry Pi 4.
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -2,6 +2,7 @@
+ /* Copyright (C) 2009 - 2019 Broadcom */
+ #include <linux/bitfield.h>
++#include <linux/bitops.h>
+ #include <linux/clk.h>
+ #include <linux/compiler.h>
+ #include <linux/delay.h>
+@@ -9,11 +10,13 @@
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/ioport.h>
++#include <linux/irqchip/chained_irq.h>
+ #include <linux/irqdomain.h>
+ #include <linux/kernel.h>
+ #include <linux/list.h>
+ #include <linux/log2.h>
+ #include <linux/module.h>
++#include <linux/msi.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_pci.h>
+@@ -67,6 +70,12 @@
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
+ #define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK                0x1f
++#define PCIE_MISC_MSI_BAR_CONFIG_LO                   0x4044
++#define PCIE_MISC_MSI_BAR_CONFIG_HI                   0x4048
++
++#define PCIE_MISC_MSI_DATA_CONFIG                     0x404c
++#define  PCIE_MISC_MSI_DATA_CONFIG_VAL                        0xffe06540
++
+ #define PCIE_MISC_PCIE_CTRL                           0x4064
+ #define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK    0x1
+@@ -114,6 +123,11 @@
+ /* PCIe parameters */
+ #define BRCM_NUM_PCIE_OUT_WINS                0x4
++#define BRCM_INT_PCI_MSI_NR           32
++
++/* MSI target adresses */
++#define BRCM_MSI_TARGET_ADDR_LT_4GB   0x0fffffffcULL
++#define BRCM_MSI_TARGET_ADDR_GT_4GB   0xffffffffcULL
+ /* MDIO registers */
+ #define MDIO_PORT0                    0x0
+@@ -135,6 +149,19 @@
+ #define SSC_STATUS_SSC_MASK           0x400
+ #define SSC_STATUS_PLL_LOCK_MASK      0x800
++struct brcm_msi {
++      struct device           *dev;
++      void __iomem            *base;
++      struct device_node      *np;
++      struct irq_domain       *msi_domain;
++      struct irq_domain       *inner_domain;
++      struct mutex            lock; /* guards the alloc/free operations */
++      u64                     target_addr;
++      int                     irq;
++      /* used indicates which MSI interrupts have been alloc'd */
++      unsigned long           used;
++};
++
+ /* Internal PCIe Host Controller Information.*/
+ struct brcm_pcie {
+       struct device           *dev;
+@@ -144,6 +171,8 @@ struct brcm_pcie {
+       struct device_node      *np;
+       bool                    ssc;
+       int                     gen;
++      u64                     msi_target_addr;
++      struct brcm_msi         *msi;
+ };
+ /*
+@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(s
+       writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
+ }
++static struct irq_chip brcm_msi_irq_chip = {
++      .name            = "BRCM STB PCIe MSI",
++      .irq_ack         = irq_chip_ack_parent,
++      .irq_mask        = pci_msi_mask_irq,
++      .irq_unmask      = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info brcm_msi_domain_info = {
++      /* Multi MSI is supported by the controller, but not by this driver */
++      .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
++      .chip   = &brcm_msi_irq_chip,
++};
++
++static void brcm_pcie_msi_isr(struct irq_desc *desc)
++{
++      struct irq_chip *chip = irq_desc_get_chip(desc);
++      unsigned long status, virq;
++      struct brcm_msi *msi;
++      struct device *dev;
++      u32 bit;
++
++      chained_irq_enter(chip, desc);
++      msi = irq_desc_get_handler_data(desc);
++      dev = msi->dev;
++
++      status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
++      for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
++              virq = irq_find_mapping(msi->inner_domain, bit);
++              if (virq)
++                      generic_handle_irq(virq);
++              else
++                      dev_dbg(dev, "unexpected MSI\n");
++      }
++
++      chained_irq_exit(chip, desc);
++}
++
++static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
++
++      msg->address_lo = lower_32_bits(msi->target_addr);
++      msg->address_hi = upper_32_bits(msi->target_addr);
++      msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
++}
++
++static int brcm_msi_set_affinity(struct irq_data *irq_data,
++                               const struct cpumask *mask, bool force)
++{
++      return -EINVAL;
++}
++
++static void brcm_msi_ack_irq(struct irq_data *data)
++{
++      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
++
++      writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
++}
++
++
++static struct irq_chip brcm_msi_bottom_irq_chip = {
++      .name                   = "BRCM STB MSI",
++      .irq_compose_msi_msg    = brcm_msi_compose_msi_msg,
++      .irq_set_affinity       = brcm_msi_set_affinity,
++      .irq_ack                = brcm_msi_ack_irq,
++};
++
++static int brcm_msi_alloc(struct brcm_msi *msi)
++{
++      int hwirq;
++
++      mutex_lock(&msi->lock);
++      hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
++      mutex_unlock(&msi->lock);
++
++      return hwirq;
++}
++
++static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
++{
++      mutex_lock(&msi->lock);
++      bitmap_release_region(&msi->used, hwirq, 0);
++      mutex_unlock(&msi->lock);
++}
++
++static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
++                               unsigned int nr_irqs, void *args)
++{
++      struct brcm_msi *msi = domain->host_data;
++      int hwirq;
++
++      hwirq = brcm_msi_alloc(msi);
++
++      if (hwirq < 0)
++              return hwirq;
++
++      irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
++                          &brcm_msi_bottom_irq_chip, domain->host_data,
++                          handle_edge_irq, NULL, NULL);
++      return 0;
++}
++
++static void brcm_irq_domain_free(struct irq_domain *domain,
++                               unsigned int virq, unsigned int nr_irqs)
++{
++      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++      struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
++
++      brcm_msi_free(msi, d->hwirq);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++      .alloc  = brcm_irq_domain_alloc,
++      .free   = brcm_irq_domain_free,
++};
++
++static int brcm_allocate_domains(struct brcm_msi *msi)
++{
++      struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
++      struct device *dev = msi->dev;
++
++      msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
++                                                &msi_domain_ops, msi);
++      if (!msi->inner_domain) {
++              dev_err(dev, "failed to create IRQ domain\n");
++              return -ENOMEM;
++      }
++
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
++                                                  &brcm_msi_domain_info,
++                                                  msi->inner_domain);
++      if (!msi->msi_domain) {
++              dev_err(dev, "failed to create MSI domain\n");
++              irq_domain_remove(msi->inner_domain);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void brcm_free_domains(struct brcm_msi *msi)
++{
++      irq_domain_remove(msi->msi_domain);
++      irq_domain_remove(msi->inner_domain);
++}
++
++static void brcm_msi_remove(struct brcm_pcie *pcie)
++{
++      struct brcm_msi *msi = pcie->msi;
++
++      if (!msi)
++              return;
++      irq_set_chained_handler(msi->irq, NULL);
++      irq_set_handler_data(msi->irq, NULL);
++      brcm_free_domains(msi);
++}
++
++static void brcm_msi_set_regs(struct brcm_msi *msi)
++{
++      writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
++
++      /*
++       * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
++       * enable, which we set to 1.
++       */
++      writel(lower_32_bits(msi->target_addr) | 0x1,
++             msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
++      writel(upper_32_bits(msi->target_addr),
++             msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
++
++      writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
++             msi->base + PCIE_MISC_MSI_DATA_CONFIG);
++}
++
++static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
++{
++      struct brcm_msi *msi;
++      int irq, ret;
++      struct device *dev = pcie->dev;
++
++      irq = irq_of_parse_and_map(dev->of_node, 1);
++      if (irq <= 0) {
++              dev_err(dev, "cannot map MSI interrupt\n");
++              return -ENODEV;
++      }
++
++      msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
++      if (!msi)
++              return -ENOMEM;
++
++      mutex_init(&msi->lock);
++      msi->dev = dev;
++      msi->base = pcie->base;
++      msi->np = pcie->np;
++      msi->target_addr = pcie->msi_target_addr;
++      msi->irq = irq;
++
++      ret = brcm_allocate_domains(msi);
++      if (ret)
++              return ret;
++
++      irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
++
++      brcm_msi_set_regs(msi);
++      pcie->msi = msi;
++
++      return 0;
++}
++
+ /* The controller is capable of serving in both RC and EP roles */
+ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
+ {
+@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_p
+                         PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
+       writel(tmp, base + PCIE_MISC_MISC_CTRL);
++      /*
++       * We ideally want the MSI target address to be located in the 32bit
++       * addressable memory area. Some devices might depend on it. This is
++       * possible either when the inbound window is located above the lower
++       * 4GB or when the inbound area is smaller than 4GB (taking into
++       * account the rounding-up we're forced to perform).
++       */
++      if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
++              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
++      else
++              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
++
+       /* disable the PCIe->GISB memory window (RC_BAR1) */
+       tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
+       tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
+@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct br
+ static void __brcm_pcie_remove(struct brcm_pcie *pcie)
+ {
++      brcm_msi_remove(pcie);
+       brcm_pcie_turn_off(pcie);
+       clk_disable_unprepare(pcie->clk);
+       clk_put(pcie->clk);
+@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platf
+ static int brcm_pcie_probe(struct platform_device *pdev)
+ {
+-      struct device_node *np = pdev->dev.of_node;
++      struct device_node *np = pdev->dev.of_node, *msi_np;
+       struct pci_host_bridge *bridge;
+       struct brcm_pcie *pcie;
+       struct pci_bus *child;
+@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platfo
+       if (ret)
+               goto fail;
++      msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
++      if (pci_msi_enabled() && msi_np == pcie->np) {
++              ret = brcm_pcie_enable_msi(pcie);
++              if (ret) {
++                      dev_err(pcie->dev, "probe of internal MSI failed");
++                      goto fail;
++              }
++      }
++
+       bridge->dev.parent = &pdev->dev;
+       bridge->busnr = 0;
+       bridge->ops = &brcm_pcie_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Add-MSI-support.patch b/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Add-MSI-support.patch
deleted file mode 100644 (file)
index a27259b..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-From 1a90ecdfae1c0cf1b242276f6f0e3d98b5877f14 Mon Sep 17 00:00:00 2001
-From: Jim Quinlan <james.quinlan@broadcom.com>
-Date: Mon, 16 Dec 2019 12:01:10 +0100
-Subject: [PATCH] PCI: brcmstb: Add MSI support
-
-commit 40ca1bf580ef24df30702032ba5e40dfdcaa200b upstream.
-
-This adds MSI support to the Broadcom STB PCIe host controller. The MSI
-controller is physically located within the PCIe block, however, there
-is no reason why the MSI controller could not be moved elsewhere in the
-future. MSIX is not supported by the HW.
-
-Since the internal Brcmstb MSI controller is intertwined with the PCIe
-controller, it is not its own platform device but rather part of the
-PCIe platform device.
-
-Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
-Co-developed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-Reviewed-by: Marc Zyngier <maz@kernel.org>
-Reviewed-by: Andrew Murray <andrew.murray@arm.com>
----
- drivers/pci/controller/Kconfig        |   1 +
- drivers/pci/controller/pcie-brcmstb.c | 262 +++++++++++++++++++++++++-
- 2 files changed, 262 insertions(+), 1 deletion(-)
-
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -285,6 +285,7 @@ config PCIE_BRCMSTB
-       tristate "Broadcom Brcmstb PCIe host controller"
-       depends on ARCH_BCM2835 || COMPILE_TEST
-       depends on OF
-+      depends on PCI_MSI_IRQ_DOMAIN
-       help
-         Say Y here to enable PCIe host controller support for
-         Broadcom STB based SoCs, like the Raspberry Pi 4.
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -2,6 +2,7 @@
- /* Copyright (C) 2009 - 2019 Broadcom */
- #include <linux/bitfield.h>
-+#include <linux/bitops.h>
- #include <linux/clk.h>
- #include <linux/compiler.h>
- #include <linux/delay.h>
-@@ -9,11 +10,13 @@
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
-+#include <linux/irqchip/chained_irq.h>
- #include <linux/irqdomain.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/log2.h>
- #include <linux/module.h>
-+#include <linux/msi.h>
- #include <linux/of_address.h>
- #include <linux/of_irq.h>
- #include <linux/of_pci.h>
-@@ -67,6 +70,12 @@
- #define PCIE_MISC_RC_BAR3_CONFIG_LO                   0x403c
- #define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK                0x1f
-+#define PCIE_MISC_MSI_BAR_CONFIG_LO                   0x4044
-+#define PCIE_MISC_MSI_BAR_CONFIG_HI                   0x4048
-+
-+#define PCIE_MISC_MSI_DATA_CONFIG                     0x404c
-+#define  PCIE_MISC_MSI_DATA_CONFIG_VAL                        0xffe06540
-+
- #define PCIE_MISC_PCIE_CTRL                           0x4064
- #define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK    0x1
-@@ -114,6 +123,11 @@
- /* PCIe parameters */
- #define BRCM_NUM_PCIE_OUT_WINS                0x4
-+#define BRCM_INT_PCI_MSI_NR           32
-+
-+/* MSI target adresses */
-+#define BRCM_MSI_TARGET_ADDR_LT_4GB   0x0fffffffcULL
-+#define BRCM_MSI_TARGET_ADDR_GT_4GB   0xffffffffcULL
- /* MDIO registers */
- #define MDIO_PORT0                    0x0
-@@ -135,6 +149,19 @@
- #define SSC_STATUS_SSC_MASK           0x400
- #define SSC_STATUS_PLL_LOCK_MASK      0x800
-+struct brcm_msi {
-+      struct device           *dev;
-+      void __iomem            *base;
-+      struct device_node      *np;
-+      struct irq_domain       *msi_domain;
-+      struct irq_domain       *inner_domain;
-+      struct mutex            lock; /* guards the alloc/free operations */
-+      u64                     target_addr;
-+      int                     irq;
-+      /* used indicates which MSI interrupts have been alloc'd */
-+      unsigned long           used;
-+};
-+
- /* Internal PCIe Host Controller Information.*/
- struct brcm_pcie {
-       struct device           *dev;
-@@ -144,6 +171,8 @@ struct brcm_pcie {
-       struct device_node      *np;
-       bool                    ssc;
-       int                     gen;
-+      u64                     msi_target_addr;
-+      struct brcm_msi         *msi;
- };
- /*
-@@ -309,6 +338,215 @@ static void brcm_pcie_set_outbound_win(s
-       writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
- }
-+static struct irq_chip brcm_msi_irq_chip = {
-+      .name            = "BRCM STB PCIe MSI",
-+      .irq_ack         = irq_chip_ack_parent,
-+      .irq_mask        = pci_msi_mask_irq,
-+      .irq_unmask      = pci_msi_unmask_irq,
-+};
-+
-+static struct msi_domain_info brcm_msi_domain_info = {
-+      /* Multi MSI is supported by the controller, but not by this driver */
-+      .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
-+      .chip   = &brcm_msi_irq_chip,
-+};
-+
-+static void brcm_pcie_msi_isr(struct irq_desc *desc)
-+{
-+      struct irq_chip *chip = irq_desc_get_chip(desc);
-+      unsigned long status, virq;
-+      struct brcm_msi *msi;
-+      struct device *dev;
-+      u32 bit;
-+
-+      chained_irq_enter(chip, desc);
-+      msi = irq_desc_get_handler_data(desc);
-+      dev = msi->dev;
-+
-+      status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
-+      for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
-+              virq = irq_find_mapping(msi->inner_domain, bit);
-+              if (virq)
-+                      generic_handle_irq(virq);
-+              else
-+                      dev_dbg(dev, "unexpected MSI\n");
-+      }
-+
-+      chained_irq_exit(chip, desc);
-+}
-+
-+static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
-+{
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+      msg->address_lo = lower_32_bits(msi->target_addr);
-+      msg->address_hi = upper_32_bits(msi->target_addr);
-+      msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
-+}
-+
-+static int brcm_msi_set_affinity(struct irq_data *irq_data,
-+                               const struct cpumask *mask, bool force)
-+{
-+      return -EINVAL;
-+}
-+
-+static void brcm_msi_ack_irq(struct irq_data *data)
-+{
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
-+
-+      writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
-+}
-+
-+
-+static struct irq_chip brcm_msi_bottom_irq_chip = {
-+      .name                   = "BRCM STB MSI",
-+      .irq_compose_msi_msg    = brcm_msi_compose_msi_msg,
-+      .irq_set_affinity       = brcm_msi_set_affinity,
-+      .irq_ack                = brcm_msi_ack_irq,
-+};
-+
-+static int brcm_msi_alloc(struct brcm_msi *msi)
-+{
-+      int hwirq;
-+
-+      mutex_lock(&msi->lock);
-+      hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
-+      mutex_unlock(&msi->lock);
-+
-+      return hwirq;
-+}
-+
-+static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)
-+{
-+      mutex_lock(&msi->lock);
-+      bitmap_release_region(&msi->used, hwirq, 0);
-+      mutex_unlock(&msi->lock);
-+}
-+
-+static int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
-+                               unsigned int nr_irqs, void *args)
-+{
-+      struct brcm_msi *msi = domain->host_data;
-+      int hwirq;
-+
-+      hwirq = brcm_msi_alloc(msi);
-+
-+      if (hwirq < 0)
-+              return hwirq;
-+
-+      irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq,
-+                          &brcm_msi_bottom_irq_chip, domain->host_data,
-+                          handle_edge_irq, NULL, NULL);
-+      return 0;
-+}
-+
-+static void brcm_irq_domain_free(struct irq_domain *domain,
-+                               unsigned int virq, unsigned int nr_irqs)
-+{
-+      struct irq_data *d = irq_domain_get_irq_data(domain, virq);
-+      struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
-+
-+      brcm_msi_free(msi, d->hwirq);
-+}
-+
-+static const struct irq_domain_ops msi_domain_ops = {
-+      .alloc  = brcm_irq_domain_alloc,
-+      .free   = brcm_irq_domain_free,
-+};
-+
-+static int brcm_allocate_domains(struct brcm_msi *msi)
-+{
-+      struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
-+      struct device *dev = msi->dev;
-+
-+      msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
-+                                                &msi_domain_ops, msi);
-+      if (!msi->inner_domain) {
-+              dev_err(dev, "failed to create IRQ domain\n");
-+              return -ENOMEM;
-+      }
-+
-+      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
-+                                                  &brcm_msi_domain_info,
-+                                                  msi->inner_domain);
-+      if (!msi->msi_domain) {
-+              dev_err(dev, "failed to create MSI domain\n");
-+              irq_domain_remove(msi->inner_domain);
-+              return -ENOMEM;
-+      }
-+
-+      return 0;
-+}
-+
-+static void brcm_free_domains(struct brcm_msi *msi)
-+{
-+      irq_domain_remove(msi->msi_domain);
-+      irq_domain_remove(msi->inner_domain);
-+}
-+
-+static void brcm_msi_remove(struct brcm_pcie *pcie)
-+{
-+      struct brcm_msi *msi = pcie->msi;
-+
-+      if (!msi)
-+              return;
-+      irq_set_chained_handler(msi->irq, NULL);
-+      irq_set_handler_data(msi->irq, NULL);
-+      brcm_free_domains(msi);
-+}
-+
-+static void brcm_msi_set_regs(struct brcm_msi *msi)
-+{
-+      writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
-+
-+      /*
-+       * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
-+       * enable, which we set to 1.
-+       */
-+      writel(lower_32_bits(msi->target_addr) | 0x1,
-+             msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
-+      writel(upper_32_bits(msi->target_addr),
-+             msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
-+
-+      writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
-+             msi->base + PCIE_MISC_MSI_DATA_CONFIG);
-+}
-+
-+static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
-+{
-+      struct brcm_msi *msi;
-+      int irq, ret;
-+      struct device *dev = pcie->dev;
-+
-+      irq = irq_of_parse_and_map(dev->of_node, 1);
-+      if (irq <= 0) {
-+              dev_err(dev, "cannot map MSI interrupt\n");
-+              return -ENODEV;
-+      }
-+
-+      msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
-+      if (!msi)
-+              return -ENOMEM;
-+
-+      mutex_init(&msi->lock);
-+      msi->dev = dev;
-+      msi->base = pcie->base;
-+      msi->np = pcie->np;
-+      msi->target_addr = pcie->msi_target_addr;
-+      msi->irq = irq;
-+
-+      ret = brcm_allocate_domains(msi);
-+      if (ret)
-+              return ret;
-+
-+      irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
-+
-+      brcm_msi_set_regs(msi);
-+      pcie->msi = msi;
-+
-+      return 0;
-+}
-+
- /* The controller is capable of serving in both RC and EP roles */
- static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
- {
-@@ -497,6 +735,18 @@ static int brcm_pcie_setup(struct brcm_p
-                         PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
-       writel(tmp, base + PCIE_MISC_MISC_CTRL);
-+      /*
-+       * We ideally want the MSI target address to be located in the 32bit
-+       * addressable memory area. Some devices might depend on it. This is
-+       * possible either when the inbound window is located above the lower
-+       * 4GB or when the inbound area is smaller than 4GB (taking into
-+       * account the rounding-up we're forced to perform).
-+       */
-+      if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
-+              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
-+      else
-+              pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
-+
-       /* disable the PCIe->GISB memory window (RC_BAR1) */
-       tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-       tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
-@@ -646,6 +896,7 @@ static void brcm_pcie_turn_off(struct br
- static void __brcm_pcie_remove(struct brcm_pcie *pcie)
- {
-+      brcm_msi_remove(pcie);
-       brcm_pcie_turn_off(pcie);
-       clk_disable_unprepare(pcie->clk);
-       clk_put(pcie->clk);
-@@ -664,7 +915,7 @@ static int brcm_pcie_remove(struct platf
- static int brcm_pcie_probe(struct platform_device *pdev)
- {
--      struct device_node *np = pdev->dev.of_node;
-+      struct device_node *np = pdev->dev.of_node, *msi_np;
-       struct pci_host_bridge *bridge;
-       struct brcm_pcie *pcie;
-       struct pci_bus *child;
-@@ -708,6 +959,15 @@ static int brcm_pcie_probe(struct platfo
-       if (ret)
-               goto fail;
-+      msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
-+      if (pci_msi_enabled() && msi_np == pcie->np) {
-+              ret = brcm_pcie_enable_msi(pcie);
-+              if (ret) {
-+                      dev_err(pcie->dev, "probe of internal MSI failed");
-+                      goto fail;
-+              }
-+      }
-+
-       bridge->dev.parent = &pdev->dev;
-       bridge->busnr = 0;
-       bridge->ops = &brcm_pcie_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch b/target/linux/bcm27xx/patches-5.4/950-0460-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
new file mode 100644 (file)
index 0000000..6bb45cc
--- /dev/null
@@ -0,0 +1,38 @@
+From 39192141aa16809323c24d8910e3a63488f7f55d Mon Sep 17 00:00:00 2001
+From: Marek Szyprowski <m.szyprowski@samsung.com>
+Date: Thu, 27 Feb 2020 12:51:46 +0100
+Subject: [PATCH] PCI: brcmstb: Fix build on 32bit ARM platforms with
+ older compilers
+
+commit 73a7a271b3eee7b83f29b13866163776f1cbef89 upstream.
+
+Some older compilers have no implementation for the helper for 64-bit
+unsigned division/modulo, so linking pcie-brcmstb driver causes the
+"undefined reference to `__aeabi_uldivmod'" error.
+
+*rc_bar2_size is always a power of two, because it is calculated as:
+"1ULL << fls64(entry->res->end - entry->res->start)", so the modulo
+operation in the subsequent check can be replaced by a simple logical
+AND with a proper mask.
+
+Link: https://lore.kernel.org/r/20200227115146.24515-1-m.szyprowski@samsung.com
+Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver")
+Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -670,7 +670,7 @@ static inline int brcm_pcie_get_rc_bar2_
+        *   outbound memory @ 3GB). So instead it will  start at the 1x
+        *   multiple of its size
+        */
+-      if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
++      if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
+           (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
+               dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
+                       *rc_bar2_size, *rc_bar2_offset);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0461-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch b/target/linux/bcm27xx/patches-5.4/950-0461-PCI-brcmstb-Fix-build-on-32bit-ARM-platforms-with-ol.patch
deleted file mode 100644 (file)
index 6bb45cc..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-From 39192141aa16809323c24d8910e3a63488f7f55d Mon Sep 17 00:00:00 2001
-From: Marek Szyprowski <m.szyprowski@samsung.com>
-Date: Thu, 27 Feb 2020 12:51:46 +0100
-Subject: [PATCH] PCI: brcmstb: Fix build on 32bit ARM platforms with
- older compilers
-
-commit 73a7a271b3eee7b83f29b13866163776f1cbef89 upstream.
-
-Some older compilers have no implementation for the helper for 64-bit
-unsigned division/modulo, so linking pcie-brcmstb driver causes the
-"undefined reference to `__aeabi_uldivmod'" error.
-
-*rc_bar2_size is always a power of two, because it is calculated as:
-"1ULL << fls64(entry->res->end - entry->res->start)", so the modulo
-operation in the subsequent check can be replaced by a simple logical
-AND with a proper mask.
-
-Link: https://lore.kernel.org/r/20200227115146.24515-1-m.szyprowski@samsung.com
-Fixes: c0452137034b ("PCI: brcmstb: Add Broadcom STB PCIe host controller driver")
-Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
-Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
----
- drivers/pci/controller/pcie-brcmstb.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/pci/controller/pcie-brcmstb.c
-+++ b/drivers/pci/controller/pcie-brcmstb.c
-@@ -670,7 +670,7 @@ static inline int brcm_pcie_get_rc_bar2_
-        *   outbound memory @ 3GB). So instead it will  start at the 1x
-        *   multiple of its size
-        */
--      if (!*rc_bar2_size || *rc_bar2_offset % *rc_bar2_size ||
-+      if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
-           (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
-               dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
-                       *rc_bar2_size, *rc_bar2_offset);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch b/target/linux/bcm27xx/patches-5.4/950-0461-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
new file mode 100644 (file)
index 0000000..729d6e6
--- /dev/null
@@ -0,0 +1,75 @@
+From 5e9b9f246802f492e7740ab2589aa8c81df5ef20 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 2 Mar 2020 15:05:25 +0000
+Subject: [PATCH] bcm2711-rpi.dtsi: Use upstream pcie node
+
+Now that the upstream bcm2711 DT has a pcie DT node there's no need to
+define one downstream.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts |  2 +-
+ arch/arm/boot/dts/bcm2711-rpi.dtsi    | 41 ---------------------------
+ 2 files changed, 1 insertion(+), 42 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -163,7 +163,7 @@
+               i2c6 = &i2c6;
+               /delete-property/ ethernet;
+               /delete-property/ intc;
+-              pcie0 = &pcie_0;
++              pcie0 = &pcie0;
+       };
+       /delete-node/ wifi-pwrseq;
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -66,47 +66,6 @@
+                <0x0 0x00000000  0x0 0x00000000  0xfc000000>;
+       dma-ranges = <0x0 0x00000000  0x0 0x00000000  0xfc000000>;
+-      pcie_0: pcie@7d500000 {
+-              reg = <0x0 0x7d500000 0x9310>,
+-                    <0x0 0x7e00f300 0x20>;
+-              msi-controller;
+-              msi-parent = <&pcie_0>;
+-              #address-cells = <3>;
+-              #interrupt-cells = <1>;
+-              #size-cells = <2>;
+-              bus-range = <0x0 0x01>;
+-              compatible = "brcm,bcm2711b0-pcie", // Safe value
+-                           "brcm,bcm2711-pcie",
+-                           "brcm,pci-plat-dev";
+-              max-link-speed = <2>;
+-              tot-num-pcie = <1>;
+-              linux,pci-domain = <0>;
+-              interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
+-                           <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+-              interrupt-names = "pcie", "msi";
+-              interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+-              interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
+-                                      IRQ_TYPE_LEVEL_HIGH
+-                               0 0 0 2 &gicv2 GIC_SPI 144
+-                                      IRQ_TYPE_LEVEL_HIGH
+-                               0 0 0 3 &gicv2 GIC_SPI 145
+-                                      IRQ_TYPE_LEVEL_HIGH
+-                               0 0 0 4 &gicv2 GIC_SPI 146
+-                                      IRQ_TYPE_LEVEL_HIGH>;
+-
+-              /* Map outbound accesses from scb:0x6_00000000-03ffffff
+-               * to pci:0x0_f8000000-fbffffff
+-               */
+-              ranges = <0x02000000 0x0 0xf8000000  0x6 0x00000000
+-                        0x0 0x04000000>;
+-              /* Map inbound accesses from pci:0x0_00000000..ffffffff
+-               * to scb:0x0_00000000-ffffffff
+-               */
+-              dma-ranges = <0x02000000 0x0 0x00000000  0x0 0x00000000
+-                            0x1 0x00000000>;
+-              status = "okay";
+-      };
+-
+       dma40: dma@7e007b00 {
+               compatible = "brcm,bcm2711-dma";
+               reg = <0x0 0x7e007b00 0x400>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0462-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch b/target/linux/bcm27xx/patches-5.4/950-0462-bcm2711-rpi.dtsi-Use-upstream-pcie-node.patch
deleted file mode 100644 (file)
index 729d6e6..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-From 5e9b9f246802f492e7740ab2589aa8c81df5ef20 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Mon, 2 Mar 2020 15:05:25 +0000
-Subject: [PATCH] bcm2711-rpi.dtsi: Use upstream pcie node
-
-Now that the upstream bcm2711 DT has a pcie DT node there's no need to
-define one downstream.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2711-rpi-4-b.dts |  2 +-
- arch/arm/boot/dts/bcm2711-rpi.dtsi    | 41 ---------------------------
- 2 files changed, 1 insertion(+), 42 deletions(-)
-
---- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
-+++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
-@@ -163,7 +163,7 @@
-               i2c6 = &i2c6;
-               /delete-property/ ethernet;
-               /delete-property/ intc;
--              pcie0 = &pcie_0;
-+              pcie0 = &pcie0;
-       };
-       /delete-node/ wifi-pwrseq;
---- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
-+++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
-@@ -66,47 +66,6 @@
-                <0x0 0x00000000  0x0 0x00000000  0xfc000000>;
-       dma-ranges = <0x0 0x00000000  0x0 0x00000000  0xfc000000>;
--      pcie_0: pcie@7d500000 {
--              reg = <0x0 0x7d500000 0x9310>,
--                    <0x0 0x7e00f300 0x20>;
--              msi-controller;
--              msi-parent = <&pcie_0>;
--              #address-cells = <3>;
--              #interrupt-cells = <1>;
--              #size-cells = <2>;
--              bus-range = <0x0 0x01>;
--              compatible = "brcm,bcm2711b0-pcie", // Safe value
--                           "brcm,bcm2711-pcie",
--                           "brcm,pci-plat-dev";
--              max-link-speed = <2>;
--              tot-num-pcie = <1>;
--              linux,pci-domain = <0>;
--              interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
--                           <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
--              interrupt-names = "pcie", "msi";
--              interrupt-map-mask = <0x0 0x0 0x0 0x7>;
--              interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
--                                      IRQ_TYPE_LEVEL_HIGH
--                               0 0 0 2 &gicv2 GIC_SPI 144
--                                      IRQ_TYPE_LEVEL_HIGH
--                               0 0 0 3 &gicv2 GIC_SPI 145
--                                      IRQ_TYPE_LEVEL_HIGH
--                               0 0 0 4 &gicv2 GIC_SPI 146
--                                      IRQ_TYPE_LEVEL_HIGH>;
--
--              /* Map outbound accesses from scb:0x6_00000000-03ffffff
--               * to pci:0x0_f8000000-fbffffff
--               */
--              ranges = <0x02000000 0x0 0xf8000000  0x6 0x00000000
--                        0x0 0x04000000>;
--              /* Map inbound accesses from pci:0x0_00000000..ffffffff
--               * to scb:0x0_00000000-ffffffff
--               */
--              dma-ranges = <0x02000000 0x0 0x00000000  0x0 0x00000000
--                            0x1 0x00000000>;
--              status = "okay";
--      };
--
-       dma40: dma@7e007b00 {
-               compatible = "brcm,bcm2711-dma";
-               reg = <0x0 0x7e007b00 0x400>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch b/target/linux/bcm27xx/patches-5.4/950-0462-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
new file mode 100644 (file)
index 0000000..16eaedd
--- /dev/null
@@ -0,0 +1,156 @@
+From a3ceeebaaa66e6786490e850b5019808da3785c0 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 20 Jan 2020 05:15:57 -0300
+Subject: [PATCH] media: dt-bindings: media: i2c: Add IMX219 CMOS
+ sensor binding
+
+Commit 9d730f2cf4c0391785855dd231577d2de2594df9 upstream.
+(Currently on linux-media/master, queued for 5.7)
+
+Add YAML device tree binding for IMX219 CMOS image sensor, and
+the relevant MAINTAINERS entries.
+
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
+ MAINTAINERS                                   |   8 ++
+ 2 files changed, 122 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
+@@ -0,0 +1,114 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
++
++maintainers:
++  - Dave Stevenson <dave.stevenson@raspberrypi.com>
++
++description: |-
++  The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
++  with an active array size of 3280H x 2464V. It is programmable through
++  I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
++  Image data is sent through MIPI CSI-2, which is configured as either 2 or
++  4 data lanes.
++
++properties:
++  compatible:
++    const: sony,imx219
++
++  reg:
++    description: I2C device address
++    maxItems: 1
++
++  clocks:
++    maxItems: 1
++
++  VDIG-supply:
++    description:
++      Digital I/O voltage supply, 1.8 volts
++
++  VANA-supply:
++    description:
++      Analog voltage supply, 2.8 volts
++
++  VDDL-supply:
++    description:
++      Digital core voltage supply, 1.2 volts
++
++  reset-gpios:
++    description: |-
++      Reference to the GPIO connected to the xclr pin, if any.
++      Must be released (set high) after all supplies are applied.
++
++  # See ../video-interfaces.txt for more details
++  port:
++    type: object
++    properties:
++      endpoint:
++        type: object
++        properties:
++          data-lanes:
++            description: |-
++              The sensor supports either two-lane, or four-lane operation.
++              If this property is omitted four-lane operation is assumed.
++              For two-lane operation the property must be set to <1 2>.
++            items:
++              - const: 1
++              - const: 2
++
++          clock-noncontinuous:
++            type: boolean
++            description: |-
++              MIPI CSI-2 clock is non-continuous if this property is present,
++              otherwise it's continuous.
++
++          link-frequencies:
++            allOf:
++              - $ref: /schemas/types.yaml#/definitions/uint64-array
++            description:
++              Allowed data bus frequencies.
++
++        required:
++          - link-frequencies
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - VANA-supply
++  - VDIG-supply
++  - VDDL-supply
++  - port
++
++additionalProperties: false
++
++examples:
++  - |
++    i2c0 {
++        #address-cells = <1>;
++        #size-cells = <0>;
++
++        imx219: sensor@10 {
++            compatible = "sony,imx219";
++            reg = <0x10>;
++            clocks = <&imx219_clk>;
++            VANA-supply = <&imx219_vana>;   /* 2.8v */
++            VDIG-supply = <&imx219_vdig>;   /* 1.8v */
++            VDDL-supply = <&imx219_vddl>;   /* 1.2v */
++
++            port {
++                imx219_0: endpoint {
++                    remote-endpoint = <&csi1_ep>;
++                    data-lanes = <1 2>;
++                    clock-noncontinuous;
++                    link-frequencies = /bits/ 64 <456000000>;
++                };
++            };
++        };
++    };
++
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15142,6 +15142,14 @@ S:    Maintained
+ F:    drivers/media/i2c/imx214.c
+ F:    Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
++SONY IMX219 SENSOR DRIVER
++M:    Dave Stevenson <dave.stevenson@raspberrypi.com>
++L:    linux-media@vger.kernel.org
++T:    git git://linuxtv.org/media_tree.git
++S:    Maintained
++F:    drivers/media/i2c/imx219.c
++F:    Documentation/devicetree/bindings/media/i2c/imx219.yaml
++
+ SONY IMX258 SENSOR DRIVER
+ M:    Sakari Ailus <sakari.ailus@linux.intel.com>
+ L:    linux-media@vger.kernel.org
diff --git a/target/linux/bcm27xx/patches-5.4/950-0463-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch b/target/linux/bcm27xx/patches-5.4/950-0463-media-dt-bindings-media-i2c-Add-IMX219-CMOS-sensor-b.patch
deleted file mode 100644 (file)
index 16eaedd..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-From a3ceeebaaa66e6786490e850b5019808da3785c0 Mon Sep 17 00:00:00 2001
-From: Andrey Konovalov <andrey.konovalov@linaro.org>
-Date: Mon, 20 Jan 2020 05:15:57 -0300
-Subject: [PATCH] media: dt-bindings: media: i2c: Add IMX219 CMOS
- sensor binding
-
-Commit 9d730f2cf4c0391785855dd231577d2de2594df9 upstream.
-(Currently on linux-media/master, queued for 5.7)
-
-Add YAML device tree binding for IMX219 CMOS image sensor, and
-the relevant MAINTAINERS entries.
-
-Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
-Reviewed-by: Rob Herring <robh@kernel.org>
-Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
-Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
----
- .../devicetree/bindings/media/i2c/imx219.yaml | 114 ++++++++++++++++++
- MAINTAINERS                                   |   8 ++
- 2 files changed, 122 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/media/i2c/imx219.yaml
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/media/i2c/imx219.yaml
-@@ -0,0 +1,114 @@
-+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/media/i2c/imx219.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: Sony 1/4.0-Inch 8Mpixel CMOS Digital Image Sensor
-+
-+maintainers:
-+  - Dave Stevenson <dave.stevenson@raspberrypi.com>
-+
-+description: |-
-+  The Sony imx219 is a 1/4.0-inch CMOS active pixel digital image sensor
-+  with an active array size of 3280H x 2464V. It is programmable through
-+  I2C interface. The I2C address is fixed to 0x10 as per sensor data sheet.
-+  Image data is sent through MIPI CSI-2, which is configured as either 2 or
-+  4 data lanes.
-+
-+properties:
-+  compatible:
-+    const: sony,imx219
-+
-+  reg:
-+    description: I2C device address
-+    maxItems: 1
-+
-+  clocks:
-+    maxItems: 1
-+
-+  VDIG-supply:
-+    description:
-+      Digital I/O voltage supply, 1.8 volts
-+
-+  VANA-supply:
-+    description:
-+      Analog voltage supply, 2.8 volts
-+
-+  VDDL-supply:
-+    description:
-+      Digital core voltage supply, 1.2 volts
-+
-+  reset-gpios:
-+    description: |-
-+      Reference to the GPIO connected to the xclr pin, if any.
-+      Must be released (set high) after all supplies are applied.
-+
-+  # See ../video-interfaces.txt for more details
-+  port:
-+    type: object
-+    properties:
-+      endpoint:
-+        type: object
-+        properties:
-+          data-lanes:
-+            description: |-
-+              The sensor supports either two-lane, or four-lane operation.
-+              If this property is omitted four-lane operation is assumed.
-+              For two-lane operation the property must be set to <1 2>.
-+            items:
-+              - const: 1
-+              - const: 2
-+
-+          clock-noncontinuous:
-+            type: boolean
-+            description: |-
-+              MIPI CSI-2 clock is non-continuous if this property is present,
-+              otherwise it's continuous.
-+
-+          link-frequencies:
-+            allOf:
-+              - $ref: /schemas/types.yaml#/definitions/uint64-array
-+            description:
-+              Allowed data bus frequencies.
-+
-+        required:
-+          - link-frequencies
-+
-+required:
-+  - compatible
-+  - reg
-+  - clocks
-+  - VANA-supply
-+  - VDIG-supply
-+  - VDDL-supply
-+  - port
-+
-+additionalProperties: false
-+
-+examples:
-+  - |
-+    i2c0 {
-+        #address-cells = <1>;
-+        #size-cells = <0>;
-+
-+        imx219: sensor@10 {
-+            compatible = "sony,imx219";
-+            reg = <0x10>;
-+            clocks = <&imx219_clk>;
-+            VANA-supply = <&imx219_vana>;   /* 2.8v */
-+            VDIG-supply = <&imx219_vdig>;   /* 1.8v */
-+            VDDL-supply = <&imx219_vddl>;   /* 1.2v */
-+
-+            port {
-+                imx219_0: endpoint {
-+                    remote-endpoint = <&csi1_ep>;
-+                    data-lanes = <1 2>;
-+                    clock-noncontinuous;
-+                    link-frequencies = /bits/ 64 <456000000>;
-+                };
-+            };
-+        };
-+    };
-+
-+...
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -15142,6 +15142,14 @@ S:    Maintained
- F:    drivers/media/i2c/imx214.c
- F:    Documentation/devicetree/bindings/media/i2c/sony,imx214.txt
-+SONY IMX219 SENSOR DRIVER
-+M:    Dave Stevenson <dave.stevenson@raspberrypi.com>
-+L:    linux-media@vger.kernel.org
-+T:    git git://linuxtv.org/media_tree.git
-+S:    Maintained
-+F:    drivers/media/i2c/imx219.c
-+F:    Documentation/devicetree/bindings/media/i2c/imx219.yaml
-+
- SONY IMX258 SENSOR DRIVER
- M:    Sakari Ailus <sakari.ailus@linux.intel.com>
- L:    linux-media@vger.kernel.org
diff --git a/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0463-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
new file mode 100644 (file)
index 0000000..4ca345f
--- /dev/null
@@ -0,0 +1,1372 @@
+From 5cd8c4efeb46ce1ef370dd3012a7951ba430b58f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 20 Jan 2020 05:15:58 -0300
+Subject: [PATCH] media: i2c: Add driver for Sony IMX219 sensor
+
+Commit 1283b3b8f82b9004fbb94398cade5c8e797a2c8d upstream.
+(Currently on linux-media/master, queued for 5.7)
+
+Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
+Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
+currently only supports 2 lanes.
+8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
+@ 30fps are currently supported.
+
+[Sakari Ailus: make imx219_check_hwcfg static]
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/i2c/Kconfig  |   11 +
+ drivers/media/i2c/Makefile |    1 +
+ drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1324 insertions(+)
+ create mode 100644 drivers/media/i2c/imx219.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -578,6 +578,17 @@ config VIDEO_IMX214
+         To compile this driver as a module, choose M here: the
+         module will be called imx214.
++config VIDEO_IMX219
++      tristate "Sony IMX219 sensor support"
++      depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
++      select V4L2_FWNODE
++      help
++        This is a Video4Linux2 sensor driver for the Sony
++        IMX219 camera.
++
++        To compile this driver as a module, choose M here: the
++        module will be called imx219.
++
+ config VIDEO_IMX258
+       tristate "Sony IMX258 sensor support"
+       depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_ML86V7667)      += ml86v76
+ obj-$(CONFIG_VIDEO_OV2659)    += ov2659.o
+ obj-$(CONFIG_VIDEO_TC358743)  += tc358743.o
+ obj-$(CONFIG_VIDEO_IMX214)    += imx214.o
++obj-$(CONFIG_VIDEO_IMX219)    += imx219.o
+ obj-$(CONFIG_VIDEO_IMX258)    += imx258.o
+ obj-$(CONFIG_VIDEO_IMX274)    += imx274.o
+ obj-$(CONFIG_VIDEO_IMX319)    += imx319.o
+--- /dev/null
++++ b/drivers/media/i2c/imx219.c
+@@ -0,0 +1,1312 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * A V4L2 driver for Sony IMX219 cameras.
++ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
++ *
++ * Based on Sony imx258 camera driver
++ * Copyright (C) 2018 Intel Corporation
++ *
++ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
++ * Copyright 2018 Qtechnology A/S
++ *
++ * Flip handling taken from the Sony IMX319 driver.
++ * Copyright (C) 2018 Intel Corporation
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <asm/unaligned.h>
++
++#define IMX219_REG_VALUE_08BIT                1
++#define IMX219_REG_VALUE_16BIT                2
++
++#define IMX219_REG_MODE_SELECT                0x0100
++#define IMX219_MODE_STANDBY           0x00
++#define IMX219_MODE_STREAMING         0x01
++
++/* Chip ID */
++#define IMX219_REG_CHIP_ID            0x0000
++#define IMX219_CHIP_ID                        0x0219
++
++/* External clock frequency is 24.0M */
++#define IMX219_XCLK_FREQ              24000000
++
++/* Pixel rate is fixed at 182.4M for all the modes */
++#define IMX219_PIXEL_RATE             182400000
++
++#define IMX219_DEFAULT_LINK_FREQ      456000000
++
++/* V_TIMING internal */
++#define IMX219_REG_VTS                        0x0160
++#define IMX219_VTS_15FPS              0x0dc6
++#define IMX219_VTS_30FPS_1080P                0x06e3
++#define IMX219_VTS_30FPS_BINNED               0x06e3
++#define IMX219_VTS_MAX                        0xffff
++
++#define IMX219_VBLANK_MIN             4
++
++/*Frame Length Line*/
++#define IMX219_FLL_MIN                        0x08a6
++#define IMX219_FLL_MAX                        0xffff
++#define IMX219_FLL_STEP                       1
++#define IMX219_FLL_DEFAULT            0x0c98
++
++/* HBLANK control - read only */
++#define IMX219_PPL_DEFAULT            3448
++
++/* Exposure control */
++#define IMX219_REG_EXPOSURE           0x015a
++#define IMX219_EXPOSURE_MIN           4
++#define IMX219_EXPOSURE_STEP          1
++#define IMX219_EXPOSURE_DEFAULT               0x640
++#define IMX219_EXPOSURE_MAX           65535
++
++/* Analog gain control */
++#define IMX219_REG_ANALOG_GAIN                0x0157
++#define IMX219_ANA_GAIN_MIN           0
++#define IMX219_ANA_GAIN_MAX           232
++#define IMX219_ANA_GAIN_STEP          1
++#define IMX219_ANA_GAIN_DEFAULT               0x0
++
++/* Digital gain control */
++#define IMX219_REG_DIGITAL_GAIN               0x0158
++#define IMX219_DGTL_GAIN_MIN          0x0100
++#define IMX219_DGTL_GAIN_MAX          0x0fff
++#define IMX219_DGTL_GAIN_DEFAULT      0x0100
++#define IMX219_DGTL_GAIN_STEP         1
++
++#define IMX219_REG_ORIENTATION                0x0172
++
++/* Test Pattern Control */
++#define IMX219_REG_TEST_PATTERN               0x0600
++#define IMX219_TEST_PATTERN_DISABLE   0
++#define IMX219_TEST_PATTERN_SOLID_COLOR       1
++#define IMX219_TEST_PATTERN_COLOR_BARS        2
++#define IMX219_TEST_PATTERN_GREY_COLOR        3
++#define IMX219_TEST_PATTERN_PN9               4
++
++/* Test pattern colour components */
++#define IMX219_REG_TESTP_RED          0x0602
++#define IMX219_REG_TESTP_GREENR               0x0604
++#define IMX219_REG_TESTP_BLUE         0x0606
++#define IMX219_REG_TESTP_GREENB               0x0608
++#define IMX219_TESTP_COLOUR_MIN               0
++#define IMX219_TESTP_COLOUR_MAX               0x03ff
++#define IMX219_TESTP_COLOUR_STEP      1
++#define IMX219_TESTP_RED_DEFAULT      IMX219_TESTP_COLOUR_MAX
++#define IMX219_TESTP_GREENR_DEFAULT   0
++#define IMX219_TESTP_BLUE_DEFAULT     0
++#define IMX219_TESTP_GREENB_DEFAULT   0
++
++struct imx219_reg {
++      u16 address;
++      u8 val;
++};
++
++struct imx219_reg_list {
++      unsigned int num_of_regs;
++      const struct imx219_reg *regs;
++};
++
++/* Mode : resolution and related config&values */
++struct imx219_mode {
++      /* Frame width */
++      unsigned int width;
++      /* Frame height */
++      unsigned int height;
++
++      /* V-timing */
++      unsigned int vts_def;
++
++      /* Default register values */
++      struct imx219_reg_list reg_list;
++};
++
++/*
++ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
++ * driver.
++ * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
++ */
++static const struct imx219_reg mode_3280x2464_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x0c},
++      {0x30eb, 0x05},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0164, 0x00},
++      {0x0165, 0x00},
++      {0x0166, 0x0c},
++      {0x0167, 0xcf},
++      {0x0168, 0x00},
++      {0x0169, 0x00},
++      {0x016a, 0x09},
++      {0x016b, 0x9f},
++      {0x016c, 0x0c},
++      {0x016d, 0xd0},
++      {0x016e, 0x09},
++      {0x016f, 0xa0},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x00},
++      {0x0175, 0x00},
++      {0x018c, 0x0a},
++      {0x018d, 0x0a},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x0309, 0x0a},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x0c},
++      {0x0625, 0xd0},
++      {0x0626, 0x09},
++      {0x0627, 0xa0},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++};
++
++static const struct imx219_reg mode_1920_1080_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x05},
++      {0x30eb, 0x0c},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++      {0x0164, 0x02},
++      {0x0165, 0xa8},
++      {0x0166, 0x0a},
++      {0x0167, 0x27},
++      {0x0168, 0x02},
++      {0x0169, 0xb4},
++      {0x016a, 0x06},
++      {0x016b, 0xeb},
++      {0x016c, 0x07},
++      {0x016d, 0x80},
++      {0x016e, 0x04},
++      {0x016f, 0x38},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x00},
++      {0x0175, 0x00},
++      {0x018c, 0x0a},
++      {0x018d, 0x0a},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x0309, 0x0a},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x07},
++      {0x0625, 0x80},
++      {0x0626, 0x04},
++      {0x0627, 0x38},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++};
++
++static const struct imx219_reg mode_1640_1232_regs[] = {
++      {0x0100, 0x00},
++      {0x30eb, 0x0c},
++      {0x30eb, 0x05},
++      {0x300a, 0xff},
++      {0x300b, 0xff},
++      {0x30eb, 0x05},
++      {0x30eb, 0x09},
++      {0x0114, 0x01},
++      {0x0128, 0x00},
++      {0x012a, 0x18},
++      {0x012b, 0x00},
++      {0x0164, 0x00},
++      {0x0165, 0x00},
++      {0x0166, 0x0c},
++      {0x0167, 0xcf},
++      {0x0168, 0x00},
++      {0x0169, 0x00},
++      {0x016a, 0x09},
++      {0x016b, 0x9f},
++      {0x016c, 0x06},
++      {0x016d, 0x68},
++      {0x016e, 0x04},
++      {0x016f, 0xd0},
++      {0x0170, 0x01},
++      {0x0171, 0x01},
++      {0x0174, 0x01},
++      {0x0175, 0x01},
++      {0x018c, 0x0a},
++      {0x018d, 0x0a},
++      {0x0301, 0x05},
++      {0x0303, 0x01},
++      {0x0304, 0x03},
++      {0x0305, 0x03},
++      {0x0306, 0x00},
++      {0x0307, 0x39},
++      {0x0309, 0x0a},
++      {0x030b, 0x01},
++      {0x030c, 0x00},
++      {0x030d, 0x72},
++      {0x0624, 0x06},
++      {0x0625, 0x68},
++      {0x0626, 0x04},
++      {0x0627, 0xd0},
++      {0x455e, 0x00},
++      {0x471e, 0x4b},
++      {0x4767, 0x0f},
++      {0x4750, 0x14},
++      {0x4540, 0x00},
++      {0x47b4, 0x14},
++      {0x4713, 0x30},
++      {0x478b, 0x10},
++      {0x478f, 0x10},
++      {0x4793, 0x10},
++      {0x4797, 0x0e},
++      {0x479b, 0x0e},
++      {0x0162, 0x0d},
++      {0x0163, 0x78},
++};
++
++static const char * const imx219_test_pattern_menu[] = {
++      "Disabled",
++      "Color Bars",
++      "Solid Color",
++      "Grey Color Bars",
++      "PN9"
++};
++
++static const int imx219_test_pattern_val[] = {
++      IMX219_TEST_PATTERN_DISABLE,
++      IMX219_TEST_PATTERN_COLOR_BARS,
++      IMX219_TEST_PATTERN_SOLID_COLOR,
++      IMX219_TEST_PATTERN_GREY_COLOR,
++      IMX219_TEST_PATTERN_PN9,
++};
++
++/* regulator supplies */
++static const char * const imx219_supply_name[] = {
++      /* Supplies can be enabled in any order */
++      "VANA",  /* Analog (2.8V) supply */
++      "VDIG",  /* Digital Core (1.8V) supply */
++      "VDDL",  /* IF (1.2V) supply */
++};
++
++#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
++
++/*
++ * Initialisation delay between XCLR low->high and the moment when the sensor
++ * can start capture (i.e. can leave software stanby) must be not less than:
++ *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
++ * where
++ *   t4 is fixed, and is max 200uS,
++ *   t5 is fixed, and is 6000uS,
++ *   t6 depends on the sensor external clock, and is max 32000 clock periods.
++ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
++ * So for any acceptable external clock t6 is always within the range of
++ * 1185 to 5333 uS, and is always less than t5.
++ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
++ * initialize the sensor over I2C, and then exit the software standby.
++ *
++ * This start-up time can be optimized a bit more, if we start the writes
++ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
++ * initialization over I2C may complete before (t4+t5) expires, and we must
++ * ensure that capture is not started before (t4+t5).
++ *
++ * This delay doesn't account for the power supply startup time. If needed,
++ * this should be taken care of via the regulator framework. E.g. in the
++ * case of DT for regulator-fixed one should define the startup-delay-us
++ * property.
++ */
++#define IMX219_XCLR_MIN_DELAY_US      6200
++#define IMX219_XCLR_DELAY_RANGE_US    1000
++
++/* Mode configs */
++static const struct imx219_mode supported_modes[] = {
++      {
++              /* 8MPix 15fps mode */
++              .width = 3280,
++              .height = 2464,
++              .vts_def = IMX219_VTS_15FPS,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
++                      .regs = mode_3280x2464_regs,
++              },
++      },
++      {
++              /* 1080P 30fps cropped */
++              .width = 1920,
++              .height = 1080,
++              .vts_def = IMX219_VTS_30FPS_1080P,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
++                      .regs = mode_1920_1080_regs,
++              },
++      },
++      {
++              /* 2x2 binned 30fps mode */
++              .width = 1640,
++              .height = 1232,
++              .vts_def = IMX219_VTS_30FPS_BINNED,
++              .reg_list = {
++                      .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
++                      .regs = mode_1640_1232_regs,
++              },
++      },
++};
++
++struct imx219 {
++      struct v4l2_subdev sd;
++      struct media_pad pad;
++
++      struct clk *xclk; /* system clock to IMX219 */
++      u32 xclk_freq;
++
++      struct gpio_desc *reset_gpio;
++      struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
++
++      struct v4l2_ctrl_handler ctrl_handler;
++      /* V4L2 Controls */
++      struct v4l2_ctrl *pixel_rate;
++      struct v4l2_ctrl *exposure;
++      struct v4l2_ctrl *vflip;
++      struct v4l2_ctrl *hflip;
++      struct v4l2_ctrl *vblank;
++      struct v4l2_ctrl *hblank;
++
++      /* Current mode */
++      const struct imx219_mode *mode;
++
++      /*
++       * Mutex for serialized access:
++       * Protect sensor module set pad format and start/stop streaming safely.
++       */
++      struct mutex mutex;
++
++      /* Streaming on/off */
++      bool streaming;
++};
++
++static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
++{
++      return container_of(_sd, struct imx219, sd);
++}
++
++/* Read registers up to 2 at a time */
++static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      struct i2c_msg msgs[2];
++      u8 addr_buf[2] = { reg >> 8, reg & 0xff };
++      u8 data_buf[4] = { 0, };
++      int ret;
++
++      if (len > 4)
++              return -EINVAL;
++
++      /* Write register address */
++      msgs[0].addr = client->addr;
++      msgs[0].flags = 0;
++      msgs[0].len = ARRAY_SIZE(addr_buf);
++      msgs[0].buf = addr_buf;
++
++      /* Read data from register */
++      msgs[1].addr = client->addr;
++      msgs[1].flags = I2C_M_RD;
++      msgs[1].len = len;
++      msgs[1].buf = &data_buf[4 - len];
++
++      ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++      if (ret != ARRAY_SIZE(msgs))
++              return -EIO;
++
++      *val = get_unaligned_be32(data_buf);
++
++      return 0;
++}
++
++/* Write registers up to 2 at a time */
++static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      u8 buf[6];
++
++      if (len > 4)
++              return -EINVAL;
++
++      put_unaligned_be16(reg, buf);
++      put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
++      if (i2c_master_send(client, buf, len + 2) != len + 2)
++              return -EIO;
++
++      return 0;
++}
++
++/* Write a list of registers */
++static int imx219_write_regs(struct imx219 *imx219,
++                           const struct imx219_reg *regs, u32 len)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      unsigned int i;
++      int ret;
++
++      for (i = 0; i < len; i++) {
++              ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
++              if (ret) {
++                      dev_err_ratelimited(&client->dev,
++                                          "Failed to write reg 0x%4.4x. error = %d\n",
++                                          regs[i].address, ret);
++
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++/* Get bayer order based on flip setting. */
++static u32 imx219_get_format_code(struct imx219 *imx219)
++{
++      /*
++       * Only one bayer order is supported.
++       * It depends on the flip settings.
++       */
++      static const u32 codes[2][2] = {
++              { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
++              { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
++      };
++
++      lockdep_assert_held(&imx219->mutex);
++      return codes[imx219->vflip->val][imx219->hflip->val];
++}
++
++static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      struct v4l2_mbus_framefmt *try_fmt =
++              v4l2_subdev_get_try_format(sd, fh->pad, 0);
++
++      mutex_lock(&imx219->mutex);
++
++      /* Initialize try_fmt */
++      try_fmt->width = supported_modes[0].width;
++      try_fmt->height = supported_modes[0].height;
++      try_fmt->code = imx219_get_format_code(imx219);
++      try_fmt->field = V4L2_FIELD_NONE;
++
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct imx219 *imx219 =
++              container_of(ctrl->handler, struct imx219, ctrl_handler);
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++
++      if (ctrl->id == V4L2_CID_VBLANK) {
++              int exposure_max, exposure_def;
++
++              /* Update max exposure while meeting expected vblanking */
++              exposure_max = imx219->mode->height + ctrl->val - 4;
++              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++                      exposure_max : IMX219_EXPOSURE_DEFAULT;
++              __v4l2_ctrl_modify_range(imx219->exposure,
++                                       imx219->exposure->minimum,
++                                       exposure_max, imx219->exposure->step,
++                                       exposure_def);
++      }
++
++      /*
++       * Applying V4L2 control value only happens
++       * when power is up for streaming
++       */
++      if (pm_runtime_get_if_in_use(&client->dev) == 0)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
++                                     IMX219_REG_VALUE_08BIT, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_DIGITAL_GAIN:
++              ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN:
++              ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
++                                     IMX219_REG_VALUE_16BIT,
++                                     imx219_test_pattern_val[ctrl->val]);
++              break;
++      case V4L2_CID_HFLIP:
++      case V4L2_CID_VFLIP:
++              ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
++                                     imx219->hflip->val |
++                                     imx219->vflip->val << 1);
++              break;
++      case V4L2_CID_VBLANK:
++              ret = imx219_write_reg(imx219, IMX219_REG_VTS,
++                                     IMX219_REG_VALUE_16BIT,
++                                     imx219->mode->height + ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_RED:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENR:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_BLUE:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      case V4L2_CID_TEST_PATTERN_GREENB:
++              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
++                                     IMX219_REG_VALUE_16BIT, ctrl->val);
++              break;
++      default:
++              dev_info(&client->dev,
++                       "ctrl(id:0x%x,val:0x%x) is not handled\n",
++                       ctrl->id, ctrl->val);
++              ret = -EINVAL;
++              break;
++      }
++
++      pm_runtime_put(&client->dev);
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
++      .s_ctrl = imx219_set_ctrl,
++};
++
++static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_pad_config *cfg,
++                               struct v4l2_subdev_mbus_code_enum *code)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++
++      /*
++       * Only one bayer order is supported (though it depends on the flip
++       * settings)
++       */
++      if (code->index > 0)
++              return -EINVAL;
++
++      code->code = imx219_get_format_code(imx219);
++
++      return 0;
++}
++
++static int imx219_enum_frame_size(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_pad_config *cfg,
++                                struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++
++      if (fse->index >= ARRAY_SIZE(supported_modes))
++              return -EINVAL;
++
++      if (fse->code != imx219_get_format_code(imx219))
++              return -EINVAL;
++
++      fse->min_width = supported_modes[fse->index].width;
++      fse->max_width = fse->min_width;
++      fse->min_height = supported_modes[fse->index].height;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
++{
++      fmt->colorspace = V4L2_COLORSPACE_SRGB;
++      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
++      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
++                                                        fmt->colorspace,
++                                                        fmt->ycbcr_enc);
++      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
++}
++
++static void imx219_update_pad_format(struct imx219 *imx219,
++                                   const struct imx219_mode *mode,
++                                   struct v4l2_subdev_format *fmt)
++{
++      fmt->format.width = mode->width;
++      fmt->format.height = mode->height;
++      fmt->format.code = imx219_get_format_code(imx219);
++      fmt->format.field = V4L2_FIELD_NONE;
++
++      imx219_reset_colorspace(&fmt->format);
++}
++
++static int __imx219_get_pad_format(struct imx219 *imx219,
++                                 struct v4l2_subdev_pad_config *cfg,
++                                 struct v4l2_subdev_format *fmt)
++{
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              struct v4l2_mbus_framefmt *try_fmt =
++                      v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
++              /* update the code which could change due to vflip or hflip: */
++              try_fmt->code = imx219_get_format_code(imx219);
++              fmt->format = *try_fmt;
++      } else {
++              imx219_update_pad_format(imx219, imx219->mode, fmt);
++      }
++
++      return 0;
++}
++
++static int imx219_get_pad_format(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_pad_config *cfg,
++                               struct v4l2_subdev_format *fmt)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      int ret;
++
++      mutex_lock(&imx219->mutex);
++      ret = __imx219_get_pad_format(imx219, cfg, fmt);
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++}
++
++static int imx219_set_pad_format(struct v4l2_subdev *sd,
++                               struct v4l2_subdev_pad_config *cfg,
++                               struct v4l2_subdev_format *fmt)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      const struct imx219_mode *mode;
++      struct v4l2_mbus_framefmt *framefmt;
++      int exposure_max, exposure_def, hblank;
++
++      mutex_lock(&imx219->mutex);
++
++      /* Bayer order varies with flips */
++      fmt->format.code = imx219_get_format_code(imx219);
++
++      mode = v4l2_find_nearest_size(supported_modes,
++                                    ARRAY_SIZE(supported_modes),
++                                    width, height,
++                                    fmt->format.width, fmt->format.height);
++      imx219_update_pad_format(imx219, mode, fmt);
++      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++              framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
++              *framefmt = fmt->format;
++      } else if (imx219->mode != mode) {
++              imx219->mode = mode;
++              /* Update limits and set FPS to default */
++              __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
++                                       IMX219_VTS_MAX - mode->height, 1,
++                                       mode->vts_def - mode->height);
++              __v4l2_ctrl_s_ctrl(imx219->vblank,
++                                 mode->vts_def - mode->height);
++              /* Update max exposure while meeting expected vblanking */
++              exposure_max = mode->vts_def - 4;
++              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++                      exposure_max : IMX219_EXPOSURE_DEFAULT;
++              __v4l2_ctrl_modify_range(imx219->exposure,
++                                       imx219->exposure->minimum,
++                                       exposure_max, imx219->exposure->step,
++                                       exposure_def);
++              /*
++               * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
++               * depends on mode->width only, and is not changeble in any
++               * way other than changing the mode.
++               */
++              hblank = IMX219_PPL_DEFAULT - mode->width;
++              __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
++                                       hblank);
++      }
++
++      mutex_unlock(&imx219->mutex);
++
++      return 0;
++}
++
++static int imx219_start_streaming(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      const struct imx219_reg_list *reg_list;
++      int ret;
++
++      /* Apply default values of current mode */
++      reg_list = &imx219->mode->reg_list;
++      ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
++      if (ret) {
++              dev_err(&client->dev, "%s failed to set mode\n", __func__);
++              return ret;
++      }
++
++      /* Apply customized values from user */
++      ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
++      if (ret)
++              return ret;
++
++      /* set stream on register */
++      return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                              IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
++}
++
++static void imx219_stop_streaming(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++
++      /* set stream off register */
++      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
++                             IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
++      if (ret)
++              dev_err(&client->dev, "%s failed to set stream\n", __func__);
++}
++
++static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
++{
++      struct imx219 *imx219 = to_imx219(sd);
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
++      int ret = 0;
++
++      mutex_lock(&imx219->mutex);
++      if (imx219->streaming == enable) {
++              mutex_unlock(&imx219->mutex);
++              return 0;
++      }
++
++      if (enable) {
++              ret = pm_runtime_get_sync(&client->dev);
++              if (ret < 0) {
++                      pm_runtime_put_noidle(&client->dev);
++                      goto err_unlock;
++              }
++
++              /*
++               * Apply default & customized values
++               * and then start streaming.
++               */
++              ret = imx219_start_streaming(imx219);
++              if (ret)
++                      goto err_rpm_put;
++      } else {
++              imx219_stop_streaming(imx219);
++              pm_runtime_put(&client->dev);
++      }
++
++      imx219->streaming = enable;
++
++      /* vflip and hflip cannot change during streaming */
++      __v4l2_ctrl_grab(imx219->vflip, enable);
++      __v4l2_ctrl_grab(imx219->hflip, enable);
++
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++
++err_rpm_put:
++      pm_runtime_put(&client->dev);
++err_unlock:
++      mutex_unlock(&imx219->mutex);
++
++      return ret;
++}
++
++/* Power/clock management functions */
++static int imx219_power_on(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++      int ret;
++
++      ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
++                                  imx219->supplies);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable regulators\n",
++                      __func__);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(imx219->xclk);
++      if (ret) {
++              dev_err(&client->dev, "%s: failed to enable clock\n",
++                      __func__);
++              goto reg_off;
++      }
++
++      gpiod_set_value_cansleep(imx219->reset_gpio, 1);
++      usleep_range(IMX219_XCLR_MIN_DELAY_US,
++                   IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
++
++      return 0;
++
++reg_off:
++      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++
++      return ret;
++}
++
++static int imx219_power_off(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++
++      gpiod_set_value_cansleep(imx219->reset_gpio, 0);
++      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
++      clk_disable_unprepare(imx219->xclk);
++
++      return 0;
++}
++
++static int __maybe_unused imx219_suspend(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++
++      if (imx219->streaming)
++              imx219_stop_streaming(imx219);
++
++      return 0;
++}
++
++static int __maybe_unused imx219_resume(struct device *dev)
++{
++      struct i2c_client *client = to_i2c_client(dev);
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++      int ret;
++
++      if (imx219->streaming) {
++              ret = imx219_start_streaming(imx219);
++              if (ret)
++                      goto error;
++      }
++
++      return 0;
++
++error:
++      imx219_stop_streaming(imx219);
++      imx219->streaming = 0;
++
++      return ret;
++}
++
++static int imx219_get_regulators(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      unsigned int i;
++
++      for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
++              imx219->supplies[i].supply = imx219_supply_name[i];
++
++      return devm_regulator_bulk_get(&client->dev,
++                                     IMX219_NUM_SUPPLIES,
++                                     imx219->supplies);
++}
++
++/* Verify chip ID */
++static int imx219_identify_module(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      int ret;
++      u32 val;
++
++      ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
++                            IMX219_REG_VALUE_16BIT, &val);
++      if (ret) {
++              dev_err(&client->dev, "failed to read chip id %x\n",
++                      IMX219_CHIP_ID);
++              return ret;
++      }
++
++      if (val != IMX219_CHIP_ID) {
++              dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
++                      IMX219_CHIP_ID, val);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static const struct v4l2_subdev_core_ops imx219_core_ops = {
++      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_video_ops imx219_video_ops = {
++      .s_stream = imx219_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
++      .enum_mbus_code = imx219_enum_mbus_code,
++      .get_fmt = imx219_get_pad_format,
++      .set_fmt = imx219_set_pad_format,
++      .enum_frame_size = imx219_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops imx219_subdev_ops = {
++      .core = &imx219_core_ops,
++      .video = &imx219_video_ops,
++      .pad = &imx219_pad_ops,
++};
++
++static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
++      .open = imx219_open,
++};
++
++/* Initialize control handlers */
++static int imx219_init_controls(struct imx219 *imx219)
++{
++      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
++      struct v4l2_ctrl_handler *ctrl_hdlr;
++      unsigned int height = imx219->mode->height;
++      int exposure_max, exposure_def, hblank;
++      int i, ret;
++
++      ctrl_hdlr = &imx219->ctrl_handler;
++      ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
++      if (ret)
++              return ret;
++
++      mutex_init(&imx219->mutex);
++      ctrl_hdlr->lock = &imx219->mutex;
++
++      /* By default, PIXEL_RATE is read only */
++      imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                             V4L2_CID_PIXEL_RATE,
++                                             IMX219_PIXEL_RATE,
++                                             IMX219_PIXEL_RATE, 1,
++                                             IMX219_PIXEL_RATE);
++
++      /* Initial vblank/hblank/exposure parameters based on current mode */
++      imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                         V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
++                                         IMX219_VTS_MAX - height, 1,
++                                         imx219->mode->vts_def - height);
++      hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
++      imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                         V4L2_CID_HBLANK, hblank, hblank,
++                                         1, hblank);
++      if (imx219->hblank)
++              imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++      exposure_max = imx219->mode->vts_def - 4;
++      exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
++              exposure_max : IMX219_EXPOSURE_DEFAULT;
++      imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                           V4L2_CID_EXPOSURE,
++                                           IMX219_EXPOSURE_MIN, exposure_max,
++                                           IMX219_EXPOSURE_STEP,
++                                           exposure_def);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++                        IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
++                        IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
++
++      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++                        IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
++                        IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
++
++      imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                        V4L2_CID_HFLIP, 0, 1, 1, 0);
++      if (imx219->hflip)
++              imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                        V4L2_CID_VFLIP, 0, 1, 1, 0);
++      if (imx219->vflip)
++              imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++      v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
++                                   V4L2_CID_TEST_PATTERN,
++                                   ARRAY_SIZE(imx219_test_pattern_menu) - 1,
++                                   0, 0, imx219_test_pattern_menu);
++      for (i = 0; i < 4; i++) {
++              /*
++               * The assumption is that
++               * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
++               * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
++               * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
++               */
++              v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
++                                V4L2_CID_TEST_PATTERN_RED + i,
++                                IMX219_TESTP_COLOUR_MIN,
++                                IMX219_TESTP_COLOUR_MAX,
++                                IMX219_TESTP_COLOUR_STEP,
++                                IMX219_TESTP_COLOUR_MAX);
++              /* The "Solid color" pattern is white by default */
++      }
++
++      if (ctrl_hdlr->error) {
++              ret = ctrl_hdlr->error;
++              dev_err(&client->dev, "%s control init failed (%d)\n",
++                      __func__, ret);
++              goto error;
++      }
++
++      imx219->sd.ctrl_handler = ctrl_hdlr;
++
++      return 0;
++
++error:
++      v4l2_ctrl_handler_free(ctrl_hdlr);
++      mutex_destroy(&imx219->mutex);
++
++      return ret;
++}
++
++static void imx219_free_controls(struct imx219 *imx219)
++{
++      v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
++      mutex_destroy(&imx219->mutex);
++}
++
++static int imx219_check_hwcfg(struct device *dev)
++{
++      struct fwnode_handle *endpoint;
++      struct v4l2_fwnode_endpoint ep_cfg = {
++              .bus_type = V4L2_MBUS_CSI2_DPHY
++      };
++      int ret = -EINVAL;
++
++      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
++      if (!endpoint) {
++              dev_err(dev, "endpoint node not found\n");
++              return -EINVAL;
++      }
++
++      if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
++              dev_err(dev, "could not parse endpoint\n");
++              goto error_out;
++      }
++
++      /* Check the number of MIPI CSI2 data lanes */
++      if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
++              dev_err(dev, "only 2 data lanes are currently supported\n");
++              goto error_out;
++      }
++
++      /* Check the link frequency set in device tree */
++      if (!ep_cfg.nr_of_link_frequencies) {
++              dev_err(dev, "link-frequency property not found in DT\n");
++              goto error_out;
++      }
++
++      if (ep_cfg.nr_of_link_frequencies != 1 ||
++          ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
++              dev_err(dev, "Link frequency not supported: %lld\n",
++                      ep_cfg.link_frequencies[0]);
++              goto error_out;
++      }
++
++      ret = 0;
++
++error_out:
++      v4l2_fwnode_endpoint_free(&ep_cfg);
++      fwnode_handle_put(endpoint);
++
++      return ret;
++}
++
++static int imx219_probe(struct i2c_client *client)
++{
++      struct device *dev = &client->dev;
++      struct imx219 *imx219;
++      int ret;
++
++      imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
++      if (!imx219)
++              return -ENOMEM;
++
++      v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
++
++      /* Check the hardware configuration in device tree */
++      if (imx219_check_hwcfg(dev))
++              return -EINVAL;
++
++      /* Get system clock (xclk) */
++      imx219->xclk = devm_clk_get(dev, NULL);
++      if (IS_ERR(imx219->xclk)) {
++              dev_err(dev, "failed to get xclk\n");
++              return PTR_ERR(imx219->xclk);
++      }
++
++      imx219->xclk_freq = clk_get_rate(imx219->xclk);
++      if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
++              dev_err(dev, "xclk frequency not supported: %d Hz\n",
++                      imx219->xclk_freq);
++              return -EINVAL;
++      }
++
++      ret = imx219_get_regulators(imx219);
++      if (ret) {
++              dev_err(dev, "failed to get regulators\n");
++              return ret;
++      }
++
++      /* Request optional enable pin */
++      imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
++                                                   GPIOD_OUT_HIGH);
++
++      /*
++       * The sensor must be powered for imx219_identify_module()
++       * to be able to read the CHIP_ID register
++       */
++      ret = imx219_power_on(dev);
++      if (ret)
++              return ret;
++
++      ret = imx219_identify_module(imx219);
++      if (ret)
++              goto error_power_off;
++
++      /* Set default mode to max resolution */
++      imx219->mode = &supported_modes[0];
++
++      ret = imx219_init_controls(imx219);
++      if (ret)
++              goto error_power_off;
++
++      /* Initialize subdev */
++      imx219->sd.internal_ops = &imx219_internal_ops;
++      imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++      imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++      /* Initialize source pad */
++      imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
++
++      ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
++      if (ret) {
++              dev_err(dev, "failed to init entity pads: %d\n", ret);
++              goto error_handler_free;
++      }
++
++      ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
++      if (ret < 0) {
++              dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
++              goto error_media_entity;
++      }
++
++      /* Enable runtime PM and turn off the device */
++      pm_runtime_set_active(dev);
++      pm_runtime_enable(dev);
++      pm_runtime_idle(dev);
++
++      return 0;
++
++error_media_entity:
++      media_entity_cleanup(&imx219->sd.entity);
++
++error_handler_free:
++      imx219_free_controls(imx219);
++
++error_power_off:
++      imx219_power_off(dev);
++
++      return ret;
++}
++
++static int imx219_remove(struct i2c_client *client)
++{
++      struct v4l2_subdev *sd = i2c_get_clientdata(client);
++      struct imx219 *imx219 = to_imx219(sd);
++
++      v4l2_async_unregister_subdev(sd);
++      media_entity_cleanup(&sd->entity);
++      imx219_free_controls(imx219);
++
++      pm_runtime_disable(&client->dev);
++      if (!pm_runtime_status_suspended(&client->dev))
++              imx219_power_off(&client->dev);
++      pm_runtime_set_suspended(&client->dev);
++
++      return 0;
++}
++
++static const struct of_device_id imx219_dt_ids[] = {
++      { .compatible = "sony,imx219" },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, imx219_dt_ids);
++
++static const struct dev_pm_ops imx219_pm_ops = {
++      SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
++      SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
++};
++
++static struct i2c_driver imx219_i2c_driver = {
++      .driver = {
++              .name = "imx219",
++              .of_match_table = imx219_dt_ids,
++              .pm = &imx219_pm_ops,
++      },
++      .probe_new = imx219_probe,
++      .remove = imx219_remove,
++};
++
++module_i2c_driver(imx219_i2c_driver);
++
++MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
++MODULE_DESCRIPTION("Sony IMX219 sensor driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0464-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch b/target/linux/bcm27xx/patches-5.4/950-0464-media-i2c-Add-driver-for-Sony-IMX219-sensor.patch
deleted file mode 100644 (file)
index 4ca345f..0000000
+++ /dev/null
@@ -1,1372 +0,0 @@
-From 5cd8c4efeb46ce1ef370dd3012a7951ba430b58f Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Mon, 20 Jan 2020 05:15:58 -0300
-Subject: [PATCH] media: i2c: Add driver for Sony IMX219 sensor
-
-Commit 1283b3b8f82b9004fbb94398cade5c8e797a2c8d upstream.
-(Currently on linux-media/master, queued for 5.7)
-
-Adds a driver for the 8MPix Sony IMX219 CSI2 sensor.
-Whilst the sensor supports 2 or 4 CSI2 data lanes, this driver
-currently only supports 2 lanes.
-8MPix @ 15fps, 1080P @ 30fps (cropped FOV), and 1640x1232 (2x2 binned)
-@ 30fps are currently supported.
-
-[Sakari Ailus: make imx219_check_hwcfg static]
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
-Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
-Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
----
- drivers/media/i2c/Kconfig  |   11 +
- drivers/media/i2c/Makefile |    1 +
- drivers/media/i2c/imx219.c | 1312 ++++++++++++++++++++++++++++++++++++
- 3 files changed, 1324 insertions(+)
- create mode 100644 drivers/media/i2c/imx219.c
-
---- a/drivers/media/i2c/Kconfig
-+++ b/drivers/media/i2c/Kconfig
-@@ -578,6 +578,17 @@ config VIDEO_IMX214
-         To compile this driver as a module, choose M here: the
-         module will be called imx214.
-+config VIDEO_IMX219
-+      tristate "Sony IMX219 sensor support"
-+      depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
-+      select V4L2_FWNODE
-+      help
-+        This is a Video4Linux2 sensor driver for the Sony
-+        IMX219 camera.
-+
-+        To compile this driver as a module, choose M here: the
-+        module will be called imx219.
-+
- config VIDEO_IMX258
-       tristate "Sony IMX258 sensor support"
-       depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
---- a/drivers/media/i2c/Makefile
-+++ b/drivers/media/i2c/Makefile
-@@ -111,6 +111,7 @@ obj-$(CONFIG_VIDEO_ML86V7667)      += ml86v76
- obj-$(CONFIG_VIDEO_OV2659)    += ov2659.o
- obj-$(CONFIG_VIDEO_TC358743)  += tc358743.o
- obj-$(CONFIG_VIDEO_IMX214)    += imx214.o
-+obj-$(CONFIG_VIDEO_IMX219)    += imx219.o
- obj-$(CONFIG_VIDEO_IMX258)    += imx258.o
- obj-$(CONFIG_VIDEO_IMX274)    += imx274.o
- obj-$(CONFIG_VIDEO_IMX319)    += imx319.o
---- /dev/null
-+++ b/drivers/media/i2c/imx219.c
-@@ -0,0 +1,1312 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * A V4L2 driver for Sony IMX219 cameras.
-+ * Copyright (C) 2019, Raspberry Pi (Trading) Ltd
-+ *
-+ * Based on Sony imx258 camera driver
-+ * Copyright (C) 2018 Intel Corporation
-+ *
-+ * DT / fwnode changes, and regulator / GPIO control taken from imx214 driver
-+ * Copyright 2018 Qtechnology A/S
-+ *
-+ * Flip handling taken from the Sony IMX319 driver.
-+ * Copyright (C) 2018 Intel Corporation
-+ *
-+ */
-+
-+#include <linux/clk.h>
-+#include <linux/clk-provider.h>
-+#include <linux/clkdev.h>
-+#include <linux/delay.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/i2c.h>
-+#include <linux/module.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/regulator/consumer.h>
-+#include <media/v4l2-ctrls.h>
-+#include <media/v4l2-device.h>
-+#include <media/v4l2-event.h>
-+#include <media/v4l2-fwnode.h>
-+#include <media/v4l2-mediabus.h>
-+#include <asm/unaligned.h>
-+
-+#define IMX219_REG_VALUE_08BIT                1
-+#define IMX219_REG_VALUE_16BIT                2
-+
-+#define IMX219_REG_MODE_SELECT                0x0100
-+#define IMX219_MODE_STANDBY           0x00
-+#define IMX219_MODE_STREAMING         0x01
-+
-+/* Chip ID */
-+#define IMX219_REG_CHIP_ID            0x0000
-+#define IMX219_CHIP_ID                        0x0219
-+
-+/* External clock frequency is 24.0M */
-+#define IMX219_XCLK_FREQ              24000000
-+
-+/* Pixel rate is fixed at 182.4M for all the modes */
-+#define IMX219_PIXEL_RATE             182400000
-+
-+#define IMX219_DEFAULT_LINK_FREQ      456000000
-+
-+/* V_TIMING internal */
-+#define IMX219_REG_VTS                        0x0160
-+#define IMX219_VTS_15FPS              0x0dc6
-+#define IMX219_VTS_30FPS_1080P                0x06e3
-+#define IMX219_VTS_30FPS_BINNED               0x06e3
-+#define IMX219_VTS_MAX                        0xffff
-+
-+#define IMX219_VBLANK_MIN             4
-+
-+/*Frame Length Line*/
-+#define IMX219_FLL_MIN                        0x08a6
-+#define IMX219_FLL_MAX                        0xffff
-+#define IMX219_FLL_STEP                       1
-+#define IMX219_FLL_DEFAULT            0x0c98
-+
-+/* HBLANK control - read only */
-+#define IMX219_PPL_DEFAULT            3448
-+
-+/* Exposure control */
-+#define IMX219_REG_EXPOSURE           0x015a
-+#define IMX219_EXPOSURE_MIN           4
-+#define IMX219_EXPOSURE_STEP          1
-+#define IMX219_EXPOSURE_DEFAULT               0x640
-+#define IMX219_EXPOSURE_MAX           65535
-+
-+/* Analog gain control */
-+#define IMX219_REG_ANALOG_GAIN                0x0157
-+#define IMX219_ANA_GAIN_MIN           0
-+#define IMX219_ANA_GAIN_MAX           232
-+#define IMX219_ANA_GAIN_STEP          1
-+#define IMX219_ANA_GAIN_DEFAULT               0x0
-+
-+/* Digital gain control */
-+#define IMX219_REG_DIGITAL_GAIN               0x0158
-+#define IMX219_DGTL_GAIN_MIN          0x0100
-+#define IMX219_DGTL_GAIN_MAX          0x0fff
-+#define IMX219_DGTL_GAIN_DEFAULT      0x0100
-+#define IMX219_DGTL_GAIN_STEP         1
-+
-+#define IMX219_REG_ORIENTATION                0x0172
-+
-+/* Test Pattern Control */
-+#define IMX219_REG_TEST_PATTERN               0x0600
-+#define IMX219_TEST_PATTERN_DISABLE   0
-+#define IMX219_TEST_PATTERN_SOLID_COLOR       1
-+#define IMX219_TEST_PATTERN_COLOR_BARS        2
-+#define IMX219_TEST_PATTERN_GREY_COLOR        3
-+#define IMX219_TEST_PATTERN_PN9               4
-+
-+/* Test pattern colour components */
-+#define IMX219_REG_TESTP_RED          0x0602
-+#define IMX219_REG_TESTP_GREENR               0x0604
-+#define IMX219_REG_TESTP_BLUE         0x0606
-+#define IMX219_REG_TESTP_GREENB               0x0608
-+#define IMX219_TESTP_COLOUR_MIN               0
-+#define IMX219_TESTP_COLOUR_MAX               0x03ff
-+#define IMX219_TESTP_COLOUR_STEP      1
-+#define IMX219_TESTP_RED_DEFAULT      IMX219_TESTP_COLOUR_MAX
-+#define IMX219_TESTP_GREENR_DEFAULT   0
-+#define IMX219_TESTP_BLUE_DEFAULT     0
-+#define IMX219_TESTP_GREENB_DEFAULT   0
-+
-+struct imx219_reg {
-+      u16 address;
-+      u8 val;
-+};
-+
-+struct imx219_reg_list {
-+      unsigned int num_of_regs;
-+      const struct imx219_reg *regs;
-+};
-+
-+/* Mode : resolution and related config&values */
-+struct imx219_mode {
-+      /* Frame width */
-+      unsigned int width;
-+      /* Frame height */
-+      unsigned int height;
-+
-+      /* V-timing */
-+      unsigned int vts_def;
-+
-+      /* Default register values */
-+      struct imx219_reg_list reg_list;
-+};
-+
-+/*
-+ * Register sets lifted off the i2C interface from the Raspberry Pi firmware
-+ * driver.
-+ * 3280x2464 = mode 2, 1920x1080 = mode 1, and 1640x1232 = mode 4.
-+ */
-+static const struct imx219_reg mode_3280x2464_regs[] = {
-+      {0x0100, 0x00},
-+      {0x30eb, 0x0c},
-+      {0x30eb, 0x05},
-+      {0x300a, 0xff},
-+      {0x300b, 0xff},
-+      {0x30eb, 0x05},
-+      {0x30eb, 0x09},
-+      {0x0114, 0x01},
-+      {0x0128, 0x00},
-+      {0x012a, 0x18},
-+      {0x012b, 0x00},
-+      {0x0164, 0x00},
-+      {0x0165, 0x00},
-+      {0x0166, 0x0c},
-+      {0x0167, 0xcf},
-+      {0x0168, 0x00},
-+      {0x0169, 0x00},
-+      {0x016a, 0x09},
-+      {0x016b, 0x9f},
-+      {0x016c, 0x0c},
-+      {0x016d, 0xd0},
-+      {0x016e, 0x09},
-+      {0x016f, 0xa0},
-+      {0x0170, 0x01},
-+      {0x0171, 0x01},
-+      {0x0174, 0x00},
-+      {0x0175, 0x00},
-+      {0x018c, 0x0a},
-+      {0x018d, 0x0a},
-+      {0x0301, 0x05},
-+      {0x0303, 0x01},
-+      {0x0304, 0x03},
-+      {0x0305, 0x03},
-+      {0x0306, 0x00},
-+      {0x0307, 0x39},
-+      {0x0309, 0x0a},
-+      {0x030b, 0x01},
-+      {0x030c, 0x00},
-+      {0x030d, 0x72},
-+      {0x0624, 0x0c},
-+      {0x0625, 0xd0},
-+      {0x0626, 0x09},
-+      {0x0627, 0xa0},
-+      {0x455e, 0x00},
-+      {0x471e, 0x4b},
-+      {0x4767, 0x0f},
-+      {0x4750, 0x14},
-+      {0x4540, 0x00},
-+      {0x47b4, 0x14},
-+      {0x4713, 0x30},
-+      {0x478b, 0x10},
-+      {0x478f, 0x10},
-+      {0x4793, 0x10},
-+      {0x4797, 0x0e},
-+      {0x479b, 0x0e},
-+      {0x0162, 0x0d},
-+      {0x0163, 0x78},
-+};
-+
-+static const struct imx219_reg mode_1920_1080_regs[] = {
-+      {0x0100, 0x00},
-+      {0x30eb, 0x05},
-+      {0x30eb, 0x0c},
-+      {0x300a, 0xff},
-+      {0x300b, 0xff},
-+      {0x30eb, 0x05},
-+      {0x30eb, 0x09},
-+      {0x0114, 0x01},
-+      {0x0128, 0x00},
-+      {0x012a, 0x18},
-+      {0x012b, 0x00},
-+      {0x0162, 0x0d},
-+      {0x0163, 0x78},
-+      {0x0164, 0x02},
-+      {0x0165, 0xa8},
-+      {0x0166, 0x0a},
-+      {0x0167, 0x27},
-+      {0x0168, 0x02},
-+      {0x0169, 0xb4},
-+      {0x016a, 0x06},
-+      {0x016b, 0xeb},
-+      {0x016c, 0x07},
-+      {0x016d, 0x80},
-+      {0x016e, 0x04},
-+      {0x016f, 0x38},
-+      {0x0170, 0x01},
-+      {0x0171, 0x01},
-+      {0x0174, 0x00},
-+      {0x0175, 0x00},
-+      {0x018c, 0x0a},
-+      {0x018d, 0x0a},
-+      {0x0301, 0x05},
-+      {0x0303, 0x01},
-+      {0x0304, 0x03},
-+      {0x0305, 0x03},
-+      {0x0306, 0x00},
-+      {0x0307, 0x39},
-+      {0x0309, 0x0a},
-+      {0x030b, 0x01},
-+      {0x030c, 0x00},
-+      {0x030d, 0x72},
-+      {0x0624, 0x07},
-+      {0x0625, 0x80},
-+      {0x0626, 0x04},
-+      {0x0627, 0x38},
-+      {0x455e, 0x00},
-+      {0x471e, 0x4b},
-+      {0x4767, 0x0f},
-+      {0x4750, 0x14},
-+      {0x4540, 0x00},
-+      {0x47b4, 0x14},
-+      {0x4713, 0x30},
-+      {0x478b, 0x10},
-+      {0x478f, 0x10},
-+      {0x4793, 0x10},
-+      {0x4797, 0x0e},
-+      {0x479b, 0x0e},
-+      {0x0162, 0x0d},
-+      {0x0163, 0x78},
-+};
-+
-+static const struct imx219_reg mode_1640_1232_regs[] = {
-+      {0x0100, 0x00},
-+      {0x30eb, 0x0c},
-+      {0x30eb, 0x05},
-+      {0x300a, 0xff},
-+      {0x300b, 0xff},
-+      {0x30eb, 0x05},
-+      {0x30eb, 0x09},
-+      {0x0114, 0x01},
-+      {0x0128, 0x00},
-+      {0x012a, 0x18},
-+      {0x012b, 0x00},
-+      {0x0164, 0x00},
-+      {0x0165, 0x00},
-+      {0x0166, 0x0c},
-+      {0x0167, 0xcf},
-+      {0x0168, 0x00},
-+      {0x0169, 0x00},
-+      {0x016a, 0x09},
-+      {0x016b, 0x9f},
-+      {0x016c, 0x06},
-+      {0x016d, 0x68},
-+      {0x016e, 0x04},
-+      {0x016f, 0xd0},
-+      {0x0170, 0x01},
-+      {0x0171, 0x01},
-+      {0x0174, 0x01},
-+      {0x0175, 0x01},
-+      {0x018c, 0x0a},
-+      {0x018d, 0x0a},
-+      {0x0301, 0x05},
-+      {0x0303, 0x01},
-+      {0x0304, 0x03},
-+      {0x0305, 0x03},
-+      {0x0306, 0x00},
-+      {0x0307, 0x39},
-+      {0x0309, 0x0a},
-+      {0x030b, 0x01},
-+      {0x030c, 0x00},
-+      {0x030d, 0x72},
-+      {0x0624, 0x06},
-+      {0x0625, 0x68},
-+      {0x0626, 0x04},
-+      {0x0627, 0xd0},
-+      {0x455e, 0x00},
-+      {0x471e, 0x4b},
-+      {0x4767, 0x0f},
-+      {0x4750, 0x14},
-+      {0x4540, 0x00},
-+      {0x47b4, 0x14},
-+      {0x4713, 0x30},
-+      {0x478b, 0x10},
-+      {0x478f, 0x10},
-+      {0x4793, 0x10},
-+      {0x4797, 0x0e},
-+      {0x479b, 0x0e},
-+      {0x0162, 0x0d},
-+      {0x0163, 0x78},
-+};
-+
-+static const char * const imx219_test_pattern_menu[] = {
-+      "Disabled",
-+      "Color Bars",
-+      "Solid Color",
-+      "Grey Color Bars",
-+      "PN9"
-+};
-+
-+static const int imx219_test_pattern_val[] = {
-+      IMX219_TEST_PATTERN_DISABLE,
-+      IMX219_TEST_PATTERN_COLOR_BARS,
-+      IMX219_TEST_PATTERN_SOLID_COLOR,
-+      IMX219_TEST_PATTERN_GREY_COLOR,
-+      IMX219_TEST_PATTERN_PN9,
-+};
-+
-+/* regulator supplies */
-+static const char * const imx219_supply_name[] = {
-+      /* Supplies can be enabled in any order */
-+      "VANA",  /* Analog (2.8V) supply */
-+      "VDIG",  /* Digital Core (1.8V) supply */
-+      "VDDL",  /* IF (1.2V) supply */
-+};
-+
-+#define IMX219_NUM_SUPPLIES ARRAY_SIZE(imx219_supply_name)
-+
-+/*
-+ * Initialisation delay between XCLR low->high and the moment when the sensor
-+ * can start capture (i.e. can leave software stanby) must be not less than:
-+ *   t4 + max(t5, t6 + <time to initialize the sensor register over I2C>)
-+ * where
-+ *   t4 is fixed, and is max 200uS,
-+ *   t5 is fixed, and is 6000uS,
-+ *   t6 depends on the sensor external clock, and is max 32000 clock periods.
-+ * As per sensor datasheet, the external clock must be from 6MHz to 27MHz.
-+ * So for any acceptable external clock t6 is always within the range of
-+ * 1185 to 5333 uS, and is always less than t5.
-+ * For this reason this is always safe to wait (t4 + t5) = 6200 uS, then
-+ * initialize the sensor over I2C, and then exit the software standby.
-+ *
-+ * This start-up time can be optimized a bit more, if we start the writes
-+ * over I2C after (t4+t6), but before (t4+t5) expires. But then sensor
-+ * initialization over I2C may complete before (t4+t5) expires, and we must
-+ * ensure that capture is not started before (t4+t5).
-+ *
-+ * This delay doesn't account for the power supply startup time. If needed,
-+ * this should be taken care of via the regulator framework. E.g. in the
-+ * case of DT for regulator-fixed one should define the startup-delay-us
-+ * property.
-+ */
-+#define IMX219_XCLR_MIN_DELAY_US      6200
-+#define IMX219_XCLR_DELAY_RANGE_US    1000
-+
-+/* Mode configs */
-+static const struct imx219_mode supported_modes[] = {
-+      {
-+              /* 8MPix 15fps mode */
-+              .width = 3280,
-+              .height = 2464,
-+              .vts_def = IMX219_VTS_15FPS,
-+              .reg_list = {
-+                      .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
-+                      .regs = mode_3280x2464_regs,
-+              },
-+      },
-+      {
-+              /* 1080P 30fps cropped */
-+              .width = 1920,
-+              .height = 1080,
-+              .vts_def = IMX219_VTS_30FPS_1080P,
-+              .reg_list = {
-+                      .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
-+                      .regs = mode_1920_1080_regs,
-+              },
-+      },
-+      {
-+              /* 2x2 binned 30fps mode */
-+              .width = 1640,
-+              .height = 1232,
-+              .vts_def = IMX219_VTS_30FPS_BINNED,
-+              .reg_list = {
-+                      .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
-+                      .regs = mode_1640_1232_regs,
-+              },
-+      },
-+};
-+
-+struct imx219 {
-+      struct v4l2_subdev sd;
-+      struct media_pad pad;
-+
-+      struct clk *xclk; /* system clock to IMX219 */
-+      u32 xclk_freq;
-+
-+      struct gpio_desc *reset_gpio;
-+      struct regulator_bulk_data supplies[IMX219_NUM_SUPPLIES];
-+
-+      struct v4l2_ctrl_handler ctrl_handler;
-+      /* V4L2 Controls */
-+      struct v4l2_ctrl *pixel_rate;
-+      struct v4l2_ctrl *exposure;
-+      struct v4l2_ctrl *vflip;
-+      struct v4l2_ctrl *hflip;
-+      struct v4l2_ctrl *vblank;
-+      struct v4l2_ctrl *hblank;
-+
-+      /* Current mode */
-+      const struct imx219_mode *mode;
-+
-+      /*
-+       * Mutex for serialized access:
-+       * Protect sensor module set pad format and start/stop streaming safely.
-+       */
-+      struct mutex mutex;
-+
-+      /* Streaming on/off */
-+      bool streaming;
-+};
-+
-+static inline struct imx219 *to_imx219(struct v4l2_subdev *_sd)
-+{
-+      return container_of(_sd, struct imx219, sd);
-+}
-+
-+/* Read registers up to 2 at a time */
-+static int imx219_read_reg(struct imx219 *imx219, u16 reg, u32 len, u32 *val)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      struct i2c_msg msgs[2];
-+      u8 addr_buf[2] = { reg >> 8, reg & 0xff };
-+      u8 data_buf[4] = { 0, };
-+      int ret;
-+
-+      if (len > 4)
-+              return -EINVAL;
-+
-+      /* Write register address */
-+      msgs[0].addr = client->addr;
-+      msgs[0].flags = 0;
-+      msgs[0].len = ARRAY_SIZE(addr_buf);
-+      msgs[0].buf = addr_buf;
-+
-+      /* Read data from register */
-+      msgs[1].addr = client->addr;
-+      msgs[1].flags = I2C_M_RD;
-+      msgs[1].len = len;
-+      msgs[1].buf = &data_buf[4 - len];
-+
-+      ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
-+      if (ret != ARRAY_SIZE(msgs))
-+              return -EIO;
-+
-+      *val = get_unaligned_be32(data_buf);
-+
-+      return 0;
-+}
-+
-+/* Write registers up to 2 at a time */
-+static int imx219_write_reg(struct imx219 *imx219, u16 reg, u32 len, u32 val)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      u8 buf[6];
-+
-+      if (len > 4)
-+              return -EINVAL;
-+
-+      put_unaligned_be16(reg, buf);
-+      put_unaligned_be32(val << (8 * (4 - len)), buf + 2);
-+      if (i2c_master_send(client, buf, len + 2) != len + 2)
-+              return -EIO;
-+
-+      return 0;
-+}
-+
-+/* Write a list of registers */
-+static int imx219_write_regs(struct imx219 *imx219,
-+                           const struct imx219_reg *regs, u32 len)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      unsigned int i;
-+      int ret;
-+
-+      for (i = 0; i < len; i++) {
-+              ret = imx219_write_reg(imx219, regs[i].address, 1, regs[i].val);
-+              if (ret) {
-+                      dev_err_ratelimited(&client->dev,
-+                                          "Failed to write reg 0x%4.4x. error = %d\n",
-+                                          regs[i].address, ret);
-+
-+                      return ret;
-+              }
-+      }
-+
-+      return 0;
-+}
-+
-+/* Get bayer order based on flip setting. */
-+static u32 imx219_get_format_code(struct imx219 *imx219)
-+{
-+      /*
-+       * Only one bayer order is supported.
-+       * It depends on the flip settings.
-+       */
-+      static const u32 codes[2][2] = {
-+              { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, },
-+              { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, },
-+      };
-+
-+      lockdep_assert_held(&imx219->mutex);
-+      return codes[imx219->vflip->val][imx219->hflip->val];
-+}
-+
-+static int imx219_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+      struct v4l2_mbus_framefmt *try_fmt =
-+              v4l2_subdev_get_try_format(sd, fh->pad, 0);
-+
-+      mutex_lock(&imx219->mutex);
-+
-+      /* Initialize try_fmt */
-+      try_fmt->width = supported_modes[0].width;
-+      try_fmt->height = supported_modes[0].height;
-+      try_fmt->code = imx219_get_format_code(imx219);
-+      try_fmt->field = V4L2_FIELD_NONE;
-+
-+      mutex_unlock(&imx219->mutex);
-+
-+      return 0;
-+}
-+
-+static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
-+{
-+      struct imx219 *imx219 =
-+              container_of(ctrl->handler, struct imx219, ctrl_handler);
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      int ret;
-+
-+      if (ctrl->id == V4L2_CID_VBLANK) {
-+              int exposure_max, exposure_def;
-+
-+              /* Update max exposure while meeting expected vblanking */
-+              exposure_max = imx219->mode->height + ctrl->val - 4;
-+              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+                      exposure_max : IMX219_EXPOSURE_DEFAULT;
-+              __v4l2_ctrl_modify_range(imx219->exposure,
-+                                       imx219->exposure->minimum,
-+                                       exposure_max, imx219->exposure->step,
-+                                       exposure_def);
-+      }
-+
-+      /*
-+       * Applying V4L2 control value only happens
-+       * when power is up for streaming
-+       */
-+      if (pm_runtime_get_if_in_use(&client->dev) == 0)
-+              return 0;
-+
-+      switch (ctrl->id) {
-+      case V4L2_CID_ANALOGUE_GAIN:
-+              ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
-+                                     IMX219_REG_VALUE_08BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_EXPOSURE:
-+              ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_DIGITAL_GAIN:
-+              ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_TEST_PATTERN:
-+              ret = imx219_write_reg(imx219, IMX219_REG_TEST_PATTERN,
-+                                     IMX219_REG_VALUE_16BIT,
-+                                     imx219_test_pattern_val[ctrl->val]);
-+              break;
-+      case V4L2_CID_HFLIP:
-+      case V4L2_CID_VFLIP:
-+              ret = imx219_write_reg(imx219, IMX219_REG_ORIENTATION, 1,
-+                                     imx219->hflip->val |
-+                                     imx219->vflip->val << 1);
-+              break;
-+      case V4L2_CID_VBLANK:
-+              ret = imx219_write_reg(imx219, IMX219_REG_VTS,
-+                                     IMX219_REG_VALUE_16BIT,
-+                                     imx219->mode->height + ctrl->val);
-+              break;
-+      case V4L2_CID_TEST_PATTERN_RED:
-+              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_RED,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_TEST_PATTERN_GREENR:
-+              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENR,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_TEST_PATTERN_BLUE:
-+              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_BLUE,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      case V4L2_CID_TEST_PATTERN_GREENB:
-+              ret = imx219_write_reg(imx219, IMX219_REG_TESTP_GREENB,
-+                                     IMX219_REG_VALUE_16BIT, ctrl->val);
-+              break;
-+      default:
-+              dev_info(&client->dev,
-+                       "ctrl(id:0x%x,val:0x%x) is not handled\n",
-+                       ctrl->id, ctrl->val);
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      pm_runtime_put(&client->dev);
-+
-+      return ret;
-+}
-+
-+static const struct v4l2_ctrl_ops imx219_ctrl_ops = {
-+      .s_ctrl = imx219_set_ctrl,
-+};
-+
-+static int imx219_enum_mbus_code(struct v4l2_subdev *sd,
-+                               struct v4l2_subdev_pad_config *cfg,
-+                               struct v4l2_subdev_mbus_code_enum *code)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+
-+      /*
-+       * Only one bayer order is supported (though it depends on the flip
-+       * settings)
-+       */
-+      if (code->index > 0)
-+              return -EINVAL;
-+
-+      code->code = imx219_get_format_code(imx219);
-+
-+      return 0;
-+}
-+
-+static int imx219_enum_frame_size(struct v4l2_subdev *sd,
-+                                struct v4l2_subdev_pad_config *cfg,
-+                                struct v4l2_subdev_frame_size_enum *fse)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+
-+      if (fse->index >= ARRAY_SIZE(supported_modes))
-+              return -EINVAL;
-+
-+      if (fse->code != imx219_get_format_code(imx219))
-+              return -EINVAL;
-+
-+      fse->min_width = supported_modes[fse->index].width;
-+      fse->max_width = fse->min_width;
-+      fse->min_height = supported_modes[fse->index].height;
-+      fse->max_height = fse->min_height;
-+
-+      return 0;
-+}
-+
-+static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
-+{
-+      fmt->colorspace = V4L2_COLORSPACE_SRGB;
-+      fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
-+      fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
-+                                                        fmt->colorspace,
-+                                                        fmt->ycbcr_enc);
-+      fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
-+}
-+
-+static void imx219_update_pad_format(struct imx219 *imx219,
-+                                   const struct imx219_mode *mode,
-+                                   struct v4l2_subdev_format *fmt)
-+{
-+      fmt->format.width = mode->width;
-+      fmt->format.height = mode->height;
-+      fmt->format.code = imx219_get_format_code(imx219);
-+      fmt->format.field = V4L2_FIELD_NONE;
-+
-+      imx219_reset_colorspace(&fmt->format);
-+}
-+
-+static int __imx219_get_pad_format(struct imx219 *imx219,
-+                                 struct v4l2_subdev_pad_config *cfg,
-+                                 struct v4l2_subdev_format *fmt)
-+{
-+      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-+              struct v4l2_mbus_framefmt *try_fmt =
-+                      v4l2_subdev_get_try_format(&imx219->sd, cfg, fmt->pad);
-+              /* update the code which could change due to vflip or hflip: */
-+              try_fmt->code = imx219_get_format_code(imx219);
-+              fmt->format = *try_fmt;
-+      } else {
-+              imx219_update_pad_format(imx219, imx219->mode, fmt);
-+      }
-+
-+      return 0;
-+}
-+
-+static int imx219_get_pad_format(struct v4l2_subdev *sd,
-+                               struct v4l2_subdev_pad_config *cfg,
-+                               struct v4l2_subdev_format *fmt)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+      int ret;
-+
-+      mutex_lock(&imx219->mutex);
-+      ret = __imx219_get_pad_format(imx219, cfg, fmt);
-+      mutex_unlock(&imx219->mutex);
-+
-+      return ret;
-+}
-+
-+static int imx219_set_pad_format(struct v4l2_subdev *sd,
-+                               struct v4l2_subdev_pad_config *cfg,
-+                               struct v4l2_subdev_format *fmt)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+      const struct imx219_mode *mode;
-+      struct v4l2_mbus_framefmt *framefmt;
-+      int exposure_max, exposure_def, hblank;
-+
-+      mutex_lock(&imx219->mutex);
-+
-+      /* Bayer order varies with flips */
-+      fmt->format.code = imx219_get_format_code(imx219);
-+
-+      mode = v4l2_find_nearest_size(supported_modes,
-+                                    ARRAY_SIZE(supported_modes),
-+                                    width, height,
-+                                    fmt->format.width, fmt->format.height);
-+      imx219_update_pad_format(imx219, mode, fmt);
-+      if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
-+              framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
-+              *framefmt = fmt->format;
-+      } else if (imx219->mode != mode) {
-+              imx219->mode = mode;
-+              /* Update limits and set FPS to default */
-+              __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
-+                                       IMX219_VTS_MAX - mode->height, 1,
-+                                       mode->vts_def - mode->height);
-+              __v4l2_ctrl_s_ctrl(imx219->vblank,
-+                                 mode->vts_def - mode->height);
-+              /* Update max exposure while meeting expected vblanking */
-+              exposure_max = mode->vts_def - 4;
-+              exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+                      exposure_max : IMX219_EXPOSURE_DEFAULT;
-+              __v4l2_ctrl_modify_range(imx219->exposure,
-+                                       imx219->exposure->minimum,
-+                                       exposure_max, imx219->exposure->step,
-+                                       exposure_def);
-+              /*
-+               * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
-+               * depends on mode->width only, and is not changeble in any
-+               * way other than changing the mode.
-+               */
-+              hblank = IMX219_PPL_DEFAULT - mode->width;
-+              __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
-+                                       hblank);
-+      }
-+
-+      mutex_unlock(&imx219->mutex);
-+
-+      return 0;
-+}
-+
-+static int imx219_start_streaming(struct imx219 *imx219)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      const struct imx219_reg_list *reg_list;
-+      int ret;
-+
-+      /* Apply default values of current mode */
-+      reg_list = &imx219->mode->reg_list;
-+      ret = imx219_write_regs(imx219, reg_list->regs, reg_list->num_of_regs);
-+      if (ret) {
-+              dev_err(&client->dev, "%s failed to set mode\n", __func__);
-+              return ret;
-+      }
-+
-+      /* Apply customized values from user */
-+      ret =  __v4l2_ctrl_handler_setup(imx219->sd.ctrl_handler);
-+      if (ret)
-+              return ret;
-+
-+      /* set stream on register */
-+      return imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
-+                              IMX219_REG_VALUE_08BIT, IMX219_MODE_STREAMING);
-+}
-+
-+static void imx219_stop_streaming(struct imx219 *imx219)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      int ret;
-+
-+      /* set stream off register */
-+      ret = imx219_write_reg(imx219, IMX219_REG_MODE_SELECT,
-+                             IMX219_REG_VALUE_08BIT, IMX219_MODE_STANDBY);
-+      if (ret)
-+              dev_err(&client->dev, "%s failed to set stream\n", __func__);
-+}
-+
-+static int imx219_set_stream(struct v4l2_subdev *sd, int enable)
-+{
-+      struct imx219 *imx219 = to_imx219(sd);
-+      struct i2c_client *client = v4l2_get_subdevdata(sd);
-+      int ret = 0;
-+
-+      mutex_lock(&imx219->mutex);
-+      if (imx219->streaming == enable) {
-+              mutex_unlock(&imx219->mutex);
-+              return 0;
-+      }
-+
-+      if (enable) {
-+              ret = pm_runtime_get_sync(&client->dev);
-+              if (ret < 0) {
-+                      pm_runtime_put_noidle(&client->dev);
-+                      goto err_unlock;
-+              }
-+
-+              /*
-+               * Apply default & customized values
-+               * and then start streaming.
-+               */
-+              ret = imx219_start_streaming(imx219);
-+              if (ret)
-+                      goto err_rpm_put;
-+      } else {
-+              imx219_stop_streaming(imx219);
-+              pm_runtime_put(&client->dev);
-+      }
-+
-+      imx219->streaming = enable;
-+
-+      /* vflip and hflip cannot change during streaming */
-+      __v4l2_ctrl_grab(imx219->vflip, enable);
-+      __v4l2_ctrl_grab(imx219->hflip, enable);
-+
-+      mutex_unlock(&imx219->mutex);
-+
-+      return ret;
-+
-+err_rpm_put:
-+      pm_runtime_put(&client->dev);
-+err_unlock:
-+      mutex_unlock(&imx219->mutex);
-+
-+      return ret;
-+}
-+
-+/* Power/clock management functions */
-+static int imx219_power_on(struct device *dev)
-+{
-+      struct i2c_client *client = to_i2c_client(dev);
-+      struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+      struct imx219 *imx219 = to_imx219(sd);
-+      int ret;
-+
-+      ret = regulator_bulk_enable(IMX219_NUM_SUPPLIES,
-+                                  imx219->supplies);
-+      if (ret) {
-+              dev_err(&client->dev, "%s: failed to enable regulators\n",
-+                      __func__);
-+              return ret;
-+      }
-+
-+      ret = clk_prepare_enable(imx219->xclk);
-+      if (ret) {
-+              dev_err(&client->dev, "%s: failed to enable clock\n",
-+                      __func__);
-+              goto reg_off;
-+      }
-+
-+      gpiod_set_value_cansleep(imx219->reset_gpio, 1);
-+      usleep_range(IMX219_XCLR_MIN_DELAY_US,
-+                   IMX219_XCLR_MIN_DELAY_US + IMX219_XCLR_DELAY_RANGE_US);
-+
-+      return 0;
-+
-+reg_off:
-+      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
-+
-+      return ret;
-+}
-+
-+static int imx219_power_off(struct device *dev)
-+{
-+      struct i2c_client *client = to_i2c_client(dev);
-+      struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+      struct imx219 *imx219 = to_imx219(sd);
-+
-+      gpiod_set_value_cansleep(imx219->reset_gpio, 0);
-+      regulator_bulk_disable(IMX219_NUM_SUPPLIES, imx219->supplies);
-+      clk_disable_unprepare(imx219->xclk);
-+
-+      return 0;
-+}
-+
-+static int __maybe_unused imx219_suspend(struct device *dev)
-+{
-+      struct i2c_client *client = to_i2c_client(dev);
-+      struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+      struct imx219 *imx219 = to_imx219(sd);
-+
-+      if (imx219->streaming)
-+              imx219_stop_streaming(imx219);
-+
-+      return 0;
-+}
-+
-+static int __maybe_unused imx219_resume(struct device *dev)
-+{
-+      struct i2c_client *client = to_i2c_client(dev);
-+      struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+      struct imx219 *imx219 = to_imx219(sd);
-+      int ret;
-+
-+      if (imx219->streaming) {
-+              ret = imx219_start_streaming(imx219);
-+              if (ret)
-+                      goto error;
-+      }
-+
-+      return 0;
-+
-+error:
-+      imx219_stop_streaming(imx219);
-+      imx219->streaming = 0;
-+
-+      return ret;
-+}
-+
-+static int imx219_get_regulators(struct imx219 *imx219)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      unsigned int i;
-+
-+      for (i = 0; i < IMX219_NUM_SUPPLIES; i++)
-+              imx219->supplies[i].supply = imx219_supply_name[i];
-+
-+      return devm_regulator_bulk_get(&client->dev,
-+                                     IMX219_NUM_SUPPLIES,
-+                                     imx219->supplies);
-+}
-+
-+/* Verify chip ID */
-+static int imx219_identify_module(struct imx219 *imx219)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      int ret;
-+      u32 val;
-+
-+      ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID,
-+                            IMX219_REG_VALUE_16BIT, &val);
-+      if (ret) {
-+              dev_err(&client->dev, "failed to read chip id %x\n",
-+                      IMX219_CHIP_ID);
-+              return ret;
-+      }
-+
-+      if (val != IMX219_CHIP_ID) {
-+              dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
-+                      IMX219_CHIP_ID, val);
-+              return -EIO;
-+      }
-+
-+      return 0;
-+}
-+
-+static const struct v4l2_subdev_core_ops imx219_core_ops = {
-+      .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
-+      .unsubscribe_event = v4l2_event_subdev_unsubscribe,
-+};
-+
-+static const struct v4l2_subdev_video_ops imx219_video_ops = {
-+      .s_stream = imx219_set_stream,
-+};
-+
-+static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
-+      .enum_mbus_code = imx219_enum_mbus_code,
-+      .get_fmt = imx219_get_pad_format,
-+      .set_fmt = imx219_set_pad_format,
-+      .enum_frame_size = imx219_enum_frame_size,
-+};
-+
-+static const struct v4l2_subdev_ops imx219_subdev_ops = {
-+      .core = &imx219_core_ops,
-+      .video = &imx219_video_ops,
-+      .pad = &imx219_pad_ops,
-+};
-+
-+static const struct v4l2_subdev_internal_ops imx219_internal_ops = {
-+      .open = imx219_open,
-+};
-+
-+/* Initialize control handlers */
-+static int imx219_init_controls(struct imx219 *imx219)
-+{
-+      struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
-+      struct v4l2_ctrl_handler *ctrl_hdlr;
-+      unsigned int height = imx219->mode->height;
-+      int exposure_max, exposure_def, hblank;
-+      int i, ret;
-+
-+      ctrl_hdlr = &imx219->ctrl_handler;
-+      ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
-+      if (ret)
-+              return ret;
-+
-+      mutex_init(&imx219->mutex);
-+      ctrl_hdlr->lock = &imx219->mutex;
-+
-+      /* By default, PIXEL_RATE is read only */
-+      imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                             V4L2_CID_PIXEL_RATE,
-+                                             IMX219_PIXEL_RATE,
-+                                             IMX219_PIXEL_RATE, 1,
-+                                             IMX219_PIXEL_RATE);
-+
-+      /* Initial vblank/hblank/exposure parameters based on current mode */
-+      imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                         V4L2_CID_VBLANK, IMX219_VBLANK_MIN,
-+                                         IMX219_VTS_MAX - height, 1,
-+                                         imx219->mode->vts_def - height);
-+      hblank = IMX219_PPL_DEFAULT - imx219->mode->width;
-+      imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                         V4L2_CID_HBLANK, hblank, hblank,
-+                                         1, hblank);
-+      if (imx219->hblank)
-+              imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-+      exposure_max = imx219->mode->vts_def - 4;
-+      exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
-+              exposure_max : IMX219_EXPOSURE_DEFAULT;
-+      imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                           V4L2_CID_EXPOSURE,
-+                                           IMX219_EXPOSURE_MIN, exposure_max,
-+                                           IMX219_EXPOSURE_STEP,
-+                                           exposure_def);
-+
-+      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
-+                        IMX219_ANA_GAIN_MIN, IMX219_ANA_GAIN_MAX,
-+                        IMX219_ANA_GAIN_STEP, IMX219_ANA_GAIN_DEFAULT);
-+
-+      v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
-+                        IMX219_DGTL_GAIN_MIN, IMX219_DGTL_GAIN_MAX,
-+                        IMX219_DGTL_GAIN_STEP, IMX219_DGTL_GAIN_DEFAULT);
-+
-+      imx219->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                        V4L2_CID_HFLIP, 0, 1, 1, 0);
-+      if (imx219->hflip)
-+              imx219->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-+
-+      imx219->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                        V4L2_CID_VFLIP, 0, 1, 1, 0);
-+      if (imx219->vflip)
-+              imx219->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
-+
-+      v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx219_ctrl_ops,
-+                                   V4L2_CID_TEST_PATTERN,
-+                                   ARRAY_SIZE(imx219_test_pattern_menu) - 1,
-+                                   0, 0, imx219_test_pattern_menu);
-+      for (i = 0; i < 4; i++) {
-+              /*
-+               * The assumption is that
-+               * V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1
-+               * V4L2_CID_TEST_PATTERN_BLUE   == V4L2_CID_TEST_PATTERN_RED + 2
-+               * V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3
-+               */
-+              v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
-+                                V4L2_CID_TEST_PATTERN_RED + i,
-+                                IMX219_TESTP_COLOUR_MIN,
-+                                IMX219_TESTP_COLOUR_MAX,
-+                                IMX219_TESTP_COLOUR_STEP,
-+                                IMX219_TESTP_COLOUR_MAX);
-+              /* The "Solid color" pattern is white by default */
-+      }
-+
-+      if (ctrl_hdlr->error) {
-+              ret = ctrl_hdlr->error;
-+              dev_err(&client->dev, "%s control init failed (%d)\n",
-+                      __func__, ret);
-+              goto error;
-+      }
-+
-+      imx219->sd.ctrl_handler = ctrl_hdlr;
-+
-+      return 0;
-+
-+error:
-+      v4l2_ctrl_handler_free(ctrl_hdlr);
-+      mutex_destroy(&imx219->mutex);
-+
-+      return ret;
-+}
-+
-+static void imx219_free_controls(struct imx219 *imx219)
-+{
-+      v4l2_ctrl_handler_free(imx219->sd.ctrl_handler);
-+      mutex_destroy(&imx219->mutex);
-+}
-+
-+static int imx219_check_hwcfg(struct device *dev)
-+{
-+      struct fwnode_handle *endpoint;
-+      struct v4l2_fwnode_endpoint ep_cfg = {
-+              .bus_type = V4L2_MBUS_CSI2_DPHY
-+      };
-+      int ret = -EINVAL;
-+
-+      endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
-+      if (!endpoint) {
-+              dev_err(dev, "endpoint node not found\n");
-+              return -EINVAL;
-+      }
-+
-+      if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) {
-+              dev_err(dev, "could not parse endpoint\n");
-+              goto error_out;
-+      }
-+
-+      /* Check the number of MIPI CSI2 data lanes */
-+      if (ep_cfg.bus.mipi_csi2.num_data_lanes != 2) {
-+              dev_err(dev, "only 2 data lanes are currently supported\n");
-+              goto error_out;
-+      }
-+
-+      /* Check the link frequency set in device tree */
-+      if (!ep_cfg.nr_of_link_frequencies) {
-+              dev_err(dev, "link-frequency property not found in DT\n");
-+              goto error_out;
-+      }
-+
-+      if (ep_cfg.nr_of_link_frequencies != 1 ||
-+          ep_cfg.link_frequencies[0] != IMX219_DEFAULT_LINK_FREQ) {
-+              dev_err(dev, "Link frequency not supported: %lld\n",
-+                      ep_cfg.link_frequencies[0]);
-+              goto error_out;
-+      }
-+
-+      ret = 0;
-+
-+error_out:
-+      v4l2_fwnode_endpoint_free(&ep_cfg);
-+      fwnode_handle_put(endpoint);
-+
-+      return ret;
-+}
-+
-+static int imx219_probe(struct i2c_client *client)
-+{
-+      struct device *dev = &client->dev;
-+      struct imx219 *imx219;
-+      int ret;
-+
-+      imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL);
-+      if (!imx219)
-+              return -ENOMEM;
-+
-+      v4l2_i2c_subdev_init(&imx219->sd, client, &imx219_subdev_ops);
-+
-+      /* Check the hardware configuration in device tree */
-+      if (imx219_check_hwcfg(dev))
-+              return -EINVAL;
-+
-+      /* Get system clock (xclk) */
-+      imx219->xclk = devm_clk_get(dev, NULL);
-+      if (IS_ERR(imx219->xclk)) {
-+              dev_err(dev, "failed to get xclk\n");
-+              return PTR_ERR(imx219->xclk);
-+      }
-+
-+      imx219->xclk_freq = clk_get_rate(imx219->xclk);
-+      if (imx219->xclk_freq != IMX219_XCLK_FREQ) {
-+              dev_err(dev, "xclk frequency not supported: %d Hz\n",
-+                      imx219->xclk_freq);
-+              return -EINVAL;
-+      }
-+
-+      ret = imx219_get_regulators(imx219);
-+      if (ret) {
-+              dev_err(dev, "failed to get regulators\n");
-+              return ret;
-+      }
-+
-+      /* Request optional enable pin */
-+      imx219->reset_gpio = devm_gpiod_get_optional(dev, "reset",
-+                                                   GPIOD_OUT_HIGH);
-+
-+      /*
-+       * The sensor must be powered for imx219_identify_module()
-+       * to be able to read the CHIP_ID register
-+       */
-+      ret = imx219_power_on(dev);
-+      if (ret)
-+              return ret;
-+
-+      ret = imx219_identify_module(imx219);
-+      if (ret)
-+              goto error_power_off;
-+
-+      /* Set default mode to max resolution */
-+      imx219->mode = &supported_modes[0];
-+
-+      ret = imx219_init_controls(imx219);
-+      if (ret)
-+              goto error_power_off;
-+
-+      /* Initialize subdev */
-+      imx219->sd.internal_ops = &imx219_internal_ops;
-+      imx219->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-+      imx219->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-+
-+      /* Initialize source pad */
-+      imx219->pad.flags = MEDIA_PAD_FL_SOURCE;
-+
-+      ret = media_entity_pads_init(&imx219->sd.entity, 1, &imx219->pad);
-+      if (ret) {
-+              dev_err(dev, "failed to init entity pads: %d\n", ret);
-+              goto error_handler_free;
-+      }
-+
-+      ret = v4l2_async_register_subdev_sensor_common(&imx219->sd);
-+      if (ret < 0) {
-+              dev_err(dev, "failed to register sensor sub-device: %d\n", ret);
-+              goto error_media_entity;
-+      }
-+
-+      /* Enable runtime PM and turn off the device */
-+      pm_runtime_set_active(dev);
-+      pm_runtime_enable(dev);
-+      pm_runtime_idle(dev);
-+
-+      return 0;
-+
-+error_media_entity:
-+      media_entity_cleanup(&imx219->sd.entity);
-+
-+error_handler_free:
-+      imx219_free_controls(imx219);
-+
-+error_power_off:
-+      imx219_power_off(dev);
-+
-+      return ret;
-+}
-+
-+static int imx219_remove(struct i2c_client *client)
-+{
-+      struct v4l2_subdev *sd = i2c_get_clientdata(client);
-+      struct imx219 *imx219 = to_imx219(sd);
-+
-+      v4l2_async_unregister_subdev(sd);
-+      media_entity_cleanup(&sd->entity);
-+      imx219_free_controls(imx219);
-+
-+      pm_runtime_disable(&client->dev);
-+      if (!pm_runtime_status_suspended(&client->dev))
-+              imx219_power_off(&client->dev);
-+      pm_runtime_set_suspended(&client->dev);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id imx219_dt_ids[] = {
-+      { .compatible = "sony,imx219" },
-+      { /* sentinel */ }
-+};
-+MODULE_DEVICE_TABLE(of, imx219_dt_ids);
-+
-+static const struct dev_pm_ops imx219_pm_ops = {
-+      SET_SYSTEM_SLEEP_PM_OPS(imx219_suspend, imx219_resume)
-+      SET_RUNTIME_PM_OPS(imx219_power_off, imx219_power_on, NULL)
-+};
-+
-+static struct i2c_driver imx219_i2c_driver = {
-+      .driver = {
-+              .name = "imx219",
-+              .of_match_table = imx219_dt_ids,
-+              .pm = &imx219_pm_ops,
-+      },
-+      .probe_new = imx219_probe,
-+      .remove = imx219_remove,
-+};
-+
-+module_i2c_driver(imx219_i2c_driver);
-+
-+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com");
-+MODULE_DESCRIPTION("Sony IMX219 sensor driver");
-+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0464-overlays-imx219-Correct-link-frequency-to-match-the-.patch
new file mode 100644 (file)
index 0000000..e9eed21
--- /dev/null
@@ -0,0 +1,25 @@
+From eae83133532e44adae2f632b2168119a4c7249a2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Mar 2020 12:07:57 +0000
+Subject: [PATCH] overlays: imx219: Correct link frequency to match the
+ upstream driver
+
+The upstream driver is checking the link frequency parameter, and
+the overlay had the wrong value.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx219-overlay.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -40,7 +40,7 @@
+                                               data-lanes = <1 2>;
+                                               clock-noncontinuous;
+                                               link-frequencies =
+-                                                      /bits/ 64 <297000000>;
++                                                      /bits/ 64 <456000000>;
+                                       };
+                               };
+                       };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch b/target/linux/bcm27xx/patches-5.4/950-0465-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
new file mode 100644 (file)
index 0000000..5795d47
--- /dev/null
@@ -0,0 +1,26 @@
+From 61113c5463166d734dc6560574f5fb1536bae795 Mon Sep 17 00:00:00 2001
+From: Nataliya Korovkina <malus.brandywine@gmail.com>
+Date: Thu, 12 Mar 2020 17:22:53 -0400
+Subject: [PATCH] Kbuild: Allow .dtbo overlays to be built, adjust.
+
+This is adjustment to commit
+d368ceaacdccd7732dc97d1d7987bdf7149d62e3 "kbuild: Allow .dtbo overlays to be built piecemeal"
+
+prepare3 target has gone from mainline tree in branch 5.4.y
+
+Signed-off-by: Nataliya Korovkina <malus.brandywine@gmail.com>
+---
+ Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -1238,7 +1238,7 @@ ifneq ($(dtstree),)
+ %.dtb: include/config/kernel.release scripts_dtc
+       $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
+-%.dtbo: prepare3 scripts_dtc
++%.dtbo: include/config/kernel.release scripts_dtc
+       $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
+ PHONY += dtbs dtbs_install dtbs_check
diff --git a/target/linux/bcm27xx/patches-5.4/950-0465-overlays-imx219-Correct-link-frequency-to-match-the-.patch b/target/linux/bcm27xx/patches-5.4/950-0465-overlays-imx219-Correct-link-frequency-to-match-the-.patch
deleted file mode 100644 (file)
index e9eed21..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From eae83133532e44adae2f632b2168119a4c7249a2 Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Wed, 11 Mar 2020 12:07:57 +0000
-Subject: [PATCH] overlays: imx219: Correct link frequency to match the
- upstream driver
-
-The upstream driver is checking the link frequency parameter, and
-the overlay had the wrong value.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- arch/arm/boot/dts/overlays/imx219-overlay.dts | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
-@@ -40,7 +40,7 @@
-                                               data-lanes = <1 2>;
-                                               clock-noncontinuous;
-                                               link-frequencies =
--                                                      /bits/ 64 <297000000>;
-+                                                      /bits/ 64 <456000000>;
-                                       };
-                               };
-                       };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0466-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch b/target/linux/bcm27xx/patches-5.4/950-0466-Kbuild-Allow-.dtbo-overlays-to-be-built-adjust.patch
deleted file mode 100644 (file)
index 5795d47..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-From 61113c5463166d734dc6560574f5fb1536bae795 Mon Sep 17 00:00:00 2001
-From: Nataliya Korovkina <malus.brandywine@gmail.com>
-Date: Thu, 12 Mar 2020 17:22:53 -0400
-Subject: [PATCH] Kbuild: Allow .dtbo overlays to be built, adjust.
-
-This is adjustment to commit
-d368ceaacdccd7732dc97d1d7987bdf7149d62e3 "kbuild: Allow .dtbo overlays to be built piecemeal"
-
-prepare3 target has gone from mainline tree in branch 5.4.y
-
-Signed-off-by: Nataliya Korovkina <malus.brandywine@gmail.com>
----
- Makefile | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/Makefile
-+++ b/Makefile
-@@ -1238,7 +1238,7 @@ ifneq ($(dtstree),)
- %.dtb: include/config/kernel.release scripts_dtc
-       $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
--%.dtbo: prepare3 scripts_dtc
-+%.dtbo: include/config/kernel.release scripts_dtc
-       $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@
- PHONY += dtbs dtbs_install dtbs_check
diff --git a/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch b/target/linux/bcm27xx/patches-5.4/950-0466-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
new file mode 100644 (file)
index 0000000..c7e10cb
--- /dev/null
@@ -0,0 +1,74 @@
+From 23f717168dc55f69ad517d3ab5f412d04a5afc0e Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.org>
+Date: Wed, 15 Jan 2020 13:40:38 +0000
+Subject: [PATCH] media: ov5647: Fix return codes from
+ ov5647_write/ov5647_read functions.
+
+Previously they were returning positive non-zero codes for success,
+which were getting passed up the call stack. Since release 4.19,
+do_dentry_open (fs/open.c) has been catching these and flagging an
+error. (So this driver has been broken since that date.)
+
+Fixes: 3c2472a [media] media: i2c: Add support for OV5647 sensor
+Signed-off-by: David Plowman <david.plowman@raspberrypi.org>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 30 +++++++++++++++++++++++++++---
+ 1 file changed, 27 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -214,9 +214,18 @@ static int ov5647_write(struct v4l2_subd
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       ret = i2c_master_send(client, data, 3);
+-      if (ret < 0)
++      /*
++       * Writing the wrong number of bytes also needs to be flagged as an
++       * error. Success needs to produce a 0 return code.
++       */
++      if (ret == 3) {
++              ret = 0;
++      } else {
+               dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+                               __func__, reg);
++              if (ret >= 0)
++                      ret = -EINVAL;
++      }
+       return ret;
+ }
+@@ -228,16 +237,31 @@ static int ov5647_read(struct v4l2_subde
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       ret = i2c_master_send(client, data_w, 2);
+-      if (ret < 0) {
++      /*
++       * A negative return code, or sending the wrong number of bytes, both
++       * count as an error.
++       */
++      if (ret != 2) {
+               dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+                       __func__, reg);
++              if (ret >= 0)
++                      ret = -EINVAL;
+               return ret;
+       }
+       ret = i2c_master_recv(client, val, 1);
+-      if (ret < 0)
++      /*
++       * The only return value indicating success is 1. Anything else, even
++       * a non-negative value, indicates something went wrong.
++       */
++      if (ret == 1) {
++              ret = 0;
++      } else {
+               dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
+                               __func__, reg);
++              if (ret >= 0)
++                      ret = -EINVAL;
++      }
+       return ret;
+ }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
new file mode 100644 (file)
index 0000000..5846e96
--- /dev/null
@@ -0,0 +1,407 @@
+From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:30:53 +0000
+Subject: [PATCH] media: ov5647: Add basic support for multiple sensor
+ modes.
+
+Specifically:
+
+Added a structure ov5647_mode and a list of supported_modes (though no
+actual new modes as yet). The state object points to the "current mode".
+
+ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
+ov5647_get_fmt all needed upgrading to cope with multiple modes.
+
+__sensor_init (which writes all the registers) is now called by
+ov5647_stream_on (once the mode is known) rather than by
+ov5647_sensor_power.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++---------
+ 1 file changed, 202 insertions(+), 66 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -86,13 +86,17 @@ struct regval_list {
+       u8 data;
+ };
++struct ov5647_mode {
++      struct v4l2_mbus_framefmt       format;
++      struct regval_list              *reg_list;
++      unsigned int                    num_regs;
++};
++
+ struct ov5647 {
+       struct v4l2_subdev              sd;
+       struct media_pad                pad;
+       struct mutex                    lock;
+-      struct v4l2_mbus_framefmt       format;
+-      unsigned int                    width;
+-      unsigned int                    height;
++      const struct ov5647_mode        *mode;
+       int                             power_count;
+       struct clk                      *xclk;
+       struct gpio_desc                *pwdn;
+@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480
+       {0x0100, 0x01},
+ };
++static struct ov5647_mode supported_modes_8bit[] = {
++      /*
++       * Original 8-bit VGA mode
++       * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
++       */
++      {
++              {
++                      .code = MEDIA_BUS_FMT_SBGGR8_1X8,
++                      .colorspace = V4L2_COLORSPACE_SRGB,
++                      .field = V4L2_FIELD_NONE,
++                      .width = 640,
++                      .height = 480
++              },
++              ov5647_640x480,
++              ARRAY_SIZE(ov5647_640x480)
++      },
++      /* more modes below here... */
++};
++
++static struct ov5647_mode supported_modes_10bit[] = {
++      /* no 10-bit modes yet */
++};
++
++/* Use original 8-bit VGA mode as default. */
++#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
++
+ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+ {
+       int ret;
+@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st
+       return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
+ }
++static int __sensor_init(struct v4l2_subdev *sd)
++{
++      int ret;
++      u8 resetval, rdval;
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
++      struct ov5647 *state = to_state(sd);
++
++      ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
++      if (ret < 0)
++              return ret;
++
++      ret = ov5647_write_array(sd, state->mode->reg_list,
++                               state->mode->num_regs);
++      if (ret < 0) {
++              dev_err(&client->dev, "write sensor default regs error\n");
++              return ret;
++      }
++
++      ret = ov5647_set_virtual_channel(sd, 0);
++      if (ret < 0)
++              return ret;
++
++      ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
++      if (ret < 0)
++              return ret;
++
++      if (!(resetval & 0x01)) {
++              dev_err(&client->dev, "Device was in SW standby");
++              ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
++              if (ret < 0)
++                      return ret;
++      }
++
++      return 0;
++}
++
+ static int ov5647_stream_on(struct v4l2_subdev *sd)
+ {
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct ov5647 *ov5647 = to_state(sd);
+       u8 val = MIPI_CTRL00_BUS_IDLE;
+       int ret;
++      ret = __sensor_init(sd);
++      if (ret < 0) {
++              dev_err(&client->dev, "sensor_init failed\n");
++              return ret;
++      }
++
+       if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
+               val |= MIPI_CTRL00_CLOCK_LANE_GATE |
+                      MIPI_CTRL00_LINE_SYNC_ENABLE;
+@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su
+       return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
+ }
+-static int __sensor_init(struct v4l2_subdev *sd)
+-{
+-      int ret;
+-      u8 resetval, rdval;
+-      struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+-      ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = ov5647_write_array(sd, ov5647_640x480,
+-                                      ARRAY_SIZE(ov5647_640x480));
+-      if (ret < 0) {
+-              dev_err(&client->dev, "write sensor default regs error\n");
+-              return ret;
+-      }
+-
+-      ret = ov5647_set_virtual_channel(sd, 0);
+-      if (ret < 0)
+-              return ret;
+-
+-      ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
+-      if (ret < 0)
+-              return ret;
+-
+-      if (!(resetval & 0x01)) {
+-              dev_err(&client->dev, "Device was in SW standby");
+-              ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
+-              if (ret < 0)
+-                      return ret;
+-      }
+-
+-      /*
+-       * stream off to make the clock lane into LP-11 state.
+-       */
+-      return ov5647_stream_off(sd);
+-}
+-
+ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
+ {
+       int ret = 0;
+@@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4
+               }
+               ret = ov5647_write_array(sd, sensor_oe_enable_regs,
+-                              ARRAY_SIZE(sensor_oe_enable_regs));
++                                       ARRAY_SIZE(sensor_oe_enable_regs));
+               if (ret < 0) {
+                       clk_disable_unprepare(ov5647->xclk);
+                       dev_err(&client->dev,
+@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4
+                       goto out;
+               }
+-              ret = __sensor_init(sd);
++              /*
++               * Ensure streaming off to make clock lane go into LP-11 state.
++               */
++              ret = ov5647_stream_off(sd);
+               if (ret < 0) {
+                       clk_disable_unprepare(ov5647->xclk);
+                       dev_err(&client->dev,
+@@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4
+               dev_dbg(&client->dev, "OV5647 power off\n");
+               ret = ov5647_write_array(sd, sensor_oe_disable_regs,
+-                              ARRAY_SIZE(sensor_oe_disable_regs));
++                                       ARRAY_SIZE(sensor_oe_disable_regs));
+               if (ret < 0)
+                       dev_dbg(&client->dev, "disable oe failed\n");
+@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops
+ static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
+ {
++      struct ov5647 *state = to_state(sd);
++      int ret = 0;
++
++      mutex_lock(&state->lock);
++
+       if (enable)
+-              return ov5647_stream_on(sd);
++              ret = ov5647_stream_on(sd);
+       else
+-              return ov5647_stream_off(sd);
++              ret = ov5647_stream_off(sd);
++
++      mutex_unlock(&state->lock);
++
++      return ret;
+ }
+ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
+@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct
+                               struct v4l2_subdev_pad_config *cfg,
+                               struct v4l2_subdev_mbus_code_enum *code)
+ {
+-      if (code->index > 0)
++      if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
++              code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
++      else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
++               ARRAY_SIZE(supported_modes_10bit))
++              code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
++               ARRAY_SIZE(supported_modes_10bit))
++              code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
++      else
+               return -EINVAL;
+-      code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
++      return 0;
++}
++
++static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
++                                struct v4l2_subdev_pad_config *cfg,
++                                struct v4l2_subdev_frame_size_enum *fse)
++{
++      struct ov5647_mode *mode = NULL;
++
++      if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
++              if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
++                      return -EINVAL;
++              mode = &supported_modes_8bit[fse->index];
++      } else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
++              if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
++                      return -EINVAL;
++              mode = &supported_modes_10bit[fse->index];
++      } else {
++              return -EINVAL;
++      }
++
++      fse->min_width = mode->format.width;
++      fse->max_width = fse->min_width;
++      fse->min_height = mode->format.height;
++      fse->max_height = fse->min_height;
++
++      return 0;
++}
++
++static int ov5647_set_fmt(struct v4l2_subdev *sd,
++                        struct v4l2_subdev_pad_config *cfg,
++                        struct v4l2_subdev_format *format)
++{
++      struct v4l2_mbus_framefmt *fmt = &format->format;
++      struct ov5647 *state = to_state(sd);
++      struct v4l2_mbus_framefmt *framefmt;
++      const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
++
++      if (format->pad != 0)
++              return -EINVAL;
++
++      mutex_lock(&state->lock);
++
++      /*
++       * Try to respect any given pixel format, otherwise try for a 10-bit
++       * mode.
++       */
++      mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
++                                         ARRAY_SIZE(supported_modes_8bit),
++                                         format.width, format.height,
++                                         format->format.width,
++                                         format->format.height);
++      mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
++                                          ARRAY_SIZE(supported_modes_10bit),
++                                          format.width, format.height,
++                                          format->format.width,
++                                          format->format.height);
++      if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
++              mode = mode_8bit;
++      else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
++               mode_10bit)
++              mode = mode_10bit;
++      else if (mode_10bit)
++              mode = mode_10bit;
++      else
++              mode = mode_8bit;
++
++      if (!mode)
++              return -EINVAL;
++
++      *fmt = mode->format;
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
++              framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
++              *framefmt = format->format;
++      } else {
++              state->mode = mode;
++      }
++
++      mutex_unlock(&state->lock);
+       return 0;
+ }
+-static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
+-                            struct v4l2_subdev_pad_config *cfg,
+-                            struct v4l2_subdev_format *format)
++static int ov5647_get_fmt(struct v4l2_subdev *sd,
++                        struct v4l2_subdev_pad_config *cfg,
++                        struct v4l2_subdev_format *format)
+ {
+       struct v4l2_mbus_framefmt *fmt = &format->format;
++      struct ov5647 *state = to_state(sd);
+       if (format->pad != 0)
+               return -EINVAL;
+-      /* Only one format is supported, so return that */
+-      memset(fmt, 0, sizeof(*fmt));
+-      fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+-      fmt->colorspace = V4L2_COLORSPACE_SRGB;
+-      fmt->field = V4L2_FIELD_NONE;
+-      fmt->width = 640;
+-      fmt->height = 480;
++      mutex_lock(&state->lock);
++
++      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
++              *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
++      else
++              *fmt = state->mode->format;
++
++      mutex_unlock(&state->lock);
+       return 0;
+ }
+ static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
+       .enum_mbus_code = ov5647_enum_mbus_code,
+-      .set_fmt =        ov5647_set_get_fmt,
+-      .get_fmt =        ov5647_set_get_fmt,
++      .set_fmt =        ov5647_set_fmt,
++      .get_fmt =        ov5647_get_fmt,
++      .enum_frame_size = ov5647_enum_frame_size,
+ };
+ static const struct v4l2_subdev_ops ov5647_subdev_ops = {
+@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde
+                               v4l2_subdev_get_try_format(sd, fh->pad, 0);
+       struct v4l2_rect *crop =
+                               v4l2_subdev_get_try_crop(sd, fh->pad, 0);
++      struct ov5647 *state = to_state(sd);
+       crop->left = OV5647_COLUMN_START_DEF;
+       crop->top = OV5647_ROW_START_DEF;
+       crop->width = OV5647_WINDOW_WIDTH_DEF;
+       crop->height = OV5647_WINDOW_HEIGHT_DEF;
+-      format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+-
+-      format->width = OV5647_WINDOW_WIDTH_DEF;
+-      format->height = OV5647_WINDOW_HEIGHT_DEF;
+-      format->field = V4L2_FIELD_NONE;
+-      format->colorspace = V4L2_COLORSPACE_SRGB;
++      /* Set the default format to the same as the sensor. */
++      *format = state->mode->format;
+       return 0;
+ }
+@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien
+       mutex_init(&sensor->lock);
++      /* Set the default mode before we init the subdev */
++      sensor->mode = OV5647_DEFAULT_MODE;
++
+       sd = &sensor->sd;
+       v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+       sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch b/target/linux/bcm27xx/patches-5.4/950-0467-media-ov5647-Fix-return-codes-from-ov5647_write-ov56.patch
deleted file mode 100644 (file)
index c7e10cb..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-From 23f717168dc55f69ad517d3ab5f412d04a5afc0e Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.org>
-Date: Wed, 15 Jan 2020 13:40:38 +0000
-Subject: [PATCH] media: ov5647: Fix return codes from
- ov5647_write/ov5647_read functions.
-
-Previously they were returning positive non-zero codes for success,
-which were getting passed up the call stack. Since release 4.19,
-do_dentry_open (fs/open.c) has been catching these and flagging an
-error. (So this driver has been broken since that date.)
-
-Fixes: 3c2472a [media] media: i2c: Add support for OV5647 sensor
-Signed-off-by: David Plowman <david.plowman@raspberrypi.org>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 30 +++++++++++++++++++++++++++---
- 1 file changed, 27 insertions(+), 3 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -214,9 +214,18 @@ static int ov5647_write(struct v4l2_subd
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       ret = i2c_master_send(client, data, 3);
--      if (ret < 0)
-+      /*
-+       * Writing the wrong number of bytes also needs to be flagged as an
-+       * error. Success needs to produce a 0 return code.
-+       */
-+      if (ret == 3) {
-+              ret = 0;
-+      } else {
-               dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
-                               __func__, reg);
-+              if (ret >= 0)
-+                      ret = -EINVAL;
-+      }
-       return ret;
- }
-@@ -228,16 +237,31 @@ static int ov5647_read(struct v4l2_subde
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
-       ret = i2c_master_send(client, data_w, 2);
--      if (ret < 0) {
-+      /*
-+       * A negative return code, or sending the wrong number of bytes, both
-+       * count as an error.
-+       */
-+      if (ret != 2) {
-               dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
-                       __func__, reg);
-+              if (ret >= 0)
-+                      ret = -EINVAL;
-               return ret;
-       }
-       ret = i2c_master_recv(client, val, 1);
--      if (ret < 0)
-+      /*
-+       * The only return value indicating success is 1. Anything else, even
-+       * a non-negative value, indicates something went wrong.
-+       */
-+      if (ret == 1) {
-+              ret = 0;
-+      } else {
-               dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
-                               __func__, reg);
-+              if (ret >= 0)
-+                      ret = -EINVAL;
-+      }
-       return ret;
- }
diff --git a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch b/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
new file mode 100644 (file)
index 0000000..907bab5
--- /dev/null
@@ -0,0 +1,277 @@
+From d9d333b717f220439868edd533994f2709b3a95f Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:23 +0000
+Subject: [PATCH] media: ov5647: Add V4L2 controls for analogue gain,
+ exposure and AWB
+
+Added basic v4l2_ctrl_handler infrastructure (there was none
+previously).
+
+Added controls to let AWB/AEC/AGC run in the sensor's auto mode or
+manually. Also controls to set exposure (in lines) and analogue gain
+(as a register code) from user code.
+
+Also delete registers (just the one) from the VGA mode register set
+that are now controlled by the new V4L2 controls.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 175 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 174 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -29,11 +29,13 @@
+ #include <linux/of_graph.h>
+ #include <linux/slab.h>
+ #include <linux/videodev2.h>
++#include <media/v4l2-ctrls.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-image-sizes.h>
+ #include <media/v4l2-mediabus.h>
++
+ #define SENSOR_NAME "ov5647"
+ /*
+@@ -53,9 +55,16 @@
+ #define OV5647_REG_CHIPID_H           0x300A
+ #define OV5647_REG_CHIPID_L           0x300B
+ #define OV5640_REG_PAD_OUT            0x300D
++#define OV5647_REG_EXP_HI             0x3500
++#define OV5647_REG_EXP_MID            0x3501
++#define OV5647_REG_EXP_LO             0x3502
++#define OV5647_REG_AEC_AGC            0x3503
++#define OV5647_REG_GAIN_HI            0x350A
++#define OV5647_REG_GAIN_LO            0x350B
+ #define OV5647_REG_FRAME_OFF_NUMBER   0x4202
+ #define OV5647_REG_MIPI_CTRL00                0x4800
+ #define OV5647_REG_MIPI_CTRL14                0x4814
++#define OV5647_REG_AWB                        0x5001
+ #define REG_TERM 0xfffe
+ #define VAL_TERM 0xfe
+@@ -101,6 +110,7 @@ struct ov5647 {
+       struct clk                      *xclk;
+       struct gpio_desc                *pwdn;
+       unsigned int                    flags;
++      struct v4l2_ctrl_handler        ctrls;
+ };
+ static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
+@@ -135,7 +145,6 @@ static struct regval_list ov5647_640x480
+       {0x3612, 0x59},
+       {0x3618, 0x00},
+       {0x5000, 0x06},
+-      {0x5001, 0x01},
+       {0x5002, 0x41},
+       {0x5003, 0x08},
+       {0x5a00, 0x08},
+@@ -372,6 +381,11 @@ static int ov5647_stream_on(struct v4l2_
+               return ret;
+       }
++      /* Apply customized values from user when stream starts */
++      ret =  __v4l2_ctrl_handler_setup(sd->ctrl_handler);
++      if (ret)
++              return ret;
++
+       if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
+               val |= MIPI_CTRL00_CLOCK_LANE_GATE |
+                      MIPI_CTRL00_LINE_SYNC_ENABLE;
+@@ -753,6 +767,120 @@ static int ov5647_parse_dt(struct device
+       return ret;
+ }
++static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
++{
++      /* non-zero turns on AWB */
++      return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
++}
++
++static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
++{
++      int ret;
++      u8 reg;
++
++      /* non-zero turns on AGC by clearing bit 1 */
++      ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
++      if (ret == 0)
++              ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
++                                 val ? reg & ~2 : reg | 2);
++
++      return ret;
++}
++
++static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
++{
++      int ret;
++      u8 reg;
++
++      /* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
++       * clearing bit 0
++       */
++      ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
++      if (ret == 0)
++              ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
++                                 val == V4L2_EXPOSURE_MANUAL ?
++                                 reg | 1 : reg & ~1);
++
++      return ret;
++}
++
++static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
++{
++      int ret;
++
++      /* 10 bits of gain, 2 in the high register */
++      ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
++      if (ret == 0)
++              ret = ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
++
++      return ret;
++}
++
++static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
++{
++      int ret;
++
++      /* Sensor has 20 bits, but the bottom 4 bits are fractions of a line
++       * which we leave as zero (and don't receive in "val").
++       */
++      ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
++      if (ret == 0)
++              ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
++      if (ret == 0)
++              ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
++
++      return ret;
++}
++
++static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
++{
++      struct ov5647 *state = container_of(ctrl->handler,
++                                           struct ov5647, ctrls);
++      struct v4l2_subdev *sd = &state->sd;
++      struct i2c_client *client = v4l2_get_subdevdata(sd);
++      int ret = 0;
++
++      /* v4l2_ctrl_lock() locks our own mutex */
++
++      /*
++       * If the device is not powered up by the host driver do
++       * not apply any controls to H/W at this time. Instead
++       * the controls will be restored right after power-up.
++       */
++      if (state->power_count == 0)
++              return 0;
++
++      switch (ctrl->id) {
++      case V4L2_CID_AUTO_WHITE_BALANCE:
++              ret = ov5647_s_auto_white_balance(sd, ctrl->val);
++              break;
++      case V4L2_CID_AUTOGAIN:
++              ret = ov5647_s_autogain(sd, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE_AUTO:
++              ret = ov5647_s_exposure_auto(sd, ctrl->val);
++              break;
++      case V4L2_CID_ANALOGUE_GAIN:
++              ret = ov5647_s_analogue_gain(sd, ctrl->val);
++              break;
++      case V4L2_CID_EXPOSURE:
++              ret = ov5647_s_exposure(sd, ctrl->val);
++              break;
++      default:
++              dev_info(&client->dev,
++                       "ctrl(id:0x%x,val:0x%x) is not handled\n",
++                       ctrl->id, ctrl->val);
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
++      .s_ctrl = ov5647_s_ctrl,
++};
++
+ static int ov5647_probe(struct i2c_client *client)
+ {
+       struct device *dev = &client->dev;
+@@ -761,6 +889,7 @@ static int ov5647_probe(struct i2c_clien
+       struct v4l2_subdev *sd;
+       struct device_node *np = client->dev.of_node;
+       u32 xclk_freq;
++      struct v4l2_ctrl *ctrl;
+       sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+       if (!sensor)
+@@ -793,6 +922,48 @@ static int ov5647_probe(struct i2c_clien
+       mutex_init(&sensor->lock);
++      /* Initialise controls. */
++      v4l2_ctrl_handler_init(&sensor->ctrls, 3);
++      v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++                        V4L2_CID_AUTOGAIN,
++                        0,  /* min */
++                        1,  /* max */
++                        1,  /* step */
++                        1); /* default */
++      v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++                        V4L2_CID_AUTO_WHITE_BALANCE,
++                        0,  /* min */
++                        1,  /* max */
++                        1,  /* step */
++                        1); /* default */
++      v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
++                             V4L2_CID_EXPOSURE_AUTO,
++                             V4L2_EXPOSURE_MANUAL,  /* max */
++                             0,                     /* skip_mask */
++                             V4L2_EXPOSURE_AUTO);   /* default */
++      ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++                               V4L2_CID_EXPOSURE,
++                               4,     /* min lines */
++                               65535, /* max lines (4+8+4 bits)*/
++                               1,     /* step */
++                               1000); /* default number of lines */
++      ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
++      ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
++                               V4L2_CID_ANALOGUE_GAIN,
++                               16,   /* min, 16 = 1.0x */
++                               1023, /* max (10 bits) */
++                               1,    /* step */
++                               32);  /* default, 32 = 2.0x */
++      ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
++
++      if (sensor->ctrls.error) {
++              ret = sensor->ctrls.error;
++              dev_err(&client->dev, "%s control init failed (%d)\n",
++                      __func__, ret);
++              goto error;
++      }
++      sensor->sd.ctrl_handler = &sensor->ctrls;
++
+       /* Set the default mode before we init the subdev */
+       sensor->mode = OV5647_DEFAULT_MODE;
+@@ -828,6 +999,7 @@ static int ov5647_probe(struct i2c_clien
+ error:
+       media_entity_cleanup(&sd->entity);
+ mutex_remove:
++      v4l2_ctrl_handler_free(&sensor->ctrls);
+       mutex_destroy(&sensor->lock);
+       return ret;
+ }
+@@ -839,6 +1011,7 @@ static int ov5647_remove(struct i2c_clie
+       v4l2_async_unregister_subdev(&ov5647->sd);
+       media_entity_cleanup(&ov5647->sd.entity);
++      v4l2_ctrl_handler_free(&ov5647->ctrls);
+       v4l2_device_unregister_subdev(sd);
+       mutex_destroy(&ov5647->lock);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch b/target/linux/bcm27xx/patches-5.4/950-0468-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
deleted file mode 100644 (file)
index 5846e96..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:30:53 +0000
-Subject: [PATCH] media: ov5647: Add basic support for multiple sensor
- modes.
-
-Specifically:
-
-Added a structure ov5647_mode and a list of supported_modes (though no
-actual new modes as yet). The state object points to the "current mode".
-
-ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
-ov5647_get_fmt all needed upgrading to cope with multiple modes.
-
-__sensor_init (which writes all the registers) is now called by
-ov5647_stream_on (once the mode is known) rather than by
-ov5647_sensor_power.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++---------
- 1 file changed, 202 insertions(+), 66 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -86,13 +86,17 @@ struct regval_list {
-       u8 data;
- };
-+struct ov5647_mode {
-+      struct v4l2_mbus_framefmt       format;
-+      struct regval_list              *reg_list;
-+      unsigned int                    num_regs;
-+};
-+
- struct ov5647 {
-       struct v4l2_subdev              sd;
-       struct media_pad                pad;
-       struct mutex                    lock;
--      struct v4l2_mbus_framefmt       format;
--      unsigned int                    width;
--      unsigned int                    height;
-+      const struct ov5647_mode        *mode;
-       int                             power_count;
-       struct clk                      *xclk;
-       struct gpio_desc                *pwdn;
-@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480
-       {0x0100, 0x01},
- };
-+static struct ov5647_mode supported_modes_8bit[] = {
-+      /*
-+       * Original 8-bit VGA mode
-+       * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
-+       */
-+      {
-+              {
-+                      .code = MEDIA_BUS_FMT_SBGGR8_1X8,
-+                      .colorspace = V4L2_COLORSPACE_SRGB,
-+                      .field = V4L2_FIELD_NONE,
-+                      .width = 640,
-+                      .height = 480
-+              },
-+              ov5647_640x480,
-+              ARRAY_SIZE(ov5647_640x480)
-+      },
-+      /* more modes below here... */
-+};
-+
-+static struct ov5647_mode supported_modes_10bit[] = {
-+      /* no 10-bit modes yet */
-+};
-+
-+/* Use original 8-bit VGA mode as default. */
-+#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
-+
- static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
- {
-       int ret;
-@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st
-       return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
- }
-+static int __sensor_init(struct v4l2_subdev *sd)
-+{
-+      int ret;
-+      u8 resetval, rdval;
-+      struct i2c_client *client = v4l2_get_subdevdata(sd);
-+      struct ov5647 *state = to_state(sd);
-+
-+      ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = ov5647_write_array(sd, state->mode->reg_list,
-+                               state->mode->num_regs);
-+      if (ret < 0) {
-+              dev_err(&client->dev, "write sensor default regs error\n");
-+              return ret;
-+      }
-+
-+      ret = ov5647_set_virtual_channel(sd, 0);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
-+      if (ret < 0)
-+              return ret;
-+
-+      if (!(resetval & 0x01)) {
-+              dev_err(&client->dev, "Device was in SW standby");
-+              ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
-+              if (ret < 0)
-+                      return ret;
-+      }
-+
-+      return 0;
-+}
-+
- static int ov5647_stream_on(struct v4l2_subdev *sd)
- {
-+      struct i2c_client *client = v4l2_get_subdevdata(sd);
-       struct ov5647 *ov5647 = to_state(sd);
-       u8 val = MIPI_CTRL00_BUS_IDLE;
-       int ret;
-+      ret = __sensor_init(sd);
-+      if (ret < 0) {
-+              dev_err(&client->dev, "sensor_init failed\n");
-+              return ret;
-+      }
-+
-       if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
-               val |= MIPI_CTRL00_CLOCK_LANE_GATE |
-                      MIPI_CTRL00_LINE_SYNC_ENABLE;
-@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su
-       return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
- }
--static int __sensor_init(struct v4l2_subdev *sd)
--{
--      int ret;
--      u8 resetval, rdval;
--      struct i2c_client *client = v4l2_get_subdevdata(sd);
--
--      ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
--      if (ret < 0)
--              return ret;
--
--      ret = ov5647_write_array(sd, ov5647_640x480,
--                                      ARRAY_SIZE(ov5647_640x480));
--      if (ret < 0) {
--              dev_err(&client->dev, "write sensor default regs error\n");
--              return ret;
--      }
--
--      ret = ov5647_set_virtual_channel(sd, 0);
--      if (ret < 0)
--              return ret;
--
--      ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
--      if (ret < 0)
--              return ret;
--
--      if (!(resetval & 0x01)) {
--              dev_err(&client->dev, "Device was in SW standby");
--              ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
--              if (ret < 0)
--                      return ret;
--      }
--
--      /*
--       * stream off to make the clock lane into LP-11 state.
--       */
--      return ov5647_stream_off(sd);
--}
--
- static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
- {
-       int ret = 0;
-@@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4
-               }
-               ret = ov5647_write_array(sd, sensor_oe_enable_regs,
--                              ARRAY_SIZE(sensor_oe_enable_regs));
-+                                       ARRAY_SIZE(sensor_oe_enable_regs));
-               if (ret < 0) {
-                       clk_disable_unprepare(ov5647->xclk);
-                       dev_err(&client->dev,
-@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4
-                       goto out;
-               }
--              ret = __sensor_init(sd);
-+              /*
-+               * Ensure streaming off to make clock lane go into LP-11 state.
-+               */
-+              ret = ov5647_stream_off(sd);
-               if (ret < 0) {
-                       clk_disable_unprepare(ov5647->xclk);
-                       dev_err(&client->dev,
-@@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4
-               dev_dbg(&client->dev, "OV5647 power off\n");
-               ret = ov5647_write_array(sd, sensor_oe_disable_regs,
--                              ARRAY_SIZE(sensor_oe_disable_regs));
-+                                       ARRAY_SIZE(sensor_oe_disable_regs));
-               if (ret < 0)
-                       dev_dbg(&client->dev, "disable oe failed\n");
-@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops
- static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
- {
-+      struct ov5647 *state = to_state(sd);
-+      int ret = 0;
-+
-+      mutex_lock(&state->lock);
-+
-       if (enable)
--              return ov5647_stream_on(sd);
-+              ret = ov5647_stream_on(sd);
-       else
--              return ov5647_stream_off(sd);
-+              ret = ov5647_stream_off(sd);
-+
-+      mutex_unlock(&state->lock);
-+
-+      return ret;
- }
- static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
-@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct
-                               struct v4l2_subdev_pad_config *cfg,
-                               struct v4l2_subdev_mbus_code_enum *code)
- {
--      if (code->index > 0)
-+      if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
-+              code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-+      else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
-+               ARRAY_SIZE(supported_modes_10bit))
-+              code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
-+      else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
-+               ARRAY_SIZE(supported_modes_10bit))
-+              code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
-+      else
-               return -EINVAL;
--      code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
-+      return 0;
-+}
-+
-+static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
-+                                struct v4l2_subdev_pad_config *cfg,
-+                                struct v4l2_subdev_frame_size_enum *fse)
-+{
-+      struct ov5647_mode *mode = NULL;
-+
-+      if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
-+              if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
-+                      return -EINVAL;
-+              mode = &supported_modes_8bit[fse->index];
-+      } else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
-+              if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
-+                      return -EINVAL;
-+              mode = &supported_modes_10bit[fse->index];
-+      } else {
-+              return -EINVAL;
-+      }
-+
-+      fse->min_width = mode->format.width;
-+      fse->max_width = fse->min_width;
-+      fse->min_height = mode->format.height;
-+      fse->max_height = fse->min_height;
-+
-+      return 0;
-+}
-+
-+static int ov5647_set_fmt(struct v4l2_subdev *sd,
-+                        struct v4l2_subdev_pad_config *cfg,
-+                        struct v4l2_subdev_format *format)
-+{
-+      struct v4l2_mbus_framefmt *fmt = &format->format;
-+      struct ov5647 *state = to_state(sd);
-+      struct v4l2_mbus_framefmt *framefmt;
-+      const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
-+
-+      if (format->pad != 0)
-+              return -EINVAL;
-+
-+      mutex_lock(&state->lock);
-+
-+      /*
-+       * Try to respect any given pixel format, otherwise try for a 10-bit
-+       * mode.
-+       */
-+      mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
-+                                         ARRAY_SIZE(supported_modes_8bit),
-+                                         format.width, format.height,
-+                                         format->format.width,
-+                                         format->format.height);
-+      mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
-+                                          ARRAY_SIZE(supported_modes_10bit),
-+                                          format.width, format.height,
-+                                          format->format.width,
-+                                          format->format.height);
-+      if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
-+              mode = mode_8bit;
-+      else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
-+               mode_10bit)
-+              mode = mode_10bit;
-+      else if (mode_10bit)
-+              mode = mode_10bit;
-+      else
-+              mode = mode_8bit;
-+
-+      if (!mode)
-+              return -EINVAL;
-+
-+      *fmt = mode->format;
-+      if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
-+              framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
-+              *framefmt = format->format;
-+      } else {
-+              state->mode = mode;
-+      }
-+
-+      mutex_unlock(&state->lock);
-       return 0;
- }
--static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
--                            struct v4l2_subdev_pad_config *cfg,
--                            struct v4l2_subdev_format *format)
-+static int ov5647_get_fmt(struct v4l2_subdev *sd,
-+                        struct v4l2_subdev_pad_config *cfg,
-+                        struct v4l2_subdev_format *format)
- {
-       struct v4l2_mbus_framefmt *fmt = &format->format;
-+      struct ov5647 *state = to_state(sd);
-       if (format->pad != 0)
-               return -EINVAL;
--      /* Only one format is supported, so return that */
--      memset(fmt, 0, sizeof(*fmt));
--      fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
--      fmt->colorspace = V4L2_COLORSPACE_SRGB;
--      fmt->field = V4L2_FIELD_NONE;
--      fmt->width = 640;
--      fmt->height = 480;
-+      mutex_lock(&state->lock);
-+
-+      if (format->which == V4L2_SUBDEV_FORMAT_TRY)
-+              *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
-+      else
-+              *fmt = state->mode->format;
-+
-+      mutex_unlock(&state->lock);
-       return 0;
- }
- static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
-       .enum_mbus_code = ov5647_enum_mbus_code,
--      .set_fmt =        ov5647_set_get_fmt,
--      .get_fmt =        ov5647_set_get_fmt,
-+      .set_fmt =        ov5647_set_fmt,
-+      .get_fmt =        ov5647_get_fmt,
-+      .enum_frame_size = ov5647_enum_frame_size,
- };
- static const struct v4l2_subdev_ops ov5647_subdev_ops = {
-@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde
-                               v4l2_subdev_get_try_format(sd, fh->pad, 0);
-       struct v4l2_rect *crop =
-                               v4l2_subdev_get_try_crop(sd, fh->pad, 0);
-+      struct ov5647 *state = to_state(sd);
-       crop->left = OV5647_COLUMN_START_DEF;
-       crop->top = OV5647_ROW_START_DEF;
-       crop->width = OV5647_WINDOW_WIDTH_DEF;
-       crop->height = OV5647_WINDOW_HEIGHT_DEF;
--      format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
--
--      format->width = OV5647_WINDOW_WIDTH_DEF;
--      format->height = OV5647_WINDOW_HEIGHT_DEF;
--      format->field = V4L2_FIELD_NONE;
--      format->colorspace = V4L2_COLORSPACE_SRGB;
-+      /* Set the default format to the same as the sensor. */
-+      *format = state->mode->format;
-       return 0;
- }
-@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien
-       mutex_init(&sensor->lock);
-+      /* Set the default mode before we init the subdev */
-+      sensor->mode = OV5647_DEFAULT_MODE;
-+
-       sd = &sensor->sd;
-       v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
-       sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch b/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-V4L2-controls-for-analogue-gain-exp.patch
deleted file mode 100644 (file)
index 907bab5..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-From d9d333b717f220439868edd533994f2709b3a95f Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:23 +0000
-Subject: [PATCH] media: ov5647: Add V4L2 controls for analogue gain,
- exposure and AWB
-
-Added basic v4l2_ctrl_handler infrastructure (there was none
-previously).
-
-Added controls to let AWB/AEC/AGC run in the sensor's auto mode or
-manually. Also controls to set exposure (in lines) and analogue gain
-(as a register code) from user code.
-
-Also delete registers (just the one) from the VGA mode register set
-that are now controlled by the new V4L2 controls.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 175 ++++++++++++++++++++++++++++++++++++-
- 1 file changed, 174 insertions(+), 1 deletion(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -29,11 +29,13 @@
- #include <linux/of_graph.h>
- #include <linux/slab.h>
- #include <linux/videodev2.h>
-+#include <media/v4l2-ctrls.h>
- #include <media/v4l2-device.h>
- #include <media/v4l2-fwnode.h>
- #include <media/v4l2-image-sizes.h>
- #include <media/v4l2-mediabus.h>
-+
- #define SENSOR_NAME "ov5647"
- /*
-@@ -53,9 +55,16 @@
- #define OV5647_REG_CHIPID_H           0x300A
- #define OV5647_REG_CHIPID_L           0x300B
- #define OV5640_REG_PAD_OUT            0x300D
-+#define OV5647_REG_EXP_HI             0x3500
-+#define OV5647_REG_EXP_MID            0x3501
-+#define OV5647_REG_EXP_LO             0x3502
-+#define OV5647_REG_AEC_AGC            0x3503
-+#define OV5647_REG_GAIN_HI            0x350A
-+#define OV5647_REG_GAIN_LO            0x350B
- #define OV5647_REG_FRAME_OFF_NUMBER   0x4202
- #define OV5647_REG_MIPI_CTRL00                0x4800
- #define OV5647_REG_MIPI_CTRL14                0x4814
-+#define OV5647_REG_AWB                        0x5001
- #define REG_TERM 0xfffe
- #define VAL_TERM 0xfe
-@@ -101,6 +110,7 @@ struct ov5647 {
-       struct clk                      *xclk;
-       struct gpio_desc                *pwdn;
-       unsigned int                    flags;
-+      struct v4l2_ctrl_handler        ctrls;
- };
- static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
-@@ -135,7 +145,6 @@ static struct regval_list ov5647_640x480
-       {0x3612, 0x59},
-       {0x3618, 0x00},
-       {0x5000, 0x06},
--      {0x5001, 0x01},
-       {0x5002, 0x41},
-       {0x5003, 0x08},
-       {0x5a00, 0x08},
-@@ -372,6 +381,11 @@ static int ov5647_stream_on(struct v4l2_
-               return ret;
-       }
-+      /* Apply customized values from user when stream starts */
-+      ret =  __v4l2_ctrl_handler_setup(sd->ctrl_handler);
-+      if (ret)
-+              return ret;
-+
-       if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
-               val |= MIPI_CTRL00_CLOCK_LANE_GATE |
-                      MIPI_CTRL00_LINE_SYNC_ENABLE;
-@@ -753,6 +767,120 @@ static int ov5647_parse_dt(struct device
-       return ret;
- }
-+static int ov5647_s_auto_white_balance(struct v4l2_subdev *sd, u32 val)
-+{
-+      /* non-zero turns on AWB */
-+      return ov5647_write(sd, OV5647_REG_AWB, val ? 1 : 0);
-+}
-+
-+static int ov5647_s_autogain(struct v4l2_subdev *sd, u32 val)
-+{
-+      int ret;
-+      u8 reg;
-+
-+      /* non-zero turns on AGC by clearing bit 1 */
-+      ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
-+      if (ret == 0)
-+              ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
-+                                 val ? reg & ~2 : reg | 2);
-+
-+      return ret;
-+}
-+
-+static int ov5647_s_exposure_auto(struct v4l2_subdev *sd, u32 val)
-+{
-+      int ret;
-+      u8 reg;
-+
-+      /* Everything except V4L2_EXPOSURE_MANUAL turns on AEC by
-+       * clearing bit 0
-+       */
-+      ret = ov5647_read(sd, OV5647_REG_AEC_AGC, &reg);
-+      if (ret == 0)
-+              ret = ov5647_write(sd, OV5647_REG_AEC_AGC,
-+                                 val == V4L2_EXPOSURE_MANUAL ?
-+                                 reg | 1 : reg & ~1);
-+
-+      return ret;
-+}
-+
-+static int ov5647_s_analogue_gain(struct v4l2_subdev *sd, u32 val)
-+{
-+      int ret;
-+
-+      /* 10 bits of gain, 2 in the high register */
-+      ret = ov5647_write(sd, OV5647_REG_GAIN_HI, (val >> 8) & 3);
-+      if (ret == 0)
-+              ret = ov5647_write(sd, OV5647_REG_GAIN_LO, val & 0xff);
-+
-+      return ret;
-+}
-+
-+static int ov5647_s_exposure(struct v4l2_subdev *sd, u32 val)
-+{
-+      int ret;
-+
-+      /* Sensor has 20 bits, but the bottom 4 bits are fractions of a line
-+       * which we leave as zero (and don't receive in "val").
-+       */
-+      ret = ov5647_write(sd, OV5647_REG_EXP_HI, (val >> 12) & 0xf);
-+      if (ret == 0)
-+              ov5647_write(sd, OV5647_REG_EXP_MID, (val >> 4) & 0xff);
-+      if (ret == 0)
-+              ov5647_write(sd, OV5647_REG_EXP_LO, (val & 0xf) << 4);
-+
-+      return ret;
-+}
-+
-+static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl)
-+{
-+      struct ov5647 *state = container_of(ctrl->handler,
-+                                           struct ov5647, ctrls);
-+      struct v4l2_subdev *sd = &state->sd;
-+      struct i2c_client *client = v4l2_get_subdevdata(sd);
-+      int ret = 0;
-+
-+      /* v4l2_ctrl_lock() locks our own mutex */
-+
-+      /*
-+       * If the device is not powered up by the host driver do
-+       * not apply any controls to H/W at this time. Instead
-+       * the controls will be restored right after power-up.
-+       */
-+      if (state->power_count == 0)
-+              return 0;
-+
-+      switch (ctrl->id) {
-+      case V4L2_CID_AUTO_WHITE_BALANCE:
-+              ret = ov5647_s_auto_white_balance(sd, ctrl->val);
-+              break;
-+      case V4L2_CID_AUTOGAIN:
-+              ret = ov5647_s_autogain(sd, ctrl->val);
-+              break;
-+      case V4L2_CID_EXPOSURE_AUTO:
-+              ret = ov5647_s_exposure_auto(sd, ctrl->val);
-+              break;
-+      case V4L2_CID_ANALOGUE_GAIN:
-+              ret = ov5647_s_analogue_gain(sd, ctrl->val);
-+              break;
-+      case V4L2_CID_EXPOSURE:
-+              ret = ov5647_s_exposure(sd, ctrl->val);
-+              break;
-+      default:
-+              dev_info(&client->dev,
-+                       "ctrl(id:0x%x,val:0x%x) is not handled\n",
-+                       ctrl->id, ctrl->val);
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+static const struct v4l2_ctrl_ops ov5647_ctrl_ops = {
-+      .s_ctrl = ov5647_s_ctrl,
-+};
-+
- static int ov5647_probe(struct i2c_client *client)
- {
-       struct device *dev = &client->dev;
-@@ -761,6 +889,7 @@ static int ov5647_probe(struct i2c_clien
-       struct v4l2_subdev *sd;
-       struct device_node *np = client->dev.of_node;
-       u32 xclk_freq;
-+      struct v4l2_ctrl *ctrl;
-       sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
-       if (!sensor)
-@@ -793,6 +922,48 @@ static int ov5647_probe(struct i2c_clien
-       mutex_init(&sensor->lock);
-+      /* Initialise controls. */
-+      v4l2_ctrl_handler_init(&sensor->ctrls, 3);
-+      v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+                        V4L2_CID_AUTOGAIN,
-+                        0,  /* min */
-+                        1,  /* max */
-+                        1,  /* step */
-+                        1); /* default */
-+      v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+                        V4L2_CID_AUTO_WHITE_BALANCE,
-+                        0,  /* min */
-+                        1,  /* max */
-+                        1,  /* step */
-+                        1); /* default */
-+      v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
-+                             V4L2_CID_EXPOSURE_AUTO,
-+                             V4L2_EXPOSURE_MANUAL,  /* max */
-+                             0,                     /* skip_mask */
-+                             V4L2_EXPOSURE_AUTO);   /* default */
-+      ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+                               V4L2_CID_EXPOSURE,
-+                               4,     /* min lines */
-+                               65535, /* max lines (4+8+4 bits)*/
-+                               1,     /* step */
-+                               1000); /* default number of lines */
-+      ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
-+      ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-+                               V4L2_CID_ANALOGUE_GAIN,
-+                               16,   /* min, 16 = 1.0x */
-+                               1023, /* max (10 bits) */
-+                               1,    /* step */
-+                               32);  /* default, 32 = 2.0x */
-+      ctrl->flags |= V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
-+
-+      if (sensor->ctrls.error) {
-+              ret = sensor->ctrls.error;
-+              dev_err(&client->dev, "%s control init failed (%d)\n",
-+                      __func__, ret);
-+              goto error;
-+      }
-+      sensor->sd.ctrl_handler = &sensor->ctrls;
-+
-       /* Set the default mode before we init the subdev */
-       sensor->mode = OV5647_DEFAULT_MODE;
-@@ -828,6 +999,7 @@ static int ov5647_probe(struct i2c_clien
- error:
-       media_entity_cleanup(&sd->entity);
- mutex_remove:
-+      v4l2_ctrl_handler_free(&sensor->ctrls);
-       mutex_destroy(&sensor->lock);
-       return ret;
- }
-@@ -839,6 +1011,7 @@ static int ov5647_remove(struct i2c_clie
-       v4l2_async_unregister_subdev(&ov5647->sd);
-       media_entity_cleanup(&ov5647->sd.entity);
-+      v4l2_ctrl_handler_free(&ov5647->ctrls);
-       v4l2_device_unregister_subdev(sd);
-       mutex_destroy(&ov5647->lock);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch b/target/linux/bcm27xx/patches-5.4/950-0469-media-ov5647-Add-extra-10-bit-sensor-modes.patch
new file mode 100644 (file)
index 0000000..9e40b51
--- /dev/null
@@ -0,0 +1,549 @@
+From 28c0004a54ce9b2c5862b38408952583b07458f9 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:28 +0000
+Subject: [PATCH] media: ov5647: Add extra 10-bit sensor modes.
+
+The 8-bit VGA mode remains, we add the following 10-bit modes:
+
+Mode 0: 2592x1944 full resolution.
+
+Mode 1: 1920x1080 full resolution, but centre-cropped.
+(This mode achieves 30fps, mode 0 does not.)
+
+Mode 2: 1296x972 full field-of-view 2x2 binned mode.
+
+Mode 3: VGA full field of view mode.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 463 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 452 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -111,6 +111,7 @@ struct ov5647 {
+       struct gpio_desc                *pwdn;
+       unsigned int                    flags;
+       struct v4l2_ctrl_handler        ctrls;
++      bool                            write_mode_regs;
+ };
+ static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
+@@ -130,7 +131,7 @@ static struct regval_list sensor_oe_enab
+       {0x3002, 0xe4},
+ };
+-static struct regval_list ov5647_640x480[] = {
++static struct regval_list ov5647_640x480_8bit[] = {
+       {0x0100, 0x00},
+       {0x0103, 0x01},
+       {0x3034, 0x08},
+@@ -220,9 +221,378 @@ static struct regval_list ov5647_640x480
+       {0x0100, 0x01},
+ };
++static struct regval_list ov5647_2592x1944_10bit[] = {
++      {0x0100, 0x00},
++      {0x0103, 0x01},
++      {0x3034, 0x1a},
++      {0x3035, 0x21},
++      {0x3036, 0x69},
++      {0x303c, 0x11},
++      {0x3106, 0xf5},
++      {0x3821, 0x06},
++      {0x3820, 0x00},
++      {0x3827, 0xec},
++      {0x370c, 0x03},
++      {0x3612, 0x5b},
++      {0x3618, 0x04},
++      {0x5000, 0x06},
++      {0x5002, 0x41},
++      {0x5003, 0x08},
++      {0x5a00, 0x08},
++      {0x3000, 0x00},
++      {0x3001, 0x00},
++      {0x3002, 0x00},
++      {0x3016, 0x08},
++      {0x3017, 0xe0},
++      {0x3018, 0x44},
++      {0x301c, 0xf8},
++      {0x301d, 0xf0},
++      {0x3a18, 0x00},
++      {0x3a19, 0xf8},
++      {0x3c01, 0x80},
++      {0x3b07, 0x0c},
++      {0x380c, 0x0b},
++      {0x380d, 0x1c},
++      {0x380e, 0x07},
++      {0x380f, 0xb0},
++      {0x3814, 0x11},
++      {0x3815, 0x11},
++      {0x3708, 0x64},
++      {0x3709, 0x12},
++      {0x3808, 0x0a},
++      {0x3809, 0x20},
++      {0x380a, 0x07},
++      {0x380b, 0x98},
++      {0x3800, 0x00},
++      {0x3801, 0x00},
++      {0x3802, 0x00},
++      {0x3803, 0x00},
++      {0x3804, 0x0a},
++      {0x3805, 0x3f},
++      {0x3806, 0x07},
++      {0x3807, 0xa3},
++      {0x3811, 0x10},
++      {0x3813, 0x06},
++      {0x3630, 0x2e},
++      {0x3632, 0xe2},
++      {0x3633, 0x23},
++      {0x3634, 0x44},
++      {0x3636, 0x06},
++      {0x3620, 0x64},
++      {0x3621, 0xe0},
++      {0x3600, 0x37},
++      {0x3704, 0xa0},
++      {0x3703, 0x5a},
++      {0x3715, 0x78},
++      {0x3717, 0x01},
++      {0x3731, 0x02},
++      {0x370b, 0x60},
++      {0x3705, 0x1a},
++      {0x3f05, 0x02},
++      {0x3f06, 0x10},
++      {0x3f01, 0x0a},
++      {0x3a08, 0x01},
++      {0x3a09, 0x28},
++      {0x3a0a, 0x00},
++      {0x3a0b, 0xf6},
++      {0x3a0d, 0x08},
++      {0x3a0e, 0x06},
++      {0x3a0f, 0x58},
++      {0x3a10, 0x50},
++      {0x3a1b, 0x58},
++      {0x3a1e, 0x50},
++      {0x3a11, 0x60},
++      {0x3a1f, 0x28},
++      {0x4001, 0x02},
++      {0x4004, 0x04},
++      {0x4000, 0x09},
++      {0x4837, 0x19},
++      {0x4800, 0x24},
++      {0x3503, 0x03},
++      {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_1080p30_10bit[] = {
++      {0x0100, 0x00},
++      {0x0103, 0x01},
++      {0x3034, 0x1a},
++      {0x3035, 0x21},
++      {0x3036, 0x62},
++      {0x303c, 0x11},
++      {0x3106, 0xf5},
++      {0x3821, 0x06},
++      {0x3820, 0x00},
++      {0x3827, 0xec},
++      {0x370c, 0x03},
++      {0x3612, 0x5b},
++      {0x3618, 0x04},
++      {0x5000, 0x06},
++      {0x5002, 0x41},
++      {0x5003, 0x08},
++      {0x5a00, 0x08},
++      {0x3000, 0x00},
++      {0x3001, 0x00},
++      {0x3002, 0x00},
++      {0x3016, 0x08},
++      {0x3017, 0xe0},
++      {0x3018, 0x44},
++      {0x301c, 0xf8},
++      {0x301d, 0xf0},
++      {0x3a18, 0x00},
++      {0x3a19, 0xf8},
++      {0x3c01, 0x80},
++      {0x3b07, 0x0c},
++      {0x380c, 0x09},
++      {0x380d, 0x70},
++      {0x380e, 0x04},
++      {0x380f, 0x50},
++      {0x3814, 0x11},
++      {0x3815, 0x11},
++      {0x3708, 0x64},
++      {0x3709, 0x12},
++      {0x3808, 0x07},
++      {0x3809, 0x80},
++      {0x380a, 0x04},
++      {0x380b, 0x38},
++      {0x3800, 0x01},
++      {0x3801, 0x5c},
++      {0x3802, 0x01},
++      {0x3803, 0xb2},
++      {0x3804, 0x08},
++      {0x3805, 0xe3},
++      {0x3806, 0x05},
++      {0x3807, 0xf1},
++      {0x3811, 0x04},
++      {0x3813, 0x02},
++      {0x3630, 0x2e},
++      {0x3632, 0xe2},
++      {0x3633, 0x23},
++      {0x3634, 0x44},
++      {0x3636, 0x06},
++      {0x3620, 0x64},
++      {0x3621, 0xe0},
++      {0x3600, 0x37},
++      {0x3704, 0xa0},
++      {0x3703, 0x5a},
++      {0x3715, 0x78},
++      {0x3717, 0x01},
++      {0x3731, 0x02},
++      {0x370b, 0x60},
++      {0x3705, 0x1a},
++      {0x3f05, 0x02},
++      {0x3f06, 0x10},
++      {0x3f01, 0x0a},
++      {0x3a08, 0x01},
++      {0x3a09, 0x4b},
++      {0x3a0a, 0x01},
++      {0x3a0b, 0x13},
++      {0x3a0d, 0x04},
++      {0x3a0e, 0x03},
++      {0x3a0f, 0x58},
++      {0x3a10, 0x50},
++      {0x3a1b, 0x58},
++      {0x3a1e, 0x50},
++      {0x3a11, 0x60},
++      {0x3a1f, 0x28},
++      {0x4001, 0x02},
++      {0x4004, 0x04},
++      {0x4000, 0x09},
++      {0x4837, 0x19},
++      {0x4800, 0x34},
++      {0x3503, 0x03},
++      {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_2x2binned_10bit[] = {
++      {0x0100, 0x00},
++      {0x0103, 0x01},
++      {0x3034, 0x1A},
++      {0x3035, 0x21},
++      {0x3036, 0x62},
++      {0x303C, 0x11},
++      {0x3106, 0xF5},
++      {0x3827, 0xEC},
++      {0x370C, 0x03},
++      {0x3612, 0x59},
++      {0x3618, 0x00},
++      {0x5000, 0x06},
++      {0x5002, 0x41},
++      {0x5003, 0x08},
++      {0x5A00, 0x08},
++      {0x3000, 0x00},
++      {0x3001, 0x00},
++      {0x3002, 0x00},
++      {0x3016, 0x08},
++      {0x3017, 0xE0},
++      {0x3018, 0x44},
++      {0x301C, 0xF8},
++      {0x301D, 0xF0},
++      {0x3A18, 0x00},
++      {0x3A19, 0xF8},
++      {0x3C01, 0x80},
++      {0x3B07, 0x0C},
++      {0x3800, 0x00},
++      {0x3801, 0x00},
++      {0x3802, 0x00},
++      {0x3803, 0x00},
++      {0x3804, 0x0A},
++      {0x3805, 0x3F},
++      {0x3806, 0x07},
++      {0x3807, 0xA3},
++      {0x3808, 0x05},
++      {0x3809, 0x10},
++      {0x380A, 0x03},
++      {0x380B, 0xCC},
++      {0x380C, 0x07},
++      {0x380D, 0x68},
++      {0x3811, 0x0c},
++      {0x3813, 0x06},
++      {0x3814, 0x31},
++      {0x3815, 0x31},
++      {0x3630, 0x2E},
++      {0x3632, 0xE2},
++      {0x3633, 0x23},
++      {0x3634, 0x44},
++      {0x3636, 0x06},
++      {0x3620, 0x64},
++      {0x3621, 0xE0},
++      {0x3600, 0x37},
++      {0x3704, 0xA0},
++      {0x3703, 0x5A},
++      {0x3715, 0x78},
++      {0x3717, 0x01},
++      {0x3731, 0x02},
++      {0x370B, 0x60},
++      {0x3705, 0x1A},
++      {0x3F05, 0x02},
++      {0x3F06, 0x10},
++      {0x3F01, 0x0A},
++      {0x3A08, 0x01},
++      {0x3A09, 0x28},
++      {0x3A0A, 0x00},
++      {0x3A0B, 0xF6},
++      {0x3A0D, 0x08},
++      {0x3A0E, 0x06},
++      {0x3A0F, 0x58},
++      {0x3A10, 0x50},
++      {0x3A1B, 0x58},
++      {0x3A1E, 0x50},
++      {0x3A11, 0x60},
++      {0x3A1F, 0x28},
++      {0x4001, 0x02},
++      {0x4004, 0x04},
++      {0x4000, 0x09},
++      {0x4837, 0x16},
++      {0x4800, 0x24},
++      {0x3503, 0x03},
++      {0x3820, 0x41},
++      {0x3821, 0x07},
++      {0x380E, 0x05},
++      {0x380F, 0x9B},
++      {0x350A, 0x00},
++      {0x350B, 0x10},
++      {0x3500, 0x00},
++      {0x3501, 0x1A},
++      {0x3502, 0xF0},
++      {0x3212, 0xA0},
++      {0x0100, 0x01},
++};
++
++static struct regval_list ov5647_640x480_10bit[] = {
++      {0x0100, 0x00},
++      {0x0103, 0x01},
++      {0x3035, 0x11},
++      {0x3036, 0x46},
++      {0x303c, 0x11},
++      {0x3821, 0x07},
++      {0x3820, 0x41},
++      {0x370c, 0x03},
++      {0x3612, 0x59},
++      {0x3618, 0x00},
++      {0x5000, 0x06},
++      {0x5003, 0x08},
++      {0x5a00, 0x08},
++      {0x3000, 0xff},
++      {0x3001, 0xff},
++      {0x3002, 0xff},
++      {0x301d, 0xf0},
++      {0x3a18, 0x00},
++      {0x3a19, 0xf8},
++      {0x3c01, 0x80},
++      {0x3b07, 0x0c},
++      {0x380c, 0x07},
++      {0x380d, 0x3c},
++      {0x380e, 0x01},
++      {0x380f, 0xf8},
++      {0x3814, 0x35},
++      {0x3815, 0x35},
++      {0x3708, 0x64},
++      {0x3709, 0x52},
++      {0x3808, 0x02},
++      {0x3809, 0x80},
++      {0x380a, 0x01},
++      {0x380b, 0xe0},
++      {0x3800, 0x00},
++      {0x3801, 0x10},
++      {0x3802, 0x00},
++      {0x3803, 0x00},
++      {0x3804, 0x0a},
++      {0x3805, 0x2f},
++      {0x3806, 0x07},
++      {0x3807, 0x9f},
++      {0x3630, 0x2e},
++      {0x3632, 0xe2},
++      {0x3633, 0x23},
++      {0x3634, 0x44},
++      {0x3620, 0x64},
++      {0x3621, 0xe0},
++      {0x3600, 0x37},
++      {0x3704, 0xa0},
++      {0x3703, 0x5a},
++      {0x3715, 0x78},
++      {0x3717, 0x01},
++      {0x3731, 0x02},
++      {0x370b, 0x60},
++      {0x3705, 0x1a},
++      {0x3f05, 0x02},
++      {0x3f06, 0x10},
++      {0x3f01, 0x0a},
++      {0x3a08, 0x01},
++      {0x3a09, 0x2e},
++      {0x3a0a, 0x00},
++      {0x3a0b, 0xfb},
++      {0x3a0d, 0x02},
++      {0x3a0e, 0x01},
++      {0x3a0f, 0x58},
++      {0x3a10, 0x50},
++      {0x3a1b, 0x58},
++      {0x3a1e, 0x50},
++      {0x3a11, 0x60},
++      {0x3a1f, 0x28},
++      {0x4001, 0x02},
++      {0x4004, 0x02},
++      {0x4000, 0x09},
++      {0x3000, 0x00},
++      {0x3001, 0x00},
++      {0x3002, 0x00},
++      {0x3017, 0xe0},
++      {0x301c, 0xfc},
++      {0x3636, 0x06},
++      {0x3016, 0x08},
++      {0x3827, 0xec},
++      {0x3018, 0x44},
++      {0x3035, 0x21},
++      {0x3106, 0xf5},
++      {0x3034, 0x1a},
++      {0x301c, 0xf8},
++      {0x4800, 0x34},
++      {0x3503, 0x03},
++      {0x0100, 0x01},
++};
++
+ static struct ov5647_mode supported_modes_8bit[] = {
+       /*
+-       * Original 8-bit VGA mode
++       * MODE 0: Original 8-bit VGA mode.
+        * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
+        */
+       {
+@@ -233,14 +603,70 @@ static struct ov5647_mode supported_mode
+                       .width = 640,
+                       .height = 480
+               },
+-              ov5647_640x480,
+-              ARRAY_SIZE(ov5647_640x480)
++              ov5647_640x480_8bit,
++              ARRAY_SIZE(ov5647_640x480_8bit)
+       },
+-      /* more modes below here... */
+ };
+ static struct ov5647_mode supported_modes_10bit[] = {
+-      /* no 10-bit modes yet */
++      /*
++       * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
++       */
++      {
++              {
++                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++                      .colorspace = V4L2_COLORSPACE_SRGB,
++                      .field = V4L2_FIELD_NONE,
++                      .width = 2592,
++                      .height = 1944
++              },
++              ov5647_2592x1944_10bit,
++              ARRAY_SIZE(ov5647_2592x1944_10bit)
++      },
++      /*
++       * MODE 1: 1080p30 10-bit mode.
++       * Full resolution centre-cropped down to 1080p.
++       */
++      {
++              {
++                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++                      .colorspace = V4L2_COLORSPACE_SRGB,
++                      .field = V4L2_FIELD_NONE,
++                      .width = 1920,
++                      .height = 1080
++              },
++              ov5647_1080p30_10bit,
++              ARRAY_SIZE(ov5647_1080p30_10bit)
++      },
++      /*
++       * MODE 2: 2x2 binned full FOV 10-bit mode.
++       */
++      {
++              {
++                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++                      .colorspace = V4L2_COLORSPACE_SRGB,
++                      .field = V4L2_FIELD_NONE,
++                      .width = 1296,
++                      .height = 972
++              },
++              ov5647_2x2binned_10bit,
++              ARRAY_SIZE(ov5647_2x2binned_10bit)
++      },
++      /*
++       * MODE 3: 10-bit VGA full FOV mode 60fps.
++       * 2x2 binned and subsampled down to VGA.
++       */
++      {
++              {
++                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++                      .colorspace = V4L2_COLORSPACE_SRGB,
++                      .field = V4L2_FIELD_NONE,
++                      .width = 640,
++                      .height = 480
++              },
++              ov5647_640x480_10bit,
++              ARRAY_SIZE(ov5647_640x480_10bit)
++      },
+ };
+ /* Use original 8-bit VGA mode as default. */
+@@ -343,11 +769,14 @@ static int __sensor_init(struct v4l2_sub
+       if (ret < 0)
+               return ret;
+-      ret = ov5647_write_array(sd, state->mode->reg_list,
+-                               state->mode->num_regs);
+-      if (ret < 0) {
+-              dev_err(&client->dev, "write sensor default regs error\n");
+-              return ret;
++      if (state->write_mode_regs) {
++              ret = ov5647_write_array(sd, state->mode->reg_list,
++                                       state->mode->num_regs);
++              if (ret < 0) {
++                      dev_err(&client->dev, "write sensor default regs error\n");
++                      return ret;
++              }
++              state->write_mode_regs = false;
+       }
+       ret = ov5647_set_virtual_channel(sd, 0);
+@@ -475,6 +904,9 @@ static int ov5647_sensor_power(struct v4
+                               "Camera not available, check Power\n");
+                       goto out;
+               }
++
++              /* Write out the register set over I2C on stream-on. */
++              ov5647->write_mode_regs = true;
+       } else if (!on && ov5647->power_count == 1) {
+               dev_dbg(&client->dev, "OV5647 power off\n");
+@@ -650,6 +1082,12 @@ static int ov5647_set_fmt(struct v4l2_su
+               framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+               *framefmt = format->format;
+       } else {
++              /*
++               * If we have changed modes, write the I2C register list on
++               * a stream_on().
++               */
++              if (state->mode != mode)
++                      state->write_mode_regs = true;
+               state->mode = mode;
+       }
+@@ -967,6 +1405,9 @@ static int ov5647_probe(struct i2c_clien
+       /* Set the default mode before we init the subdev */
+       sensor->mode = OV5647_DEFAULT_MODE;
++      /* Write out the register set over I2C on stream-on. */
++      sensor->write_mode_regs = true;
++
+       sd = &sensor->sd;
+       v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+       sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-Add-extra-10-bit-sensor-modes.patch b/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-Add-extra-10-bit-sensor-modes.patch
deleted file mode 100644 (file)
index 9e40b51..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-From 28c0004a54ce9b2c5862b38408952583b07458f9 Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:28 +0000
-Subject: [PATCH] media: ov5647: Add extra 10-bit sensor modes.
-
-The 8-bit VGA mode remains, we add the following 10-bit modes:
-
-Mode 0: 2592x1944 full resolution.
-
-Mode 1: 1920x1080 full resolution, but centre-cropped.
-(This mode achieves 30fps, mode 0 does not.)
-
-Mode 2: 1296x972 full field-of-view 2x2 binned mode.
-
-Mode 3: VGA full field of view mode.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 463 ++++++++++++++++++++++++++++++++++++-
- 1 file changed, 452 insertions(+), 11 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -111,6 +111,7 @@ struct ov5647 {
-       struct gpio_desc                *pwdn;
-       unsigned int                    flags;
-       struct v4l2_ctrl_handler        ctrls;
-+      bool                            write_mode_regs;
- };
- static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
-@@ -130,7 +131,7 @@ static struct regval_list sensor_oe_enab
-       {0x3002, 0xe4},
- };
--static struct regval_list ov5647_640x480[] = {
-+static struct regval_list ov5647_640x480_8bit[] = {
-       {0x0100, 0x00},
-       {0x0103, 0x01},
-       {0x3034, 0x08},
-@@ -220,9 +221,378 @@ static struct regval_list ov5647_640x480
-       {0x0100, 0x01},
- };
-+static struct regval_list ov5647_2592x1944_10bit[] = {
-+      {0x0100, 0x00},
-+      {0x0103, 0x01},
-+      {0x3034, 0x1a},
-+      {0x3035, 0x21},
-+      {0x3036, 0x69},
-+      {0x303c, 0x11},
-+      {0x3106, 0xf5},
-+      {0x3821, 0x06},
-+      {0x3820, 0x00},
-+      {0x3827, 0xec},
-+      {0x370c, 0x03},
-+      {0x3612, 0x5b},
-+      {0x3618, 0x04},
-+      {0x5000, 0x06},
-+      {0x5002, 0x41},
-+      {0x5003, 0x08},
-+      {0x5a00, 0x08},
-+      {0x3000, 0x00},
-+      {0x3001, 0x00},
-+      {0x3002, 0x00},
-+      {0x3016, 0x08},
-+      {0x3017, 0xe0},
-+      {0x3018, 0x44},
-+      {0x301c, 0xf8},
-+      {0x301d, 0xf0},
-+      {0x3a18, 0x00},
-+      {0x3a19, 0xf8},
-+      {0x3c01, 0x80},
-+      {0x3b07, 0x0c},
-+      {0x380c, 0x0b},
-+      {0x380d, 0x1c},
-+      {0x380e, 0x07},
-+      {0x380f, 0xb0},
-+      {0x3814, 0x11},
-+      {0x3815, 0x11},
-+      {0x3708, 0x64},
-+      {0x3709, 0x12},
-+      {0x3808, 0x0a},
-+      {0x3809, 0x20},
-+      {0x380a, 0x07},
-+      {0x380b, 0x98},
-+      {0x3800, 0x00},
-+      {0x3801, 0x00},
-+      {0x3802, 0x00},
-+      {0x3803, 0x00},
-+      {0x3804, 0x0a},
-+      {0x3805, 0x3f},
-+      {0x3806, 0x07},
-+      {0x3807, 0xa3},
-+      {0x3811, 0x10},
-+      {0x3813, 0x06},
-+      {0x3630, 0x2e},
-+      {0x3632, 0xe2},
-+      {0x3633, 0x23},
-+      {0x3634, 0x44},
-+      {0x3636, 0x06},
-+      {0x3620, 0x64},
-+      {0x3621, 0xe0},
-+      {0x3600, 0x37},
-+      {0x3704, 0xa0},
-+      {0x3703, 0x5a},
-+      {0x3715, 0x78},
-+      {0x3717, 0x01},
-+      {0x3731, 0x02},
-+      {0x370b, 0x60},
-+      {0x3705, 0x1a},
-+      {0x3f05, 0x02},
-+      {0x3f06, 0x10},
-+      {0x3f01, 0x0a},
-+      {0x3a08, 0x01},
-+      {0x3a09, 0x28},
-+      {0x3a0a, 0x00},
-+      {0x3a0b, 0xf6},
-+      {0x3a0d, 0x08},
-+      {0x3a0e, 0x06},
-+      {0x3a0f, 0x58},
-+      {0x3a10, 0x50},
-+      {0x3a1b, 0x58},
-+      {0x3a1e, 0x50},
-+      {0x3a11, 0x60},
-+      {0x3a1f, 0x28},
-+      {0x4001, 0x02},
-+      {0x4004, 0x04},
-+      {0x4000, 0x09},
-+      {0x4837, 0x19},
-+      {0x4800, 0x24},
-+      {0x3503, 0x03},
-+      {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_1080p30_10bit[] = {
-+      {0x0100, 0x00},
-+      {0x0103, 0x01},
-+      {0x3034, 0x1a},
-+      {0x3035, 0x21},
-+      {0x3036, 0x62},
-+      {0x303c, 0x11},
-+      {0x3106, 0xf5},
-+      {0x3821, 0x06},
-+      {0x3820, 0x00},
-+      {0x3827, 0xec},
-+      {0x370c, 0x03},
-+      {0x3612, 0x5b},
-+      {0x3618, 0x04},
-+      {0x5000, 0x06},
-+      {0x5002, 0x41},
-+      {0x5003, 0x08},
-+      {0x5a00, 0x08},
-+      {0x3000, 0x00},
-+      {0x3001, 0x00},
-+      {0x3002, 0x00},
-+      {0x3016, 0x08},
-+      {0x3017, 0xe0},
-+      {0x3018, 0x44},
-+      {0x301c, 0xf8},
-+      {0x301d, 0xf0},
-+      {0x3a18, 0x00},
-+      {0x3a19, 0xf8},
-+      {0x3c01, 0x80},
-+      {0x3b07, 0x0c},
-+      {0x380c, 0x09},
-+      {0x380d, 0x70},
-+      {0x380e, 0x04},
-+      {0x380f, 0x50},
-+      {0x3814, 0x11},
-+      {0x3815, 0x11},
-+      {0x3708, 0x64},
-+      {0x3709, 0x12},
-+      {0x3808, 0x07},
-+      {0x3809, 0x80},
-+      {0x380a, 0x04},
-+      {0x380b, 0x38},
-+      {0x3800, 0x01},
-+      {0x3801, 0x5c},
-+      {0x3802, 0x01},
-+      {0x3803, 0xb2},
-+      {0x3804, 0x08},
-+      {0x3805, 0xe3},
-+      {0x3806, 0x05},
-+      {0x3807, 0xf1},
-+      {0x3811, 0x04},
-+      {0x3813, 0x02},
-+      {0x3630, 0x2e},
-+      {0x3632, 0xe2},
-+      {0x3633, 0x23},
-+      {0x3634, 0x44},
-+      {0x3636, 0x06},
-+      {0x3620, 0x64},
-+      {0x3621, 0xe0},
-+      {0x3600, 0x37},
-+      {0x3704, 0xa0},
-+      {0x3703, 0x5a},
-+      {0x3715, 0x78},
-+      {0x3717, 0x01},
-+      {0x3731, 0x02},
-+      {0x370b, 0x60},
-+      {0x3705, 0x1a},
-+      {0x3f05, 0x02},
-+      {0x3f06, 0x10},
-+      {0x3f01, 0x0a},
-+      {0x3a08, 0x01},
-+      {0x3a09, 0x4b},
-+      {0x3a0a, 0x01},
-+      {0x3a0b, 0x13},
-+      {0x3a0d, 0x04},
-+      {0x3a0e, 0x03},
-+      {0x3a0f, 0x58},
-+      {0x3a10, 0x50},
-+      {0x3a1b, 0x58},
-+      {0x3a1e, 0x50},
-+      {0x3a11, 0x60},
-+      {0x3a1f, 0x28},
-+      {0x4001, 0x02},
-+      {0x4004, 0x04},
-+      {0x4000, 0x09},
-+      {0x4837, 0x19},
-+      {0x4800, 0x34},
-+      {0x3503, 0x03},
-+      {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_2x2binned_10bit[] = {
-+      {0x0100, 0x00},
-+      {0x0103, 0x01},
-+      {0x3034, 0x1A},
-+      {0x3035, 0x21},
-+      {0x3036, 0x62},
-+      {0x303C, 0x11},
-+      {0x3106, 0xF5},
-+      {0x3827, 0xEC},
-+      {0x370C, 0x03},
-+      {0x3612, 0x59},
-+      {0x3618, 0x00},
-+      {0x5000, 0x06},
-+      {0x5002, 0x41},
-+      {0x5003, 0x08},
-+      {0x5A00, 0x08},
-+      {0x3000, 0x00},
-+      {0x3001, 0x00},
-+      {0x3002, 0x00},
-+      {0x3016, 0x08},
-+      {0x3017, 0xE0},
-+      {0x3018, 0x44},
-+      {0x301C, 0xF8},
-+      {0x301D, 0xF0},
-+      {0x3A18, 0x00},
-+      {0x3A19, 0xF8},
-+      {0x3C01, 0x80},
-+      {0x3B07, 0x0C},
-+      {0x3800, 0x00},
-+      {0x3801, 0x00},
-+      {0x3802, 0x00},
-+      {0x3803, 0x00},
-+      {0x3804, 0x0A},
-+      {0x3805, 0x3F},
-+      {0x3806, 0x07},
-+      {0x3807, 0xA3},
-+      {0x3808, 0x05},
-+      {0x3809, 0x10},
-+      {0x380A, 0x03},
-+      {0x380B, 0xCC},
-+      {0x380C, 0x07},
-+      {0x380D, 0x68},
-+      {0x3811, 0x0c},
-+      {0x3813, 0x06},
-+      {0x3814, 0x31},
-+      {0x3815, 0x31},
-+      {0x3630, 0x2E},
-+      {0x3632, 0xE2},
-+      {0x3633, 0x23},
-+      {0x3634, 0x44},
-+      {0x3636, 0x06},
-+      {0x3620, 0x64},
-+      {0x3621, 0xE0},
-+      {0x3600, 0x37},
-+      {0x3704, 0xA0},
-+      {0x3703, 0x5A},
-+      {0x3715, 0x78},
-+      {0x3717, 0x01},
-+      {0x3731, 0x02},
-+      {0x370B, 0x60},
-+      {0x3705, 0x1A},
-+      {0x3F05, 0x02},
-+      {0x3F06, 0x10},
-+      {0x3F01, 0x0A},
-+      {0x3A08, 0x01},
-+      {0x3A09, 0x28},
-+      {0x3A0A, 0x00},
-+      {0x3A0B, 0xF6},
-+      {0x3A0D, 0x08},
-+      {0x3A0E, 0x06},
-+      {0x3A0F, 0x58},
-+      {0x3A10, 0x50},
-+      {0x3A1B, 0x58},
-+      {0x3A1E, 0x50},
-+      {0x3A11, 0x60},
-+      {0x3A1F, 0x28},
-+      {0x4001, 0x02},
-+      {0x4004, 0x04},
-+      {0x4000, 0x09},
-+      {0x4837, 0x16},
-+      {0x4800, 0x24},
-+      {0x3503, 0x03},
-+      {0x3820, 0x41},
-+      {0x3821, 0x07},
-+      {0x380E, 0x05},
-+      {0x380F, 0x9B},
-+      {0x350A, 0x00},
-+      {0x350B, 0x10},
-+      {0x3500, 0x00},
-+      {0x3501, 0x1A},
-+      {0x3502, 0xF0},
-+      {0x3212, 0xA0},
-+      {0x0100, 0x01},
-+};
-+
-+static struct regval_list ov5647_640x480_10bit[] = {
-+      {0x0100, 0x00},
-+      {0x0103, 0x01},
-+      {0x3035, 0x11},
-+      {0x3036, 0x46},
-+      {0x303c, 0x11},
-+      {0x3821, 0x07},
-+      {0x3820, 0x41},
-+      {0x370c, 0x03},
-+      {0x3612, 0x59},
-+      {0x3618, 0x00},
-+      {0x5000, 0x06},
-+      {0x5003, 0x08},
-+      {0x5a00, 0x08},
-+      {0x3000, 0xff},
-+      {0x3001, 0xff},
-+      {0x3002, 0xff},
-+      {0x301d, 0xf0},
-+      {0x3a18, 0x00},
-+      {0x3a19, 0xf8},
-+      {0x3c01, 0x80},
-+      {0x3b07, 0x0c},
-+      {0x380c, 0x07},
-+      {0x380d, 0x3c},
-+      {0x380e, 0x01},
-+      {0x380f, 0xf8},
-+      {0x3814, 0x35},
-+      {0x3815, 0x35},
-+      {0x3708, 0x64},
-+      {0x3709, 0x52},
-+      {0x3808, 0x02},
-+      {0x3809, 0x80},
-+      {0x380a, 0x01},
-+      {0x380b, 0xe0},
-+      {0x3800, 0x00},
-+      {0x3801, 0x10},
-+      {0x3802, 0x00},
-+      {0x3803, 0x00},
-+      {0x3804, 0x0a},
-+      {0x3805, 0x2f},
-+      {0x3806, 0x07},
-+      {0x3807, 0x9f},
-+      {0x3630, 0x2e},
-+      {0x3632, 0xe2},
-+      {0x3633, 0x23},
-+      {0x3634, 0x44},
-+      {0x3620, 0x64},
-+      {0x3621, 0xe0},
-+      {0x3600, 0x37},
-+      {0x3704, 0xa0},
-+      {0x3703, 0x5a},
-+      {0x3715, 0x78},
-+      {0x3717, 0x01},
-+      {0x3731, 0x02},
-+      {0x370b, 0x60},
-+      {0x3705, 0x1a},
-+      {0x3f05, 0x02},
-+      {0x3f06, 0x10},
-+      {0x3f01, 0x0a},
-+      {0x3a08, 0x01},
-+      {0x3a09, 0x2e},
-+      {0x3a0a, 0x00},
-+      {0x3a0b, 0xfb},
-+      {0x3a0d, 0x02},
-+      {0x3a0e, 0x01},
-+      {0x3a0f, 0x58},
-+      {0x3a10, 0x50},
-+      {0x3a1b, 0x58},
-+      {0x3a1e, 0x50},
-+      {0x3a11, 0x60},
-+      {0x3a1f, 0x28},
-+      {0x4001, 0x02},
-+      {0x4004, 0x02},
-+      {0x4000, 0x09},
-+      {0x3000, 0x00},
-+      {0x3001, 0x00},
-+      {0x3002, 0x00},
-+      {0x3017, 0xe0},
-+      {0x301c, 0xfc},
-+      {0x3636, 0x06},
-+      {0x3016, 0x08},
-+      {0x3827, 0xec},
-+      {0x3018, 0x44},
-+      {0x3035, 0x21},
-+      {0x3106, 0xf5},
-+      {0x3034, 0x1a},
-+      {0x301c, 0xf8},
-+      {0x4800, 0x34},
-+      {0x3503, 0x03},
-+      {0x0100, 0x01},
-+};
-+
- static struct ov5647_mode supported_modes_8bit[] = {
-       /*
--       * Original 8-bit VGA mode
-+       * MODE 0: Original 8-bit VGA mode.
-        * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
-        */
-       {
-@@ -233,14 +603,70 @@ static struct ov5647_mode supported_mode
-                       .width = 640,
-                       .height = 480
-               },
--              ov5647_640x480,
--              ARRAY_SIZE(ov5647_640x480)
-+              ov5647_640x480_8bit,
-+              ARRAY_SIZE(ov5647_640x480_8bit)
-       },
--      /* more modes below here... */
- };
- static struct ov5647_mode supported_modes_10bit[] = {
--      /* no 10-bit modes yet */
-+      /*
-+       * MODE 0: 2592x1944 full resolution full FOV 10-bit mode.
-+       */
-+      {
-+              {
-+                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+                      .colorspace = V4L2_COLORSPACE_SRGB,
-+                      .field = V4L2_FIELD_NONE,
-+                      .width = 2592,
-+                      .height = 1944
-+              },
-+              ov5647_2592x1944_10bit,
-+              ARRAY_SIZE(ov5647_2592x1944_10bit)
-+      },
-+      /*
-+       * MODE 1: 1080p30 10-bit mode.
-+       * Full resolution centre-cropped down to 1080p.
-+       */
-+      {
-+              {
-+                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+                      .colorspace = V4L2_COLORSPACE_SRGB,
-+                      .field = V4L2_FIELD_NONE,
-+                      .width = 1920,
-+                      .height = 1080
-+              },
-+              ov5647_1080p30_10bit,
-+              ARRAY_SIZE(ov5647_1080p30_10bit)
-+      },
-+      /*
-+       * MODE 2: 2x2 binned full FOV 10-bit mode.
-+       */
-+      {
-+              {
-+                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+                      .colorspace = V4L2_COLORSPACE_SRGB,
-+                      .field = V4L2_FIELD_NONE,
-+                      .width = 1296,
-+                      .height = 972
-+              },
-+              ov5647_2x2binned_10bit,
-+              ARRAY_SIZE(ov5647_2x2binned_10bit)
-+      },
-+      /*
-+       * MODE 3: 10-bit VGA full FOV mode 60fps.
-+       * 2x2 binned and subsampled down to VGA.
-+       */
-+      {
-+              {
-+                      .code = MEDIA_BUS_FMT_SBGGR10_1X10,
-+                      .colorspace = V4L2_COLORSPACE_SRGB,
-+                      .field = V4L2_FIELD_NONE,
-+                      .width = 640,
-+                      .height = 480
-+              },
-+              ov5647_640x480_10bit,
-+              ARRAY_SIZE(ov5647_640x480_10bit)
-+      },
- };
- /* Use original 8-bit VGA mode as default. */
-@@ -343,11 +769,14 @@ static int __sensor_init(struct v4l2_sub
-       if (ret < 0)
-               return ret;
--      ret = ov5647_write_array(sd, state->mode->reg_list,
--                               state->mode->num_regs);
--      if (ret < 0) {
--              dev_err(&client->dev, "write sensor default regs error\n");
--              return ret;
-+      if (state->write_mode_regs) {
-+              ret = ov5647_write_array(sd, state->mode->reg_list,
-+                                       state->mode->num_regs);
-+              if (ret < 0) {
-+                      dev_err(&client->dev, "write sensor default regs error\n");
-+                      return ret;
-+              }
-+              state->write_mode_regs = false;
-       }
-       ret = ov5647_set_virtual_channel(sd, 0);
-@@ -475,6 +904,9 @@ static int ov5647_sensor_power(struct v4
-                               "Camera not available, check Power\n");
-                       goto out;
-               }
-+
-+              /* Write out the register set over I2C on stream-on. */
-+              ov5647->write_mode_regs = true;
-       } else if (!on && ov5647->power_count == 1) {
-               dev_dbg(&client->dev, "OV5647 power off\n");
-@@ -650,6 +1082,12 @@ static int ov5647_set_fmt(struct v4l2_su
-               framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
-               *framefmt = format->format;
-       } else {
-+              /*
-+               * If we have changed modes, write the I2C register list on
-+               * a stream_on().
-+               */
-+              if (state->mode != mode)
-+                      state->write_mode_regs = true;
-               state->mode = mode;
-       }
-@@ -967,6 +1405,9 @@ static int ov5647_probe(struct i2c_clien
-       /* Set the default mode before we init the subdev */
-       sensor->mode = OV5647_DEFAULT_MODE;
-+      /* Write out the register set over I2C on stream-on. */
-+      sensor->write_mode_regs = true;
-+
-       sd = &sensor->sd;
-       v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
-       sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch b/target/linux/bcm27xx/patches-5.4/950-0470-media-ov5647-change-defaults-to-better-match-raw-cam.patch
new file mode 100644 (file)
index 0000000..58d23b7
--- /dev/null
@@ -0,0 +1,59 @@
+From 8bc19baeeca276374bed2d2ec95029d34fd93d7d Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Wed, 29 Jan 2020 15:31:32 +0000
+Subject: [PATCH] media: ov5647: change defaults to better match raw
+ camera applications.
+
+Specifically:
+
+* AWB is now off by default.
+
+* AEC/AGC is also off by default.
+
+* The default mode is changed to the 10-bit 2x2 binned mode.
+
+AWB and AEC/AGC can be re-enabled using the usual V4L2 controls. The
+original 8-bit mode will be respected if an application requests the
+8-bit format.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/ov5647.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/i2c/ov5647.c
++++ b/drivers/media/i2c/ov5647.c
+@@ -669,8 +669,8 @@ static struct ov5647_mode supported_mode
+       },
+ };
+-/* Use original 8-bit VGA mode as default. */
+-#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
++/* Use 2x2 binned 10-bit mode as default. */
++#define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
+ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+ {
+@@ -1367,18 +1367,18 @@ static int ov5647_probe(struct i2c_clien
+                         0,  /* min */
+                         1,  /* max */
+                         1,  /* step */
+-                        1); /* default */
++                        0); /* default */
+       v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+                         V4L2_CID_AUTO_WHITE_BALANCE,
+                         0,  /* min */
+                         1,  /* max */
+                         1,  /* step */
+-                        1); /* default */
++                        0); /* default */
+       v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
+                              V4L2_CID_EXPOSURE_AUTO,
+                              V4L2_EXPOSURE_MANUAL,  /* max */
+                              0,                     /* skip_mask */
+-                             V4L2_EXPOSURE_AUTO);   /* default */
++                             V4L2_EXPOSURE_MANUAL); /* default */
+       ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
+                                V4L2_CID_EXPOSURE,
+                                4,     /* min lines */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch b/target/linux/bcm27xx/patches-5.4/950-0471-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
new file mode 100644 (file)
index 0000000..0b48018
--- /dev/null
@@ -0,0 +1,79 @@
+From c0b2ca6abdde60a111fd6d3257be78c7f44e16ff Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 26 Dec 2019 15:44:31 +0100
+Subject: [PATCH] drm/vc4: fkms: Change crtc_state structure name to
+ avoid conflict
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -260,7 +260,7 @@ static inline struct vc4_crtc *to_vc4_cr
+       return container_of(crtc, struct vc4_crtc, base);
+ }
+-struct vc4_crtc_state {
++struct fkms_crtc_state {
+       struct drm_crtc_state base;
+       struct {
+@@ -271,10 +271,10 @@ struct vc4_crtc_state {
+       } margins;
+ };
+-static inline struct vc4_crtc_state *
+-to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
++static inline struct fkms_crtc_state *
++to_fkms_crtc_state(struct drm_crtc_state *crtc_state)
+ {
+-      return (struct vc4_crtc_state *)crtc_state;
++      return (struct fkms_crtc_state *)crtc_state;
+ }
+ struct vc4_fkms_encoder {
+@@ -410,7 +410,7 @@ static void vc4_fkms_crtc_get_margins(st
+                                     unsigned int *left, unsigned int *right,
+                                     unsigned int *top, unsigned int *bottom)
+ {
+-      struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
++      struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
+       struct drm_connector_state *conn_state;
+       struct drm_connector *conn;
+       int i;
+@@ -423,7 +423,7 @@ static void vc4_fkms_crtc_get_margins(st
+       /* We have to interate over all new connector states because
+        * vc4_fkms_crtc_get_margins() might be called before
+        * vc4_fkms_crtc_atomic_check() which means margins info in
+-       * vc4_crtc_state might be outdated.
++       * fkms_crtc_state might be outdated.
+        */
+       for_each_new_connector_in_state(state->state, conn, conn_state, i) {
+               if (conn_state->crtc != state->crtc)
+@@ -1068,7 +1068,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
+ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+                                struct drm_crtc_state *state)
+ {
+-      struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
++      struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
+       struct drm_connector *conn;
+       struct drm_connector_state *conn_state;
+       int i;
+@@ -1178,13 +1178,13 @@ static int vc4_page_flip(struct drm_crtc
+ static struct drm_crtc_state *
+ vc4_crtc_duplicate_state(struct drm_crtc *crtc)
+ {
+-      struct vc4_crtc_state *vc4_state, *old_vc4_state;
++      struct fkms_crtc_state *vc4_state, *old_vc4_state;
+       vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+       if (!vc4_state)
+               return NULL;
+-      old_vc4_state = to_vc4_crtc_state(crtc->state);
++      old_vc4_state = to_fkms_crtc_state(crtc->state);
+       vc4_state->margins = old_vc4_state->margins;
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0471-media-ov5647-change-defaults-to-better-match-raw-cam.patch b/target/linux/bcm27xx/patches-5.4/950-0471-media-ov5647-change-defaults-to-better-match-raw-cam.patch
deleted file mode 100644 (file)
index 58d23b7..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-From 8bc19baeeca276374bed2d2ec95029d34fd93d7d Mon Sep 17 00:00:00 2001
-From: David Plowman <david.plowman@raspberrypi.com>
-Date: Wed, 29 Jan 2020 15:31:32 +0000
-Subject: [PATCH] media: ov5647: change defaults to better match raw
- camera applications.
-
-Specifically:
-
-* AWB is now off by default.
-
-* AEC/AGC is also off by default.
-
-* The default mode is changed to the 10-bit 2x2 binned mode.
-
-AWB and AEC/AGC can be re-enabled using the usual V4L2 controls. The
-original 8-bit mode will be respected if an application requests the
-8-bit format.
-
-Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
-Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
----
- drivers/media/i2c/ov5647.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/media/i2c/ov5647.c
-+++ b/drivers/media/i2c/ov5647.c
-@@ -669,8 +669,8 @@ static struct ov5647_mode supported_mode
-       },
- };
--/* Use original 8-bit VGA mode as default. */
--#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
-+/* Use 2x2 binned 10-bit mode as default. */
-+#define OV5647_DEFAULT_MODE (&supported_modes_10bit[2])
- static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
- {
-@@ -1367,18 +1367,18 @@ static int ov5647_probe(struct i2c_clien
-                         0,  /* min */
-                         1,  /* max */
-                         1,  /* step */
--                        1); /* default */
-+                        0); /* default */
-       v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-                         V4L2_CID_AUTO_WHITE_BALANCE,
-                         0,  /* min */
-                         1,  /* max */
-                         1,  /* step */
--                        1); /* default */
-+                        0); /* default */
-       v4l2_ctrl_new_std_menu(&sensor->ctrls, &ov5647_ctrl_ops,
-                              V4L2_CID_EXPOSURE_AUTO,
-                              V4L2_EXPOSURE_MANUAL,  /* max */
-                              0,                     /* skip_mask */
--                             V4L2_EXPOSURE_AUTO);   /* default */
-+                             V4L2_EXPOSURE_MANUAL); /* default */
-       ctrl = v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops,
-                                V4L2_CID_EXPOSURE,
-                                4,     /* min lines */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch b/target/linux/bcm27xx/patches-5.4/950-0472-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
new file mode 100644 (file)
index 0000000..ac4fe16
--- /dev/null
@@ -0,0 +1,55 @@
+From 0d392a430d7dc84d8654972e9dbfa4d13009d3e8 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:22:06 +0000
+Subject: [PATCH] drm/fourcc: Add packed 10bit YUV 4:2:0 format
+
+Adds a format that is 3 10bit YUV 4:2:0 samples packed into
+a 32bit work (with 2 spare bits).
+
+Supported on Broadcom BCM2711 chips.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fourcc.c  |  3 +++
+ include/uapi/drm/drm_fourcc.h | 11 +++++++++++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/drm_fourcc.c
++++ b/drivers/gpu/drm/drm_fourcc.c
+@@ -274,6 +274,9 @@ const struct drm_format_info *__drm_form
+               { .format = DRM_FORMAT_YUV420_10BIT,    .depth = 0,
+                 .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
+                 .is_yuv = true },
++              { .format = DRM_FORMAT_P030,            .depth = 0,  .num_planes = 2,
++                .char_per_block = { 4, 4, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
++                .hsub = 2, .vsub = 2, .is_yuv = true},
+       };
+       unsigned int i;
+--- a/include/uapi/drm/drm_fourcc.h
++++ b/include/uapi/drm/drm_fourcc.h
+@@ -266,6 +266,13 @@ extern "C" {
+ #define DRM_FORMAT_P016               fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */
+ /*
++ * 2 plane YCbCr MSB aligned, 3 pixels packed into 4 bytes.
++ * index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
++ * index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0 [2:10:10:10:2:10:10:10] little endian
++ */
++#define DRM_FORMAT_P030               fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */
++
++/*
+  * 3 plane YCbCr
+  * index 0: Y plane, [7:0] Y
+  * index 1: Cb plane, [7:0] Cb
+@@ -593,6 +600,10 @@ extern "C" {
+  * and UV.  Some SAND-using hardware stores UV in a separate tiled
+  * image from Y to reduce the column height, which is not supported
+  * with these modifiers.
++ *
++ * The DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT modifier is also
++ * supported for DRM_FORMAT_P030 where the columns remain as 128 bytes
++ * wide, but as this is a 10 bpp format that translates to 96 pixels.
+  */
+ #define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \
diff --git a/target/linux/bcm27xx/patches-5.4/950-0472-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch b/target/linux/bcm27xx/patches-5.4/950-0472-drm-vc4-fkms-Change-crtc_state-structure-name-to-avo.patch
deleted file mode 100644 (file)
index 0b48018..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-From c0b2ca6abdde60a111fd6d3257be78c7f44e16ff Mon Sep 17 00:00:00 2001
-From: Maxime Ripard <maxime@cerno.tech>
-Date: Thu, 26 Dec 2019 15:44:31 +0100
-Subject: [PATCH] drm/vc4: fkms: Change crtc_state structure name to
- avoid conflict
-
-Signed-off-by: Maxime Ripard <maxime@cerno.tech>
----
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
---- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-+++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-@@ -260,7 +260,7 @@ static inline struct vc4_crtc *to_vc4_cr
-       return container_of(crtc, struct vc4_crtc, base);
- }
--struct vc4_crtc_state {
-+struct fkms_crtc_state {
-       struct drm_crtc_state base;
-       struct {
-@@ -271,10 +271,10 @@ struct vc4_crtc_state {
-       } margins;
- };
--static inline struct vc4_crtc_state *
--to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
-+static inline struct fkms_crtc_state *
-+to_fkms_crtc_state(struct drm_crtc_state *crtc_state)
- {
--      return (struct vc4_crtc_state *)crtc_state;
-+      return (struct fkms_crtc_state *)crtc_state;
- }
- struct vc4_fkms_encoder {
-@@ -410,7 +410,7 @@ static void vc4_fkms_crtc_get_margins(st
-                                     unsigned int *left, unsigned int *right,
-                                     unsigned int *top, unsigned int *bottom)
- {
--      struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
-+      struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
-       struct drm_connector_state *conn_state;
-       struct drm_connector *conn;
-       int i;
-@@ -423,7 +423,7 @@ static void vc4_fkms_crtc_get_margins(st
-       /* We have to interate over all new connector states because
-        * vc4_fkms_crtc_get_margins() might be called before
-        * vc4_fkms_crtc_atomic_check() which means margins info in
--       * vc4_crtc_state might be outdated.
-+       * fkms_crtc_state might be outdated.
-        */
-       for_each_new_connector_in_state(state->state, conn, conn_state, i) {
-               if (conn_state->crtc != state->crtc)
-@@ -1068,7 +1068,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
- static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
-                                struct drm_crtc_state *state)
- {
--      struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
-+      struct fkms_crtc_state *vc4_state = to_fkms_crtc_state(state);
-       struct drm_connector *conn;
-       struct drm_connector_state *conn_state;
-       int i;
-@@ -1178,13 +1178,13 @@ static int vc4_page_flip(struct drm_crtc
- static struct drm_crtc_state *
- vc4_crtc_duplicate_state(struct drm_crtc *crtc)
- {
--      struct vc4_crtc_state *vc4_state, *old_vc4_state;
-+      struct fkms_crtc_state *vc4_state, *old_vc4_state;
-       vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
-       if (!vc4_state)
-               return NULL;
--      old_vc4_state = to_vc4_crtc_state(crtc->state);
-+      old_vc4_state = to_fkms_crtc_state(crtc->state);
-       vc4_state->margins = old_vc4_state->margins;
-       __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0473-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch b/target/linux/bcm27xx/patches-5.4/950-0473-drm-fourcc-Add-packed-10bit-YUV-4-2-0-format.patch
deleted file mode 100644 (file)
index ac4fe16..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-From 0d392a430d7dc84d8654972e9dbfa4d13009d3e8 Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Fri, 24 Jan 2020 14:22:06 +0000
-Subject: [PATCH] drm/fourcc: Add packed 10bit YUV 4:2:0 format
-
-Adds a format that is 3 10bit YUV 4:2:0 samples packed into
-a 32bit work (with 2 spare bits).
-
-Supported on Broadcom BCM2711 chips.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- drivers/gpu/drm/drm_fourcc.c  |  3 +++
- include/uapi/drm/drm_fourcc.h | 11 +++++++++++
- 2 files changed, 14 insertions(+)
-
---- a/drivers/gpu/drm/drm_fourcc.c
-+++ b/drivers/gpu/drm/drm_fourcc.c
-@@ -274,6 +274,9 @@ const struct drm_format_info *__drm_form
-               { .format = DRM_FORMAT_YUV420_10BIT,    .depth = 0,
-                 .num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
-                 .is_yuv = true },
-+              { .format = DRM_FORMAT_P030,            .depth = 0,  .num_planes = 2,
-+                .char_per_block = { 4, 4, 0 }, .block_w = { 3, 0, 0 }, .block_h = { 1, 0, 0 },
-+                .hsub = 2, .vsub = 2, .is_yuv = true},
-       };
-       unsigned int i;
---- a/include/uapi/drm/drm_fourcc.h
-+++ b/include/uapi/drm/drm_fourcc.h
-@@ -266,6 +266,13 @@ extern "C" {
- #define DRM_FORMAT_P016               fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */
- /*
-+ * 2 plane YCbCr MSB aligned, 3 pixels packed into 4 bytes.
-+ * index 0 = Y plane, [31:0] x:Y2:Y1:Y0 2:10:10:10 little endian
-+ * index 1 = Cr:Cb plane, [63:0] x:Cr2:Cb2:Cr1:x:Cb1:Cr0:Cb0 [2:10:10:10:2:10:10:10] little endian
-+ */
-+#define DRM_FORMAT_P030               fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */
-+
-+/*
-  * 3 plane YCbCr
-  * index 0: Y plane, [7:0] Y
-  * index 1: Cb plane, [7:0] Cb
-@@ -593,6 +600,10 @@ extern "C" {
-  * and UV.  Some SAND-using hardware stores UV in a separate tiled
-  * image from Y to reduce the column height, which is not supported
-  * with these modifiers.
-+ *
-+ * The DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT modifier is also
-+ * supported for DRM_FORMAT_P030 where the columns remain as 128 bytes
-+ * wide, but as this is a 10 bpp format that translates to 96 pixels.
-  */
- #define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \
diff --git a/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0473-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
new file mode 100644 (file)
index 0000000..f6264ff
--- /dev/null
@@ -0,0 +1,71 @@
+From 531d3d5c89825bade52f4257d264bbb06775a6fa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:24:33 +0000
+Subject: [PATCH] drm/vc4: Add DRM_FORMAT_P030 support to firmware-kms
+
+Adds support for this format which is 3 10bit samples packed into
+4 bytes, as used by the HEVC codec block.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 21 ++++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc_image_types.h   |  4 ++++
+ 2 files changed, 24 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -216,6 +216,10 @@ static const struct vc_image_format {
+               .vc_image = VC_IMAGE_YUV420SP,
+               .is_vu = 1,
+       },
++      {
++              .drm = DRM_FORMAT_P030,
++              .vc_image = VC_IMAGE_YUV10COL,
++      },
+ };
+ static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
+@@ -622,7 +626,15 @@ static int vc4_plane_to_mb(struct drm_pl
+               }
+               break;
+       case DRM_FORMAT_MOD_BROADCOM_SAND128:
+-              mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
++              switch (mb->plane.vc_image_type) {
++              case VC_IMAGE_YUV420SP:
++                      mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
++                      break;
++              /* VC_IMAGE_YUV10COL could be included in here, but it is only
++               * valid as a SAND128 format, so the table at the top will have
++               * already set the correct format.
++               */
++              }
+               /* Note that the column pitch is passed across in lines, not
+                * bytes.
+                */
+@@ -707,6 +719,13 @@ static bool vc4_fkms_format_mod_supporte
+               case DRM_FORMAT_MOD_BROADCOM_SAND128:
+                       return true;
+               default:
++                      return false;
++              }
++      case DRM_FORMAT_P030:
++              switch (fourcc_mod_broadcom_mod(modifier)) {
++              case DRM_FORMAT_MOD_BROADCOM_SAND128:
++                      return true;
++              default:
+                       return false;
+               }
+       case DRM_FORMAT_NV21:
+--- a/drivers/gpu/drm/vc4/vc_image_types.h
++++ b/drivers/gpu/drm/vc4/vc_image_types.h
+@@ -139,6 +139,10 @@ enum {
+       VC_IMAGE_YUV_UV_16,
+       /* YUV4:2:0 with U,V in side-by-side format */
+       VC_IMAGE_YUV420_S,
++      /* 10-bit YUV 420 column image format */
++      VC_IMAGE_YUV10COL,
++      /* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
++      VC_IMAGE_RGBA1010102,
+       VC_IMAGE_MAX,     /* bounds for error checking */
+       VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0474-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch b/target/linux/bcm27xx/patches-5.4/950-0474-drm-vc4-Add-DRM_FORMAT_P030-support-to-firmware-kms.patch
deleted file mode 100644 (file)
index f6264ff..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-From 531d3d5c89825bade52f4257d264bbb06775a6fa Mon Sep 17 00:00:00 2001
-From: Dave Stevenson <dave.stevenson@raspberrypi.com>
-Date: Fri, 24 Jan 2020 14:24:33 +0000
-Subject: [PATCH] drm/vc4: Add DRM_FORMAT_P030 support to firmware-kms
-
-Adds support for this format which is 3 10bit samples packed into
-4 bytes, as used by the HEVC codec block.
-
-Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
----
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 21 ++++++++++++++++++++-
- drivers/gpu/drm/vc4/vc_image_types.h   |  4 ++++
- 2 files changed, 24 insertions(+), 1 deletion(-)
-
---- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-+++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
-@@ -216,6 +216,10 @@ static const struct vc_image_format {
-               .vc_image = VC_IMAGE_YUV420SP,
-               .is_vu = 1,
-       },
-+      {
-+              .drm = DRM_FORMAT_P030,
-+              .vc_image = VC_IMAGE_YUV10COL,
-+      },
- };
- static const struct vc_image_format *vc4_get_vc_image_fmt(u32 drm_format)
-@@ -622,7 +626,15 @@ static int vc4_plane_to_mb(struct drm_pl
-               }
-               break;
-       case DRM_FORMAT_MOD_BROADCOM_SAND128:
--              mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
-+              switch (mb->plane.vc_image_type) {
-+              case VC_IMAGE_YUV420SP:
-+                      mb->plane.vc_image_type = VC_IMAGE_YUV_UV;
-+                      break;
-+              /* VC_IMAGE_YUV10COL could be included in here, but it is only
-+               * valid as a SAND128 format, so the table at the top will have
-+               * already set the correct format.
-+               */
-+              }
-               /* Note that the column pitch is passed across in lines, not
-                * bytes.
-                */
-@@ -707,6 +719,13 @@ static bool vc4_fkms_format_mod_supporte
-               case DRM_FORMAT_MOD_BROADCOM_SAND128:
-                       return true;
-               default:
-+                      return false;
-+              }
-+      case DRM_FORMAT_P030:
-+              switch (fourcc_mod_broadcom_mod(modifier)) {
-+              case DRM_FORMAT_MOD_BROADCOM_SAND128:
-+                      return true;
-+              default:
-                       return false;
-               }
-       case DRM_FORMAT_NV21:
---- a/drivers/gpu/drm/vc4/vc_image_types.h
-+++ b/drivers/gpu/drm/vc4/vc_image_types.h
-@@ -139,6 +139,10 @@ enum {
-       VC_IMAGE_YUV_UV_16,
-       /* YUV4:2:0 with U,V in side-by-side format */
-       VC_IMAGE_YUV420_S,
-+      /* 10-bit YUV 420 column image format */
-+      VC_IMAGE_YUV10COL,
-+      /* 32-bpp, 10-bit R/G/B, 2-bit Alpha */
-+      VC_IMAGE_RGBA1010102,
-       VC_IMAGE_MAX,     /* bounds for error checking */
-       VC_IMAGE_FORCE_ENUM_16BIT = 0xffff,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch b/target/linux/bcm27xx/patches-5.4/950-0474-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
new file mode 100644 (file)
index 0000000..cdd14f7
--- /dev/null
@@ -0,0 +1,45 @@
+From e253d03936265dc4ab8ae9ae89d2a885e80a45a6 Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Fri, 6 Mar 2020 11:08:10 +0100
+Subject: [PATCH] gpio-ir-overlay: add parameter to configure signal
+ polarity (#3490)
+
+Standard IR receivers use inverted / active-low signalling
+and the gpio-ir overlay configures the GPIO appropriately
+as GPIO_ACTIVE_LOW (1).
+
+In order to support (rather rare) non-inverted / active-high
+signalling the GPIO needs to be configured as GPIO_ACTIVE_HIGH (0).
+
+Add an "invert" parameter to override this like in the gpio-ir-tx
+overlay.
+
+Signed-off-by: Matthias Reichl <hias@horus.com>
+---
+ arch/arm/boot/dts/overlays/README              | 4 ++++
+ arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 1 +
+ 2 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -754,6 +754,10 @@ Params: gpio_pin                Input pi
+         gpio_pull               Desired pull-up/down state (off, down, up)
+                                 Default is "up".
++        invert                  "1" = invert the input (active-low signalling).
++                                "0" = non-inverted input (active-high
++                                signalling). Default is "1".
++
+         rc-map-name             Default rc keymap (can also be changed by
+                                 ir-keytable), defaults to "rc-rc6-mce"
+--- a/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
++++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
+@@ -42,6 +42,7 @@
+                                 <&gpio_ir_pins>,"brcm,pins:0",
+                                 <&gpio_ir_pins>,"reg:0";
+                 gpio_pull = <&gpio_ir_pins>,"brcm,pull:0";              // pull-up/down state
++                invert = <&gpio_ir>,"gpios:8";                          // 0 = active high input
+                 rc-map-name = <&gpio_ir>,"linux,rc-map-name";           // default rc map
+         };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch b/target/linux/bcm27xx/patches-5.4/950-0475-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
new file mode 100644 (file)
index 0000000..8e66bcc
--- /dev/null
@@ -0,0 +1,1589 @@
+From 76e0edf9676388c58bb5f0d7dda8eb8029926c6d Mon Sep 17 00:00:00 2001
+From: AMuszkat <ariel.muszkat@gmail.com>
+Date: Mon, 24 Feb 2020 22:56:59 +0100
+Subject: [PATCH] Add support for merus-amp soundcard and ma120x0p
+ codec
+
+correct checkpatch warnings and errors
+
+Signed-off-by: AMuszkat <ariel.muszkat@gmail.com>
+---
+ arch/arm/boot/dts/overlays/Makefile           |    1 +
+ arch/arm/boot/dts/overlays/README             |    6 +
+ .../boot/dts/overlays/merus-amp-overlay.dts   |   60 +
+ sound/soc/bcm/rpi-simple-soundcard.c          |   28 +
+ sound/soc/codecs/Kconfig                      |    8 +
+ sound/soc/codecs/Makefile                     |    2 +
+ sound/soc/codecs/ma120x0p.c                   | 1384 +++++++++++++++++
+ 7 files changed, 1489 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+ create mode 100644 sound/soc/codecs/ma120x0p.c
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -103,6 +103,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+       mcp3202.dtbo \
+       mcp342x.dtbo \
+       media-center.dtbo \
++      merus-amp.dtbo \
+       midi-uart0.dtbo \
+       midi-uart1.dtbo \
+       miniuart-bt.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1662,6 +1662,12 @@ Params: speed                   Display
+                                 (default "off")
++Name:   merus-amp
++Info:   Configures the merus-amp audio card
++Load:   dtoverlay=merus-amp
++Params: <None>
++
++
+ Name:   midi-uart0
+ Info:   Configures UART0 (ttyAMA0) so that a requested 38.4kbaud actually gets
+         31.25kbaud, the frequency required for MIDI
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+@@ -0,0 +1,60 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for Infineon Merus-Amp
++/dts-v1/;
++/plugin/;
++#include <dt-bindings/pinctrl/bcm2835.h>
++#include <dt-bindings/gpio/gpio.h>
++
++
++/ {
++      compatible = "brcm,bcm2835";
++
++      fragment@0 {
++              target = <&i2s>;
++              __overlay__ {
++                      status = "okay";
++              };
++      };
++
++      fragment@1 {
++              target = <&gpio>;
++              __overlay__ {
++                      merus_amp_pins: merus_amp_pins {
++                              brcm,pins = <23>;
++                              brcm,function = <0>; /* in */
++                              brcm,pull = <2>; /* up */
++                      };
++              };
++      };
++
++      fragment@2 {
++              target = <&i2c1>;
++              __overlay__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "okay";
++
++                      merus_amp: ma120x0p@20 {
++                              #sound-dai-cells = <0>;
++                              compatible = "ma,ma120x0p";
++                              reg = <0x20>;
++                              status = "okay";
++                              pinctrl-names = "default";
++                              pinctrl-0 = <&merus_amp_pins>;
++                              enable_gp-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>;
++                              mute_gp-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
++                              booster_gp-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
++                              error_gp-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
++                      };
++              };
++      };
++
++      fragment@3 {
++              target = <&sound>;
++              __overlay__ {
++                      compatible = "merus,merus-amp";
++                      i2s-controller = <&i2s>;
++                      status = "okay";
++              };
++      };
++};
+--- a/sound/soc/bcm/rpi-simple-soundcard.c
++++ b/sound/soc/bcm/rpi-simple-soundcard.c
+@@ -16,6 +16,10 @@
+  * adau1977-adc.c
+  * by Andrey Grodzovsky <andrey2805@gmail.com>
+  *
++ * merus-amp.c
++ * by Ariel Muszkat <ariel.muszkat@gmail.com>
++ *            Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
++ *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 as published by the Free Software Foundation.
+@@ -229,6 +233,28 @@ static struct snd_rpi_simple_drvdata drv
+       .fixed_bclk_ratio = 64,
+ };
++SND_SOC_DAILINK_DEFS(merus_amp,
++      DAILINK_COMP_ARRAY(COMP_EMPTY()),
++      DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p-amp", "ma120x0p.1-0020")),
++      DAILINK_COMP_ARRAY(COMP_EMPTY()));
++
++static struct snd_soc_dai_link snd_merus_amp_dai[] = {
++      {
++              .name           = "MerusAmp",
++              .stream_name    = "Merus Audio Amp",
++              .dai_fmt        = SND_SOC_DAIFMT_I2S |
++                                      SND_SOC_DAIFMT_NB_NF |
++                                      SND_SOC_DAIFMT_CBS_CFS,
++              SND_SOC_DAILINK_REG(merus_amp),
++      },
++};
++
++static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
++      .card_name        = "snd_rpi_merus_amp",
++      .dai              = snd_merus_amp_dai,
++      .fixed_bclk_ratio = 64,
++};
++
+ static const struct of_device_id snd_rpi_simple_of_match[] = {
+       { .compatible = "adi,adau1977-adc",
+               .data = (void *) &drvdata_adau1977 },
+@@ -241,6 +267,8 @@ static const struct of_device_id snd_rpi
+       { .compatible = "hifiberry,hifiberry-dac",
+               .data = (void *) &drvdata_hifiberry_dac },
+       { .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
++      { .compatible = "merus,merus-amp",
++              .data = (void *) &drvdata_merus_amp },
+       {},
+ };
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS
+       select SND_SOC_LM4857 if I2C
+       select SND_SOC_LM49453 if I2C
+       select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
++      select SND_SOC_MA120X0P if I2C
+       select SND_SOC_MAX98088 if I2C
+       select SND_SOC_MAX98090 if I2C
+       select SND_SOC_MAX98095 if I2C
+@@ -732,6 +733,13 @@ config SND_SOC_LOCHNAGAR_SC
+         This driver support the sound card functionality of the Cirrus
+         Logic Lochnagar audio development board.
++config SND_SOC_MA120X0P
++      tristate "Infineon Merus(TM) MA120X0P Multilevel Class-D Audio amplifiers"
++      depends on I2C
++      help
++              Enable support for Infineon MA120X0P Multilevel Class-D audio power
++              amplifiers.
++
+ config SND_SOC_MADERA
+       tristate
+       default y if SND_SOC_CS47L15=y
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -99,6 +99,7 @@ snd-soc-l3-objs := l3.o
+ snd-soc-lm4857-objs := lm4857.o
+ snd-soc-lm49453-objs := lm49453.o
+ snd-soc-lochnagar-sc-objs := lochnagar-sc.o
++snd-soc-ma120x0p-objs := ma120x0p.o
+ snd-soc-madera-objs := madera.o
+ snd-soc-max9759-objs := max9759.o
+ snd-soc-max9768-objs := max9768.o
+@@ -386,6 +387,7 @@ obj-$(CONFIG_SND_SOC_L3)   += snd-soc-l3.o
+ obj-$(CONFIG_SND_SOC_LM4857)  += snd-soc-lm4857.o
+ obj-$(CONFIG_SND_SOC_LM49453)   += snd-soc-lm49453.o
+ obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC)    += snd-soc-lochnagar-sc.o
++obj-$(CONFIG_SND_SOC_MA120X0P)   += snd-soc-ma120x0p.o
+ obj-$(CONFIG_SND_SOC_MADERA)  += snd-soc-madera.o
+ obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
+ obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
+--- /dev/null
++++ b/sound/soc/codecs/ma120x0p.c
+@@ -0,0 +1,1384 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * ASoC Driver for Infineon Merus(TM) ma120x0p multi-level class-D amplifier
++ *
++ * Authors:   Ariel Muszkat <ariel.muszkat@gmail.com>
++ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
++ *
++ * Copyright (C) 2019 Infineon Technologies AG
++ *
++ */
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm_runtime.h>
++#include <linux/i2c.h>
++#include <linux/of_device.h>
++#include <linux/spi/spi.h>
++#include <linux/regmap.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/gpio/consumer.h>
++#include <linux/gpio.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++#include <sound/tlv.h>
++#include <linux/interrupt.h>
++
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/uaccess.h>
++
++#ifndef _MA120X0P_
++#define _MA120X0P_
++//------------------------------------------------------------------manualPM---
++// Select Manual PowerMode control
++#define ma_manualpm__a 0
++#define ma_manualpm__len 1
++#define ma_manualpm__mask 0x40
++#define ma_manualpm__shift 0x06
++#define ma_manualpm__reset 0x00
++//--------------------------------------------------------------------pm_man---
++// manual selected power mode
++#define ma_pm_man__a 0
++#define ma_pm_man__len 2
++#define ma_pm_man__mask 0x30
++#define ma_pm_man__shift 0x04
++#define ma_pm_man__reset 0x03
++//------------------------------------------ ----------------------mthr_1to2---
++// mod. index threshold value for pm1=>pm2 change.
++#define ma_mthr_1to2__a 1
++#define ma_mthr_1to2__len 8
++#define ma_mthr_1to2__mask 0xff
++#define ma_mthr_1to2__shift 0x00
++#define ma_mthr_1to2__reset 0x3c
++//-----------------------------------------------------------------mthr_2to1---
++// mod. index threshold value for pm2=>pm1 change.
++#define ma_mthr_2to1__a 2
++#define ma_mthr_2to1__len 8
++#define ma_mthr_2to1__mask 0xff
++#define ma_mthr_2to1__shift 0x00
++#define ma_mthr_2to1__reset 0x32
++//-----------------------------------------------------------------mthr_2to3---
++// mod. index threshold value for pm2=>pm3 change.
++#define ma_mthr_2to3__a 3
++#define ma_mthr_2to3__len 8
++#define ma_mthr_2to3__mask 0xff
++#define ma_mthr_2to3__shift 0x00
++#define ma_mthr_2to3__reset 0x5a
++//-----------------------------------------------------------------mthr_3to2---
++// mod. index threshold value for pm3=>pm2 change.
++#define ma_mthr_3to2__a 4
++#define ma_mthr_3to2__len 8
++#define ma_mthr_3to2__mask 0xff
++#define ma_mthr_3to2__shift 0x00
++#define ma_mthr_3to2__reset 0x50
++//-------------------------------------------------------------pwmclkdiv_nom---
++// pwm default clock divider value
++#define ma_pwmclkdiv_nom__a 8
++#define ma_pwmclkdiv_nom__len 8
++#define ma_pwmclkdiv_nom__mask 0xff
++#define ma_pwmclkdiv_nom__shift 0x00
++#define ma_pwmclkdiv_nom__reset 0x26
++//--------- ----------------------------------------------------ocp_latch_en---
++// high to use permanently latching level-2 ocp
++#define ma_ocp_latch_en__a 10
++#define ma_ocp_latch_en__len 1
++#define ma_ocp_latch_en__mask 0x02
++#define ma_ocp_latch_en__shift 0x01
++#define ma_ocp_latch_en__reset 0x00
++//---------------------------------------------------------------lf_clamp_en---
++// high (default) to enable lf int2+3 clamping on clip
++#define ma_lf_clamp_en__a 10
++#define ma_lf_clamp_en__len 1
++#define ma_lf_clamp_en__mask 0x80
++#define ma_lf_clamp_en__shift 0x07
++#define ma_lf_clamp_en__reset 0x00
++//-------------------------------------------------------pmcfg_btl_b.modtype---
++//
++#define ma_pmcfg_btl_b__modtype__a 18
++#define ma_pmcfg_btl_b__modtype__len 2
++#define ma_pmcfg_btl_b__modtype__mask 0x18
++#define ma_pmcfg_btl_b__modtype__shift 0x03
++#define ma_pmcfg_btl_b__modtype__reset 0x02
++//-------------------------------------------------------pmcfg_btl_b.freqdiv---
++#define ma_pmcfg_btl_b__freqdiv__a 18
++#define ma_pmcfg_btl_b__freqdiv__len 2
++#define ma_pmcfg_btl_b__freqdiv__mask 0x06
++#define ma_pmcfg_btl_b__freqdiv__shift 0x01
++#define ma_pmcfg_btl_b__freqdiv__reset 0x01
++//----------------------------------------------------pmcfg_btl_b.lf_gain_ol---
++//
++#define ma_pmcfg_btl_b__lf_gain_ol__a 18
++#define ma_pmcfg_btl_b__lf_gain_ol__len 1
++#define ma_pmcfg_btl_b__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_b__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_b__lf_gain_ol__reset 0x01
++//-------------------------------------------------------pmcfg_btl_c.freqdiv---
++//
++#define ma_pmcfg_btl_c__freqdiv__a 19
++#define ma_pmcfg_btl_c__freqdiv__len 2
++#define ma_pmcfg_btl_c__freqdiv__mask 0x06
++#define ma_pmcfg_btl_c__freqdiv__shift 0x01
++#define ma_pmcfg_btl_c__freqdiv__reset 0x01
++//-------------------------------------------------------pmcfg_btl_c.modtype---
++//
++#define ma_pmcfg_btl_c__modtype__a 19
++#define ma_pmcfg_btl_c__modtype__len 2
++#define ma_pmcfg_btl_c__modtype__mask 0x18
++#define ma_pmcfg_btl_c__modtype__shift 0x03
++#define ma_pmcfg_btl_c__modtype__reset 0x01
++//----------------------------------------------------pmcfg_btl_c.lf_gain_ol---
++//
++#define ma_pmcfg_btl_c__lf_gain_ol__a 19
++#define ma_pmcfg_btl_c__lf_gain_ol__len 1
++#define ma_pmcfg_btl_c__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_c__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_c__lf_gain_ol__reset 0x00
++//-------------------------------------------------------pmcfg_btl_d.modtype---
++//
++#define ma_pmcfg_btl_d__modtype__a 20
++#define ma_pmcfg_btl_d__modtype__len 2
++#define ma_pmcfg_btl_d__modtype__mask 0x18
++#define ma_pmcfg_btl_d__modtype__shift 0x03
++#define ma_pmcfg_btl_d__modtype__reset 0x02
++//-------------------------------------------------------pmcfg_btl_d.freqdiv---
++//
++#define ma_pmcfg_btl_d__freqdiv__a 20
++#define ma_pmcfg_btl_d__freqdiv__len 2
++#define ma_pmcfg_btl_d__freqdiv__mask 0x06
++#define ma_pmcfg_btl_d__freqdiv__shift 0x01
++#define ma_pmcfg_btl_d__freqdiv__reset 0x02
++//----------------------------------------------------pmcfg_btl_d.lf_gain_ol---
++//
++#define ma_pmcfg_btl_d__lf_gain_ol__a 20
++#define ma_pmcfg_btl_d__lf_gain_ol__len 1
++#define ma_pmcfg_btl_d__lf_gain_ol__mask 0x01
++#define ma_pmcfg_btl_d__lf_gain_ol__shift 0x00
++#define ma_pmcfg_btl_d__lf_gain_ol__reset 0x00
++//------------ -------------------------------------------pmcfg_se_a.modtype---
++//
++#define ma_pmcfg_se_a__modtype__a 21
++#define ma_pmcfg_se_a__modtype__len 2
++#define ma_pmcfg_se_a__modtype__mask 0x18
++#define ma_pmcfg_se_a__modtype__shift 0x03
++#define ma_pmcfg_se_a__modtype__reset 0x01
++//--------------------------------------------------------pmcfg_se_a.freqdiv---
++//
++#define ma_pmcfg_se_a__freqdiv__a 21
++#define ma_pmcfg_se_a__freqdiv__len 2
++#define ma_pmcfg_se_a__freqdiv__mask 0x06
++#define ma_pmcfg_se_a__freqdiv__shift 0x01
++#define ma_pmcfg_se_a__freqdiv__reset 0x00
++//-----------------------------------------------------pmcfg_se_a.lf_gain_ol---
++//
++#define ma_pmcfg_se_a__lf_gain_ol__a 21
++#define ma_pmcfg_se_a__lf_gain_ol__len 1
++#define ma_pmcfg_se_a__lf_gain_ol__mask 0x01
++#define ma_pmcfg_se_a__lf_gain_ol__shift 0x00
++#define ma_pmcfg_se_a__lf_gain_ol__reset 0x01
++//-----------------------------------------------------pmcfg_se_b.lf_gain_ol---
++//
++#define ma_pmcfg_se_b__lf_gain_ol__a 22
++#define ma_pmcfg_se_b__lf_gain_ol__len 1
++#define ma_pmcfg_se_b__lf_gain_ol__mask 0x01
++#define ma_pmcfg_se_b__lf_gain_ol__shift 0x00
++#define ma_pmcfg_se_b__lf_gain_ol__reset 0x00
++//--------------------------------------------------------pmcfg_se_b.freqdiv---
++//
++#define ma_pmcfg_se_b__freqdiv__a 22
++#define ma_pmcfg_se_b__freqdiv__len 2
++#define ma_pmcfg_se_b__freqdiv__mask 0x06
++#define ma_pmcfg_se_b__freqdiv__shift 0x01
++#define ma_pmcfg_se_b__freqdiv__reset 0x01
++//--------------------------------------------------------pmcfg_se_b.modtype---
++//
++#define ma_pmcfg_se_b__modtype__a 22
++#define ma_pmcfg_se_b__modtype__len 2
++#define ma_pmcfg_se_b__modtype__mask 0x18
++#define ma_pmcfg_se_b__modtype__shift 0x03
++#define ma_pmcfg_se_b__modtype__reset 0x01
++//----------------------------------------------------------balwaitcount_pm1---
++// pm1 balancing period.
++#define ma_balwaitcount_pm1__a 23
++#define ma_balwaitcount_pm1__len 8
++#define ma_balwaitcount_pm1__mask 0xff
++#define ma_balwaitcount_pm1__shift 0x00
++#define ma_balwaitcount_pm1__reset 0x14
++//----------------------------------------------------------balwaitcount_pm2---
++// pm2 balancing period.
++#define ma_balwaitcount_pm2__a 24
++#define ma_balwaitcount_pm2__len 8
++#define ma_balwaitcount_pm2__mask 0xff
++#define ma_balwaitcount_pm2__shift 0x00
++#define ma_balwaitcount_pm2__reset 0x14
++//----------------------------------------------------------balwaitcount_pm3---
++// pm3 balancing period.
++#define ma_balwaitcount_pm3__a 25
++#define ma_balwaitcount_pm3__len 8
++#define ma_balwaitcount_pm3__mask 0xff
++#define ma_balwaitcount_pm3__shift 0x00
++#define ma_balwaitcount_pm3__reset 0x1a
++//-------------------------------------------------------------usespread_pm1---
++// pm1 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm1__a 26
++#define ma_usespread_pm1__len 1
++#define ma_usespread_pm1__mask 0x40
++#define ma_usespread_pm1__shift 0x06
++#define ma_usespread_pm1__reset 0x00
++//---------------------------------------------------------------dtsteps_pm1---
++// pm1 dead time setting [10ns steps].
++#define ma_dtsteps_pm1__a 26
++#define ma_dtsteps_pm1__len 3
++#define ma_dtsteps_pm1__mask 0x38
++#define ma_dtsteps_pm1__shift 0x03
++#define ma_dtsteps_pm1__reset 0x04
++//---------------------------------------------------------------baltype_pm1---
++// pm1 balancing sensor scheme.
++#define ma_baltype_pm1__a 26
++#define ma_baltype_pm1__len 3
++#define ma_baltype_pm1__mask 0x07
++#define ma_baltype_pm1__shift 0x00
++#define ma_baltype_pm1__reset 0x00
++//-------------------------------------------------------------usespread_pm2---
++// pm2 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm2__a 27
++#define ma_usespread_pm2__len 1
++#define ma_usespread_pm2__mask 0x40
++#define ma_usespread_pm2__shift 0x06
++#define ma_usespread_pm2__reset 0x00
++//---------------------------------------------------------------dtsteps_pm2---
++// pm2 dead time setting [10ns steps].
++#define ma_dtsteps_pm2__a 27
++#define ma_dtsteps_pm2__len 3
++#define ma_dtsteps_pm2__mask 0x38
++#define ma_dtsteps_pm2__shift 0x03
++#define ma_dtsteps_pm2__reset 0x03
++//---------------------------------------------------------------baltype_pm2---
++// pm2 balancing sensor scheme.
++#define ma_baltype_pm2__a 27
++#define ma_baltype_pm2__len 3
++#define ma_baltype_pm2__mask 0x07
++#define ma_baltype_pm2__shift 0x00
++#define ma_baltype_pm2__reset 0x01
++//-------------------------------------------------------------usespread_pm3---
++// pm3 pwm spread-spectrum mode on/off.
++#define ma_usespread_pm3__a 28
++#define ma_usespread_pm3__len 1
++#define ma_usespread_pm3__mask 0x40
++#define ma_usespread_pm3__shift 0x06
++#define ma_usespread_pm3__reset 0x00
++//---------------------------------------------------------------dtsteps_pm3---
++// pm3 dead time setting [10ns steps].
++#define ma_dtsteps_pm3__a 28
++#define ma_dtsteps_pm3__len 3
++#define ma_dtsteps_pm3__mask 0x38
++#define ma_dtsteps_pm3__shift 0x03
++#define ma_dtsteps_pm3__reset 0x01
++//---------------------------------------------------------------baltype_pm3---
++// pm3 balancing sensor scheme.
++#define ma_baltype_pm3__a 28
++#define ma_baltype_pm3__len 3
++#define ma_baltype_pm3__mask 0x07
++#define ma_baltype_pm3__shift 0x00
++#define ma_baltype_pm3__reset 0x03
++//-----------------------------------------------------------------pmprofile---
++// pm profile select. valid presets: 0-1-2-3-4. 5=> custom profile.
++#define ma_pmprofile__a 29
++#define ma_pmprofile__len 3
++#define ma_pmprofile__mask 0x07
++#define ma_pmprofile__shift 0x00
++#define ma_pmprofile__reset 0x00
++//-------------------------------------------------------------------pm3_man---
++// custom profile pm3 contents. 0=>a,  1=>b,  2=>c,  3=>d
++#define ma_pm3_man__a 30
++#define ma_pm3_man__len 2
++#define ma_pm3_man__mask 0x30
++#define ma_pm3_man__shift 0x04
++#define ma_pm3_man__reset 0x02
++//-------------------------------------------------------------------pm2_man---
++// custom profile pm2 contents. 0=>a,  1=>b,  2=>c,  3=>d
++#define ma_pm2_man__a 30
++#define ma_pm2_man__len 2
++#define ma_pm2_man__mask 0x0c
++#define ma_pm2_man__shift 0x02
++#define ma_pm2_man__reset 0x03
++//-------------------------------------------------------------------pm1_man---
++// custom profile pm1 contents. 0=>a,  1=>b,  2=>c,  3=>d
++#define ma_pm1_man__a 30
++#define ma_pm1_man__len 2
++#define ma_pm1_man__mask 0x03
++#define ma_pm1_man__shift 0x00
++#define ma_pm1_man__reset 0x03
++//-----------------------------------------------------------ocp_latch_clear---
++// low-high clears current ocp latched condition.
++#define ma_ocp_latch_clear__a 32
++#define ma_ocp_latch_clear__len 1
++#define ma_ocp_latch_clear__mask 0x80
++#define ma_ocp_latch_clear__shift 0x07
++#define ma_ocp_latch_clear__reset 0x00
++//-------------------------------------------------------------audio_in_mode---
++// audio input mode; 0-1-2-3-4-5
++#define ma_audio_in_mode__a 37
++#define ma_audio_in_mode__len 3
++#define ma_audio_in_mode__mask 0xe0
++#define ma_audio_in_mode__shift 0x05
++#define ma_audio_in_mode__reset 0x00
++//-----------------------------------------------------------------eh_dcshdn---
++// high to enable dc protection
++#define ma_eh_dcshdn__a 38
++#define ma_eh_dcshdn__len 1
++#define ma_eh_dcshdn__mask 0x04
++#define ma_eh_dcshdn__shift 0x02
++#define ma_eh_dcshdn__reset 0x01
++//---------------------------------------------------------audio_in_mode_ext---
++// if set,  audio_in_mode is controlled from audio_in_mode register. if not set
++//audio_in_mode is set from fuse bank setting
++#define ma_audio_in_mode_ext__a 39
++#define ma_audio_in_mode_ext__len 1
++#define ma_audio_in_mode_ext__mask 0x20
++#define ma_audio_in_mode_ext__shift 0x05
++#define ma_audio_in_mode_ext__reset 0x00
++//------------------------------------------------------------------eh_clear---
++// flip to clear error registers
++#define ma_eh_clear__a 45
++#define ma_eh_clear__len 1
++#define ma_eh_clear__mask 0x04
++#define ma_eh_clear__shift 0x02
++#define ma_eh_clear__reset 0x00
++//----------------------------------------------------------thermal_compr_en---
++// enable otw-contr.  input compression?
++#define ma_thermal_compr_en__a 45
++#define ma_thermal_compr_en__len 1
++#define ma_thermal_compr_en__mask 0x20
++#define ma_thermal_compr_en__shift 0x05
++#define ma_thermal_compr_en__reset 0x01
++//---------------------------------------------------------------system_mute---
++// 1 = mute system,  0 = normal operation
++#define ma_system_mute__a 45
++#define ma_system_mute__len 1
++#define ma_system_mute__mask 0x40
++#define ma_system_mute__shift 0x06
++#define ma_system_mute__reset 0x00
++//------------------------------------------------------thermal_compr_max_db---
++// audio limiter max thermal reduction
++#define ma_thermal_compr_max_db__a 46
++#define ma_thermal_compr_max_db__len 3
++#define ma_thermal_compr_max_db__mask 0x07
++#define ma_thermal_compr_max_db__shift 0x00
++#define ma_thermal_compr_max_db__reset 0x04
++//---------------------------------------------------------audio_proc_enable---
++// enable audio proc,  bypass if not enabled
++#define ma_audio_proc_enable__a 53
++#define ma_audio_proc_enable__len 1
++#define ma_audio_proc_enable__mask 0x08
++#define ma_audio_proc_enable__shift 0x03
++#define ma_audio_proc_enable__reset 0x00
++//--------------------------------------------------------audio_proc_release---
++// 00:slow,  01:normal,  10:fast
++#define ma_audio_proc_release__a 53
++#define ma_audio_proc_release__len 2
++#define ma_audio_proc_release__mask 0x30
++#define ma_audio_proc_release__shift 0x04
++#define ma_audio_proc_release__reset 0x00
++//---------------------------------------------------------audio_proc_attack---
++// 00:slow,  01:normal,  10:fast
++#define ma_audio_proc_attack__a 53
++#define ma_audio_proc_attack__len 2
++#define ma_audio_proc_attack__mask 0xc0
++#define ma_audio_proc_attack__shift 0x06
++#define ma_audio_proc_attack__reset 0x00
++//----------------------------------------------------------------i2s_format---
++// i2s basic data format,  000 = std. i2s,  001 = left justified (default)
++#define ma_i2s_format__a 53
++#define ma_i2s_format__len 3
++#define ma_i2s_format__mask 0x07
++#define ma_i2s_format__shift 0x00
++#define ma_i2s_format__reset 0x01
++//--------------------------------------------------audio_proc_limiterenable---
++// 1: enable limiter,  0: disable limiter
++#define ma_audio_proc_limiterenable__a 54
++#define ma_audio_proc_limiterenable__len 1
++#define ma_audio_proc_limiterenable__mask 0x40
++#define ma_audio_proc_limiterenable__shift 0x06
++#define ma_audio_proc_limiterenable__reset 0x00
++//-----------------------------------------------------------audio_proc_mute---
++// 1: mute,  0: unmute
++#define ma_audio_proc_mute__a 54
++#define ma_audio_proc_mute__len 1
++#define ma_audio_proc_mute__mask 0x80
++#define ma_audio_proc_mute__shift 0x07
++#define ma_audio_proc_mute__reset 0x00
++//---------------------------------------------------------------i2s_sck_pol---
++// i2s sck polarity cfg. 0 = rising edge data change
++#define ma_i2s_sck_pol__a 54
++#define ma_i2s_sck_pol__len 1
++#define ma_i2s_sck_pol__mask 0x01
++#define ma_i2s_sck_pol__shift 0x00
++#define ma_i2s_sck_pol__reset 0x01
++//-------------------------------------------------------------i2s_framesize---
++// i2s word length. 00 = 32bit,  01 = 24bit
++#define ma_i2s_framesize__a 54
++#define ma_i2s_framesize__len 2
++#define ma_i2s_framesize__mask 0x18
++#define ma_i2s_framesize__shift 0x03
++#define ma_i2s_framesize__reset 0x00
++//----------------------------------------------------------------i2s_ws_pol---
++// i2s ws polarity. 0 = low first
++#define ma_i2s_ws_pol__a 54
++#define ma_i2s_ws_pol__len 1
++#define ma_i2s_ws_pol__mask 0x02
++#define ma_i2s_ws_pol__shift 0x01
++#define ma_i2s_ws_pol__reset 0x00
++//-----------------------------------------------------------------i2s_order---
++// i2s word bit order. 0 = msb first
++#define ma_i2s_order__a 54
++#define ma_i2s_order__len 1
++#define ma_i2s_order__mask 0x04
++#define ma_i2s_order__shift 0x02
++#define ma_i2s_order__reset 0x00
++//------------------------------------------------------------i2s_rightfirst---
++// i2s l/r word order; 0 = left first
++#define ma_i2s_rightfirst__a 54
++#define ma_i2s_rightfirst__len 1
++#define ma_i2s_rightfirst__mask 0x20
++#define ma_i2s_rightfirst__shift 0x05
++#define ma_i2s_rightfirst__reset 0x00
++//-------------------------------------------------------------vol_db_master---
++// master volume db
++#define ma_vol_db_master__a 64
++#define ma_vol_db_master__len 8
++#define ma_vol_db_master__mask 0xff
++#define ma_vol_db_master__shift 0x00
++#define ma_vol_db_master__reset 0x18
++//------------------------------------------------------------vol_lsb_master---
++// master volume lsb 1/4 steps
++#define ma_vol_lsb_master__a 65
++#define ma_vol_lsb_master__len 2
++#define ma_vol_lsb_master__mask 0x03
++#define ma_vol_lsb_master__shift 0x00
++#define ma_vol_lsb_master__reset 0x00
++//----------------------------------------------------------------vol_db_ch0---
++// volume channel 0
++#define ma_vol_db_ch0__a 66
++#define ma_vol_db_ch0__len 8
++#define ma_vol_db_ch0__mask 0xff
++#define ma_vol_db_ch0__shift 0x00
++#define ma_vol_db_ch0__reset 0x18
++//----------------------------------------------------------------vol_db_ch1---
++// volume channel 1
++#define ma_vol_db_ch1__a 67
++#define ma_vol_db_ch1__len 8
++#define ma_vol_db_ch1__mask 0xff
++#define ma_vol_db_ch1__shift 0x00
++#define ma_vol_db_ch1__reset 0x18
++//----------------------------------------------------------------vol_db_ch2---
++// volume channel 2
++#define ma_vol_db_ch2__a 68
++#define ma_vol_db_ch2__len 8
++#define ma_vol_db_ch2__mask 0xff
++#define ma_vol_db_ch2__shift 0x00
++#define ma_vol_db_ch2__reset 0x18
++//----------------------------------------------------------------vol_db_ch3---
++// volume channel 3
++#define ma_vol_db_ch3__a 69
++#define ma_vol_db_ch3__len 8
++#define ma_vol_db_ch3__mask 0xff
++#define ma_vol_db_ch3__shift 0x00
++#define ma_vol_db_ch3__reset 0x18
++//---------------------------------------------------------------vol_lsb_ch0---
++// volume channel 1 - 1/4 steps
++#define ma_vol_lsb_ch0__a 70
++#define ma_vol_lsb_ch0__len 2
++#define ma_vol_lsb_ch0__mask 0x03
++#define ma_vol_lsb_ch0__shift 0x00
++#define ma_vol_lsb_ch0__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch1---
++// volume channel 3 - 1/4 steps
++#define ma_vol_lsb_ch1__a 70
++#define ma_vol_lsb_ch1__len 2
++#define ma_vol_lsb_ch1__mask 0x0c
++#define ma_vol_lsb_ch1__shift 0x02
++#define ma_vol_lsb_ch1__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch2---
++// volume channel 2 - 1/4 steps
++#define ma_vol_lsb_ch2__a 70
++#define ma_vol_lsb_ch2__len 2
++#define ma_vol_lsb_ch2__mask 0x30
++#define ma_vol_lsb_ch2__shift 0x04
++#define ma_vol_lsb_ch2__reset 0x00
++//---------------------------------------------------------------vol_lsb_ch3---
++// volume channel 3 - 1/4 steps
++#define ma_vol_lsb_ch3__a 70
++#define ma_vol_lsb_ch3__len 2
++#define ma_vol_lsb_ch3__mask 0xc0
++#define ma_vol_lsb_ch3__shift 0x06
++#define ma_vol_lsb_ch3__reset 0x00
++//----------------------------------------------------------------thr_db_ch0---
++// thr_db channel 0
++#define ma_thr_db_ch0__a 71
++#define ma_thr_db_ch0__len 8
++#define ma_thr_db_ch0__mask 0xff
++#define ma_thr_db_ch0__shift 0x00
++#define ma_thr_db_ch0__reset 0x18
++//----------------------------------------------------------------thr_db_ch1---
++// thr db ch1
++#define ma_thr_db_ch1__a 72
++#define ma_thr_db_ch1__len 8
++#define ma_thr_db_ch1__mask 0xff
++#define ma_thr_db_ch1__shift 0x00
++#define ma_thr_db_ch1__reset 0x18
++//----------------------------------------------------------------thr_db_ch2---
++// thr db ch2
++#define ma_thr_db_ch2__a 73
++#define ma_thr_db_ch2__len 8
++#define ma_thr_db_ch2__mask 0xff
++#define ma_thr_db_ch2__shift 0x00
++#define ma_thr_db_ch2__reset 0x18
++//----------------------------------------------------------------thr_db_ch3---
++// threshold db ch3
++#define ma_thr_db_ch3__a 74
++#define ma_thr_db_ch3__len 8
++#define ma_thr_db_ch3__mask 0xff
++#define ma_thr_db_ch3__shift 0x00
++#define ma_thr_db_ch3__reset 0x18
++//---------------------------------------------------------------thr_lsb_ch0---
++// thr lsb ch0
++#define ma_thr_lsb_ch0__a 75
++#define ma_thr_lsb_ch0__len 2
++#define ma_thr_lsb_ch0__mask 0x03
++#define ma_thr_lsb_ch0__shift 0x00
++#define ma_thr_lsb_ch0__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch1---
++// thr lsb ch1
++#define ma_thr_lsb_ch1__a 75
++#define ma_thr_lsb_ch1__len 2
++#define ma_thr_lsb_ch1__mask 0x0c
++#define ma_thr_lsb_ch1__shift 0x02
++#define ma_thr_lsb_ch1__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch2---
++// thr lsb ch2 1/4 db step
++#define ma_thr_lsb_ch2__a 75
++#define ma_thr_lsb_ch2__len 2
++#define ma_thr_lsb_ch2__mask 0x30
++#define ma_thr_lsb_ch2__shift 0x04
++#define ma_thr_lsb_ch2__reset 0x00
++//---------------------------------------------------------------thr_lsb_ch3---
++// threshold lsb ch3
++#define ma_thr_lsb_ch3__a 75
++#define ma_thr_lsb_ch3__len 2
++#define ma_thr_lsb_ch3__mask 0xc0
++#define ma_thr_lsb_ch3__shift 0x06
++#define ma_thr_lsb_ch3__reset 0x00
++//-----------------------------------------------------------dcu_mon0.pm_mon---
++// power mode monitor channel 0
++#define ma_dcu_mon0__pm_mon__a 96
++#define ma_dcu_mon0__pm_mon__len 2
++#define ma_dcu_mon0__pm_mon__mask 0x03
++#define ma_dcu_mon0__pm_mon__shift 0x00
++#define ma_dcu_mon0__pm_mon__reset 0x00
++//-----------------------------------------------------dcu_mon0.freqmode_mon---
++// frequence mode monitor channel 0
++#define ma_dcu_mon0__freqmode_mon__a 96
++#define ma_dcu_mon0__freqmode_mon__len 3
++#define ma_dcu_mon0__freqmode_mon__mask 0x70
++#define ma_dcu_mon0__freqmode_mon__shift 0x04
++#define ma_dcu_mon0__freqmode_mon__reset 0x00
++//-------------------------------------------------------dcu_mon0.pps_passed---
++// dcu0 pps completion indicator
++#define ma_dcu_mon0__pps_passed__a 96
++#define ma_dcu_mon0__pps_passed__len 1
++#define ma_dcu_mon0__pps_passed__mask 0x80
++#define ma_dcu_mon0__pps_passed__shift 0x07
++#define ma_dcu_mon0__pps_passed__reset 0x00
++//----------------------------------------------------------dcu_mon0.ocp_mon---
++// ocp monitor channel 0
++#define ma_dcu_mon0__ocp_mon__a 97
++#define ma_dcu_mon0__ocp_mon__len 1
++#define ma_dcu_mon0__ocp_mon__mask 0x01
++#define ma_dcu_mon0__ocp_mon__shift 0x00
++#define ma_dcu_mon0__ocp_mon__reset 0x00
++//--------------------------------------------------------dcu_mon0.vcfly1_ok---
++// cfly1 protection monitor channel 0.
++#define ma_dcu_mon0__vcfly1_ok__a 97
++#define ma_dcu_mon0__vcfly1_ok__len 1
++#define ma_dcu_mon0__vcfly1_ok__mask 0x02
++#define ma_dcu_mon0__vcfly1_ok__shift 0x01
++#define ma_dcu_mon0__vcfly1_ok__reset 0x00
++//--------------------------------------------------------dcu_mon0.vcfly2_ok---
++// cfly2 protection monitor channel 0.
++#define ma_dcu_mon0__vcfly2_ok__a 97
++#define ma_dcu_mon0__vcfly2_ok__len 1
++#define ma_dcu_mon0__vcfly2_ok__mask 0x04
++#define ma_dcu_mon0__vcfly2_ok__shift 0x02
++#define ma_dcu_mon0__vcfly2_ok__reset 0x00
++//----------------------------------------------------------dcu_mon0.pvdd_ok---
++// dcu0 pvdd monitor
++#define ma_dcu_mon0__pvdd_ok__a 97
++#define ma_dcu_mon0__pvdd_ok__len 1
++#define ma_dcu_mon0__pvdd_ok__mask 0x08
++#define ma_dcu_mon0__pvdd_ok__shift 0x03
++#define ma_dcu_mon0__pvdd_ok__reset 0x00
++//-----------------------------------------------------------dcu_mon0.vdd_ok---
++// dcu0 vdd monitor
++#define ma_dcu_mon0__vdd_ok__a 97
++#define ma_dcu_mon0__vdd_ok__len 1
++#define ma_dcu_mon0__vdd_ok__mask 0x10
++#define ma_dcu_mon0__vdd_ok__shift 0x04
++#define ma_dcu_mon0__vdd_ok__reset 0x00
++//-------------------------------------------------------------dcu_mon0.mute---
++// dcu0 mute monitor
++#define ma_dcu_mon0__mute__a 97
++#define ma_dcu_mon0__mute__len 1
++#define ma_dcu_mon0__mute__mask 0x20
++#define ma_dcu_mon0__mute__shift 0x05
++#define ma_dcu_mon0__mute__reset 0x00
++//------------------------------------------------------------dcu_mon0.m_mon---
++// m sense monitor channel 0
++#define ma_dcu_mon0__m_mon__a 98
++#define ma_dcu_mon0__m_mon__len 8
++#define ma_dcu_mon0__m_mon__mask 0xff
++#define ma_dcu_mon0__m_mon__shift 0x00
++#define ma_dcu_mon0__m_mon__reset 0x00
++//-----------------------------------------------------------dcu_mon1.pm_mon---
++// power mode monitor channel 1
++#define ma_dcu_mon1__pm_mon__a 100
++#define ma_dcu_mon1__pm_mon__len 2
++#define ma_dcu_mon1__pm_mon__mask 0x03
++#define ma_dcu_mon1__pm_mon__shift 0x00
++#define ma_dcu_mon1__pm_mon__reset 0x00
++//-----------------------------------------------------dcu_mon1.freqmode_mon---
++// frequence mode monitor channel 1
++#define ma_dcu_mon1__freqmode_mon__a 100
++#define ma_dcu_mon1__freqmode_mon__len 3
++#define ma_dcu_mon1__freqmode_mon__mask 0x70
++#define ma_dcu_mon1__freqmode_mon__shift 0x04
++#define ma_dcu_mon1__freqmode_mon__reset 0x00
++//-------------------------------------------------------dcu_mon1.pps_passed---
++// dcu1 pps completion indicator
++#define ma_dcu_mon1__pps_passed__a 100
++#define ma_dcu_mon1__pps_passed__len 1
++#define ma_dcu_mon1__pps_passed__mask 0x80
++#define ma_dcu_mon1__pps_passed__shift 0x07
++#define ma_dcu_mon1__pps_passed__reset 0x00
++//----------------------------------------------------------dcu_mon1.ocp_mon---
++// ocp monitor channel 1
++#define ma_dcu_mon1__ocp_mon__a 101
++#define ma_dcu_mon1__ocp_mon__len 1
++#define ma_dcu_mon1__ocp_mon__mask 0x01
++#define ma_dcu_mon1__ocp_mon__shift 0x00
++#define ma_dcu_mon1__ocp_mon__reset 0x00
++//--------------------------------------------------------dcu_mon1.vcfly1_ok---
++// cfly1 protcetion monitor channel 1
++#define ma_dcu_mon1__vcfly1_ok__a 101
++#define ma_dcu_mon1__vcfly1_ok__len 1
++#define ma_dcu_mon1__vcfly1_ok__mask 0x02
++#define ma_dcu_mon1__vcfly1_ok__shift 0x01
++#define ma_dcu_mon1__vcfly1_ok__reset 0x00
++//--------------------------------------------------------dcu_mon1.vcfly2_ok---
++// cfly2 protection monitor channel 1
++#define ma_dcu_mon1__vcfly2_ok__a 101
++#define ma_dcu_mon1__vcfly2_ok__len 1
++#define ma_dcu_mon1__vcfly2_ok__mask 0x04
++#define ma_dcu_mon1__vcfly2_ok__shift 0x02
++#define ma_dcu_mon1__vcfly2_ok__reset 0x00
++//----------------------------------------------------------dcu_mon1.pvdd_ok---
++// dcu1 pvdd monitor
++#define ma_dcu_mon1__pvdd_ok__a 101
++#define ma_dcu_mon1__pvdd_ok__len 1
++#define ma_dcu_mon1__pvdd_ok__mask 0x08
++#define ma_dcu_mon1__pvdd_ok__shift 0x03
++#define ma_dcu_mon1__pvdd_ok__reset 0x00
++//-----------------------------------------------------------dcu_mon1.vdd_ok---
++// dcu1 vdd monitor
++#define ma_dcu_mon1__vdd_ok__a 101
++#define ma_dcu_mon1__vdd_ok__len 1
++#define ma_dcu_mon1__vdd_ok__mask 0x10
++#define ma_dcu_mon1__vdd_ok__shift 0x04
++#define ma_dcu_mon1__vdd_ok__reset 0x00
++//-------------------------------------------------------------dcu_mon1.mute---
++// dcu1 mute monitor
++#define ma_dcu_mon1__mute__a 101
++#define ma_dcu_mon1__mute__len 1
++#define ma_dcu_mon1__mute__mask 0x20
++#define ma_dcu_mon1__mute__shift 0x05
++#define ma_dcu_mon1__mute__reset 0x00
++//------------------------------------------------------------dcu_mon1.m_mon---
++// m sense monitor channel 1
++#define ma_dcu_mon1__m_mon__a 102
++#define ma_dcu_mon1__m_mon__len 8
++#define ma_dcu_mon1__m_mon__mask 0xff
++#define ma_dcu_mon1__m_mon__shift 0x00
++#define ma_dcu_mon1__m_mon__reset 0x00
++//--------------------------------------------------------dcu_mon0.sw_enable---
++// dcu0 switch enable monitor
++#define ma_dcu_mon0__sw_enable__a 104
++#define ma_dcu_mon0__sw_enable__len 1
++#define ma_dcu_mon0__sw_enable__mask 0x40
++#define ma_dcu_mon0__sw_enable__shift 0x06
++#define ma_dcu_mon0__sw_enable__reset 0x00
++//--------------------------------------------------------dcu_mon1.sw_enable---
++// dcu1 switch enable monitor
++#define ma_dcu_mon1__sw_enable__a 104
++#define ma_dcu_mon1__sw_enable__len 1
++#define ma_dcu_mon1__sw_enable__mask 0x80
++#define ma_dcu_mon1__sw_enable__shift 0x07
++#define ma_dcu_mon1__sw_enable__reset 0x00
++//------------------------------------------------------------hvboot0_ok_mon---
++// hvboot0_ok for test/debug
++#define ma_hvboot0_ok_mon__a 105
++#define ma_hvboot0_ok_mon__len 1
++#define ma_hvboot0_ok_mon__mask 0x40
++#define ma_hvboot0_ok_mon__shift 0x06
++#define ma_hvboot0_ok_mon__reset 0x00
++//------------------------------------------------------------hvboot1_ok_mon---
++// hvboot1_ok for test/debug
++#define ma_hvboot1_ok_mon__a 105
++#define ma_hvboot1_ok_mon__len 1
++#define ma_hvboot1_ok_mon__mask 0x80
++#define ma_hvboot1_ok_mon__shift 0x07
++#define ma_hvboot1_ok_mon__reset 0x00
++//-----------------------------------------------------------------error_acc---
++// accumulated errors,  at and after triggering
++#define ma_error_acc__a 109
++#define ma_error_acc__len 8
++#define ma_error_acc__mask 0xff
++#define ma_error_acc__shift 0x00
++#define ma_error_acc__reset 0x00
++//-------------------------------------------------------------i2s_data_rate---
++// detected i2s data rate: 00/01/10 = x1/x2/x4
++#define ma_i2s_data_rate__a 116
++#define ma_i2s_data_rate__len 2
++#define ma_i2s_data_rate__mask 0x03
++#define ma_i2s_data_rate__shift 0x00
++#define ma_i2s_data_rate__reset 0x00
++//---------------------------------------------------------audio_in_mode_mon---
++// audio input mode monitor
++#define ma_audio_in_mode_mon__a 116
++#define ma_audio_in_mode_mon__len 3
++#define ma_audio_in_mode_mon__mask 0x1c
++#define ma_audio_in_mode_mon__shift 0x02
++#define ma_audio_in_mode_mon__reset 0x00
++//------------------------------------------------------------------msel_mon---
++// msel[2:0] monitor register
++#define ma_msel_mon__a 117
++#define ma_msel_mon__len 3
++#define ma_msel_mon__mask 0x07
++#define ma_msel_mon__shift 0x00
++#define ma_msel_mon__reset 0x00
++//---------------------------------------------------------------------error---
++// current error flag monitor reg - for app. ctrl.
++#define ma_error__a 124
++#define ma_error__len 8
++#define ma_error__mask 0xff
++#define ma_error__shift 0x00
++#define ma_error__reset 0x00
++//----------------------------------------------------audio_proc_limiter_mon---
++// b7-b4: channel 3-0 limiter active
++#define ma_audio_proc_limiter_mon__a 126
++#define ma_audio_proc_limiter_mon__len 4
++#define ma_audio_proc_limiter_mon__mask 0xf0
++#define ma_audio_proc_limiter_mon__shift 0x04
++#define ma_audio_proc_limiter_mon__reset 0x00
++//-------------------------------------------------------audio_proc_clip_mon---
++// b3-b0: channel 3-0 clipping monitor
++#define ma_audio_proc_clip_mon__a 126
++#define ma_audio_proc_clip_mon__len 4
++#define ma_audio_proc_clip_mon__mask 0x0f
++#define ma_audio_proc_clip_mon__shift 0x00
++#define ma_audio_proc_clip_mon__reset 0x00
++#endif
++
++#define SOC_ENUM_ERR(xname, xenum)\
++{     .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
++      .access = SNDRV_CTL_ELEM_ACCESS_READ,\
++      .info = snd_soc_info_enum_double,\
++      .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
++      .private_value = (unsigned long)&(xenum) }
++
++static struct i2c_client *i2c;
++
++struct ma120x0p_priv {
++      struct regmap *regmap;
++      int mclk_div;
++      struct snd_soc_component *component;
++      struct gpio_desc *enable_gpio;
++      struct gpio_desc *mute_gpio;
++      struct gpio_desc *booster_gpio;
++      struct gpio_desc *error_gpio;
++};
++
++static struct ma120x0p_priv *priv_data;
++
++//Used to share the IRQ number within this file
++static unsigned int irqNumber;
++
++// Function prototype for the custom IRQ handler function
++static irqreturn_t ma120x0p_irq_handler(int irq, void *data);
++
++//Alsa Controls
++static const char * const limenable_text[] = {"Bypassed", "Enabled"};
++static const char * const limatack_text[] = {"Slow", "Normal", "Fast"};
++static const char * const limrelease_text[] = {"Slow", "Normal", "Fast"};
++
++static const char * const err_flycap_text[] = {"Ok", "Error"};
++static const char * const err_overcurr_text[] = {"Ok", "Error"};
++static const char * const err_pllerr_text[] = {"Ok", "Error"};
++static const char * const err_pvddunder_text[] = {"Ok", "Error"};
++static const char * const err_overtempw_text[] = {"Ok", "Error"};
++static const char * const err_overtempe_text[] = {"Ok", "Error"};
++static const char * const err_pinlowimp_text[] = {"Ok", "Error"};
++static const char * const err_dcprot_text[] = {"Ok", "Error"};
++
++static const char * const pwr_mode_prof_text[] = {"PMF0", "PMF1", "PMF2",
++"PMF3", "PMF4"};
++
++static const struct soc_enum lim_enable_ctrl =
++      SOC_ENUM_SINGLE(ma_audio_proc_limiterenable__a,
++              ma_audio_proc_limiterenable__shift,
++              ma_audio_proc_limiterenable__len + 1,
++              limenable_text);
++static const struct soc_enum limatack_ctrl =
++      SOC_ENUM_SINGLE(ma_audio_proc_attack__a,
++              ma_audio_proc_attack__shift,
++              ma_audio_proc_attack__len + 1,
++              limatack_text);
++static const struct soc_enum limrelease_ctrl =
++      SOC_ENUM_SINGLE(ma_audio_proc_release__a,
++              ma_audio_proc_release__shift,
++              ma_audio_proc_release__len + 1,
++              limrelease_text);
++static const struct soc_enum err_flycap_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 0, 3, err_flycap_text);
++static const struct soc_enum err_overcurr_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 1, 3, err_overcurr_text);
++static const struct soc_enum err_pllerr_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 2, 3, err_pllerr_text);
++static const struct soc_enum err_pvddunder_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 3, 3, err_pvddunder_text);
++static const struct soc_enum err_overtempw_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 4, 3, err_overtempw_text);
++static const struct soc_enum err_overtempe_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 5, 3, err_overtempe_text);
++static const struct soc_enum err_pinlowimp_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 6, 3, err_pinlowimp_text);
++static const struct soc_enum err_dcprot_ctrl =
++      SOC_ENUM_SINGLE(ma_error__a, 7, 3, err_dcprot_text);
++static const struct soc_enum pwr_mode_prof_ctrl =
++      SOC_ENUM_SINGLE(ma_pmprofile__a, ma_pmprofile__shift, 5,
++              pwr_mode_prof_text);
++
++static const char * const pwr_mode_texts[] = {
++              "Dynamic power mode",
++              "Power mode 1",
++              "Power mode 2",
++              "Power mode 3",
++      };
++
++static const int pwr_mode_values[] = {
++              0x10,
++              0x50,
++              0x60,
++              0x70,
++      };
++
++static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl,
++      ma_pm_man__a, 0, 0x70,
++      pwr_mode_texts,
++      pwr_mode_values);
++
++static const DECLARE_TLV_DB_SCALE(ma120x0p_vol_tlv, -5000, 100,  0);
++static const DECLARE_TLV_DB_SCALE(ma120x0p_lim_tlv, -5000, 100,  0);
++static const DECLARE_TLV_DB_SCALE(ma120x0p_lr_tlv, -5000, 100,  0);
++
++static const struct snd_kcontrol_new ma120x0p_snd_controls[] = {
++      //Master Volume
++      SOC_SINGLE_RANGE_TLV("A.Mstr Vol Volume",
++              ma_vol_db_master__a, 0, 0x18, 0x4a, 1, ma120x0p_vol_tlv),
++
++      //L-R Volume ch0
++      SOC_SINGLE_RANGE_TLV("B.L Vol Volume",
++              ma_vol_db_ch0__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
++      SOC_SINGLE_RANGE_TLV("C.R Vol Volume",
++              ma_vol_db_ch1__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
++
++      //L-R Limiter Threshold ch0-ch1
++      SOC_DOUBLE_R_RANGE_TLV("D.Lim thresh Volume",
++              ma_thr_db_ch0__a, ma_thr_db_ch1__a, 0, 0x0e, 0x4a, 1,
++              ma120x0p_lim_tlv),
++
++      //Enum Switches/Selectors
++      //SOC_ENUM("E.AudioProc Mute", audioproc_mute_ctrl),
++      SOC_ENUM("F.Limiter Enable", lim_enable_ctrl),
++      SOC_ENUM("G.Limiter Attck", limatack_ctrl),
++      SOC_ENUM("H.Limiter Rls", limrelease_ctrl),
++
++      //Enum Error Monitor (read-only)
++      SOC_ENUM_ERR("I.Err flycap", err_flycap_ctrl),
++      SOC_ENUM_ERR("J.Err overcurr", err_overcurr_ctrl),
++      SOC_ENUM_ERR("K.Err pllerr", err_pllerr_ctrl),
++      SOC_ENUM_ERR("L.Err pvddunder", err_pvddunder_ctrl),
++      SOC_ENUM_ERR("M.Err overtempw", err_overtempw_ctrl),
++      SOC_ENUM_ERR("N.Err overtempe", err_overtempe_ctrl),
++      SOC_ENUM_ERR("O.Err pinlowimp", err_pinlowimp_ctrl),
++      SOC_ENUM_ERR("P.Err dcprot", err_dcprot_ctrl),
++
++      //Power modes profiles
++      SOC_ENUM("Q.PM Prof", pwr_mode_prof_ctrl),
++
++      // Power mode selection (Dynamic,1,2,3)
++      SOC_ENUM("R.Power Mode", pwr_mode_ctrl),
++};
++
++//Machine Driver
++static int ma120x0p_hw_params(struct snd_pcm_substream *substream,
++      struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
++{
++      u16 blen = 0x00;
++
++      struct snd_soc_component *component = dai->component;
++
++      priv_data->component = component;
++
++      switch (params_format(params)) {
++      case SNDRV_PCM_FORMAT_S16_LE:
++              blen = 0x10;
++              break;
++      case SNDRV_PCM_FORMAT_S24_LE:
++              blen = 0x00;
++              break;
++      case SNDRV_PCM_FORMAT_S32_LE:
++              blen = 0x00;
++              break;
++      default:
++              dev_err(dai->dev, "Unsupported word length: %u\n",
++              params_format(params));
++              return -EINVAL;
++      }
++
++      // set word length
++      snd_soc_component_update_bits(component, ma_i2s_framesize__a,
++              ma_i2s_framesize__mask, blen);
++
++      return 0;
++}
++
++static int ma120x0p_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
++{
++      int val = 0;
++
++      struct ma120x0p_priv *ma120x0p;
++
++      struct snd_soc_component *component = dai->component;
++
++      ma120x0p = snd_soc_component_get_drvdata(component);
++
++      if (mute)
++              val = 0;
++      else
++              val = 1;
++
++      gpiod_set_value_cansleep(priv_data->mute_gpio, val);
++
++      return 0;
++}
++
++static const struct snd_soc_dai_ops ma120x0p_dai_ops = {
++      .hw_params              =       ma120x0p_hw_params,
++      .mute_stream    =       ma120x0p_mute_stream,
++};
++
++static struct snd_soc_dai_driver ma120x0p_dai = {
++      .name           = "ma120x0p-amp",
++      .playback       =       {
++              .stream_name    = "Playback",
++              .channels_min   = 2,
++              .channels_max   = 2,
++              .rates = SNDRV_PCM_RATE_CONTINUOUS,
++              .rate_min = 44100,
++              .rate_max = 48000,
++              .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
++      },
++      .ops        = &ma120x0p_dai_ops,
++};
++
++//Codec Driver
++static int ma120x0p_clear_err(struct snd_soc_component *component)
++{
++      int ret = 0;
++
++      struct ma120x0p_priv *ma120x0p;
++
++      ma120x0p = snd_soc_component_get_drvdata(component);
++
++      ret = snd_soc_component_update_bits(component,
++              ma_eh_clear__a, ma_eh_clear__mask, 0x00);
++      if (ret < 0)
++              return ret;
++
++      ret = snd_soc_component_update_bits(component,
++              ma_eh_clear__a, ma_eh_clear__mask, 0x04);
++      if (ret < 0)
++              return ret;
++
++      ret = snd_soc_component_update_bits(component,
++              ma_eh_clear__a, ma_eh_clear__mask, 0x00);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static void ma120x0p_remove(struct snd_soc_component *component)
++{
++      struct ma120x0p_priv *ma120x0p;
++
++      ma120x0p = snd_soc_component_get_drvdata(component);
++}
++
++static int ma120x0p_probe(struct snd_soc_component *component)
++{
++      struct ma120x0p_priv *ma120x0p;
++
++      int ret = 0;
++
++      i2c = container_of(component->dev, struct i2c_client, dev);
++
++      ma120x0p = snd_soc_component_get_drvdata(component);
++
++      //Reset error
++      ma120x0p_clear_err(component);
++      if (ret < 0)
++              return ret;
++
++      // set serial audio format I2S and enable audio processor
++      ret = snd_soc_component_write(component, ma_i2s_format__a, 0x08);
++      if (ret < 0)
++              return ret;
++
++      // Enable audio limiter
++      ret = snd_soc_component_update_bits(component,
++              ma_audio_proc_limiterenable__a,
++              ma_audio_proc_limiterenable__mask, 0x40);
++      if (ret < 0)
++              return ret;
++
++      // Set lim attack to fast
++      ret = snd_soc_component_update_bits(component,
++              ma_audio_proc_attack__a, ma_audio_proc_attack__mask, 0x80);
++      if (ret < 0)
++              return ret;
++
++      // Set lim attack to low
++      ret = snd_soc_component_update_bits(component,
++              ma_audio_proc_release__a, ma_audio_proc_release__mask, 0x00);
++      if (ret < 0)
++              return ret;
++
++      // set volume to 0dB
++      ret = snd_soc_component_write(component, ma_vol_db_master__a, 0x18);
++      if (ret < 0)
++              return ret;
++
++      // set ch0 lim thresh to -15dB
++      ret = snd_soc_component_write(component, ma_thr_db_ch0__a, 0x27);
++      if (ret < 0)
++              return ret;
++
++      // set ch1 lim thresh to -15dB
++      ret = snd_soc_component_write(component, ma_thr_db_ch1__a, 0x27);
++      if (ret < 0)
++              return ret;
++
++      //Check for errors
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x00, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x01, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x02, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x08, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x10, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x20, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x40, 0);
++      if (ret < 0)
++              return ret;
++      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x80, 0);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int ma120x0p_set_bias_level(struct snd_soc_component *component,
++      enum snd_soc_bias_level level)
++{
++      int ret = 0;
++
++      struct ma120x0p_priv *ma120x0p;
++
++      ma120x0p = snd_soc_component_get_drvdata(component);
++
++      switch (level) {
++      case SND_SOC_BIAS_ON:
++              break;
++
++      case SND_SOC_BIAS_PREPARE:
++              break;
++
++      case SND_SOC_BIAS_STANDBY:
++              ret = gpiod_get_value_cansleep(priv_data->enable_gpio);
++              if (ret != 0) {
++                      dev_err(component->dev, "Device ma120x0p disabled in STANDBY BIAS: %d\n",
++                      ret);
++                      return ret;
++              }
++              break;
++
++      case SND_SOC_BIAS_OFF:
++              break;
++      }
++
++      return 0;
++}
++
++static const struct snd_soc_dapm_widget ma120x0p_dapm_widgets[] = {
++      SND_SOC_DAPM_OUTPUT("OUT_A"),
++      SND_SOC_DAPM_OUTPUT("OUT_B"),
++};
++
++static const struct snd_soc_dapm_route ma120x0p_dapm_routes[] = {
++      { "OUT_B",  NULL, "Playback" },
++      { "OUT_A",  NULL, "Playback" },
++};
++
++static const struct snd_soc_component_driver ma120x0p_component_driver = {
++      .probe = ma120x0p_probe,
++      .remove = ma120x0p_remove,
++      .set_bias_level = ma120x0p_set_bias_level,
++      .dapm_widgets           = ma120x0p_dapm_widgets,
++      .num_dapm_widgets       = ARRAY_SIZE(ma120x0p_dapm_widgets),
++      .dapm_routes            = ma120x0p_dapm_routes,
++      .num_dapm_routes        = ARRAY_SIZE(ma120x0p_dapm_routes),
++      .controls = ma120x0p_snd_controls,
++      .num_controls = ARRAY_SIZE(ma120x0p_snd_controls),
++      .use_pmdown_time        = 1,
++      .endianness             = 1,
++      .non_legacy_dai_naming  = 1,
++};
++
++//I2C Driver
++static const struct reg_default ma120x0p_reg_defaults[] = {
++      {       0x01,   0x3c    },
++};
++
++static bool ma120x0p_reg_volatile(struct device *dev, unsigned int reg)
++{
++      switch (reg) {
++      case ma_error__a:
++                      return true;
++      default:
++                      return false;
++      }
++}
++
++static const struct of_device_id ma120x0p_of_match[] = {
++      { .compatible = "ma,ma120x0p", },
++      { }
++};
++
++MODULE_DEVICE_TABLE(of, ma120x0p_of_match);
++
++static struct regmap_config ma120x0p_regmap_config = {
++      .reg_bits = 8,
++      .val_bits = 8,
++
++      .max_register = 255,
++      .volatile_reg = ma120x0p_reg_volatile,
++
++      .cache_type = REGCACHE_RBTREE,
++      .reg_defaults = ma120x0p_reg_defaults,
++      .num_reg_defaults = ARRAY_SIZE(ma120x0p_reg_defaults),
++};
++
++static int ma120x0p_i2c_probe(struct i2c_client *i2c,
++      const struct i2c_device_id *id)
++{
++      int ret;
++
++      priv_data = devm_kzalloc(&i2c->dev, sizeof(*priv_data), GFP_KERNEL);
++      if (!priv_data)
++              return -ENOMEM;
++      i2c_set_clientdata(i2c, priv_data);
++
++      priv_data->regmap = devm_regmap_init_i2c(i2c, &ma120x0p_regmap_config);
++      if (IS_ERR(priv_data->regmap)) {
++              ret = PTR_ERR(priv_data->regmap);
++              return ret;
++      }
++
++      //Startup sequence
++
++      //Make sure the device is muted
++      priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp",
++              GPIOD_OUT_LOW);
++      if (IS_ERR(priv_data->mute_gpio)) {
++              ret = PTR_ERR(priv_data->mute_gpio);
++              dev_err(&i2c->dev, "Failed to get mute gpio line: %d\n", ret);
++              return ret;
++      }
++      msleep(50);
++
++// MA120xx0P devices are usually powered by an integrated boost converter.
++// An option GPIO control line is provided to enable the booster properly and
++// in sync with the enable and mute GPIO lines.
++      priv_data->booster_gpio = devm_gpiod_get_optional(&i2c->dev,
++              "booster_gp", GPIOD_OUT_LOW);
++      if (IS_ERR(priv_data->booster_gpio)) {
++              ret = PTR_ERR(priv_data->booster_gpio);
++              dev_err(&i2c->dev,
++              "Failed to get booster enable gpio line: %d\n", ret);
++              return ret;
++      }
++      msleep(50);
++
++      //Enable booster and wait 200ms until stable PVDD
++      gpiod_set_value_cansleep(priv_data->booster_gpio, 1);
++      msleep(200);
++
++      //Enable ma120x0pp
++      priv_data->enable_gpio = devm_gpiod_get(&i2c->dev,
++              "enable_gp", GPIOD_OUT_LOW);
++      if (IS_ERR(priv_data->enable_gpio)) {
++              ret = PTR_ERR(priv_data->enable_gpio);
++              dev_err(&i2c->dev,
++              "Failed to get ma120x0p enable gpio line: %d\n", ret);
++              return ret;
++      }
++      msleep(50);
++
++      //Optional use of ma120x0pp error line as an interrupt trigger to
++      //platform GPIO.
++      //Get error input gpio ma120x0p
++      priv_data->error_gpio = devm_gpiod_get_optional(&i2c->dev,
++               "error_gp", GPIOD_IN);
++      if (IS_ERR(priv_data->error_gpio)) {
++              ret = PTR_ERR(priv_data->error_gpio);
++              dev_err(&i2c->dev,
++                      "Failed to get ma120x0p error gpio line: %d\n", ret);
++              return ret;
++      }
++
++      if (priv_data->error_gpio != NULL) {
++              irqNumber = gpiod_to_irq(priv_data->error_gpio);
++
++              ret = devm_request_threaded_irq(&i2c->dev,
++                       irqNumber, ma120x0p_irq_handler,
++                       NULL, IRQF_TRIGGER_FALLING,
++                       "ma120x0p", priv_data);
++              if (ret != 0)
++                      dev_warn(&i2c->dev, "Failed to request IRQ: %d\n",
++                              ret);
++      }
++
++      ret = devm_snd_soc_register_component(&i2c->dev,
++              &ma120x0p_component_driver, &ma120x0p_dai, 1);
++
++      return ret;
++}
++
++static irqreturn_t ma120x0p_irq_handler(int irq, void *data)
++{
++      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++      return IRQ_HANDLED;
++}
++
++static int ma120x0p_i2c_remove(struct i2c_client *i2c)
++{
++      snd_soc_unregister_component(&i2c->dev);
++      i2c_set_clientdata(i2c, NULL);
++
++      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++      msleep(30);
++      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++      msleep(200);
++      gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
++      msleep(200);
++
++      kfree(priv_data);
++
++      return 0;
++}
++
++static void ma120x0p_i2c_shutdown(struct i2c_client *i2c)
++{
++      snd_soc_unregister_component(&i2c->dev);
++      i2c_set_clientdata(i2c, NULL);
++
++      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
++      msleep(30);
++      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
++      msleep(200);
++      gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
++      msleep(200);
++
++      kfree(priv_data);
++}
++
++static const struct i2c_device_id ma120x0p_i2c_id[] = {
++      { "ma120x0p", 0 },
++      { }
++};
++
++MODULE_DEVICE_TABLE(i2c, ma120x0p_i2c_id);
++
++static struct i2c_driver ma120x0p_i2c_driver = {
++      .driver = {
++              .name = "ma120x0p",
++              .owner = THIS_MODULE,
++              .of_match_table = ma120x0p_of_match,
++      },
++      .probe = ma120x0p_i2c_probe,
++      .remove = ma120x0p_i2c_remove,
++      .shutdown = ma120x0p_i2c_shutdown,
++      .id_table = ma120x0p_i2c_id
++};
++
++static int __init ma120x0p_modinit(void)
++{
++      int ret = 0;
++
++      ret = i2c_add_driver(&ma120x0p_i2c_driver);
++      if (ret != 0) {
++              pr_err("Failed to register MA120X0P I2C driver: %d\n", ret);
++              return ret;
++      }
++      return ret;
++}
++module_init(ma120x0p_modinit);
++
++static void __exit ma120x0p_exit(void)
++{
++      i2c_del_driver(&ma120x0p_i2c_driver);
++}
++module_exit(ma120x0p_exit);
++
++MODULE_AUTHOR("Ariel Muszkat ariel.muszkat@gmail.com>");
++MODULE_DESCRIPTION("ASoC driver for ma120x0p");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0475-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch b/target/linux/bcm27xx/patches-5.4/950-0475-gpio-ir-overlay-add-parameter-to-configure-signal-po.patch
deleted file mode 100644 (file)
index cdd14f7..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-From e253d03936265dc4ab8ae9ae89d2a885e80a45a6 Mon Sep 17 00:00:00 2001
-From: Matthias Reichl <hias@horus.com>
-Date: Fri, 6 Mar 2020 11:08:10 +0100
-Subject: [PATCH] gpio-ir-overlay: add parameter to configure signal
- polarity (#3490)
-
-Standard IR receivers use inverted / active-low signalling
-and the gpio-ir overlay configures the GPIO appropriately
-as GPIO_ACTIVE_LOW (1).
-
-In order to support (rather rare) non-inverted / active-high
-signalling the GPIO needs to be configured as GPIO_ACTIVE_HIGH (0).
-
-Add an "invert" parameter to override this like in the gpio-ir-tx
-overlay.
-
-Signed-off-by: Matthias Reichl <hias@horus.com>
----
- arch/arm/boot/dts/overlays/README              | 4 ++++
- arch/arm/boot/dts/overlays/gpio-ir-overlay.dts | 1 +
- 2 files changed, 5 insertions(+)
-
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -754,6 +754,10 @@ Params: gpio_pin                Input pi
-         gpio_pull               Desired pull-up/down state (off, down, up)
-                                 Default is "up".
-+        invert                  "1" = invert the input (active-low signalling).
-+                                "0" = non-inverted input (active-high
-+                                signalling). Default is "1".
-+
-         rc-map-name             Default rc keymap (can also be changed by
-                                 ir-keytable), defaults to "rc-rc6-mce"
---- a/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/gpio-ir-overlay.dts
-@@ -42,6 +42,7 @@
-                                 <&gpio_ir_pins>,"brcm,pins:0",
-                                 <&gpio_ir_pins>,"reg:0";
-                 gpio_pull = <&gpio_ir_pins>,"brcm,pull:0";              // pull-up/down state
-+                invert = <&gpio_ir>,"gpios:8";                          // 0 = active high input
-                 rc-map-name = <&gpio_ir>,"linux,rc-map-name";           // default rc map
-         };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch b/target/linux/bcm27xx/patches-5.4/950-0476-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
new file mode 100644 (file)
index 0000000..f549e73
--- /dev/null
@@ -0,0 +1,27 @@
+From e25d9a93812847b4ddc9e883d0cd45b32f8e2f76 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 17 Mar 2020 16:39:07 +0000
+Subject: [PATCH] ARM: dts: bcm2711: Add 32-bit PMU compatibility
+
+The "arm" architecture has no support for the cortex-a72 as such, but
+the performance and measurement unit from the cortex-a15 seems to be
+compatible.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
+@@ -12,6 +12,10 @@
+               sd_poll_once = <&emmc2>, "non-removable?";
+       };
++      arm-pmu {
++              compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu";
++      };
++
+       v3dbus {
+               compatible = "simple-bus";
+               #address-cells = <1>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0476-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch b/target/linux/bcm27xx/patches-5.4/950-0476-Add-support-for-merus-amp-soundcard-and-ma120x0p-cod.patch
deleted file mode 100644 (file)
index 8e66bcc..0000000
+++ /dev/null
@@ -1,1589 +0,0 @@
-From 76e0edf9676388c58bb5f0d7dda8eb8029926c6d Mon Sep 17 00:00:00 2001
-From: AMuszkat <ariel.muszkat@gmail.com>
-Date: Mon, 24 Feb 2020 22:56:59 +0100
-Subject: [PATCH] Add support for merus-amp soundcard and ma120x0p
- codec
-
-correct checkpatch warnings and errors
-
-Signed-off-by: AMuszkat <ariel.muszkat@gmail.com>
----
- arch/arm/boot/dts/overlays/Makefile           |    1 +
- arch/arm/boot/dts/overlays/README             |    6 +
- .../boot/dts/overlays/merus-amp-overlay.dts   |   60 +
- sound/soc/bcm/rpi-simple-soundcard.c          |   28 +
- sound/soc/codecs/Kconfig                      |    8 +
- sound/soc/codecs/Makefile                     |    2 +
- sound/soc/codecs/ma120x0p.c                   | 1384 +++++++++++++++++
- 7 files changed, 1489 insertions(+)
- create mode 100644 arch/arm/boot/dts/overlays/merus-amp-overlay.dts
- create mode 100644 sound/soc/codecs/ma120x0p.c
-
---- a/arch/arm/boot/dts/overlays/Makefile
-+++ b/arch/arm/boot/dts/overlays/Makefile
-@@ -103,6 +103,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
-       mcp3202.dtbo \
-       mcp342x.dtbo \
-       media-center.dtbo \
-+      merus-amp.dtbo \
-       midi-uart0.dtbo \
-       midi-uart1.dtbo \
-       miniuart-bt.dtbo \
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -1662,6 +1662,12 @@ Params: speed                   Display
-                                 (default "off")
-+Name:   merus-amp
-+Info:   Configures the merus-amp audio card
-+Load:   dtoverlay=merus-amp
-+Params: <None>
-+
-+
- Name:   midi-uart0
- Info:   Configures UART0 (ttyAMA0) so that a requested 38.4kbaud actually gets
-         31.25kbaud, the frequency required for MIDI
---- /dev/null
-+++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
-@@ -0,0 +1,60 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+// Definitions for Infineon Merus-Amp
-+/dts-v1/;
-+/plugin/;
-+#include <dt-bindings/pinctrl/bcm2835.h>
-+#include <dt-bindings/gpio/gpio.h>
-+
-+
-+/ {
-+      compatible = "brcm,bcm2835";
-+
-+      fragment@0 {
-+              target = <&i2s>;
-+              __overlay__ {
-+                      status = "okay";
-+              };
-+      };
-+
-+      fragment@1 {
-+              target = <&gpio>;
-+              __overlay__ {
-+                      merus_amp_pins: merus_amp_pins {
-+                              brcm,pins = <23>;
-+                              brcm,function = <0>; /* in */
-+                              brcm,pull = <2>; /* up */
-+                      };
-+              };
-+      };
-+
-+      fragment@2 {
-+              target = <&i2c1>;
-+              __overlay__ {
-+                      #address-cells = <1>;
-+                      #size-cells = <0>;
-+                      status = "okay";
-+
-+                      merus_amp: ma120x0p@20 {
-+                              #sound-dai-cells = <0>;
-+                              compatible = "ma,ma120x0p";
-+                              reg = <0x20>;
-+                              status = "okay";
-+                              pinctrl-names = "default";
-+                              pinctrl-0 = <&merus_amp_pins>;
-+                              enable_gp-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>;
-+                              mute_gp-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>;
-+                              booster_gp-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
-+                              error_gp-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>;
-+                      };
-+              };
-+      };
-+
-+      fragment@3 {
-+              target = <&sound>;
-+              __overlay__ {
-+                      compatible = "merus,merus-amp";
-+                      i2s-controller = <&i2s>;
-+                      status = "okay";
-+              };
-+      };
-+};
---- a/sound/soc/bcm/rpi-simple-soundcard.c
-+++ b/sound/soc/bcm/rpi-simple-soundcard.c
-@@ -16,6 +16,10 @@
-  * adau1977-adc.c
-  * by Andrey Grodzovsky <andrey2805@gmail.com>
-  *
-+ * merus-amp.c
-+ * by Ariel Muszkat <ariel.muszkat@gmail.com>
-+ *            Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
-+ *
-  * This program is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU General Public License
-  * version 2 as published by the Free Software Foundation.
-@@ -229,6 +233,28 @@ static struct snd_rpi_simple_drvdata drv
-       .fixed_bclk_ratio = 64,
- };
-+SND_SOC_DAILINK_DEFS(merus_amp,
-+      DAILINK_COMP_ARRAY(COMP_EMPTY()),
-+      DAILINK_COMP_ARRAY(COMP_CODEC("ma120x0p-amp", "ma120x0p.1-0020")),
-+      DAILINK_COMP_ARRAY(COMP_EMPTY()));
-+
-+static struct snd_soc_dai_link snd_merus_amp_dai[] = {
-+      {
-+              .name           = "MerusAmp",
-+              .stream_name    = "Merus Audio Amp",
-+              .dai_fmt        = SND_SOC_DAIFMT_I2S |
-+                                      SND_SOC_DAIFMT_NB_NF |
-+                                      SND_SOC_DAIFMT_CBS_CFS,
-+              SND_SOC_DAILINK_REG(merus_amp),
-+      },
-+};
-+
-+static struct snd_rpi_simple_drvdata drvdata_merus_amp = {
-+      .card_name        = "snd_rpi_merus_amp",
-+      .dai              = snd_merus_amp_dai,
-+      .fixed_bclk_ratio = 64,
-+};
-+
- static const struct of_device_id snd_rpi_simple_of_match[] = {
-       { .compatible = "adi,adau1977-adc",
-               .data = (void *) &drvdata_adau1977 },
-@@ -241,6 +267,8 @@ static const struct of_device_id snd_rpi
-       { .compatible = "hifiberry,hifiberry-dac",
-               .data = (void *) &drvdata_hifiberry_dac },
-       { .compatible = "rpi,rpi-dac", &drvdata_rpi_dac},
-+      { .compatible = "merus,merus-amp",
-+              .data = (void *) &drvdata_merus_amp },
-       {},
- };
---- a/sound/soc/codecs/Kconfig
-+++ b/sound/soc/codecs/Kconfig
-@@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS
-       select SND_SOC_LM4857 if I2C
-       select SND_SOC_LM49453 if I2C
-       select SND_SOC_LOCHNAGAR_SC if MFD_LOCHNAGAR
-+      select SND_SOC_MA120X0P if I2C
-       select SND_SOC_MAX98088 if I2C
-       select SND_SOC_MAX98090 if I2C
-       select SND_SOC_MAX98095 if I2C
-@@ -732,6 +733,13 @@ config SND_SOC_LOCHNAGAR_SC
-         This driver support the sound card functionality of the Cirrus
-         Logic Lochnagar audio development board.
-+config SND_SOC_MA120X0P
-+      tristate "Infineon Merus(TM) MA120X0P Multilevel Class-D Audio amplifiers"
-+      depends on I2C
-+      help
-+              Enable support for Infineon MA120X0P Multilevel Class-D audio power
-+              amplifiers.
-+
- config SND_SOC_MADERA
-       tristate
-       default y if SND_SOC_CS47L15=y
---- a/sound/soc/codecs/Makefile
-+++ b/sound/soc/codecs/Makefile
-@@ -99,6 +99,7 @@ snd-soc-l3-objs := l3.o
- snd-soc-lm4857-objs := lm4857.o
- snd-soc-lm49453-objs := lm49453.o
- snd-soc-lochnagar-sc-objs := lochnagar-sc.o
-+snd-soc-ma120x0p-objs := ma120x0p.o
- snd-soc-madera-objs := madera.o
- snd-soc-max9759-objs := max9759.o
- snd-soc-max9768-objs := max9768.o
-@@ -386,6 +387,7 @@ obj-$(CONFIG_SND_SOC_L3)   += snd-soc-l3.o
- obj-$(CONFIG_SND_SOC_LM4857)  += snd-soc-lm4857.o
- obj-$(CONFIG_SND_SOC_LM49453)   += snd-soc-lm49453.o
- obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC)    += snd-soc-lochnagar-sc.o
-+obj-$(CONFIG_SND_SOC_MA120X0P)   += snd-soc-ma120x0p.o
- obj-$(CONFIG_SND_SOC_MADERA)  += snd-soc-madera.o
- obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
- obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
---- /dev/null
-+++ b/sound/soc/codecs/ma120x0p.c
-@@ -0,0 +1,1384 @@
-+// SPDX-License-Identifier: GPL-2.0-or-later
-+/*
-+ * ASoC Driver for Infineon Merus(TM) ma120x0p multi-level class-D amplifier
-+ *
-+ * Authors:   Ariel Muszkat <ariel.muszkat@gmail.com>
-+ * Jorgen Kragh Jakobsen <jorgen.kraghjakobsen@infineon.com>
-+ *
-+ * Copyright (C) 2019 Infineon Technologies AG
-+ *
-+ */
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/i2c.h>
-+#include <linux/of_device.h>
-+#include <linux/spi/spi.h>
-+#include <linux/regmap.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/slab.h>
-+#include <linux/gpio/consumer.h>
-+#include <linux/gpio.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+#include <sound/tlv.h>
-+#include <linux/interrupt.h>
-+
-+#include <linux/kernel.h>
-+#include <linux/string.h>
-+#include <linux/fs.h>
-+#include <linux/uaccess.h>
-+
-+#ifndef _MA120X0P_
-+#define _MA120X0P_
-+//------------------------------------------------------------------manualPM---
-+// Select Manual PowerMode control
-+#define ma_manualpm__a 0
-+#define ma_manualpm__len 1
-+#define ma_manualpm__mask 0x40
-+#define ma_manualpm__shift 0x06
-+#define ma_manualpm__reset 0x00
-+//--------------------------------------------------------------------pm_man---
-+// manual selected power mode
-+#define ma_pm_man__a 0
-+#define ma_pm_man__len 2
-+#define ma_pm_man__mask 0x30
-+#define ma_pm_man__shift 0x04
-+#define ma_pm_man__reset 0x03
-+//------------------------------------------ ----------------------mthr_1to2---
-+// mod. index threshold value for pm1=>pm2 change.
-+#define ma_mthr_1to2__a 1
-+#define ma_mthr_1to2__len 8
-+#define ma_mthr_1to2__mask 0xff
-+#define ma_mthr_1to2__shift 0x00
-+#define ma_mthr_1to2__reset 0x3c
-+//-----------------------------------------------------------------mthr_2to1---
-+// mod. index threshold value for pm2=>pm1 change.
-+#define ma_mthr_2to1__a 2
-+#define ma_mthr_2to1__len 8
-+#define ma_mthr_2to1__mask 0xff
-+#define ma_mthr_2to1__shift 0x00
-+#define ma_mthr_2to1__reset 0x32
-+//-----------------------------------------------------------------mthr_2to3---
-+// mod. index threshold value for pm2=>pm3 change.
-+#define ma_mthr_2to3__a 3
-+#define ma_mthr_2to3__len 8
-+#define ma_mthr_2to3__mask 0xff
-+#define ma_mthr_2to3__shift 0x00
-+#define ma_mthr_2to3__reset 0x5a
-+//-----------------------------------------------------------------mthr_3to2---
-+// mod. index threshold value for pm3=>pm2 change.
-+#define ma_mthr_3to2__a 4
-+#define ma_mthr_3to2__len 8
-+#define ma_mthr_3to2__mask 0xff
-+#define ma_mthr_3to2__shift 0x00
-+#define ma_mthr_3to2__reset 0x50
-+//-------------------------------------------------------------pwmclkdiv_nom---
-+// pwm default clock divider value
-+#define ma_pwmclkdiv_nom__a 8
-+#define ma_pwmclkdiv_nom__len 8
-+#define ma_pwmclkdiv_nom__mask 0xff
-+#define ma_pwmclkdiv_nom__shift 0x00
-+#define ma_pwmclkdiv_nom__reset 0x26
-+//--------- ----------------------------------------------------ocp_latch_en---
-+// high to use permanently latching level-2 ocp
-+#define ma_ocp_latch_en__a 10
-+#define ma_ocp_latch_en__len 1
-+#define ma_ocp_latch_en__mask 0x02
-+#define ma_ocp_latch_en__shift 0x01
-+#define ma_ocp_latch_en__reset 0x00
-+//---------------------------------------------------------------lf_clamp_en---
-+// high (default) to enable lf int2+3 clamping on clip
-+#define ma_lf_clamp_en__a 10
-+#define ma_lf_clamp_en__len 1
-+#define ma_lf_clamp_en__mask 0x80
-+#define ma_lf_clamp_en__shift 0x07
-+#define ma_lf_clamp_en__reset 0x00
-+//-------------------------------------------------------pmcfg_btl_b.modtype---
-+//
-+#define ma_pmcfg_btl_b__modtype__a 18
-+#define ma_pmcfg_btl_b__modtype__len 2
-+#define ma_pmcfg_btl_b__modtype__mask 0x18
-+#define ma_pmcfg_btl_b__modtype__shift 0x03
-+#define ma_pmcfg_btl_b__modtype__reset 0x02
-+//-------------------------------------------------------pmcfg_btl_b.freqdiv---
-+#define ma_pmcfg_btl_b__freqdiv__a 18
-+#define ma_pmcfg_btl_b__freqdiv__len 2
-+#define ma_pmcfg_btl_b__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_b__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_b__freqdiv__reset 0x01
-+//----------------------------------------------------pmcfg_btl_b.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_b__lf_gain_ol__a 18
-+#define ma_pmcfg_btl_b__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_b__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_b__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_b__lf_gain_ol__reset 0x01
-+//-------------------------------------------------------pmcfg_btl_c.freqdiv---
-+//
-+#define ma_pmcfg_btl_c__freqdiv__a 19
-+#define ma_pmcfg_btl_c__freqdiv__len 2
-+#define ma_pmcfg_btl_c__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_c__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_c__freqdiv__reset 0x01
-+//-------------------------------------------------------pmcfg_btl_c.modtype---
-+//
-+#define ma_pmcfg_btl_c__modtype__a 19
-+#define ma_pmcfg_btl_c__modtype__len 2
-+#define ma_pmcfg_btl_c__modtype__mask 0x18
-+#define ma_pmcfg_btl_c__modtype__shift 0x03
-+#define ma_pmcfg_btl_c__modtype__reset 0x01
-+//----------------------------------------------------pmcfg_btl_c.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_c__lf_gain_ol__a 19
-+#define ma_pmcfg_btl_c__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_c__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_c__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_c__lf_gain_ol__reset 0x00
-+//-------------------------------------------------------pmcfg_btl_d.modtype---
-+//
-+#define ma_pmcfg_btl_d__modtype__a 20
-+#define ma_pmcfg_btl_d__modtype__len 2
-+#define ma_pmcfg_btl_d__modtype__mask 0x18
-+#define ma_pmcfg_btl_d__modtype__shift 0x03
-+#define ma_pmcfg_btl_d__modtype__reset 0x02
-+//-------------------------------------------------------pmcfg_btl_d.freqdiv---
-+//
-+#define ma_pmcfg_btl_d__freqdiv__a 20
-+#define ma_pmcfg_btl_d__freqdiv__len 2
-+#define ma_pmcfg_btl_d__freqdiv__mask 0x06
-+#define ma_pmcfg_btl_d__freqdiv__shift 0x01
-+#define ma_pmcfg_btl_d__freqdiv__reset 0x02
-+//----------------------------------------------------pmcfg_btl_d.lf_gain_ol---
-+//
-+#define ma_pmcfg_btl_d__lf_gain_ol__a 20
-+#define ma_pmcfg_btl_d__lf_gain_ol__len 1
-+#define ma_pmcfg_btl_d__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_btl_d__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_btl_d__lf_gain_ol__reset 0x00
-+//------------ -------------------------------------------pmcfg_se_a.modtype---
-+//
-+#define ma_pmcfg_se_a__modtype__a 21
-+#define ma_pmcfg_se_a__modtype__len 2
-+#define ma_pmcfg_se_a__modtype__mask 0x18
-+#define ma_pmcfg_se_a__modtype__shift 0x03
-+#define ma_pmcfg_se_a__modtype__reset 0x01
-+//--------------------------------------------------------pmcfg_se_a.freqdiv---
-+//
-+#define ma_pmcfg_se_a__freqdiv__a 21
-+#define ma_pmcfg_se_a__freqdiv__len 2
-+#define ma_pmcfg_se_a__freqdiv__mask 0x06
-+#define ma_pmcfg_se_a__freqdiv__shift 0x01
-+#define ma_pmcfg_se_a__freqdiv__reset 0x00
-+//-----------------------------------------------------pmcfg_se_a.lf_gain_ol---
-+//
-+#define ma_pmcfg_se_a__lf_gain_ol__a 21
-+#define ma_pmcfg_se_a__lf_gain_ol__len 1
-+#define ma_pmcfg_se_a__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_se_a__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_se_a__lf_gain_ol__reset 0x01
-+//-----------------------------------------------------pmcfg_se_b.lf_gain_ol---
-+//
-+#define ma_pmcfg_se_b__lf_gain_ol__a 22
-+#define ma_pmcfg_se_b__lf_gain_ol__len 1
-+#define ma_pmcfg_se_b__lf_gain_ol__mask 0x01
-+#define ma_pmcfg_se_b__lf_gain_ol__shift 0x00
-+#define ma_pmcfg_se_b__lf_gain_ol__reset 0x00
-+//--------------------------------------------------------pmcfg_se_b.freqdiv---
-+//
-+#define ma_pmcfg_se_b__freqdiv__a 22
-+#define ma_pmcfg_se_b__freqdiv__len 2
-+#define ma_pmcfg_se_b__freqdiv__mask 0x06
-+#define ma_pmcfg_se_b__freqdiv__shift 0x01
-+#define ma_pmcfg_se_b__freqdiv__reset 0x01
-+//--------------------------------------------------------pmcfg_se_b.modtype---
-+//
-+#define ma_pmcfg_se_b__modtype__a 22
-+#define ma_pmcfg_se_b__modtype__len 2
-+#define ma_pmcfg_se_b__modtype__mask 0x18
-+#define ma_pmcfg_se_b__modtype__shift 0x03
-+#define ma_pmcfg_se_b__modtype__reset 0x01
-+//----------------------------------------------------------balwaitcount_pm1---
-+// pm1 balancing period.
-+#define ma_balwaitcount_pm1__a 23
-+#define ma_balwaitcount_pm1__len 8
-+#define ma_balwaitcount_pm1__mask 0xff
-+#define ma_balwaitcount_pm1__shift 0x00
-+#define ma_balwaitcount_pm1__reset 0x14
-+//----------------------------------------------------------balwaitcount_pm2---
-+// pm2 balancing period.
-+#define ma_balwaitcount_pm2__a 24
-+#define ma_balwaitcount_pm2__len 8
-+#define ma_balwaitcount_pm2__mask 0xff
-+#define ma_balwaitcount_pm2__shift 0x00
-+#define ma_balwaitcount_pm2__reset 0x14
-+//----------------------------------------------------------balwaitcount_pm3---
-+// pm3 balancing period.
-+#define ma_balwaitcount_pm3__a 25
-+#define ma_balwaitcount_pm3__len 8
-+#define ma_balwaitcount_pm3__mask 0xff
-+#define ma_balwaitcount_pm3__shift 0x00
-+#define ma_balwaitcount_pm3__reset 0x1a
-+//-------------------------------------------------------------usespread_pm1---
-+// pm1 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm1__a 26
-+#define ma_usespread_pm1__len 1
-+#define ma_usespread_pm1__mask 0x40
-+#define ma_usespread_pm1__shift 0x06
-+#define ma_usespread_pm1__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm1---
-+// pm1 dead time setting [10ns steps].
-+#define ma_dtsteps_pm1__a 26
-+#define ma_dtsteps_pm1__len 3
-+#define ma_dtsteps_pm1__mask 0x38
-+#define ma_dtsteps_pm1__shift 0x03
-+#define ma_dtsteps_pm1__reset 0x04
-+//---------------------------------------------------------------baltype_pm1---
-+// pm1 balancing sensor scheme.
-+#define ma_baltype_pm1__a 26
-+#define ma_baltype_pm1__len 3
-+#define ma_baltype_pm1__mask 0x07
-+#define ma_baltype_pm1__shift 0x00
-+#define ma_baltype_pm1__reset 0x00
-+//-------------------------------------------------------------usespread_pm2---
-+// pm2 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm2__a 27
-+#define ma_usespread_pm2__len 1
-+#define ma_usespread_pm2__mask 0x40
-+#define ma_usespread_pm2__shift 0x06
-+#define ma_usespread_pm2__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm2---
-+// pm2 dead time setting [10ns steps].
-+#define ma_dtsteps_pm2__a 27
-+#define ma_dtsteps_pm2__len 3
-+#define ma_dtsteps_pm2__mask 0x38
-+#define ma_dtsteps_pm2__shift 0x03
-+#define ma_dtsteps_pm2__reset 0x03
-+//---------------------------------------------------------------baltype_pm2---
-+// pm2 balancing sensor scheme.
-+#define ma_baltype_pm2__a 27
-+#define ma_baltype_pm2__len 3
-+#define ma_baltype_pm2__mask 0x07
-+#define ma_baltype_pm2__shift 0x00
-+#define ma_baltype_pm2__reset 0x01
-+//-------------------------------------------------------------usespread_pm3---
-+// pm3 pwm spread-spectrum mode on/off.
-+#define ma_usespread_pm3__a 28
-+#define ma_usespread_pm3__len 1
-+#define ma_usespread_pm3__mask 0x40
-+#define ma_usespread_pm3__shift 0x06
-+#define ma_usespread_pm3__reset 0x00
-+//---------------------------------------------------------------dtsteps_pm3---
-+// pm3 dead time setting [10ns steps].
-+#define ma_dtsteps_pm3__a 28
-+#define ma_dtsteps_pm3__len 3
-+#define ma_dtsteps_pm3__mask 0x38
-+#define ma_dtsteps_pm3__shift 0x03
-+#define ma_dtsteps_pm3__reset 0x01
-+//---------------------------------------------------------------baltype_pm3---
-+// pm3 balancing sensor scheme.
-+#define ma_baltype_pm3__a 28
-+#define ma_baltype_pm3__len 3
-+#define ma_baltype_pm3__mask 0x07
-+#define ma_baltype_pm3__shift 0x00
-+#define ma_baltype_pm3__reset 0x03
-+//-----------------------------------------------------------------pmprofile---
-+// pm profile select. valid presets: 0-1-2-3-4. 5=> custom profile.
-+#define ma_pmprofile__a 29
-+#define ma_pmprofile__len 3
-+#define ma_pmprofile__mask 0x07
-+#define ma_pmprofile__shift 0x00
-+#define ma_pmprofile__reset 0x00
-+//-------------------------------------------------------------------pm3_man---
-+// custom profile pm3 contents. 0=>a,  1=>b,  2=>c,  3=>d
-+#define ma_pm3_man__a 30
-+#define ma_pm3_man__len 2
-+#define ma_pm3_man__mask 0x30
-+#define ma_pm3_man__shift 0x04
-+#define ma_pm3_man__reset 0x02
-+//-------------------------------------------------------------------pm2_man---
-+// custom profile pm2 contents. 0=>a,  1=>b,  2=>c,  3=>d
-+#define ma_pm2_man__a 30
-+#define ma_pm2_man__len 2
-+#define ma_pm2_man__mask 0x0c
-+#define ma_pm2_man__shift 0x02
-+#define ma_pm2_man__reset 0x03
-+//-------------------------------------------------------------------pm1_man---
-+// custom profile pm1 contents. 0=>a,  1=>b,  2=>c,  3=>d
-+#define ma_pm1_man__a 30
-+#define ma_pm1_man__len 2
-+#define ma_pm1_man__mask 0x03
-+#define ma_pm1_man__shift 0x00
-+#define ma_pm1_man__reset 0x03
-+//-----------------------------------------------------------ocp_latch_clear---
-+// low-high clears current ocp latched condition.
-+#define ma_ocp_latch_clear__a 32
-+#define ma_ocp_latch_clear__len 1
-+#define ma_ocp_latch_clear__mask 0x80
-+#define ma_ocp_latch_clear__shift 0x07
-+#define ma_ocp_latch_clear__reset 0x00
-+//-------------------------------------------------------------audio_in_mode---
-+// audio input mode; 0-1-2-3-4-5
-+#define ma_audio_in_mode__a 37
-+#define ma_audio_in_mode__len 3
-+#define ma_audio_in_mode__mask 0xe0
-+#define ma_audio_in_mode__shift 0x05
-+#define ma_audio_in_mode__reset 0x00
-+//-----------------------------------------------------------------eh_dcshdn---
-+// high to enable dc protection
-+#define ma_eh_dcshdn__a 38
-+#define ma_eh_dcshdn__len 1
-+#define ma_eh_dcshdn__mask 0x04
-+#define ma_eh_dcshdn__shift 0x02
-+#define ma_eh_dcshdn__reset 0x01
-+//---------------------------------------------------------audio_in_mode_ext---
-+// if set,  audio_in_mode is controlled from audio_in_mode register. if not set
-+//audio_in_mode is set from fuse bank setting
-+#define ma_audio_in_mode_ext__a 39
-+#define ma_audio_in_mode_ext__len 1
-+#define ma_audio_in_mode_ext__mask 0x20
-+#define ma_audio_in_mode_ext__shift 0x05
-+#define ma_audio_in_mode_ext__reset 0x00
-+//------------------------------------------------------------------eh_clear---
-+// flip to clear error registers
-+#define ma_eh_clear__a 45
-+#define ma_eh_clear__len 1
-+#define ma_eh_clear__mask 0x04
-+#define ma_eh_clear__shift 0x02
-+#define ma_eh_clear__reset 0x00
-+//----------------------------------------------------------thermal_compr_en---
-+// enable otw-contr.  input compression?
-+#define ma_thermal_compr_en__a 45
-+#define ma_thermal_compr_en__len 1
-+#define ma_thermal_compr_en__mask 0x20
-+#define ma_thermal_compr_en__shift 0x05
-+#define ma_thermal_compr_en__reset 0x01
-+//---------------------------------------------------------------system_mute---
-+// 1 = mute system,  0 = normal operation
-+#define ma_system_mute__a 45
-+#define ma_system_mute__len 1
-+#define ma_system_mute__mask 0x40
-+#define ma_system_mute__shift 0x06
-+#define ma_system_mute__reset 0x00
-+//------------------------------------------------------thermal_compr_max_db---
-+// audio limiter max thermal reduction
-+#define ma_thermal_compr_max_db__a 46
-+#define ma_thermal_compr_max_db__len 3
-+#define ma_thermal_compr_max_db__mask 0x07
-+#define ma_thermal_compr_max_db__shift 0x00
-+#define ma_thermal_compr_max_db__reset 0x04
-+//---------------------------------------------------------audio_proc_enable---
-+// enable audio proc,  bypass if not enabled
-+#define ma_audio_proc_enable__a 53
-+#define ma_audio_proc_enable__len 1
-+#define ma_audio_proc_enable__mask 0x08
-+#define ma_audio_proc_enable__shift 0x03
-+#define ma_audio_proc_enable__reset 0x00
-+//--------------------------------------------------------audio_proc_release---
-+// 00:slow,  01:normal,  10:fast
-+#define ma_audio_proc_release__a 53
-+#define ma_audio_proc_release__len 2
-+#define ma_audio_proc_release__mask 0x30
-+#define ma_audio_proc_release__shift 0x04
-+#define ma_audio_proc_release__reset 0x00
-+//---------------------------------------------------------audio_proc_attack---
-+// 00:slow,  01:normal,  10:fast
-+#define ma_audio_proc_attack__a 53
-+#define ma_audio_proc_attack__len 2
-+#define ma_audio_proc_attack__mask 0xc0
-+#define ma_audio_proc_attack__shift 0x06
-+#define ma_audio_proc_attack__reset 0x00
-+//----------------------------------------------------------------i2s_format---
-+// i2s basic data format,  000 = std. i2s,  001 = left justified (default)
-+#define ma_i2s_format__a 53
-+#define ma_i2s_format__len 3
-+#define ma_i2s_format__mask 0x07
-+#define ma_i2s_format__shift 0x00
-+#define ma_i2s_format__reset 0x01
-+//--------------------------------------------------audio_proc_limiterenable---
-+// 1: enable limiter,  0: disable limiter
-+#define ma_audio_proc_limiterenable__a 54
-+#define ma_audio_proc_limiterenable__len 1
-+#define ma_audio_proc_limiterenable__mask 0x40
-+#define ma_audio_proc_limiterenable__shift 0x06
-+#define ma_audio_proc_limiterenable__reset 0x00
-+//-----------------------------------------------------------audio_proc_mute---
-+// 1: mute,  0: unmute
-+#define ma_audio_proc_mute__a 54
-+#define ma_audio_proc_mute__len 1
-+#define ma_audio_proc_mute__mask 0x80
-+#define ma_audio_proc_mute__shift 0x07
-+#define ma_audio_proc_mute__reset 0x00
-+//---------------------------------------------------------------i2s_sck_pol---
-+// i2s sck polarity cfg. 0 = rising edge data change
-+#define ma_i2s_sck_pol__a 54
-+#define ma_i2s_sck_pol__len 1
-+#define ma_i2s_sck_pol__mask 0x01
-+#define ma_i2s_sck_pol__shift 0x00
-+#define ma_i2s_sck_pol__reset 0x01
-+//-------------------------------------------------------------i2s_framesize---
-+// i2s word length. 00 = 32bit,  01 = 24bit
-+#define ma_i2s_framesize__a 54
-+#define ma_i2s_framesize__len 2
-+#define ma_i2s_framesize__mask 0x18
-+#define ma_i2s_framesize__shift 0x03
-+#define ma_i2s_framesize__reset 0x00
-+//----------------------------------------------------------------i2s_ws_pol---
-+// i2s ws polarity. 0 = low first
-+#define ma_i2s_ws_pol__a 54
-+#define ma_i2s_ws_pol__len 1
-+#define ma_i2s_ws_pol__mask 0x02
-+#define ma_i2s_ws_pol__shift 0x01
-+#define ma_i2s_ws_pol__reset 0x00
-+//-----------------------------------------------------------------i2s_order---
-+// i2s word bit order. 0 = msb first
-+#define ma_i2s_order__a 54
-+#define ma_i2s_order__len 1
-+#define ma_i2s_order__mask 0x04
-+#define ma_i2s_order__shift 0x02
-+#define ma_i2s_order__reset 0x00
-+//------------------------------------------------------------i2s_rightfirst---
-+// i2s l/r word order; 0 = left first
-+#define ma_i2s_rightfirst__a 54
-+#define ma_i2s_rightfirst__len 1
-+#define ma_i2s_rightfirst__mask 0x20
-+#define ma_i2s_rightfirst__shift 0x05
-+#define ma_i2s_rightfirst__reset 0x00
-+//-------------------------------------------------------------vol_db_master---
-+// master volume db
-+#define ma_vol_db_master__a 64
-+#define ma_vol_db_master__len 8
-+#define ma_vol_db_master__mask 0xff
-+#define ma_vol_db_master__shift 0x00
-+#define ma_vol_db_master__reset 0x18
-+//------------------------------------------------------------vol_lsb_master---
-+// master volume lsb 1/4 steps
-+#define ma_vol_lsb_master__a 65
-+#define ma_vol_lsb_master__len 2
-+#define ma_vol_lsb_master__mask 0x03
-+#define ma_vol_lsb_master__shift 0x00
-+#define ma_vol_lsb_master__reset 0x00
-+//----------------------------------------------------------------vol_db_ch0---
-+// volume channel 0
-+#define ma_vol_db_ch0__a 66
-+#define ma_vol_db_ch0__len 8
-+#define ma_vol_db_ch0__mask 0xff
-+#define ma_vol_db_ch0__shift 0x00
-+#define ma_vol_db_ch0__reset 0x18
-+//----------------------------------------------------------------vol_db_ch1---
-+// volume channel 1
-+#define ma_vol_db_ch1__a 67
-+#define ma_vol_db_ch1__len 8
-+#define ma_vol_db_ch1__mask 0xff
-+#define ma_vol_db_ch1__shift 0x00
-+#define ma_vol_db_ch1__reset 0x18
-+//----------------------------------------------------------------vol_db_ch2---
-+// volume channel 2
-+#define ma_vol_db_ch2__a 68
-+#define ma_vol_db_ch2__len 8
-+#define ma_vol_db_ch2__mask 0xff
-+#define ma_vol_db_ch2__shift 0x00
-+#define ma_vol_db_ch2__reset 0x18
-+//----------------------------------------------------------------vol_db_ch3---
-+// volume channel 3
-+#define ma_vol_db_ch3__a 69
-+#define ma_vol_db_ch3__len 8
-+#define ma_vol_db_ch3__mask 0xff
-+#define ma_vol_db_ch3__shift 0x00
-+#define ma_vol_db_ch3__reset 0x18
-+//---------------------------------------------------------------vol_lsb_ch0---
-+// volume channel 1 - 1/4 steps
-+#define ma_vol_lsb_ch0__a 70
-+#define ma_vol_lsb_ch0__len 2
-+#define ma_vol_lsb_ch0__mask 0x03
-+#define ma_vol_lsb_ch0__shift 0x00
-+#define ma_vol_lsb_ch0__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch1---
-+// volume channel 3 - 1/4 steps
-+#define ma_vol_lsb_ch1__a 70
-+#define ma_vol_lsb_ch1__len 2
-+#define ma_vol_lsb_ch1__mask 0x0c
-+#define ma_vol_lsb_ch1__shift 0x02
-+#define ma_vol_lsb_ch1__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch2---
-+// volume channel 2 - 1/4 steps
-+#define ma_vol_lsb_ch2__a 70
-+#define ma_vol_lsb_ch2__len 2
-+#define ma_vol_lsb_ch2__mask 0x30
-+#define ma_vol_lsb_ch2__shift 0x04
-+#define ma_vol_lsb_ch2__reset 0x00
-+//---------------------------------------------------------------vol_lsb_ch3---
-+// volume channel 3 - 1/4 steps
-+#define ma_vol_lsb_ch3__a 70
-+#define ma_vol_lsb_ch3__len 2
-+#define ma_vol_lsb_ch3__mask 0xc0
-+#define ma_vol_lsb_ch3__shift 0x06
-+#define ma_vol_lsb_ch3__reset 0x00
-+//----------------------------------------------------------------thr_db_ch0---
-+// thr_db channel 0
-+#define ma_thr_db_ch0__a 71
-+#define ma_thr_db_ch0__len 8
-+#define ma_thr_db_ch0__mask 0xff
-+#define ma_thr_db_ch0__shift 0x00
-+#define ma_thr_db_ch0__reset 0x18
-+//----------------------------------------------------------------thr_db_ch1---
-+// thr db ch1
-+#define ma_thr_db_ch1__a 72
-+#define ma_thr_db_ch1__len 8
-+#define ma_thr_db_ch1__mask 0xff
-+#define ma_thr_db_ch1__shift 0x00
-+#define ma_thr_db_ch1__reset 0x18
-+//----------------------------------------------------------------thr_db_ch2---
-+// thr db ch2
-+#define ma_thr_db_ch2__a 73
-+#define ma_thr_db_ch2__len 8
-+#define ma_thr_db_ch2__mask 0xff
-+#define ma_thr_db_ch2__shift 0x00
-+#define ma_thr_db_ch2__reset 0x18
-+//----------------------------------------------------------------thr_db_ch3---
-+// threshold db ch3
-+#define ma_thr_db_ch3__a 74
-+#define ma_thr_db_ch3__len 8
-+#define ma_thr_db_ch3__mask 0xff
-+#define ma_thr_db_ch3__shift 0x00
-+#define ma_thr_db_ch3__reset 0x18
-+//---------------------------------------------------------------thr_lsb_ch0---
-+// thr lsb ch0
-+#define ma_thr_lsb_ch0__a 75
-+#define ma_thr_lsb_ch0__len 2
-+#define ma_thr_lsb_ch0__mask 0x03
-+#define ma_thr_lsb_ch0__shift 0x00
-+#define ma_thr_lsb_ch0__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch1---
-+// thr lsb ch1
-+#define ma_thr_lsb_ch1__a 75
-+#define ma_thr_lsb_ch1__len 2
-+#define ma_thr_lsb_ch1__mask 0x0c
-+#define ma_thr_lsb_ch1__shift 0x02
-+#define ma_thr_lsb_ch1__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch2---
-+// thr lsb ch2 1/4 db step
-+#define ma_thr_lsb_ch2__a 75
-+#define ma_thr_lsb_ch2__len 2
-+#define ma_thr_lsb_ch2__mask 0x30
-+#define ma_thr_lsb_ch2__shift 0x04
-+#define ma_thr_lsb_ch2__reset 0x00
-+//---------------------------------------------------------------thr_lsb_ch3---
-+// threshold lsb ch3
-+#define ma_thr_lsb_ch3__a 75
-+#define ma_thr_lsb_ch3__len 2
-+#define ma_thr_lsb_ch3__mask 0xc0
-+#define ma_thr_lsb_ch3__shift 0x06
-+#define ma_thr_lsb_ch3__reset 0x00
-+//-----------------------------------------------------------dcu_mon0.pm_mon---
-+// power mode monitor channel 0
-+#define ma_dcu_mon0__pm_mon__a 96
-+#define ma_dcu_mon0__pm_mon__len 2
-+#define ma_dcu_mon0__pm_mon__mask 0x03
-+#define ma_dcu_mon0__pm_mon__shift 0x00
-+#define ma_dcu_mon0__pm_mon__reset 0x00
-+//-----------------------------------------------------dcu_mon0.freqmode_mon---
-+// frequence mode monitor channel 0
-+#define ma_dcu_mon0__freqmode_mon__a 96
-+#define ma_dcu_mon0__freqmode_mon__len 3
-+#define ma_dcu_mon0__freqmode_mon__mask 0x70
-+#define ma_dcu_mon0__freqmode_mon__shift 0x04
-+#define ma_dcu_mon0__freqmode_mon__reset 0x00
-+//-------------------------------------------------------dcu_mon0.pps_passed---
-+// dcu0 pps completion indicator
-+#define ma_dcu_mon0__pps_passed__a 96
-+#define ma_dcu_mon0__pps_passed__len 1
-+#define ma_dcu_mon0__pps_passed__mask 0x80
-+#define ma_dcu_mon0__pps_passed__shift 0x07
-+#define ma_dcu_mon0__pps_passed__reset 0x00
-+//----------------------------------------------------------dcu_mon0.ocp_mon---
-+// ocp monitor channel 0
-+#define ma_dcu_mon0__ocp_mon__a 97
-+#define ma_dcu_mon0__ocp_mon__len 1
-+#define ma_dcu_mon0__ocp_mon__mask 0x01
-+#define ma_dcu_mon0__ocp_mon__shift 0x00
-+#define ma_dcu_mon0__ocp_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon0.vcfly1_ok---
-+// cfly1 protection monitor channel 0.
-+#define ma_dcu_mon0__vcfly1_ok__a 97
-+#define ma_dcu_mon0__vcfly1_ok__len 1
-+#define ma_dcu_mon0__vcfly1_ok__mask 0x02
-+#define ma_dcu_mon0__vcfly1_ok__shift 0x01
-+#define ma_dcu_mon0__vcfly1_ok__reset 0x00
-+//--------------------------------------------------------dcu_mon0.vcfly2_ok---
-+// cfly2 protection monitor channel 0.
-+#define ma_dcu_mon0__vcfly2_ok__a 97
-+#define ma_dcu_mon0__vcfly2_ok__len 1
-+#define ma_dcu_mon0__vcfly2_ok__mask 0x04
-+#define ma_dcu_mon0__vcfly2_ok__shift 0x02
-+#define ma_dcu_mon0__vcfly2_ok__reset 0x00
-+//----------------------------------------------------------dcu_mon0.pvdd_ok---
-+// dcu0 pvdd monitor
-+#define ma_dcu_mon0__pvdd_ok__a 97
-+#define ma_dcu_mon0__pvdd_ok__len 1
-+#define ma_dcu_mon0__pvdd_ok__mask 0x08
-+#define ma_dcu_mon0__pvdd_ok__shift 0x03
-+#define ma_dcu_mon0__pvdd_ok__reset 0x00
-+//-----------------------------------------------------------dcu_mon0.vdd_ok---
-+// dcu0 vdd monitor
-+#define ma_dcu_mon0__vdd_ok__a 97
-+#define ma_dcu_mon0__vdd_ok__len 1
-+#define ma_dcu_mon0__vdd_ok__mask 0x10
-+#define ma_dcu_mon0__vdd_ok__shift 0x04
-+#define ma_dcu_mon0__vdd_ok__reset 0x00
-+//-------------------------------------------------------------dcu_mon0.mute---
-+// dcu0 mute monitor
-+#define ma_dcu_mon0__mute__a 97
-+#define ma_dcu_mon0__mute__len 1
-+#define ma_dcu_mon0__mute__mask 0x20
-+#define ma_dcu_mon0__mute__shift 0x05
-+#define ma_dcu_mon0__mute__reset 0x00
-+//------------------------------------------------------------dcu_mon0.m_mon---
-+// m sense monitor channel 0
-+#define ma_dcu_mon0__m_mon__a 98
-+#define ma_dcu_mon0__m_mon__len 8
-+#define ma_dcu_mon0__m_mon__mask 0xff
-+#define ma_dcu_mon0__m_mon__shift 0x00
-+#define ma_dcu_mon0__m_mon__reset 0x00
-+//-----------------------------------------------------------dcu_mon1.pm_mon---
-+// power mode monitor channel 1
-+#define ma_dcu_mon1__pm_mon__a 100
-+#define ma_dcu_mon1__pm_mon__len 2
-+#define ma_dcu_mon1__pm_mon__mask 0x03
-+#define ma_dcu_mon1__pm_mon__shift 0x00
-+#define ma_dcu_mon1__pm_mon__reset 0x00
-+//-----------------------------------------------------dcu_mon1.freqmode_mon---
-+// frequence mode monitor channel 1
-+#define ma_dcu_mon1__freqmode_mon__a 100
-+#define ma_dcu_mon1__freqmode_mon__len 3
-+#define ma_dcu_mon1__freqmode_mon__mask 0x70
-+#define ma_dcu_mon1__freqmode_mon__shift 0x04
-+#define ma_dcu_mon1__freqmode_mon__reset 0x00
-+//-------------------------------------------------------dcu_mon1.pps_passed---
-+// dcu1 pps completion indicator
-+#define ma_dcu_mon1__pps_passed__a 100
-+#define ma_dcu_mon1__pps_passed__len 1
-+#define ma_dcu_mon1__pps_passed__mask 0x80
-+#define ma_dcu_mon1__pps_passed__shift 0x07
-+#define ma_dcu_mon1__pps_passed__reset 0x00
-+//----------------------------------------------------------dcu_mon1.ocp_mon---
-+// ocp monitor channel 1
-+#define ma_dcu_mon1__ocp_mon__a 101
-+#define ma_dcu_mon1__ocp_mon__len 1
-+#define ma_dcu_mon1__ocp_mon__mask 0x01
-+#define ma_dcu_mon1__ocp_mon__shift 0x00
-+#define ma_dcu_mon1__ocp_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon1.vcfly1_ok---
-+// cfly1 protcetion monitor channel 1
-+#define ma_dcu_mon1__vcfly1_ok__a 101
-+#define ma_dcu_mon1__vcfly1_ok__len 1
-+#define ma_dcu_mon1__vcfly1_ok__mask 0x02
-+#define ma_dcu_mon1__vcfly1_ok__shift 0x01
-+#define ma_dcu_mon1__vcfly1_ok__reset 0x00
-+//--------------------------------------------------------dcu_mon1.vcfly2_ok---
-+// cfly2 protection monitor channel 1
-+#define ma_dcu_mon1__vcfly2_ok__a 101
-+#define ma_dcu_mon1__vcfly2_ok__len 1
-+#define ma_dcu_mon1__vcfly2_ok__mask 0x04
-+#define ma_dcu_mon1__vcfly2_ok__shift 0x02
-+#define ma_dcu_mon1__vcfly2_ok__reset 0x00
-+//----------------------------------------------------------dcu_mon1.pvdd_ok---
-+// dcu1 pvdd monitor
-+#define ma_dcu_mon1__pvdd_ok__a 101
-+#define ma_dcu_mon1__pvdd_ok__len 1
-+#define ma_dcu_mon1__pvdd_ok__mask 0x08
-+#define ma_dcu_mon1__pvdd_ok__shift 0x03
-+#define ma_dcu_mon1__pvdd_ok__reset 0x00
-+//-----------------------------------------------------------dcu_mon1.vdd_ok---
-+// dcu1 vdd monitor
-+#define ma_dcu_mon1__vdd_ok__a 101
-+#define ma_dcu_mon1__vdd_ok__len 1
-+#define ma_dcu_mon1__vdd_ok__mask 0x10
-+#define ma_dcu_mon1__vdd_ok__shift 0x04
-+#define ma_dcu_mon1__vdd_ok__reset 0x00
-+//-------------------------------------------------------------dcu_mon1.mute---
-+// dcu1 mute monitor
-+#define ma_dcu_mon1__mute__a 101
-+#define ma_dcu_mon1__mute__len 1
-+#define ma_dcu_mon1__mute__mask 0x20
-+#define ma_dcu_mon1__mute__shift 0x05
-+#define ma_dcu_mon1__mute__reset 0x00
-+//------------------------------------------------------------dcu_mon1.m_mon---
-+// m sense monitor channel 1
-+#define ma_dcu_mon1__m_mon__a 102
-+#define ma_dcu_mon1__m_mon__len 8
-+#define ma_dcu_mon1__m_mon__mask 0xff
-+#define ma_dcu_mon1__m_mon__shift 0x00
-+#define ma_dcu_mon1__m_mon__reset 0x00
-+//--------------------------------------------------------dcu_mon0.sw_enable---
-+// dcu0 switch enable monitor
-+#define ma_dcu_mon0__sw_enable__a 104
-+#define ma_dcu_mon0__sw_enable__len 1
-+#define ma_dcu_mon0__sw_enable__mask 0x40
-+#define ma_dcu_mon0__sw_enable__shift 0x06
-+#define ma_dcu_mon0__sw_enable__reset 0x00
-+//--------------------------------------------------------dcu_mon1.sw_enable---
-+// dcu1 switch enable monitor
-+#define ma_dcu_mon1__sw_enable__a 104
-+#define ma_dcu_mon1__sw_enable__len 1
-+#define ma_dcu_mon1__sw_enable__mask 0x80
-+#define ma_dcu_mon1__sw_enable__shift 0x07
-+#define ma_dcu_mon1__sw_enable__reset 0x00
-+//------------------------------------------------------------hvboot0_ok_mon---
-+// hvboot0_ok for test/debug
-+#define ma_hvboot0_ok_mon__a 105
-+#define ma_hvboot0_ok_mon__len 1
-+#define ma_hvboot0_ok_mon__mask 0x40
-+#define ma_hvboot0_ok_mon__shift 0x06
-+#define ma_hvboot0_ok_mon__reset 0x00
-+//------------------------------------------------------------hvboot1_ok_mon---
-+// hvboot1_ok for test/debug
-+#define ma_hvboot1_ok_mon__a 105
-+#define ma_hvboot1_ok_mon__len 1
-+#define ma_hvboot1_ok_mon__mask 0x80
-+#define ma_hvboot1_ok_mon__shift 0x07
-+#define ma_hvboot1_ok_mon__reset 0x00
-+//-----------------------------------------------------------------error_acc---
-+// accumulated errors,  at and after triggering
-+#define ma_error_acc__a 109
-+#define ma_error_acc__len 8
-+#define ma_error_acc__mask 0xff
-+#define ma_error_acc__shift 0x00
-+#define ma_error_acc__reset 0x00
-+//-------------------------------------------------------------i2s_data_rate---
-+// detected i2s data rate: 00/01/10 = x1/x2/x4
-+#define ma_i2s_data_rate__a 116
-+#define ma_i2s_data_rate__len 2
-+#define ma_i2s_data_rate__mask 0x03
-+#define ma_i2s_data_rate__shift 0x00
-+#define ma_i2s_data_rate__reset 0x00
-+//---------------------------------------------------------audio_in_mode_mon---
-+// audio input mode monitor
-+#define ma_audio_in_mode_mon__a 116
-+#define ma_audio_in_mode_mon__len 3
-+#define ma_audio_in_mode_mon__mask 0x1c
-+#define ma_audio_in_mode_mon__shift 0x02
-+#define ma_audio_in_mode_mon__reset 0x00
-+//------------------------------------------------------------------msel_mon---
-+// msel[2:0] monitor register
-+#define ma_msel_mon__a 117
-+#define ma_msel_mon__len 3
-+#define ma_msel_mon__mask 0x07
-+#define ma_msel_mon__shift 0x00
-+#define ma_msel_mon__reset 0x00
-+//---------------------------------------------------------------------error---
-+// current error flag monitor reg - for app. ctrl.
-+#define ma_error__a 124
-+#define ma_error__len 8
-+#define ma_error__mask 0xff
-+#define ma_error__shift 0x00
-+#define ma_error__reset 0x00
-+//----------------------------------------------------audio_proc_limiter_mon---
-+// b7-b4: channel 3-0 limiter active
-+#define ma_audio_proc_limiter_mon__a 126
-+#define ma_audio_proc_limiter_mon__len 4
-+#define ma_audio_proc_limiter_mon__mask 0xf0
-+#define ma_audio_proc_limiter_mon__shift 0x04
-+#define ma_audio_proc_limiter_mon__reset 0x00
-+//-------------------------------------------------------audio_proc_clip_mon---
-+// b3-b0: channel 3-0 clipping monitor
-+#define ma_audio_proc_clip_mon__a 126
-+#define ma_audio_proc_clip_mon__len 4
-+#define ma_audio_proc_clip_mon__mask 0x0f
-+#define ma_audio_proc_clip_mon__shift 0x00
-+#define ma_audio_proc_clip_mon__reset 0x00
-+#endif
-+
-+#define SOC_ENUM_ERR(xname, xenum)\
-+{     .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
-+      .access = SNDRV_CTL_ELEM_ACCESS_READ,\
-+      .info = snd_soc_info_enum_double,\
-+      .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
-+      .private_value = (unsigned long)&(xenum) }
-+
-+static struct i2c_client *i2c;
-+
-+struct ma120x0p_priv {
-+      struct regmap *regmap;
-+      int mclk_div;
-+      struct snd_soc_component *component;
-+      struct gpio_desc *enable_gpio;
-+      struct gpio_desc *mute_gpio;
-+      struct gpio_desc *booster_gpio;
-+      struct gpio_desc *error_gpio;
-+};
-+
-+static struct ma120x0p_priv *priv_data;
-+
-+//Used to share the IRQ number within this file
-+static unsigned int irqNumber;
-+
-+// Function prototype for the custom IRQ handler function
-+static irqreturn_t ma120x0p_irq_handler(int irq, void *data);
-+
-+//Alsa Controls
-+static const char * const limenable_text[] = {"Bypassed", "Enabled"};
-+static const char * const limatack_text[] = {"Slow", "Normal", "Fast"};
-+static const char * const limrelease_text[] = {"Slow", "Normal", "Fast"};
-+
-+static const char * const err_flycap_text[] = {"Ok", "Error"};
-+static const char * const err_overcurr_text[] = {"Ok", "Error"};
-+static const char * const err_pllerr_text[] = {"Ok", "Error"};
-+static const char * const err_pvddunder_text[] = {"Ok", "Error"};
-+static const char * const err_overtempw_text[] = {"Ok", "Error"};
-+static const char * const err_overtempe_text[] = {"Ok", "Error"};
-+static const char * const err_pinlowimp_text[] = {"Ok", "Error"};
-+static const char * const err_dcprot_text[] = {"Ok", "Error"};
-+
-+static const char * const pwr_mode_prof_text[] = {"PMF0", "PMF1", "PMF2",
-+"PMF3", "PMF4"};
-+
-+static const struct soc_enum lim_enable_ctrl =
-+      SOC_ENUM_SINGLE(ma_audio_proc_limiterenable__a,
-+              ma_audio_proc_limiterenable__shift,
-+              ma_audio_proc_limiterenable__len + 1,
-+              limenable_text);
-+static const struct soc_enum limatack_ctrl =
-+      SOC_ENUM_SINGLE(ma_audio_proc_attack__a,
-+              ma_audio_proc_attack__shift,
-+              ma_audio_proc_attack__len + 1,
-+              limatack_text);
-+static const struct soc_enum limrelease_ctrl =
-+      SOC_ENUM_SINGLE(ma_audio_proc_release__a,
-+              ma_audio_proc_release__shift,
-+              ma_audio_proc_release__len + 1,
-+              limrelease_text);
-+static const struct soc_enum err_flycap_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 0, 3, err_flycap_text);
-+static const struct soc_enum err_overcurr_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 1, 3, err_overcurr_text);
-+static const struct soc_enum err_pllerr_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 2, 3, err_pllerr_text);
-+static const struct soc_enum err_pvddunder_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 3, 3, err_pvddunder_text);
-+static const struct soc_enum err_overtempw_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 4, 3, err_overtempw_text);
-+static const struct soc_enum err_overtempe_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 5, 3, err_overtempe_text);
-+static const struct soc_enum err_pinlowimp_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 6, 3, err_pinlowimp_text);
-+static const struct soc_enum err_dcprot_ctrl =
-+      SOC_ENUM_SINGLE(ma_error__a, 7, 3, err_dcprot_text);
-+static const struct soc_enum pwr_mode_prof_ctrl =
-+      SOC_ENUM_SINGLE(ma_pmprofile__a, ma_pmprofile__shift, 5,
-+              pwr_mode_prof_text);
-+
-+static const char * const pwr_mode_texts[] = {
-+              "Dynamic power mode",
-+              "Power mode 1",
-+              "Power mode 2",
-+              "Power mode 3",
-+      };
-+
-+static const int pwr_mode_values[] = {
-+              0x10,
-+              0x50,
-+              0x60,
-+              0x70,
-+      };
-+
-+static const SOC_VALUE_ENUM_SINGLE_DECL(pwr_mode_ctrl,
-+      ma_pm_man__a, 0, 0x70,
-+      pwr_mode_texts,
-+      pwr_mode_values);
-+
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_vol_tlv, -5000, 100,  0);
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_lim_tlv, -5000, 100,  0);
-+static const DECLARE_TLV_DB_SCALE(ma120x0p_lr_tlv, -5000, 100,  0);
-+
-+static const struct snd_kcontrol_new ma120x0p_snd_controls[] = {
-+      //Master Volume
-+      SOC_SINGLE_RANGE_TLV("A.Mstr Vol Volume",
-+              ma_vol_db_master__a, 0, 0x18, 0x4a, 1, ma120x0p_vol_tlv),
-+
-+      //L-R Volume ch0
-+      SOC_SINGLE_RANGE_TLV("B.L Vol Volume",
-+              ma_vol_db_ch0__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
-+      SOC_SINGLE_RANGE_TLV("C.R Vol Volume",
-+              ma_vol_db_ch1__a, 0, 0x18, 0x4a, 1, ma120x0p_lr_tlv),
-+
-+      //L-R Limiter Threshold ch0-ch1
-+      SOC_DOUBLE_R_RANGE_TLV("D.Lim thresh Volume",
-+              ma_thr_db_ch0__a, ma_thr_db_ch1__a, 0, 0x0e, 0x4a, 1,
-+              ma120x0p_lim_tlv),
-+
-+      //Enum Switches/Selectors
-+      //SOC_ENUM("E.AudioProc Mute", audioproc_mute_ctrl),
-+      SOC_ENUM("F.Limiter Enable", lim_enable_ctrl),
-+      SOC_ENUM("G.Limiter Attck", limatack_ctrl),
-+      SOC_ENUM("H.Limiter Rls", limrelease_ctrl),
-+
-+      //Enum Error Monitor (read-only)
-+      SOC_ENUM_ERR("I.Err flycap", err_flycap_ctrl),
-+      SOC_ENUM_ERR("J.Err overcurr", err_overcurr_ctrl),
-+      SOC_ENUM_ERR("K.Err pllerr", err_pllerr_ctrl),
-+      SOC_ENUM_ERR("L.Err pvddunder", err_pvddunder_ctrl),
-+      SOC_ENUM_ERR("M.Err overtempw", err_overtempw_ctrl),
-+      SOC_ENUM_ERR("N.Err overtempe", err_overtempe_ctrl),
-+      SOC_ENUM_ERR("O.Err pinlowimp", err_pinlowimp_ctrl),
-+      SOC_ENUM_ERR("P.Err dcprot", err_dcprot_ctrl),
-+
-+      //Power modes profiles
-+      SOC_ENUM("Q.PM Prof", pwr_mode_prof_ctrl),
-+
-+      // Power mode selection (Dynamic,1,2,3)
-+      SOC_ENUM("R.Power Mode", pwr_mode_ctrl),
-+};
-+
-+//Machine Driver
-+static int ma120x0p_hw_params(struct snd_pcm_substream *substream,
-+      struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-+{
-+      u16 blen = 0x00;
-+
-+      struct snd_soc_component *component = dai->component;
-+
-+      priv_data->component = component;
-+
-+      switch (params_format(params)) {
-+      case SNDRV_PCM_FORMAT_S16_LE:
-+              blen = 0x10;
-+              break;
-+      case SNDRV_PCM_FORMAT_S24_LE:
-+              blen = 0x00;
-+              break;
-+      case SNDRV_PCM_FORMAT_S32_LE:
-+              blen = 0x00;
-+              break;
-+      default:
-+              dev_err(dai->dev, "Unsupported word length: %u\n",
-+              params_format(params));
-+              return -EINVAL;
-+      }
-+
-+      // set word length
-+      snd_soc_component_update_bits(component, ma_i2s_framesize__a,
-+              ma_i2s_framesize__mask, blen);
-+
-+      return 0;
-+}
-+
-+static int ma120x0p_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
-+{
-+      int val = 0;
-+
-+      struct ma120x0p_priv *ma120x0p;
-+
-+      struct snd_soc_component *component = dai->component;
-+
-+      ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+      if (mute)
-+              val = 0;
-+      else
-+              val = 1;
-+
-+      gpiod_set_value_cansleep(priv_data->mute_gpio, val);
-+
-+      return 0;
-+}
-+
-+static const struct snd_soc_dai_ops ma120x0p_dai_ops = {
-+      .hw_params              =       ma120x0p_hw_params,
-+      .mute_stream    =       ma120x0p_mute_stream,
-+};
-+
-+static struct snd_soc_dai_driver ma120x0p_dai = {
-+      .name           = "ma120x0p-amp",
-+      .playback       =       {
-+              .stream_name    = "Playback",
-+              .channels_min   = 2,
-+              .channels_max   = 2,
-+              .rates = SNDRV_PCM_RATE_CONTINUOUS,
-+              .rate_min = 44100,
-+              .rate_max = 48000,
-+              .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE
-+      },
-+      .ops        = &ma120x0p_dai_ops,
-+};
-+
-+//Codec Driver
-+static int ma120x0p_clear_err(struct snd_soc_component *component)
-+{
-+      int ret = 0;
-+
-+      struct ma120x0p_priv *ma120x0p;
-+
-+      ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+      ret = snd_soc_component_update_bits(component,
-+              ma_eh_clear__a, ma_eh_clear__mask, 0x00);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = snd_soc_component_update_bits(component,
-+              ma_eh_clear__a, ma_eh_clear__mask, 0x04);
-+      if (ret < 0)
-+              return ret;
-+
-+      ret = snd_soc_component_update_bits(component,
-+              ma_eh_clear__a, ma_eh_clear__mask, 0x00);
-+      if (ret < 0)
-+              return ret;
-+
-+      return 0;
-+}
-+
-+static void ma120x0p_remove(struct snd_soc_component *component)
-+{
-+      struct ma120x0p_priv *ma120x0p;
-+
-+      ma120x0p = snd_soc_component_get_drvdata(component);
-+}
-+
-+static int ma120x0p_probe(struct snd_soc_component *component)
-+{
-+      struct ma120x0p_priv *ma120x0p;
-+
-+      int ret = 0;
-+
-+      i2c = container_of(component->dev, struct i2c_client, dev);
-+
-+      ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+      //Reset error
-+      ma120x0p_clear_err(component);
-+      if (ret < 0)
-+              return ret;
-+
-+      // set serial audio format I2S and enable audio processor
-+      ret = snd_soc_component_write(component, ma_i2s_format__a, 0x08);
-+      if (ret < 0)
-+              return ret;
-+
-+      // Enable audio limiter
-+      ret = snd_soc_component_update_bits(component,
-+              ma_audio_proc_limiterenable__a,
-+              ma_audio_proc_limiterenable__mask, 0x40);
-+      if (ret < 0)
-+              return ret;
-+
-+      // Set lim attack to fast
-+      ret = snd_soc_component_update_bits(component,
-+              ma_audio_proc_attack__a, ma_audio_proc_attack__mask, 0x80);
-+      if (ret < 0)
-+              return ret;
-+
-+      // Set lim attack to low
-+      ret = snd_soc_component_update_bits(component,
-+              ma_audio_proc_release__a, ma_audio_proc_release__mask, 0x00);
-+      if (ret < 0)
-+              return ret;
-+
-+      // set volume to 0dB
-+      ret = snd_soc_component_write(component, ma_vol_db_master__a, 0x18);
-+      if (ret < 0)
-+              return ret;
-+
-+      // set ch0 lim thresh to -15dB
-+      ret = snd_soc_component_write(component, ma_thr_db_ch0__a, 0x27);
-+      if (ret < 0)
-+              return ret;
-+
-+      // set ch1 lim thresh to -15dB
-+      ret = snd_soc_component_write(component, ma_thr_db_ch1__a, 0x27);
-+      if (ret < 0)
-+              return ret;
-+
-+      //Check for errors
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x00, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x01, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x02, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x08, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x10, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x20, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x40, 0);
-+      if (ret < 0)
-+              return ret;
-+      ret = snd_soc_component_test_bits(component, ma_error_acc__a, 0x80, 0);
-+      if (ret < 0)
-+              return ret;
-+
-+      return 0;
-+}
-+
-+static int ma120x0p_set_bias_level(struct snd_soc_component *component,
-+      enum snd_soc_bias_level level)
-+{
-+      int ret = 0;
-+
-+      struct ma120x0p_priv *ma120x0p;
-+
-+      ma120x0p = snd_soc_component_get_drvdata(component);
-+
-+      switch (level) {
-+      case SND_SOC_BIAS_ON:
-+              break;
-+
-+      case SND_SOC_BIAS_PREPARE:
-+              break;
-+
-+      case SND_SOC_BIAS_STANDBY:
-+              ret = gpiod_get_value_cansleep(priv_data->enable_gpio);
-+              if (ret != 0) {
-+                      dev_err(component->dev, "Device ma120x0p disabled in STANDBY BIAS: %d\n",
-+                      ret);
-+                      return ret;
-+              }
-+              break;
-+
-+      case SND_SOC_BIAS_OFF:
-+              break;
-+      }
-+
-+      return 0;
-+}
-+
-+static const struct snd_soc_dapm_widget ma120x0p_dapm_widgets[] = {
-+      SND_SOC_DAPM_OUTPUT("OUT_A"),
-+      SND_SOC_DAPM_OUTPUT("OUT_B"),
-+};
-+
-+static const struct snd_soc_dapm_route ma120x0p_dapm_routes[] = {
-+      { "OUT_B",  NULL, "Playback" },
-+      { "OUT_A",  NULL, "Playback" },
-+};
-+
-+static const struct snd_soc_component_driver ma120x0p_component_driver = {
-+      .probe = ma120x0p_probe,
-+      .remove = ma120x0p_remove,
-+      .set_bias_level = ma120x0p_set_bias_level,
-+      .dapm_widgets           = ma120x0p_dapm_widgets,
-+      .num_dapm_widgets       = ARRAY_SIZE(ma120x0p_dapm_widgets),
-+      .dapm_routes            = ma120x0p_dapm_routes,
-+      .num_dapm_routes        = ARRAY_SIZE(ma120x0p_dapm_routes),
-+      .controls = ma120x0p_snd_controls,
-+      .num_controls = ARRAY_SIZE(ma120x0p_snd_controls),
-+      .use_pmdown_time        = 1,
-+      .endianness             = 1,
-+      .non_legacy_dai_naming  = 1,
-+};
-+
-+//I2C Driver
-+static const struct reg_default ma120x0p_reg_defaults[] = {
-+      {       0x01,   0x3c    },
-+};
-+
-+static bool ma120x0p_reg_volatile(struct device *dev, unsigned int reg)
-+{
-+      switch (reg) {
-+      case ma_error__a:
-+                      return true;
-+      default:
-+                      return false;
-+      }
-+}
-+
-+static const struct of_device_id ma120x0p_of_match[] = {
-+      { .compatible = "ma,ma120x0p", },
-+      { }
-+};
-+
-+MODULE_DEVICE_TABLE(of, ma120x0p_of_match);
-+
-+static struct regmap_config ma120x0p_regmap_config = {
-+      .reg_bits = 8,
-+      .val_bits = 8,
-+
-+      .max_register = 255,
-+      .volatile_reg = ma120x0p_reg_volatile,
-+
-+      .cache_type = REGCACHE_RBTREE,
-+      .reg_defaults = ma120x0p_reg_defaults,
-+      .num_reg_defaults = ARRAY_SIZE(ma120x0p_reg_defaults),
-+};
-+
-+static int ma120x0p_i2c_probe(struct i2c_client *i2c,
-+      const struct i2c_device_id *id)
-+{
-+      int ret;
-+
-+      priv_data = devm_kzalloc(&i2c->dev, sizeof(*priv_data), GFP_KERNEL);
-+      if (!priv_data)
-+              return -ENOMEM;
-+      i2c_set_clientdata(i2c, priv_data);
-+
-+      priv_data->regmap = devm_regmap_init_i2c(i2c, &ma120x0p_regmap_config);
-+      if (IS_ERR(priv_data->regmap)) {
-+              ret = PTR_ERR(priv_data->regmap);
-+              return ret;
-+      }
-+
-+      //Startup sequence
-+
-+      //Make sure the device is muted
-+      priv_data->mute_gpio = devm_gpiod_get(&i2c->dev, "mute_gp",
-+              GPIOD_OUT_LOW);
-+      if (IS_ERR(priv_data->mute_gpio)) {
-+              ret = PTR_ERR(priv_data->mute_gpio);
-+              dev_err(&i2c->dev, "Failed to get mute gpio line: %d\n", ret);
-+              return ret;
-+      }
-+      msleep(50);
-+
-+// MA120xx0P devices are usually powered by an integrated boost converter.
-+// An option GPIO control line is provided to enable the booster properly and
-+// in sync with the enable and mute GPIO lines.
-+      priv_data->booster_gpio = devm_gpiod_get_optional(&i2c->dev,
-+              "booster_gp", GPIOD_OUT_LOW);
-+      if (IS_ERR(priv_data->booster_gpio)) {
-+              ret = PTR_ERR(priv_data->booster_gpio);
-+              dev_err(&i2c->dev,
-+              "Failed to get booster enable gpio line: %d\n", ret);
-+              return ret;
-+      }
-+      msleep(50);
-+
-+      //Enable booster and wait 200ms until stable PVDD
-+      gpiod_set_value_cansleep(priv_data->booster_gpio, 1);
-+      msleep(200);
-+
-+      //Enable ma120x0pp
-+      priv_data->enable_gpio = devm_gpiod_get(&i2c->dev,
-+              "enable_gp", GPIOD_OUT_LOW);
-+      if (IS_ERR(priv_data->enable_gpio)) {
-+              ret = PTR_ERR(priv_data->enable_gpio);
-+              dev_err(&i2c->dev,
-+              "Failed to get ma120x0p enable gpio line: %d\n", ret);
-+              return ret;
-+      }
-+      msleep(50);
-+
-+      //Optional use of ma120x0pp error line as an interrupt trigger to
-+      //platform GPIO.
-+      //Get error input gpio ma120x0p
-+      priv_data->error_gpio = devm_gpiod_get_optional(&i2c->dev,
-+               "error_gp", GPIOD_IN);
-+      if (IS_ERR(priv_data->error_gpio)) {
-+              ret = PTR_ERR(priv_data->error_gpio);
-+              dev_err(&i2c->dev,
-+                      "Failed to get ma120x0p error gpio line: %d\n", ret);
-+              return ret;
-+      }
-+
-+      if (priv_data->error_gpio != NULL) {
-+              irqNumber = gpiod_to_irq(priv_data->error_gpio);
-+
-+              ret = devm_request_threaded_irq(&i2c->dev,
-+                       irqNumber, ma120x0p_irq_handler,
-+                       NULL, IRQF_TRIGGER_FALLING,
-+                       "ma120x0p", priv_data);
-+              if (ret != 0)
-+                      dev_warn(&i2c->dev, "Failed to request IRQ: %d\n",
-+                              ret);
-+      }
-+
-+      ret = devm_snd_soc_register_component(&i2c->dev,
-+              &ma120x0p_component_driver, &ma120x0p_dai, 1);
-+
-+      return ret;
-+}
-+
-+static irqreturn_t ma120x0p_irq_handler(int irq, void *data)
-+{
-+      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+      return IRQ_HANDLED;
-+}
-+
-+static int ma120x0p_i2c_remove(struct i2c_client *i2c)
-+{
-+      snd_soc_unregister_component(&i2c->dev);
-+      i2c_set_clientdata(i2c, NULL);
-+
-+      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+      msleep(30);
-+      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+      msleep(200);
-+      gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
-+      msleep(200);
-+
-+      kfree(priv_data);
-+
-+      return 0;
-+}
-+
-+static void ma120x0p_i2c_shutdown(struct i2c_client *i2c)
-+{
-+      snd_soc_unregister_component(&i2c->dev);
-+      i2c_set_clientdata(i2c, NULL);
-+
-+      gpiod_set_value_cansleep(priv_data->mute_gpio, 0);
-+      msleep(30);
-+      gpiod_set_value_cansleep(priv_data->enable_gpio, 1);
-+      msleep(200);
-+      gpiod_set_value_cansleep(priv_data->booster_gpio, 0);
-+      msleep(200);
-+
-+      kfree(priv_data);
-+}
-+
-+static const struct i2c_device_id ma120x0p_i2c_id[] = {
-+      { "ma120x0p", 0 },
-+      { }
-+};
-+
-+MODULE_DEVICE_TABLE(i2c, ma120x0p_i2c_id);
-+
-+static struct i2c_driver ma120x0p_i2c_driver = {
-+      .driver = {
-+              .name = "ma120x0p",
-+              .owner = THIS_MODULE,
-+              .of_match_table = ma120x0p_of_match,
-+      },
-+      .probe = ma120x0p_i2c_probe,
-+      .remove = ma120x0p_i2c_remove,
-+      .shutdown = ma120x0p_i2c_shutdown,
-+      .id_table = ma120x0p_i2c_id
-+};
-+
-+static int __init ma120x0p_modinit(void)
-+{
-+      int ret = 0;
-+
-+      ret = i2c_add_driver(&ma120x0p_i2c_driver);
-+      if (ret != 0) {
-+              pr_err("Failed to register MA120X0P I2C driver: %d\n", ret);
-+              return ret;
-+      }
-+      return ret;
-+}
-+module_init(ma120x0p_modinit);
-+
-+static void __exit ma120x0p_exit(void)
-+{
-+      i2c_del_driver(&ma120x0p_i2c_driver);
-+}
-+module_exit(ma120x0p_exit);
-+
-+MODULE_AUTHOR("Ariel Muszkat ariel.muszkat@gmail.com>");
-+MODULE_DESCRIPTION("ASoC driver for ma120x0p");
-+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch b/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm2711-Add-32-bit-PMU-compatibility.patch
deleted file mode 100644 (file)
index f549e73..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-From e25d9a93812847b4ddc9e883d0cd45b32f8e2f76 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Tue, 17 Mar 2020 16:39:07 +0000
-Subject: [PATCH] ARM: dts: bcm2711: Add 32-bit PMU compatibility
-
-The "arm" architecture has no support for the cortex-a72 as such, but
-the performance and measurement unit from the cortex-a15 seems to be
-compatible.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2711-rpi.dtsi | 4 ++++
- 1 file changed, 4 insertions(+)
-
---- a/arch/arm/boot/dts/bcm2711-rpi.dtsi
-+++ b/arch/arm/boot/dts/bcm2711-rpi.dtsi
-@@ -12,6 +12,10 @@
-               sd_poll_once = <&emmc2>, "non-removable?";
-       };
-+      arm-pmu {
-+              compatible = "arm,cortex-a72-pmu", "arm,cortex-a15-pmu";
-+      };
-+
-       v3dbus {
-               compatible = "simple-bus";
-               #address-cells = <1>;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch b/target/linux/bcm27xx/patches-5.4/950-0477-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
new file mode 100644 (file)
index 0000000..9504bb3
--- /dev/null
@@ -0,0 +1,67 @@
+From 70b0d5d07426e1b9c34ddd6ab4ee99b8c2fb81a6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 19 Mar 2020 10:04:46 +0000
+Subject: [PATCH] ARM: dts: bcm271x: Use a53 pmu, drop RPI364
+
+The upstream bcm2837.dtsi uses cortex-a53-pmu, so we can do the same
+but with a fallback to the cortex-a7-pmu which is supported by the
+32-bit kernel.
+
+Now that we're using the natural fallback mechanism of compatible
+strings, the RPI364 macro no longer serves any purpose - remove it.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2710.dtsi                        | 6 +-----
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts      | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts      | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts      | 2 --
+ arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts      | 2 --
+ 6 files changed, 1 insertion(+), 15 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2710.dtsi
++++ b/arch/arm/boot/dts/bcm2710.dtsi
+@@ -5,11 +5,7 @@
+       compatible = "brcm,bcm2837", "brcm,bcm2836";
+       arm-pmu {
+-#ifdef RPI364
+-              compatible = "arm,armv8-pmuv3", "arm,cortex-a7-pmu";
+-#else
+-              compatible = "arm,cortex-a7-pmu";
+-#endif
++              compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
+       };
+       soc {
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-2-b.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-3-b-plus.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-3-b.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2710-rpi-cm3.dts"
+--- a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
++++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
+@@ -1,3 +1 @@
+-#define RPI364
+-
+ #include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"
diff --git a/target/linux/bcm27xx/patches-5.4/950-0478-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch b/target/linux/bcm27xx/patches-5.4/950-0478-ARM-dts-bcm271x-Use-a53-pmu-drop-RPI364.patch
deleted file mode 100644 (file)
index 9504bb3..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-From 70b0d5d07426e1b9c34ddd6ab4ee99b8c2fb81a6 Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Thu, 19 Mar 2020 10:04:46 +0000
-Subject: [PATCH] ARM: dts: bcm271x: Use a53 pmu, drop RPI364
-
-The upstream bcm2837.dtsi uses cortex-a53-pmu, so we can do the same
-but with a fallback to the cortex-a7-pmu which is supported by the
-32-bit kernel.
-
-Now that we're using the natural fallback mechanism of compatible
-strings, the RPI364 macro no longer serves any purpose - remove it.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- arch/arm/boot/dts/bcm2710.dtsi                        | 6 +-----
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts      | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts      | 2 --
- arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts      | 2 --
- arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts      | 2 --
- 6 files changed, 1 insertion(+), 15 deletions(-)
-
---- a/arch/arm/boot/dts/bcm2710.dtsi
-+++ b/arch/arm/boot/dts/bcm2710.dtsi
-@@ -5,11 +5,7 @@
-       compatible = "brcm,bcm2837", "brcm,bcm2836";
-       arm-pmu {
--#ifdef RPI364
--              compatible = "arm,armv8-pmuv3", "arm,cortex-a7-pmu";
--#else
--              compatible = "arm,cortex-a7-pmu";
--#endif
-+              compatible = "arm,cortex-a53-pmu", "arm,cortex-a7-pmu";
-       };
-       soc {
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-2-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-2-b.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b-plus.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-3-b-plus.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-3-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-3-b.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2710-rpi-cm3.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2710-rpi-cm3.dts"
---- a/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
-+++ b/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dts
-@@ -1,3 +1 @@
--#define RPI364
--
- #include "../../../../arm/boot/dts/bcm2711-rpi-4-b.dts"
diff --git a/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch b/target/linux/bcm27xx/patches-5.4/950-0478-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
new file mode 100644 (file)
index 0000000..9d80904
--- /dev/null
@@ -0,0 +1,32 @@
+From cff8c5c2a95a4afd65bfa3198258d03bc790cddb Mon Sep 17 00:00:00 2001
+From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Date: Tue, 25 Feb 2020 14:11:59 +0100
+Subject: [PATCH] net: bcmgenet: Clear ID_MODE_DIS in
+ EXT_RGMII_OOB_CTRL when not needed
+
+commit 402482a6a78e5c61d8a2ec6311fc5b4aca392cd6 upstream.
+
+Outdated Raspberry Pi 4 firmware might configure the external PHY as
+rgmii although the kernel currently sets it as rgmii-rxid. This makes
+connections unreliable as ID_MODE_DIS is left enabled. To avoid this,
+explicitly clear that bit whenever we don't need it.
+
+Fixes: da38802211cc ("net: bcmgenet: Add RGMII_RXID support")
+Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Matthias Reichl <hias@horus.com>
+---
+ drivers/net/ethernet/broadcom/genet/bcmmii.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
++++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
+@@ -292,6 +292,7 @@ int bcmgenet_mii_config(struct net_devic
+        */
+       if (priv->ext_phy) {
+               reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
++              reg &= ~ID_MODE_DIS;
+               reg |= id_mode_dis;
+               if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
+                       reg |= RGMII_MODE_EN_V123;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch b/target/linux/bcm27xx/patches-5.4/950-0479-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
new file mode 100644 (file)
index 0000000..9b514c3
--- /dev/null
@@ -0,0 +1,141 @@
+From fade8b3cf37785297b4f8a9bbd13ab107208af5a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:22 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Fix possible reference past
+ end of string
+
+Commit 8582e244e5fe72d2e9ace186fa8f3ed3bb4122e1 upstream.
+
+Before this commit, if the last option of a video=... option is for
+example "rotate" without a "=<value>" after it then delim will point to
+the terminating 0 of the string, and value which is sets to <delim + 1>
+will point one position past the end of the string.
+
+This commit fixes this by enforcing that the contents of delim equals '='
+as it should be for options which take a value, this check is done in a
+new drm_mode_parse_cmdline_int helper function which factors out the
+common integer parsing code for all the options which take an int.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-1-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 68 ++++++++++++++++---------------------
+ 1 file changed, 30 insertions(+), 38 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1568,11 +1568,34 @@ static int drm_mode_parse_cmdline_res_mo
+       return 0;
+ }
++static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret)
++{
++      const char *value;
++      char *endp;
++
++      /*
++       * delim must point to the '=', otherwise it is a syntax error and
++       * if delim points to the terminating zero, then delim + 1 wil point
++       * past the end of the string.
++       */
++      if (*delim != '=')
++              return -EINVAL;
++
++      value = delim + 1;
++      *int_ret = simple_strtol(value, &endp, 10);
++
++      /* Make sure we have parsed something */
++      if (endp == value)
++              return -EINVAL;
++
++      return 0;
++}
++
+ static int drm_mode_parse_cmdline_options(char *str, size_t len,
+                                         const struct drm_connector *connector,
+                                         struct drm_cmdline_mode *mode)
+ {
+-      unsigned int rotation = 0;
++      unsigned int deg, margin, rotation = 0;
+       char *sep = str;
+       while ((sep = strchr(sep, ','))) {
+@@ -1588,13 +1611,7 @@ static int drm_mode_parse_cmdline_option
+               }
+               if (!strncmp(option, "rotate", delim - option)) {
+-                      const char *value = delim + 1;
+-                      unsigned int deg;
+-
+-                      deg = simple_strtol(value, &sep, 10);
+-
+-                      /* Make sure we have parsed something */
+-                      if (sep == value)
++                      if (drm_mode_parse_cmdline_int(delim, &deg))
+                               return -EINVAL;
+                       switch (deg) {
+@@ -1619,57 +1636,32 @@ static int drm_mode_parse_cmdline_option
+                       }
+               } else if (!strncmp(option, "reflect_x", delim - option)) {
+                       rotation |= DRM_MODE_REFLECT_X;
+-                      sep = delim;
+               } else if (!strncmp(option, "reflect_y", delim - option)) {
+                       rotation |= DRM_MODE_REFLECT_Y;
+-                      sep = delim;
+               } else if (!strncmp(option, "margin_right", delim - option)) {
+-                      const char *value = delim + 1;
+-                      unsigned int margin;
+-
+-                      margin = simple_strtol(value, &sep, 10);
+-
+-                      /* Make sure we have parsed something */
+-                      if (sep == value)
++                      if (drm_mode_parse_cmdline_int(delim, &margin))
+                               return -EINVAL;
+                       mode->tv_margins.right = margin;
+               } else if (!strncmp(option, "margin_left", delim - option)) {
+-                      const char *value = delim + 1;
+-                      unsigned int margin;
+-
+-                      margin = simple_strtol(value, &sep, 10);
+-
+-                      /* Make sure we have parsed something */
+-                      if (sep == value)
++                      if (drm_mode_parse_cmdline_int(delim, &margin))
+                               return -EINVAL;
+                       mode->tv_margins.left = margin;
+               } else if (!strncmp(option, "margin_top", delim - option)) {
+-                      const char *value = delim + 1;
+-                      unsigned int margin;
+-
+-                      margin = simple_strtol(value, &sep, 10);
+-
+-                      /* Make sure we have parsed something */
+-                      if (sep == value)
++                      if (drm_mode_parse_cmdline_int(delim, &margin))
+                               return -EINVAL;
+                       mode->tv_margins.top = margin;
+               } else if (!strncmp(option, "margin_bottom", delim - option)) {
+-                      const char *value = delim + 1;
+-                      unsigned int margin;
+-
+-                      margin = simple_strtol(value, &sep, 10);
+-
+-                      /* Make sure we have parsed something */
+-                      if (sep == value)
++                      if (drm_mode_parse_cmdline_int(delim, &margin))
+                               return -EINVAL;
+                       mode->tv_margins.bottom = margin;
+               } else {
+                       return -EINVAL;
+               }
++              sep = delim;
+       }
+       if (!(rotation & DRM_MODE_ROTATE_MASK))
diff --git a/target/linux/bcm27xx/patches-5.4/950-0479-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch b/target/linux/bcm27xx/patches-5.4/950-0479-net-bcmgenet-Clear-ID_MODE_DIS-in-EXT_RGMII_OOB_CTRL.patch
deleted file mode 100644 (file)
index 9d80904..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-From cff8c5c2a95a4afd65bfa3198258d03bc790cddb Mon Sep 17 00:00:00 2001
-From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Date: Tue, 25 Feb 2020 14:11:59 +0100
-Subject: [PATCH] net: bcmgenet: Clear ID_MODE_DIS in
- EXT_RGMII_OOB_CTRL when not needed
-
-commit 402482a6a78e5c61d8a2ec6311fc5b4aca392cd6 upstream.
-
-Outdated Raspberry Pi 4 firmware might configure the external PHY as
-rgmii although the kernel currently sets it as rgmii-rxid. This makes
-connections unreliable as ID_MODE_DIS is left enabled. To avoid this,
-explicitly clear that bit whenever we don't need it.
-
-Fixes: da38802211cc ("net: bcmgenet: Add RGMII_RXID support")
-Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
-Acked-by: Florian Fainelli <f.fainelli@gmail.com>
-Signed-off-by: David S. Miller <davem@davemloft.net>
-Signed-off-by: Matthias Reichl <hias@horus.com>
----
- drivers/net/ethernet/broadcom/genet/bcmmii.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
-+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
-@@ -292,6 +292,7 @@ int bcmgenet_mii_config(struct net_devic
-        */
-       if (priv->ext_phy) {
-               reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
-+              reg &= ~ID_MODE_DIS;
-               reg |= id_mode_dis;
-               if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv))
-                       reg |= RGMII_MODE_EN_V123;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Fix-possible-reference-past-.patch
deleted file mode 100644 (file)
index 9b514c3..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-From fade8b3cf37785297b4f8a9bbd13ab107208af5a Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:22 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Fix possible reference past
- end of string
-
-Commit 8582e244e5fe72d2e9ace186fa8f3ed3bb4122e1 upstream.
-
-Before this commit, if the last option of a video=... option is for
-example "rotate" without a "=<value>" after it then delim will point to
-the terminating 0 of the string, and value which is sets to <delim + 1>
-will point one position past the end of the string.
-
-This commit fixes this by enforcing that the contents of delim equals '='
-as it should be for options which take a value, this check is done in a
-new drm_mode_parse_cmdline_int helper function which factors out the
-common integer parsing code for all the options which take an int.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-1-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 68 ++++++++++++++++---------------------
- 1 file changed, 30 insertions(+), 38 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1568,11 +1568,34 @@ static int drm_mode_parse_cmdline_res_mo
-       return 0;
- }
-+static int drm_mode_parse_cmdline_int(const char *delim, unsigned int *int_ret)
-+{
-+      const char *value;
-+      char *endp;
-+
-+      /*
-+       * delim must point to the '=', otherwise it is a syntax error and
-+       * if delim points to the terminating zero, then delim + 1 wil point
-+       * past the end of the string.
-+       */
-+      if (*delim != '=')
-+              return -EINVAL;
-+
-+      value = delim + 1;
-+      *int_ret = simple_strtol(value, &endp, 10);
-+
-+      /* Make sure we have parsed something */
-+      if (endp == value)
-+              return -EINVAL;
-+
-+      return 0;
-+}
-+
- static int drm_mode_parse_cmdline_options(char *str, size_t len,
-                                         const struct drm_connector *connector,
-                                         struct drm_cmdline_mode *mode)
- {
--      unsigned int rotation = 0;
-+      unsigned int deg, margin, rotation = 0;
-       char *sep = str;
-       while ((sep = strchr(sep, ','))) {
-@@ -1588,13 +1611,7 @@ static int drm_mode_parse_cmdline_option
-               }
-               if (!strncmp(option, "rotate", delim - option)) {
--                      const char *value = delim + 1;
--                      unsigned int deg;
--
--                      deg = simple_strtol(value, &sep, 10);
--
--                      /* Make sure we have parsed something */
--                      if (sep == value)
-+                      if (drm_mode_parse_cmdline_int(delim, &deg))
-                               return -EINVAL;
-                       switch (deg) {
-@@ -1619,57 +1636,32 @@ static int drm_mode_parse_cmdline_option
-                       }
-               } else if (!strncmp(option, "reflect_x", delim - option)) {
-                       rotation |= DRM_MODE_REFLECT_X;
--                      sep = delim;
-               } else if (!strncmp(option, "reflect_y", delim - option)) {
-                       rotation |= DRM_MODE_REFLECT_Y;
--                      sep = delim;
-               } else if (!strncmp(option, "margin_right", delim - option)) {
--                      const char *value = delim + 1;
--                      unsigned int margin;
--
--                      margin = simple_strtol(value, &sep, 10);
--
--                      /* Make sure we have parsed something */
--                      if (sep == value)
-+                      if (drm_mode_parse_cmdline_int(delim, &margin))
-                               return -EINVAL;
-                       mode->tv_margins.right = margin;
-               } else if (!strncmp(option, "margin_left", delim - option)) {
--                      const char *value = delim + 1;
--                      unsigned int margin;
--
--                      margin = simple_strtol(value, &sep, 10);
--
--                      /* Make sure we have parsed something */
--                      if (sep == value)
-+                      if (drm_mode_parse_cmdline_int(delim, &margin))
-                               return -EINVAL;
-                       mode->tv_margins.left = margin;
-               } else if (!strncmp(option, "margin_top", delim - option)) {
--                      const char *value = delim + 1;
--                      unsigned int margin;
--
--                      margin = simple_strtol(value, &sep, 10);
--
--                      /* Make sure we have parsed something */
--                      if (sep == value)
-+                      if (drm_mode_parse_cmdline_int(delim, &margin))
-                               return -EINVAL;
-                       mode->tv_margins.top = margin;
-               } else if (!strncmp(option, "margin_bottom", delim - option)) {
--                      const char *value = delim + 1;
--                      unsigned int margin;
--
--                      margin = simple_strtol(value, &sep, 10);
--
--                      /* Make sure we have parsed something */
--                      if (sep == value)
-+                      if (drm_mode_parse_cmdline_int(delim, &margin))
-                               return -EINVAL;
-                       mode->tv_margins.bottom = margin;
-               } else {
-                       return -EINVAL;
-               }
-+              sep = delim;
-       }
-       if (!(rotation & DRM_MODE_ROTATE_MASK))
diff --git a/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch b/target/linux/bcm27xx/patches-5.4/950-0480-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
new file mode 100644 (file)
index 0000000..6abe7be
--- /dev/null
@@ -0,0 +1,50 @@
+From 250363a413cd08e723789e1b8821608ff5eebfe6 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:23 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Make various char pointers
+ const
+
+Commit 83e14ea3a64f00897cc31974d3ae4e27e5a7405b upstream.
+
+We are not supposed to modify the passed in string, make char pointers
+used in drm_mode_parse_cmdline_options() const char * where possible.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-2-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,15 +1591,15 @@ static int drm_mode_parse_cmdline_int(co
+       return 0;
+ }
+-static int drm_mode_parse_cmdline_options(char *str, size_t len,
++static int drm_mode_parse_cmdline_options(const char *str, size_t len,
+                                         const struct drm_connector *connector,
+                                         struct drm_cmdline_mode *mode)
+ {
+       unsigned int deg, margin, rotation = 0;
+-      char *sep = str;
++      const char *sep = str;
+       while ((sep = strchr(sep, ','))) {
+-              char *delim, *option;
++              const char *delim, *option;
+               option = sep + 1;
+               delim = strchr(option, '=');
+@@ -1725,8 +1725,8 @@ bool drm_mode_parse_command_line_for_con
+       bool named_mode = false, parse_extras = false;
+       unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
+       unsigned int mode_end = 0;
+-      char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+-      char *options_ptr = NULL;
++      const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
++      const char *options_ptr = NULL;
+       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+       int ret;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Make-various-char-pointers-c.patch
deleted file mode 100644 (file)
index 6abe7be..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-From 250363a413cd08e723789e1b8821608ff5eebfe6 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:23 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Make various char pointers
- const
-
-Commit 83e14ea3a64f00897cc31974d3ae4e27e5a7405b upstream.
-
-We are not supposed to modify the passed in string, make char pointers
-used in drm_mode_parse_cmdline_options() const char * where possible.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-2-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,15 +1591,15 @@ static int drm_mode_parse_cmdline_int(co
-       return 0;
- }
--static int drm_mode_parse_cmdline_options(char *str, size_t len,
-+static int drm_mode_parse_cmdline_options(const char *str, size_t len,
-                                         const struct drm_connector *connector,
-                                         struct drm_cmdline_mode *mode)
- {
-       unsigned int deg, margin, rotation = 0;
--      char *sep = str;
-+      const char *sep = str;
-       while ((sep = strchr(sep, ','))) {
--              char *delim, *option;
-+              const char *delim, *option;
-               option = sep + 1;
-               delim = strchr(option, '=');
-@@ -1725,8 +1725,8 @@ bool drm_mode_parse_command_line_for_con
-       bool named_mode = false, parse_extras = false;
-       unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
-       unsigned int mode_end = 0;
--      char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
--      char *options_ptr = NULL;
-+      const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-+      const char *options_ptr = NULL;
-       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
-       int ret;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch b/target/linux/bcm27xx/patches-5.4/950-0481-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
new file mode 100644 (file)
index 0000000..1d356eb
--- /dev/null
@@ -0,0 +1,95 @@
+From 0e7c5e80d8d310a881d723a426762e8822d5bf35 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:24 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Stop parsing extras after
+ bpp / refresh at ', '
+
+Commit c2ed3e941901810ad3d55ce1935fa22c5007fee4 upstream.
+
+Before this commit it was impossible to add an extra mode argument after
+a bpp or refresh specifier, combined with an option, e.g.
+video=HDMI-1:720x480-24e,rotate=180 would not work, either the "e" to
+force enable would need to be dropped or the ",rotate=180", otherwise
+the mode_option would not be accepted.
+
+This commit fixes this by fixing the length calculation if extras_ptr
+is set to stop the extra parsing at the start of the options (stop at the
+',' options_ptr points to).
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-3-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c                   | 10 ++++---
+ .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
+ .../drm/selftests/test-drm_cmdline_parser.c   | 26 +++++++++++++++++++
+ 3 files changed, 33 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1728,7 +1728,7 @@ bool drm_mode_parse_command_line_for_con
+       const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+       const char *options_ptr = NULL;
+       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+-      int ret;
++      int i, len, ret;
+ #ifdef CONFIG_FB
+       if (!mode_option)
+@@ -1848,9 +1848,11 @@ bool drm_mode_parse_command_line_for_con
+       else if (refresh_ptr)
+               extra_ptr = refresh_end_ptr;
+-      if (extra_ptr &&
+-          extra_ptr != options_ptr) {
+-              int len = strlen(name) - (extra_ptr - name);
++      if (extra_ptr) {
++              if (options_ptr)
++                      len = options_ptr - extra_ptr;
++              else
++                      len = strlen(extra_ptr);
+               ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
+                                                  connector, mode);
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -61,3 +61,4 @@ cmdline_test(drm_cmdline_test_vmirror)
+ cmdline_test(drm_cmdline_test_margin_options)
+ cmdline_test(drm_cmdline_test_multiple_options)
+ cmdline_test(drm_cmdline_test_invalid_option)
++cmdline_test(drm_cmdline_test_bpp_extra_and_option)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1003,6 +1003,32 @@ static int drm_cmdline_test_invalid_opti
+       return 0;
+ }
++static int drm_cmdline_test_bpp_extra_and_option(void *ignored)
++{
++      struct drm_cmdline_mode mode = { };
++
++      FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24e,rotate=180",
++                                                         &no_connector,
++                                                         &mode));
++      FAIL_ON(!mode.specified);
++      FAIL_ON(mode.xres != 720);
++      FAIL_ON(mode.yres != 480);
++      FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
++
++      FAIL_ON(mode.refresh_specified);
++
++      FAIL_ON(!mode.bpp_specified);
++      FAIL_ON(mode.bpp != 24);
++
++      FAIL_ON(mode.rb);
++      FAIL_ON(mode.cvt);
++      FAIL_ON(mode.interlace);
++      FAIL_ON(mode.margins);
++      FAIL_ON(mode.force != DRM_FORCE_ON);
++
++      return 0;
++}
++
+ #include "drm_selftest.c"
+ static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
new file mode 100644 (file)
index 0000000..8d9a92e
--- /dev/null
@@ -0,0 +1,77 @@
+From bc4d8c5519c74b9bdf4d35369ba76bac01be8ca2 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:25 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Accept extras directly
+ after mode combined with options
+
+Commit cfb0881b8f621b656a9e23b31944a5db94cf5842 upstream.
+
+Before this commit it was impossible to combine an extra mode argument
+specified directly after the resolution with an option, e.g.
+video=HDMI-1:720x480e,rotate=180 would not work, either the "e" to force
+enable would need to be dropped or the ",rotate=180", otherwise the
+mode_option would not be accepted.
+
+This commit fixes this by setting parse_extras to true in this case, so
+that drm_mode_parse_cmdline_res_mode() parses the extra arguments directly
+after the resolution.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-4-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c                   |  1 +
+ .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
+ .../drm/selftests/test-drm_cmdline_parser.c   | 24 +++++++++++++++++++
+ 3 files changed, 26 insertions(+)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1801,6 +1801,7 @@ bool drm_mode_parse_command_line_for_con
+               mode_end = refresh_off;
+       } else if (options_ptr) {
+               mode_end = options_off;
++              parse_extras = true;
+       } else {
+               mode_end = strlen(name);
+               parse_extras = true;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -62,3 +62,4 @@ cmdline_test(drm_cmdline_test_margin_opt
+ cmdline_test(drm_cmdline_test_multiple_options)
+ cmdline_test(drm_cmdline_test_invalid_option)
+ cmdline_test(drm_cmdline_test_bpp_extra_and_option)
++cmdline_test(drm_cmdline_test_extra_and_option)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1029,6 +1029,30 @@ static int drm_cmdline_test_bpp_extra_an
+       return 0;
+ }
++static int drm_cmdline_test_extra_and_option(void *ignored)
++{
++      struct drm_cmdline_mode mode = { };
++
++      FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480e,rotate=180",
++                                                         &no_connector,
++                                                         &mode));
++      FAIL_ON(!mode.specified);
++      FAIL_ON(mode.xres != 720);
++      FAIL_ON(mode.yres != 480);
++      FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
++
++      FAIL_ON(mode.refresh_specified);
++      FAIL_ON(mode.bpp_specified);
++
++      FAIL_ON(mode.rb);
++      FAIL_ON(mode.cvt);
++      FAIL_ON(mode.interlace);
++      FAIL_ON(mode.margins);
++      FAIL_ON(mode.force != DRM_FORCE_ON);
++
++      return 0;
++}
++
+ #include "drm_selftest.c"
+ static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch b/target/linux/bcm27xx/patches-5.4/950-0482-drm-modes-parse_cmdline-Stop-parsing-extras-after-bp.patch
deleted file mode 100644 (file)
index 1d356eb..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-From 0e7c5e80d8d310a881d723a426762e8822d5bf35 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:24 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Stop parsing extras after
- bpp / refresh at ', '
-
-Commit c2ed3e941901810ad3d55ce1935fa22c5007fee4 upstream.
-
-Before this commit it was impossible to add an extra mode argument after
-a bpp or refresh specifier, combined with an option, e.g.
-video=HDMI-1:720x480-24e,rotate=180 would not work, either the "e" to
-force enable would need to be dropped or the ",rotate=180", otherwise
-the mode_option would not be accepted.
-
-This commit fixes this by fixing the length calculation if extras_ptr
-is set to stop the extra parsing at the start of the options (stop at the
-',' options_ptr points to).
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-3-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c                   | 10 ++++---
- .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
- .../drm/selftests/test-drm_cmdline_parser.c   | 26 +++++++++++++++++++
- 3 files changed, 33 insertions(+), 4 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1728,7 +1728,7 @@ bool drm_mode_parse_command_line_for_con
-       const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-       const char *options_ptr = NULL;
-       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
--      int ret;
-+      int i, len, ret;
- #ifdef CONFIG_FB
-       if (!mode_option)
-@@ -1848,9 +1848,11 @@ bool drm_mode_parse_command_line_for_con
-       else if (refresh_ptr)
-               extra_ptr = refresh_end_ptr;
--      if (extra_ptr &&
--          extra_ptr != options_ptr) {
--              int len = strlen(name) - (extra_ptr - name);
-+      if (extra_ptr) {
-+              if (options_ptr)
-+                      len = options_ptr - extra_ptr;
-+              else
-+                      len = strlen(extra_ptr);
-               ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
-                                                  connector, mode);
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -61,3 +61,4 @@ cmdline_test(drm_cmdline_test_vmirror)
- cmdline_test(drm_cmdline_test_margin_options)
- cmdline_test(drm_cmdline_test_multiple_options)
- cmdline_test(drm_cmdline_test_invalid_option)
-+cmdline_test(drm_cmdline_test_bpp_extra_and_option)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1003,6 +1003,32 @@ static int drm_cmdline_test_invalid_opti
-       return 0;
- }
-+static int drm_cmdline_test_bpp_extra_and_option(void *ignored)
-+{
-+      struct drm_cmdline_mode mode = { };
-+
-+      FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24e,rotate=180",
-+                                                         &no_connector,
-+                                                         &mode));
-+      FAIL_ON(!mode.specified);
-+      FAIL_ON(mode.xres != 720);
-+      FAIL_ON(mode.yres != 480);
-+      FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
-+
-+      FAIL_ON(mode.refresh_specified);
-+
-+      FAIL_ON(!mode.bpp_specified);
-+      FAIL_ON(mode.bpp != 24);
-+
-+      FAIL_ON(mode.rb);
-+      FAIL_ON(mode.cvt);
-+      FAIL_ON(mode.interlace);
-+      FAIL_ON(mode.margins);
-+      FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+      return 0;
-+}
-+
- #include "drm_selftest.c"
- static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Accept-extras-directly-after.patch b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Accept-extras-directly-after.patch
deleted file mode 100644 (file)
index 8d9a92e..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-From bc4d8c5519c74b9bdf4d35369ba76bac01be8ca2 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:25 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Accept extras directly
- after mode combined with options
-
-Commit cfb0881b8f621b656a9e23b31944a5db94cf5842 upstream.
-
-Before this commit it was impossible to combine an extra mode argument
-specified directly after the resolution with an option, e.g.
-video=HDMI-1:720x480e,rotate=180 would not work, either the "e" to force
-enable would need to be dropped or the ",rotate=180", otherwise the
-mode_option would not be accepted.
-
-This commit fixes this by setting parse_extras to true in this case, so
-that drm_mode_parse_cmdline_res_mode() parses the extra arguments directly
-after the resolution.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-4-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c                   |  1 +
- .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
- .../drm/selftests/test-drm_cmdline_parser.c   | 24 +++++++++++++++++++
- 3 files changed, 26 insertions(+)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1801,6 +1801,7 @@ bool drm_mode_parse_command_line_for_con
-               mode_end = refresh_off;
-       } else if (options_ptr) {
-               mode_end = options_off;
-+              parse_extras = true;
-       } else {
-               mode_end = strlen(name);
-               parse_extras = true;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -62,3 +62,4 @@ cmdline_test(drm_cmdline_test_margin_opt
- cmdline_test(drm_cmdline_test_multiple_options)
- cmdline_test(drm_cmdline_test_invalid_option)
- cmdline_test(drm_cmdline_test_bpp_extra_and_option)
-+cmdline_test(drm_cmdline_test_extra_and_option)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1029,6 +1029,30 @@ static int drm_cmdline_test_bpp_extra_an
-       return 0;
- }
-+static int drm_cmdline_test_extra_and_option(void *ignored)
-+{
-+      struct drm_cmdline_mode mode = { };
-+
-+      FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480e,rotate=180",
-+                                                         &no_connector,
-+                                                         &mode));
-+      FAIL_ON(!mode.specified);
-+      FAIL_ON(mode.xres != 720);
-+      FAIL_ON(mode.yres != 480);
-+      FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180);
-+
-+      FAIL_ON(mode.refresh_specified);
-+      FAIL_ON(mode.bpp_specified);
-+
-+      FAIL_ON(mode.rb);
-+      FAIL_ON(mode.cvt);
-+      FAIL_ON(mode.interlace);
-+      FAIL_ON(mode.margins);
-+      FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+      return 0;
-+}
-+
- #include "drm_selftest.c"
- static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch b/target/linux/bcm27xx/patches-5.4/950-0483-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
new file mode 100644 (file)
index 0000000..1716ebd
--- /dev/null
@@ -0,0 +1,76 @@
+From 5b6257773b43e7a7b28f86359d2e9ebe15346b78 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:26 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Rework
+ drm_mode_parse_cmdline_options()
+
+Commit 739b200c2edcaaa7a86f37b0c11db57956433dfb upstream.
+
+Refactor drm_mode_parse_cmdline_options() so that it takes a pointer
+to the first option, rather then a pointer to the ',' before the first
+option.
+
+This is a preparation patch for allowing parsing of stand-alone options
+without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-5-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,23 +1591,21 @@ static int drm_mode_parse_cmdline_int(co
+       return 0;
+ }
+-static int drm_mode_parse_cmdline_options(const char *str, size_t len,
++static int drm_mode_parse_cmdline_options(const char *str,
+                                         const struct drm_connector *connector,
+                                         struct drm_cmdline_mode *mode)
+ {
+       unsigned int deg, margin, rotation = 0;
+-      const char *sep = str;
++      const char *delim, *option, *sep;
+-      while ((sep = strchr(sep, ','))) {
+-              const char *delim, *option;
+-
+-              option = sep + 1;
++      option = str;
++      do {
+               delim = strchr(option, '=');
+               if (!delim) {
+                       delim = strchr(option, ',');
+                       if (!delim)
+-                              delim = str + len;
++                              delim = option + strlen(option);
+               }
+               if (!strncmp(option, "rotate", delim - option)) {
+@@ -1661,8 +1659,9 @@ static int drm_mode_parse_cmdline_option
+               } else {
+                       return -EINVAL;
+               }
+-              sep = delim;
+-      }
++              sep = strchr(delim, ',');
++              option = sep + 1;
++      } while (sep);
+       if (!(rotation & DRM_MODE_ROTATE_MASK))
+               rotation |= DRM_MODE_ROTATE_0;
+@@ -1862,9 +1861,7 @@ bool drm_mode_parse_command_line_for_con
+       }
+       if (options_ptr) {
+-              int len = strlen(name) - (options_ptr - name);
+-
+-              ret = drm_mode_parse_cmdline_options(options_ptr, len,
++              ret = drm_mode_parse_cmdline_options(options_ptr + 1,
+                                                    connector, mode);
+               if (ret)
+                       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
new file mode 100644 (file)
index 0000000..6ed952b
--- /dev/null
@@ -0,0 +1,49 @@
+From d3c76025a7de614fade5ffcaa8c1d88d8d64213e Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:27 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Add freestanding argument
+ to drm_mode_parse_cmdline_options()
+
+Commit 99e2716e053734b70434502867be24d20a3e2d84 upstream.
+
+Add a freestanding function argument to drm_mode_parse_cmdline_options()
+similar to how drm_mode_parse_cmdline_extra() already has this.
+
+This is a preparation patch for allowing parsing of stand-alone options
+without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-6-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1592,6 +1592,7 @@ static int drm_mode_parse_cmdline_int(co
+ }
+ static int drm_mode_parse_cmdline_options(const char *str,
++                                        bool freestanding,
+                                         const struct drm_connector *connector,
+                                         struct drm_cmdline_mode *mode)
+ {
+@@ -1670,6 +1671,9 @@ static int drm_mode_parse_cmdline_option
+       if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK))
+               return -EINVAL;
++      if (rotation && freestanding)
++              return -EINVAL;
++
+       mode->rotation_reflection = rotation;
+       return 0;
+@@ -1862,6 +1866,7 @@ bool drm_mode_parse_command_line_for_con
+       if (options_ptr) {
+               ret = drm_mode_parse_cmdline_options(options_ptr + 1,
++                                                   false,
+                                                    connector, mode);
+               if (ret)
+                       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch b/target/linux/bcm27xx/patches-5.4/950-0484-drm-modes-parse_cmdline-Rework-drm_mode_parse_cmdlin.patch
deleted file mode 100644 (file)
index 1716ebd..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-From 5b6257773b43e7a7b28f86359d2e9ebe15346b78 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:26 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Rework
- drm_mode_parse_cmdline_options()
-
-Commit 739b200c2edcaaa7a86f37b0c11db57956433dfb upstream.
-
-Refactor drm_mode_parse_cmdline_options() so that it takes a pointer
-to the first option, rather then a pointer to the ',' before the first
-option.
-
-This is a preparation patch for allowing parsing of stand-alone options
-without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-5-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 21 +++++++++------------
- 1 file changed, 9 insertions(+), 12 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,23 +1591,21 @@ static int drm_mode_parse_cmdline_int(co
-       return 0;
- }
--static int drm_mode_parse_cmdline_options(const char *str, size_t len,
-+static int drm_mode_parse_cmdline_options(const char *str,
-                                         const struct drm_connector *connector,
-                                         struct drm_cmdline_mode *mode)
- {
-       unsigned int deg, margin, rotation = 0;
--      const char *sep = str;
-+      const char *delim, *option, *sep;
--      while ((sep = strchr(sep, ','))) {
--              const char *delim, *option;
--
--              option = sep + 1;
-+      option = str;
-+      do {
-               delim = strchr(option, '=');
-               if (!delim) {
-                       delim = strchr(option, ',');
-                       if (!delim)
--                              delim = str + len;
-+                              delim = option + strlen(option);
-               }
-               if (!strncmp(option, "rotate", delim - option)) {
-@@ -1661,8 +1659,9 @@ static int drm_mode_parse_cmdline_option
-               } else {
-                       return -EINVAL;
-               }
--              sep = delim;
--      }
-+              sep = strchr(delim, ',');
-+              option = sep + 1;
-+      } while (sep);
-       if (!(rotation & DRM_MODE_ROTATE_MASK))
-               rotation |= DRM_MODE_ROTATE_0;
-@@ -1862,9 +1861,7 @@ bool drm_mode_parse_command_line_for_con
-       }
-       if (options_ptr) {
--              int len = strlen(name) - (options_ptr - name);
--
--              ret = drm_mode_parse_cmdline_options(options_ptr, len,
-+              ret = drm_mode_parse_cmdline_options(options_ptr + 1,
-                                                    connector, mode);
-               if (ret)
-                       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Add-freestanding-argument-to.patch
deleted file mode 100644 (file)
index 6ed952b..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-From d3c76025a7de614fade5ffcaa8c1d88d8d64213e Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:27 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Add freestanding argument
- to drm_mode_parse_cmdline_options()
-
-Commit 99e2716e053734b70434502867be24d20a3e2d84 upstream.
-
-Add a freestanding function argument to drm_mode_parse_cmdline_options()
-similar to how drm_mode_parse_cmdline_extra() already has this.
-
-This is a preparation patch for allowing parsing of stand-alone options
-without a mode before them, e.g.: video=HDMI-1:margin_right=14,...
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-6-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1592,6 +1592,7 @@ static int drm_mode_parse_cmdline_int(co
- }
- static int drm_mode_parse_cmdline_options(const char *str,
-+                                        bool freestanding,
-                                         const struct drm_connector *connector,
-                                         struct drm_cmdline_mode *mode)
- {
-@@ -1670,6 +1671,9 @@ static int drm_mode_parse_cmdline_option
-       if (!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK))
-               return -EINVAL;
-+      if (rotation && freestanding)
-+              return -EINVAL;
-+
-       mode->rotation_reflection = rotation;
-       return 0;
-@@ -1862,6 +1866,7 @@ bool drm_mode_parse_command_line_for_con
-       if (options_ptr) {
-               ret = drm_mode_parse_cmdline_options(options_ptr + 1,
-+                                                   false,
-                                                    connector, mode);
-               if (ret)
-                       return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch b/target/linux/bcm27xx/patches-5.4/950-0485-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
new file mode 100644 (file)
index 0000000..ac973e1
--- /dev/null
@@ -0,0 +1,64 @@
+From 5b7efd2fa0c75164373d6faf28fec4b89065d39c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:28 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Set bpp/refresh_specified
+ after successful parsing
+
+Commit 6a2d163756545aa3180d7851d5f8322b865e72be upstream.
+
+drm_connector_get_cmdline_mode() calls
+drm_mode_parse_command_line_for_connector() with &connector->cmdline_mode
+as mode argument, so anything which we store in the mode arguments gets
+kept even if we return false.
+
+Avoid storing a possibly false-postive bpp/refresh_specified setting
+in connector->cmdline_mode by moving the setting of these to after
+successful parsing of the bpp/refresh parts of the video= argument.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-7-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1778,10 +1778,8 @@ bool drm_mode_parse_command_line_for_con
+       /* Try to locate the bpp and refresh specifiers, if any */
+       bpp_ptr = strchr(name, '-');
+-      if (bpp_ptr) {
++      if (bpp_ptr)
+               bpp_off = bpp_ptr - name;
+-              mode->bpp_specified = true;
+-      }
+       refresh_ptr = strchr(name, '@');
+       if (refresh_ptr) {
+@@ -1789,7 +1787,6 @@ bool drm_mode_parse_command_line_for_con
+                       return false;
+               refresh_off = refresh_ptr - name;
+-              mode->refresh_specified = true;
+       }
+       /* Locate the start of named options */
+@@ -1832,6 +1829,8 @@ bool drm_mode_parse_command_line_for_con
+               ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
+               if (ret)
+                       return false;
++
++              mode->bpp_specified = true;
+       }
+       if (refresh_ptr) {
+@@ -1839,6 +1838,8 @@ bool drm_mode_parse_command_line_for_con
+                                                    &refresh_end_ptr, mode);
+               if (ret)
+                       return false;
++
++              mode->refresh_specified = true;
+       }
+       /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
new file mode 100644 (file)
index 0000000..746f35f
--- /dev/null
@@ -0,0 +1,247 @@
+From b3212eba63b541206e12c8dc31fc0d99d916b210 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:29 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Allow specifying
+ stand-alone options
+
+Commit 7b1cce760afe38b40f0989cdf10b2190dccf9815 upstream.
+
+Some options which can be specified on the commandline, such as
+margin_right=..., margin_left=..., etc. are applied not only to the
+specified mode, but to all modes. As such it would be nice if the user
+can simply say e.g.
+video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42
+
+This commit refactors drm_mode_parse_command_line_for_connector() to
+add support for this, and as a nice side effect also cleans up the
+function a bit.
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c                   | 92 +++++++------------
+ .../gpu/drm/selftests/drm_cmdline_selftests.h |  2 +
+ .../drm/selftests/test-drm_cmdline_parser.c   | 50 ++++++++++
+ 3 files changed, 86 insertions(+), 58 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1684,17 +1684,6 @@ static const char * const drm_named_mode
+       "PAL",
+ };
+-static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size)
+-{
+-      int i;
+-
+-      for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++)
+-              if (!strncmp(mode, drm_named_modes_whitelist[i], size))
+-                      return true;
+-
+-      return false;
+-}
+-
+ /**
+  * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
+  * @mode_option: optional per connector mode option
+@@ -1725,7 +1714,7 @@ bool drm_mode_parse_command_line_for_con
+                                              struct drm_cmdline_mode *mode)
+ {
+       const char *name;
+-      bool named_mode = false, parse_extras = false;
++      bool freestanding = false, parse_extras = false;
+       unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
+       unsigned int mode_end = 0;
+       const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
+@@ -1745,49 +1734,14 @@ bool drm_mode_parse_command_line_for_con
+       name = mode_option;
+-      /*
+-       * This is a bit convoluted. To differentiate between the
+-       * named modes and poorly formatted resolutions, we need a
+-       * bunch of things:
+-       *   - We need to make sure that the first character (which
+-       *     would be our resolution in X) is a digit.
+-       *   - If not, then it's either a named mode or a force on/off.
+-       *     To distinguish between the two, we need to run the
+-       *     extra parsing function, and if not, then we consider it
+-       *     a named mode.
+-       *
+-       * If this isn't enough, we should add more heuristics here,
+-       * and matching unit-tests.
+-       */
+-      if (!isdigit(name[0]) && name[0] != 'x') {
+-              unsigned int namelen = strlen(name);
+-
+-              /*
+-               * Only the force on/off options can be in that case,
+-               * and they all take a single character.
+-               */
+-              if (namelen == 1) {
+-                      ret = drm_mode_parse_cmdline_extra(name, namelen, true,
+-                                                         connector, mode);
+-                      if (!ret)
+-                              return true;
+-              }
+-
+-              named_mode = true;
+-      }
+-
+       /* Try to locate the bpp and refresh specifiers, if any */
+       bpp_ptr = strchr(name, '-');
+       if (bpp_ptr)
+               bpp_off = bpp_ptr - name;
+       refresh_ptr = strchr(name, '@');
+-      if (refresh_ptr) {
+-              if (named_mode)
+-                      return false;
+-
++      if (refresh_ptr)
+               refresh_off = refresh_ptr - name;
+-      }
+       /* Locate the start of named options */
+       options_ptr = strchr(name, ',');
+@@ -1807,23 +1761,45 @@ bool drm_mode_parse_command_line_for_con
+               parse_extras = true;
+       }
+-      if (named_mode) {
+-              if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
+-                      return false;
+-
+-              if (!drm_named_mode_is_in_whitelist(name, mode_end))
+-                      return false;
++      /* First check for a named mode */
++      for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
++              ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
++              if (ret == mode_end) {
++                      if (refresh_ptr)
++                              return false; /* named + refresh is invalid */
++
++                      strcpy(mode->name, drm_named_modes_whitelist[i]);
++                      mode->specified = true;
++                      break;
++              }
++      }
+-              strscpy(mode->name, name, mode_end + 1);
+-      } else {
++      /* No named mode? Check for a normal mode argument, e.g. 1024x768 */
++      if (!mode->specified && isdigit(name[0])) {
+               ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
+                                                     parse_extras,
+                                                     connector,
+                                                     mode);
+               if (ret)
+                       return false;
++
++              mode->specified = true;
++      }
++
++      /* No mode? Check for freestanding extras and/or options */
++      if (!mode->specified) {
++              unsigned int len = strlen(mode_option);
++
++              if (bpp_ptr || refresh_ptr)
++                      return false; /* syntax error */
++
++              if (len == 1 || (len >= 2 && mode_option[1] == ','))
++                      extra_ptr = mode_option;
++              else
++                      options_ptr = mode_option - 1;
++
++              freestanding = true;
+       }
+-      mode->specified = true;
+       if (bpp_ptr) {
+               ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
+@@ -1859,7 +1835,7 @@ bool drm_mode_parse_command_line_for_con
+               else
+                       len = strlen(extra_ptr);
+-              ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
++              ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,
+                                                  connector, mode);
+               if (ret)
+                       return false;
+@@ -1867,7 +1843,7 @@ bool drm_mode_parse_command_line_for_con
+       if (options_ptr) {
+               ret = drm_mode_parse_cmdline_options(options_ptr + 1,
+-                                                   false,
++                                                   freestanding,
+                                                    connector, mode);
+               if (ret)
+                       return false;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -63,3 +63,5 @@ cmdline_test(drm_cmdline_test_multiple_o
+ cmdline_test(drm_cmdline_test_invalid_option)
+ cmdline_test(drm_cmdline_test_bpp_extra_and_option)
+ cmdline_test(drm_cmdline_test_extra_and_option)
++cmdline_test(drm_cmdline_test_freestanding_options)
++cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1053,6 +1053,56 @@ static int drm_cmdline_test_extra_and_op
+       return 0;
+ }
++static int drm_cmdline_test_freestanding_options(void *ignored)
++{
++      struct drm_cmdline_mode mode = { };
++
++      FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
++                                                         &no_connector,
++                                                         &mode));
++      FAIL_ON(mode.specified);
++      FAIL_ON(mode.refresh_specified);
++      FAIL_ON(mode.bpp_specified);
++
++      FAIL_ON(mode.tv_margins.right != 14);
++      FAIL_ON(mode.tv_margins.left != 24);
++      FAIL_ON(mode.tv_margins.bottom != 36);
++      FAIL_ON(mode.tv_margins.top != 42);
++
++      FAIL_ON(mode.rb);
++      FAIL_ON(mode.cvt);
++      FAIL_ON(mode.interlace);
++      FAIL_ON(mode.margins);
++      FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
++
++      return 0;
++}
++
++static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored)
++{
++      struct drm_cmdline_mode mode = { };
++
++      FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
++                                                         &no_connector,
++                                                         &mode));
++      FAIL_ON(mode.specified);
++      FAIL_ON(mode.refresh_specified);
++      FAIL_ON(mode.bpp_specified);
++
++      FAIL_ON(mode.tv_margins.right != 14);
++      FAIL_ON(mode.tv_margins.left != 24);
++      FAIL_ON(mode.tv_margins.bottom != 36);
++      FAIL_ON(mode.tv_margins.top != 42);
++
++      FAIL_ON(mode.rb);
++      FAIL_ON(mode.cvt);
++      FAIL_ON(mode.interlace);
++      FAIL_ON(mode.margins);
++      FAIL_ON(mode.force != DRM_FORCE_ON);
++
++      return 0;
++}
++
+ #include "drm_selftest.c"
+ static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch b/target/linux/bcm27xx/patches-5.4/950-0486-drm-modes-parse_cmdline-Set-bpp-refresh_specified-af.patch
deleted file mode 100644 (file)
index ac973e1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-From 5b7efd2fa0c75164373d6faf28fec4b89065d39c Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:28 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Set bpp/refresh_specified
- after successful parsing
-
-Commit 6a2d163756545aa3180d7851d5f8322b865e72be upstream.
-
-drm_connector_get_cmdline_mode() calls
-drm_mode_parse_command_line_for_connector() with &connector->cmdline_mode
-as mode argument, so anything which we store in the mode arguments gets
-kept even if we return false.
-
-Avoid storing a possibly false-postive bpp/refresh_specified setting
-in connector->cmdline_mode by moving the setting of these to after
-successful parsing of the bpp/refresh parts of the video= argument.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-7-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 9 +++++----
- 1 file changed, 5 insertions(+), 4 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1778,10 +1778,8 @@ bool drm_mode_parse_command_line_for_con
-       /* Try to locate the bpp and refresh specifiers, if any */
-       bpp_ptr = strchr(name, '-');
--      if (bpp_ptr) {
-+      if (bpp_ptr)
-               bpp_off = bpp_ptr - name;
--              mode->bpp_specified = true;
--      }
-       refresh_ptr = strchr(name, '@');
-       if (refresh_ptr) {
-@@ -1789,7 +1787,6 @@ bool drm_mode_parse_command_line_for_con
-                       return false;
-               refresh_off = refresh_ptr - name;
--              mode->refresh_specified = true;
-       }
-       /* Locate the start of named options */
-@@ -1832,6 +1829,8 @@ bool drm_mode_parse_command_line_for_con
-               ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
-               if (ret)
-                       return false;
-+
-+              mode->bpp_specified = true;
-       }
-       if (refresh_ptr) {
-@@ -1839,6 +1838,8 @@ bool drm_mode_parse_command_line_for_con
-                                                    &refresh_end_ptr, mode);
-               if (ret)
-                       return false;
-+
-+              mode->refresh_specified = true;
-       }
-       /*
diff --git a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch b/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
new file mode 100644 (file)
index 0000000..cbda9ec
--- /dev/null
@@ -0,0 +1,157 @@
+From 7d395633947fa6595a117f40e0f27ba87be77d6c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:30 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Add support for specifying
+ panel_orientation (v2)
+
+Commit 4e7a4a6fbdc669c44e6079f9d5eb25673749455f upstream.
+
+Sometimes we want to override a connector's panel_orientation from the
+kernel commandline. Either for testing and for special cases, e.g. a kiosk
+like setup which uses a TV mounted in portrait mode.
+
+Users can already specify a "rotate" option through a video= kernel cmdline
+option. But that only supports 0/180 degrees (see drm_client_modeset TODO)
+and only works for in kernel modeset clients, not for userspace kms users.
+
+The "panel-orientation" connector property OTOH does support 90/270 degrees
+as it leaves dealing with the rotation up to userspace and this does work
+for userspace kms clients (at least those which support this property).
+
+Changes in v2:
+-Add missing ':' after @panel_orientation (reported by kbuild test robot)
+
+BugLink: https://gitlab.freedesktop.org/plymouth/plymouth/merge_requests/83
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-9-hdegoede@redhat.com
+---
+ Documentation/fb/modedb.rst                   |  3 ++
+ drivers/gpu/drm/drm_modes.c                   | 32 +++++++++++++++++++
+ .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
+ .../drm/selftests/test-drm_cmdline_parser.c   | 22 +++++++++++++
+ include/drm/drm_connector.h                   |  8 +++++
+ 5 files changed, 66 insertions(+)
+
+--- a/Documentation/fb/modedb.rst
++++ b/Documentation/fb/modedb.rst
+@@ -65,6 +65,9 @@ Valid options are::
+   - reflect_y (boolean): Perform an axial symmetry on the Y axis
+   - rotate (integer): Rotate the initial framebuffer by x
+     degrees. Valid values are 0, 90, 180 and 270.
++  - panel_orientation, one of "normal", "upside_down", "left_side_up", or
++    "right_side_up". For KMS drivers only, this sets the "panel orientation"
++    property on the kms connector as hint for kms users.
+ -----------------------------------------------------------------------------
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1591,6 +1591,33 @@ static int drm_mode_parse_cmdline_int(co
+       return 0;
+ }
++static int drm_mode_parse_panel_orientation(const char *delim,
++                                          struct drm_cmdline_mode *mode)
++{
++      const char *value;
++
++      if (*delim != '=')
++              return -EINVAL;
++
++      value = delim + 1;
++      delim = strchr(value, ',');
++      if (!delim)
++              delim = value + strlen(value);
++
++      if (!strncmp(value, "normal", delim - value))
++              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
++      else if (!strncmp(value, "upside_down", delim - value))
++              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
++      else if (!strncmp(value, "left_side_up", delim - value))
++              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
++      else if (!strncmp(value, "right_side_up", delim - value))
++              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
++      else
++              return -EINVAL;
++
++      return 0;
++}
++
+ static int drm_mode_parse_cmdline_options(const char *str,
+                                         bool freestanding,
+                                         const struct drm_connector *connector,
+@@ -1657,6 +1684,9 @@ static int drm_mode_parse_cmdline_option
+                               return -EINVAL;
+                       mode->tv_margins.bottom = margin;
++              } else if (!strncmp(option, "panel_orientation", delim - option)) {
++                      if (drm_mode_parse_panel_orientation(delim, mode))
++                              return -EINVAL;
+               } else {
+                       return -EINVAL;
+               }
+@@ -1722,6 +1752,8 @@ bool drm_mode_parse_command_line_for_con
+       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+       int i, len, ret;
++      mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
++
+ #ifdef CONFIG_FB
+       if (!mode_option)
+               mode_option = fb_mode_option;
+--- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
++++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
+@@ -65,3 +65,4 @@ cmdline_test(drm_cmdline_test_bpp_extra_
+ cmdline_test(drm_cmdline_test_extra_and_option)
+ cmdline_test(drm_cmdline_test_freestanding_options)
+ cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
++cmdline_test(drm_cmdline_test_panel_orientation)
+--- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
++++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
+@@ -1103,6 +1103,28 @@ static int drm_cmdline_test_freestanding
+       return 0;
+ }
++static int drm_cmdline_test_panel_orientation(void *ignored)
++{
++      struct drm_cmdline_mode mode = { };
++
++      FAIL_ON(!drm_mode_parse_command_line_for_connector("panel_orientation=upside_down",
++                                                         &no_connector,
++                                                         &mode));
++      FAIL_ON(mode.specified);
++      FAIL_ON(mode.refresh_specified);
++      FAIL_ON(mode.bpp_specified);
++
++      FAIL_ON(mode.panel_orientation != DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP);
++
++      FAIL_ON(mode.rb);
++      FAIL_ON(mode.cvt);
++      FAIL_ON(mode.interlace);
++      FAIL_ON(mode.margins);
++      FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
++
++      return 0;
++}
++
+ #include "drm_selftest.c"
+ static int __init test_drm_cmdline_init(void)
+--- a/include/drm/drm_connector.h
++++ b/include/drm/drm_connector.h
+@@ -1066,6 +1066,14 @@ struct drm_cmdline_mode {
+       unsigned int rotation_reflection;
+       /**
++       * @panel_orientation:
++       *
++       * drm-connector "panel orientation" property override value,
++       * DRM_MODE_PANEL_ORIENTATION_UNKNOWN if not set.
++       */
++      enum drm_panel_orientation panel_orientation;
++
++      /**
+        * @tv_margins: TV margins to apply to the mode.
+        */
+       struct drm_connector_tv_margins tv_margins;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch b/target/linux/bcm27xx/patches-5.4/950-0487-drm-modes-parse_cmdline-Allow-specifying-stand-alone.patch
deleted file mode 100644 (file)
index 746f35f..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-From b3212eba63b541206e12c8dc31fc0d99d916b210 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:29 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Allow specifying
- stand-alone options
-
-Commit 7b1cce760afe38b40f0989cdf10b2190dccf9815 upstream.
-
-Some options which can be specified on the commandline, such as
-margin_right=..., margin_left=..., etc. are applied not only to the
-specified mode, but to all modes. As such it would be nice if the user
-can simply say e.g.
-video=HDMI-1:margin_right=14,margin_left=24,margin_bottom=36,margin_top=42
-
-This commit refactors drm_mode_parse_command_line_for_connector() to
-add support for this, and as a nice side effect also cleans up the
-function a bit.
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-8-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c                   | 92 +++++++------------
- .../gpu/drm/selftests/drm_cmdline_selftests.h |  2 +
- .../drm/selftests/test-drm_cmdline_parser.c   | 50 ++++++++++
- 3 files changed, 86 insertions(+), 58 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1684,17 +1684,6 @@ static const char * const drm_named_mode
-       "PAL",
- };
--static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size)
--{
--      int i;
--
--      for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++)
--              if (!strncmp(mode, drm_named_modes_whitelist[i], size))
--                      return true;
--
--      return false;
--}
--
- /**
-  * drm_mode_parse_command_line_for_connector - parse command line modeline for connector
-  * @mode_option: optional per connector mode option
-@@ -1725,7 +1714,7 @@ bool drm_mode_parse_command_line_for_con
-                                              struct drm_cmdline_mode *mode)
- {
-       const char *name;
--      bool named_mode = false, parse_extras = false;
-+      bool freestanding = false, parse_extras = false;
-       unsigned int bpp_off = 0, refresh_off = 0, options_off = 0;
-       unsigned int mode_end = 0;
-       const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
-@@ -1745,49 +1734,14 @@ bool drm_mode_parse_command_line_for_con
-       name = mode_option;
--      /*
--       * This is a bit convoluted. To differentiate between the
--       * named modes and poorly formatted resolutions, we need a
--       * bunch of things:
--       *   - We need to make sure that the first character (which
--       *     would be our resolution in X) is a digit.
--       *   - If not, then it's either a named mode or a force on/off.
--       *     To distinguish between the two, we need to run the
--       *     extra parsing function, and if not, then we consider it
--       *     a named mode.
--       *
--       * If this isn't enough, we should add more heuristics here,
--       * and matching unit-tests.
--       */
--      if (!isdigit(name[0]) && name[0] != 'x') {
--              unsigned int namelen = strlen(name);
--
--              /*
--               * Only the force on/off options can be in that case,
--               * and they all take a single character.
--               */
--              if (namelen == 1) {
--                      ret = drm_mode_parse_cmdline_extra(name, namelen, true,
--                                                         connector, mode);
--                      if (!ret)
--                              return true;
--              }
--
--              named_mode = true;
--      }
--
-       /* Try to locate the bpp and refresh specifiers, if any */
-       bpp_ptr = strchr(name, '-');
-       if (bpp_ptr)
-               bpp_off = bpp_ptr - name;
-       refresh_ptr = strchr(name, '@');
--      if (refresh_ptr) {
--              if (named_mode)
--                      return false;
--
-+      if (refresh_ptr)
-               refresh_off = refresh_ptr - name;
--      }
-       /* Locate the start of named options */
-       options_ptr = strchr(name, ',');
-@@ -1807,23 +1761,45 @@ bool drm_mode_parse_command_line_for_con
-               parse_extras = true;
-       }
--      if (named_mode) {
--              if (mode_end + 1 > DRM_DISPLAY_MODE_LEN)
--                      return false;
--
--              if (!drm_named_mode_is_in_whitelist(name, mode_end))
--                      return false;
-+      /* First check for a named mode */
-+      for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
-+              ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
-+              if (ret == mode_end) {
-+                      if (refresh_ptr)
-+                              return false; /* named + refresh is invalid */
-+
-+                      strcpy(mode->name, drm_named_modes_whitelist[i]);
-+                      mode->specified = true;
-+                      break;
-+              }
-+      }
--              strscpy(mode->name, name, mode_end + 1);
--      } else {
-+      /* No named mode? Check for a normal mode argument, e.g. 1024x768 */
-+      if (!mode->specified && isdigit(name[0])) {
-               ret = drm_mode_parse_cmdline_res_mode(name, mode_end,
-                                                     parse_extras,
-                                                     connector,
-                                                     mode);
-               if (ret)
-                       return false;
-+
-+              mode->specified = true;
-+      }
-+
-+      /* No mode? Check for freestanding extras and/or options */
-+      if (!mode->specified) {
-+              unsigned int len = strlen(mode_option);
-+
-+              if (bpp_ptr || refresh_ptr)
-+                      return false; /* syntax error */
-+
-+              if (len == 1 || (len >= 2 && mode_option[1] == ','))
-+                      extra_ptr = mode_option;
-+              else
-+                      options_ptr = mode_option - 1;
-+
-+              freestanding = true;
-       }
--      mode->specified = true;
-       if (bpp_ptr) {
-               ret = drm_mode_parse_cmdline_bpp(bpp_ptr, &bpp_end_ptr, mode);
-@@ -1859,7 +1835,7 @@ bool drm_mode_parse_command_line_for_con
-               else
-                       len = strlen(extra_ptr);
--              ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false,
-+              ret = drm_mode_parse_cmdline_extra(extra_ptr, len, freestanding,
-                                                  connector, mode);
-               if (ret)
-                       return false;
-@@ -1867,7 +1843,7 @@ bool drm_mode_parse_command_line_for_con
-       if (options_ptr) {
-               ret = drm_mode_parse_cmdline_options(options_ptr + 1,
--                                                   false,
-+                                                   freestanding,
-                                                    connector, mode);
-               if (ret)
-                       return false;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -63,3 +63,5 @@ cmdline_test(drm_cmdline_test_multiple_o
- cmdline_test(drm_cmdline_test_invalid_option)
- cmdline_test(drm_cmdline_test_bpp_extra_and_option)
- cmdline_test(drm_cmdline_test_extra_and_option)
-+cmdline_test(drm_cmdline_test_freestanding_options)
-+cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1053,6 +1053,56 @@ static int drm_cmdline_test_extra_and_op
-       return 0;
- }
-+static int drm_cmdline_test_freestanding_options(void *ignored)
-+{
-+      struct drm_cmdline_mode mode = { };
-+
-+      FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
-+                                                         &no_connector,
-+                                                         &mode));
-+      FAIL_ON(mode.specified);
-+      FAIL_ON(mode.refresh_specified);
-+      FAIL_ON(mode.bpp_specified);
-+
-+      FAIL_ON(mode.tv_margins.right != 14);
-+      FAIL_ON(mode.tv_margins.left != 24);
-+      FAIL_ON(mode.tv_margins.bottom != 36);
-+      FAIL_ON(mode.tv_margins.top != 42);
-+
-+      FAIL_ON(mode.rb);
-+      FAIL_ON(mode.cvt);
-+      FAIL_ON(mode.interlace);
-+      FAIL_ON(mode.margins);
-+      FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
-+
-+      return 0;
-+}
-+
-+static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored)
-+{
-+      struct drm_cmdline_mode mode = { };
-+
-+      FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42",
-+                                                         &no_connector,
-+                                                         &mode));
-+      FAIL_ON(mode.specified);
-+      FAIL_ON(mode.refresh_specified);
-+      FAIL_ON(mode.bpp_specified);
-+
-+      FAIL_ON(mode.tv_margins.right != 14);
-+      FAIL_ON(mode.tv_margins.left != 24);
-+      FAIL_ON(mode.tv_margins.bottom != 36);
-+      FAIL_ON(mode.tv_margins.top != 42);
-+
-+      FAIL_ON(mode.rb);
-+      FAIL_ON(mode.cvt);
-+      FAIL_ON(mode.interlace);
-+      FAIL_ON(mode.margins);
-+      FAIL_ON(mode.force != DRM_FORCE_ON);
-+
-+      return 0;
-+}
-+
- #include "drm_selftest.c"
- static int __init test_drm_cmdline_init(void)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch b/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Add-support-for-specifying-p.patch
deleted file mode 100644 (file)
index cbda9ec..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-From 7d395633947fa6595a117f40e0f27ba87be77d6c Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:30 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Add support for specifying
- panel_orientation (v2)
-
-Commit 4e7a4a6fbdc669c44e6079f9d5eb25673749455f upstream.
-
-Sometimes we want to override a connector's panel_orientation from the
-kernel commandline. Either for testing and for special cases, e.g. a kiosk
-like setup which uses a TV mounted in portrait mode.
-
-Users can already specify a "rotate" option through a video= kernel cmdline
-option. But that only supports 0/180 degrees (see drm_client_modeset TODO)
-and only works for in kernel modeset clients, not for userspace kms users.
-
-The "panel-orientation" connector property OTOH does support 90/270 degrees
-as it leaves dealing with the rotation up to userspace and this does work
-for userspace kms clients (at least those which support this property).
-
-Changes in v2:
--Add missing ':' after @panel_orientation (reported by kbuild test robot)
-
-BugLink: https://gitlab.freedesktop.org/plymouth/plymouth/merge_requests/83
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-9-hdegoede@redhat.com
----
- Documentation/fb/modedb.rst                   |  3 ++
- drivers/gpu/drm/drm_modes.c                   | 32 +++++++++++++++++++
- .../gpu/drm/selftests/drm_cmdline_selftests.h |  1 +
- .../drm/selftests/test-drm_cmdline_parser.c   | 22 +++++++++++++
- include/drm/drm_connector.h                   |  8 +++++
- 5 files changed, 66 insertions(+)
-
---- a/Documentation/fb/modedb.rst
-+++ b/Documentation/fb/modedb.rst
-@@ -65,6 +65,9 @@ Valid options are::
-   - reflect_y (boolean): Perform an axial symmetry on the Y axis
-   - rotate (integer): Rotate the initial framebuffer by x
-     degrees. Valid values are 0, 90, 180 and 270.
-+  - panel_orientation, one of "normal", "upside_down", "left_side_up", or
-+    "right_side_up". For KMS drivers only, this sets the "panel orientation"
-+    property on the kms connector as hint for kms users.
- -----------------------------------------------------------------------------
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1591,6 +1591,33 @@ static int drm_mode_parse_cmdline_int(co
-       return 0;
- }
-+static int drm_mode_parse_panel_orientation(const char *delim,
-+                                          struct drm_cmdline_mode *mode)
-+{
-+      const char *value;
-+
-+      if (*delim != '=')
-+              return -EINVAL;
-+
-+      value = delim + 1;
-+      delim = strchr(value, ',');
-+      if (!delim)
-+              delim = value + strlen(value);
-+
-+      if (!strncmp(value, "normal", delim - value))
-+              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
-+      else if (!strncmp(value, "upside_down", delim - value))
-+              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
-+      else if (!strncmp(value, "left_side_up", delim - value))
-+              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
-+      else if (!strncmp(value, "right_side_up", delim - value))
-+              mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
-+      else
-+              return -EINVAL;
-+
-+      return 0;
-+}
-+
- static int drm_mode_parse_cmdline_options(const char *str,
-                                         bool freestanding,
-                                         const struct drm_connector *connector,
-@@ -1657,6 +1684,9 @@ static int drm_mode_parse_cmdline_option
-                               return -EINVAL;
-                       mode->tv_margins.bottom = margin;
-+              } else if (!strncmp(option, "panel_orientation", delim - option)) {
-+                      if (drm_mode_parse_panel_orientation(delim, mode))
-+                              return -EINVAL;
-               } else {
-                       return -EINVAL;
-               }
-@@ -1722,6 +1752,8 @@ bool drm_mode_parse_command_line_for_con
-       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
-       int i, len, ret;
-+      mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
-+
- #ifdef CONFIG_FB
-       if (!mode_option)
-               mode_option = fb_mode_option;
---- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-+++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h
-@@ -65,3 +65,4 @@ cmdline_test(drm_cmdline_test_bpp_extra_
- cmdline_test(drm_cmdline_test_extra_and_option)
- cmdline_test(drm_cmdline_test_freestanding_options)
- cmdline_test(drm_cmdline_test_freestanding_force_e_and_options)
-+cmdline_test(drm_cmdline_test_panel_orientation)
---- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-+++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c
-@@ -1103,6 +1103,28 @@ static int drm_cmdline_test_freestanding
-       return 0;
- }
-+static int drm_cmdline_test_panel_orientation(void *ignored)
-+{
-+      struct drm_cmdline_mode mode = { };
-+
-+      FAIL_ON(!drm_mode_parse_command_line_for_connector("panel_orientation=upside_down",
-+                                                         &no_connector,
-+                                                         &mode));
-+      FAIL_ON(mode.specified);
-+      FAIL_ON(mode.refresh_specified);
-+      FAIL_ON(mode.bpp_specified);
-+
-+      FAIL_ON(mode.panel_orientation != DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP);
-+
-+      FAIL_ON(mode.rb);
-+      FAIL_ON(mode.cvt);
-+      FAIL_ON(mode.interlace);
-+      FAIL_ON(mode.margins);
-+      FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED);
-+
-+      return 0;
-+}
-+
- #include "drm_selftest.c"
- static int __init test_drm_cmdline_init(void)
---- a/include/drm/drm_connector.h
-+++ b/include/drm/drm_connector.h
-@@ -1066,6 +1066,14 @@ struct drm_cmdline_mode {
-       unsigned int rotation_reflection;
-       /**
-+       * @panel_orientation:
-+       *
-+       * drm-connector "panel orientation" property override value,
-+       * DRM_MODE_PANEL_ORIENTATION_UNKNOWN if not set.
-+       */
-+      enum drm_panel_orientation panel_orientation;
-+
-+      /**
-        * @tv_margins: TV margins to apply to the mode.
-        */
-       struct drm_connector_tv_margins tv_margins;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch b/target/linux/bcm27xx/patches-5.4/950-0488-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
new file mode 100644 (file)
index 0000000..fb4a7f1
--- /dev/null
@@ -0,0 +1,38 @@
+From 339666068713986cfe1456175dd8a7514f6ed2ab Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:31 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Remove some unnecessary
+ code (v2)
+
+Commit 5b926617cdef41ce0696e09834991194b1759e28 upstream.
+
+fb_get_options() will return fb_mode_option if no video=<connector-name>
+argument is present on the kernel commandline, so there is no need to also
+do this in drm_mode_parse_command_line_for_connector() as our only caller
+uses fb_get_options() to get the mode_option argument.
+
+Changes in v2:
+-Split out the changes dealing with the initialization of the mode struct
+ into a separate patch
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-10-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1754,11 +1754,6 @@ bool drm_mode_parse_command_line_for_con
+       mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+-#ifdef CONFIG_FB
+-      if (!mode_option)
+-              mode_option = fb_mode_option;
+-#endif
+-
+       if (!mode_option) {
+               mode->specified = false;
+               return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch b/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
new file mode 100644 (file)
index 0000000..372cd0d
--- /dev/null
@@ -0,0 +1,41 @@
+From d89b3f22cf7b6bba8081f6d16c9087019fdcf586 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 18 Nov 2019 16:51:32 +0100
+Subject: [PATCH] drm/modes: parse_cmdline: Explicitly memset the
+ passed in drm_cmdline_mode struct
+
+Commit d1fe276b5115f0d581c3cfe6154633b3547e8aab upstream.
+
+Instead of only setting mode->specified on false on an early exit and
+leaving e.g. mode->bpp_specified and mode->refresh_specified as is,
+lets be consistent and just zero out the entire passed in struct at
+the top of drm_mode_parse_command_line_for_connector()
+
+Changes in v3:
+-Drop "mode->specified = false;" line instead of the "return false;" (oops)
+ This crasher was reported-by: kernel test robot <lkp@intel.com>
+
+Acked-by: Maxime Ripard <mripard@kernel.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-11-hdegoede@redhat.com
+---
+ drivers/gpu/drm/drm_modes.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1752,12 +1752,11 @@ bool drm_mode_parse_command_line_for_con
+       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
+       int i, len, ret;
++      memset(mode, 0, sizeof(*mode));
+       mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+-      if (!mode_option) {
+-              mode->specified = false;
++      if (!mode_option)
+               return false;
+-      }
+       name = mode_option;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch b/target/linux/bcm27xx/patches-5.4/950-0489-drm-modes-parse_cmdline-Remove-some-unnecessary-code.patch
deleted file mode 100644 (file)
index fb4a7f1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-From 339666068713986cfe1456175dd8a7514f6ed2ab Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:31 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Remove some unnecessary
- code (v2)
-
-Commit 5b926617cdef41ce0696e09834991194b1759e28 upstream.
-
-fb_get_options() will return fb_mode_option if no video=<connector-name>
-argument is present on the kernel commandline, so there is no need to also
-do this in drm_mode_parse_command_line_for_connector() as our only caller
-uses fb_get_options() to get the mode_option argument.
-
-Changes in v2:
--Split out the changes dealing with the initialization of the mode struct
- into a separate patch
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-10-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 -----
- 1 file changed, 5 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1754,11 +1754,6 @@ bool drm_mode_parse_command_line_for_con
-       mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
--#ifdef CONFIG_FB
--      if (!mode_option)
--              mode_option = fb_mode_option;
--#endif
--
-       if (!mode_option) {
-               mode->specified = false;
-               return false;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0490-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch b/target/linux/bcm27xx/patches-5.4/950-0490-drm-modes-parse_cmdline-Explicitly-memset-the-passed.patch
deleted file mode 100644 (file)
index 372cd0d..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-From d89b3f22cf7b6bba8081f6d16c9087019fdcf586 Mon Sep 17 00:00:00 2001
-From: Hans de Goede <hdegoede@redhat.com>
-Date: Mon, 18 Nov 2019 16:51:32 +0100
-Subject: [PATCH] drm/modes: parse_cmdline: Explicitly memset the
- passed in drm_cmdline_mode struct
-
-Commit d1fe276b5115f0d581c3cfe6154633b3547e8aab upstream.
-
-Instead of only setting mode->specified on false on an early exit and
-leaving e.g. mode->bpp_specified and mode->refresh_specified as is,
-lets be consistent and just zero out the entire passed in struct at
-the top of drm_mode_parse_command_line_for_connector()
-
-Changes in v3:
--Drop "mode->specified = false;" line instead of the "return false;" (oops)
- This crasher was reported-by: kernel test robot <lkp@intel.com>
-
-Acked-by: Maxime Ripard <mripard@kernel.org>
-Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-Link: https://patchwork.freedesktop.org/patch/msgid/20191118155134.30468-11-hdegoede@redhat.com
----
- drivers/gpu/drm/drm_modes.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
---- a/drivers/gpu/drm/drm_modes.c
-+++ b/drivers/gpu/drm/drm_modes.c
-@@ -1752,12 +1752,11 @@ bool drm_mode_parse_command_line_for_con
-       char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
-       int i, len, ret;
-+      memset(mode, 0, sizeof(*mode));
-       mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
--      if (!mode_option) {
--              mode->specified = false;
-+      if (!mode_option)
-               return false;
--      }
-       name = mode_option;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch b/target/linux/bcm27xx/patches-5.4/950-0490-drm-v3d-Replace-wait_for-macros-to-remove-use-of-msl.patch
new file mode 100644 (file)
index 0000000..830bc11
--- /dev/null
@@ -0,0 +1,89 @@
+From 12b60ef71cc005ee7290f692169d46a7e78df01a Mon Sep 17 00:00:00 2001
+From: Yukimasa Sugizaki <4298265+Terminus-IMRC@users.noreply.github.com>
+Date: Fri, 20 Mar 2020 19:01:23 +0900
+Subject: [PATCH] drm/v3d: Replace wait_for macros to remove use of
+ msleep (#3510)
+
+commit 9daee6141cc9c75b09659b02b1cb9eeb2f5e16cc upstream.
+
+The wait_for macro's for Broadcom V3D driver used msleep, which is
+inappropriate due to its inaccuracy at low values (minimum wait time
+is about 30ms on the Raspberry Pi).  This sleep was triggering in
+v3d_clean_caches(), causing us to only be able to dispatch ~33 compute
+jobs per second.
+
+This patch replaces the macro with the one from the Intel i915 version
+which uses usleep_range to provide more accurate waits.
+
+v2: Split from the vc4 patch so that we can confidently apply to
+    stable (by anholt)
+
+Signed-off-by: James Hughes <james.hughes@raspberrypi.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Link: https://patchwork.freedesktop.org/patch/msgid/20200217153145.13780-1-james.hughes@raspberrypi.com
+Link: https://github.com/raspberrypi/linux/issues/3460
+Fixes: 57692c94dcbe ("drm/v3d: Introduce a new DRM driver for Broadcom V3D V3.x+")
+
+Co-authored-by: James Hughes <james.hughes@raspberrypi.com>
+---
+ drivers/gpu/drm/v3d/v3d_drv.h | 41 ++++++++++++++++++++++++-----------
+ 1 file changed, 28 insertions(+), 13 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_drv.h
++++ b/drivers/gpu/drm/v3d/v3d_drv.h
+@@ -260,27 +260,42 @@ struct v3d_csd_job {
+ };
+ /**
+- * _wait_for - magic (register) wait macro
++ * __wait_for - magic wait macro
+  *
+- * Does the right thing for modeset paths when run under kdgb or similar atomic
+- * contexts. Note that it's important that we check the condition again after
+- * having timed out, since the timeout could be due to preemption or similar and
+- * we've never had a chance to check the condition before the timeout.
++ * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
++ * important that we check the condition again after having timed out, since the
++ * timeout could be due to preemption or similar and we've never had a chance to
++ * check the condition before the timeout.
+  */
+-#define wait_for(COND, MS) ({ \
+-      unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1;   \
+-      int ret__ = 0;                                                  \
+-      while (!(COND)) {                                               \
+-              if (time_after(jiffies, timeout__)) {                   \
+-                      if (!(COND))                                    \
+-                              ret__ = -ETIMEDOUT;                     \
++#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
++      const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (US)); \
++      long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
++      int ret__;                                                      \
++      might_sleep();                                                  \
++      for (;;) {                                                      \
++              const bool expired__ = ktime_after(ktime_get_raw(), end__); \
++              OP;                                                     \
++              /* Guarantee COND check prior to timeout */             \
++              barrier();                                              \
++              if (COND) {                                             \
++                      ret__ = 0;                                      \
+                       break;                                          \
+               }                                                       \
+-              msleep(1);                                      \
++              if (expired__) {                                        \
++                      ret__ = -ETIMEDOUT;                             \
++                      break;                                          \
++              }                                                       \
++              usleep_range(wait__, wait__ * 2);                       \
++              if (wait__ < (Wmax))                                    \
++                      wait__ <<= 1;                                   \
+       }                                                               \
+       ret__;                                                          \
+ })
++#define _wait_for(COND, US, Wmin, Wmax)       __wait_for(, (COND), (US), (Wmin), \
++                                                 (Wmax))
++#define wait_for(COND, MS)            _wait_for((COND), (MS) * 1000, 10, 1000)
++
+ static inline unsigned long nsecs_to_jiffies_timeout(const u64 n)
+ {
+       /* nsecs_to_jiffies64() does not guard against overflow */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch b/target/linux/bcm27xx/patches-5.4/950-0491-Reduce-noise-from-rpi-poe-hat-fan.patch
new file mode 100644 (file)
index 0000000..7c50843
--- /dev/null
@@ -0,0 +1,96 @@
+From 863dace20e48954a7e013a2e88e27c692ce165b0 Mon Sep 17 00:00:00 2001
+From: Nick B <nick@pelagiris.org>
+Date: Mon, 9 Mar 2020 09:05:39 -0400
+Subject: [PATCH] Reduce noise from rpi poe hat fan
+
+This adds 2 extra states, at 40c and 45c, with PWM of 31 and 63 (out
+of 255) for the rpi poe hat fan.  This significantly improves user
+experience by providing a smoother ramp up of the fan, from a pwm 0
+to 31 to 63 then finally to 150, and additionally makes it very easy
+for users to further tweak the values as needed for their specific
+application.
+
+The possible concerns I have are that a hysteresis of 2000 (2c) could
+be too narrow, and that running the fan more at a reduced temperature
+(40000 - 40c) could cause problems.
+
+Signed-off-by: Nick B <nick@pelagiris.org>
+---
+ .../arm/boot/dts/overlays/rpi-poe-overlay.dts | 35 ++++++++++++++++---
+ 1 file changed, 30 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rpi-poe-overlay.dts
+@@ -14,9 +14,9 @@
+                               compatible = "raspberrypi,rpi-poe-fan";
+                               firmware = <&firmware>;
+                               cooling-min-state = <0>;
+-                              cooling-max-state = <2>;
++                              cooling-max-state = <4>;
+                               #cooling-cells = <2>;
+-                              cooling-levels = <0 150 255>;
++                              cooling-levels = <0 31 63 150 255>;
+                               status = "okay";
+                       };
+               };
+@@ -27,12 +27,21 @@
+               __overlay__ {
+                       trips {
+                               trip0: trip0 {
+-                                      temperature = <50000>;
+-                                      hysteresis = <5000>;
++                                      temperature = <40000>;
++                                      hysteresis = <2000>;
+                                       type = "active";
+                               };
+                               trip1: trip1 {
+-
++                                      temperature = <45000>;
++                                      hysteresis = <2000>;
++                                      type = "active";
++                              };
++                              trip2: trip2 {
++                                      temperature = <50000>;
++                                      hysteresis = <2000>;
++                                      type = "active";
++                              };
++                              trip3: trip3 {
+                                       temperature = <55000>;
+                                       hysteresis = <5000>;
+                                       type = "active";
+@@ -47,6 +56,14 @@
+                                       trip = <&trip1>;
+                                       cooling-device = <&fan0 1 2>;
+                               };
++                              map2 {
++                                      trip = <&trip2>;
++                                      cooling-device = <&fan0 2 3>;
++                              };
++                              map3 {
++                                      trip = <&trip3>;
++                                      cooling-device = <&fan0 3 4>;
++                              };
+                       };
+               };
+       };
+@@ -58,6 +75,10 @@
+                       poe_fan_temp0_hyst =    <&trip0>,"hysteresis:0";
+                       poe_fan_temp1 =         <&trip1>,"temperature:0";
+                       poe_fan_temp1_hyst =    <&trip1>,"hysteresis:0";
++                      poe_fan_temp2 =         <&trip2>,"temperature:0";
++                      poe_fan_temp2_hyst =    <&trip2>,"hysteresis:0";
++                      poe_fan_temp3 =         <&trip3>,"temperature:0";
++                      poe_fan_temp3_hyst =    <&trip3>,"hysteresis:0";
+               };
+       };
+@@ -66,5 +87,9 @@
+               poe_fan_temp0_hyst =    <&trip0>,"hysteresis:0";
+               poe_fan_temp1 =         <&trip1>,"temperature:0";
+               poe_fan_temp1_hyst =    <&trip1>,"hysteresis:0";
++              poe_fan_temp2 =         <&trip2>,"temperature:0";
++              poe_fan_temp2_hyst =    <&trip2>,"hysteresis:0";
++              poe_fan_temp3 =         <&trip3>,"temperature:0";
++              poe_fan_temp3_hyst =    <&trip3>,"hysteresis:0";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0491-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0491-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
deleted file mode 100644 (file)
index e85329a..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
-Date: Sat, 7 Mar 2020 22:37:52 +0100
-Subject: [PATCH] add Sensirion SPS30 to i2c-sensor overlay
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Add support for Sensirion SPS30 particulate matter sensor with fixed
-address 0x69.
-
-Signed-off-by: Petr Štetiar <ynezz@true.cz>
-
-diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README
-index 62ad35f78bad..0d7d00ac92c4 100644
---- a/arch/arm/boot/dts/overlays/README
-+++ b/arch/arm/boot/dts/overlays/README
-@@ -1261,6 +1261,9 @@ Params: addr                    Set the address for the BME280, BME680, BMP280,
-         si7020                  Select the Silicon Labs Si7013/20/21 humidity/
-                                 temperature sensor
-+        sps30                   Select the Sensirion SPS30 particulate matter
-+                                sensor. Fixed address 0x69.
-+
-         tmp102                  Select the Texas Instruments TMP102 temp sensor
-                                 Valid addresses 0x48-0x4b, default 0x48
-diff --git a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-index 40881d72a157..ce97837b0db5 100644
---- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-+++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
-@@ -231,6 +231,20 @@
-               };
-       };
-+      fragment@15 {
-+              target = <&i2c_arm>;
-+              __dormant__ {
-+                      #address-cells = <1>;
-+                      #size-cells = <0>;
-+                      status = "okay";
-+
-+                      sps30: sps30@69 {
-+                              compatible = "sensirion,sps30";
-+                              reg = <0x69>;
-+                              status = "okay";
-+                      };
-+              };
-+      };
-       __overrides__ {
-               addr =  <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
-@@ -252,5 +266,6 @@
-               ds1621 = <0>,"+12";
-               max17040 = <0>,"+13";
-               bme680 = <0>,"+14";
-+              sps30 = <0>,"+15";
-       };
- };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-5.4/950-0492-add-Sensirion-SPS30-to-i2c-sensor-overlay.patch
new file mode 100644 (file)
index 0000000..72941b5
--- /dev/null
@@ -0,0 +1,59 @@
+From 60f3874207c50db6f6d9dbac40977843cb77acd5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Petr=20=C5=A0tetiar?= <ynezz@true.cz>
+Date: Sat, 7 Mar 2020 22:37:52 +0100
+Subject: [PATCH] add Sensirion SPS30 to i2c-sensor overlay
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add support for Sensirion SPS30 particulate matter sensor with fixed
+address 0x69.
+
+Signed-off-by: Petr Štetiar <ynezz@true.cz>
+---
+ arch/arm/boot/dts/overlays/README                 |  3 +++
+ arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts | 15 +++++++++++++++
+ 2 files changed, 18 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1261,6 +1261,9 @@ Params: addr                    Set the
+         si7020                  Select the Silicon Labs Si7013/20/21 humidity/
+                                 temperature sensor
++        sps30                   Select the Sensirion SPS30 particulate matter
++                                sensor. Fixed address 0x69.
++
+         tmp102                  Select the Texas Instruments TMP102 temp sensor
+                                 Valid addresses 0x48-0x4b, default 0x48
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-overlay.dts
+@@ -231,6 +231,20 @@
+               };
+       };
++      fragment@15 {
++              target = <&i2c_arm>;
++              __dormant__ {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      status = "okay";
++
++                      sps30: sps30@69 {
++                              compatible = "sensirion,sps30";
++                              reg = <0x69>;
++                              status = "okay";
++                      };
++              };
++      };
+       __overrides__ {
+               addr =  <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
+@@ -252,5 +266,6 @@
+               ds1621 = <0>,"+12";
+               max17040 = <0>,"+13";
+               bme680 = <0>,"+14";
++              sps30 = <0>,"+15";
+       };
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch b/target/linux/bcm27xx/patches-5.4/950-0493-media-add-V4L2_CTRL_TYPE_AREA-control-type.patch
new file mode 100644 (file)
index 0000000..265533e
--- /dev/null
@@ -0,0 +1,157 @@
+From 4af6218f1d01e5ae54dc43e4bd2421617c777570 Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:31 -0300
+Subject: [PATCH] media: add V4L2_CTRL_TYPE_AREA control type
+
+Commit d1dc49370f8371b00e682ac409aa1987ce641e93 upstream.
+
+This type contains the width and the height of a rectangular area.
+
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-ctrls.c | 21 ++++++++++++++
+ include/media/v4l2-ctrls.h           | 42 ++++++++++++++++++++++++++++
+ include/uapi/linux/videodev2.h       |  6 ++++
+ 3 files changed, 69 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -1673,6 +1673,7 @@ static int std_validate_compound(const s
+ {
+       struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params;
+       struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++      struct v4l2_area *area;
+       void *p = ptr.p + idx * ctrl->elem_size;
+       switch ((u32)ctrl->type) {
+@@ -1749,6 +1750,11 @@ static int std_validate_compound(const s
+               zero_padding(p_vp8_frame_header->entropy_header);
+               zero_padding(p_vp8_frame_header->coder_state);
+               break;
++      case V4L2_CTRL_TYPE_AREA:
++              area = p;
++              if (!area->width || !area->height)
++                      return -EINVAL;
++              break;
+       default:
+               return -EINVAL;
+       }
+@@ -2422,6 +2428,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+       case V4L2_CTRL_TYPE_VP8_FRAME_HEADER:
+               elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header);
+               break;
++      case V4L2_CTRL_TYPE_AREA:
++              elem_size = sizeof(struct v4l2_area);
++              break;
+       default:
+               if (type < V4L2_CTRL_COMPOUND_TYPES)
+                       elem_size = sizeof(s32);
+@@ -4086,6 +4095,18 @@ int __v4l2_ctrl_s_ctrl_string(struct v4l
+ }
+ EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++                          const struct v4l2_area *area)
++{
++      lockdep_assert_held(ctrl->handler->lock);
++
++      /* It's a driver bug if this happens. */
++      WARN_ON(ctrl->type != V4L2_CTRL_TYPE_AREA);
++      *ctrl->p_new.p_area = *area;
++      return set_ctrl(NULL, ctrl, 0);
++}
++EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_area);
++
+ void v4l2_ctrl_request_complete(struct media_request *req,
+                               struct v4l2_ctrl_handler *main_hdl)
+ {
+--- a/include/media/v4l2-ctrls.h
++++ b/include/media/v4l2-ctrls.h
+@@ -50,6 +50,7 @@ struct poll_table_struct;
+  * @p_h264_slice_params:      Pointer to a struct v4l2_ctrl_h264_slice_params.
+  * @p_h264_decode_params:     Pointer to a struct v4l2_ctrl_h264_decode_params.
+  * @p_vp8_frame_header:               Pointer to a VP8 frame header structure.
++ * @p_area:                   Pointer to an area.
+  * @p:                                Pointer to a compound value.
+  */
+ union v4l2_ctrl_ptr {
+@@ -68,6 +69,7 @@ union v4l2_ctrl_ptr {
+       struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
+       struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
+       struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++      struct v4l2_area *p_area;
+       void *p;
+ };
+@@ -1063,6 +1065,46 @@ static inline int v4l2_ctrl_s_ctrl_strin
+       v4l2_ctrl_unlock(ctrl);
+       return rval;
++}
++
++/**
++ * __v4l2_ctrl_s_ctrl_area() - Unlocked variant of v4l2_ctrl_s_ctrl_area().
++ *
++ * @ctrl:     The control.
++ * @area:     The new area.
++ *
++ * This sets the control's new area safely by going through the control
++ * framework. This function assumes the control's handler is already locked,
++ * allowing it to be used from within the &v4l2_ctrl_ops functions.
++ *
++ * This function is for area type controls only.
++ */
++int __v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++                          const struct v4l2_area *area);
++
++/**
++ * v4l2_ctrl_s_ctrl_area() - Helper function to set a control's area value
++ *     from within a driver.
++ *
++ * @ctrl:     The control.
++ * @area:     The new area.
++ *
++ * This sets the control's new area safely by going through the control
++ * framework. This function will lock the control's handler, so it cannot be
++ * used from within the &v4l2_ctrl_ops functions.
++ *
++ * This function is for area type controls only.
++ */
++static inline int v4l2_ctrl_s_ctrl_area(struct v4l2_ctrl *ctrl,
++                                      const struct v4l2_area *area)
++{
++      int rval;
++
++      v4l2_ctrl_lock(ctrl);
++      rval = __v4l2_ctrl_s_ctrl_area(ctrl, area);
++      v4l2_ctrl_unlock(ctrl);
++
++      return rval;
+ }
+ /* Internal helper functions that deal with control events. */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -427,6 +427,11 @@ struct v4l2_fract {
+       __u32   denominator;
+ };
++struct v4l2_area {
++      __u32   width;
++      __u32   height;
++};
++
+ /**
+   * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
+   *
+@@ -1725,6 +1730,7 @@ enum v4l2_ctrl_type {
+       V4L2_CTRL_TYPE_U8            = 0x0100,
+       V4L2_CTRL_TYPE_U16           = 0x0101,
+       V4L2_CTRL_TYPE_U32           = 0x0102,
++      V4L2_CTRL_TYPE_AREA          = 0x0106,
+ };
+ /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch b/target/linux/bcm27xx/patches-5.4/950-0494-media-add-V4L2_CID_UNIT_CELL_SIZE-control.patch
new file mode 100644 (file)
index 0000000..0c860c7
--- /dev/null
@@ -0,0 +1,52 @@
+From 12eba72027d415bb3dfd4c8124813a322b27c793 Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:33 -0300
+Subject: [PATCH] media: add V4L2_CID_UNIT_CELL_SIZE control
+
+Commit 61fd036d01111679b01e4b92e6bd0cdd33809aea upstream.
+
+This control returns the unit cell size in nanometres. The struct provides
+the width and the height in separated fields to take into consideration
+asymmetric pixels and/or hardware binning.
+This control is required for automatic calibration of sensors/cameras.
+
+Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-ctrls.c | 5 +++++
+ include/uapi/linux/v4l2-controls.h   | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -995,6 +995,7 @@ const char *v4l2_ctrl_get_name(u32 id)
+       case V4L2_CID_AUTO_FOCUS_RANGE:         return "Auto Focus, Range";
+       case V4L2_CID_PAN_SPEED:                return "Pan, Speed";
+       case V4L2_CID_TILT_SPEED:               return "Tilt, Speed";
++      case V4L2_CID_UNIT_CELL_SIZE:           return "Unit Cell Size";
+       /* FM Radio Modulator controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
+@@ -1376,6 +1377,10 @@ void v4l2_ctrl_fill(u32 id, const char *
+       case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER:
+               *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER;
+               break;
++      case V4L2_CID_UNIT_CELL_SIZE:
++              *type = V4L2_CTRL_TYPE_AREA;
++              *flags |= V4L2_CTRL_FLAG_READ_ONLY;
++              break;
+       default:
+               *type = V4L2_CTRL_TYPE_INTEGER;
+               break;
+--- a/include/uapi/linux/v4l2-controls.h
++++ b/include/uapi/linux/v4l2-controls.h
+@@ -1035,6 +1035,7 @@ enum v4l2_jpeg_chroma_subsampling {
+ #define V4L2_CID_TEST_PATTERN_GREENR          (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 5)
+ #define V4L2_CID_TEST_PATTERN_BLUE            (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 6)
+ #define V4L2_CID_TEST_PATTERN_GREENB          (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 7)
++#define V4L2_CID_UNIT_CELL_SIZE                       (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 8)
+ /* Image processing controls */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch b/target/linux/bcm27xx/patches-5.4/950-0495-media-v4l2-common-add-pixel-encoding-support.patch
new file mode 100644 (file)
index 0000000..aa127ab
--- /dev/null
@@ -0,0 +1,228 @@
+From c63ea6a840ad87e32239eb6b771ac8bbc3279b54 Mon Sep 17 00:00:00 2001
+From: Benoit Parrot <bparrot@ti.com>
+Date: Mon, 7 Oct 2019 12:10:07 -0300
+Subject: [PATCH] media: v4l2-common: add pixel encoding support
+
+Commit d5a897c8428b38053df4b427a4277b1a0722bfa0 upstream.
+
+It is often useful to figure out if a pixel_format is either YUV or RGB
+especially for driver who can perform the pixel encoding conversion.
+
+Instead of having each driver implement its own "is_this_yuv/rgb"
+function based on a restricted set of pixel value, it is better to do
+this in centralized manner.
+
+We therefore add a pixel_enc member to the v4l2_format_info structure to
+quickly identify the related pixel encoding.
+And add helper functions to check pixel encoding.
+
+Signed-off-by: Benoit Parrot <bparrot@ti.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-common.c | 126 +++++++++++++-------------
+ include/media/v4l2-common.h           |  33 ++++++-
+ 2 files changed, 95 insertions(+), 64 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-common.c
++++ b/drivers/media/v4l2-core/v4l2-common.c
+@@ -236,77 +236,77 @@ const struct v4l2_format_info *v4l2_form
+ {
+       static const struct v4l2_format_info formats[] = {
+               /* RGB formats */
+-              { .format = V4L2_PIX_FMT_BGR24,   .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_RGB24,   .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_HSV24,   .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_BGR32,   .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_XBGR32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_BGRX32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_RGB32,   .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_XRGB32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_RGBX32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_HSV32,   .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_ARGB32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_RGBA32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_ABGR32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_BGRA32,  .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_GREY,    .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_BGR24,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGB24,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_HSV24,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_BGR32,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_XBGR32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_BGRX32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGB32,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_XRGB32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGBX32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_HSV32,   .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_ARGB32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGBA32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_ABGR32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_BGRA32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_GREY,    .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+               /* YUV packed formats */
+-              { .format = V4L2_PIX_FMT_YUYV,    .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_YVYU,    .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_UYVY,    .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_VYUY,    .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YUYV,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YVYU,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_UYVY,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_VYUY,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+               /* YUV planar formats */
+-              { .format = V4L2_PIX_FMT_NV12,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_NV21,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_NV16,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_NV61,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_NV24,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_NV42,    .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-
+-              { .format = V4L2_PIX_FMT_YUV410,  .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
+-              { .format = V4L2_PIX_FMT_YVU410,  .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
+-              { .format = V4L2_PIX_FMT_YUV411P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_YUV420,  .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_YVU420,  .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_YUV422P, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_NV12,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_NV21,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_NV16,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_NV61,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_NV24,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_NV42,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++
++              { .format = V4L2_PIX_FMT_YUV410,  .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
++              { .format = V4L2_PIX_FMT_YVU410,  .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 4 },
++              { .format = V4L2_PIX_FMT_YUV411P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 4, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YUV420,  .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_YVU420,  .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+               /* YUV planar formats, non contiguous variant */
+-              { .format = V4L2_PIX_FMT_YUV420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_YVU420M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_YUV422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_YVU422M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_YUV444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_YVU444M, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
+-
+-              { .format = V4L2_PIX_FMT_NV12M,   .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_NV21M,   .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
+-              { .format = V4L2_PIX_FMT_NV16M,   .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_NV61M,   .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
++
++              { .format = V4L2_PIX_FMT_NV12M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_NV21M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
++              { .format = V4L2_PIX_FMT_NV16M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_NV61M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+               /* Bayer RGB formats */
+-              { .format = V4L2_PIX_FMT_SBGGR8,        .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGBRG8,        .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGRBG8,        .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SRGGB8,        .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SBGGR10,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGBRG10,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGRBG10,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SRGGB10,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SBGGR10ALAW8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGBRG10ALAW8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGRBG10ALAW8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SRGGB10ALAW8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SBGGR10DPCM8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGBRG10DPCM8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGRBG10DPCM8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SRGGB10DPCM8,  .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SBGGR12,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGBRG12,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SGRBG12,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+-              { .format = V4L2_PIX_FMT_SRGGB12,       .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SBGGR8,        .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGBRG8,        .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGRBG8,        .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SRGGB8,        .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SBGGR10,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGBRG10,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGRBG10,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SRGGB10,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SBGGR10ALAW8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGBRG10ALAW8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGRBG10ALAW8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SRGGB10ALAW8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SBGGR10DPCM8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGBRG10DPCM8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGRBG10DPCM8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SRGGB10DPCM8,  .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SBGGR12,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGBRG12,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SGRBG12,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_SRGGB12,       .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+       };
+       unsigned int i;
+--- a/include/media/v4l2-common.h
++++ b/include/media/v4l2-common.h
+@@ -457,8 +457,24 @@ int v4l2_s_parm_cap(struct video_device
+ /* Pixel format and FourCC helpers */
+ /**
++ * enum v4l2_pixel_encoding - specifies the pixel encoding value
++ *
++ * @V4L2_PIXEL_ENC_UNKNOWN:   Pixel encoding is unknown/un-initialized
++ * @V4L2_PIXEL_ENC_YUV:               Pixel encoding is YUV
++ * @V4L2_PIXEL_ENC_RGB:               Pixel encoding is RGB
++ * @V4L2_PIXEL_ENC_BAYER:     Pixel encoding is Bayer
++ */
++enum v4l2_pixel_encoding {
++      V4L2_PIXEL_ENC_UNKNOWN = 0,
++      V4L2_PIXEL_ENC_YUV = 1,
++      V4L2_PIXEL_ENC_RGB = 2,
++      V4L2_PIXEL_ENC_BAYER = 3,
++};
++
++/**
+  * struct v4l2_format_info - information about a V4L2 format
+  * @format: 4CC format identifier (V4L2_PIX_FMT_*)
++ * @pixel_enc: Pixel encoding (see enum v4l2_pixel_encoding above)
+  * @mem_planes: Number of memory planes, which includes the alpha plane (1 to 4).
+  * @comp_planes: Number of component planes, which includes the alpha plane (1 to 4).
+  * @bpp: Array of per-plane bytes per pixel
+@@ -469,6 +485,7 @@ int v4l2_s_parm_cap(struct video_device
+  */
+ struct v4l2_format_info {
+       u32 format;
++      u8 pixel_enc;
+       u8 mem_planes;
+       u8 comp_planes;
+       u8 bpp[4];
+@@ -478,8 +495,22 @@ struct v4l2_format_info {
+       u8 block_h[4];
+ };
+-const struct v4l2_format_info *v4l2_format_info(u32 format);
++static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
++{
++      return f && f->pixel_enc == V4L2_PIXEL_ENC_RGB;
++}
++
++static inline bool v4l2_is_format_yuv(const struct v4l2_format_info *f)
++{
++      return f && f->pixel_enc == V4L2_PIXEL_ENC_YUV;
++}
++static inline bool v4l2_is_format_bayer(const struct v4l2_format_info *f)
++{
++      return f && f->pixel_enc == V4L2_PIXEL_ENC_BAYER;
++}
++
++const struct v4l2_format_info *v4l2_format_info(u32 format);
+ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
+                                   const struct v4l2_frmsize_stepwise *frmsize);
+ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
diff --git a/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch b/target/linux/bcm27xx/patches-5.4/950-0496-media-v4l2-common-add-RGB565-and-RGB55-to-v4l2_forma.patch
new file mode 100644 (file)
index 0000000..0171cdf
--- /dev/null
@@ -0,0 +1,28 @@
+From 560f3a9051578499e72ce4b1beaedd007ff46f96 Mon Sep 17 00:00:00 2001
+From: Benoit Parrot <bparrot@ti.com>
+Date: Mon, 7 Oct 2019 12:10:08 -0300
+Subject: [PATCH] media: v4l2-common: add RGB565 and RGB55 to
+ v4l2_format_info
+
+Commit b373f84d77e1c409aacb4ff5bb5726c45fc8b166 upstream.
+
+Add RGB565 and RGB555 to the v4l2_format_info table.
+
+Signed-off-by: Benoit Parrot <bparrot@ti.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-common.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-common.c
++++ b/drivers/media/v4l2-core/v4l2-common.c
+@@ -251,6 +251,8 @@ const struct v4l2_format_info *v4l2_form
+               { .format = V4L2_PIX_FMT_ABGR32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+               { .format = V4L2_PIX_FMT_BGRA32,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+               { .format = V4L2_PIX_FMT_GREY,    .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGB565,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
++              { .format = V4L2_PIX_FMT_RGB555,  .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
+               /* YUV packed formats */
+               { .format = V4L2_PIX_FMT_YUYV,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .hdiv = 2, .vdiv = 1 },
diff --git a/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch b/target/linux/bcm27xx/patches-5.4/950-0497-media-vb2-add-V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF.patch
new file mode 100644 (file)
index 0000000..b114aef
--- /dev/null
@@ -0,0 +1,184 @@
+From dfcdc4ed9a514cd5d77dd18c6527f257f8aaf378 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:40 -0300
+Subject: [PATCH] media: vb2: add V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF
+
+This patch adds support for the V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF
+flag.
+
+It also adds a new V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF
+capability.
+
+Drivers should set vb2_queue->subsystem_flags to
+VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF to indicate support
+for this flag.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/buffer.rst         | 13 +++++++++++++
+ Documentation/media/uapi/v4l/vidioc-reqbufs.rst |  6 ++++++
+ drivers/media/common/videobuf2/videobuf2-v4l2.c | 12 ++++++++++--
+ include/media/videobuf2-core.h                  |  3 +++
+ include/media/videobuf2-v4l2.h                  |  5 +++++
+ include/uapi/linux/videodev2.h                  | 13 ++++++++-----
+ 6 files changed, 45 insertions(+), 7 deletions(-)
+
+--- a/Documentation/media/uapi/v4l/buffer.rst
++++ b/Documentation/media/uapi/v4l/buffer.rst
+@@ -607,6 +607,19 @@ Buffer Flags
+       applications shall use this flag for output buffers if the data in
+       this buffer has not been created by the CPU but by some
+       DMA-capable unit, in which case caches have not been used.
++    * .. _`V4L2-BUF-FLAG-M2M-HOLD-CAPTURE-BUF`:
++
++      - ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF``
++      - 0x00000200
++      - Only valid if ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF`` is
++      set. It is typically used with stateless decoders where multiple
++      output buffers each decode to a slice of the decoded frame.
++      Applications can set this flag when queueing the output buffer
++      to prevent the driver from dequeueing the capture buffer after
++      the output buffer has been decoded (i.e. the capture buffer is
++      'held'). If the timestamp of this output buffer differs from that
++      of the previous output buffer, then that indicates the start of a
++      new frame and the previously held capture buffer is dequeued.
+     * .. _`V4L2-BUF-FLAG-LAST`:
+       - ``V4L2_BUF_FLAG_LAST``
+--- a/Documentation/media/uapi/v4l/vidioc-reqbufs.rst
++++ b/Documentation/media/uapi/v4l/vidioc-reqbufs.rst
+@@ -125,6 +125,7 @@ aborting or finishing any DMA in progres
+ .. _V4L2-BUF-CAP-SUPPORTS-DMABUF:
+ .. _V4L2-BUF-CAP-SUPPORTS-REQUESTS:
+ .. _V4L2-BUF-CAP-SUPPORTS-ORPHANED-BUFS:
++.. _V4L2-BUF-CAP-SUPPORTS-M2M-HOLD-CAPTURE-BUF:
+ .. cssclass:: longtable
+@@ -150,6 +151,11 @@ aborting or finishing any DMA in progres
+       - The kernel allows calling :ref:`VIDIOC_REQBUFS` while buffers are still
+         mapped or exported via DMABUF. These orphaned buffers will be freed
+         when they are unmapped or when the exported DMABUF fds are closed.
++    * - ``V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF``
++      - 0x00000020
++      - Only valid for stateless decoders. If set, then userspace can set the
++        ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag to hold off on returning the
++      capture buffer until the OUTPUT timestamp changes.
+ Return Value
+ ============
+--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
++++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
+@@ -49,8 +49,11 @@ module_param(debug, int, 0644);
+                                V4L2_BUF_FLAG_REQUEST_FD | \
+                                V4L2_BUF_FLAG_TIMESTAMP_MASK)
+ /* Output buffer flags that should be passed on to the driver */
+-#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME | \
+-                               V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_TIMECODE)
++#define V4L2_BUFFER_OUT_FLAGS (V4L2_BUF_FLAG_PFRAME | \
++                               V4L2_BUF_FLAG_BFRAME | \
++                               V4L2_BUF_FLAG_KEYFRAME | \
++                               V4L2_BUF_FLAG_TIMECODE | \
++                               V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF)
+ /*
+  * __verify_planes_array() - verify that the planes array passed in struct
+@@ -194,6 +197,7 @@ static int vb2_fill_vb2_v4l2_buffer(stru
+       }
+       vbuf->sequence = 0;
+       vbuf->request_fd = -1;
++      vbuf->is_held = false;
+       if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+               switch (b->memory) {
+@@ -321,6 +325,8 @@ static int vb2_fill_vb2_v4l2_buffer(stru
+                */
+               vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
+               vbuf->field = b->field;
++              if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
++                      vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+       } else {
+               /* Zero any output buffer flags as this is a capture buffer */
+               vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
+@@ -654,6 +660,8 @@ static void fill_buf_caps(struct vb2_que
+               *caps |= V4L2_BUF_CAP_SUPPORTS_USERPTR;
+       if (q->io_modes & VB2_DMABUF)
+               *caps |= V4L2_BUF_CAP_SUPPORTS_DMABUF;
++      if (q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
++              *caps |= V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
+ #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API
+       if (q->supports_requests)
+               *caps |= V4L2_BUF_CAP_SUPPORTS_REQUESTS;
+--- a/include/media/videobuf2-core.h
++++ b/include/media/videobuf2-core.h
+@@ -505,6 +505,8 @@ struct vb2_buf_ops {
+  * @buf_ops:  callbacks to deliver buffer information.
+  *            between user-space and kernel-space.
+  * @drv_priv: driver private data.
++ * @subsystem_flags: Flags specific to the subsystem (V4L2/DVB/etc.). Not used
++ *            by the vb2 core.
+  * @buf_struct_size: size of the driver-specific buffer structure;
+  *            "0" indicates the driver doesn't want to use a custom buffer
+  *            structure type. for example, ``sizeof(struct vb2_v4l2_buffer)``
+@@ -571,6 +573,7 @@ struct vb2_queue {
+       const struct vb2_buf_ops        *buf_ops;
+       void                            *drv_priv;
++      u32                             subsystem_flags;
+       unsigned int                    buf_struct_size;
+       u32                             timestamp_flags;
+       gfp_t                           gfp_flags;
+--- a/include/media/videobuf2-v4l2.h
++++ b/include/media/videobuf2-v4l2.h
+@@ -33,6 +33,7 @@
+  * @timecode: frame timecode.
+  * @sequence: sequence count of this frame.
+  * @request_fd:       the request_fd associated with this buffer
++ * @is_held:  if true, then this capture buffer was held
+  * @planes:   plane information (userptr/fd, length, bytesused, data_offset).
+  *
+  * Should contain enough information to be able to cover all the fields
+@@ -46,9 +47,13 @@ struct vb2_v4l2_buffer {
+       struct v4l2_timecode    timecode;
+       __u32                   sequence;
+       __s32                   request_fd;
++      bool                    is_held;
+       struct vb2_plane        planes[VB2_MAX_PLANES];
+ };
++/* VB2 V4L2 flags as set in vb2_queue.subsystem_flags */
++#define VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF (1 << 0)
++
+ /*
+  * to_vb2_v4l2_buffer() - cast struct vb2_buffer * to struct vb2_v4l2_buffer *
+  */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -925,11 +925,12 @@ struct v4l2_requestbuffers {
+ };
+ /* capabilities for struct v4l2_requestbuffers and v4l2_create_buffers */
+-#define V4L2_BUF_CAP_SUPPORTS_MMAP    (1 << 0)
+-#define V4L2_BUF_CAP_SUPPORTS_USERPTR (1 << 1)
+-#define V4L2_BUF_CAP_SUPPORTS_DMABUF  (1 << 2)
+-#define V4L2_BUF_CAP_SUPPORTS_REQUESTS        (1 << 3)
+-#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS (1 << 4)
++#define V4L2_BUF_CAP_SUPPORTS_MMAP                    (1 << 0)
++#define V4L2_BUF_CAP_SUPPORTS_USERPTR                 (1 << 1)
++#define V4L2_BUF_CAP_SUPPORTS_DMABUF                  (1 << 2)
++#define V4L2_BUF_CAP_SUPPORTS_REQUESTS                        (1 << 3)
++#define V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS           (1 << 4)
++#define V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF    (1 << 5)
+ /**
+  * struct v4l2_plane - plane info for multi-planar buffers
+@@ -1051,6 +1052,8 @@ static inline __u64 v4l2_timeval_to_ns(c
+ #define V4L2_BUF_FLAG_IN_REQUEST              0x00000080
+ /* timecode field is valid */
+ #define V4L2_BUF_FLAG_TIMECODE                        0x00000100
++/* Don't return the capture buffer until OUTPUT timestamp changes */
++#define V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF    0x00000200
+ /* Buffer is prepared for queuing */
+ #define V4L2_BUF_FLAG_PREPARED                        0x00000400
+ /* Cache handling flags */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch b/target/linux/bcm27xx/patches-5.4/950-0498-media-v4l2-mem2mem-support-held-capture-buffers.patch
new file mode 100644 (file)
index 0000000..bb66baf
--- /dev/null
@@ -0,0 +1,260 @@
+From dc9b786e4b9a1262b536b3c9d0fa88e34a2b3f8f Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:41 -0300
+Subject: [PATCH] media: v4l2-mem2mem: support held capture buffers
+
+Commit f8cca8c97a63d77f48334cde81d15014f43530ef upstream.
+
+Check for held buffers that are ready to be returned to vb2 in
+__v4l2_m2m_try_queue(). This avoids drivers having to handle this
+case.
+
+Add v4l2_m2m_buf_done_and_job_finish() to correctly return source
+and destination buffers and mark the job as finished while taking
+a held destination buffer into account (i.e. that buffer won't be
+returned). This has to be done while job_spinlock is held to avoid
+race conditions.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 130 ++++++++++++++++++-------
+ include/media/v4l2-mem2mem.h           |  33 ++++++-
+ 2 files changed, 128 insertions(+), 35 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -284,7 +284,8 @@ static void v4l2_m2m_try_run(struct v4l2
+ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev,
+                                struct v4l2_m2m_ctx *m2m_ctx)
+ {
+-      unsigned long flags_job, flags_out, flags_cap;
++      unsigned long flags_job;
++      struct vb2_v4l2_buffer *dst, *src;
+       dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+@@ -307,20 +308,30 @@ static void __v4l2_m2m_try_queue(struct
+               goto job_unlock;
+       }
+-      spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+-      if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)
+-          && !m2m_ctx->out_q_ctx.buffered) {
++      src = v4l2_m2m_next_src_buf(m2m_ctx);
++      dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++      if (!src && !m2m_ctx->out_q_ctx.buffered) {
+               dprintk("No input buffers available\n");
+-              goto out_unlock;
++              goto job_unlock;
+       }
+-      spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+-      if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)
+-          && !m2m_ctx->cap_q_ctx.buffered) {
++      if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
+               dprintk("No output buffers available\n");
+-              goto cap_unlock;
++              goto job_unlock;
++      }
++
++      if (src && dst &&
++          dst->is_held && dst->vb2_buf.copied_timestamp &&
++          dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
++              dst->is_held = false;
++              v4l2_m2m_dst_buf_remove(m2m_ctx);
++              v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
++              dst = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++              if (!dst && !m2m_ctx->cap_q_ctx.buffered) {
++                      dprintk("No output buffers available after returning held buffer\n");
++                      goto job_unlock;
++              }
+       }
+-      spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+-      spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+       if (m2m_dev->m2m_ops->job_ready
+               && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+@@ -331,13 +342,6 @@ static void __v4l2_m2m_try_queue(struct
+       list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+       m2m_ctx->job_flags |= TRANS_QUEUED;
+-      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+-      return;
+-
+-cap_unlock:
+-      spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap);
+-out_unlock:
+-      spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out);
+ job_unlock:
+       spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ }
+@@ -412,37 +416,97 @@ static void v4l2_m2m_cancel_job(struct v
+       }
+ }
+-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+-                       struct v4l2_m2m_ctx *m2m_ctx)
++/*
++ * Schedule the next job, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static void v4l2_m2m_schedule_next_job(struct v4l2_m2m_dev *m2m_dev,
++                                     struct v4l2_m2m_ctx *m2m_ctx)
+ {
+-      unsigned long flags;
++      /*
++       * This instance might have more buffers ready, but since we do not
++       * allow more than one job on the job_queue per instance, each has
++       * to be scheduled separately after the previous one finishes.
++       */
++      __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
+-      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      /*
++       * We might be running in atomic context,
++       * but the job must be run in non-atomic context.
++       */
++      schedule_work(&m2m_dev->job_work);
++}
++
++/*
++ * Assumes job_spinlock is held, called from v4l2_m2m_job_finish() or
++ * v4l2_m2m_buf_done_and_job_finish().
++ */
++static bool _v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++                               struct v4l2_m2m_ctx *m2m_ctx)
++{
+       if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+-              spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               dprintk("Called by an instance not currently running\n");
+-              return;
++              return false;
+       }
+       list_del(&m2m_dev->curr_ctx->queue);
+       m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
+       wake_up(&m2m_dev->curr_ctx->finished);
+       m2m_dev->curr_ctx = NULL;
++      return true;
++}
+-      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+-
+-      /* This instance might have more buffers ready, but since we do not
+-       * allow more than one job on the job_queue per instance, each has
+-       * to be scheduled separately after the previous one finishes. */
+-      __v4l2_m2m_try_queue(m2m_dev, m2m_ctx);
++void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
++                       struct v4l2_m2m_ctx *m2m_ctx)
++{
++      unsigned long flags;
++      bool schedule_next;
+-      /* We might be running in atomic context,
+-       * but the job must be run in non-atomic context.
++      /*
++       * This function should not be used for drivers that support
++       * holding capture buffers. Those should use
++       * v4l2_m2m_buf_done_and_job_finish() instead.
+        */
+-      schedule_work(&m2m_dev->job_work);
++      WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags &
++              VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
++      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++      if (schedule_next)
++              v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
+ }
+ EXPORT_SYMBOL(v4l2_m2m_job_finish);
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++                                    struct v4l2_m2m_ctx *m2m_ctx,
++                                    enum vb2_buffer_state state)
++{
++      struct vb2_v4l2_buffer *src_buf, *dst_buf;
++      bool schedule_next = false;
++      unsigned long flags;
++
++      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
++      dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
++
++      if (WARN_ON(!src_buf || !dst_buf))
++              goto unlock;
++      v4l2_m2m_buf_done(src_buf, state);
++      dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++      if (!dst_buf->is_held) {
++              v4l2_m2m_dst_buf_remove(m2m_ctx);
++              v4l2_m2m_buf_done(dst_buf, state);
++      }
++      schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
++unlock:
++      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++      if (schedule_next)
++              v4l2_m2m_schedule_next_job(m2m_dev, m2m_ctx);
++}
++EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish);
++
+ int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+                    struct v4l2_requestbuffers *reqbufs)
+ {
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -21,7 +21,8 @@
+  *            callback.
+  *            The job does NOT have to end before this callback returns
+  *            (and it will be the usual case). When the job finishes,
+- *            v4l2_m2m_job_finish() has to be called.
++ *            v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish()
++ *            has to be called.
+  * @job_ready:        optional. Should return 0 if the driver does not have a job
+  *            fully prepared to run yet (i.e. it will not be able to finish a
+  *            transaction without sleeping). If not provided, it will be
+@@ -33,7 +34,8 @@
+  *            stop the device safely; e.g. in the next interrupt handler),
+  *            even if the transaction would not have been finished by then.
+  *            After the driver performs the necessary steps, it has to call
+- *            v4l2_m2m_job_finish() (as if the transaction ended normally).
++ *            v4l2_m2m_job_finish() or v4l2_m2m_buf_done_and_job_finish() as
++ *            if the transaction ended normally.
+  *            This function does not have to (and will usually not) wait
+  *            until the device enters a state when it can be stopped.
+  */
+@@ -173,6 +175,33 @@ void v4l2_m2m_try_schedule(struct v4l2_m
+ void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+                        struct v4l2_m2m_ctx *m2m_ctx);
++/**
++ * v4l2_m2m_buf_done_and_job_finish() - return source/destination buffers with
++ * state and inform the framework that a job has been finished and have it
++ * clean up
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done().
++ *
++ * Drivers that set V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF must use this
++ * function instead of job_finish() to take held buffers into account. It is
++ * optional for other drivers.
++ *
++ * This function removes the source buffer from the ready list and returns
++ * it with the given state. The same is done for the destination buffer, unless
++ * it is marked 'held'. In that case the buffer is kept on the ready list.
++ *
++ * After that the job is finished (see job_finish()).
++ *
++ * This allows for multiple output buffers to be used to fill in a single
++ * capture buffer. This is typically used by stateless decoders where
++ * multiple e.g. H.264 slices contribute to a single decoded frame.
++ */
++void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
++                                    struct v4l2_m2m_ctx *m2m_ctx,
++                                    enum vb2_buffer_state state);
++
+ static inline void
+ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state)
+ {
diff --git a/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch b/target/linux/bcm27xx/patches-5.4/950-0499-media-videodev2.h-add-V4L2_DEC_CMD_FLUSH.patch
new file mode 100644 (file)
index 0000000..ef075fd
--- /dev/null
@@ -0,0 +1,57 @@
+From b2ea711d2c21ec021de4ff09a0a2b5b4224f9749 Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:42 -0300
+Subject: [PATCH] media: videodev2.h: add V4L2_DEC_CMD_FLUSH
+
+Add this new V4L2_DEC_CMD_FLUSH decoder command and document it.
+
+Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
+Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst | 10 +++++++++-
+ Documentation/media/videodev2.h.rst.exceptions      |  1 +
+ include/uapi/linux/videodev2.h                      |  1 +
+ 3 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst
++++ b/Documentation/media/uapi/v4l/vidioc-decoder-cmd.rst
+@@ -208,7 +208,15 @@ introduced in Linux 3.3. They are, howev
+       been started yet, the driver will return an ``EPERM`` error code. When
+       the decoder is already running, this command does nothing. No
+       flags are defined for this command.
+-
++    * - ``V4L2_DEC_CMD_FLUSH``
++      - 4
++      - Flush any held capture buffers. Only valid for stateless decoders.
++      This command is typically used when the application reached the
++      end of the stream and the last output buffer had the
++      ``V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF`` flag set. This would prevent
++      dequeueing the capture buffer containing the last decoded frame.
++      So this command can be used to explicitly flush that final decoded
++      frame. This command does nothing if there are no held capture buffers.
+ Return Value
+ ============
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -434,6 +434,7 @@ replace define V4L2_DEC_CMD_START decode
+ replace define V4L2_DEC_CMD_STOP decoder-cmds
+ replace define V4L2_DEC_CMD_PAUSE decoder-cmds
+ replace define V4L2_DEC_CMD_RESUME decoder-cmds
++replace define V4L2_DEC_CMD_FLUSH decoder-cmds
+ replace define V4L2_DEC_CMD_START_MUTE_AUDIO decoder-cmds
+ replace define V4L2_DEC_CMD_PAUSE_TO_BLACK decoder-cmds
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -1989,6 +1989,7 @@ struct v4l2_encoder_cmd {
+ #define V4L2_DEC_CMD_STOP        (1)
+ #define V4L2_DEC_CMD_PAUSE       (2)
+ #define V4L2_DEC_CMD_RESUME      (3)
++#define V4L2_DEC_CMD_FLUSH       (4)
+ /* Flags for V4L2_DEC_CMD_START */
+ #define V4L2_DEC_CMD_START_MUTE_AUDIO (1 << 0)
diff --git a/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch b/target/linux/bcm27xx/patches-5.4/950-0500-media-v4l2-mem2mem-add-stateless_-try_-decoder_cmd-i.patch
new file mode 100644 (file)
index 0000000..0b74dbf
--- /dev/null
@@ -0,0 +1,96 @@
+From 1decb017f990ea61ab421e316bf1af3a5199b73a Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 11 Oct 2019 06:32:43 -0300
+Subject: [PATCH] media: v4l2-mem2mem: add stateless_(try_)decoder_cmd
+ ioctl helpers
+
+Commit bef41d93aac64b54c3008ca6170bec54f85784f5 upstream.
+
+These helpers are used by stateless codecs when they support multiple
+slices per frame and hold capture buffer flag is set. It's expected that
+all such codecs will use this code.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Co-developed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 53 ++++++++++++++++++++++++++
+ include/media/v4l2-mem2mem.h           |  4 ++
+ 2 files changed, 57 insertions(+)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -1218,6 +1218,59 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struc
+ }
+ EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd);
++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
++                                           struct v4l2_decoder_cmd *dc)
++{
++      if (dc->cmd != V4L2_DEC_CMD_FLUSH)
++              return -EINVAL;
++
++      dc->flags = 0;
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_try_decoder_cmd);
++
++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
++                                       struct v4l2_decoder_cmd *dc)
++{
++      struct v4l2_fh *fh = file->private_data;
++      struct vb2_v4l2_buffer *out_vb, *cap_vb;
++      struct v4l2_m2m_dev *m2m_dev = fh->m2m_ctx->m2m_dev;
++      unsigned long flags;
++      int ret;
++
++      ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, dc);
++      if (ret < 0)
++              return ret;
++
++      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      out_vb = v4l2_m2m_last_src_buf(fh->m2m_ctx);
++      cap_vb = v4l2_m2m_last_dst_buf(fh->m2m_ctx);
++
++      /*
++       * If there is an out buffer pending, then clear any HOLD flag.
++       *
++       * By clearing this flag we ensure that when this output
++       * buffer is processed any held capture buffer will be released.
++       */
++      if (out_vb) {
++              out_vb->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++      } else if (cap_vb && cap_vb->is_held) {
++              /*
++               * If there were no output buffers, but there is a
++               * capture buffer that is held, then release that
++               * buffer.
++               */
++              cap_vb->is_held = false;
++              v4l2_m2m_dst_buf_remove(fh->m2m_ctx);
++              v4l2_m2m_buf_done(cap_vb, VB2_BUF_STATE_DONE);
++      }
++      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_stateless_decoder_cmd);
++
+ /*
+  * v4l2_file_operations helpers. It is assumed here same lock is used
+  * for the output and the capture buffer queue.
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -701,6 +701,10 @@ int v4l2_m2m_ioctl_try_encoder_cmd(struc
+                                  struct v4l2_encoder_cmd *ec);
+ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh,
+                                  struct v4l2_decoder_cmd *dc);
++int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh,
++                                           struct v4l2_decoder_cmd *dc);
++int v4l2_m2m_ioctl_stateless_decoder_cmd(struct file *file, void *priv,
++                                       struct v4l2_decoder_cmd *dc);
+ int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma);
+ __poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch b/target/linux/bcm27xx/patches-5.4/950-0501-media-v4l2-mem2mem-add-new_frame-detection.patch
new file mode 100644 (file)
index 0000000..3c77792
--- /dev/null
@@ -0,0 +1,69 @@
+From 1d55acac432983ad8301f5430c42ac549b4b4c6f Mon Sep 17 00:00:00 2001
+From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Date: Fri, 11 Oct 2019 06:32:44 -0300
+Subject: [PATCH] media: v4l2-mem2mem: add new_frame detection
+
+Commit f07602ac388723233e9e3c5a05b54baf34e0a3e9 upstream.
+
+Drivers that support VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF
+typically want to know if a new frame is started (i.e. the first
+slice is about to be processed). Add a new_frame bool to v4l2_m2m_ctx
+and set it accordingly.
+
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 11 +++++++++--
+ include/media/v4l2-mem2mem.h           |  7 +++++++
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -319,8 +319,10 @@ static void __v4l2_m2m_try_queue(struct
+               goto job_unlock;
+       }
+-      if (src && dst &&
+-          dst->is_held && dst->vb2_buf.copied_timestamp &&
++      m2m_ctx->new_frame = true;
++
++      if (src && dst && dst->is_held &&
++          dst->vb2_buf.copied_timestamp &&
+           dst->vb2_buf.timestamp != src->vb2_buf.timestamp) {
+               dst->is_held = false;
+               v4l2_m2m_dst_buf_remove(m2m_ctx);
+@@ -333,6 +335,11 @@ static void __v4l2_m2m_try_queue(struct
+               }
+       }
++      if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags &
++                         VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
++              m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
++                      dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
++
+       if (m2m_dev->m2m_ops->job_ready
+               && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+               dprintk("Driver not ready\n");
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -75,6 +75,11 @@ struct v4l2_m2m_queue_ctx {
+  * struct v4l2_m2m_ctx - Memory to memory context structure
+  *
+  * @q_lock: struct &mutex lock
++ * @new_frame: valid in the device_run callback: if true, then this
++ *            starts a new frame; if false, then this is a new slice
++ *            for an existing frame. This is always true unless
++ *            V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which
++ *            indicates slicing support.
+  * @m2m_dev: opaque pointer to the internal data to handle M2M context
+  * @cap_q_ctx: Capture (output to memory) queue context
+  * @out_q_ctx: Output (input from memory) queue context
+@@ -91,6 +96,8 @@ struct v4l2_m2m_ctx {
+       /* optional cap/out vb2 queues lock */
+       struct mutex                    *q_lock;
++      bool                            new_frame;
++
+       /* internal use only */
+       struct v4l2_m2m_dev             *m2m_dev;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch b/target/linux/bcm27xx/patches-5.4/950-0502-media-Documentation-media-Document-V4L2_CTRL_TYPE_AR.patch
new file mode 100644 (file)
index 0000000..1d478fc
--- /dev/null
@@ -0,0 +1,46 @@
+From 20076d276d045c03f809bb16f0e1fafcfe63a81f Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Date: Mon, 7 Oct 2019 12:06:32 -0300
+Subject: [PATCH] media: Documentation: media: Document
+ V4L2_CTRL_TYPE_AREA
+
+Commit 8ae3a0862993c09a8ef0f9abb379553370c517e3 upstream.
+
+A struct v4l2_area containing the width and the height of a rectangular
+area.
+
+Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
+Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
+Signed-off-by: Ricardo Ribalda Delgado <ribalda@kernel.org>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/vidioc-queryctrl.rst | 6 ++++++
+ Documentation/media/videodev2.h.rst.exceptions    | 1 +
+ 2 files changed, 7 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
++++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
+@@ -443,6 +443,12 @@ See also the examples in :ref:`control`.
+       - n/a
+       - A struct :c:type:`v4l2_ctrl_mpeg2_quantization`, containing MPEG-2
+       quantization matrices for stateless video decoders.
++    * - ``V4L2_CTRL_TYPE_AREA``
++      - n/a
++      - n/a
++      - n/a
++      - A struct :c:type:`v4l2_area`, containing the width and the height
++        of a rectangular area. Units depend on the use case.
+     * - ``V4L2_CTRL_TYPE_H264_SPS``
+       - n/a
+       - n/a
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -141,6 +141,7 @@ replace symbol V4L2_CTRL_TYPE_H264_PPS :
+ replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+ # V4L2 capability defines
+ replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities
diff --git a/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch b/target/linux/bcm27xx/patches-5.4/950-0503-media-v4l-Add-definitions-for-HEVC-stateless-decodin.patch
new file mode 100644 (file)
index 0000000..0fe0f8c
--- /dev/null
@@ -0,0 +1,1093 @@
+From 5f6c08984a6578201fe3a2394ccb0d3a30fdf027 Mon Sep 17 00:00:00 2001
+From: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Date: Tue, 22 Oct 2019 12:26:52 -0300
+Subject: [PATCH] media: v4l: Add definitions for HEVC stateless
+ decoding
+
+This introduces the required definitions for HEVC decoding support with
+stateless VPUs. The controls associated to the HEVC slice format provide
+the required meta-data for decoding slices extracted from the bitstream.
+
+They are not exported to the public V4L2 API since reworking this API
+will likely be needed for covering various use-cases and new hardware.
+
+Multi-slice decoding is exposed as a valid decoding mode to match current
+H.264 support but it is not yet implemented.
+
+The interface comes with the following limitations:
+* No custom quantization matrices (scaling lists);
+* Support for a single temporal layer only;
+* No slice entry point offsets support;
+* No conformance window support;
+* No VUI parameters support;
+* No support for SPS extensions: range, multilayer, 3d, scc, 4 bits;
+* No support for PPS extensions: range, multilayer, 3d, scc, 4 bits.
+
+Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+[hverkuil-cisco@xs4all.nl: use 1ULL in flags defines in hevc-ctrls.h]
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ Documentation/media/uapi/v4l/biblio.rst       |   9 +
+ .../media/uapi/v4l/ext-ctrls-codec.rst        | 553 +++++++++++++++++-
+ .../media/uapi/v4l/vidioc-queryctrl.rst       |  18 +
+ .../media/videodev2.h.rst.exceptions          |   3 +
+ drivers/media/v4l2-core/v4l2-ctrls.c          | 109 +++-
+ drivers/media/v4l2-core/v4l2-ioctl.c          |   1 +
+ include/media/hevc-ctrls.h                    | 212 +++++++
+ include/media/v4l2-ctrls.h                    |   7 +
+ 8 files changed, 908 insertions(+), 4 deletions(-)
+ create mode 100644 include/media/hevc-ctrls.h
+
+--- a/Documentation/media/uapi/v4l/biblio.rst
++++ b/Documentation/media/uapi/v4l/biblio.rst
+@@ -131,6 +131,15 @@ ITU-T Rec. H.264 Specification (04/2017
+ :author:    International Telecommunication Union (http://www.itu.ch)
++.. _hevc:
++
++ITU H.265/HEVC
++==============
++
++:title:     ITU-T Rec. H.265 | ISO/IEC 23008-2 "High Efficiency Video Coding"
++
++:author:    International Telecommunication Union (http://www.itu.ch), International Organisation for Standardisation (http://www.iso.ch)
++
+ .. _jfif:
+ JFIF
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -1983,9 +1983,9 @@ enum v4l2_mpeg_video_h264_hierarchical_c
+       - ``reference_ts``
+       - Timestamp of the V4L2 capture buffer to use as reference, used
+         with B-coded and P-coded frames. The timestamp refers to the
+-      ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
+-      :c:func:`v4l2_timeval_to_ns()` function to convert the struct
+-      :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
++        ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
++        :c:func:`v4l2_timeval_to_ns()` function to convert the struct
++        :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
+     * - __u16
+       - ``frame_num``
+       -
+@@ -3693,3 +3693,550 @@ enum v4l2_mpeg_video_hevc_size_of_length
+     Indicates whether to generate SPS and PPS at every IDR. Setting it to 0
+     disables generating SPS and PPS at every IDR. Setting it to one enables
+     generating SPS and PPS at every IDR.
++
++.. _v4l2-mpeg-hevc:
++
++``V4L2_CID_MPEG_VIDEO_HEVC_SPS (struct)``
++    Specifies the Sequence Parameter Set fields (as extracted from the
++    bitstream) for the associated HEVC slice data.
++    These bitstream parameters are defined according to :ref:`hevc`.
++    They are described in section 7.4.3.2 "Sequence parameter set RBSP
++    semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_sps
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_sps
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u16
++      - ``pic_width_in_luma_samples``
++      -
++    * - __u16
++      - ``pic_height_in_luma_samples``
++      -
++    * - __u8
++      - ``bit_depth_luma_minus8``
++      -
++    * - __u8
++      - ``bit_depth_chroma_minus8``
++      -
++    * - __u8
++      - ``log2_max_pic_order_cnt_lsb_minus4``
++      -
++    * - __u8
++      - ``sps_max_dec_pic_buffering_minus1``
++      -
++    * - __u8
++      - ``sps_max_num_reorder_pics``
++      -
++    * - __u8
++      - ``sps_max_latency_increase_plus1``
++      -
++    * - __u8
++      - ``log2_min_luma_coding_block_size_minus3``
++      -
++    * - __u8
++      - ``log2_diff_max_min_luma_coding_block_size``
++      -
++    * - __u8
++      - ``log2_min_luma_transform_block_size_minus2``
++      -
++    * - __u8
++      - ``log2_diff_max_min_luma_transform_block_size``
++      -
++    * - __u8
++      - ``max_transform_hierarchy_depth_inter``
++      -
++    * - __u8
++      - ``max_transform_hierarchy_depth_intra``
++      -
++    * - __u8
++      - ``pcm_sample_bit_depth_luma_minus1``
++      -
++    * - __u8
++      - ``pcm_sample_bit_depth_chroma_minus1``
++      -
++    * - __u8
++      - ``log2_min_pcm_luma_coding_block_size_minus3``
++      -
++    * - __u8
++      - ``log2_diff_max_min_pcm_luma_coding_block_size``
++      -
++    * - __u8
++      - ``num_short_term_ref_pic_sets``
++      -
++    * - __u8
++      - ``num_long_term_ref_pics_sps``
++      -
++    * - __u8
++      - ``chroma_format_idc``
++      -
++    * - __u64
++      - ``flags``
++      - See :ref:`Sequence Parameter Set Flags <hevc_sps_flags>`
++
++.. _hevc_sps_flags:
++
++``Sequence Parameter Set Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - ``V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE``
++      - 0x00000001
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED``
++      - 0x00000002
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_AMP_ENABLED``
++      - 0x00000004
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET``
++      - 0x00000008
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_PCM_ENABLED``
++      - 0x00000010
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED``
++      - 0x00000020
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT``
++      - 0x00000040
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED``
++      - 0x00000080
++      -
++    * - ``V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED``
++      - 0x00000100
++      -
++
++``V4L2_CID_MPEG_VIDEO_HEVC_PPS (struct)``
++    Specifies the Picture Parameter Set fields (as extracted from the
++    bitstream) for the associated HEVC slice data.
++    These bitstream parameters are defined according to :ref:`hevc`.
++    They are described in section 7.4.3.3 "Picture parameter set RBSP
++    semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_pps
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_pps
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u8
++      - ``num_extra_slice_header_bits``
++      -
++    * - __s8
++      - ``init_qp_minus26``
++      -
++    * - __u8
++      - ``diff_cu_qp_delta_depth``
++      -
++    * - __s8
++      - ``pps_cb_qp_offset``
++      -
++    * - __s8
++      - ``pps_cr_qp_offset``
++      -
++    * - __u8
++      - ``num_tile_columns_minus1``
++      -
++    * - __u8
++      - ``num_tile_rows_minus1``
++      -
++    * - __u8
++      - ``column_width_minus1[20]``
++      -
++    * - __u8
++      - ``row_height_minus1[22]``
++      -
++    * - __s8
++      - ``pps_beta_offset_div2``
++      -
++    * - __s8
++      - ``pps_tc_offset_div2``
++      -
++    * - __u8
++      - ``log2_parallel_merge_level_minus2``
++      -
++    * - __u8
++      - ``padding[4]``
++      - Applications and drivers must set this to zero.
++    * - __u64
++      - ``flags``
++      - See :ref:`Picture Parameter Set Flags <hevc_pps_flags>`
++
++.. _hevc_pps_flags:
++
++``Picture Parameter Set Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - ``V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT``
++      - 0x00000001
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT``
++      - 0x00000002
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED``
++      - 0x00000004
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT``
++      - 0x00000008
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED``
++      - 0x00000010
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED``
++      - 0x00000020
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED``
++      - 0x00000040
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT``
++      - 0x00000080
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED``
++      - 0x00000100
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED``
++      - 0x00000200
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED``
++      - 0x00000400
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_TILES_ENABLED``
++      - 0x00000800
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED``
++      - 0x00001000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED``
++      - 0x00002000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED``
++      - 0x00004000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED``
++      - 0x00008000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER``
++      - 0x00010000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT``
++      - 0x00020000
++      -
++    * - ``V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT``
++      - 0x00040000
++      -
++
++``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (struct)``
++    Specifies various slice-specific parameters, especially from the NAL unit
++    header, general slice segment header and weighted prediction parameter
++    parts of the bitstream.
++    These bitstream parameters are defined according to :ref:`hevc`.
++    They are described in section 7.4.7 "General slice segment header
++    semantics" of the specification.
++
++.. c:type:: v4l2_ctrl_hevc_slice_params
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_slice_params
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u32
++      - ``bit_size``
++      - Size (in bits) of the current slice data.
++    * - __u32
++      - ``data_bit_offset``
++      - Offset (in bits) to the video data in the current slice data.
++    * - __u8
++      - ``nal_unit_type``
++      -
++    * - __u8
++      - ``nuh_temporal_id_plus1``
++      -
++    * - __u8
++      - ``slice_type``
++      -
++      (V4L2_HEVC_SLICE_TYPE_I, V4L2_HEVC_SLICE_TYPE_P or
++      V4L2_HEVC_SLICE_TYPE_B).
++    * - __u8
++      - ``colour_plane_id``
++      -
++    * - __u16
++      - ``slice_pic_order_cnt``
++      -
++    * - __u8
++      - ``num_ref_idx_l0_active_minus1``
++      -
++    * - __u8
++      - ``num_ref_idx_l1_active_minus1``
++      -
++    * - __u8
++      - ``collocated_ref_idx``
++      -
++    * - __u8
++      - ``five_minus_max_num_merge_cand``
++      -
++    * - __s8
++      - ``slice_qp_delta``
++      -
++    * - __s8
++      - ``slice_cb_qp_offset``
++      -
++    * - __s8
++      - ``slice_cr_qp_offset``
++      -
++    * - __s8
++      - ``slice_act_y_qp_offset``
++      -
++    * - __s8
++      - ``slice_act_cb_qp_offset``
++      -
++    * - __s8
++      - ``slice_act_cr_qp_offset``
++      -
++    * - __s8
++      - ``slice_beta_offset_div2``
++      -
++    * - __s8
++      - ``slice_tc_offset_div2``
++      -
++    * - __u8
++      - ``pic_struct``
++      -
++    * - __u8
++      - ``num_active_dpb_entries``
++      - The number of entries in ``dpb``.
++    * - __u8
++      - ``ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      - The list of L0 reference elements as indices in the DPB.
++    * - __u8
++      - ``ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      - The list of L1 reference elements as indices in the DPB.
++    * - __u8
++      - ``num_rps_poc_st_curr_before``
++      - The number of reference pictures in the short-term set that come before
++        the current frame.
++    * - __u8
++      - ``num_rps_poc_st_curr_after``
++      - The number of reference pictures in the short-term set that come after
++        the current frame.
++    * - __u8
++      - ``num_rps_poc_lt_curr``
++      - The number of reference pictures in the long-term set.
++    * - __u8
++      - ``padding[7]``
++      - Applications and drivers must set this to zero.
++    * - struct :c:type:`v4l2_hevc_dpb_entry`
++      - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      - The decoded picture buffer, for meta-data about reference frames.
++    * - struct :c:type:`v4l2_hevc_pred_weight_table`
++      - ``pred_weight_table``
++      - The prediction weight coefficients for inter-picture prediction.
++    * - __u64
++      - ``flags``
++      - See :ref:`Slice Parameters Flags <hevc_slice_params_flags>`
++
++.. _hevc_slice_params_flags:
++
++``Slice Parameters Flags``
++
++.. cssclass:: longtable
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA``
++      - 0x00000001
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA``
++      - 0x00000002
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED``
++      - 0x00000004
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO``
++      - 0x00000008
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT``
++      - 0x00000010
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0``
++      - 0x00000020
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV``
++      - 0x00000040
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED``
++      - 0x00000080
++      -
++    * - ``V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED``
++      - 0x00000100
++      -
++
++.. c:type:: v4l2_hevc_dpb_entry
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_hevc_dpb_entry
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u64
++      - ``timestamp``
++      - Timestamp of the V4L2 capture buffer to use as reference, used
++        with B-coded and P-coded frames. The timestamp refers to the
++      ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the
++      :c:func:`v4l2_timeval_to_ns()` function to convert the struct
++      :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64.
++    * - __u8
++      - ``rps``
++      - The reference set for the reference frame
++        (V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE,
++        V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER or
++        V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR)
++    * - __u8
++      - ``field_pic``
++      - Whether the reference is a field picture or a frame.
++    * - __u16
++      - ``pic_order_cnt[2]``
++      - The picture order count of the reference. Only the first element of the
++        array is used for frame pictures, while the first element identifies the
++        top field and the second the bottom field in field-coded pictures.
++    * - __u8
++      - ``padding[2]``
++      - Applications and drivers must set this to zero.
++
++.. c:type:: v4l2_hevc_pred_weight_table
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_hevc_pred_weight_table
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u8
++      - ``luma_log2_weight_denom``
++      -
++    * - __s8
++      - ``delta_chroma_log2_weight_denom``
++      -
++    * - __s8
++      - ``delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      -
++    * - __s8
++      - ``luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      -
++    * - __s8
++      - ``delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++      -
++    * - __s8
++      - ``chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++      -
++    * - __s8
++      - ``delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      -
++    * - __s8
++      - ``luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
++      -
++    * - __s8
++      - ``delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++      -
++    * - __s8
++      - ``chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2]``
++      -
++    * - __u8
++      - ``padding[6]``
++      - Applications and drivers must set this to zero.
++
++``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)``
++    Specifies the decoding mode to use. Currently exposes slice-based and
++    frame-based decoding but new modes might be added later on.
++    This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE
++    pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE
++    are required to set this control in order to specify the decoding mode
++    that is expected for the buffer.
++    Drivers may expose a single or multiple decoding modes, depending
++    on what they can support.
++
++    .. note::
++
++       This menu control is not yet part of the public kernel API and
++       it is expected to change.
++
++.. c:type:: v4l2_mpeg_video_hevc_decode_mode
++
++.. cssclass:: longtable
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED``
++      - 0
++      - Decoding is done at the slice granularity.
++        The OUTPUT buffer must contain a single slice.
++    * - ``V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED``
++      - 1
++      - Decoding is done at the frame granularity.
++        The OUTPUT buffer must contain all slices needed to decode the
++        frame. The OUTPUT buffer must also contain both fields.
++
++``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE (enum)``
++    Specifies the HEVC slice start code expected for each slice.
++    This control is used as a modifier for V4L2_PIX_FMT_HEVC_SLICE
++    pixel format. Applications that support V4L2_PIX_FMT_HEVC_SLICE
++    are required to set this control in order to specify the start code
++    that is expected for the buffer.
++    Drivers may expose a single or multiple start codes, depending
++    on what they can support.
++
++    .. note::
++
++       This menu control is not yet part of the public kernel API and
++       it is expected to change.
++
++.. c:type:: v4l2_mpeg_video_hevc_start_code
++
++.. cssclass:: longtable
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE``
++      - 0
++      - Selecting this value specifies that HEVC slices are passed
++        to the driver without any start code.
++    * - ``V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B``
++      - 1
++      - Selecting this value specifies that HEVC slices are expected
++        to be prefixed by Annex B start codes. According to :ref:`hevc`
++        valid start codes can be 3-bytes 0x000001 or 4-bytes 0x00000001.
+--- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
++++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst
+@@ -479,6 +479,24 @@ See also the examples in :ref:`control`.
+       - n/a
+       - A struct :c:type:`v4l2_ctrl_h264_decode_params`, containing H264
+       decode parameters for stateless video decoders.
++    * - ``V4L2_CTRL_TYPE_HEVC_SPS``
++      - n/a
++      - n/a
++      - n/a
++      - A struct :c:type:`v4l2_ctrl_hevc_sps`, containing HEVC Sequence
++      Parameter Set for stateless video decoders.
++    * - ``V4L2_CTRL_TYPE_HEVC_PPS``
++      - n/a
++      - n/a
++      - n/a
++      - A struct :c:type:`v4l2_ctrl_hevc_pps`, containing HEVC Picture
++      Parameter Set for stateless video decoders.
++    * - ``V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS``
++      - n/a
++      - n/a
++      - n/a
++      - A struct :c:type:`v4l2_ctrl_hevc_slice_params`, containing HEVC
++      slice parameters for stateless video decoders.
+ .. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}|
+--- a/Documentation/media/videodev2.h.rst.exceptions
++++ b/Documentation/media/videodev2.h.rst.exceptions
+@@ -141,6 +141,9 @@ replace symbol V4L2_CTRL_TYPE_H264_PPS :
+ replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
++replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
+ replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+ # V4L2 capability defines
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -567,6 +567,16 @@ const char * const *v4l2_ctrl_get_menu(u
+               "Disabled at slice boundary",
+               "NULL",
+       };
++      static const char * const hevc_decode_mode[] = {
++              "Slice-Based",
++              "Frame-Based",
++              NULL,
++      };
++      static const char * const hevc_start_code[] = {
++              "No Start Code",
++              "Annex B Start Code",
++              NULL,
++      };
+       switch (id) {
+       case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+@@ -688,7 +698,10 @@ const char * const *v4l2_ctrl_get_menu(u
+               return hevc_tier;
+       case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
+               return hevc_loop_filter_mode;
+-
++      case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
++              return hevc_decode_mode;
++      case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
++              return hevc_start_code;
+       default:
+               return NULL;
+       }
+@@ -958,6 +971,11 @@ const char *v4l2_ctrl_get_name(u32 id)
+       case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:     return "HEVC Size of Length Field";
+       case V4L2_CID_MPEG_VIDEO_REF_NUMBER_FOR_PFRAMES:        return "Reference Frames for a P-Frame";
+       case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR:         return "Prepend SPS and PPS to IDR";
++      case V4L2_CID_MPEG_VIDEO_HEVC_SPS:                      return "HEVC Sequence Parameter Set";
++      case V4L2_CID_MPEG_VIDEO_HEVC_PPS:                      return "HEVC Picture Parameter Set";
++      case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:             return "HEVC Slice Parameters";
++      case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:              return "HEVC Decode Mode";
++      case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:               return "HEVC Start Code";
+       /* CAMERA controls */
+       /* Keep the order of the 'case's the same as in v4l2-controls.h! */
+@@ -1267,6 +1285,8 @@ void v4l2_ctrl_fill(u32 id, const char *
+       case V4L2_CID_MPEG_VIDEO_HEVC_SIZE_OF_LENGTH_FIELD:
+       case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+       case V4L2_CID_MPEG_VIDEO_HEVC_LOOP_FILTER_MODE:
++      case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:
++      case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:
+               *type = V4L2_CTRL_TYPE_MENU;
+               break;
+       case V4L2_CID_LINK_FREQ:
+@@ -1377,6 +1397,15 @@ void v4l2_ctrl_fill(u32 id, const char *
+       case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER:
+               *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER;
+               break;
++      case V4L2_CID_MPEG_VIDEO_HEVC_SPS:
++              *type = V4L2_CTRL_TYPE_HEVC_SPS;
++              break;
++      case V4L2_CID_MPEG_VIDEO_HEVC_PPS:
++              *type = V4L2_CTRL_TYPE_HEVC_PPS;
++              break;
++      case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
++              *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
++              break;
+       case V4L2_CID_UNIT_CELL_SIZE:
+               *type = V4L2_CTRL_TYPE_AREA;
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+@@ -1678,8 +1707,12 @@ static int std_validate_compound(const s
+ {
+       struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params;
+       struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++      struct v4l2_ctrl_hevc_sps *p_hevc_sps;
++      struct v4l2_ctrl_hevc_pps *p_hevc_pps;
++      struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
+       struct v4l2_area *area;
+       void *p = ptr.p + idx * ctrl->elem_size;
++      unsigned int i;
+       switch ((u32)ctrl->type) {
+       case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS:
+@@ -1755,11 +1788,76 @@ static int std_validate_compound(const s
+               zero_padding(p_vp8_frame_header->entropy_header);
+               zero_padding(p_vp8_frame_header->coder_state);
+               break;
++
++      case V4L2_CTRL_TYPE_HEVC_SPS:
++              p_hevc_sps = p;
++
++              if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) {
++                      p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0;
++                      p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0;
++                      p_hevc_sps->log2_min_pcm_luma_coding_block_size_minus3 = 0;
++                      p_hevc_sps->log2_diff_max_min_pcm_luma_coding_block_size = 0;
++              }
++
++              if (!(p_hevc_sps->flags &
++                    V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT))
++                      p_hevc_sps->num_long_term_ref_pics_sps = 0;
++              break;
++
++      case V4L2_CTRL_TYPE_HEVC_PPS:
++              p_hevc_pps = p;
++
++              if (!(p_hevc_pps->flags &
++                    V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED))
++                      p_hevc_pps->diff_cu_qp_delta_depth = 0;
++
++              if (!(p_hevc_pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) {
++                      p_hevc_pps->num_tile_columns_minus1 = 0;
++                      p_hevc_pps->num_tile_rows_minus1 = 0;
++                      memset(&p_hevc_pps->column_width_minus1, 0,
++                             sizeof(p_hevc_pps->column_width_minus1));
++                      memset(&p_hevc_pps->row_height_minus1, 0,
++                             sizeof(p_hevc_pps->row_height_minus1));
++
++                      p_hevc_pps->flags &=
++                              ~V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED;
++              }
++
++              if (p_hevc_pps->flags &
++                  V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER) {
++                      p_hevc_pps->pps_beta_offset_div2 = 0;
++                      p_hevc_pps->pps_tc_offset_div2 = 0;
++              }
++
++              zero_padding(*p_hevc_pps);
++              break;
++
++      case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
++              p_hevc_slice_params = p;
++
++              if (p_hevc_slice_params->num_active_dpb_entries >
++                  V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
++                      return -EINVAL;
++
++              zero_padding(p_hevc_slice_params->pred_weight_table);
++
++              for (i = 0; i < p_hevc_slice_params->num_active_dpb_entries;
++                   i++) {
++                      struct v4l2_hevc_dpb_entry *dpb_entry =
++                              &p_hevc_slice_params->dpb[i];
++
++                      zero_padding(*dpb_entry);
++              }
++
++              zero_padding(*p_hevc_slice_params);
++              break;
++
+       case V4L2_CTRL_TYPE_AREA:
+               area = p;
+               if (!area->width || !area->height)
+                       return -EINVAL;
+               break;
++
+       default:
+               return -EINVAL;
+       }
+@@ -2433,6 +2531,15 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+       case V4L2_CTRL_TYPE_VP8_FRAME_HEADER:
+               elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header);
+               break;
++      case V4L2_CTRL_TYPE_HEVC_SPS:
++              elem_size = sizeof(struct v4l2_ctrl_hevc_sps);
++              break;
++      case V4L2_CTRL_TYPE_HEVC_PPS:
++              elem_size = sizeof(struct v4l2_ctrl_hevc_pps);
++              break;
++      case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
++              elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
++              break;
+       case V4L2_CTRL_TYPE_AREA:
+               elem_size = sizeof(struct v4l2_area);
+               break;
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1356,6 +1356,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+               case V4L2_PIX_FMT_VP8_FRAME:    descr = "VP8 Frame"; break;
+               case V4L2_PIX_FMT_VP9:          descr = "VP9"; break;
+               case V4L2_PIX_FMT_HEVC:         descr = "HEVC"; break; /* aka H.265 */
++              case V4L2_PIX_FMT_HEVC_SLICE:   descr = "HEVC Parsed Slice Data"; break;
+               case V4L2_PIX_FMT_FWHT:         descr = "FWHT"; break; /* used in vicodec */
+               case V4L2_PIX_FMT_FWHT_STATELESS:       descr = "FWHT Stateless"; break; /* used in vicodec */
+               case V4L2_PIX_FMT_CPIA1:        descr = "GSPCA CPiA YUV"; break;
+--- /dev/null
++++ b/include/media/hevc-ctrls.h
+@@ -0,0 +1,212 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * These are the HEVC state controls for use with stateless HEVC
++ * codec drivers.
++ *
++ * It turns out that these structs are not stable yet and will undergo
++ * more changes. So keep them private until they are stable and ready to
++ * become part of the official public API.
++ */
++
++#ifndef _HEVC_CTRLS_H_
++#define _HEVC_CTRLS_H_
++
++#include <linux/videodev2.h>
++
++/* The pixel format isn't stable at the moment and will likely be renamed. */
++#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */
++
++#define V4L2_CID_MPEG_VIDEO_HEVC_SPS          (V4L2_CID_MPEG_BASE + 1008)
++#define V4L2_CID_MPEG_VIDEO_HEVC_PPS          (V4L2_CID_MPEG_BASE + 1009)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010)
++#define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE  (V4L2_CID_MPEG_BASE + 1015)
++#define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE   (V4L2_CID_MPEG_BASE + 1016)
++
++/* enum v4l2_ctrl_type type values */
++#define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
++#define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
++#define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
++
++enum v4l2_mpeg_video_hevc_decode_mode {
++      V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++      V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED,
++};
++
++enum v4l2_mpeg_video_hevc_start_code {
++      V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++      V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B,
++};
++
++#define V4L2_HEVC_SLICE_TYPE_B        0
++#define V4L2_HEVC_SLICE_TYPE_P        1
++#define V4L2_HEVC_SLICE_TYPE_I        2
++
++#define V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE              (1ULL << 0)
++#define V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED                       (1ULL << 1)
++#define V4L2_HEVC_SPS_FLAG_AMP_ENABLED                                (1ULL << 2)
++#define V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET             (1ULL << 3)
++#define V4L2_HEVC_SPS_FLAG_PCM_ENABLED                                (1ULL << 4)
++#define V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED           (1ULL << 5)
++#define V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT         (1ULL << 6)
++#define V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED           (1ULL << 7)
++#define V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED     (1ULL << 8)
++
++/* The controls are not stable at the moment and will likely be reworked. */
++struct v4l2_ctrl_hevc_sps {
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */
++      __u16   pic_width_in_luma_samples;
++      __u16   pic_height_in_luma_samples;
++      __u8    bit_depth_luma_minus8;
++      __u8    bit_depth_chroma_minus8;
++      __u8    log2_max_pic_order_cnt_lsb_minus4;
++      __u8    sps_max_dec_pic_buffering_minus1;
++      __u8    sps_max_num_reorder_pics;
++      __u8    sps_max_latency_increase_plus1;
++      __u8    log2_min_luma_coding_block_size_minus3;
++      __u8    log2_diff_max_min_luma_coding_block_size;
++      __u8    log2_min_luma_transform_block_size_minus2;
++      __u8    log2_diff_max_min_luma_transform_block_size;
++      __u8    max_transform_hierarchy_depth_inter;
++      __u8    max_transform_hierarchy_depth_intra;
++      __u8    pcm_sample_bit_depth_luma_minus1;
++      __u8    pcm_sample_bit_depth_chroma_minus1;
++      __u8    log2_min_pcm_luma_coding_block_size_minus3;
++      __u8    log2_diff_max_min_pcm_luma_coding_block_size;
++      __u8    num_short_term_ref_pic_sets;
++      __u8    num_long_term_ref_pics_sps;
++      __u8    chroma_format_idc;
++
++      __u8    padding;
++
++      __u64   flags;
++};
++
++#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT            (1ULL << 0)
++#define V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT                        (1ULL << 1)
++#define V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED           (1ULL << 2)
++#define V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT                 (1ULL << 3)
++#define V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED             (1ULL << 4)
++#define V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED             (1ULL << 5)
++#define V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED                        (1ULL << 6)
++#define V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT        (1ULL << 7)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED                      (1ULL << 8)
++#define V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED                    (1ULL << 9)
++#define V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED          (1ULL << 10)
++#define V4L2_HEVC_PPS_FLAG_TILES_ENABLED                      (1ULL << 11)
++#define V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED                (1ULL << 12)
++#define V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED   (1ULL << 13)
++#define V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 14)
++#define V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED (1ULL << 15)
++#define V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER      (1ULL << 16)
++#define V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT         (1ULL << 17)
++#define V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (1ULL << 18)
++
++struct v4l2_ctrl_hevc_pps {
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */
++      __u8    num_extra_slice_header_bits;
++      __s8    init_qp_minus26;
++      __u8    diff_cu_qp_delta_depth;
++      __s8    pps_cb_qp_offset;
++      __s8    pps_cr_qp_offset;
++      __u8    num_tile_columns_minus1;
++      __u8    num_tile_rows_minus1;
++      __u8    column_width_minus1[20];
++      __u8    row_height_minus1[22];
++      __s8    pps_beta_offset_div2;
++      __s8    pps_tc_offset_div2;
++      __u8    log2_parallel_merge_level_minus2;
++
++      __u8    padding[4];
++      __u64   flags;
++};
++
++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE        0x01
++#define V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER 0x02
++#define V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR               0x03
++
++#define V4L2_HEVC_DPB_ENTRIES_NUM_MAX         16
++
++struct v4l2_hevc_dpb_entry {
++      __u64   timestamp;
++      __u8    rps;
++      __u8    field_pic;
++      __u16   pic_order_cnt[2];
++      __u8    padding[2];
++};
++
++struct v4l2_hevc_pred_weight_table {
++      __s8    delta_luma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      __s8    luma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      __s8    delta_chroma_weight_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++      __s8    chroma_offset_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++      __s8    delta_luma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      __s8    luma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      __s8    delta_chroma_weight_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++      __s8    chroma_offset_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2];
++
++      __u8    padding[6];
++
++      __u8    luma_log2_weight_denom;
++      __s8    delta_chroma_log2_weight_denom;
++};
++
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA            (1ULL << 0)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA          (1ULL << 1)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED        (1ULL << 2)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO                       (1ULL << 3)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT                        (1ULL << 4)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0                (1ULL << 5)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV            (1ULL << 6)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++
++struct v4l2_ctrl_hevc_slice_params {
++      __u32   bit_size;
++      __u32   data_bit_offset;
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
++      __u8    nal_unit_type;
++      __u8    nuh_temporal_id_plus1;
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++      __u8    slice_type;
++      __u8    colour_plane_id;
++      __u16   slice_pic_order_cnt;
++      __u8    num_ref_idx_l0_active_minus1;
++      __u8    num_ref_idx_l1_active_minus1;
++      __u8    collocated_ref_idx;
++      __u8    five_minus_max_num_merge_cand;
++      __s8    slice_qp_delta;
++      __s8    slice_cb_qp_offset;
++      __s8    slice_cr_qp_offset;
++      __s8    slice_act_y_qp_offset;
++      __s8    slice_act_cb_qp_offset;
++      __s8    slice_act_cr_qp_offset;
++      __s8    slice_beta_offset_div2;
++      __s8    slice_tc_offset_div2;
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
++      __u8    pic_struct;
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++      __u8    num_active_dpb_entries;
++      __u8    ref_idx_l0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      __u8    ref_idx_l1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++
++      __u8    num_rps_poc_st_curr_before;
++      __u8    num_rps_poc_st_curr_after;
++      __u8    num_rps_poc_lt_curr;
++
++      __u8    padding;
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++      struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: Weighted prediction parameter */
++      struct v4l2_hevc_pred_weight_table pred_weight_table;
++
++      __u64   flags;
++};
++
++#endif
+--- a/include/media/v4l2-ctrls.h
++++ b/include/media/v4l2-ctrls.h
+@@ -21,6 +21,7 @@
+ #include <media/fwht-ctrls.h>
+ #include <media/h264-ctrls.h>
+ #include <media/vp8-ctrls.h>
++#include <media/hevc-ctrls.h>
+ /* forward references */
+ struct file;
+@@ -50,6 +51,9 @@ struct poll_table_struct;
+  * @p_h264_slice_params:      Pointer to a struct v4l2_ctrl_h264_slice_params.
+  * @p_h264_decode_params:     Pointer to a struct v4l2_ctrl_h264_decode_params.
+  * @p_vp8_frame_header:               Pointer to a VP8 frame header structure.
++ * @p_hevc_sps:                       Pointer to an HEVC sequence parameter set structure.
++ * @p_hevc_pps:                       Pointer to an HEVC picture parameter set structure.
++ * @p_hevc_slice_params:      Pointer to an HEVC slice parameters structure.
+  * @p_area:                   Pointer to an area.
+  * @p:                                Pointer to a compound value.
+  */
+@@ -69,6 +73,9 @@ union v4l2_ctrl_ptr {
+       struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
+       struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
+       struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header;
++      struct v4l2_ctrl_hevc_sps *p_hevc_sps;
++      struct v4l2_ctrl_hevc_pps *p_hevc_pps;
++      struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
+       struct v4l2_area *p_area;
+       void *p;
+ };
diff --git a/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch b/target/linux/bcm27xx/patches-5.4/950-0504-media-v4l2-mem2mem-Fix-hold-buf-flag-checks.patch
new file mode 100644 (file)
index 0000000..18073a8
--- /dev/null
@@ -0,0 +1,37 @@
+From 73d8a76ec5b5e1240af4142a9ccbd39179d779af Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Wed, 6 Nov 2019 08:02:53 +0100
+Subject: [PATCH] media: v4l2-mem2mem: Fix hold buf flag checks
+
+Commit 1076df3a77b490d33429560a9e0603b3673223e2 upstream.
+
+Hold buf flag is set on output queue, not capture. Fix that.
+
+Fixes: f07602ac3887 ("media: v4l2-mem2mem: add new_frame detection")
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -335,7 +335,7 @@ static void __v4l2_m2m_try_queue(struct
+               }
+       }
+-      if (src && dst && (m2m_ctx->cap_q_ctx.q.subsystem_flags &
++      if (src && dst && (m2m_ctx->out_q_ctx.q.subsystem_flags &
+                          VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
+               m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp ||
+                       dst->vb2_buf.timestamp != src->vb2_buf.timestamp;
+@@ -474,7 +474,7 @@ void v4l2_m2m_job_finish(struct v4l2_m2m
+        * holding capture buffers. Those should use
+        * v4l2_m2m_buf_done_and_job_finish() instead.
+        */
+-      WARN_ON(m2m_ctx->cap_q_ctx.q.subsystem_flags &
++      WARN_ON(m2m_ctx->out_q_ctx.q.subsystem_flags &
+               VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF);
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
diff --git a/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch b/target/linux/bcm27xx/patches-5.4/950-0505-media-pixfmt-Document-the-HEVC-slice-pixel-format.patch
new file mode 100644 (file)
index 0000000..7398807
--- /dev/null
@@ -0,0 +1,50 @@
+From 662256810630f6ac6d06ee0cdc5f4660b25f7e98 Mon Sep 17 00:00:00 2001
+From: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Date: Tue, 22 Oct 2019 12:26:53 -0300
+Subject: [PATCH] media: pixfmt: Document the HEVC slice pixel format
+
+Commit de06f289283298e2938445019999cec46435375c upstream.
+
+Document the current state of the HEVC slice pixel format.
+The format will need to evolve in the future, which is why it is
+not part of the public API.
+
+Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
+---
+ .../media/uapi/v4l/pixfmt-compressed.rst      | 23 +++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst
+@@ -188,6 +188,29 @@ Compressed Formats
+       If :ref:`VIDIOC_ENUM_FMT` reports ``V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM``
+       then the decoder has no requirements since it can parse all the
+       information from the raw bytestream.
++    * .. _V4L2-PIX-FMT-HEVC-SLICE:
++
++      - ``V4L2_PIX_FMT_HEVC_SLICE``
++      - 'S265'
++      - HEVC parsed slice data, as extracted from the HEVC bitstream.
++      This format is adapted for stateless video decoders that implement a
++      HEVC pipeline (using the :ref:`mem2mem` and :ref:`media-request-api`).
++      This pixelformat has two modifiers that must be set at least once
++      through the ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE``
++        and ``V4L2_CID_MPEG_VIDEO_HEVC_START_CODE`` controls.
++      Metadata associated with the frame to decode is required to be passed
++      through the following controls :
++        * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``
++        * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``
++        * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``
++      See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`.
++      Buffers associated with this pixel format must contain the appropriate
++      number of macroblocks to decode a full corresponding frame.
++
++      .. note::
++
++         This format is not yet part of the public kernel API and it
++         is expected to change.
+     * .. _V4L2-PIX-FMT-FWHT:
+       - ``V4L2_PIX_FMT_FWHT``
diff --git a/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch b/target/linux/bcm27xx/patches-5.4/950-0506-media-uapi-hevc-Add-scaling-matrix-control.patch
new file mode 100644 (file)
index 0000000..c2cf27a
--- /dev/null
@@ -0,0 +1,150 @@
+From 70b5a28786215c996503210abd3e44c200771640 Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 13 Dec 2019 17:04:25 +0100
+Subject: [PATCH] media: uapi: hevc: Add scaling matrix control
+
+Taken from https://patchwork.linuxtv.org/patch/60728/
+Changes (mainly documentation) have been requested.
+
+HEVC has a scaling matrix concept. Add support for it.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+---
+ .../media/uapi/v4l/ext-ctrls-codec.rst        | 41 +++++++++++++++++++
+ .../media/uapi/v4l/pixfmt-compressed.rst      |  1 +
+ drivers/media/v4l2-core/v4l2-ctrls.c          | 10 +++++
+ include/media/hevc-ctrls.h                    | 11 +++++
+ 4 files changed, 63 insertions(+)
+
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -4174,6 +4174,47 @@ enum v4l2_mpeg_video_hevc_size_of_length
+       - ``padding[6]``
+       - Applications and drivers must set this to zero.
++``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX (struct)``
++    Specifies the scaling matrix (as extracted from the bitstream) for
++    the associated HEVC slice data. The bitstream parameters are
++    defined according to :ref:`hevc`, section 7.4.5 "Scaling list
++    data semantics". For further documentation, refer to the above
++    specification, unless there is an explicit comment stating
++    otherwise.
++
++    .. note::
++
++       This compound control is not yet part of the public kernel API and
++       it is expected to change.
++
++.. c:type:: v4l2_ctrl_hevc_scaling_matrix
++
++.. cssclass:: longtable
++
++.. flat-table:: struct v4l2_ctrl_hevc_scaling_matrix
++    :header-rows:  0
++    :stub-columns: 0
++    :widths:       1 1 2
++
++    * - __u8
++      - ``scaling_list_4x4[6][16]``
++      -
++    * - __u8
++      - ``scaling_list_8x8[6][64]``
++      -
++    * - __u8
++      - ``scaling_list_16x16[6][64]``
++      -
++    * - __u8
++      - ``scaling_list_32x32[2][64]``
++      -
++    * - __u8
++      - ``scaling_list_dc_coef_16x16[6]``
++      -
++    * - __u8
++      - ``scaling_list_dc_coef_32x32[2]``
++      -
++
+ ``V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE (enum)``
+     Specifies the decoding mode to use. Currently exposes slice-based and
+     frame-based decoding but new modes might be added later on.
+--- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst
+@@ -203,6 +203,7 @@ Compressed Formats
+         * ``V4L2_CID_MPEG_VIDEO_HEVC_SPS``
+         * ``V4L2_CID_MPEG_VIDEO_HEVC_PPS``
+         * ``V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS``
++        * ``V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX``
+       See the :ref:`associated Codec Control IDs <v4l2-mpeg-hevc>`.
+       Buffers associated with this pixel format must contain the appropriate
+       number of macroblocks to decode a full corresponding frame.
+--- a/drivers/media/v4l2-core/v4l2-ctrls.c
++++ b/drivers/media/v4l2-core/v4l2-ctrls.c
+@@ -974,6 +974,7 @@ const char *v4l2_ctrl_get_name(u32 id)
+       case V4L2_CID_MPEG_VIDEO_HEVC_SPS:                      return "HEVC Sequence Parameter Set";
+       case V4L2_CID_MPEG_VIDEO_HEVC_PPS:                      return "HEVC Picture Parameter Set";
+       case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:             return "HEVC Slice Parameters";
++      case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:           return "HEVC Scaling Matrix";
+       case V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE:              return "HEVC Decode Mode";
+       case V4L2_CID_MPEG_VIDEO_HEVC_START_CODE:               return "HEVC Start Code";
+@@ -1406,6 +1407,9 @@ void v4l2_ctrl_fill(u32 id, const char *
+       case V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS:
+               *type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS;
+               break;
++      case V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX:
++              *type = V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX;
++              break;
+       case V4L2_CID_UNIT_CELL_SIZE:
+               *type = V4L2_CTRL_TYPE_AREA;
+               *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+@@ -1852,6 +1856,9 @@ static int std_validate_compound(const s
+               zero_padding(*p_hevc_slice_params);
+               break;
++      case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
++              break;
++
+       case V4L2_CTRL_TYPE_AREA:
+               area = p;
+               if (!area->width || !area->height)
+@@ -2540,6 +2547,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s
+       case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+               elem_size = sizeof(struct v4l2_ctrl_hevc_slice_params);
+               break;
++      case V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX:
++              elem_size = sizeof(struct v4l2_ctrl_hevc_scaling_matrix);
++              break;
+       case V4L2_CTRL_TYPE_AREA:
+               elem_size = sizeof(struct v4l2_area);
+               break;
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -19,6 +19,7 @@
+ #define V4L2_CID_MPEG_VIDEO_HEVC_SPS          (V4L2_CID_MPEG_BASE + 1008)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_PPS          (V4L2_CID_MPEG_BASE + 1009)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS (V4L2_CID_MPEG_BASE + 1010)
++#define V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX       (V4L2_CID_MPEG_BASE + 1011)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE  (V4L2_CID_MPEG_BASE + 1015)
+ #define V4L2_CID_MPEG_VIDEO_HEVC_START_CODE   (V4L2_CID_MPEG_BASE + 1016)
+@@ -26,6 +27,7 @@
+ #define V4L2_CTRL_TYPE_HEVC_SPS 0x0120
+ #define V4L2_CTRL_TYPE_HEVC_PPS 0x0121
+ #define V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS 0x0122
++#define V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX 0x0123
+ enum v4l2_mpeg_video_hevc_decode_mode {
+       V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
+@@ -209,4 +211,13 @@ struct v4l2_ctrl_hevc_slice_params {
+       __u64   flags;
+ };
++struct v4l2_ctrl_hevc_scaling_matrix {
++      __u8    scaling_list_4x4[6][16];
++      __u8    scaling_list_8x8[6][64];
++      __u8    scaling_list_16x16[6][64];
++      __u8    scaling_list_32x32[2][64];
++      __u8    scaling_list_dc_coef_16x16[6];
++      __u8    scaling_list_dc_coef_32x32[2];
++};
++
+ #endif
diff --git a/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch b/target/linux/bcm27xx/patches-5.4/950-0507-media-uapi-hevc-Add-segment-address-field.patch
new file mode 100644 (file)
index 0000000..91f195b
--- /dev/null
@@ -0,0 +1,61 @@
+From 88eb3b015b6f61252fd214d39fc7fc0379ee0442 Mon Sep 17 00:00:00 2001
+From: Jernej Skrabec <jernej.skrabec@siol.net>
+Date: Fri, 13 Dec 2019 17:04:27 +0100
+Subject: [PATCH] media: uapi: hevc: Add segment address field
+
+From https://patchwork.linuxtv.org/patch/60725/
+Changes requested, but mainly docs.
+
+If HEVC frame consists of multiple slices, segment address has to be
+known in order to properly decode it.
+
+Add segment address field to slice parameters.
+
+Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
+---
+ Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 5 ++++-
+ include/media/hevc-ctrls.h                       | 5 ++++-
+ 2 files changed, 8 insertions(+), 2 deletions(-)
+
+--- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
++++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst
+@@ -3969,6 +3969,9 @@ enum v4l2_mpeg_video_hevc_size_of_length
+     * - __u32
+       - ``data_bit_offset``
+       - Offset (in bits) to the video data in the current slice data.
++    * - __u32
++      - ``slice_segment_addr``
++      -
+     * - __u8
+       - ``nal_unit_type``
+       -
+@@ -4046,7 +4049,7 @@ enum v4l2_mpeg_video_hevc_size_of_length
+       - ``num_rps_poc_lt_curr``
+       - The number of reference pictures in the long-term set.
+     * - __u8
+-      - ``padding[7]``
++      - ``padding[5]``
+       - Applications and drivers must set this to zero.
+     * - struct :c:type:`v4l2_hevc_dpb_entry`
+       - ``dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]``
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -167,6 +167,9 @@ struct v4l2_ctrl_hevc_slice_params {
+       __u32   bit_size;
+       __u32   data_bit_offset;
++      /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
++      __u32   slice_segment_addr;
++
+       /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
+       __u8    nal_unit_type;
+       __u8    nuh_temporal_id_plus1;
+@@ -200,7 +203,7 @@ struct v4l2_ctrl_hevc_slice_params {
+       __u8    num_rps_poc_st_curr_after;
+       __u8    num_rps_poc_lt_curr;
+-      __u8    padding;
++      __u8    padding[5];
+       /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+       struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
diff --git a/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch b/target/linux/bcm27xx/patches-5.4/950-0508-media-hevc_ctrls-Add-slice-param-dependent-slice-seg.patch
new file mode 100644 (file)
index 0000000..1353480
--- /dev/null
@@ -0,0 +1,23 @@
+From e8355c6b60adb6704c9fb863f380f2d7b457d82c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Mar 2020 18:34:01 +0000
+Subject: [PATCH] media: hevc_ctrls: Add slice param dependent slice
+ segment
+
+Adds V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT define.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ include/media/hevc-ctrls.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -162,6 +162,7 @@ struct v4l2_hevc_pred_weight_table {
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV            (1ULL << 6)
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED (1ULL << 7)
+ #define V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED (1ULL << 8)
++#define V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT   (1ULL << 9)
+ struct v4l2_ctrl_hevc_slice_params {
+       __u32   bit_size;
diff --git a/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch b/target/linux/bcm27xx/patches-5.4/950-0509-media-uapi-Add-hevc-ctrls-for-WPP-decoding.patch
new file mode 100644 (file)
index 0000000..234cb82
--- /dev/null
@@ -0,0 +1,40 @@
+From 6a42d17668699234bfa2d459e29cc2732e59759b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Mar 2020 19:00:17 +0000
+Subject: [PATCH] media: uapi: Add hevc ctrls for WPP decoding
+
+WPP can allow greater parallelism within the decode, but needs
+offset information to be passed in.
+
+Adds num_entry_point_offsets and entry_point_offset_minus1 to
+v4l2_ctrl_hevc_slice_params.
+
+This is based on Jernej Skrabec's patches for cedrus which
+implement the same feature.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ include/media/hevc-ctrls.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/include/media/hevc-ctrls.h
++++ b/include/media/hevc-ctrls.h
+@@ -170,6 +170,7 @@ struct v4l2_ctrl_hevc_slice_params {
+       /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+       __u32   slice_segment_addr;
++      __u32   num_entry_point_offsets;
+       /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
+       __u8    nal_unit_type;
+@@ -204,7 +205,9 @@ struct v4l2_ctrl_hevc_slice_params {
+       __u8    num_rps_poc_st_curr_after;
+       __u8    num_rps_poc_lt_curr;
+-      __u8    padding[5];
++      __u8    padding;
++
++      __u32   entry_point_offset_minus1[256];
+       /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
+       struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
diff --git a/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch b/target/linux/bcm27xx/patches-5.4/950-0510-media-videodev2.h-Add-a-format-for-column-YUV4-2-0-m.patch
new file mode 100644 (file)
index 0000000..840541c
--- /dev/null
@@ -0,0 +1,302 @@
+From a8f52dad0ed65192eb880a4a1ca90b236e99711e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Jan 2020 14:28:21 +0000
+Subject: [PATCH] media: videodev2.h: Add a format for column YUV4:2:0
+ modes
+
+Some of the Broadcom codec blocks use a column based YUV4:2:0 image
+format, so add the documentation and defines for both 8 and 10 bit
+versions.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../media/uapi/v4l/pixfmt-nv12-col128.rst     | 215 ++++++++++++++++++
+ Documentation/media/uapi/v4l/pixfmt-nv12.rst  |  14 +-
+ Documentation/media/uapi/v4l/yuv-formats.rst  |   1 +
+ drivers/media/v4l2-core/v4l2-ioctl.c          |   2 +
+ include/uapi/linux/videodev2.h                |   4 +
+ 5 files changed, 233 insertions(+), 3 deletions(-)
+ create mode 100644 Documentation/media/uapi/v4l/pixfmt-nv12-col128.rst
+
+--- /dev/null
++++ b/Documentation/media/uapi/v4l/pixfmt-nv12-col128.rst
+@@ -0,0 +1,215 @@
++.. Permission is granted to copy, distribute and/or modify this
++.. document under the terms of the GNU Free Documentation License,
++.. Version 1.1 or any later version published by the Free Software
++.. Foundation, with no Invariant Sections, no Front-Cover Texts
++.. and no Back-Cover Texts. A copy of the license is included at
++.. Documentation/media/uapi/fdl-appendix.rst.
++..
++.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
++
++.. _V4L2_PIX_FMT_NV12_COL128:
++.. _V4L2_PIX_FMT_NV12_10_COL128:
++
++********************************************************************************
++V4L2_PIX_FMT_NV12_COL128, V4L2_PIX_FMT_NV12_10_COL128
++********************************************************************************
++
++
++V4L2_PIX_FMT_NV21_COL128
++Formats with ½ horizontal and vertical chroma resolution. This format
++has two planes - one for luminance and one for chrominance. Chroma
++samples are interleaved. The difference to ``V4L2_PIX_FMT_NV12`` is the
++memory layout. The image is split into columns of 128 bytes wide rather than
++being in raster order.
++
++V4L2_PIX_FMT_NV12_10_COL128
++Follows the same pattern as ``V4L2_PIX_FMT_NV21_COL128`` with 128 byte, but is
++a 10bit format with 3 10-bit samples being packed into 4 bytes. Each 128 byte
++wide column therefore contains 96 samples.
++
++
++Description
++===========
++
++This is the two-plane versions of the YUV 4:2:0 format where data is
++grouped into 128 byte wide columns. The three components are separated into
++two sub-images or planes. The Y plane has one byte per pixel and pixels
++are grouped into 128 byte wide columns. The CbCr plane has the same width,
++in bytes, as the Y plane (and the image), but is half as tall in pixels.
++The chroma plane is also in 128 byte columns, reflecting 64 Cb and 64 Cr
++samples.
++
++The chroma samples for a column follow the luma samples. If there is any
++paddding, then that will be reflected via the selection API.
++The luma height must be a multiple of 2 lines.
++
++The normal bytesperline is effectively fixed at 128. However the format
++requires knowledge of the stride between columns, therefore the bytesperline
++value has been repurposed to denote the number of 128 byte long lines between
++the start of each column.
++
++**Byte Order.**
++
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 12 12 12 12 4 12 12 12 12
++
++    * - start + 0:
++      - Y'\ :sub:`0,0`
++      - Y'\ :sub:`0,1`
++      - Y'\ :sub:`0,2`
++      - Y'\ :sub:`0,3`
++      - ...
++      - Y'\ :sub:`0,124`
++      - Y'\ :sub:`0,125`
++      - Y'\ :sub:`0,126`
++      - Y'\ :sub:`0,127`
++    * - start + 128:
++      - Y'\ :sub:`1,0`
++      - Y'\ :sub:`1,1`
++      - Y'\ :sub:`1,2`
++      - Y'\ :sub:`1,3`
++      - ...
++      - Y'\ :sub:`1,124`
++      - Y'\ :sub:`1,125`
++      - Y'\ :sub:`1,126`
++      - Y'\ :sub:`1,127`
++    * - start + 256:
++      - Y'\ :sub:`2,0`
++      - Y'\ :sub:`2,1`
++      - Y'\ :sub:`2,2`
++      - Y'\ :sub:`2,3`
++      - ...
++      - Y'\ :sub:`2,124`
++      - Y'\ :sub:`2,125`
++      - Y'\ :sub:`2,126`
++      - Y'\ :sub:`2,127`
++    * - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++    * - start + ((height-1) * 128):
++      - Y'\ :sub:`height-1,0`
++      - Y'\ :sub:`height-1,1`
++      - Y'\ :sub:`height-1,2`
++      - Y'\ :sub:`height-1,3`
++      - ...
++      - Y'\ :sub:`height-1,124`
++      - Y'\ :sub:`height-1,125`
++      - Y'\ :sub:`height-1,126`
++      - Y'\ :sub:`height-1,127`
++    * - start + ((height) * 128):
++      - Cb\ :sub:`0,0`
++      - Cr\ :sub:`0,0`
++      - Cb\ :sub:`0,1`
++      - Cr\ :sub:`0,1`
++      - ...
++      - Cb\ :sub:`0,62`
++      - Cr\ :sub:`0,62`
++      - Cb\ :sub:`0,63`
++      - Cr\ :sub:`0,63`
++    * - start + ((height+1) * 128):
++      - Cb\ :sub:`1,0`
++      - Cr\ :sub:`1,0`
++      - Cb\ :sub:`1,1`
++      - Cr\ :sub:`1,1`
++      - ...
++      - Cb\ :sub:`1,62`
++      - Cr\ :sub:`1,62`
++      - Cb\ :sub:`1,63`
++      - Cr\ :sub:`1,63`
++    * - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++    * - start + ((height+(height/2)-1) * 128):
++      - Cb\ :sub:`(height/2)-1,0`
++      - Cr\ :sub:`(height/2)-1,0`
++      - Cb\ :sub:`(height/2)-1,1`
++      - Cr\ :sub:`(height/2)-1,1`
++      - ...
++      - Cb\ :sub:`(height/2)-1,62`
++      - Cr\ :sub:`(height/2)-1,62`
++      - Cb\ :sub:`(height/2)-1,63`
++      - Cr\ :sub:`(height/2)-1,63`
++    * - start + (bytesperline * 128):
++      - Y'\ :sub:`0,128`
++      - Y'\ :sub:`0,129`
++      - Y'\ :sub:`0,130`
++      - Y'\ :sub:`0,131`
++      - ...
++      - Y'\ :sub:`0,252`
++      - Y'\ :sub:`0,253`
++      - Y'\ :sub:`0,254`
++      - Y'\ :sub:`0,255`
++    * - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++      - ...
++
++V4L2_PIX_FMT_NV12_10_COL128 uses the same 128 byte column structure, but
++encodes 10-bit YUV.
++3 10-bit values are packed into 4 bytes as bits 9:0, 19:10, and 29:20, with
++bits 30 & 31 unused. For the luma plane, bits 9:0 are Y0, 19:10 are Y1, and
++29:20 are Y2. For the chroma plane the samples always come in pairs of Cr
++and Cb, so it needs to be considered 6 values packed in 8 bytes.
++
++Bit-packed representation.
++
++.. raw:: latex
++
++    \small
++
++.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 8 8 8 8
++
++    * - Y'\ :sub:`00[7:0]`
++      - Y'\ :sub:`01[5:0] (bits 7--2)` Y'\ :sub:`00[9:8]`\ (bits 1--0)
++      - Y'\ :sub:`02[3:0] (bits 7--4)` Y'\ :sub:`01[9:6]`\ (bits 3--0)
++      - unused (bits 7--6)` Y'\ :sub:`02[9:4]`\ (bits 5--0)
++
++.. raw:: latex
++
++    \small
++
++.. tabularcolumns:: |p{1.2cm}||p{1.2cm}||p{1.2cm}||p{1.2cm}|p{3.2cm}|p{3.2cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 12 12 12 12 12 12 12
++
++    * - Cb\ :sub:`00[7:0]`
++      - Cr\ :sub:`00[5:0]`\ (bits 7--2) Cb\ :sub:`00[9:8]`\ (bits 1--0)
++      - Cb\ :sub:`01[3:0]`\ (bits 7--4) Cr\ :sub:`00[9:6]`\ (bits 3--0)
++      - unused (bits 7--6) Cb\ :sub:`02[9:4]`\ (bits 5--0)
++      - Cr\ :sub:`01[7:0]`
++      - Cb\ :sub:`02[5:0]`\ (bits 7--2) Cr\ :sub:`01[9:8]`\ (bits 1--0)
++      - Cr\ :sub:`02[3:0]`\ (bits 7--4) Cb\ :sub:`02[9:6]`\ (bits 3--0)
++      - unused (bits 7--6) Cr\ :sub:`02[9:4]`\ (bits 5--0)
++
++.. raw:: latex
++
++    \normalsize
++
++
++
++
+--- a/Documentation/media/uapi/v4l/pixfmt-nv12.rst
++++ b/Documentation/media/uapi/v4l/pixfmt-nv12.rst
+@@ -10,9 +10,9 @@
+ .. _V4L2-PIX-FMT-NV12:
+ .. _V4L2-PIX-FMT-NV21:
+-******************************************************
+-V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21')
+-******************************************************
++********************************************************************************
++V4L2_PIX_FMT_NV12 ('NV12'), V4L2_PIX_FMT_NV21 ('NV21'), V4L2_PIX_FMT_NV12_COL128
++********************************************************************************
+ V4L2_PIX_FMT_NV21
+@@ -38,6 +38,14 @@ with a Cr byte.
+ If the Y plane has pad bytes after each row, then the CbCr plane has as
+ many pad bytes after its rows.
++``V4L2_PIX_FMT_NV12_COL128`` is the tiled version of
++``V4L2_PIX_FMT_NV12`` with the image broken down into 128 pixel wide columns of
++Y followed by the associated combined CbCr plane.
++The normal bytesperline is effectively fixed at 128. However the format
++requires knowledge of the stride between columns, therefore the bytesperline
++value has been repurposed to denote the number of 128 byte long lines between
++the start of each column.
++
+ **Byte Order.**
+ Each cell is one byte.
+--- a/Documentation/media/uapi/v4l/yuv-formats.rst
++++ b/Documentation/media/uapi/v4l/yuv-formats.rst
+@@ -57,6 +57,7 @@ to brightness information.
+     pixfmt-nv12
+     pixfmt-nv12m
+     pixfmt-nv12mt
++    pixfmt-nv12-col128
+     pixfmt-nv16
+     pixfmt-nv16m
+     pixfmt-nv24
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1258,6 +1258,8 @@ static void v4l_fill_fmtdesc(struct v4l2
+       case V4L2_PIX_FMT_NV61M:        descr = "Y/CrCb 4:2:2 (N-C)"; break;
+       case V4L2_PIX_FMT_NV12MT:       descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break;
+       case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break;
++      case V4L2_PIX_FMT_NV12_COL128:  descr = "Y/CbCr 4:2:0 (128b cols)"; break;
++      case V4L2_PIX_FMT_NV12_10_COL128: descr = "10-bit Y/CbCr 4:2:0 (128b cols)"; break;
+       case V4L2_PIX_FMT_YUV420M:      descr = "Planar YUV 4:2:0 (N-C)"; break;
+       case V4L2_PIX_FMT_YVU420M:      descr = "Planar YVU 4:2:0 (N-C)"; break;
+       case V4L2_PIX_FMT_YUV422M:      descr = "Planar YUV 4:2:2 (N-C)"; break;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -737,6 +737,10 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_INZI     v4l2_fourcc('I', 'N', 'Z', 'I') /* Intel Planar Greyscale 10-bit and Depth 16-bit */
+ #define V4L2_PIX_FMT_SUNXI_TILED_NV12 v4l2_fourcc('S', 'T', '1', '2') /* Sunxi Tiled NV12 Format */
+ #define V4L2_PIX_FMT_CNF4     v4l2_fourcc('C', 'N', 'F', '4') /* Intel 4-bit packed depth confidence information */
++#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12  Y/CbCr 4:2:0 128 pixel wide column */
++#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0')
++                                                              /* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in
++                                                               * a 128 bytes / 96 pixel wide column */
+ /* 10bit raw bayer packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
+ #define V4L2_PIX_FMT_IPU3_SBGGR10     v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch b/target/linux/bcm27xx/patches-5.4/950-0511-media-v4l2-mem2mem-allow-request-job-buffer-processi.patch
new file mode 100644 (file)
index 0000000..a3023ca
--- /dev/null
@@ -0,0 +1,274 @@
+From b8ae9d55d468a9f55524296247dba93531c29c99 Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Thu, 5 Mar 2020 14:46:54 +0000
+Subject: [PATCH] media: v4l2-mem2mem: allow request job buffer
+ processing after job finish
+
+Allow the capture buffer to be detached from a v4l2 request job such
+that another job can start before the capture buffer is returned. This
+allows h/w codecs that can process multiple requests at the same time
+to operate more efficiently.
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/media/v4l2-core/v4l2-mem2mem.c | 105 +++++++++++++++++++++++--
+ include/media/v4l2-mem2mem.h           |  47 +++++++++++
+ include/media/videobuf2-v4l2.h         |   3 +
+ 3 files changed, 149 insertions(+), 6 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
++++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
+@@ -399,15 +399,18 @@ static void v4l2_m2m_cancel_job(struct v
+ {
+       struct v4l2_m2m_dev *m2m_dev;
+       unsigned long flags;
++      bool det_abort_req;
+       m2m_dev = m2m_ctx->m2m_dev;
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      det_abort_req = !list_empty(&m2m_ctx->det_list);
+       m2m_ctx->job_flags |= TRANS_ABORT;
+       if (m2m_ctx->job_flags & TRANS_RUNNING) {
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+               if (m2m_dev->m2m_ops->job_abort)
+                       m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
++              det_abort_req = false;
+               dprintk("m2m_ctx %p running, will wait to complete\n", m2m_ctx);
+               wait_event(m2m_ctx->finished,
+                               !(m2m_ctx->job_flags & TRANS_RUNNING));
+@@ -421,6 +424,11 @@ static void v4l2_m2m_cancel_job(struct v
+               /* Do nothing, was not on queue/running */
+               spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+       }
++
++      /* Wait for detached buffers to come back too */
++      if (det_abort_req && m2m_dev->m2m_ops->job_abort)
++              m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
++      wait_event(m2m_ctx->det_empty, list_empty(&m2m_ctx->det_list));
+ }
+ /*
+@@ -458,6 +466,7 @@ static bool _v4l2_m2m_job_finish(struct
+       list_del(&m2m_dev->curr_ctx->queue);
+       m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
++      m2m_ctx->cap_detached = false;
+       wake_up(&m2m_dev->curr_ctx->finished);
+       m2m_dev->curr_ctx = NULL;
+       return true;
+@@ -485,6 +494,80 @@ void v4l2_m2m_job_finish(struct v4l2_m2m
+ }
+ EXPORT_SYMBOL(v4l2_m2m_job_finish);
++struct vb2_v4l2_buffer *_v4l2_m2m_cap_buf_detach(struct v4l2_m2m_ctx *m2m_ctx)
++{
++      struct vb2_v4l2_buffer *buf;
++
++      buf = v4l2_m2m_dst_buf_remove(m2m_ctx);
++      list_add_tail(&container_of(buf, struct v4l2_m2m_buffer, vb)->list,
++                    &m2m_ctx->det_list);
++      m2m_ctx->cap_detached = true;
++      buf->is_held = true;
++      buf->det_state = VB2_BUF_STATE_ACTIVE;
++
++      return buf;
++}
++
++struct vb2_v4l2_buffer *v4l2_m2m_cap_buf_detach(struct v4l2_m2m_dev *m2m_dev,
++                                              struct v4l2_m2m_ctx *m2m_ctx)
++{
++      unsigned long flags;
++      struct vb2_v4l2_buffer *src_buf, *dst_buf;
++
++      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++
++      dst_buf = NULL;
++      src_buf = v4l2_m2m_next_src_buf(m2m_ctx);
++
++      if (!(src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) &&
++          !m2m_ctx->cap_detached)
++              dst_buf = _v4l2_m2m_cap_buf_detach(m2m_ctx);
++
++      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++      return dst_buf;
++}
++EXPORT_SYMBOL(v4l2_m2m_cap_buf_detach);
++
++static void _v4l2_m2m_cap_buf_return(struct v4l2_m2m_ctx *m2m_ctx,
++                                   struct vb2_v4l2_buffer *buf,
++                                   enum vb2_buffer_state state)
++{
++      buf->det_state = state;
++
++      /*
++       * Always signal done in the order we got stuff
++       * Stop if we find a buf that is still in use
++       */
++      while (!list_empty(&m2m_ctx->det_list)) {
++              buf = &list_first_entry(&m2m_ctx->det_list,
++                                      struct v4l2_m2m_buffer, list)->vb;
++              state = buf->det_state;
++              if (state != VB2_BUF_STATE_DONE &&
++                  state != VB2_BUF_STATE_ERROR)
++                      return;
++              list_del(&container_of(buf, struct v4l2_m2m_buffer, vb)->list);
++              buf->det_state = VB2_BUF_STATE_DEQUEUED;
++              v4l2_m2m_buf_done(buf, state);
++      }
++      wake_up(&m2m_ctx->det_empty);
++}
++
++void v4l2_m2m_cap_buf_return(struct v4l2_m2m_dev *m2m_dev,
++                           struct v4l2_m2m_ctx *m2m_ctx,
++                           struct vb2_v4l2_buffer *buf,
++                           enum vb2_buffer_state state)
++{
++      unsigned long flags;
++
++      if (!buf)
++              return;
++
++      spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
++      _v4l2_m2m_cap_buf_return(m2m_ctx, buf, state);
++      spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
++}
++EXPORT_SYMBOL(v4l2_m2m_cap_buf_return);
++
+ void v4l2_m2m_buf_done_and_job_finish(struct v4l2_m2m_dev *m2m_dev,
+                                     struct v4l2_m2m_ctx *m2m_ctx,
+                                     enum vb2_buffer_state state)
+@@ -495,15 +578,23 @@ void v4l2_m2m_buf_done_and_job_finish(st
+       spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+       src_buf = v4l2_m2m_src_buf_remove(m2m_ctx);
+-      dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
+-      if (WARN_ON(!src_buf || !dst_buf))
++      if (WARN_ON(!src_buf))
+               goto unlock;
+       v4l2_m2m_buf_done(src_buf, state);
+-      dst_buf->is_held = src_buf->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+-      if (!dst_buf->is_held) {
+-              v4l2_m2m_dst_buf_remove(m2m_ctx);
+-              v4l2_m2m_buf_done(dst_buf, state);
++
++      if (!m2m_ctx->cap_detached) {
++              dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
++              if (WARN_ON(!dst_buf))
++                      goto unlock;
++
++              dst_buf->is_held = src_buf->flags
++                                  & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
++
++              if (!dst_buf->is_held) {
++                      dst_buf = _v4l2_m2m_cap_buf_detach(m2m_ctx);
++                      _v4l2_m2m_cap_buf_return(m2m_ctx, dst_buf, state);
++              }
+       }
+       schedule_next = _v4l2_m2m_job_finish(m2m_dev, m2m_ctx);
+ unlock:
+@@ -983,12 +1074,14 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(s
+       m2m_ctx->priv = drv_priv;
+       m2m_ctx->m2m_dev = m2m_dev;
+       init_waitqueue_head(&m2m_ctx->finished);
++      init_waitqueue_head(&m2m_ctx->det_empty);
+       out_q_ctx = &m2m_ctx->out_q_ctx;
+       cap_q_ctx = &m2m_ctx->cap_q_ctx;
+       INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
+       INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
++      INIT_LIST_HEAD(&m2m_ctx->det_list);
+       spin_lock_init(&out_q_ctx->rdy_spinlock);
+       spin_lock_init(&cap_q_ctx->rdy_spinlock);
+--- a/include/media/v4l2-mem2mem.h
++++ b/include/media/v4l2-mem2mem.h
+@@ -88,6 +88,9 @@ struct v4l2_m2m_queue_ctx {
+  *            %TRANS_QUEUED, %TRANS_RUNNING and %TRANS_ABORT.
+  * @finished: Wait queue used to signalize when a job queue finished.
+  * @priv: Instance private data
++ * @cap_detached: Current job's capture buffer has been detached
++ * @det_list: List of detached (post-job but still in flight) capture buffers
++ * @det_empty: Wait queue signalled when det_list goes empty
+  *
+  * The memory to memory context is specific to a file handle, NOT to e.g.
+  * a device.
+@@ -111,6 +114,11 @@ struct v4l2_m2m_ctx {
+       wait_queue_head_t               finished;
+       void                            *priv;
++
++      /* Detached buffer handling */
++      bool    cap_detached;
++      struct list_head                det_list;
++      wait_queue_head_t               det_empty;
+ };
+ /**
+@@ -216,6 +224,45 @@ v4l2_m2m_buf_done(struct vb2_v4l2_buffer
+ }
+ /**
++ * v4l2_m2m_cap_buf_detach() - detach the capture buffer from the job and
++ * return it.
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ *
++ * This function is designed to be used in conjunction with
++ * v4l2_m2m_buf_done_and_job_finish(). It allows the next job to start
++ * execution before the capture buffer is returned to the user which can be
++ * important if the underlying processing has multiple phases that are more
++ * efficiently executed in parallel.
++ *
++ * If used then it must be called before v4l2_m2m_buf_done_and_job_finish()
++ * as otherwise the buffer will have already gone.
++ *
++ * It is the callers reponsibilty to ensure that all detached buffers are
++ * returned.
++ */
++struct vb2_v4l2_buffer *v4l2_m2m_cap_buf_detach(struct v4l2_m2m_dev *m2m_dev,
++                                              struct v4l2_m2m_ctx *m2m_ctx);
++
++/**
++ * v4l2_m2m_cap_buf_return() - return a capture buffer, previously detached
++ * with v4l2_m2m_cap_buf_detach() to the user.
++ *
++ * @m2m_dev: opaque pointer to the internal data to handle M2M context
++ * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx
++ * @buf: the buffer to return
++ * @state: vb2 buffer state passed to v4l2_m2m_buf_done().
++ *
++ * Buffers returned by this function will be returned to the user in the order
++ * of the original jobs rather than the order in which this function is called.
++ */
++void v4l2_m2m_cap_buf_return(struct v4l2_m2m_dev *m2m_dev,
++                           struct v4l2_m2m_ctx *m2m_ctx,
++                           struct vb2_v4l2_buffer *buf,
++                           enum vb2_buffer_state state);
++
++/**
+  * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
+  *
+  * @file: pointer to struct &file
+--- a/include/media/videobuf2-v4l2.h
++++ b/include/media/videobuf2-v4l2.h
+@@ -35,6 +35,8 @@
+  * @request_fd:       the request_fd associated with this buffer
+  * @is_held:  if true, then this capture buffer was held
+  * @planes:   plane information (userptr/fd, length, bytesused, data_offset).
++ * @det_state:        if a detached request capture buffer then this contains its
++ *            current state
+  *
+  * Should contain enough information to be able to cover all the fields
+  * of &struct v4l2_buffer at ``videodev2.h``.
+@@ -49,6 +51,7 @@ struct vb2_v4l2_buffer {
+       __s32                   request_fd;
+       bool                    is_held;
+       struct vb2_plane        planes[VB2_MAX_PLANES];
++      enum vb2_buffer_state   det_state;
+ };
+ /* VB2 V4L2 flags as set in vb2_queue.subsystem_flags */
diff --git a/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch b/target/linux/bcm27xx/patches-5.4/950-0512-media-dt-bindings-media-Add-binding-for-the-Raspberr.patch
new file mode 100644 (file)
index 0000000..203e112
--- /dev/null
@@ -0,0 +1,106 @@
+From 15b4e8fa2d5101b989856c42cdae6ec764c99db0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 17 Mar 2020 10:53:16 +0000
+Subject: [PATCH] media: dt-bindings: media: Add binding for the
+ Raspberry PI HEVC decoder
+
+Adds a binding for the HEVC decoder found on the BCM2711 / Raspberry Pi 4.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../bindings/media/rpivid_hevc.yaml           | 72 +++++++++++++++++++
+ MAINTAINERS                                   |  7 ++
+ 2 files changed, 79 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/rpivid_hevc.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/rpivid_hevc.yaml
+@@ -0,0 +1,72 @@
++# SPDX-License-Identifier: GPL-2.0-only
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/rpivid_hevc.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi HEVC Decoder
++
++maintainers:
++  - Raspberry Pi <kernel-list@raspberrypi.com>
++
++description: |-
++  The Camera Adaptation Layer (CAL) is a key component for image capture
++  applications. The capture module provides the system interface and the
++  processing capability to connect CSI2 image-sensor modules to the
++  DRA72x device.
++
++properties:
++  compatible:
++    enum:
++      - raspberrypi,rpivid-vid-decoder
++
++  reg:
++    minItems: 2
++    items:
++      - description: The HEVC main register region
++      - description: The Interrupt controller register region
++
++  reg-names:
++    minItems: 2
++    items:
++      - const: hevc
++      - const: intc
++
++  interrupts:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: The HEVC block clock
++
++  clock-names:
++    items:
++      - const: hevc
++
++required:
++  - compatible
++  - reg
++  - reg-names
++  - interrupts
++  - clocks
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/interrupt-controller/arm-gic.h>
++
++    video-codec@7eb10000 {
++        compatible = "raspberrypi,rpivid-vid-decoder";
++        reg = <0x0 0x7eb10000 0x1000>,        /* INTC */
++              <0x0 0x7eb00000 0x10000>; /* HEVC */
++        reg-names = "intc",
++                    "hevc";
++
++        interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++        clocks = <&clk 0>;
++        clock-names = "hevc";
++    };
++
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3198,6 +3198,13 @@ N:      bcm2711
+ N:    bcm2835
+ F:    drivers/staging/vc04_services
++BROADCOM BCM2711 HEVC DECODER
++M:    Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
++L:    linux-media@vger.kernel.org
++S:    Maintained
++F:    Documentation/devicetree/bindings/media/rpivid_hevc.jaml
++F:    drivers/staging/media/rpivid
++
+ BROADCOM BCM2835 CAMERA DRIVER
+ M:    Dave Stevenson <dave.stevenson@raspberrypi.org>
+ L:    linux-media@vger.kernel.org
diff --git a/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch b/target/linux/bcm27xx/patches-5.4/950-0513-staging-media-Add-Raspberry-Pi-V4L2-H265-decoder.patch
new file mode 100644 (file)
index 0000000..134a685
--- /dev/null
@@ -0,0 +1,4341 @@
+From 82bbd353e2dc364bf37e6f0b91890cb432b1a72f Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Thu, 5 Mar 2020 18:30:41 +0000
+Subject: [PATCH] staging: media: Add Raspberry Pi V4L2 H265 decoder
+
+This driver is for the HEVC/H265 decoder block on the Raspberry
+Pi 4, and conforms to the V4L2 stateless decoder API.
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/staging/media/Kconfig               |    2 +
+ drivers/staging/media/Makefile              |    1 +
+ drivers/staging/media/rpivid/Kconfig        |   16 +
+ drivers/staging/media/rpivid/Makefile       |    5 +
+ drivers/staging/media/rpivid/rpivid.c       |  432 ++++
+ drivers/staging/media/rpivid/rpivid.h       |  181 ++
+ drivers/staging/media/rpivid/rpivid_dec.c   |   79 +
+ drivers/staging/media/rpivid/rpivid_dec.h   |   19 +
+ drivers/staging/media/rpivid/rpivid_h265.c  | 2275 +++++++++++++++++++
+ drivers/staging/media/rpivid/rpivid_hw.c    |  321 +++
+ drivers/staging/media/rpivid/rpivid_hw.h    |  300 +++
+ drivers/staging/media/rpivid/rpivid_video.c |  593 +++++
+ drivers/staging/media/rpivid/rpivid_video.h |   30 +
+ 14 files changed, 4256 insertions(+)
+ create mode 100644 drivers/staging/media/rpivid/Kconfig
+ create mode 100644 drivers/staging/media/rpivid/Makefile
+ create mode 100644 drivers/staging/media/rpivid/rpivid.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_dec.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_dec.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_h265.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_hw.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_hw.h
+ create mode 100644 drivers/staging/media/rpivid/rpivid_video.c
+ create mode 100644 drivers/staging/media/rpivid/rpivid_video.h
+
+--- a/drivers/staging/media/Kconfig
++++ b/drivers/staging/media/Kconfig
+@@ -30,6 +30,8 @@ source "drivers/staging/media/meson/vdec
+ source "drivers/staging/media/omap4iss/Kconfig"
++source "drivers/staging/media/rpivid/Kconfig"
++
+ source "drivers/staging/media/sunxi/Kconfig"
+ source "drivers/staging/media/tegra-vde/Kconfig"
+--- a/drivers/staging/media/Makefile
++++ b/drivers/staging/media/Makefile
+@@ -3,6 +3,7 @@ obj-$(CONFIG_VIDEO_ALLEGRO_DVT)        += alleg
+ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
+ obj-$(CONFIG_VIDEO_MESON_VDEC)        += meson/vdec/
+ obj-$(CONFIG_VIDEO_OMAP4)     += omap4iss/
++obj-$(CONFIG_VIDEO_RPIVID)    += rpivid/
+ obj-$(CONFIG_VIDEO_SUNXI)     += sunxi/
+ obj-$(CONFIG_TEGRA_VDE)               += tegra-vde/
+ obj-$(CONFIG_VIDEO_HANTRO)    += hantro/
+--- /dev/null
++++ b/drivers/staging/media/rpivid/Kconfig
+@@ -0,0 +1,16 @@
++# SPDX-License-Identifier: GPL-2.0
++
++config VIDEO_RPIVID
++      tristate "Rpi H265 driver"
++      depends on VIDEO_DEV && VIDEO_V4L2
++      depends on MEDIA_CONTROLLER
++      depends on OF
++      depends on MEDIA_CONTROLLER_REQUEST_API
++      select VIDEOBUF2_DMA_CONTIG
++      select V4L2_MEM2MEM_DEV
++      help
++        Support for the Rpi H265 h/w decoder.
++
++        To compile this driver as a module, choose M here: the module
++        will be called rpivid-hevc.
++
+--- /dev/null
++++ b/drivers/staging/media/rpivid/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_VIDEO_RPIVID) += rpivid-hevc.o
++
++rpivid-hevc-y = rpivid.o rpivid_video.o rpivid_dec.o \
++               rpivid_hw.o rpivid_h265.o
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid.c
+@@ -0,0 +1,432 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/of.h>
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_video.h"
++#include "rpivid_hw.h"
++#include "rpivid_dec.h"
++
++/*
++ * Default /dev/videoN node number.
++ * Deliberately avoid the very low numbers as these are often taken by webcams
++ * etc, and simple apps tend to only go for /dev/video0.
++ */
++static int video_nr = 19;
++module_param(video_nr, int, 0644);
++MODULE_PARM_DESC(video_nr, "decoder video device number");
++
++static const struct rpivid_control rpivid_ctrls[] = {
++      {
++              .cfg = {
++                      .id     = V4L2_CID_MPEG_VIDEO_HEVC_SPS,
++              },
++              .required       = true,
++      },
++      {
++              .cfg = {
++                      .id     = V4L2_CID_MPEG_VIDEO_HEVC_PPS,
++              },
++              .required       = true,
++      },
++      {
++              .cfg = {
++                      .id = V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX,
++              },
++              .required       = false,
++      },
++      {
++              .cfg = {
++                      .id     = V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
++              },
++              .required       = true,
++      },
++      {
++              .cfg = {
++                      .id     = V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE,
++                      .max    = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++                      .def    = V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED,
++              },
++              .required       = false,
++      },
++      {
++              .cfg = {
++                      .id     = V4L2_CID_MPEG_VIDEO_HEVC_START_CODE,
++                      .max    = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++                      .def    = V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE,
++              },
++              .required       = false,
++      },
++};
++
++#define rpivid_ctrls_COUNT    ARRAY_SIZE(rpivid_ctrls)
++
++void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id)
++{
++      unsigned int i;
++
++      for (i = 0; ctx->ctrls[i]; i++)
++              if (ctx->ctrls[i]->id == id)
++                      return ctx->ctrls[i]->p_cur.p;
++
++      return NULL;
++}
++
++static int rpivid_init_ctrls(struct rpivid_dev *dev, struct rpivid_ctx *ctx)
++{
++      struct v4l2_ctrl_handler *hdl = &ctx->hdl;
++      struct v4l2_ctrl *ctrl;
++      unsigned int ctrl_size;
++      unsigned int i;
++
++      v4l2_ctrl_handler_init(hdl, rpivid_ctrls_COUNT);
++      if (hdl->error) {
++              v4l2_err(&dev->v4l2_dev,
++                       "Failed to initialize control handler\n");
++              return hdl->error;
++      }
++
++      ctrl_size = sizeof(ctrl) * rpivid_ctrls_COUNT + 1;
++
++      ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL);
++      if (!ctx->ctrls)
++              return -ENOMEM;
++
++      for (i = 0; i < rpivid_ctrls_COUNT; i++) {
++              ctrl = v4l2_ctrl_new_custom(hdl, &rpivid_ctrls[i].cfg,
++                                          NULL);
++              if (hdl->error) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Failed to create new custom control id=%#x\n",
++                               rpivid_ctrls[i].cfg.id);
++
++                      v4l2_ctrl_handler_free(hdl);
++                      kfree(ctx->ctrls);
++                      return hdl->error;
++              }
++
++              ctx->ctrls[i] = ctrl;
++      }
++
++      ctx->fh.ctrl_handler = hdl;
++      v4l2_ctrl_handler_setup(hdl);
++
++      return 0;
++}
++
++static int rpivid_request_validate(struct media_request *req)
++{
++      struct media_request_object *obj;
++      struct v4l2_ctrl_handler *parent_hdl, *hdl;
++      struct rpivid_ctx *ctx = NULL;
++      struct v4l2_ctrl *ctrl_test;
++      unsigned int count;
++      unsigned int i;
++
++      list_for_each_entry(obj, &req->objects, list) {
++              struct vb2_buffer *vb;
++
++              if (vb2_request_object_is_buffer(obj)) {
++                      vb = container_of(obj, struct vb2_buffer, req_obj);
++                      ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++                      break;
++              }
++      }
++
++      if (!ctx)
++              return -ENOENT;
++
++      count = vb2_request_buffer_cnt(req);
++      if (!count) {
++              v4l2_info(&ctx->dev->v4l2_dev,
++                        "No buffer was provided with the request\n");
++              return -ENOENT;
++      } else if (count > 1) {
++              v4l2_info(&ctx->dev->v4l2_dev,
++                        "More than one buffer was provided with the request\n");
++              return -EINVAL;
++      }
++
++      parent_hdl = &ctx->hdl;
++
++      hdl = v4l2_ctrl_request_hdl_find(req, parent_hdl);
++      if (!hdl) {
++              v4l2_info(&ctx->dev->v4l2_dev, "Missing codec control(s)\n");
++              return -ENOENT;
++      }
++
++      for (i = 0; i < rpivid_ctrls_COUNT; i++) {
++              if (!rpivid_ctrls[i].required)
++                      continue;
++
++              ctrl_test =
++                      v4l2_ctrl_request_hdl_ctrl_find(hdl,
++                                                      rpivid_ctrls[i].cfg.id);
++              if (!ctrl_test) {
++                      v4l2_info(&ctx->dev->v4l2_dev,
++                                "Missing required codec control\n");
++                      return -ENOENT;
++              }
++      }
++
++      v4l2_ctrl_request_hdl_put(hdl);
++
++      return vb2_request_validate(req);
++}
++
++static int rpivid_open(struct file *file)
++{
++      struct rpivid_dev *dev = video_drvdata(file);
++      struct rpivid_ctx *ctx = NULL;
++      int ret;
++
++      if (mutex_lock_interruptible(&dev->dev_mutex))
++              return -ERESTARTSYS;
++
++      ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++      if (!ctx) {
++              mutex_unlock(&dev->dev_mutex);
++              return -ENOMEM;
++      }
++
++      v4l2_fh_init(&ctx->fh, video_devdata(file));
++      file->private_data = &ctx->fh;
++      ctx->dev = dev;
++
++      ret = rpivid_init_ctrls(dev, ctx);
++      if (ret)
++              goto err_free;
++
++      ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
++                                          &rpivid_queue_init);
++      if (IS_ERR(ctx->fh.m2m_ctx)) {
++              ret = PTR_ERR(ctx->fh.m2m_ctx);
++              goto err_ctrls;
++      }
++
++      /* The only bit of format info that we can guess now is H265 src
++       * Everything else we need more info for
++       */
++      ctx->src_fmt.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
++      rpivid_prepare_src_format(&ctx->src_fmt);
++
++      v4l2_fh_add(&ctx->fh);
++
++      mutex_unlock(&dev->dev_mutex);
++
++      return 0;
++
++err_ctrls:
++      v4l2_ctrl_handler_free(&ctx->hdl);
++err_free:
++      kfree(ctx);
++      mutex_unlock(&dev->dev_mutex);
++
++      return ret;
++}
++
++static int rpivid_release(struct file *file)
++{
++      struct rpivid_dev *dev = video_drvdata(file);
++      struct rpivid_ctx *ctx = container_of(file->private_data,
++                                            struct rpivid_ctx, fh);
++
++      mutex_lock(&dev->dev_mutex);
++
++      v4l2_fh_del(&ctx->fh);
++      v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
++
++      v4l2_ctrl_handler_free(&ctx->hdl);
++      kfree(ctx->ctrls);
++
++      v4l2_fh_exit(&ctx->fh);
++
++      kfree(ctx);
++
++      mutex_unlock(&dev->dev_mutex);
++
++      return 0;
++}
++
++static const struct v4l2_file_operations rpivid_fops = {
++      .owner          = THIS_MODULE,
++      .open           = rpivid_open,
++      .release        = rpivid_release,
++      .poll           = v4l2_m2m_fop_poll,
++      .unlocked_ioctl = video_ioctl2,
++      .mmap           = v4l2_m2m_fop_mmap,
++};
++
++static const struct video_device rpivid_video_device = {
++      .name           = RPIVID_NAME,
++      .vfl_dir        = VFL_DIR_M2M,
++      .fops           = &rpivid_fops,
++      .ioctl_ops      = &rpivid_ioctl_ops,
++      .minor          = -1,
++      .release        = video_device_release_empty,
++      .device_caps    = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING,
++};
++
++static const struct v4l2_m2m_ops rpivid_m2m_ops = {
++      .device_run     = rpivid_device_run,
++};
++
++static const struct media_device_ops rpivid_m2m_media_ops = {
++      .req_validate   = rpivid_request_validate,
++      .req_queue      = v4l2_m2m_request_queue,
++};
++
++static int rpivid_probe(struct platform_device *pdev)
++{
++      struct rpivid_dev *dev;
++      struct video_device *vfd;
++      int ret;
++
++      dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
++      if (!dev)
++              return -ENOMEM;
++
++      dev->vfd = rpivid_video_device;
++      dev->dev = &pdev->dev;
++      dev->pdev = pdev;
++
++      ret = 0;
++      ret = rpivid_hw_probe(dev);
++      if (ret) {
++              dev_err(&pdev->dev, "Failed to probe hardware\n");
++              return ret;
++      }
++
++      dev->dec_ops = &rpivid_dec_ops_h265;
++
++      mutex_init(&dev->dev_mutex);
++
++      ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
++      if (ret) {
++              dev_err(&pdev->dev, "Failed to register V4L2 device\n");
++              return ret;
++      }
++
++      vfd = &dev->vfd;
++      vfd->lock = &dev->dev_mutex;
++      vfd->v4l2_dev = &dev->v4l2_dev;
++
++      snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
++      video_set_drvdata(vfd, dev);
++
++      dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
++      if (IS_ERR(dev->m2m_dev)) {
++              v4l2_err(&dev->v4l2_dev,
++                       "Failed to initialize V4L2 M2M device\n");
++              ret = PTR_ERR(dev->m2m_dev);
++
++              goto err_v4l2;
++      }
++
++      dev->mdev.dev = &pdev->dev;
++      strscpy(dev->mdev.model, RPIVID_NAME, sizeof(dev->mdev.model));
++      strscpy(dev->mdev.bus_info, "platform:" RPIVID_NAME,
++              sizeof(dev->mdev.bus_info));
++
++      media_device_init(&dev->mdev);
++      dev->mdev.ops = &rpivid_m2m_media_ops;
++      dev->v4l2_dev.mdev = &dev->mdev;
++
++      ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
++      if (ret) {
++              v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
++              goto err_m2m;
++      }
++
++      v4l2_info(&dev->v4l2_dev,
++                "Device registered as /dev/video%d\n", vfd->num);
++
++      ret = v4l2_m2m_register_media_controller(dev->m2m_dev, vfd,
++                                               MEDIA_ENT_F_PROC_VIDEO_DECODER);
++      if (ret) {
++              v4l2_err(&dev->v4l2_dev,
++                       "Failed to initialize V4L2 M2M media controller\n");
++              goto err_video;
++      }
++
++      ret = media_device_register(&dev->mdev);
++      if (ret) {
++              v4l2_err(&dev->v4l2_dev, "Failed to register media device\n");
++              goto err_m2m_mc;
++      }
++
++      platform_set_drvdata(pdev, dev);
++
++      return 0;
++
++err_m2m_mc:
++      v4l2_m2m_unregister_media_controller(dev->m2m_dev);
++err_video:
++      video_unregister_device(&dev->vfd);
++err_m2m:
++      v4l2_m2m_release(dev->m2m_dev);
++err_v4l2:
++      v4l2_device_unregister(&dev->v4l2_dev);
++
++      return ret;
++}
++
++static int rpivid_remove(struct platform_device *pdev)
++{
++      struct rpivid_dev *dev = platform_get_drvdata(pdev);
++
++      if (media_devnode_is_registered(dev->mdev.devnode)) {
++              media_device_unregister(&dev->mdev);
++              v4l2_m2m_unregister_media_controller(dev->m2m_dev);
++              media_device_cleanup(&dev->mdev);
++      }
++
++      v4l2_m2m_release(dev->m2m_dev);
++      video_unregister_device(&dev->vfd);
++      v4l2_device_unregister(&dev->v4l2_dev);
++
++      rpivid_hw_remove(dev);
++
++      return 0;
++}
++
++static const struct of_device_id rpivid_dt_match[] = {
++      {
++              .compatible = "raspberrypi,rpivid-vid-decoder",
++      },
++      { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rpivid_dt_match);
++
++static struct platform_driver rpivid_driver = {
++      .probe          = rpivid_probe,
++      .remove         = rpivid_remove,
++      .driver         = {
++              .name = RPIVID_NAME,
++              .of_match_table = of_match_ptr(rpivid_dt_match),
++      },
++};
++module_platform_driver(rpivid_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("John Cox <jc@kynesim.co.uk>");
++MODULE_DESCRIPTION("Raspberry Pi HEVC V4L2 driver");
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid.h
+@@ -0,0 +1,181 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_H_
++#define _RPIVID_H_
++
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-mem2mem.h>
++#include <media/videobuf2-v4l2.h>
++#include <media/videobuf2-dma-contig.h>
++
++#define OPT_DEBUG_POLL_IRQ  0
++
++#define RPIVID_NAME                   "rpivid"
++
++#define RPIVID_CAPABILITY_UNTILED     BIT(0)
++#define RPIVID_CAPABILITY_H265_DEC    BIT(1)
++
++#define RPIVID_QUIRK_NO_DMA_OFFSET    BIT(0)
++
++#define RPIVID_SRC_PIXELFORMAT_DEFAULT        V4L2_PIX_FMT_HEVC_SLICE
++
++enum rpivid_irq_status {
++      RPIVID_IRQ_NONE,
++      RPIVID_IRQ_ERROR,
++      RPIVID_IRQ_OK,
++};
++
++struct rpivid_control {
++      struct v4l2_ctrl_config cfg;
++      unsigned char           required:1;
++};
++
++struct rpivid_h265_run {
++      const struct v4l2_ctrl_hevc_sps                 *sps;
++      const struct v4l2_ctrl_hevc_pps                 *pps;
++      const struct v4l2_ctrl_hevc_slice_params        *slice_params;
++      const struct v4l2_ctrl_hevc_scaling_matrix      *scaling_matrix;
++};
++
++struct rpivid_run {
++      struct vb2_v4l2_buffer  *src;
++      struct vb2_v4l2_buffer  *dst;
++
++      struct rpivid_h265_run  h265;
++};
++
++struct rpivid_buffer {
++      struct v4l2_m2m_buffer          m2m_buf;
++};
++
++struct rpivid_dec_state;
++struct rpivid_dec_env;
++#define RPIVID_DEC_ENV_COUNT 3
++
++struct rpivid_gptr {
++      size_t size;
++      __u8 *ptr;
++      dma_addr_t addr;
++      unsigned long attrs;
++};
++
++struct rpivid_dev;
++typedef void (*rpivid_irq_callback)(struct rpivid_dev *dev, void *ctx);
++
++struct rpivid_q_aux;
++#define RPIVID_AUX_ENT_COUNT VB2_MAX_FRAME
++
++#define RPIVID_P2BUF_COUNT 2
++
++struct rpivid_ctx {
++      struct v4l2_fh                  fh;
++      struct rpivid_dev               *dev;
++
++      struct v4l2_pix_format          src_fmt;
++      struct v4l2_pix_format          dst_fmt;
++      int dst_fmt_set;
++
++      struct v4l2_ctrl_handler        hdl;
++      struct v4l2_ctrl                **ctrls;
++
++      /* Decode state - stateless decoder my *** */
++      /* state contains stuff that is only needed in phase0
++       * it could be held in dec_env but that would be wasteful
++       */
++      struct rpivid_dec_state *state;
++      struct rpivid_dec_env *dec0;
++
++      /* Spinlock protecting dec_free */
++      spinlock_t dec_lock;
++      struct rpivid_dec_env *dec_free;
++
++      struct rpivid_dec_env *dec_pool;
++
++      /* Some of these should be in dev */
++      struct rpivid_gptr bitbufs[1];  /* Will be 2 */
++      struct rpivid_gptr cmdbufs[1];  /* Will be 2 */
++      unsigned int p2idx;
++      atomic_t p2out;
++      struct rpivid_gptr pu_bufs[RPIVID_P2BUF_COUNT];
++      struct rpivid_gptr coeff_bufs[RPIVID_P2BUF_COUNT];
++
++      /* Spinlock protecting aux_free */
++      spinlock_t aux_lock;
++      struct rpivid_q_aux *aux_free;
++
++      struct rpivid_q_aux *aux_ents[RPIVID_AUX_ENT_COUNT];
++
++      unsigned int colmv_stride;
++      unsigned int colmv_picsize;
++};
++
++struct rpivid_dec_ops {
++      void (*setup)(struct rpivid_ctx *ctx, struct rpivid_run *run);
++      int (*start)(struct rpivid_ctx *ctx);
++      void (*stop)(struct rpivid_ctx *ctx);
++      void (*trigger)(struct rpivid_ctx *ctx);
++};
++
++struct rpivid_variant {
++      unsigned int    capabilities;
++      unsigned int    quirks;
++      unsigned int    mod_rate;
++};
++
++struct rpivid_hw_irq_ent;
++
++struct rpivid_hw_irq_ctrl {
++      /* Spinlock protecting claim and tail */
++      spinlock_t lock;
++      struct rpivid_hw_irq_ent *claim;
++      struct rpivid_hw_irq_ent *tail;
++
++      /* Ent for pending irq - also prevents sched */
++      struct rpivid_hw_irq_ent *irq;
++      /* Non-zero => do not start a new job - outer layer sched pending */
++      int no_sched;
++      /* Thread CB requested */
++      bool thread_reqed;
++};
++
++struct rpivid_dev {
++      struct v4l2_device      v4l2_dev;
++      struct video_device     vfd;
++      struct media_device     mdev;
++      struct media_pad        pad[2];
++      struct platform_device  *pdev;
++      struct device           *dev;
++      struct v4l2_m2m_dev     *m2m_dev;
++      struct rpivid_dec_ops   *dec_ops;
++
++      /* Device file mutex */
++      struct mutex            dev_mutex;
++
++      void __iomem            *base_irq;
++      void __iomem            *base_h265;
++
++      struct clk              *clock;
++
++      struct rpivid_hw_irq_ctrl ic_active1;
++      struct rpivid_hw_irq_ctrl ic_active2;
++};
++
++extern struct rpivid_dec_ops rpivid_dec_ops_h265;
++
++void *rpivid_find_control_data(struct rpivid_ctx *ctx, u32 id);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_dec.c
+@@ -0,0 +1,79 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_dec.h"
++
++void rpivid_device_run(void *priv)
++{
++      struct rpivid_ctx *ctx = priv;
++      struct rpivid_dev *dev = ctx->dev;
++      struct rpivid_run run = {};
++      struct media_request *src_req;
++
++      run.src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
++      run.dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
++
++      if (!run.src || !run.dst) {
++              v4l2_err(&dev->v4l2_dev, "%s: Missing buffer: src=%p, dst=%p\n",
++                       __func__, run.src, run.dst);
++              /* We are stuffed - this probably won't dig us out of our
++               * current situation but it is better than nothing
++               */
++              v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                               VB2_BUF_STATE_ERROR);
++              return;
++      }
++
++      /* Apply request(s) controls if needed. */
++      src_req = run.src->vb2_buf.req_obj.req;
++
++      if (src_req)
++              v4l2_ctrl_request_setup(src_req, &ctx->hdl);
++
++      switch (ctx->src_fmt.pixelformat) {
++      case V4L2_PIX_FMT_HEVC_SLICE:
++              run.h265.sps =
++                      rpivid_find_control_data(ctx,
++                                               V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++              run.h265.pps =
++                      rpivid_find_control_data(ctx,
++                                               V4L2_CID_MPEG_VIDEO_HEVC_PPS);
++              run.h265.slice_params =
++                      rpivid_find_control_data(ctx,
++                                               V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS);
++              run.h265.scaling_matrix =
++                      rpivid_find_control_data(ctx,
++                                               V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX);
++              break;
++
++      default:
++              break;
++      }
++
++      v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
++
++      dev->dec_ops->setup(ctx, &run);
++
++      /* Complete request(s) controls if needed. */
++
++      if (src_req)
++              v4l2_ctrl_request_complete(src_req, &ctx->hdl);
++
++      dev->dec_ops->trigger(ctx);
++}
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_dec.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_DEC_H_
++#define _RPIVID_DEC_H_
++
++void rpivid_device_run(void *priv);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_h265.c
+@@ -0,0 +1,2275 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <linux/delay.h>
++#include <linux/types.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "rpivid.h"
++#include "rpivid_hw.h"
++
++#define DEBUG_TRACE_P1_CMD 0
++#define DEBUG_TRACE_EXECUTION 0
++
++#if DEBUG_TRACE_EXECUTION
++#define xtrace_in(dev_, de_)\
++      v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: in\n",   __func__,\
++                (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_ok(dev_, de_)\
++      v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: ok\n",   __func__,\
++                (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_fin(dev_, de_)\
++      v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: finish\n", __func__,\
++                (de_) == NULL ? -1 : (de_)->decode_order)
++#define xtrace_fail(dev_, de_)\
++      v4l2_info(&(dev_)->v4l2_dev, "%s[%d]: FAIL\n", __func__,\
++                (de_) == NULL ? -1 : (de_)->decode_order)
++#else
++#define xtrace_in(dev_, de_)
++#define xtrace_ok(dev_, de_)
++#define xtrace_fin(dev_, de_)
++#define xtrace_fail(dev_, de_)
++#endif
++
++enum hevc_slice_type {
++      HEVC_SLICE_B = 0,
++      HEVC_SLICE_P = 1,
++      HEVC_SLICE_I = 2,
++};
++
++enum hevc_layer { L0 = 0, L1 = 1 };
++
++static int gptr_alloc(struct rpivid_dev *const dev, struct rpivid_gptr *gptr,
++                    size_t size, unsigned long attrs)
++{
++      gptr->size = size;
++      gptr->attrs = attrs;
++      gptr->addr = 0;
++      gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size, &gptr->addr,
++                                  GFP_KERNEL, gptr->attrs);
++      return !gptr->ptr ? -ENOMEM : 0;
++}
++
++static void gptr_free(struct rpivid_dev *const dev,
++                    struct rpivid_gptr *const gptr)
++{
++      if (gptr->ptr)
++              dma_free_attrs(dev->dev, gptr->size, gptr->ptr, gptr->addr,
++                             gptr->attrs);
++      gptr->size = 0;
++      gptr->ptr = NULL;
++      gptr->addr = 0;
++      gptr->attrs = 0;
++}
++
++/* Realloc but do not copy */
++static int gptr_realloc_new(struct rpivid_dev * const dev,
++                          struct rpivid_gptr * const gptr, size_t size)
++{
++      if (size == gptr->size)
++              return 0;
++
++      if (gptr->ptr)
++              dma_free_attrs(dev->dev, gptr->size, gptr->ptr,
++                             gptr->addr, gptr->attrs);
++
++      gptr->addr = 0;
++      gptr->size = size;
++      gptr->ptr = dma_alloc_attrs(dev->dev, gptr->size,
++                                  &gptr->addr, GFP_KERNEL, gptr->attrs);
++      return gptr->ptr ? 0 : -ENOMEM;
++}
++
++/* floor(log2(x)) */
++static unsigned int log2_size(size_t x)
++{
++      unsigned int n = 0;
++
++      if (x & ~0xffff) {
++              n += 16;
++              x >>= 16;
++      }
++      if (x & ~0xff) {
++              n += 8;
++              x >>= 8;
++      }
++      if (x & ~0xf) {
++              n += 4;
++              x >>= 4;
++      }
++      if (x & ~3) {
++              n += 2;
++              x >>= 2;
++      }
++      return (x & ~1) ? n + 1 : n;
++}
++
++static size_t round_up_size(const size_t x)
++{
++      /* Admit no size < 256 */
++      const unsigned int n = x < 256 ? 8 : log2_size(x) - 1;
++
++      return x >= (3 << n) ? 4 << n : (3 << n);
++}
++
++static size_t next_size(const size_t x)
++{
++      return round_up_size(x + 1);
++}
++
++#define NUM_SCALING_FACTORS 4064 /* Not a typo = 0xbe0 + 0x400 */
++
++#define AXI_BASE64 0
++
++#define PROB_BACKUP ((20 << 12) + (20 << 6) + (0 << 0))
++#define PROB_RELOAD ((20 << 12) + (20 << 0) + (0 << 6))
++
++#define HEVC_MAX_REFS V4L2_HEVC_DPB_ENTRIES_NUM_MAX
++
++//////////////////////////////////////////////////////////////////////////////
++
++struct rpi_cmd {
++      u32 addr;
++      u32 data;
++} __packed;
++
++struct rpivid_q_aux {
++      unsigned int refcount;
++      unsigned int q_index;
++      struct rpivid_q_aux *next;
++      struct rpivid_gptr col;
++};
++
++//////////////////////////////////////////////////////////////////////////////
++
++enum rpivid_decode_state {
++      RPIVID_DECODE_SLICE_START,
++      RPIVID_DECODE_SLICE_CONTINUE,
++      RPIVID_DECODE_ERROR_CONTINUE,
++      RPIVID_DECODE_ERROR_DONE,
++      RPIVID_DECODE_PHASE1,
++      RPIVID_DECODE_END,
++};
++
++struct rpivid_dec_env {
++      struct rpivid_ctx *ctx;
++      struct rpivid_dec_env *next;
++
++      enum rpivid_decode_state state;
++      unsigned int decode_order;
++      int p1_status;          /* P1 status - what to realloc */
++
++      struct rpivid_dec_env *phase_wait_q_next;
++
++      struct rpi_cmd *cmd_fifo;
++      unsigned int cmd_len, cmd_max;
++      unsigned int num_slice_msgs;
++      unsigned int pic_width_in_ctbs_y;
++      unsigned int pic_height_in_ctbs_y;
++      unsigned int dpbno_col;
++      u32 reg_slicestart;
++      int collocated_from_l0_flag;
++      unsigned int wpp_entry_x;
++      unsigned int wpp_entry_y;
++
++      u32 rpi_config2;
++      u32 rpi_framesize;
++      u32 rpi_currpoc;
++
++      struct vb2_v4l2_buffer *frame_buf; // Detached dest buffer
++      unsigned int frame_c_offset;
++      unsigned int frame_stride;
++      dma_addr_t frame_addr;
++      dma_addr_t ref_addrs[16];
++      struct rpivid_q_aux *frame_aux;
++      struct rpivid_q_aux *col_aux;
++
++      dma_addr_t pu_base_vc;
++      dma_addr_t coeff_base_vc;
++      u32 pu_stride;
++      u32 coeff_stride;
++
++      struct rpivid_gptr *bit_copy_gptr;
++      size_t bit_copy_len;
++      struct rpivid_gptr *cmd_copy_gptr;
++
++      u16 slice_msgs[2 * HEVC_MAX_REFS * 8 + 3];
++      u8 scaling_factors[NUM_SCALING_FACTORS];
++
++      struct rpivid_hw_irq_ent irq_ent;
++};
++
++#define member_size(type, member) sizeof(((type *)0)->member)
++
++struct rpivid_dec_state {
++      struct v4l2_ctrl_hevc_sps sps;
++      struct v4l2_ctrl_hevc_pps pps;
++
++      // Helper vars & tables derived from sps/pps
++      unsigned int log2_ctb_size; /* log2 width of a CTB */
++      unsigned int ctb_width; /* Width in CTBs */
++      unsigned int ctb_height; /* Height in CTBs */
++      unsigned int ctb_size; /* Pic area in CTBs */
++      unsigned int num_tile_columns;
++      unsigned int num_tile_rows;
++      u8 column_width[member_size(struct v4l2_ctrl_hevc_pps,
++                                  column_width_minus1)];
++      u8 row_height[member_size(struct v4l2_ctrl_hevc_pps,
++                                row_height_minus1)];
++
++      int *col_bd;
++      int *row_bd;
++      int *ctb_addr_rs_to_ts;
++      int *ctb_addr_ts_to_rs;
++      int *tile_id;
++
++      // Aux starage for DPB
++      // Hold refs
++      struct rpivid_q_aux *ref_aux[HEVC_MAX_REFS];
++      struct rpivid_q_aux *frame_aux;
++
++      // Slice vars
++      unsigned int slice_idx;
++      bool frame_end;
++      bool slice_temporal_mvp;  /* Slice flag but constant for frame */
++
++      // Temp vars per run - don't actually need to persist
++      u8 *src_buf;
++      dma_addr_t src_addr;
++      const struct v4l2_ctrl_hevc_slice_params *sh;
++      unsigned int nb_refs[2];
++      unsigned int slice_qp;
++      unsigned int max_num_merge_cand; // 0 if I-slice
++      bool dependent_slice_segment_flag;
++};
++
++static inline int clip_int(const int x, const int lo, const int hi)
++{
++      return x < lo ? lo : x > hi ? hi : x;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Phase 1 command and bit FIFOs
++
++#if DEBUG_TRACE_P1_CMD
++static int p1_z;
++#endif
++
++// ???? u16 addr - put in u32
++static int p1_apb_write(struct rpivid_dec_env *const de, const u16 addr,
++                      const u32 data)
++{
++      if (de->cmd_len == de->cmd_max)
++              de->cmd_fifo =
++                      krealloc(de->cmd_fifo,
++                               (de->cmd_max *= 2) * sizeof(struct rpi_cmd),
++                               GFP_KERNEL);
++      de->cmd_fifo[de->cmd_len].addr = addr;
++      de->cmd_fifo[de->cmd_len].data = data;
++
++#if DEBUG_TRACE_P1_CMD
++      if (++p1_z < 256) {
++              v4l2_info(&de->ctx->dev->v4l2_dev, "[%02x] %x %x\n",
++                        de->cmd_len, addr, data);
++      }
++#endif
++
++      return de->cmd_len++;
++}
++
++static int ctb_to_tile(unsigned int ctb, unsigned int *bd, int num)
++{
++      int i;
++
++      for (i = 1; ctb >= bd[i]; i++)
++              ; // bd[] has num+1 elements; bd[0]=0;
++      return i - 1;
++}
++
++static int ctb_to_slice_w_h(unsigned int ctb, int ctb_size, int width,
++                          unsigned int *bd, int num)
++{
++      if (ctb < bd[num - 1])
++              return ctb_size;
++      else if (width % ctb_size)
++              return width % ctb_size;
++      else
++              return ctb_size;
++}
++
++static void aux_q_free(struct rpivid_ctx *const ctx,
++                     struct rpivid_q_aux *const aq)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++
++      gptr_free(dev, &aq->col);
++      kfree(aq);
++}
++
++static struct rpivid_q_aux *aux_q_alloc(struct rpivid_ctx *const ctx)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++      struct rpivid_q_aux *const aq = kzalloc(sizeof(*aq), GFP_KERNEL);
++
++      if (!aq)
++              return NULL;
++
++      aq->refcount = 1;
++      if (gptr_alloc(dev, &aq->col, ctx->colmv_picsize,
++                     DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_KERNEL_MAPPING))
++              goto fail;
++
++      return aq;
++
++fail:
++      kfree(aq);
++      return NULL;
++}
++
++static struct rpivid_q_aux *aux_q_new(struct rpivid_ctx *const ctx,
++                                    const unsigned int q_index)
++{
++      struct rpivid_q_aux *aq;
++      unsigned long lockflags;
++
++      spin_lock_irqsave(&ctx->aux_lock, lockflags);
++      aq = ctx->aux_free;
++      if (aq) {
++              ctx->aux_free = aq->next;
++              aq->next = NULL;
++              aq->refcount = 1;
++      }
++      spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++
++      if (!aq) {
++              aq = aux_q_alloc(ctx);
++              if (!aq)
++                      return NULL;
++      }
++
++      aq->q_index = q_index;
++      ctx->aux_ents[q_index] = aq;
++      return aq;
++}
++
++static struct rpivid_q_aux *aux_q_ref(struct rpivid_ctx *const ctx,
++                                    struct rpivid_q_aux *const aq)
++{
++      if (aq) {
++              unsigned long lockflags;
++
++              spin_lock_irqsave(&ctx->aux_lock, lockflags);
++
++              ++aq->refcount;
++
++              spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++      }
++      return aq;
++}
++
++static void aux_q_release(struct rpivid_ctx *const ctx,
++                        struct rpivid_q_aux **const paq)
++{
++      struct rpivid_q_aux *const aq = *paq;
++      *paq = NULL;
++
++      if (aq) {
++              unsigned long lockflags;
++
++              spin_lock_irqsave(&ctx->aux_lock, lockflags);
++
++              if (--aq->refcount == 0) {
++                      aq->next = ctx->aux_free;
++                      ctx->aux_free = aq;
++                      ctx->aux_ents[aq->q_index] = NULL;
++              }
++
++              spin_unlock_irqrestore(&ctx->aux_lock, lockflags);
++      }
++}
++
++static void aux_q_init(struct rpivid_ctx *const ctx)
++{
++      spin_lock_init(&ctx->aux_lock);
++      ctx->aux_free = NULL;
++}
++
++static void aux_q_uninit(struct rpivid_ctx *const ctx)
++{
++      struct rpivid_q_aux *aq;
++
++      ctx->colmv_picsize = 0;
++      ctx->colmv_stride = 0;
++      while ((aq = ctx->aux_free) != NULL) {
++              ctx->aux_free = aq->next;
++              aux_q_free(ctx, aq);
++      }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++/*
++ * Initialisation process for context variables (CABAC init)
++ * see H.265 9.3.2.2
++ *
++ * N.B. If comparing with FFmpeg note that this h/w uses slightly different
++ * offsets to FFmpegs array
++ */
++
++/* Actual number of values */
++#define RPI_PROB_VALS 154U
++/* Rounded up as we copy words */
++#define RPI_PROB_ARRAY_SIZE ((154 + 3) & ~3)
++
++/* Initialiser values - see tables H.265 9-4 through 9-42 */
++static const u8 prob_init[3][156] = {
++      {
++              153, 200, 139, 141, 157, 154, 154, 154, 154, 154, 184, 154, 154,
++              154, 184, 63,  154, 154, 154, 154, 154, 154, 154, 154, 154, 154,
++              154, 154, 154, 153, 138, 138, 111, 141, 94,  138, 182, 154, 154,
++              154, 140, 92,  137, 138, 140, 152, 138, 139, 153, 74,  149, 92,
++              139, 107, 122, 152, 140, 179, 166, 182, 140, 227, 122, 197, 110,
++              110, 124, 125, 140, 153, 125, 127, 140, 109, 111, 143, 127, 111,
++              79,  108, 123, 63,  110, 110, 124, 125, 140, 153, 125, 127, 140,
++              109, 111, 143, 127, 111, 79,  108, 123, 63,  91,  171, 134, 141,
++              138, 153, 136, 167, 152, 152, 139, 139, 111, 111, 125, 110, 110,
++              94,  124, 108, 124, 107, 125, 141, 179, 153, 125, 107, 125, 141,
++              179, 153, 125, 107, 125, 141, 179, 153, 125, 140, 139, 182, 182,
++              152, 136, 152, 136, 153, 136, 139, 111, 136, 139, 111, 0,   0,
++      },
++      {
++              153, 185, 107, 139, 126, 197, 185, 201, 154, 149, 154, 139, 154,
++              154, 154, 152, 110, 122, 95,  79,  63,  31,  31,  153, 153, 168,
++              140, 198, 79,  124, 138, 94,  153, 111, 149, 107, 167, 154, 154,
++              154, 154, 196, 196, 167, 154, 152, 167, 182, 182, 134, 149, 136,
++              153, 121, 136, 137, 169, 194, 166, 167, 154, 167, 137, 182, 125,
++              110, 94,  110, 95,  79,  125, 111, 110, 78,  110, 111, 111, 95,
++              94,  108, 123, 108, 125, 110, 94,  110, 95,  79,  125, 111, 110,
++              78,  110, 111, 111, 95,  94,  108, 123, 108, 121, 140, 61,  154,
++              107, 167, 91,  122, 107, 167, 139, 139, 155, 154, 139, 153, 139,
++              123, 123, 63,  153, 166, 183, 140, 136, 153, 154, 166, 183, 140,
++              136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 123, 123,
++              107, 121, 107, 121, 167, 151, 183, 140, 151, 183, 140, 0,   0,
++      },
++      {
++              153, 160, 107, 139, 126, 197, 185, 201, 154, 134, 154, 139, 154,
++              154, 183, 152, 154, 137, 95,  79,  63,  31,  31,  153, 153, 168,
++              169, 198, 79,  224, 167, 122, 153, 111, 149, 92,  167, 154, 154,
++              154, 154, 196, 167, 167, 154, 152, 167, 182, 182, 134, 149, 136,
++              153, 121, 136, 122, 169, 208, 166, 167, 154, 152, 167, 182, 125,
++              110, 124, 110, 95,  94,  125, 111, 111, 79,  125, 126, 111, 111,
++              79,  108, 123, 93,  125, 110, 124, 110, 95,  94,  125, 111, 111,
++              79,  125, 126, 111, 111, 79,  108, 123, 93,  121, 140, 61,  154,
++              107, 167, 91,  107, 107, 167, 139, 139, 170, 154, 139, 153, 139,
++              123, 123, 63,  124, 166, 183, 140, 136, 153, 154, 166, 183, 140,
++              136, 153, 154, 166, 183, 140, 136, 153, 154, 170, 153, 138, 138,
++              122, 121, 122, 121, 167, 151, 183, 140, 151, 183, 140, 0,   0,
++      },
++};
++
++static void write_prob(struct rpivid_dec_env *const de,
++                     const struct rpivid_dec_state *const s)
++{
++      u8 dst[RPI_PROB_ARRAY_SIZE];
++
++      const unsigned int init_type =
++              ((s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) != 0 &&
++               s->sh->slice_type != HEVC_SLICE_I) ?
++                      s->sh->slice_type + 1 :
++                      2 - s->sh->slice_type;
++      const u8 *p = prob_init[init_type];
++      const int q = clip_int(s->slice_qp, 0, 51);
++      unsigned int i;
++
++      for (i = 0; i < RPI_PROB_VALS; i++) {
++              int init_value = p[i];
++              int m = (init_value >> 4) * 5 - 45;
++              int n = ((init_value & 15) << 3) - 16;
++              int pre = 2 * (((m * q) >> 4) + n) - 127;
++
++              pre ^= pre >> 31;
++              if (pre > 124)
++                      pre = 124 + (pre & 1);
++              dst[i] = pre;
++      }
++      for (i = RPI_PROB_VALS; i != RPI_PROB_ARRAY_SIZE; ++i)
++              dst[i] = 0;
++
++      for (i = 0; i < RPI_PROB_ARRAY_SIZE; i += 4)
++              p1_apb_write(de, 0x1000 + i,
++                           dst[i] + (dst[i + 1] << 8) + (dst[i + 2] << 16) +
++                                   (dst[i + 3] << 24));
++}
++
++static void write_scaling_factors(struct rpivid_dec_env *const de)
++{
++      int i;
++      const u8 *p = (u8 *)de->scaling_factors;
++
++      for (i = 0; i < NUM_SCALING_FACTORS; i += 4, p += 4)
++              p1_apb_write(de, 0x2000 + i,
++                           p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24));
++}
++
++static inline __u32 dma_to_axi_addr(dma_addr_t a)
++{
++      return (__u32)(a >> 6);
++}
++
++static void write_bitstream(struct rpivid_dec_env *const de,
++                          const struct rpivid_dec_state *const s)
++{
++      // Note that FFmpeg removes emulation prevention bytes, so this is
++      // matched in the configuration here.
++      // Whether that is the correct behaviour or not is not clear in the
++      // spec.
++      const int rpi_use_emu = 1;
++      unsigned int offset = s->sh->data_bit_offset / 8 + 1;
++      const unsigned int len = (s->sh->bit_size + 7) / 8 - offset;
++      dma_addr_t addr;
++
++      if (s->src_addr != 0) {
++              addr = s->src_addr + offset;
++      } else {
++              memcpy(de->bit_copy_gptr->ptr + de->bit_copy_len,
++                     s->src_buf + offset, len);
++              addr = de->bit_copy_gptr->addr + de->bit_copy_len;
++              de->bit_copy_len += (len + 63) & ~63;
++      }
++      offset = addr & 63;
++
++      p1_apb_write(de, RPI_BFBASE, dma_to_axi_addr(addr));
++      p1_apb_write(de, RPI_BFNUM, len);
++      p1_apb_write(de, RPI_BFCONTROL, offset + (1 << 7)); // Stop
++      p1_apb_write(de, RPI_BFCONTROL, offset + (rpi_use_emu << 6));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++static void write_slice(struct rpivid_dec_env *const de,
++                      const struct rpivid_dec_state *const s,
++                      const unsigned int slice_w,
++                      const unsigned int slice_h)
++{
++      u32 u32 = (s->sh->slice_type << 12) +
++                (((s->sh->flags &
++                   V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA) != 0)
++                 << 14) +
++                (((s->sh->flags &
++                   V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA) != 0)
++                 << 15) +
++                (slice_w << 17) + (slice_h << 24);
++
++      u32 |= (s->max_num_merge_cand << 0) + (s->nb_refs[L0] << 4) +
++             (s->nb_refs[L1] << 8);
++
++      if (s->sh->slice_type == HEVC_SLICE_B)
++              u32 |= ((s->sh->flags &
++                       V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO) != 0)
++                     << 16;
++      p1_apb_write(de, RPI_SLICE, u32);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Tiles mode
++
++static void new_entry_point(struct rpivid_dec_env *const de,
++                          const struct rpivid_dec_state *const s,
++                          const int do_bte,
++                          const int reset_qp_y, const int ctb_addr_ts)
++{
++      int ctb_col = s->ctb_addr_ts_to_rs[ctb_addr_ts] %
++                                                      de->pic_width_in_ctbs_y;
++      int ctb_row = s->ctb_addr_ts_to_rs[ctb_addr_ts] /
++                                                      de->pic_width_in_ctbs_y;
++
++      int tile_x = ctb_to_tile(ctb_col, s->col_bd, s->num_tile_columns);
++      int tile_y = ctb_to_tile(ctb_row, s->row_bd, s->num_tile_rows);
++
++      int endx = s->col_bd[tile_x + 1] - 1;
++      int endy = s->row_bd[tile_y + 1] - 1;
++
++      u8 slice_w = ctb_to_slice_w_h(ctb_col, 1 << s->log2_ctb_size,
++                                    s->sps.pic_width_in_luma_samples,
++                                    s->col_bd, s->num_tile_columns);
++      u8 slice_h = ctb_to_slice_w_h(ctb_row, 1 << s->log2_ctb_size,
++                                    s->sps.pic_height_in_luma_samples,
++                                    s->row_bd, s->num_tile_rows);
++
++      p1_apb_write(de, RPI_TILESTART,
++                   s->col_bd[tile_x] + (s->row_bd[tile_y] << 16));
++      p1_apb_write(de, RPI_TILEEND, endx + (endy << 16));
++
++      if (do_bte)
++              p1_apb_write(de, RPI_BEGINTILEEND, endx + (endy << 16));
++
++      write_slice(de, s, slice_w, slice_h);
++
++      if (reset_qp_y) {
++              unsigned int sps_qp_bd_offset =
++                      6 * s->sps.bit_depth_luma_minus8;
++
++              p1_apb_write(de, RPI_QP, sps_qp_bd_offset + s->slice_qp);
++      }
++
++      p1_apb_write(de, RPI_MODE,
++                   (0xFFFF << 0) + (0x0 << 16) +
++                           ((tile_x == s->num_tile_columns - 1) << 17) +
++                           ((tile_y == s->num_tile_rows - 1) << 18));
++
++      p1_apb_write(de, RPI_CONTROL, (ctb_col << 0) + (ctb_row << 16));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++
++static void new_slice_segment(struct rpivid_dec_env *const de,
++                            const struct rpivid_dec_state *const s)
++{
++      const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
++      const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
++
++      p1_apb_write(de,
++                   RPI_SPS0,
++                   ((sps->log2_min_luma_coding_block_size_minus3 + 3) << 0) |
++                   (s->log2_ctb_size << 4) |
++                   ((sps->log2_min_luma_transform_block_size_minus2 + 2)
++                                                      << 8) |
++                   ((sps->log2_min_luma_transform_block_size_minus2 + 2 +
++                     sps->log2_diff_max_min_luma_transform_block_size)
++                                              << 12) |
++                   ((sps->bit_depth_luma_minus8 + 8) << 16) |
++                   ((sps->bit_depth_chroma_minus8 + 8) << 20) |
++                   (sps->max_transform_hierarchy_depth_intra << 24) |
++                   (sps->max_transform_hierarchy_depth_inter << 28));
++
++      p1_apb_write(de,
++                   RPI_SPS1,
++                   ((sps->pcm_sample_bit_depth_luma_minus1 + 1) << 0) |
++                   ((sps->pcm_sample_bit_depth_chroma_minus1 + 1) << 4) |
++                   ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3)
++                                              << 8) |
++                   ((sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 +
++                     sps->log2_diff_max_min_pcm_luma_coding_block_size)
++                                              << 12) |
++                   (((sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) ?
++                              0 : sps->chroma_format_idc) << 16) |
++                   ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)) << 18) |
++                   ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) << 19) |
++                   ((!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED))
++                                              << 20) |
++                   ((!!(sps->flags &
++                         V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED))
++                                              << 21));
++
++      p1_apb_write(de,
++                   RPI_PPS,
++                   ((s->log2_ctb_size - pps->diff_cu_qp_delta_depth) << 0) |
++                   ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED))
++                                               << 4) |
++                   ((!!(pps->flags &
++                              V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED))
++                                               << 5) |
++                   ((!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED))
++                                               << 6) |
++                   ((!!(pps->flags &
++                              V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED))
++                                              << 7) |
++                   (((pps->pps_cb_qp_offset + s->sh->slice_cb_qp_offset) & 255)
++                                              << 8) |
++                   (((pps->pps_cr_qp_offset + s->sh->slice_cr_qp_offset) & 255)
++                                              << 16) |
++                   ((!!(pps->flags &
++                              V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED))
++                                              << 24));
++
++      if ((sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) != 0)
++              write_scaling_factors(de);
++
++      if (!s->dependent_slice_segment_flag) {
++              int ctb_col = s->sh->slice_segment_addr %
++                                                      de->pic_width_in_ctbs_y;
++              int ctb_row = s->sh->slice_segment_addr /
++                                                      de->pic_width_in_ctbs_y;
++
++              de->reg_slicestart = (ctb_col << 0) + (ctb_row << 16);
++      }
++
++      p1_apb_write(de, RPI_SLICESTART, de->reg_slicestart);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Slice messages
++
++static void msg_slice(struct rpivid_dec_env *const de, const u16 msg)
++{
++      de->slice_msgs[de->num_slice_msgs++] = msg;
++}
++
++static void program_slicecmds(struct rpivid_dec_env *const de,
++                            const int sliceid)
++{
++      int i;
++
++      p1_apb_write(de, RPI_SLICECMDS, de->num_slice_msgs + (sliceid << 8));
++
++      for (i = 0; i < de->num_slice_msgs; i++)
++              p1_apb_write(de, 0x4000 + 4 * i, de->slice_msgs[i] & 0xffff);
++}
++
++// NoBackwardPredictionFlag 8.3.5
++// Simply checks POCs
++static int has_backward(const struct v4l2_hevc_dpb_entry *const dpb,
++                      const __u8 *const idx, const unsigned int n,
++                      const unsigned int cur_poc)
++{
++      unsigned int i;
++
++      for (i = 0; i < n; ++i) {
++              // Compare mod 2^16
++              // We only get u16 pocs & 8.3.1 says
++              // "The bitstream shall not contain data that result in values
++              //  of DiffPicOrderCnt( picA, picB ) used in the decoding
++              //  process that are not in the range of −2^15 to 2^15 − 1,
++              //  inclusive."
++              if (((cur_poc - dpb[idx[i]].pic_order_cnt[0]) & 0x8000) != 0)
++                      return 0;
++      }
++      return 1;
++}
++
++static void pre_slice_decode(struct rpivid_dec_env *const de,
++                           const struct rpivid_dec_state *const s)
++{
++      const struct v4l2_ctrl_hevc_slice_params *const sh = s->sh;
++      int weighted_pred_flag, idx;
++      u16 cmd_slice;
++      unsigned int collocated_from_l0_flag;
++
++      de->num_slice_msgs = 0;
++
++      cmd_slice = 0;
++      if (sh->slice_type == HEVC_SLICE_I)
++              cmd_slice = 1;
++      if (sh->slice_type == HEVC_SLICE_P)
++              cmd_slice = 2;
++      if (sh->slice_type == HEVC_SLICE_B)
++              cmd_slice = 3;
++
++      cmd_slice |= (s->nb_refs[L0] << 2) | (s->nb_refs[L1] << 6) |
++                   (s->max_num_merge_cand << 11);
++
++      collocated_from_l0_flag =
++              !s->slice_temporal_mvp ||
++              sh->slice_type != HEVC_SLICE_B ||
++              (sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0);
++      cmd_slice |= collocated_from_l0_flag << 14;
++
++      if (sh->slice_type == HEVC_SLICE_P || sh->slice_type == HEVC_SLICE_B) {
++              // Flag to say all reference pictures are from the past
++              const int no_backward_pred_flag =
++                      has_backward(sh->dpb, sh->ref_idx_l0, s->nb_refs[L0],
++                                   sh->slice_pic_order_cnt) &&
++                      has_backward(sh->dpb, sh->ref_idx_l1, s->nb_refs[L1],
++                                   sh->slice_pic_order_cnt);
++              cmd_slice |= no_backward_pred_flag << 10;
++              msg_slice(de, cmd_slice);
++
++              if (s->slice_temporal_mvp) {
++                      const __u8 *const rpl = collocated_from_l0_flag ?
++                                              sh->ref_idx_l0 : sh->ref_idx_l1;
++                      de->dpbno_col = rpl[sh->collocated_ref_idx];
++                      //v4l2_info(&de->ctx->dev->v4l2_dev,
++                      //          "L0=%d col_ref_idx=%d,
++                      //          dpb_no=%d\n", collocated_from_l0_flag,
++                      //          sh->collocated_ref_idx, de->dpbno_col);
++              }
++
++              // Write reference picture descriptions
++              weighted_pred_flag =
++                      sh->slice_type == HEVC_SLICE_P ?
++                              !!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED) :
++                              !!(s->pps.flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
++
++              for (idx = 0; idx < s->nb_refs[L0]; ++idx) {
++                      unsigned int dpb_no = sh->ref_idx_l0[idx];
++                      //v4l2_info(&de->ctx->dev->v4l2_dev,
++                      //        "L0[%d]=dpb[%d]\n", idx, dpb_no);
++
++                      msg_slice(de,
++                                dpb_no |
++                                (sh->dpb[dpb_no].rps ==
++                                      V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
++                                               (1 << 4) : 0) |
++                                (weighted_pred_flag ? (3 << 5) : 0));
++                      msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
++
++                      if (weighted_pred_flag) {
++                              const struct v4l2_hevc_pred_weight_table
++                                      *const w = &sh->pred_weight_table;
++                              const int luma_weight_denom =
++                                      (1 << w->luma_log2_weight_denom);
++                              const unsigned int chroma_log2_weight_denom =
++                                      (w->luma_log2_weight_denom +
++                                       w->delta_chroma_log2_weight_denom);
++                              const int chroma_weight_denom =
++                                      (1 << chroma_log2_weight_denom);
++
++                              msg_slice(de,
++                                        w->luma_log2_weight_denom |
++                                        (((w->delta_luma_weight_l0[idx] +
++                                           luma_weight_denom) & 0x1ff)
++                                               << 3));
++                              msg_slice(de, w->luma_offset_l0[idx] & 0xff);
++                              msg_slice(de,
++                                        chroma_log2_weight_denom |
++                                        (((w->delta_chroma_weight_l0[idx][0] +
++                                           chroma_weight_denom) & 0x1ff)
++                                                 << 3));
++                              msg_slice(de,
++                                        w->chroma_offset_l0[idx][0] & 0xff);
++                              msg_slice(de,
++                                        chroma_log2_weight_denom |
++                                        (((w->delta_chroma_weight_l0[idx][1] +
++                                           chroma_weight_denom) & 0x1ff)
++                                                 << 3));
++                              msg_slice(de,
++                                        w->chroma_offset_l0[idx][1] & 0xff);
++                      }
++              }
++
++              for (idx = 0; idx < s->nb_refs[L1]; ++idx) {
++                      unsigned int dpb_no = sh->ref_idx_l1[idx];
++                      //v4l2_info(&de->ctx->dev->v4l2_dev,
++                      //          "L1[%d]=dpb[%d]\n", idx, dpb_no);
++                      msg_slice(de,
++                                dpb_no |
++                                (sh->dpb[dpb_no].rps ==
++                                       V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR ?
++                                               (1 << 4) : 0) |
++                                      (weighted_pred_flag ? (3 << 5) : 0));
++                      msg_slice(de, sh->dpb[dpb_no].pic_order_cnt[0]);
++                      if (weighted_pred_flag) {
++                              const struct v4l2_hevc_pred_weight_table
++                                      *const w = &sh->pred_weight_table;
++                              const int luma_weight_denom =
++                                      (1 << w->luma_log2_weight_denom);
++                              const unsigned int chroma_log2_weight_denom =
++                                      (w->luma_log2_weight_denom +
++                                       w->delta_chroma_log2_weight_denom);
++                              const int chroma_weight_denom =
++                                      (1 << chroma_log2_weight_denom);
++
++                              msg_slice(de,
++                                        w->luma_log2_weight_denom |
++                                        (((w->delta_luma_weight_l1[idx] +
++                                           luma_weight_denom) & 0x1ff) << 3));
++                              msg_slice(de, w->luma_offset_l1[idx] & 0xff);
++                              msg_slice(de,
++                                        chroma_log2_weight_denom |
++                                        (((w->delta_chroma_weight_l1[idx][0] +
++                                           chroma_weight_denom) & 0x1ff)
++                                                      << 3));
++                              msg_slice(de,
++                                        w->chroma_offset_l1[idx][0] & 0xff);
++                              msg_slice(de,
++                                        chroma_log2_weight_denom |
++                                        (((w->delta_chroma_weight_l1[idx][1] +
++                                           chroma_weight_denom) & 0x1ff)
++                                                 << 3));
++                              msg_slice(de,
++                                        w->chroma_offset_l1[idx][1] & 0xff);
++                      }
++              }
++      } else {
++              msg_slice(de, cmd_slice);
++      }
++
++      msg_slice(de,
++                (sh->slice_beta_offset_div2 & 15) |
++                ((sh->slice_tc_offset_div2 & 15) << 4) |
++                ((sh->flags &
++                  V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED) ?
++                                              1 << 8 : 0) |
++                ((sh->flags &
++                        V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED) ?
++                                              1 << 9 : 0) |
++                ((s->pps.flags &
++                        V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED) ?
++                                              1 << 10 : 0));
++
++      msg_slice(de, ((sh->slice_cr_qp_offset & 31) << 5) +
++                     (sh->slice_cb_qp_offset & 31)); // CMD_QPOFF
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Write STATUS register with expected end CTU address of previous slice
++
++static void end_previous_slice(struct rpivid_dec_env *const de,
++                             const struct rpivid_dec_state *const s,
++                             const int ctb_addr_ts)
++{
++      int last_x =
++              s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] % de->pic_width_in_ctbs_y;
++      int last_y =
++              s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] / de->pic_width_in_ctbs_y;
++
++      p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++}
++
++static void wpp_pause(struct rpivid_dec_env *const de, int ctb_row)
++{
++      p1_apb_write(de, RPI_STATUS, (ctb_row << 18) + 0x25);
++      p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++      p1_apb_write(de, RPI_MODE,
++                   ctb_row == de->pic_height_in_ctbs_y - 1 ?
++                                                      0x70000 : 0x30000);
++      p1_apb_write(de, RPI_CONTROL, (ctb_row << 16) + 2);
++}
++
++static void wpp_end_previous_slice(struct rpivid_dec_env *const de,
++                                 const struct rpivid_dec_state *const s,
++                                 int ctb_addr_ts)
++{
++      int new_x = s->sh->slice_segment_addr % de->pic_width_in_ctbs_y;
++      int new_y = s->sh->slice_segment_addr / de->pic_width_in_ctbs_y;
++      int last_x =
++              s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] % de->pic_width_in_ctbs_y;
++      int last_y =
++              s->ctb_addr_ts_to_rs[ctb_addr_ts - 1] / de->pic_width_in_ctbs_y;
++
++      if (de->wpp_entry_x < 2 && (de->wpp_entry_y < new_y || new_x > 2) &&
++          de->pic_width_in_ctbs_y > 2)
++              wpp_pause(de, last_y);
++      p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++      if (new_x == 2 || (de->pic_width_in_ctbs_y == 2 &&
++                         de->wpp_entry_y < new_y))
++              p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Wavefront mode
++
++static void wpp_entry_point(struct rpivid_dec_env *const de,
++                          const struct rpivid_dec_state *const s,
++                          const int do_bte,
++                          const int reset_qp_y, const int ctb_addr_ts)
++{
++      int ctb_size = 1 << s->log2_ctb_size;
++      int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++
++      int ctb_col = de->wpp_entry_x = ctb_addr_rs % de->pic_width_in_ctbs_y;
++      int ctb_row = de->wpp_entry_y = ctb_addr_rs / de->pic_width_in_ctbs_y;
++
++      int endx = de->pic_width_in_ctbs_y - 1;
++      int endy = ctb_row;
++
++      u8 slice_w = ctb_to_slice_w_h(ctb_col, ctb_size,
++                                    s->sps.pic_width_in_luma_samples,
++                                    s->col_bd, s->num_tile_columns);
++      u8 slice_h = ctb_to_slice_w_h(ctb_row, ctb_size,
++                                    s->sps.pic_height_in_luma_samples,
++                                    s->row_bd, s->num_tile_rows);
++
++      p1_apb_write(de, RPI_TILESTART, 0);
++      p1_apb_write(de, RPI_TILEEND, endx + (endy << 16));
++
++      if (do_bte)
++              p1_apb_write(de, RPI_BEGINTILEEND, endx + (endy << 16));
++
++      write_slice(de, s, slice_w,
++                  ctb_row == de->pic_height_in_ctbs_y - 1 ?
++                                                      slice_h : ctb_size);
++
++      if (reset_qp_y) {
++              unsigned int sps_qp_bd_offset =
++                      6 * s->sps.bit_depth_luma_minus8;
++
++              p1_apb_write(de, RPI_QP, sps_qp_bd_offset + s->slice_qp);
++      }
++
++      p1_apb_write(de, RPI_MODE,
++                   ctb_row == de->pic_height_in_ctbs_y - 1 ?
++                                                      0x60001 : 0x20001);
++      p1_apb_write(de, RPI_CONTROL, (ctb_col << 0) + (ctb_row << 16));
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Wavefront mode
++
++static void wpp_decode_slice(struct rpivid_dec_env *const de,
++                           const struct rpivid_dec_state *const s,
++                           const struct v4l2_ctrl_hevc_slice_params *sh,
++                           int ctb_addr_ts)
++{
++      int i, reset_qp_y = 1;
++      int indep = !s->dependent_slice_segment_flag;
++      int ctb_col = s->sh->slice_segment_addr % de->pic_width_in_ctbs_y;
++
++      if (ctb_addr_ts)
++              wpp_end_previous_slice(de, s, ctb_addr_ts);
++      pre_slice_decode(de, s);
++      write_bitstream(de, s);
++      if (ctb_addr_ts == 0 || indep || de->pic_width_in_ctbs_y == 1)
++              write_prob(de, s);
++      else if (ctb_col == 0)
++              p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
++      else
++              reset_qp_y = 0;
++      program_slicecmds(de, s->slice_idx);
++      new_slice_segment(de, s);
++      wpp_entry_point(de, s, indep, reset_qp_y, ctb_addr_ts);
++
++      for (i = 0; i < s->sh->num_entry_point_offsets; i++) {
++              int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++              int ctb_row = ctb_addr_rs / de->pic_width_in_ctbs_y;
++              int last_x = de->pic_width_in_ctbs_y - 1;
++
++              if (de->pic_width_in_ctbs_y > 2)
++                      wpp_pause(de, ctb_row);
++              p1_apb_write(de, RPI_STATUS,
++                           (ctb_row << 18) + (last_x << 5) + 2);
++              if (de->pic_width_in_ctbs_y == 2)
++                      p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP);
++              if (de->pic_width_in_ctbs_y == 1)
++                      write_prob(de, s);
++              else
++                      p1_apb_write(de, RPI_TRANSFER, PROB_RELOAD);
++              ctb_addr_ts += s->column_width[0];
++              wpp_entry_point(de, s, 0, 1, ctb_addr_ts);
++      }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Tiles mode
++
++static void decode_slice(struct rpivid_dec_env *const de,
++                       const struct rpivid_dec_state *const s,
++                       const struct v4l2_ctrl_hevc_slice_params *const sh,
++                       int ctb_addr_ts)
++{
++      int i, reset_qp_y;
++
++      if (ctb_addr_ts)
++              end_previous_slice(de, s, ctb_addr_ts);
++
++      pre_slice_decode(de, s);
++      write_bitstream(de, s);
++
++#if DEBUG_TRACE_P1_CMD
++      if (p1_z < 256) {
++              v4l2_info(&de->ctx->dev->v4l2_dev,
++                        "TS=%d, tile=%d/%d, dss=%d, flags=%#llx\n",
++                        ctb_addr_ts, s->tile_id[ctb_addr_ts],
++                        s->tile_id[ctb_addr_ts - 1],
++                        s->dependent_slice_segment_flag, sh->flags);
++      }
++#endif
++
++      reset_qp_y = ctb_addr_ts == 0 ||
++                 s->tile_id[ctb_addr_ts] != s->tile_id[ctb_addr_ts - 1] ||
++                 !s->dependent_slice_segment_flag;
++      if (reset_qp_y)
++              write_prob(de, s);
++
++      program_slicecmds(de, s->slice_idx);
++      new_slice_segment(de, s);
++      new_entry_point(de, s, !s->dependent_slice_segment_flag, reset_qp_y,
++                      ctb_addr_ts);
++
++      for (i = 0; i < s->sh->num_entry_point_offsets; i++) {
++              int ctb_addr_rs = s->ctb_addr_ts_to_rs[ctb_addr_ts];
++              int ctb_col = ctb_addr_rs % de->pic_width_in_ctbs_y;
++              int ctb_row = ctb_addr_rs / de->pic_width_in_ctbs_y;
++              int tile_x = ctb_to_tile(ctb_col, s->col_bd,
++                                       s->num_tile_columns - 1);
++              int tile_y =
++                      ctb_to_tile(ctb_row, s->row_bd, s->num_tile_rows - 1);
++              int last_x = s->col_bd[tile_x + 1] - 1;
++              int last_y = s->row_bd[tile_y + 1] - 1;
++
++              p1_apb_write(de, RPI_STATUS,
++                           2 + (last_x << 5) + (last_y << 18));
++              write_prob(de, s);
++              ctb_addr_ts += s->column_width[tile_x] * s->row_height[tile_y];
++              new_entry_point(de, s, 0, 1, ctb_addr_ts);
++      }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Scaling factors
++
++static void expand_scaling_list(const unsigned int size_id,
++                              const unsigned int matrix_id, u8 *const dst0,
++                              const u8 *const src0, uint8_t dc)
++{
++      u8 *d;
++      unsigned int x, y;
++
++      // FIXME: matrix_id is unused ?
++      switch (size_id) {
++      case 0:
++              memcpy(dst0, src0, 16);
++              break;
++      case 1:
++              memcpy(dst0, src0, 64);
++              break;
++      case 2:
++              d = dst0;
++
++              for (y = 0; y != 16; y++) {
++                      const u8 *s = src0 + (y >> 1) * 8;
++
++                      for (x = 0; x != 8; ++x) {
++                              *d++ = *s;
++                              *d++ = *s++;
++                      }
++              }
++              dst0[0] = dc;
++              break;
++      default:
++              d = dst0;
++
++              for (y = 0; y != 32; y++) {
++                      const u8 *s = src0 + (y >> 2) * 8;
++
++                      for (x = 0; x != 8; ++x) {
++                              *d++ = *s;
++                              *d++ = *s;
++                              *d++ = *s;
++                              *d++ = *s++;
++                      }
++              }
++              dst0[0] = dc;
++              break;
++      }
++}
++
++static void populate_scaling_factors(const struct rpivid_run *const run,
++                                   struct rpivid_dec_env *const de,
++                                   const struct rpivid_dec_state *const s)
++{
++      const struct v4l2_ctrl_hevc_scaling_matrix *const sl =
++              run->h265.scaling_matrix;
++      // Array of constants for scaling factors
++      static const u32 scaling_factor_offsets[4][6] = {
++              // MID0    MID1    MID2    MID3    MID4    MID5
++              // SID0 (4x4)
++              { 0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050 },
++              // SID1 (8x8)
++              { 0x0060, 0x00A0, 0x00E0, 0x0120, 0x0160, 0x01A0 },
++              // SID2 (16x16)
++              { 0x01E0, 0x02E0, 0x03E0, 0x04E0, 0x05E0, 0x06E0 },
++              // SID3 (32x32)
++              { 0x07E0, 0x0BE0, 0x0000, 0x0000, 0x0000, 0x0000 }
++      };
++
++      unsigned int mid;
++
++      for (mid = 0; mid < 6; mid++)
++              expand_scaling_list(0, mid,
++                                  de->scaling_factors +
++                                          scaling_factor_offsets[0][mid],
++                                  sl->scaling_list_4x4[mid], 0);
++      for (mid = 0; mid < 6; mid++)
++              expand_scaling_list(1, mid,
++                                  de->scaling_factors +
++                                          scaling_factor_offsets[1][mid],
++                                  sl->scaling_list_8x8[mid], 0);
++      for (mid = 0; mid < 6; mid++)
++              expand_scaling_list(2, mid,
++                                  de->scaling_factors +
++                                          scaling_factor_offsets[2][mid],
++                                  sl->scaling_list_16x16[mid],
++                                  sl->scaling_list_dc_coef_16x16[mid]);
++      for (mid = 0; mid < 2; mid += 1)
++              expand_scaling_list(3, mid,
++                                  de->scaling_factors +
++                                          scaling_factor_offsets[3][mid],
++                                  sl->scaling_list_32x32[mid],
++                                  sl->scaling_list_dc_coef_32x32[mid]);
++}
++
++static void free_ps_info(struct rpivid_dec_state *const s)
++{
++      kfree(s->ctb_addr_rs_to_ts);
++      s->ctb_addr_rs_to_ts = NULL;
++      kfree(s->ctb_addr_ts_to_rs);
++      s->ctb_addr_ts_to_rs = NULL;
++      kfree(s->tile_id);
++      s->tile_id = NULL;
++
++      kfree(s->col_bd);
++      s->col_bd = NULL;
++      kfree(s->row_bd);
++      s->row_bd = NULL;
++}
++
++static int updated_ps(struct rpivid_dec_state *const s)
++{
++      unsigned int ctb_addr_rs;
++      int j, x, y, tile_id;
++      unsigned int i;
++
++      free_ps_info(s);
++
++      // Inferred parameters
++      s->log2_ctb_size = s->sps.log2_min_luma_coding_block_size_minus3 + 3 +
++                         s->sps.log2_diff_max_min_luma_coding_block_size;
++
++      s->ctb_width = (s->sps.pic_width_in_luma_samples +
++                      (1 << s->log2_ctb_size) - 1) >>
++                     s->log2_ctb_size;
++      s->ctb_height = (s->sps.pic_height_in_luma_samples +
++                       (1 << s->log2_ctb_size) - 1) >>
++                      s->log2_ctb_size;
++      s->ctb_size = s->ctb_width * s->ctb_height;
++
++      // Inferred parameters
++
++      if (!(s->pps.flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)) {
++              s->num_tile_columns = 1;
++              s->num_tile_rows = 1;
++              s->column_width[0] = s->ctb_width;
++              s->row_height[0] = s->ctb_height;
++      } else {
++              s->num_tile_columns = s->pps.num_tile_columns_minus1 + 1;
++              s->num_tile_rows = s->pps.num_tile_rows_minus1 + 1;
++              for (i = 0; i < s->num_tile_columns; ++i)
++                      s->column_width[i] = s->pps.column_width_minus1[i] + 1;
++              for (i = 0; i < s->num_tile_rows; ++i)
++                      s->row_height[i] = s->pps.row_height_minus1[i] + 1;
++      }
++
++      s->col_bd = kmalloc((s->num_tile_columns + 1) * sizeof(*s->col_bd),
++                          GFP_KERNEL);
++      s->row_bd = kmalloc((s->num_tile_rows + 1) * sizeof(*s->row_bd),
++                          GFP_KERNEL);
++
++      s->col_bd[0] = 0;
++      for (i = 0; i < s->num_tile_columns; i++)
++              s->col_bd[i + 1] = s->col_bd[i] + s->column_width[i];
++
++      s->row_bd[0] = 0;
++      for (i = 0; i < s->num_tile_rows; i++)
++              s->row_bd[i + 1] = s->row_bd[i] + s->row_height[i];
++
++      s->ctb_addr_rs_to_ts = kmalloc_array(s->ctb_size,
++                                           sizeof(*s->ctb_addr_rs_to_ts),
++                                           GFP_KERNEL);
++      s->ctb_addr_ts_to_rs = kmalloc_array(s->ctb_size,
++                                           sizeof(*s->ctb_addr_ts_to_rs),
++                                           GFP_KERNEL);
++      s->tile_id = kmalloc_array(s->ctb_size, sizeof(*s->tile_id),
++                                 GFP_KERNEL);
++
++      for (ctb_addr_rs = 0; ctb_addr_rs < s->ctb_size; ctb_addr_rs++) {
++              int tb_x = ctb_addr_rs % s->ctb_width;
++              int tb_y = ctb_addr_rs / s->ctb_width;
++              int tile_x = 0;
++              int tile_y = 0;
++              int val = 0;
++
++              for (i = 0; i < s->num_tile_columns; i++) {
++                      if (tb_x < s->col_bd[i + 1]) {
++                              tile_x = i;
++                              break;
++                      }
++              }
++
++              for (i = 0; i < s->num_tile_rows; i++) {
++                      if (tb_y < s->row_bd[i + 1]) {
++                              tile_y = i;
++                              break;
++                      }
++              }
++
++              for (i = 0; i < tile_x; i++)
++                      val += s->row_height[tile_y] * s->column_width[i];
++              for (i = 0; i < tile_y; i++)
++                      val += s->ctb_width * s->row_height[i];
++
++              val += (tb_y - s->row_bd[tile_y]) * s->column_width[tile_x] +
++                     tb_x - s->col_bd[tile_x];
++
++              s->ctb_addr_rs_to_ts[ctb_addr_rs] = val;
++              s->ctb_addr_ts_to_rs[val] = ctb_addr_rs;
++      }
++
++      for (j = 0, tile_id = 0; j < s->num_tile_rows; j++)
++              for (i = 0; i < s->num_tile_columns; i++, tile_id++)
++                      for (y = s->row_bd[j]; y < s->row_bd[j + 1]; y++)
++                              for (x = s->col_bd[i];
++                                   x < s->col_bd[i + 1];
++                                   x++)
++                                      s->tile_id[s->ctb_addr_rs_to_ts
++                                                         [y * s->ctb_width +
++                                                          x]] = tile_id;
++
++      return 0;
++}
++
++static int frame_end(struct rpivid_dev *const dev,
++                   struct rpivid_dec_env *const de,
++                   const struct rpivid_dec_state *const s)
++{
++      const unsigned int last_x = s->col_bd[s->num_tile_columns] - 1;
++      const unsigned int last_y = s->row_bd[s->num_tile_rows] - 1;
++      size_t cmd_size;
++
++      if (s->pps.flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED) {
++              if (de->wpp_entry_x < 2 && de->pic_width_in_ctbs_y > 2)
++                      wpp_pause(de, last_y);
++      }
++      p1_apb_write(de, RPI_STATUS, 1 + (last_x << 5) + (last_y << 18));
++
++      // Copy commands out to dma buf
++      cmd_size = de->cmd_len * sizeof(de->cmd_fifo[0]);
++
++      if (!de->cmd_copy_gptr->ptr || cmd_size > de->cmd_copy_gptr->size) {
++              size_t cmd_alloc = round_up_size(cmd_size);
++
++              if (gptr_realloc_new(dev, de->cmd_copy_gptr, cmd_alloc)) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Alloc cmd buffer (%d): FAILED\n", cmd_alloc);
++                      return -ENOMEM;
++              }
++              v4l2_info(&dev->v4l2_dev, "Alloc cmd buffer (%d): OK\n",
++                        cmd_alloc);
++      }
++
++      memcpy(de->cmd_copy_gptr->ptr, de->cmd_fifo, cmd_size);
++      return 0;
++}
++
++static void setup_colmv(struct rpivid_ctx *const ctx, struct rpivid_run *run,
++                      struct rpivid_dec_state *const s)
++{
++      ctx->colmv_stride = ALIGN(s->sps.pic_width_in_luma_samples, 64);
++      ctx->colmv_picsize = ctx->colmv_stride *
++              (ALIGN(s->sps.pic_height_in_luma_samples, 64) >> 4);
++}
++
++// Can be called from irq context
++static struct rpivid_dec_env *dec_env_new(struct rpivid_ctx *const ctx)
++{
++      struct rpivid_dec_env *de;
++      unsigned long lock_flags;
++
++      spin_lock_irqsave(&ctx->dec_lock, lock_flags);
++
++      de = ctx->dec_free;
++      if (de) {
++              ctx->dec_free = de->next;
++              de->next = NULL;
++              de->state = RPIVID_DECODE_SLICE_START;
++      }
++
++      spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
++      return de;
++}
++
++// Can be called from irq context
++static void dec_env_delete(struct rpivid_dec_env *const de)
++{
++      struct rpivid_ctx * const ctx = de->ctx;
++      unsigned long lock_flags;
++
++      aux_q_release(ctx, &de->frame_aux);
++      aux_q_release(ctx, &de->col_aux);
++
++      spin_lock_irqsave(&ctx->dec_lock, lock_flags);
++
++      de->state = RPIVID_DECODE_END;
++      de->next = ctx->dec_free;
++      ctx->dec_free = de;
++
++      spin_unlock_irqrestore(&ctx->dec_lock, lock_flags);
++}
++
++static void dec_env_uninit(struct rpivid_ctx *const ctx)
++{
++      unsigned int i;
++
++      if (ctx->dec_pool) {
++              for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
++                      struct rpivid_dec_env *const de = ctx->dec_pool + i;
++
++                      kfree(de->cmd_fifo);
++              }
++
++              kfree(ctx->dec_pool);
++      }
++
++      ctx->dec_pool = NULL;
++      ctx->dec_free = NULL;
++}
++
++static int dec_env_init(struct rpivid_ctx *const ctx)
++{
++      unsigned int i;
++
++      ctx->dec_pool = kzalloc(sizeof(*ctx->dec_pool) * RPIVID_DEC_ENV_COUNT,
++                              GFP_KERNEL);
++      if (!ctx->dec_pool)
++              return -1;
++
++      spin_lock_init(&ctx->dec_lock);
++
++      // Build free chain
++      ctx->dec_free = ctx->dec_pool;
++      for (i = 0; i != RPIVID_DEC_ENV_COUNT - 1; ++i)
++              ctx->dec_pool[i].next = ctx->dec_pool + i + 1;
++
++      // Fill in other bits
++      for (i = 0; i != RPIVID_DEC_ENV_COUNT; ++i) {
++              struct rpivid_dec_env *const de = ctx->dec_pool + i;
++
++              de->ctx = ctx;
++              de->decode_order = i;
++              de->cmd_max = 1024;
++              de->cmd_fifo = kmalloc_array(de->cmd_max,
++                                           sizeof(struct rpi_cmd),
++                                           GFP_KERNEL);
++              if (!de->cmd_fifo)
++                      goto fail;
++      }
++
++      return 0;
++
++fail:
++      dec_env_uninit(ctx);
++      return -1;
++}
++
++// Assume that we get exactly the same DPB for every slice
++// it makes no real sense otherwise
++#if V4L2_HEVC_DPB_ENTRIES_NUM_MAX > 16
++#error HEVC_DPB_ENTRIES > h/w slots
++#endif
++
++static u32 mk_config2(const struct rpivid_dec_state *const s)
++{
++      const struct v4l2_ctrl_hevc_sps *const sps = &s->sps;
++      const struct v4l2_ctrl_hevc_pps *const pps = &s->pps;
++      u32 c;
++      // BitDepthY
++      c = (sps->bit_depth_luma_minus8 + 8) << 0;
++       // BitDepthC
++      c |= (sps->bit_depth_chroma_minus8 + 8) << 4;
++       // BitDepthY
++      if (sps->bit_depth_luma_minus8)
++              c |= BIT(8);
++      // BitDepthC
++      if (sps->bit_depth_chroma_minus8)
++              c |= BIT(9);
++      c |= s->log2_ctb_size << 10;
++      if (pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)
++              c |= BIT(13);
++      if (sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)
++              c |= BIT(14);
++      if (sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED)
++              c |= BIT(15); /* Write motion vectors to external memory */
++      c |= (pps->log2_parallel_merge_level_minus2 + 2) << 16;
++      if (s->slice_temporal_mvp)
++              c |= BIT(19);
++      if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)
++              c |= BIT(20);
++      c |= (pps->pps_cb_qp_offset & 31) << 21;
++      c |= (pps->pps_cr_qp_offset & 31) << 26;
++      return c;
++}
++
++static void rpivid_h265_setup(struct rpivid_ctx *ctx, struct rpivid_run *run)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++      const struct v4l2_ctrl_hevc_slice_params *const sh =
++                                              run->h265.slice_params;
++      const struct v4l2_hevc_pred_weight_table *pred_weight_table;
++      struct rpivid_q_aux *dpb_q_aux[V4L2_HEVC_DPB_ENTRIES_NUM_MAX];
++      struct rpivid_dec_state *const s = ctx->state;
++      struct vb2_queue *vq;
++      struct rpivid_dec_env *de;
++      int ctb_addr_ts;
++      unsigned int i;
++      int use_aux;
++      bool slice_temporal_mvp;
++
++      pred_weight_table = &sh->pred_weight_table;
++
++      s->frame_end =
++              ((run->src->flags & V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF) == 0);
++
++      de = ctx->dec0;
++      slice_temporal_mvp = (sh->flags &
++                 V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED);
++
++      if (de && de->state != RPIVID_DECODE_END) {
++              ++s->slice_idx;
++
++              switch (de->state) {
++              case RPIVID_DECODE_SLICE_CONTINUE:
++                      // Expected state
++                      break;
++              default:
++                      v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n",
++                               __func__, de->state);
++              /* FALLTHRU */
++              case RPIVID_DECODE_ERROR_CONTINUE:
++                      // Uncleared error - fail now
++                      goto fail;
++              }
++
++              if (s->slice_temporal_mvp != slice_temporal_mvp) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Slice Temporal MVP non-constant\n");
++                      goto fail;
++              }
++      } else {
++              /* Frame start */
++              unsigned int ctb_size_y;
++              bool sps_changed = false;
++
++              if (memcmp(&s->sps, run->h265.sps, sizeof(s->sps)) != 0) {
++                      /* SPS changed */
++                      v4l2_info(&dev->v4l2_dev, "SPS changed\n");
++                      memcpy(&s->sps, run->h265.sps, sizeof(s->sps));
++                      sps_changed = true;
++              }
++              if (sps_changed ||
++                  memcmp(&s->pps, run->h265.pps, sizeof(s->pps)) != 0) {
++                      /* SPS changed */
++                      v4l2_info(&dev->v4l2_dev, "PPS changed\n");
++                      memcpy(&s->pps, run->h265.pps, sizeof(s->pps));
++
++                      /* Recalc stuff as required */
++                      updated_ps(s);
++              }
++
++              de = dec_env_new(ctx);
++              if (!de) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Failed to find free decode env\n");
++                      goto fail;
++              }
++              ctx->dec0 = de;
++
++              ctb_size_y =
++                      1U << (s->sps.log2_min_luma_coding_block_size_minus3 +
++                             3 +
++                             s->sps.log2_diff_max_min_luma_coding_block_size);
++
++              de->pic_width_in_ctbs_y =
++                      (s->sps.pic_width_in_luma_samples + ctb_size_y - 1) /
++                              ctb_size_y; // 7-15
++              de->pic_height_in_ctbs_y =
++                      (s->sps.pic_height_in_luma_samples + ctb_size_y - 1) /
++                              ctb_size_y; // 7-17
++              de->cmd_len = 0;
++              de->dpbno_col = ~0U;
++
++              de->bit_copy_gptr = ctx->bitbufs + 0;
++              de->bit_copy_len = 0;
++              de->cmd_copy_gptr = ctx->cmdbufs + 0;
++
++              de->frame_c_offset = ctx->dst_fmt.height * 128;
++              de->frame_stride = ctx->dst_fmt.bytesperline * 128;
++              de->frame_addr =
++                      vb2_dma_contig_plane_dma_addr(&run->dst->vb2_buf, 0);
++              de->frame_aux = NULL;
++
++              if (s->sps.bit_depth_luma_minus8 !=
++                  s->sps.bit_depth_chroma_minus8) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Chroma depth (%d) != Luma depth (%d)\n",
++                                s->sps.bit_depth_chroma_minus8 + 8,
++                                s->sps.bit_depth_luma_minus8 + 8);
++                      goto fail;
++              }
++              if (s->sps.bit_depth_luma_minus8 == 0) {
++                      if (ctx->dst_fmt.pixelformat !=
++                                              V4L2_PIX_FMT_NV12_COL128) {
++                              v4l2_err(&dev->v4l2_dev,
++                                       "Pixel format %#x != NV12_COL128 for 8-bit output",
++                                       ctx->dst_fmt.pixelformat);
++                              goto fail;
++                      }
++              } else if (s->sps.bit_depth_luma_minus8 == 2) {
++                      if (ctx->dst_fmt.pixelformat !=
++                                              V4L2_PIX_FMT_NV12_10_COL128) {
++                              v4l2_err(&dev->v4l2_dev,
++                                       "Pixel format %#x != NV12_10_COL128 for 10-bit output",
++                                       ctx->dst_fmt.pixelformat);
++                              goto fail;
++                      }
++              } else {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Luma depth (%d) unsupported\n",
++                                s->sps.bit_depth_luma_minus8 + 8);
++                      goto fail;
++              }
++              if (run->dst->vb2_buf.num_planes != 1) {
++                      v4l2_warn(&dev->v4l2_dev, "Capture planes (%d) != 1\n",
++                                run->dst->vb2_buf.num_planes);
++                      goto fail;
++              }
++              if (run->dst->planes[0].length <
++                  ctx->dst_fmt.sizeimage) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Capture plane[0] length (%d) < sizeimage (%d)\n",
++                                run->dst->planes[0].length,
++                                ctx->dst_fmt.sizeimage);
++                      goto fail;
++              }
++
++              if (s->sps.pic_width_in_luma_samples > 4096 ||
++                  s->sps.pic_height_in_luma_samples > 4096) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Pic dimension (%dx%d) exeeds 4096\n",
++                                s->sps.pic_width_in_luma_samples,
++                                s->sps.pic_height_in_luma_samples);
++                      goto fail;
++              }
++
++              // Fill in ref planes with our address s.t. if we mess
++              // up refs somehow then we still have a valid address
++              // entry
++              for (i = 0; i != 16; ++i)
++                      de->ref_addrs[i] = de->frame_addr;
++
++              /*
++               * Stash initial temporal_mvp flag
++               * This must be the same for all pic slices (7.4.7.1)
++               */
++              s->slice_temporal_mvp = slice_temporal_mvp;
++
++              // Phase 2 reg pre-calc
++              de->rpi_config2 = mk_config2(s);
++              de->rpi_framesize = (s->sps.pic_height_in_luma_samples << 16) |
++                                  s->sps.pic_width_in_luma_samples;
++              de->rpi_currpoc = sh->slice_pic_order_cnt;
++
++              if (s->sps.flags &
++                  V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) {
++                      setup_colmv(ctx, run, s);
++              }
++
++              s->slice_idx = 0;
++
++              if (sh->slice_segment_addr != 0) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "New frame but segment_addr=%d\n",
++                                sh->slice_segment_addr);
++                      goto fail;
++              }
++
++              /* Allocate a bitbuf if we need one - don't need one if single
++               * slice as we can use the src buf directly
++               */
++              if (!s->frame_end && !de->bit_copy_gptr->ptr) {
++                      const size_t wxh = s->sps.pic_width_in_luma_samples *
++                              s->sps.pic_height_in_luma_samples;
++                      size_t bits_alloc;
++
++                      /* Annex A gives a min compression of 2 @ lvl 3.1
++                       * (wxh <= 983040) and min 4 thereafter but avoid
++                       * the odity of 983041 having a lower limit than
++                       * 983040.
++                       * Multiply by 3/2 for 4:2:0
++                       */
++                      bits_alloc = wxh < 983040 ? wxh * 3 / 4 :
++                              wxh < 983040 * 2 ? 983040 * 3 / 4 :
++                              wxh * 3 / 8;
++                      bits_alloc = round_up_size(bits_alloc);
++
++                      if (gptr_alloc(dev, de->bit_copy_gptr,
++                                     bits_alloc,
++                                     DMA_ATTR_FORCE_CONTIGUOUS) != 0) {
++                              v4l2_err(&dev->v4l2_dev,
++                                       "Unable to alloc buf (%d) for bit copy\n",
++                                       bits_alloc);
++                              goto fail;
++                      }
++                      v4l2_info(&dev->v4l2_dev,
++                                "Alloc buf (%d) for bit copy OK\n",
++                                bits_alloc);
++              }
++      }
++
++      // Pre calc a few things
++      s->src_addr =
++              !s->frame_end ?
++                      0 :
++                      vb2_dma_contig_plane_dma_addr(&run->src->vb2_buf, 0);
++      s->src_buf = s->src_addr != 0 ? NULL :
++                                      vb2_plane_vaddr(&run->src->vb2_buf, 0);
++      if (!s->src_addr && !s->src_buf) {
++              v4l2_err(&dev->v4l2_dev, "Failed to map src buffer\n");
++              goto fail;
++      }
++
++      s->sh = sh;
++      s->slice_qp = 26 + s->pps.init_qp_minus26 + s->sh->slice_qp_delta;
++      s->max_num_merge_cand = sh->slice_type == HEVC_SLICE_I ?
++                                      0 :
++                                      (5 - sh->five_minus_max_num_merge_cand);
++      // * SH DSS flag invented by me - but clearly needed
++      s->dependent_slice_segment_flag =
++              ((sh->flags &
++                V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT) != 0);
++
++      s->nb_refs[0] = (sh->slice_type == HEVC_SLICE_I) ?
++                              0 :
++                              sh->num_ref_idx_l0_active_minus1 + 1;
++      s->nb_refs[1] = (sh->slice_type != HEVC_SLICE_B) ?
++                              0 :
++                              sh->num_ref_idx_l1_active_minus1 + 1;
++
++      if (s->sps.flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED)
++              populate_scaling_factors(run, de, s);
++
++      ctb_addr_ts = s->ctb_addr_rs_to_ts[sh->slice_segment_addr];
++
++      if ((s->pps.flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED))
++              wpp_decode_slice(de, s, sh, ctb_addr_ts);
++      else
++              decode_slice(de, s, sh, ctb_addr_ts);
++
++      if (!s->frame_end)
++              return;
++
++      // Frame end
++      memset(dpb_q_aux, 0,
++             sizeof(*dpb_q_aux) * V4L2_HEVC_DPB_ENTRIES_NUM_MAX);
++      /*
++       * Need Aux ents for all (ref) DPB ents if temporal MV could
++       * be enabled for any pic
++       * ** At the moment we have aux ents for all pics whether or not
++       *    they are ref
++       */
++      use_aux = ((s->sps.flags &
++                V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) != 0);
++
++      // Locate ref frames
++      // At least in the current implementation this is constant across all
++      // slices. If this changes we will need idx mapping code.
++      // Uses sh so here rather than trigger
++
++      vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
++
++      if (!vq) {
++              v4l2_err(&dev->v4l2_dev, "VQ gone!\n");
++              goto fail;
++      }
++
++      //        v4l2_info(&dev->v4l2_dev, "rpivid_h265_end of frame\n");
++      if (frame_end(dev, de, s))
++              goto fail;
++
++      for (i = 0; i < sh->num_active_dpb_entries; ++i) {
++              int buffer_index =
++                      vb2_find_timestamp(vq, sh->dpb[i].timestamp, 0);
++              struct vb2_buffer *buf = buffer_index < 0 ?
++                                      NULL :
++                                      vb2_get_buffer(vq, buffer_index);
++
++              if (!buf) {
++                      v4l2_warn(&dev->v4l2_dev,
++                                "Missing DPB ent %d, timestamp=%lld, index=%d\n",
++                                i, (long long)sh->dpb[i].timestamp,
++                                buffer_index);
++                      continue;
++              }
++
++              if (use_aux) {
++                      dpb_q_aux[i] = aux_q_ref(ctx,
++                                               ctx->aux_ents[buffer_index]);
++                      if (!dpb_q_aux[i])
++                              v4l2_warn(&dev->v4l2_dev,
++                                        "Missing DPB AUX ent %d index=%d\n",
++                                        i, buffer_index);
++              }
++
++              de->ref_addrs[i] =
++                      vb2_dma_contig_plane_dma_addr(buf, 0);
++      }
++
++      // Move DPB from temp
++      for (i = 0; i != V4L2_HEVC_DPB_ENTRIES_NUM_MAX; ++i) {
++              aux_q_release(ctx, &s->ref_aux[i]);
++              s->ref_aux[i] = dpb_q_aux[i];
++      }
++      // Unref the old frame aux too - it is either in the DPB or not
++      // now
++      aux_q_release(ctx, &s->frame_aux);
++
++      if (use_aux) {
++              // New frame so new aux ent
++              // ??? Do we need this if non-ref ??? can we tell
++              s->frame_aux = aux_q_new(ctx, run->dst->vb2_buf.index);
++
++              if (!s->frame_aux) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Failed to obtain aux storage for frame\n");
++                      goto fail;
++              }
++
++              de->frame_aux = aux_q_ref(ctx, s->frame_aux);
++      }
++
++      if (de->dpbno_col != ~0U) {
++              if (de->dpbno_col >= sh->num_active_dpb_entries) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "Col ref index %d >= %d\n",
++                               de->dpbno_col,
++                               sh->num_active_dpb_entries);
++              } else {
++                      // Standard requires that the col pic is
++                      // constant for the duration of the pic
++                      // (text of collocated_ref_idx in H265-2 2018
++                      // 7.4.7.1)
++
++                      // Spot the collocated ref in passing
++                      de->col_aux = aux_q_ref(ctx,
++                                              dpb_q_aux[de->dpbno_col]);
++
++                      if (!de->col_aux) {
++                              v4l2_warn(&dev->v4l2_dev,
++                                        "Missing DPB ent for col\n");
++                              // Probably need to abort if this fails
++                              // as P2 may explode on bad data
++                              goto fail;
++                      }
++              }
++      }
++
++      de->state = RPIVID_DECODE_PHASE1;
++      return;
++
++fail:
++      if (de)
++              // Actual error reporting happens in Trigger
++              de->state = s->frame_end ? RPIVID_DECODE_ERROR_DONE :
++                                         RPIVID_DECODE_ERROR_CONTINUE;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Handle PU and COEFF stream overflow
++
++// Returns:
++// -1  Phase 1 decode error
++//  0  OK
++// >0  Out of space (bitmask)
++
++#define STATUS_COEFF_EXHAUSTED        8
++#define STATUS_PU_EXHAUSTED   16
++
++static int check_status(const struct rpivid_dev *const dev)
++{
++      const u32 cfstatus = apb_read(dev, RPI_CFSTATUS);
++      const u32 cfnum = apb_read(dev, RPI_CFNUM);
++      u32 status = apb_read(dev, RPI_STATUS);
++
++      // Handle PU and COEFF stream overflow
++
++      // this is the definition of successful completion of phase 1
++      // it assures that status register is zero and all blocks in each tile
++      // have completed
++      if (cfstatus == cfnum)
++              return 0;       //No error
++
++      status &= (STATUS_PU_EXHAUSTED | STATUS_COEFF_EXHAUSTED);
++      if (status)
++              return status;
++
++      return -1;
++}
++
++static void cb_phase2(struct rpivid_dev *const dev, void *v)
++{
++      struct rpivid_dec_env *const de = v;
++      struct rpivid_ctx *const ctx = de->ctx;
++
++      xtrace_in(dev, de);
++
++      v4l2_m2m_cap_buf_return(dev->m2m_dev, ctx->fh.m2m_ctx, de->frame_buf,
++                              VB2_BUF_STATE_DONE);
++      de->frame_buf = NULL;
++
++      /* Delete de before finish as finish might immediately trigger a reuse
++       * of de
++       */
++      dec_env_delete(de);
++
++      if (atomic_add_return(-1, &ctx->p2out) >= RPIVID_P2BUF_COUNT - 1) {
++              xtrace_fin(dev, de);
++              v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                               VB2_BUF_STATE_DONE);
++      }
++
++      xtrace_ok(dev, de);
++}
++
++static void phase2_claimed(struct rpivid_dev *const dev, void *v)
++{
++      struct rpivid_dec_env *const de = v;
++      unsigned int i;
++
++      xtrace_in(dev, de);
++
++      apb_write_vc_addr(dev, RPI_PURBASE, de->pu_base_vc);
++      apb_write_vc_len(dev, RPI_PURSTRIDE, de->pu_stride);
++      apb_write_vc_addr(dev, RPI_COEFFRBASE, de->coeff_base_vc);
++      apb_write_vc_len(dev, RPI_COEFFRSTRIDE, de->coeff_stride);
++
++      apb_write_vc_addr(dev, RPI_OUTYBASE, de->frame_addr);
++      apb_write_vc_addr(dev, RPI_OUTCBASE,
++                        de->frame_addr + de->frame_c_offset);
++      apb_write_vc_len(dev, RPI_OUTYSTRIDE, de->frame_stride);
++      apb_write_vc_len(dev, RPI_OUTCSTRIDE, de->frame_stride);
++
++      //    v4l2_info(&dev->v4l2_dev, "Frame: Y=%llx, C=%llx, Stride=%x\n",
++      //              de->frame_addr, de->frame_addr + de->frame_c_offset,
++      //              de->frame_stride);
++
++      for (i = 0; i < 16; i++) {
++              // Strides are in fact unused but fill in anyway
++              apb_write_vc_addr(dev, 0x9000 + 16 * i, de->ref_addrs[i]);
++              apb_write_vc_len(dev, 0x9004 + 16 * i, de->frame_stride);
++              apb_write_vc_addr(dev, 0x9008 + 16 * i,
++                                de->ref_addrs[i] + de->frame_c_offset);
++              apb_write_vc_len(dev, 0x900C + 16 * i, de->frame_stride);
++      }
++
++      apb_write(dev, RPI_CONFIG2, de->rpi_config2);
++      apb_write(dev, RPI_FRAMESIZE, de->rpi_framesize);
++      apb_write(dev, RPI_CURRPOC, de->rpi_currpoc);
++      //    v4l2_info(&dev->v4l2_dev, "Config2=%#x, FrameSize=%#x, POC=%#x\n",
++      //    de->rpi_config2, de->rpi_framesize, de->rpi_currpoc);
++
++      // collocated reads/writes
++      apb_write_vc_len(dev, RPI_COLSTRIDE,
++                       de->ctx->colmv_stride); // Read vals
++      apb_write_vc_len(dev, RPI_MVSTRIDE,
++                       de->ctx->colmv_stride); // Write vals
++      apb_write_vc_addr(dev, RPI_MVBASE,
++                        !de->frame_aux ? 0 : de->frame_aux->col.addr);
++      apb_write_vc_addr(dev, RPI_COLBASE,
++                        !de->col_aux ? 0 : de->col_aux->col.addr);
++
++      //v4l2_info(&dev->v4l2_dev,
++      //         "Mv=%llx, Col=%llx, Stride=%x, Buf=%llx->%llx\n",
++      //         de->rpi_mvbase, de->rpi_colbase, de->ctx->colmv_stride,
++      //         de->ctx->colmvbuf.addr, de->ctx->colmvbuf.addr +
++      //         de->ctx->colmvbuf.size);
++
++      rpivid_hw_irq_active2_irq(dev, &de->irq_ent, cb_phase2, de);
++
++      apb_write_final(dev, RPI_NUMROWS, de->pic_height_in_ctbs_y);
++
++      xtrace_ok(dev, de);
++}
++
++static void phase1_claimed(struct rpivid_dev *const dev, void *v);
++
++static void phase1_thread(struct rpivid_dev *const dev, void *v)
++{
++      struct rpivid_dec_env *const de = v;
++      struct rpivid_ctx *const ctx = de->ctx;
++
++      struct rpivid_gptr *const pu_gptr = ctx->pu_bufs + ctx->p2idx;
++      struct rpivid_gptr *const coeff_gptr = ctx->coeff_bufs + ctx->p2idx;
++
++      xtrace_in(dev, de);
++
++      if (de->p1_status & STATUS_PU_EXHAUSTED) {
++              if (gptr_realloc_new(dev, pu_gptr, next_size(pu_gptr->size))) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "%s: PU realloc (%#x) failed\n",
++                               __func__, pu_gptr->size);
++                      goto fail;
++              }
++              v4l2_info(&dev->v4l2_dev, "%s: PU realloc (%#x) OK\n",
++                        __func__, pu_gptr->size);
++      }
++
++      if (de->p1_status & STATUS_COEFF_EXHAUSTED) {
++              if (gptr_realloc_new(dev, coeff_gptr,
++                                   next_size(coeff_gptr->size))) {
++                      v4l2_err(&dev->v4l2_dev,
++                               "%s: Coeff realloc (%#x) failed\n",
++                               __func__, coeff_gptr->size);
++                      goto fail;
++              }
++              v4l2_info(&dev->v4l2_dev, "%s: Coeff realloc (%#x) OK\n",
++                        __func__, coeff_gptr->size);
++      }
++
++      phase1_claimed(dev, de);
++      xtrace_ok(dev, de);
++      return;
++
++fail:
++      dec_env_delete(de);
++      xtrace_fin(dev, de);
++      v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                       VB2_BUF_STATE_ERROR);
++      xtrace_fail(dev, de);
++}
++
++/* Always called in irq context (this is good) */
++static void cb_phase1(struct rpivid_dev *const dev, void *v)
++{
++      struct rpivid_dec_env *const de = v;
++      struct rpivid_ctx *const ctx = de->ctx;
++
++      xtrace_in(dev, de);
++
++      de->p1_status = check_status(dev);
++      if (de->p1_status != 0) {
++              v4l2_info(&dev->v4l2_dev, "%s: Post wait: %#x\n",
++                        __func__, de->p1_status);
++
++              if (de->p1_status < 0)
++                      goto fail;
++
++              /* Need to realloc - push onto a thread rather than IRQ */
++              rpivid_hw_irq_active1_thread(dev, &de->irq_ent,
++                                           phase1_thread, de);
++              return;
++      }
++
++      /* After the frame-buf is detached it must be returned but from
++       * this point onward (phase2_claimed, cb_phase2) there are no error
++       * paths so the return at the end of cb_phase2 is all that is needed
++       */
++      de->frame_buf = v4l2_m2m_cap_buf_detach(dev->m2m_dev, ctx->fh.m2m_ctx);
++      if (!de->frame_buf) {
++              v4l2_err(&dev->v4l2_dev, "%s: No detached buffer\n", __func__);
++              goto fail;
++      }
++
++      ctx->p2idx =
++              (ctx->p2idx + 1 >= RPIVID_P2BUF_COUNT) ? 0 : ctx->p2idx + 1;
++
++      // Enable the next setup if our Q isn't too big
++      if (atomic_add_return(1, &ctx->p2out) < RPIVID_P2BUF_COUNT) {
++              xtrace_fin(dev, de);
++              v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                               VB2_BUF_STATE_DONE);
++      }
++
++      rpivid_hw_irq_active2_claim(dev, &de->irq_ent, phase2_claimed, de);
++
++      xtrace_ok(dev, de);
++      return;
++
++fail:
++      dec_env_delete(de);
++      xtrace_fin(dev, de);
++      v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                       VB2_BUF_STATE_ERROR);
++      xtrace_fail(dev, de);
++}
++
++static void phase1_claimed(struct rpivid_dev *const dev, void *v)
++{
++      struct rpivid_dec_env *const de = v;
++      struct rpivid_ctx *const ctx = de->ctx;
++
++      const struct rpivid_gptr * const pu_gptr = ctx->pu_bufs + ctx->p2idx;
++      const struct rpivid_gptr * const coeff_gptr = ctx->coeff_bufs +
++                                                    ctx->p2idx;
++
++      xtrace_in(dev, de);
++
++      de->pu_base_vc = pu_gptr->addr;
++      de->pu_stride =
++              ALIGN_DOWN(pu_gptr->size / de->pic_height_in_ctbs_y, 64);
++
++      de->coeff_base_vc = coeff_gptr->addr;
++      de->coeff_stride =
++              ALIGN_DOWN(coeff_gptr->size / de->pic_height_in_ctbs_y, 64);
++
++      apb_write_vc_addr(dev, RPI_PUWBASE, de->pu_base_vc);
++      apb_write_vc_len(dev, RPI_PUWSTRIDE, de->pu_stride);
++      apb_write_vc_addr(dev, RPI_COEFFWBASE, de->coeff_base_vc);
++      apb_write_vc_len(dev, RPI_COEFFWSTRIDE, de->coeff_stride);
++
++      // Trigger command FIFO
++      apb_write(dev, RPI_CFNUM, de->cmd_len);
++
++      // Claim irq
++      rpivid_hw_irq_active1_irq(dev, &de->irq_ent, cb_phase1, de);
++
++      // And start the h/w
++      apb_write_vc_addr_final(dev, RPI_CFBASE, de->cmd_copy_gptr->addr);
++
++      xtrace_ok(dev, de);
++}
++
++static void dec_state_delete(struct rpivid_ctx *const ctx)
++{
++      unsigned int i;
++      struct rpivid_dec_state *const s = ctx->state;
++
++      if (!s)
++              return;
++      ctx->state = NULL;
++
++      free_ps_info(s);
++
++      for (i = 0; i != HEVC_MAX_REFS; ++i)
++              aux_q_release(ctx, &s->ref_aux[i]);
++      aux_q_release(ctx, &s->frame_aux);
++
++      kfree(s);
++}
++
++static void rpivid_h265_stop(struct rpivid_ctx *ctx)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++      unsigned int i;
++
++      v4l2_info(&dev->v4l2_dev, "%s\n", __func__);
++
++      dec_env_uninit(ctx);
++      dec_state_delete(ctx);
++
++      // dec_env & state must be killed before this to release the buffer to
++      // the free pool
++      aux_q_uninit(ctx);
++
++      for (i = 0; i != ARRAY_SIZE(ctx->bitbufs); ++i)
++              gptr_free(dev, ctx->bitbufs + i);
++      for (i = 0; i != ARRAY_SIZE(ctx->cmdbufs); ++i)
++              gptr_free(dev, ctx->cmdbufs + i);
++      for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i)
++              gptr_free(dev, ctx->pu_bufs + i);
++      for (i = 0; i != ARRAY_SIZE(ctx->coeff_bufs); ++i)
++              gptr_free(dev, ctx->coeff_bufs + i);
++}
++
++static int rpivid_h265_start(struct rpivid_ctx *ctx)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++      unsigned int i;
++
++      unsigned int w = ctx->dst_fmt.width;
++      unsigned int h = ctx->dst_fmt.height;
++      unsigned int wxh;
++      size_t pu_alloc;
++      size_t coeff_alloc;
++
++      // Generate a sanitised WxH for memory alloc
++      // Assume HD if unset
++      if (w == 0)
++              w = 1920;
++      if (w > 4096)
++              w = 4096;
++      if (h == 0)
++              w = 1088;
++      if (h > 4096)
++              h = 4096;
++      wxh = w * h;
++
++      v4l2_info(&dev->v4l2_dev, "%s: (%dx%d)\n", __func__,
++                ctx->dst_fmt.width, ctx->dst_fmt.height);
++
++      ctx->dec0 = NULL;
++      ctx->state = kzalloc(sizeof(*ctx->state), GFP_KERNEL);
++      if (!ctx->state) {
++              v4l2_err(&dev->v4l2_dev, "Failed to allocate decode state\n");
++              goto fail;
++      }
++
++      if (dec_env_init(ctx) != 0) {
++              v4l2_err(&dev->v4l2_dev, "Failed to allocate decode envs\n");
++              goto fail;
++      }
++
++      // 16k is plenty for most purposes but we will realloc if needed
++      for (i = 0; i != ARRAY_SIZE(ctx->cmdbufs); ++i) {
++              if (gptr_alloc(dev, ctx->cmdbufs + i, 0x4000,
++                             DMA_ATTR_FORCE_CONTIGUOUS))
++                      goto fail;
++      }
++
++      // Finger in the air PU & Coeff alloc
++      // Will be realloced if too small
++      coeff_alloc = round_up_size(wxh);
++      pu_alloc = round_up_size(wxh / 4);
++      for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i) {
++              // Don't actually need a kernel mapping here
++              if (gptr_alloc(dev, ctx->pu_bufs + i, pu_alloc,
++                             DMA_ATTR_FORCE_CONTIGUOUS |
++                                      DMA_ATTR_NO_KERNEL_MAPPING))
++                      goto fail;
++              if (gptr_alloc(dev, ctx->coeff_bufs + i, coeff_alloc,
++                             DMA_ATTR_FORCE_CONTIGUOUS |
++                                      DMA_ATTR_NO_KERNEL_MAPPING))
++                      goto fail;
++      }
++      aux_q_init(ctx);
++
++      return 0;
++
++fail:
++      rpivid_h265_stop(ctx);
++      return -ENOMEM;
++}
++
++static void rpivid_h265_trigger(struct rpivid_ctx *ctx)
++{
++      struct rpivid_dev *const dev = ctx->dev;
++      struct rpivid_dec_env *const de = ctx->dec0;
++
++      xtrace_in(dev, de);
++
++      switch (!de ? RPIVID_DECODE_ERROR_CONTINUE : de->state) {
++      case RPIVID_DECODE_SLICE_START:
++              de->state = RPIVID_DECODE_SLICE_CONTINUE;
++      /* FALLTHRU */
++      case RPIVID_DECODE_SLICE_CONTINUE:
++              v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                               VB2_BUF_STATE_DONE);
++              break;
++      default:
++              v4l2_err(&dev->v4l2_dev, "%s: Unexpected state: %d\n", __func__,
++                       de->state);
++      /* FALLTHRU */
++      case RPIVID_DECODE_ERROR_DONE:
++              ctx->dec0 = NULL;
++              dec_env_delete(de);
++      /* FALLTHRU */
++      case RPIVID_DECODE_ERROR_CONTINUE:
++              xtrace_fin(dev, de);
++              v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx,
++                                               VB2_BUF_STATE_ERROR);
++              break;
++      case RPIVID_DECODE_PHASE1:
++              ctx->dec0 = NULL;
++              rpivid_hw_irq_active1_claim(dev, &de->irq_ent, phase1_claimed,
++                                          de);
++              break;
++      }
++
++      xtrace_ok(dev, de);
++}
++
++struct rpivid_dec_ops rpivid_dec_ops_h265 = {
++      .setup = rpivid_h265_setup,
++      .start = rpivid_h265_start,
++      .stop = rpivid_h265_stop,
++      .trigger = rpivid_h265_trigger,
++};
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_hw.c
+@@ -0,0 +1,321 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++#include <linux/clk.h>
++#include <linux/component.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/of_device.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include <media/videobuf2-core.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_hw.h"
++
++static void pre_irq(struct rpivid_dev *dev, struct rpivid_hw_irq_ent *ient,
++                  rpivid_irq_callback cb, void *v,
++                  struct rpivid_hw_irq_ctrl *ictl)
++{
++      unsigned long flags;
++
++      if (ictl->irq) {
++              v4l2_err(&dev->v4l2_dev, "Attempt to claim IRQ when already claimed\n");
++              return;
++      }
++
++      ient->cb = cb;
++      ient->v = v;
++
++      // Not sure this lock is actually required
++      spin_lock_irqsave(&ictl->lock, flags);
++      ictl->irq = ient;
++      spin_unlock_irqrestore(&ictl->lock, flags);
++}
++
++static void sched_claim(struct rpivid_dev * const dev,
++                      struct rpivid_hw_irq_ctrl * const ictl)
++{
++      for (;;) {
++              struct rpivid_hw_irq_ent *ient = NULL;
++              unsigned long flags;
++
++              spin_lock_irqsave(&ictl->lock, flags);
++
++              if (--ictl->no_sched <= 0) {
++                      ient = ictl->claim;
++                      if (!ictl->irq && ient) {
++                              ictl->claim = ient->next;
++                              ictl->no_sched = 1;
++                      }
++              }
++
++              spin_unlock_irqrestore(&ictl->lock, flags);
++
++              if (!ient)
++                      break;
++
++              ient->cb(dev, ient->v);
++      }
++}
++
++/* Should only ever be called from its own IRQ cb so no lock required */
++static void pre_thread(struct rpivid_dev *dev,
++                     struct rpivid_hw_irq_ent *ient,
++                     rpivid_irq_callback cb, void *v,
++                     struct rpivid_hw_irq_ctrl *ictl)
++{
++      ient->cb = cb;
++      ient->v = v;
++      ictl->irq = ient;
++      ictl->thread_reqed = true;
++      ictl->no_sched++;
++}
++
++// Called in irq context
++static void do_irq(struct rpivid_dev * const dev,
++                 struct rpivid_hw_irq_ctrl * const ictl)
++{
++      struct rpivid_hw_irq_ent *ient;
++      unsigned long flags;
++
++      spin_lock_irqsave(&ictl->lock, flags);
++      ient = ictl->irq;
++      if (ient) {
++              ictl->no_sched++;
++              ictl->irq = NULL;
++      }
++      spin_unlock_irqrestore(&ictl->lock, flags);
++
++      if (ient) {
++              ient->cb(dev, ient->v);
++
++              sched_claim(dev, ictl);
++      }
++}
++
++static void do_claim(struct rpivid_dev * const dev,
++                   struct rpivid_hw_irq_ent *ient,
++                   const rpivid_irq_callback cb, void * const v,
++                   struct rpivid_hw_irq_ctrl * const ictl)
++{
++      unsigned long flags;
++
++      ient->next = NULL;
++      ient->cb = cb;
++      ient->v = v;
++
++      spin_lock_irqsave(&ictl->lock, flags);
++
++      if (ictl->claim) {
++              // If we have a Q then add to end
++              ictl->tail->next = ient;
++              ictl->tail = ient;
++              ient = NULL;
++      } else if (ictl->no_sched || ictl->irq) {
++              // Empty Q but other activity in progress so Q
++              ictl->claim = ient;
++              ictl->tail = ient;
++              ient = NULL;
++      } else {
++              // Nothing else going on - schedule immediately and
++              // prevent anything else scheduling claims
++              ictl->no_sched = 1;
++      }
++
++      spin_unlock_irqrestore(&ictl->lock, flags);
++
++      if (ient) {
++              ient->cb(dev, ient->v);
++
++              sched_claim(dev, ictl);
++      }
++}
++
++static void ictl_init(struct rpivid_hw_irq_ctrl * const ictl)
++{
++      spin_lock_init(&ictl->lock);
++      ictl->claim = NULL;
++      ictl->tail = NULL;
++      ictl->irq = NULL;
++      ictl->no_sched = 0;
++}
++
++static void ictl_uninit(struct rpivid_hw_irq_ctrl * const ictl)
++{
++      // Nothing to do
++}
++
++#if !OPT_DEBUG_POLL_IRQ
++static irqreturn_t rpivid_irq_irq(int irq, void *data)
++{
++      struct rpivid_dev * const dev = data;
++      __u32 ictrl;
++
++      ictrl = irq_read(dev, ARG_IC_ICTRL);
++      if (!(ictrl & ARG_IC_ICTRL_ALL_IRQ_MASK)) {
++              v4l2_warn(&dev->v4l2_dev, "IRQ but no IRQ bits set\n");
++              return IRQ_NONE;
++      }
++
++      // Cancel any/all irqs
++      irq_write(dev, ARG_IC_ICTRL, ictrl & ~ARG_IC_ICTRL_SET_ZERO_MASK);
++
++      // Service Active2 before Active1 so Phase 1 can transition to Phase 2
++      // without delay
++      if (ictrl & ARG_IC_ICTRL_ACTIVE2_INT_SET)
++              do_irq(dev, &dev->ic_active2);
++      if (ictrl & ARG_IC_ICTRL_ACTIVE1_INT_SET)
++              do_irq(dev, &dev->ic_active1);
++
++      return dev->ic_active1.thread_reqed || dev->ic_active2.thread_reqed ?
++              IRQ_WAKE_THREAD : IRQ_HANDLED;
++}
++
++static void do_thread(struct rpivid_dev * const dev,
++                    struct rpivid_hw_irq_ctrl *const ictl)
++{
++      unsigned long flags;
++      struct rpivid_hw_irq_ent *ient = NULL;
++
++      spin_lock_irqsave(&ictl->lock, flags);
++
++      if (ictl->thread_reqed) {
++              ient = ictl->irq;
++              ictl->thread_reqed = false;
++              ictl->irq = NULL;
++      }
++
++      spin_unlock_irqrestore(&ictl->lock, flags);
++
++      if (ient) {
++              ient->cb(dev, ient->v);
++
++              sched_claim(dev, ictl);
++      }
++}
++
++static irqreturn_t rpivid_irq_thread(int irq, void *data)
++{
++      struct rpivid_dev * const dev = data;
++
++      do_thread(dev, &dev->ic_active1);
++      do_thread(dev, &dev->ic_active2);
++
++      return IRQ_HANDLED;
++}
++#endif
++
++/* May only be called from Active1 CB
++ * IRQs should not be expected until execution continues in the cb
++ */
++void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
++                                struct rpivid_hw_irq_ent *ient,
++                                rpivid_irq_callback thread_cb, void *ctx)
++{
++      pre_thread(dev, ient, thread_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
++                               struct rpivid_hw_irq_ent *ient,
++                               rpivid_irq_callback ready_cb, void *ctx)
++{
++      do_claim(dev, ient, ready_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
++                             struct rpivid_hw_irq_ent *ient,
++                             rpivid_irq_callback irq_cb, void *ctx)
++{
++      pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active1);
++}
++
++void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
++                               struct rpivid_hw_irq_ent *ient,
++                               rpivid_irq_callback ready_cb, void *ctx)
++{
++      do_claim(dev, ient, ready_cb, ctx, &dev->ic_active2);
++}
++
++void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
++                             struct rpivid_hw_irq_ent *ient,
++                             rpivid_irq_callback irq_cb, void *ctx)
++{
++      pre_irq(dev, ient, irq_cb, ctx, &dev->ic_active2);
++}
++
++int rpivid_hw_probe(struct rpivid_dev *dev)
++{
++      struct resource *res;
++      __u32 irq_stat;
++      int irq_dec;
++      int ret = 0;
++
++      ictl_init(&dev->ic_active1);
++      ictl_init(&dev->ic_active2);
++
++      res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "intc");
++      if (!res)
++              return -ENODEV;
++
++      dev->base_irq = devm_ioremap(dev->dev, res->start, resource_size(res));
++      if (IS_ERR(dev->base_irq))
++              return PTR_ERR(dev->base_irq);
++
++      res = platform_get_resource_byname(dev->pdev, IORESOURCE_MEM, "hevc");
++      if (!res)
++              return -ENODEV;
++
++      dev->base_h265 = devm_ioremap(dev->dev, res->start, resource_size(res));
++      if (IS_ERR(dev->base_h265))
++              return PTR_ERR(dev->base_h265);
++
++      dev->clock = devm_clk_get(&dev->pdev->dev, "hevc");
++      if (IS_ERR(dev->clock))
++              return PTR_ERR(dev->clock);
++
++      // Disable IRQs & reset anything pending
++      irq_write(dev, 0,
++                ARG_IC_ICTRL_ACTIVE1_EN_SET | ARG_IC_ICTRL_ACTIVE2_EN_SET);
++      irq_stat = irq_read(dev, 0);
++      irq_write(dev, 0, irq_stat);
++
++#if !OPT_DEBUG_POLL_IRQ
++      irq_dec = platform_get_irq(dev->pdev, 0);
++      if (irq_dec <= 0)
++              return irq_dec;
++      ret = devm_request_threaded_irq(dev->dev, irq_dec,
++                                      rpivid_irq_irq,
++                                      rpivid_irq_thread,
++                                      0, dev_name(dev->dev), dev);
++      if (ret) {
++              dev_err(dev->dev, "Failed to request IRQ - %d\n", ret);
++
++              return ret;
++      }
++#endif
++      return ret;
++}
++
++void rpivid_hw_remove(struct rpivid_dev *dev)
++{
++      // IRQ auto freed on unload so no need to do it here
++      ictl_uninit(&dev->ic_active1);
++      ictl_uninit(&dev->ic_active2);
++}
++
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_hw.h
+@@ -0,0 +1,300 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_HW_H_
++#define _RPIVID_HW_H_
++
++struct rpivid_hw_irq_ent {
++      struct rpivid_hw_irq_ent *next;
++      rpivid_irq_callback cb;
++      void *v;
++};
++
++/* Phase 1 Register offsets */
++
++#define RPI_SPS0 0
++#define RPI_SPS1 4
++#define RPI_PPS 8
++#define RPI_SLICE 12
++#define RPI_TILESTART 16
++#define RPI_TILEEND 20
++#define RPI_SLICESTART 24
++#define RPI_MODE 28
++#define RPI_LEFT0 32
++#define RPI_LEFT1 36
++#define RPI_LEFT2 40
++#define RPI_LEFT3 44
++#define RPI_QP 48
++#define RPI_CONTROL 52
++#define RPI_STATUS 56
++#define RPI_VERSION 60
++#define RPI_BFBASE 64
++#define RPI_BFNUM 68
++#define RPI_BFCONTROL 72
++#define RPI_BFSTATUS 76
++#define RPI_PUWBASE 80
++#define RPI_PUWSTRIDE 84
++#define RPI_COEFFWBASE 88
++#define RPI_COEFFWSTRIDE 92
++#define RPI_SLICECMDS 96
++#define RPI_BEGINTILEEND 100
++#define RPI_TRANSFER 104
++#define RPI_CFBASE 108
++#define RPI_CFNUM 112
++#define RPI_CFSTATUS 116
++
++/* Phase 2 Register offsets */
++
++#define RPI_PURBASE 0x8000
++#define RPI_PURSTRIDE 0x8004
++#define RPI_COEFFRBASE 0x8008
++#define RPI_COEFFRSTRIDE 0x800C
++#define RPI_NUMROWS 0x8010
++#define RPI_CONFIG2 0x8014
++#define RPI_OUTYBASE 0x8018
++#define RPI_OUTYSTRIDE 0x801C
++#define RPI_OUTCBASE 0x8020
++#define RPI_OUTCSTRIDE 0x8024
++#define RPI_STATUS2 0x8028
++#define RPI_FRAMESIZE 0x802C
++#define RPI_MVBASE 0x8030
++#define RPI_MVSTRIDE 0x8034
++#define RPI_COLBASE 0x8038
++#define RPI_COLSTRIDE 0x803C
++#define RPI_CURRPOC 0x8040
++
++/*
++ * Write a general register value
++ * Order is unimportant
++ */
++static inline void apb_write(const struct rpivid_dev * const dev,
++                           const unsigned int offset, const u32 val)
++{
++      writel_relaxed(val, dev->base_h265 + offset);
++}
++
++/* Write the final register value that actually starts the phase */
++static inline void apb_write_final(const struct rpivid_dev * const dev,
++                                 const unsigned int offset, const u32 val)
++{
++      writel(val, dev->base_h265 + offset);
++}
++
++static inline u32 apb_read(const struct rpivid_dev * const dev,
++                         const unsigned int offset)
++{
++      return readl(dev->base_h265 + offset);
++}
++
++static inline void irq_write(const struct rpivid_dev * const dev,
++                           const unsigned int offset, const u32 val)
++{
++      writel(val, dev->base_irq + offset);
++}
++
++static inline u32 irq_read(const struct rpivid_dev * const dev,
++                         const unsigned int offset)
++{
++      return readl(dev->base_irq + offset);
++}
++
++static inline void apb_write_vc_addr(const struct rpivid_dev * const dev,
++                                   const unsigned int offset,
++                                   const dma_addr_t a)
++{
++      apb_write(dev, offset, (u32)(a >> 6));
++}
++
++static inline void apb_write_vc_addr_final(const struct rpivid_dev * const dev,
++                                         const unsigned int offset,
++                                         const dma_addr_t a)
++{
++      apb_write_final(dev, offset, (u32)(a >> 6));
++}
++
++static inline void apb_write_vc_len(const struct rpivid_dev * const dev,
++                                  const unsigned int offset,
++                                  const unsigned int x)
++{
++      apb_write(dev, offset, (x + 63) >> 6);
++}
++
++/* *ARG_IC_ICTRL - Interrupt control for ARGON Core*
++ * Offset (byte space) = 40'h2b10000
++ * Physical Address (byte space) = 40'h7eb10000
++ * Verilog Macro Address = `ARG_IC_REG_START + `ARGON_INTCTRL_ICTRL
++ * Reset Value = 32'b100x100x_100xxxxx_xxxxxxx0_x100x100
++ * Access = RW (32-bit only)
++ * Interrupt control logic for ARGON Core.
++ */
++#define ARG_IC_ICTRL 0
++
++/* acc=LWC ACTIVE1_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 1
++ * This is set and held when an hevc_active1 interrupt edge is detected
++ * The polarity of the edge is set by the ACTIVE1_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * ACTIVE1_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE1_INT_SET          BIT(0)
++
++/* ACTIVE1_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the hevc_active1 line from the argon core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE1_EDGE_SET         BIT(1)
++
++/* ACTIVE1_EN Enables ACTIVE1_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ *
++ * [JC] The above appears to be a lie - if unset then b0 is never set
++ */
++#define ARG_IC_ICTRL_ACTIVE1_EN_SET           BIT(2)
++
++/* acc=RO ACTIVE1_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the hevc_active1 signal
++ */
++#define ARG_IC_ICTRL_ACTIVE1_STATUS_SET               BIT(3)
++
++/* acc=LWC ACTIVE2_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 2
++ * This is set and held when an hevc_active2 interrupt edge is detected
++ * The polarity of the edge is set by the ACTIVE2_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * ACTIVE2_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_INT_SET          BIT(4)
++
++/* ACTIVE2_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the hevc_active2 line from the argon core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_EDGE_SET         BIT(5)
++
++/* ACTIVE2_EN Enables ACTIVE2_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ */
++#define ARG_IC_ICTRL_ACTIVE2_EN_SET           BIT(6)
++
++/* acc=RO ACTIVE2_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the hevc_active2 signal
++ */
++#define ARG_IC_ICTRL_ACTIVE2_STATUS_SET               BIT(7)
++
++/* TEST_INT Forces the argon int high for test purposes.
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_TEST_INT                 BIT(8)
++#define ARG_IC_ICTRL_SPARE                    BIT(9)
++
++/* acc=RO VP9_INTERRUPT_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the vp9_interrupt signal
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_STATUS     BIT(10)
++
++/* AIO_INT_ENABLE 1 = Or the AIO int in with the Argon int so the VPU can see
++ * it
++ * 0 = the AIO int is masked. (It should still be connected to the GIC though).
++ */
++#define ARG_IC_ICTRL_AIO_INT_ENABLE           BIT(20)
++#define ARG_IC_ICTRL_H264_ACTIVE_INT          BIT(21)
++#define ARG_IC_ICTRL_H264_ACTIVE_EDGE         BIT(22)
++#define ARG_IC_ICTRL_H264_ACTIVE_EN           BIT(23)
++#define ARG_IC_ICTRL_H264_ACTIVE_STATUS               BIT(24)
++#define ARG_IC_ICTRL_H264_INTERRUPT_INT               BIT(25)
++#define ARG_IC_ICTRL_H264_INTERRUPT_EDGE      BIT(26)
++#define ARG_IC_ICTRL_H264_INTERRUPT_EN                BIT(27)
++
++/* acc=RO H264_INTERRUPT_STATUS FIELD ACCESS: RO
++ *
++ * The current status of the h264_interrupt signal
++ */
++#define ARG_IC_ICTRL_H264_INTERRUPT_STATUS    BIT(28)
++
++/* acc=LWC VP9_INTERRUPT_INT FIELD ACCESS: LWC
++ *
++ * Interrupt 1
++ * This is set and held when an vp9_interrupt interrupt edge is detected
++ * The polarity of the edge is set by the VP9_INTERRUPT_EDGE field
++ * Write a 1 to this bit to clear down the latched interrupt
++ * The latched interrupt is only enabled out onto the interrupt line if
++ * VP9_INTERRUPT_EN is set
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_INT                BIT(29)
++
++/* VP9_INTERRUPT_EDGE Sets the polarity of the interrupt edge detection logic
++ * This logic detects edges of the vp9_interrupt line from the argon h264 core
++ * 0 = negedge, 1 = posedge
++ * Reset value is *0* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_EDGE               BIT(30)
++
++/* VP9_INTERRUPT_EN Enables VP9_INTERRUPT_INT out onto the argon interrupt line.
++ * If this isn't set, the interrupt logic will work but no interrupt will be
++ * set to the interrupt controller
++ * Reset value is *1* decimal.
++ */
++#define ARG_IC_ICTRL_VP9_INTERRUPT_EN         BIT(31)
++
++/* Bits 19:12, 11 reserved - read ?, write 0 */
++#define ARG_IC_ICTRL_SET_ZERO_MASK            ((0xff << 12) | BIT(11))
++
++/* All IRQ bits */
++#define ARG_IC_ICTRL_ALL_IRQ_MASK   (\
++              ARG_IC_ICTRL_VP9_INTERRUPT_INT  |\
++              ARG_IC_ICTRL_H264_INTERRUPT_INT |\
++              ARG_IC_ICTRL_ACTIVE1_INT_SET    |\
++              ARG_IC_ICTRL_ACTIVE2_INT_SET)
++
++/* Auto release once all CBs called */
++void rpivid_hw_irq_active1_claim(struct rpivid_dev *dev,
++                               struct rpivid_hw_irq_ent *ient,
++                               rpivid_irq_callback ready_cb, void *ctx);
++/* May only be called in claim cb */
++void rpivid_hw_irq_active1_irq(struct rpivid_dev *dev,
++                             struct rpivid_hw_irq_ent *ient,
++                             rpivid_irq_callback irq_cb, void *ctx);
++/* May only be called in irq cb */
++void rpivid_hw_irq_active1_thread(struct rpivid_dev *dev,
++                                struct rpivid_hw_irq_ent *ient,
++                                rpivid_irq_callback thread_cb, void *ctx);
++
++/* Auto release once all CBs called */
++void rpivid_hw_irq_active2_claim(struct rpivid_dev *dev,
++                               struct rpivid_hw_irq_ent *ient,
++                               rpivid_irq_callback ready_cb, void *ctx);
++/* May only be called in claim cb */
++void rpivid_hw_irq_active2_irq(struct rpivid_dev *dev,
++                             struct rpivid_hw_irq_ent *ient,
++                             rpivid_irq_callback irq_cb, void *ctx);
++
++int rpivid_hw_probe(struct rpivid_dev *dev);
++void rpivid_hw_remove(struct rpivid_dev *dev);
++
++#endif
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_video.c
+@@ -0,0 +1,593 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#include <media/videobuf2-dma-contig.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-mem2mem.h>
++
++#include "rpivid.h"
++#include "rpivid_video.h"
++#include "rpivid_dec.h"
++
++#define RPIVID_DECODE_SRC     BIT(0)
++#define RPIVID_DECODE_DST     BIT(1)
++
++#define RPIVID_MIN_WIDTH      16U
++#define RPIVID_MIN_HEIGHT     16U
++#define RPIVID_MAX_WIDTH      4096U
++#define RPIVID_MAX_HEIGHT     4096U
++
++static inline struct rpivid_ctx *rpivid_file2ctx(struct file *file)
++{
++      return container_of(file->private_data, struct rpivid_ctx, fh);
++}
++
++/* constrain x to y,y*2 */
++static inline unsigned int constrain2x(unsigned int x, unsigned int y)
++{
++      return (x < y) ?
++                      y :
++                      (x > y * 2) ? y : x;
++}
++
++int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt)
++{
++      if (pix_fmt->pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
++              return -EINVAL;
++
++      /* Zero bytes per line for encoded source. */
++      pix_fmt->bytesperline = 0;
++      /* Choose some minimum size since this can't be 0 */
++      pix_fmt->sizeimage = max_t(u32, SZ_1K, pix_fmt->sizeimage);
++      pix_fmt->field = V4L2_FIELD_NONE;
++      return 0;
++}
++
++int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt)
++{
++      unsigned int width = pix_fmt->width;
++      unsigned int height = pix_fmt->height;
++      unsigned int sizeimage = pix_fmt->sizeimage;
++      unsigned int bytesperline = pix_fmt->bytesperline;
++
++      switch (pix_fmt->pixelformat) {
++      /* For column formats set bytesperline to column height (stride2) */
++      case V4L2_PIX_FMT_NV12_COL128:
++              /* Width rounds up to columns */
++              width = ALIGN(min(width, RPIVID_MAX_WIDTH), 128);
++
++              /* 16 aligned height - not sure we even need that */
++              height = ALIGN(height, 16);
++              /* column height
++               * Accept suggested shape if at least min & < 2 * min
++               */
++              bytesperline = constrain2x(bytesperline, height * 3 / 2);
++
++              /* image size
++               * Again allow plausible variation in case added padding is
++               * required
++               */
++              sizeimage = constrain2x(sizeimage, bytesperline * width);
++              break;
++
++      case V4L2_PIX_FMT_NV12_10_COL128:
++              /* width in pixels (3 pels = 4 bytes) rounded to 128 byte
++               * columns
++               */
++              width = ALIGN(((min(width, RPIVID_MAX_WIDTH) + 2) / 3), 32) * 3;
++
++              /* 16-aligned height. */
++              height = ALIGN(height, 16);
++
++              /* column height
++               * Accept suggested shape if at least min & < 2 * min
++               */
++              bytesperline = constrain2x(bytesperline, height * 3 / 2);
++
++              /* image size
++               * Again allow plausible variation in case added padding is
++               * required
++               */
++              sizeimage = constrain2x(sizeimage,
++                                      bytesperline * width * 4 / 3);
++              break;
++
++      default:
++              return -EINVAL;
++      }
++
++      pix_fmt->width = width;
++      pix_fmt->height = height;
++
++      pix_fmt->field = V4L2_FIELD_NONE;
++      pix_fmt->bytesperline = bytesperline;
++      pix_fmt->sizeimage = sizeimage;
++      return 0;
++}
++
++static int rpivid_querycap(struct file *file, void *priv,
++                         struct v4l2_capability *cap)
++{
++      strscpy(cap->driver, RPIVID_NAME, sizeof(cap->driver));
++      strscpy(cap->card, RPIVID_NAME, sizeof(cap->card));
++      snprintf(cap->bus_info, sizeof(cap->bus_info),
++               "platform:%s", RPIVID_NAME);
++
++      return 0;
++}
++
++static int rpivid_enum_fmt_vid_out(struct file *file, void *priv,
++                                 struct v4l2_fmtdesc *f)
++{
++      // Input formats
++
++      // H.265 Slice only currently
++      if (f->index == 0) {
++              f->pixelformat = V4L2_PIX_FMT_HEVC_SLICE;
++              return 0;
++      }
++
++      return -EINVAL;
++}
++
++static int rpivid_hevc_validate_sps(const struct v4l2_ctrl_hevc_sps * const sps)
++{
++      const unsigned int ctb_log2_size_y =
++                      sps->log2_min_luma_coding_block_size_minus3 + 3 +
++                      sps->log2_diff_max_min_luma_coding_block_size;
++      const unsigned int min_tb_log2_size_y =
++                      sps->log2_min_luma_transform_block_size_minus2 + 2;
++      const unsigned int max_tb_log2_size_y = min_tb_log2_size_y +
++                      sps->log2_diff_max_min_luma_transform_block_size;
++
++      /* Local limitations */
++      if (sps->pic_width_in_luma_samples < 32 ||
++          sps->pic_width_in_luma_samples > 4096)
++              return 0;
++      if (sps->pic_height_in_luma_samples < 32 ||
++          sps->pic_height_in_luma_samples > 4096)
++              return 0;
++      if (!(sps->bit_depth_luma_minus8 == 0 ||
++            sps->bit_depth_luma_minus8 == 2))
++              return 0;
++      if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
++              return 0;
++      if (sps->chroma_format_idc != 1)
++              return 0;
++
++      /*  Limits from H.265 7.4.3.2.1 */
++      if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12)
++              return 0;
++      if (sps->sps_max_dec_pic_buffering_minus1 > 15)
++              return 0;
++      if (sps->sps_max_num_reorder_pics >
++                              sps->sps_max_dec_pic_buffering_minus1)
++              return 0;
++      if (ctb_log2_size_y > 6)
++              return 0;
++      if (max_tb_log2_size_y > 5)
++              return 0;
++      if (max_tb_log2_size_y > ctb_log2_size_y)
++              return 0;
++      if (sps->max_transform_hierarchy_depth_inter >
++                              (ctb_log2_size_y - min_tb_log2_size_y))
++              return 0;
++      if (sps->max_transform_hierarchy_depth_intra >
++                              (ctb_log2_size_y - min_tb_log2_size_y))
++              return 0;
++      /* Check pcm stuff */
++      if (sps->num_short_term_ref_pic_sets > 64)
++              return 0;
++      if (sps->num_long_term_ref_pics_sps > 32)
++              return 0;
++      return 1;
++}
++
++static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps)
++{
++      return sps && sps->pic_width_in_luma_samples != 0;
++}
++
++static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps,
++                              const int index)
++{
++      u32 pf = 0;
++
++      // Use width 0 as a signifier of unsetness
++      if (!is_sps_set(sps)) {
++              /* Treat this as an error? For now return both */
++              if (index == 0)
++                      pf = V4L2_PIX_FMT_NV12_COL128;
++              else if (index == 1)
++                      pf = V4L2_PIX_FMT_NV12_10_COL128;
++      } else if (index == 0 && rpivid_hevc_validate_sps(sps)) {
++              if (sps->bit_depth_luma_minus8 == 0)
++                      pf = V4L2_PIX_FMT_NV12_COL128;
++              else if (sps->bit_depth_luma_minus8 == 2)
++                      pf = V4L2_PIX_FMT_NV12_10_COL128;
++      }
++
++      return pf;
++}
++
++static struct v4l2_pix_format
++rpivid_hevc_default_dst_fmt(struct rpivid_ctx * const ctx)
++{
++      const struct v4l2_ctrl_hevc_sps * const sps =
++              rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++      struct v4l2_pix_format pix_fmt = {
++              .width = sps->pic_width_in_luma_samples,
++              .height = sps->pic_height_in_luma_samples,
++              .pixelformat = pixelformat_from_sps(sps, 0)
++      };
++
++      rpivid_prepare_dst_format(&pix_fmt);
++      return pix_fmt;
++}
++
++static u32 rpivid_hevc_get_dst_pixelformat(struct rpivid_ctx * const ctx,
++                                         const int index)
++{
++      const struct v4l2_ctrl_hevc_sps * const sps =
++              rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++
++      return pixelformat_from_sps(sps, index);
++}
++
++static int rpivid_enum_fmt_vid_cap(struct file *file, void *priv,
++                                 struct v4l2_fmtdesc *f)
++{
++      struct rpivid_ctx * const ctx = rpivid_file2ctx(file);
++
++      const u32 pf = rpivid_hevc_get_dst_pixelformat(ctx, f->index);
++
++      if (pf == 0)
++              return -EINVAL;
++
++      f->pixelformat = pf;
++      return 0;
++}
++
++static int rpivid_g_fmt_vid_cap(struct file *file, void *priv,
++                              struct v4l2_format *f)
++{
++      struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++
++      if (!ctx->dst_fmt_set)
++              ctx->dst_fmt = rpivid_hevc_default_dst_fmt(ctx);
++      f->fmt.pix = ctx->dst_fmt;
++      return 0;
++}
++
++static int rpivid_g_fmt_vid_out(struct file *file, void *priv,
++                              struct v4l2_format *f)
++{
++      struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++
++      f->fmt.pix = ctx->src_fmt;
++      return 0;
++}
++
++static inline void copy_color(struct v4l2_pix_format *d,
++                            const struct v4l2_pix_format *s)
++{
++      d->colorspace   = s->colorspace;
++      d->xfer_func    = s->xfer_func;
++      d->ycbcr_enc    = s->ycbcr_enc;
++      d->quantization = s->quantization;
++}
++
++static int rpivid_try_fmt_vid_cap(struct file *file, void *priv,
++                                struct v4l2_format *f)
++{
++      struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++      const struct v4l2_ctrl_hevc_sps * const sps =
++              rpivid_find_control_data(ctx, V4L2_CID_MPEG_VIDEO_HEVC_SPS);
++      u32 pixelformat;
++      int i;
++
++      /* Reject format types we don't support */
++      if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++              return -EINVAL;
++
++      for (i = 0; (pixelformat = pixelformat_from_sps(sps, i)) != 0; i++) {
++              if (f->fmt.pix.pixelformat == pixelformat)
++                      break;
++      }
++
++      // If we can't use requested fmt then set to default
++      if (pixelformat == 0) {
++              pixelformat = pixelformat_from_sps(sps, 0);
++              // If we don't have a default then give up
++              if (pixelformat == 0)
++                      return -EINVAL;
++      }
++
++      // We don't have any way of finding out colourspace so believe
++      // anything we are told - take anything set in src as a default
++      if (f->fmt.pix.colorspace == V4L2_COLORSPACE_DEFAULT)
++              copy_color(&f->fmt.pix, &ctx->src_fmt);
++
++      f->fmt.pix.pixelformat = pixelformat;
++      return rpivid_prepare_dst_format(&f->fmt.pix);
++}
++
++static int rpivid_try_fmt_vid_out(struct file *file, void *priv,
++                                struct v4l2_format *f)
++{
++      if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
++              return -EINVAL;
++
++      if (rpivid_prepare_src_format(&f->fmt.pix)) {
++              // Set default src format
++              f->fmt.pix.pixelformat = RPIVID_SRC_PIXELFORMAT_DEFAULT;
++              rpivid_prepare_src_format(&f->fmt.pix);
++      }
++      return 0;
++}
++
++static int rpivid_s_fmt_vid_cap(struct file *file, void *priv,
++                              struct v4l2_format *f)
++{
++      struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++      struct vb2_queue *vq;
++      int ret;
++
++      vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
++      if (vb2_is_busy(vq))
++              return -EBUSY;
++
++      ret = rpivid_try_fmt_vid_cap(file, priv, f);
++      if (ret)
++              return ret;
++
++      ctx->dst_fmt = f->fmt.pix;
++      ctx->dst_fmt_set = 1;
++
++      return 0;
++}
++
++static int rpivid_s_fmt_vid_out(struct file *file, void *priv,
++                              struct v4l2_format *f)
++{
++      struct rpivid_ctx *ctx = rpivid_file2ctx(file);
++      struct vb2_queue *vq;
++      int ret;
++
++      vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
++      if (vb2_is_busy(vq))
++              return -EBUSY;
++
++      ret = rpivid_try_fmt_vid_out(file, priv, f);
++      if (ret)
++              return ret;
++
++      ctx->src_fmt = f->fmt.pix;
++      ctx->dst_fmt_set = 0;  // Setting src invalidates dst
++
++      vq->subsystem_flags |=
++              VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF;
++
++      /* Propagate colorspace information to capture. */
++      copy_color(&ctx->dst_fmt, &f->fmt.pix);
++      return 0;
++}
++
++const struct v4l2_ioctl_ops rpivid_ioctl_ops = {
++      .vidioc_querycap                = rpivid_querycap,
++
++      .vidioc_enum_fmt_vid_cap        = rpivid_enum_fmt_vid_cap,
++      .vidioc_g_fmt_vid_cap           = rpivid_g_fmt_vid_cap,
++      .vidioc_try_fmt_vid_cap         = rpivid_try_fmt_vid_cap,
++      .vidioc_s_fmt_vid_cap           = rpivid_s_fmt_vid_cap,
++
++      .vidioc_enum_fmt_vid_out        = rpivid_enum_fmt_vid_out,
++      .vidioc_g_fmt_vid_out           = rpivid_g_fmt_vid_out,
++      .vidioc_try_fmt_vid_out         = rpivid_try_fmt_vid_out,
++      .vidioc_s_fmt_vid_out           = rpivid_s_fmt_vid_out,
++
++      .vidioc_reqbufs                 = v4l2_m2m_ioctl_reqbufs,
++      .vidioc_querybuf                = v4l2_m2m_ioctl_querybuf,
++      .vidioc_qbuf                    = v4l2_m2m_ioctl_qbuf,
++      .vidioc_dqbuf                   = v4l2_m2m_ioctl_dqbuf,
++      .vidioc_prepare_buf             = v4l2_m2m_ioctl_prepare_buf,
++      .vidioc_create_bufs             = v4l2_m2m_ioctl_create_bufs,
++      .vidioc_expbuf                  = v4l2_m2m_ioctl_expbuf,
++
++      .vidioc_streamon                = v4l2_m2m_ioctl_streamon,
++      .vidioc_streamoff               = v4l2_m2m_ioctl_streamoff,
++
++      .vidioc_try_decoder_cmd         = v4l2_m2m_ioctl_stateless_try_decoder_cmd,
++      .vidioc_decoder_cmd             = v4l2_m2m_ioctl_stateless_decoder_cmd,
++
++      .vidioc_subscribe_event         = v4l2_ctrl_subscribe_event,
++      .vidioc_unsubscribe_event       = v4l2_event_unsubscribe,
++};
++
++static int rpivid_queue_setup(struct vb2_queue *vq, unsigned int *nbufs,
++                            unsigned int *nplanes, unsigned int sizes[],
++                            struct device *alloc_devs[])
++{
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++      struct v4l2_pix_format *pix_fmt;
++
++      if (V4L2_TYPE_IS_OUTPUT(vq->type))
++              pix_fmt = &ctx->src_fmt;
++      else
++              pix_fmt = &ctx->dst_fmt;
++
++      if (*nplanes) {
++              if (sizes[0] < pix_fmt->sizeimage)
++                      return -EINVAL;
++      } else {
++              sizes[0] = pix_fmt->sizeimage;
++              *nplanes = 1;
++      }
++
++      return 0;
++}
++
++static void rpivid_queue_cleanup(struct vb2_queue *vq, u32 state)
++{
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++      struct vb2_v4l2_buffer *vbuf;
++
++      for (;;) {
++              if (V4L2_TYPE_IS_OUTPUT(vq->type))
++                      vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
++              else
++                      vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
++
++              if (!vbuf)
++                      return;
++
++              v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
++                                         &ctx->hdl);
++              v4l2_m2m_buf_done(vbuf, state);
++      }
++}
++
++static int rpivid_buf_out_validate(struct vb2_buffer *vb)
++{
++      struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++
++      vbuf->field = V4L2_FIELD_NONE;
++      return 0;
++}
++
++static int rpivid_buf_prepare(struct vb2_buffer *vb)
++{
++      struct vb2_queue *vq = vb->vb2_queue;
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++      struct v4l2_pix_format *pix_fmt;
++
++      if (V4L2_TYPE_IS_OUTPUT(vq->type))
++              pix_fmt = &ctx->src_fmt;
++      else
++              pix_fmt = &ctx->dst_fmt;
++
++      if (vb2_plane_size(vb, 0) < pix_fmt->sizeimage)
++              return -EINVAL;
++
++      vb2_set_plane_payload(vb, 0, pix_fmt->sizeimage);
++
++      return 0;
++}
++
++static int rpivid_start_streaming(struct vb2_queue *vq, unsigned int count)
++{
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++      struct rpivid_dev *dev = ctx->dev;
++      int ret = 0;
++
++      if (ctx->src_fmt.pixelformat != V4L2_PIX_FMT_HEVC_SLICE)
++              return -EINVAL;
++
++      if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->start)
++              ret = dev->dec_ops->start(ctx);
++
++      ret = clk_set_rate(dev->clock, 500 * 1000 * 1000);
++      if (ret) {
++              dev_err(dev->dev, "Failed to set clock rate\n");
++              goto out;
++      }
++
++      ret = clk_prepare_enable(dev->clock);
++      if (ret)
++              dev_err(dev->dev, "Failed to enable clock\n");
++
++out:
++      if (ret)
++              rpivid_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
++
++      return ret;
++}
++
++static void rpivid_stop_streaming(struct vb2_queue *vq)
++{
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vq);
++      struct rpivid_dev *dev = ctx->dev;
++
++      if (V4L2_TYPE_IS_OUTPUT(vq->type) && dev->dec_ops->stop)
++              dev->dec_ops->stop(ctx);
++
++      rpivid_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
++
++      clk_disable_unprepare(dev->clock);
++}
++
++static void rpivid_buf_queue(struct vb2_buffer *vb)
++{
++      struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++      v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
++}
++
++static void rpivid_buf_request_complete(struct vb2_buffer *vb)
++{
++      struct rpivid_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
++
++      v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
++}
++
++static struct vb2_ops rpivid_qops = {
++      .queue_setup            = rpivid_queue_setup,
++      .buf_prepare            = rpivid_buf_prepare,
++      .buf_queue              = rpivid_buf_queue,
++      .buf_out_validate       = rpivid_buf_out_validate,
++      .buf_request_complete   = rpivid_buf_request_complete,
++      .start_streaming        = rpivid_start_streaming,
++      .stop_streaming         = rpivid_stop_streaming,
++      .wait_prepare           = vb2_ops_wait_prepare,
++      .wait_finish            = vb2_ops_wait_finish,
++};
++
++int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
++                    struct vb2_queue *dst_vq)
++{
++      struct rpivid_ctx *ctx = priv;
++      int ret;
++
++      src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++      src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++      src_vq->drv_priv = ctx;
++      src_vq->buf_struct_size = sizeof(struct rpivid_buffer);
++      src_vq->min_buffers_needed = 1;
++      src_vq->ops = &rpivid_qops;
++      src_vq->mem_ops = &vb2_dma_contig_memops;
++      src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++      src_vq->lock = &ctx->dev->dev_mutex;
++      src_vq->dev = ctx->dev->dev;
++      src_vq->supports_requests = true;
++      src_vq->requires_requests = true;
++
++      ret = vb2_queue_init(src_vq);
++      if (ret)
++              return ret;
++
++      dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++      dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
++      dst_vq->drv_priv = ctx;
++      dst_vq->buf_struct_size = sizeof(struct rpivid_buffer);
++      dst_vq->min_buffers_needed = 1;
++      dst_vq->ops = &rpivid_qops;
++      dst_vq->mem_ops = &vb2_dma_contig_memops;
++      dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
++      dst_vq->lock = &ctx->dev->dev_mutex;
++      dst_vq->dev = ctx->dev->dev;
++
++      return vb2_queue_init(dst_vq);
++}
+--- /dev/null
++++ b/drivers/staging/media/rpivid/rpivid_video.h
+@@ -0,0 +1,30 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Raspberry Pi HEVC driver
++ *
++ * Copyright (C) 2020 Raspberry Pi (Trading) Ltd
++ *
++ * Based on the Cedrus VPU driver, that is:
++ *
++ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
++ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
++ * Copyright (C) 2018 Bootlin
++ */
++
++#ifndef _RPIVID_VIDEO_H_
++#define _RPIVID_VIDEO_H_
++
++struct rpivid_format {
++      u32             pixelformat;
++      u32             directions;
++      unsigned int    capabilities;
++};
++
++extern const struct v4l2_ioctl_ops rpivid_ioctl_ops;
++
++int rpivid_queue_init(void *priv, struct vb2_queue *src_vq,
++                    struct vb2_queue *dst_vq);
++int rpivid_prepare_src_format(struct v4l2_pix_format *pix_fmt);
++int rpivid_prepare_dst_format(struct v4l2_pix_format *pix_fmt);
++
++#endif
diff --git a/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch b/target/linux/bcm27xx/patches-5.4/950-0514-dtoverlays-Add-overlay-to-enable-the-HEVC-V4L2-drive.patch
new file mode 100644 (file)
index 0000000..ee92ada
--- /dev/null
@@ -0,0 +1,102 @@
+From b1d6499e00b6061ecc7061335199acf86f54d31a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 13 Mar 2020 16:52:55 +0000
+Subject: [PATCH] dtoverlays: Add overlay to enable the HEVC V4L2
+ driver
+
+This replaces the rpivid_mem register mapping driver.
+When the driver is complete, these DT changes should be
+merged into the base DT instead of being an overlay.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile           |  1 +
+ arch/arm/boot/dts/overlays/README             |  7 +++
+ .../boot/dts/overlays/rpivid-v4l2-overlay.dts | 55 +++++++++++++++++++
+ 4 files changed, 63 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -140,6 +140,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+       rpi-proto.dtbo \
+       rpi-sense.dtbo \
+       rpi-tv.dtbo \
++      rpivid-v4l2.dtbo \
+       rra-digidac1-wm8741-audio.dtbo \
+       sc16is750-i2c.dtbo \
+       sc16is752-i2c.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2064,6 +2064,13 @@ Load:   dtoverlay=rpi-tv
+ Params: <None>
++Name:   rpivid-v4l2
++Info:   Load the V4L2 stateless video decoder driver for the HEVC block,
++        disabling the memory mapped devices in the process.
++Load:   dtoverlay=rpivid-v4l2
++Params: <None>
++
++
+ Name:   rra-digidac1-wm8741-audio
+ Info:   Configures the Red Rocks Audio DigiDAC1 soundcard
+ Load:   dtoverlay=rra-digidac1-wm8741-audio
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/rpivid-v4l2-overlay.dts
+@@ -0,0 +1,55 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for Raspberry Pi video decode engine
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++
++/{
++      compatible = "brcm,bcm2711";
++
++      fragment@0 {
++              target = <&scb>;
++              __overlay__ {
++                      /* needed to avoid dtc warning */
++                      #address-cells = <2>;
++                      #size-cells = <1>;
++                      codec@7eb10000 {
++                              compatible = "raspberrypi,rpivid-vid-decoder";
++                              reg = <0x0 0x7eb10000 0x1000>,  /* INTC */
++                                    <0x0 0x7eb00000 0x10000>; /* HEVC */
++                              reg-names = "intc",
++                                          "hevc";
++
++                              interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++                              clocks = <&hevc_clk>;
++                              clock-names = "hevc";
++
++                              hevc_clk: hevc_clk {
++                                      compatible = "fixed-clock";
++                                      #clock-cells = <0>;
++                                      clock-frequency = <500000000>;
++                              };
++                      };
++              };
++      };
++
++      fragment@1 {
++              target = <&scb>;
++              __overlay__ {
++                      hevc-decoder@7eb00000 {
++                              status = "disabled";
++                      };
++                      rpivid-local-intc@7eb10000 {
++                              status = "disabled";
++                      };
++                      h264-decoder@7eb20000 {
++                              status = "disabled";
++                      };
++                      vp9-decoder@7eb30000 {
++                              status = "disabled";
++                      };
++              };
++      };
++};