From 612505170bcf7024baa93f13fa6da52e4ca4e26f Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Sat, 15 Oct 2022 17:05:54 +0200 Subject: [PATCH 1/2] Allwinner: linux: workaround EDID reading failure --- projects/Allwinner/linux/linux.aarch64.conf | 2 +- ...-dw-hdmi-Fix-ddc-en-GPIO-consumer-co.patch | 131 ++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 projects/Allwinner/patches/linux/0075-Revert-drm-sun4i-dw-hdmi-Fix-ddc-en-GPIO-consumer-co.patch diff --git a/projects/Allwinner/linux/linux.aarch64.conf b/projects/Allwinner/linux/linux.aarch64.conf index aa22e26609..d7a4b5696d 100644 --- a/projects/Allwinner/linux/linux.aarch64.conf +++ b/projects/Allwinner/linux/linux.aarch64.conf @@ -4168,7 +4168,7 @@ CONFIG_DRM_PANEL_BRIDGE=y # CONFIG_DRM_CDNS_DSI is not set # CONFIG_DRM_CHIPONE_ICN6211 is not set # CONFIG_DRM_CHRONTEL_CH7033 is not set -CONFIG_DRM_DISPLAY_CONNECTOR=y +# CONFIG_DRM_DISPLAY_CONNECTOR is not set # CONFIG_DRM_ITE_IT6505 is not set # CONFIG_DRM_LONTIUM_LT8912B is not set # CONFIG_DRM_LONTIUM_LT9211 is not set diff --git a/projects/Allwinner/patches/linux/0075-Revert-drm-sun4i-dw-hdmi-Fix-ddc-en-GPIO-consumer-co.patch b/projects/Allwinner/patches/linux/0075-Revert-drm-sun4i-dw-hdmi-Fix-ddc-en-GPIO-consumer-co.patch new file mode 100644 index 0000000000..00cb2d4f14 --- /dev/null +++ b/projects/Allwinner/patches/linux/0075-Revert-drm-sun4i-dw-hdmi-Fix-ddc-en-GPIO-consumer-co.patch @@ -0,0 +1,131 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 14 Oct 2022 23:35:13 +0200 +Subject: [PATCH] Revert "drm/sun4i: dw-hdmi: Fix ddc-en GPIO consumer + conflict" + +This reverts commit 920169041baa0a7497ed702aa97d6a2d6285efd3. +--- + drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c | 54 +++++++++++++++++++++++++-- + drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h | 2 + + 2 files changed, 52 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +index 477cb6985b4d..a8d75fd7e9f4 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c ++++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +@@ -93,10 +93,34 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm, + return crtcs; + } + ++static int sun8i_dw_hdmi_find_connector_pdev(struct device *dev, ++ struct platform_device **pdev_out) ++{ ++ struct platform_device *pdev; ++ struct device_node *remote; ++ ++ remote = of_graph_get_remote_node(dev->of_node, 1, -1); ++ if (!remote) ++ return -ENODEV; ++ ++ if (!of_device_is_compatible(remote, "hdmi-connector")) { ++ of_node_put(remote); ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(remote); ++ of_node_put(remote); ++ if (!pdev) ++ return -ENODEV; ++ ++ *pdev_out = pdev; ++ return 0; ++} ++ + static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, + void *data) + { +- struct platform_device *pdev = to_platform_device(dev); ++ struct platform_device *pdev = to_platform_device(dev), *connector_pdev; + struct dw_hdmi_plat_data *plat_data; + struct drm_device *drm = data; + struct device_node *phy_node; +@@ -143,16 +167,30 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, + return dev_err_probe(dev, PTR_ERR(hdmi->regulator), + "Couldn't get regulator\n"); + ++ ret = sun8i_dw_hdmi_find_connector_pdev(dev, &connector_pdev); ++ if (!ret) { ++ hdmi->ddc_en = gpiod_get_optional(&connector_pdev->dev, ++ "ddc-en", GPIOD_OUT_HIGH); ++ platform_device_put(connector_pdev); ++ ++ if (IS_ERR(hdmi->ddc_en)) { ++ dev_err(dev, "Couldn't get ddc-en gpio\n"); ++ return PTR_ERR(hdmi->ddc_en); ++ } ++ } ++ + ret = regulator_enable(hdmi->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); +- return ret; ++ goto err_unref_ddc_en; + } + ++ gpiod_set_value(hdmi->ddc_en, 1); ++ + ret = reset_control_deassert(hdmi->rst_ctrl); + if (ret) { + dev_err(dev, "Could not deassert ctrl reset control\n"); +- goto err_disable_regulator; ++ goto err_disable_ddc_en; + } + + ret = clk_prepare_enable(hdmi->clk_tmds); +@@ -207,8 +245,12 @@ static int sun8i_dw_hdmi_bind(struct device *dev, struct device *master, + clk_disable_unprepare(hdmi->clk_tmds); + err_assert_ctrl_reset: + reset_control_assert(hdmi->rst_ctrl); +-err_disable_regulator: ++err_disable_ddc_en: ++ gpiod_set_value(hdmi->ddc_en, 0); + regulator_disable(hdmi->regulator); ++err_unref_ddc_en: ++ if (hdmi->ddc_en) ++ gpiod_put(hdmi->ddc_en); + + return ret; + } +@@ -222,7 +264,11 @@ static void sun8i_dw_hdmi_unbind(struct device *dev, struct device *master, + sun8i_hdmi_phy_deinit(hdmi->phy); + clk_disable_unprepare(hdmi->clk_tmds); + reset_control_assert(hdmi->rst_ctrl); ++ gpiod_set_value(hdmi->ddc_en, 0); + regulator_disable(hdmi->regulator); ++ ++ if (hdmi->ddc_en) ++ gpiod_put(hdmi->ddc_en); + } + + static const struct component_ops sun8i_dw_hdmi_ops = { +diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +index ab80d52a70bb..f082b8ecfe2c 100644 +--- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h ++++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.h +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -187,6 +188,7 @@ struct sun8i_dw_hdmi { + struct regulator *regulator; + const struct sun8i_dw_hdmi_quirks *quirks; + struct reset_control *rst_ctrl; ++ struct gpio_desc *ddc_en; + }; + + extern struct platform_driver sun8i_hdmi_phy_driver; From 52779b256d9806a4c845c21e9d3539241250d338 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Sat, 15 Oct 2022 17:06:33 +0200 Subject: [PATCH 2/2] Allwinner: H6: linux: Enable iommu --- projects/Allwinner/devices/H6/options | 2 +- projects/Allwinner/linux/linux.aarch64.conf | 2 +- .../linux/0030-wip-H6-deinterlace.patch | 5 +- .../linux/0031-arm64-dts-h6-deinterlace.patch | 5 +- ...-allwinner-h6-Fix-Cedrus-IOMMU-again.patch | 26 ++++ ...inner-h6-Add-Hantro-G2-IOMMU-phandle.patch | 24 ++++ .../0068-iommu-sun50i-Fix-reset-release.patch | 44 ++++++ ...Consider-all-fault-sources-for-reset.patch | 50 +++++++ ...ommu-sun50i-Fix-R-W-permission-check.patch | 33 +++++ .../0071-iommu-sun50i-Fix-flush-size.patch | 27 ++++ ...mmu-sun50i-Implement-.iotlb_sync_map.patch | 133 ++++++++++++++++++ ...0i-Allow-page-sizes-multiple-of-4096.patch | 107 ++++++++++++++ ...bCr-YUV-terms-in-format-descriptions.patch | 61 ++++++++ 13 files changed, 514 insertions(+), 5 deletions(-) create mode 100644 projects/Allwinner/patches/linux/0066-arm64-dts-allwinner-h6-Fix-Cedrus-IOMMU-again.patch create mode 100644 projects/Allwinner/patches/linux/0067-arm64-dts-allwinner-h6-Add-Hantro-G2-IOMMU-phandle.patch create mode 100644 projects/Allwinner/patches/linux/0068-iommu-sun50i-Fix-reset-release.patch create mode 100644 projects/Allwinner/patches/linux/0069-iommu-sun50i-Consider-all-fault-sources-for-reset.patch create mode 100644 projects/Allwinner/patches/linux/0070-iommu-sun50i-Fix-R-W-permission-check.patch create mode 100644 projects/Allwinner/patches/linux/0071-iommu-sun50i-Fix-flush-size.patch create mode 100644 projects/Allwinner/patches/linux/0072-iommu-sun50i-Implement-.iotlb_sync_map.patch create mode 100644 projects/Allwinner/patches/linux/0073-iommu-sun50i-Allow-page-sizes-multiple-of-4096.patch create mode 100644 projects/Allwinner/patches/linux/0074-media-Unify-YCbCr-YUV-terms-in-format-descriptions.patch diff --git a/projects/Allwinner/devices/H6/options b/projects/Allwinner/devices/H6/options index e88c52b12e..aae179ebfe 100644 --- a/projects/Allwinner/devices/H6/options +++ b/projects/Allwinner/devices/H6/options @@ -37,4 +37,4 @@ ADDON_PROJECT="ARMv8" # additional kernel parameters - EXTRA_CMDLINE="$EXTRA_CMDLINE deferred_probe_timeout=0" + EXTRA_CMDLINE="$EXTRA_CMDLINE" diff --git a/projects/Allwinner/linux/linux.aarch64.conf b/projects/Allwinner/linux/linux.aarch64.conf index d7a4b5696d..2f54add7f0 100644 --- a/projects/Allwinner/linux/linux.aarch64.conf +++ b/projects/Allwinner/linux/linux.aarch64.conf @@ -5474,7 +5474,7 @@ CONFIG_IOMMU_DEFAULT_DMA_STRICT=y # CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set CONFIG_OF_IOMMU=y CONFIG_IOMMU_DMA=y -# CONFIG_SUN50I_IOMMU is not set +CONFIG_SUN50I_IOMMU=y # CONFIG_ARM_SMMU is not set # CONFIG_ARM_SMMU_V3 is not set diff --git a/projects/Allwinner/patches/linux/0030-wip-H6-deinterlace.patch b/projects/Allwinner/patches/linux/0030-wip-H6-deinterlace.patch index 687201e306..3ddbe7d863 100644 --- a/projects/Allwinner/patches/linux/0030-wip-H6-deinterlace.patch +++ b/projects/Allwinner/patches/linux/0030-wip-H6-deinterlace.patch @@ -47,7 +47,7 @@ Signed-off-by: Jernej Skrabec +obj-$(CONFIG_VIDEO_SUN50I_DEINTERLACE) += sun50i-di.o --- /dev/null +++ b/drivers/media/platform/sunxi/sun50i-di/sun50i-di.c -@@ -0,0 +1,1134 @@ +@@ -0,0 +1,1137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner sun50i deinterlacer driver @@ -137,6 +137,9 @@ Signed-off-by: Jernej Skrabec + deinterlace_write(dev, DEINTERLACE_OUT_FLAG_ADDR, ctx->flag2_buf_dma); + deinterlace_write(dev, DEINTERLACE_FLAG_ADDRH, 0); + deinterlace_write(dev, DEINTERLACE_FLAG_PITCH, 0x200); ++ if (device_iommu_mapped(dev->dev)) ++ deinterlace_set_bits(dev, DEINTERLACE_CTRL, ++ DEINTERLACE_CTRL_IOMMU_EN); + + width = ctx->src_fmt.width; + height = ctx->src_fmt.height; diff --git a/projects/Allwinner/patches/linux/0031-arm64-dts-h6-deinterlace.patch b/projects/Allwinner/patches/linux/0031-arm64-dts-h6-deinterlace.patch index 5d18560bb7..cfc7495b21 100644 --- a/projects/Allwinner/patches/linux/0031-arm64-dts-h6-deinterlace.patch +++ b/projects/Allwinner/patches/linux/0031-arm64-dts-h6-deinterlace.patch @@ -10,10 +10,10 @@ Signed-off-by: Jernej Skrabec --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi -@@ -160,6 +160,17 @@ +@@ -160,6 +160,18 @@ }; }; - + + deinterlace: deinterlace@1420000 { + compatible = "allwinner,sun50i-h6-deinterlace"; + reg = <0x01420000 0x2000>; @@ -23,6 +23,7 @@ Signed-off-by: Jernej Skrabec + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_DEINTERLACE>; + interrupts = ; ++ iommus = <&iommu 2>; + }; + video-codec@1c0e000 { diff --git a/projects/Allwinner/patches/linux/0066-arm64-dts-allwinner-h6-Fix-Cedrus-IOMMU-again.patch b/projects/Allwinner/patches/linux/0066-arm64-dts-allwinner-h6-Fix-Cedrus-IOMMU-again.patch new file mode 100644 index 0000000000..ec2153f81e --- /dev/null +++ b/projects/Allwinner/patches/linux/0066-arm64-dts-allwinner-h6-Fix-Cedrus-IOMMU-again.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 23:01:04 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: Fix Cedrus IOMMU, again + +Cedrus actually uses two IOMMU channels. Add the second one. + +Fixes: 62a8ccf3a248 ("arm64: dts: allwinner: h6: Fix Cedrus IOMMU usage") +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index e897559d9a89..436cc2a02d1a 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -172,7 +172,7 @@ video-codec@1c0e000 { + resets = <&ccu RST_BUS_VE>; + interrupts = ; + allwinner,sram = <&ve_sram 1>; +- iommus = <&iommu 3>; ++ iommus = <&iommu 1>, <&iommu 3>; + }; + + gpu: gpu@1800000 { diff --git a/projects/Allwinner/patches/linux/0067-arm64-dts-allwinner-h6-Add-Hantro-G2-IOMMU-phandle.patch b/projects/Allwinner/patches/linux/0067-arm64-dts-allwinner-h6-Add-Hantro-G2-IOMMU-phandle.patch new file mode 100644 index 0000000000..0486641fcd --- /dev/null +++ b/projects/Allwinner/patches/linux/0067-arm64-dts-allwinner-h6-Add-Hantro-G2-IOMMU-phandle.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 22:59:29 +0200 +Subject: [PATCH] arm64: dts: allwinner: h6: Add Hantro G2 IOMMU phandle + +Hantro G2 is connected to IOMMU. Add a phandle to it. + +Signed-off-by: Jernej Skrabec +--- + arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +index 436cc2a02d1a..d189ce4ebe37 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +@@ -161,6 +161,7 @@ video-codec-g2@1c00000 { + clocks = <&ccu CLK_BUS_VP9>, <&ccu CLK_VP9>; + clock-names = "bus", "mod"; + resets = <&ccu RST_BUS_VP9>; ++ iommus = <&iommu 5>; + }; + + video-codec@1c0e000 { diff --git a/projects/Allwinner/patches/linux/0068-iommu-sun50i-Fix-reset-release.patch b/projects/Allwinner/patches/linux/0068-iommu-sun50i-Fix-reset-release.patch new file mode 100644 index 0000000000..09b8cc17a1 --- /dev/null +++ b/projects/Allwinner/patches/linux/0068-iommu-sun50i-Fix-reset-release.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 21:10:47 +0200 +Subject: [PATCH] iommu/sun50i: Fix reset release + +Reset signal is asserted by writing 0 to the corresponding locations of +masters we want to reset. So in order to deassert all reset signals, we +should write 1's to all locations. + +Current code writes 1's to locations of masters which were just reset +which is good. However, at the same time it also writes 0's to other +locations and thus asserts reset signals of remaining masters. Fix code +by writing all 1's when we want to deassert all reset signals. + +This bug was discovered when working with Cedrus (video decoder). When +it faulted, display went blank due to reset signal assertion. + +Fixes: 4100b8c229b3 ("iommu: Add Allwinner H6 IOMMU driver") +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index a84c63518773..c777882d0ec2 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -27,6 +27,7 @@ + #include + + #define IOMMU_RESET_REG 0x010 ++#define IOMMU_RESET_RELEASE_ALL 0xffffffff + #define IOMMU_ENABLE_REG 0x020 + #define IOMMU_ENABLE_ENABLE BIT(0) + +@@ -893,7 +894,7 @@ static irqreturn_t sun50i_iommu_irq(int irq, void *dev_id) + iommu_write(iommu, IOMMU_INT_CLR_REG, status); + + iommu_write(iommu, IOMMU_RESET_REG, ~status); +- iommu_write(iommu, IOMMU_RESET_REG, status); ++ iommu_write(iommu, IOMMU_RESET_REG, IOMMU_RESET_RELEASE_ALL); + + spin_unlock(&iommu->iommu_lock); + diff --git a/projects/Allwinner/patches/linux/0069-iommu-sun50i-Consider-all-fault-sources-for-reset.patch b/projects/Allwinner/patches/linux/0069-iommu-sun50i-Consider-all-fault-sources-for-reset.patch new file mode 100644 index 0000000000..a5b1f20315 --- /dev/null +++ b/projects/Allwinner/patches/linux/0069-iommu-sun50i-Consider-all-fault-sources-for-reset.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 22:46:32 +0200 +Subject: [PATCH] iommu/sun50i: Consider all fault sources for reset + +We have to reset masters for all faults - permissions, L1 fault or L2 +fault. Currently it's done only for permissions. If other type of fault +happens, master is in locked up state. Fix that by really considering +all fault sources. + +Fixes: 4100b8c229b3 ("iommu: Add Allwinner H6 IOMMU driver") +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index c777882d0ec2..38d1069cf383 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -869,8 +869,8 @@ static phys_addr_t sun50i_iommu_handle_perm_irq(struct sun50i_iommu *iommu) + + static irqreturn_t sun50i_iommu_irq(int irq, void *dev_id) + { ++ u32 status, l1_status, l2_status, resets; + struct sun50i_iommu *iommu = dev_id; +- u32 status; + + spin_lock(&iommu->iommu_lock); + +@@ -880,6 +880,9 @@ static irqreturn_t sun50i_iommu_irq(int irq, void *dev_id) + return IRQ_NONE; + } + ++ l1_status = iommu_read(iommu, IOMMU_L1PG_INT_REG); ++ l2_status = iommu_read(iommu, IOMMU_L2PG_INT_REG); ++ + if (status & IOMMU_INT_INVALID_L2PG) + sun50i_iommu_handle_pt_irq(iommu, + IOMMU_INT_ERR_ADDR_L2_REG, +@@ -893,7 +896,8 @@ static irqreturn_t sun50i_iommu_irq(int irq, void *dev_id) + + iommu_write(iommu, IOMMU_INT_CLR_REG, status); + +- iommu_write(iommu, IOMMU_RESET_REG, ~status); ++ resets = (status | l1_status | l2_status) & IOMMU_INT_MASTER_MASK; ++ iommu_write(iommu, IOMMU_RESET_REG, ~resets); + iommu_write(iommu, IOMMU_RESET_REG, IOMMU_RESET_RELEASE_ALL); + + spin_unlock(&iommu->iommu_lock); diff --git a/projects/Allwinner/patches/linux/0070-iommu-sun50i-Fix-R-W-permission-check.patch b/projects/Allwinner/patches/linux/0070-iommu-sun50i-Fix-R-W-permission-check.patch new file mode 100644 index 0000000000..fa9844bf4e --- /dev/null +++ b/projects/Allwinner/patches/linux/0070-iommu-sun50i-Fix-R-W-permission-check.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 21:24:03 +0200 +Subject: [PATCH] iommu/sun50i: Fix R/W permission check + +Because driver has enum type permissions and iommu subsystem has bitmap +type, we have to be careful how check for combined read and write +permissions is done. In such case, we have to mask both permissions and +check that both are set at the same time. + +Current code just masks both flags but doesn't check that both are set. +In short, it always sets R/W permission, regardles if requested +permissions were RO, WO or RW. Fix that. + +Fixes: 4100b8c229b3 ("iommu: Add Allwinner H6 IOMMU driver") +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index 38d1069cf383..135df6934a9e 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -271,7 +271,7 @@ static u32 sun50i_mk_pte(phys_addr_t page, int prot) + enum sun50i_iommu_aci aci; + u32 flags = 0; + +- if (prot & (IOMMU_READ | IOMMU_WRITE)) ++ if ((prot & (IOMMU_READ | IOMMU_WRITE)) == (IOMMU_READ | IOMMU_WRITE)) + aci = SUN50I_IOMMU_ACI_RD_WR; + else if (prot & IOMMU_READ) + aci = SUN50I_IOMMU_ACI_RD; diff --git a/projects/Allwinner/patches/linux/0071-iommu-sun50i-Fix-flush-size.patch b/projects/Allwinner/patches/linux/0071-iommu-sun50i-Fix-flush-size.patch new file mode 100644 index 0000000000..82b3b18b0a --- /dev/null +++ b/projects/Allwinner/patches/linux/0071-iommu-sun50i-Fix-flush-size.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 21:35:53 +0200 +Subject: [PATCH] iommu/sun50i: Fix flush size + +Function sun50i_table_flush() takes number of entries as an argument, +not number of bytes. Fix that mistake in sun50i_dte_get_page_table(). + +Fixes: 4100b8c229b3 ("iommu: Add Allwinner H6 IOMMU driver") +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index 135df6934a9e..7c3b2ac552da 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -512,7 +512,7 @@ static u32 *sun50i_dte_get_page_table(struct sun50i_iommu_domain *sun50i_domain, + sun50i_iommu_free_page_table(iommu, drop_pt); + } + +- sun50i_table_flush(sun50i_domain, page_table, PT_SIZE); ++ sun50i_table_flush(sun50i_domain, page_table, NUM_PT_ENTRIES); + sun50i_table_flush(sun50i_domain, dte_addr, 1); + + return page_table; diff --git a/projects/Allwinner/patches/linux/0072-iommu-sun50i-Implement-.iotlb_sync_map.patch b/projects/Allwinner/patches/linux/0072-iommu-sun50i-Implement-.iotlb_sync_map.patch new file mode 100644 index 0000000000..91b1578d84 --- /dev/null +++ b/projects/Allwinner/patches/linux/0072-iommu-sun50i-Implement-.iotlb_sync_map.patch @@ -0,0 +1,133 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Wed, 12 Oct 2022 21:55:27 +0200 +Subject: [PATCH] iommu/sun50i: Implement .iotlb_sync_map + +Allocated iova ranges need to be invalidated immediately or otherwise +they might or might not work when used by master or CPU. This was +discovered when running video decoder conformity test with Cedrus. Some +videos were now and then decoded incorrectly and generated page faults. + +According to vendor driver, it's enough to invalidate just start and end +TLB and PTW cache lines. Documentation says that neighbouring lines must +be invalidated too. Finally, when page fault occurs, that iova must be +invalidated the same way, according to documentation. + +Fixes: 4100b8c229b3 ("iommu: Add Allwinner H6 IOMMU driver") +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 73 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index 7c3b2ac552da..d7c5e9b1a087 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -93,6 +93,8 @@ + #define NUM_PT_ENTRIES 256 + #define PT_SIZE (NUM_PT_ENTRIES * PT_ENTRY_SIZE) + ++#define SPAGE_SIZE 4096 ++ + struct sun50i_iommu { + struct iommu_device iommu; + +@@ -295,6 +297,62 @@ static void sun50i_table_flush(struct sun50i_iommu_domain *sun50i_domain, + dma_sync_single_for_device(iommu->dev, dma, size, DMA_TO_DEVICE); + } + ++static void sun50i_iommu_zap_iova(struct sun50i_iommu *iommu, ++ unsigned long iova) ++{ ++ u32 reg; ++ int ret; ++ ++ iommu_write(iommu, IOMMU_TLB_IVLD_ADDR_REG, iova); ++ iommu_write(iommu, IOMMU_TLB_IVLD_ADDR_MASK_REG, GENMASK(31, 12)); ++ iommu_write(iommu, IOMMU_TLB_IVLD_ENABLE_REG, ++ IOMMU_TLB_IVLD_ENABLE_ENABLE); ++ ++ ret = readl_poll_timeout_atomic(iommu->base + IOMMU_TLB_IVLD_ENABLE_REG, ++ reg, !reg, 1, 2000); ++ if (ret) ++ dev_warn(iommu->dev, "TLB invalidation timed out!\n"); ++} ++ ++static void sun50i_iommu_zap_ptw_cache(struct sun50i_iommu *iommu, ++ unsigned long iova) ++{ ++ u32 reg; ++ int ret; ++ ++ iommu_write(iommu, IOMMU_PC_IVLD_ADDR_REG, iova); ++ iommu_write(iommu, IOMMU_PC_IVLD_ENABLE_REG, ++ IOMMU_PC_IVLD_ENABLE_ENABLE); ++ ++ ret = readl_poll_timeout_atomic(iommu->base + IOMMU_PC_IVLD_ENABLE_REG, ++ reg, !reg, 1, 2000); ++ if (ret) ++ dev_warn(iommu->dev, "PTW cache invalidation timed out!\n"); ++} ++ ++static void sun50i_iommu_zap_range(struct sun50i_iommu *iommu, ++ unsigned long iova, size_t size) ++{ ++ assert_spin_locked(&iommu->iommu_lock); ++ ++ iommu_write(iommu, IOMMU_AUTO_GATING_REG, 0); ++ ++ sun50i_iommu_zap_iova(iommu, iova); ++ sun50i_iommu_zap_iova(iommu, iova + SPAGE_SIZE); ++ if (size > SPAGE_SIZE) { ++ sun50i_iommu_zap_iova(iommu, iova + size); ++ sun50i_iommu_zap_iova(iommu, iova + size + SPAGE_SIZE); ++ } ++ sun50i_iommu_zap_ptw_cache(iommu, iova); ++ sun50i_iommu_zap_ptw_cache(iommu, iova + SZ_1M); ++ if (size > SZ_1M) { ++ sun50i_iommu_zap_ptw_cache(iommu, iova + size); ++ sun50i_iommu_zap_ptw_cache(iommu, iova + size + SZ_1M); ++ } ++ ++ iommu_write(iommu, IOMMU_AUTO_GATING_REG, IOMMU_AUTO_GATING_ENABLE); ++} ++ + static int sun50i_iommu_flush_all_tlb(struct sun50i_iommu *iommu) + { + u32 reg; +@@ -344,6 +402,18 @@ static void sun50i_iommu_flush_iotlb_all(struct iommu_domain *domain) + spin_unlock_irqrestore(&iommu->iommu_lock, flags); + } + ++static void sun50i_iommu_iotlb_sync_map(struct iommu_domain *domain, ++ unsigned long iova, size_t size) ++{ ++ struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain); ++ struct sun50i_iommu *iommu = sun50i_domain->iommu; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&iommu->iommu_lock, flags); ++ sun50i_iommu_zap_range(iommu, iova, size); ++ spin_unlock_irqrestore(&iommu->iommu_lock, flags); ++} ++ + static void sun50i_iommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) + { +@@ -767,6 +837,7 @@ static const struct iommu_ops sun50i_iommu_ops = { + .attach_dev = sun50i_iommu_attach_device, + .detach_dev = sun50i_iommu_detach_device, + .flush_iotlb_all = sun50i_iommu_flush_iotlb_all, ++ .iotlb_sync_map = sun50i_iommu_iotlb_sync_map, + .iotlb_sync = sun50i_iommu_iotlb_sync, + .iova_to_phys = sun50i_iommu_iova_to_phys, + .map = sun50i_iommu_map, +@@ -786,6 +857,8 @@ static void sun50i_iommu_report_fault(struct sun50i_iommu *iommu, + report_iommu_fault(iommu->domain, iommu->dev, iova, prot); + else + dev_err(iommu->dev, "Page fault while iommu not attached to any domain?\n"); ++ ++ sun50i_iommu_zap_range(iommu, iova, SPAGE_SIZE); + } + + static phys_addr_t sun50i_iommu_handle_pt_irq(struct sun50i_iommu *iommu, diff --git a/projects/Allwinner/patches/linux/0073-iommu-sun50i-Allow-page-sizes-multiple-of-4096.patch b/projects/Allwinner/patches/linux/0073-iommu-sun50i-Allow-page-sizes-multiple-of-4096.patch new file mode 100644 index 0000000000..a6af506336 --- /dev/null +++ b/projects/Allwinner/patches/linux/0073-iommu-sun50i-Allow-page-sizes-multiple-of-4096.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Fri, 14 Oct 2022 20:15:43 +0200 +Subject: [PATCH] iommu/sun50i: Allow page sizes multiple of 4096 + +While peripheral supports only 4K page sizes, we can easily emulate +support for bigger page sizes, up to 1M. This is done by making multiple +entries in map function or clearing multiple entries in unmap. + +This considerably lowers overhead. + +Signed-off-by: Jernej Skrabec +--- + drivers/iommu/sun50i-iommu.c | 44 +++++++++++++++++++++--------------- + 1 file changed, 26 insertions(+), 18 deletions(-) + +diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c +index d7c5e9b1a087..9944266c4f58 100644 +--- a/drivers/iommu/sun50i-iommu.c ++++ b/drivers/iommu/sun50i-iommu.c +@@ -593,10 +593,12 @@ static int sun50i_iommu_map(struct iommu_domain *domain, unsigned long iova, + { + struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain); + struct sun50i_iommu *iommu = sun50i_domain->iommu; +- u32 pte_index; ++ u32 pte_index, pages, i; + u32 *page_table, *pte_addr; + int ret = 0; + ++ pages = size / SPAGE_SIZE; ++ + page_table = sun50i_dte_get_page_table(sun50i_domain, iova, gfp); + if (IS_ERR(page_table)) { + ret = PTR_ERR(page_table); +@@ -604,18 +606,21 @@ static int sun50i_iommu_map(struct iommu_domain *domain, unsigned long iova, + } + + pte_index = sun50i_iova_get_pte_index(iova); +- pte_addr = &page_table[pte_index]; +- if (unlikely(sun50i_pte_is_page_valid(*pte_addr))) { +- phys_addr_t page_phys = sun50i_pte_get_page_address(*pte_addr); +- dev_err(iommu->dev, +- "iova %pad already mapped to %pa cannot remap to %pa prot: %#x\n", +- &iova, &page_phys, &paddr, prot); +- ret = -EBUSY; +- goto out; ++ for (i = 0; i < pages; i++) { ++ pte_addr = &page_table[pte_index + i]; ++ if (unlikely(sun50i_pte_is_page_valid(*pte_addr))) { ++ phys_addr_t page_phys = sun50i_pte_get_page_address(*pte_addr); ++ dev_err(iommu->dev, ++ "iova %pad already mapped to %pa cannot remap to %pa prot: %#x\n", ++ &iova, &page_phys, &paddr, prot); ++ ret = -EBUSY; ++ goto out; ++ } ++ *pte_addr = sun50i_mk_pte(paddr, prot); ++ paddr += SPAGE_SIZE; + } + +- *pte_addr = sun50i_mk_pte(paddr, prot); +- sun50i_table_flush(sun50i_domain, pte_addr, 1); ++ sun50i_table_flush(sun50i_domain, &page_table[pte_index], pages); + + out: + return ret; +@@ -626,8 +631,10 @@ static size_t sun50i_iommu_unmap(struct iommu_domain *domain, unsigned long iova + { + struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain); + phys_addr_t pt_phys; ++ u32 dte, pages, i; + u32 *pte_addr; +- u32 dte; ++ ++ pages = size / SPAGE_SIZE; + + dte = sun50i_domain->dt[sun50i_iova_get_dte_index(iova)]; + if (!sun50i_dte_is_pt_valid(dte)) +@@ -636,13 +643,14 @@ static size_t sun50i_iommu_unmap(struct iommu_domain *domain, unsigned long iova + pt_phys = sun50i_dte_get_pt_address(dte); + pte_addr = (u32 *)phys_to_virt(pt_phys) + sun50i_iova_get_pte_index(iova); + +- if (!sun50i_pte_is_page_valid(*pte_addr)) +- return 0; ++ for (i = 0; i < pages; i++) ++ if (!sun50i_pte_is_page_valid(pte_addr[i])) ++ return 0; + +- memset(pte_addr, 0, sizeof(*pte_addr)); +- sun50i_table_flush(sun50i_domain, pte_addr, 1); ++ memset(pte_addr, 0, sizeof(*pte_addr) * pages); ++ sun50i_table_flush(sun50i_domain, pte_addr, pages); + +- return SZ_4K; ++ return size; + } + + static phys_addr_t sun50i_iommu_iova_to_phys(struct iommu_domain *domain, +@@ -828,7 +836,7 @@ static int sun50i_iommu_of_xlate(struct device *dev, + } + + static const struct iommu_ops sun50i_iommu_ops = { +- .pgsize_bitmap = SZ_4K, ++ .pgsize_bitmap = 0x1ff000, + .device_group = sun50i_iommu_device_group, + .domain_alloc = sun50i_iommu_domain_alloc, + .of_xlate = sun50i_iommu_of_xlate, diff --git a/projects/Allwinner/patches/linux/0074-media-Unify-YCbCr-YUV-terms-in-format-descriptions.patch b/projects/Allwinner/patches/linux/0074-media-Unify-YCbCr-YUV-terms-in-format-descriptions.patch new file mode 100644 index 0000000000..329dd47815 --- /dev/null +++ b/projects/Allwinner/patches/linux/0074-media-Unify-YCbCr-YUV-terms-in-format-descriptions.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +Date: Sat, 15 Oct 2022 10:43:41 +0200 +Subject: [PATCH] media: Unify YCbCr/YUV terms in format descriptions + +Format descriptions use YCbCr and YUV terms interchangeably. Let's unify +them so they all use YUV. While YCbCr is actually correct term here, YUV +is shorter and thus it also fixes too long description of P010 tiled +format. + +Fixes: 3c8e19d3d3f9 ("media: Add P010 tiled format") +Signed-off-by: Jernej Skrabec +--- + drivers/media/v4l2-core/v4l2-ioctl.c | 34 ++++++++++++++-------------- + 1 file changed, 17 insertions(+), 17 deletions(-) + +diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c +index e6fd355a2e92..de83714f0d40 100644 +--- a/drivers/media/v4l2-core/v4l2-ioctl.c ++++ b/drivers/media/v4l2-core/v4l2-ioctl.c +@@ -1347,23 +1347,23 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) + case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; + case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; + case V4L2_PIX_FMT_M420: descr = "YUV 4:2:0 (M420)"; break; +- case V4L2_PIX_FMT_NV12: descr = "Y/CbCr 4:2:0"; break; +- case V4L2_PIX_FMT_NV21: descr = "Y/CrCb 4:2:0"; break; +- case V4L2_PIX_FMT_NV16: descr = "Y/CbCr 4:2:2"; break; +- case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break; +- case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break; +- case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break; +- case V4L2_PIX_FMT_P010: descr = "10-bit Y/CbCr 4:2:0"; break; +- case V4L2_PIX_FMT_NV12_4L4: descr = "Y/CbCr 4:2:0 (4x4 Linear)"; break; +- case V4L2_PIX_FMT_NV12_16L16: descr = "Y/CbCr 4:2:0 (16x16 Linear)"; break; +- case V4L2_PIX_FMT_NV12_32L32: descr = "Y/CbCr 4:2:0 (32x32 Linear)"; break; +- case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/CbCr 4:2:0 (4x4 Linear)"; break; +- case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break; +- case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break; +- case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break; +- 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: descr = "Y/UV 4:2:0"; break; ++ case V4L2_PIX_FMT_NV21: descr = "Y/VU 4:2:0"; break; ++ case V4L2_PIX_FMT_NV16: descr = "Y/UV 4:2:2"; break; ++ case V4L2_PIX_FMT_NV61: descr = "Y/VU 4:2:2"; break; ++ case V4L2_PIX_FMT_NV24: descr = "Y/UV 4:4:4"; break; ++ case V4L2_PIX_FMT_NV42: descr = "Y/VU 4:4:4"; break; ++ case V4L2_PIX_FMT_P010: descr = "10-bit Y/UV 4:2:0"; break; ++ case V4L2_PIX_FMT_NV12_4L4: descr = "Y/UV 4:2:0 (4x4 Linear)"; break; ++ case V4L2_PIX_FMT_NV12_16L16: descr = "Y/UV 4:2:0 (16x16 Linear)"; break; ++ case V4L2_PIX_FMT_NV12_32L32: descr = "Y/UV 4:2:0 (32x32 Linear)"; break; ++ case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/UV 4:2:0 (4x4 Linear)"; break; ++ case V4L2_PIX_FMT_NV12M: descr = "Y/UV 4:2:0 (N-C)"; break; ++ case V4L2_PIX_FMT_NV21M: descr = "Y/VU 4:2:0 (N-C)"; break; ++ case V4L2_PIX_FMT_NV16M: descr = "Y/UV 4:2:2 (N-C)"; break; ++ case V4L2_PIX_FMT_NV61M: descr = "Y/VU 4:2:2 (N-C)"; break; ++ case V4L2_PIX_FMT_NV12MT: descr = "Y/UV 4:2:0 (64x32 MB, N-C)"; break; ++ case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/UV 4:2:0 (16x16 MB, N-C)"; 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;