From faae7545344704b08ec271cbf5e9c86f4cf2e0f0 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sat, 4 Jul 2026 18:26:34 +0900 Subject: [PATCH 01/33] import from ec3672599ae8dac8bd46582a02eae7c29716e802 for 7.1 dev Link: https://github.com/TeamYogaBlade2/linux/pull/33 Signed-off-by: Akari Tsuyukusa --- .../bindings/arm/mediatek/mediatek,mmsys.yaml | 1 + .../bindings/clock/mediatek,syscon.yaml | 1 - .../display/mediatek/mediatek,dsi.yaml | 1 + .../display/mediatek/mediatek,ovl.yaml | 1 + .../display/mediatek/mediatek,rdma.yaml | 1 + .../display/panel/panel-simple-dsi.yaml | 4 + .../bindings/iommu/mediatek,iommu.yaml | 1 + .../mediatek,smi-common.yaml | 22 ++- .../memory-controllers/mediatek,smi-larb.yaml | 1 + .../bindings/soc/mediatek/mediatek,mutex.yaml | 1 + .../dts/mediatek/mt6589-lenovo-b8000.dtsi | 39 ++++ arch/arm/boot/dts/mediatek/mt6589.dtsi | 151 +++++++++++++++ arch/arm/configs/lenovo-blade_defconfig | 17 +- drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 47 ++++- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 10 + drivers/gpu/drm/mediatek/mtk_dsi.c | 6 + drivers/gpu/drm/panel/panel-simple.c | 56 ++++++ drivers/iommu/mtk_iommu_v1.c | 4 +- drivers/memory/mtk-smi.c | 175 ++++++++++++++---- drivers/soc/mediatek/mt6589-dispsys.h | 172 +++++++++++++++++ drivers/soc/mediatek/mtk-mmsys.c | 8 + drivers/soc/mediatek/mtk-mutex.c | 22 +++ include/dt-bindings/memory/mt2701-larb-port.h | 14 +- include/dt-bindings/memory/mt6589-larb-port.h | 89 +++++++++ include/linux/soc/mediatek/mtk-mmsys.h | 4 + 25 files changed, 795 insertions(+), 53 deletions(-) create mode 100644 drivers/soc/mediatek/mt6589-dispsys.h create mode 100644 include/dt-bindings/memory/mt6589-larb-port.h diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml index 3f4262e93c789c..5a57e918d65411 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.yaml @@ -23,6 +23,7 @@ properties: - enum: - mediatek,mt2701-mmsys - mediatek,mt2712-mmsys + - mediatek,mt6589-dispsys - mediatek,mt6765-mmsys - mediatek,mt6779-mmsys - mediatek,mt6795-mmsys diff --git a/Documentation/devicetree/bindings/clock/mediatek,syscon.yaml b/Documentation/devicetree/bindings/clock/mediatek,syscon.yaml index 25e04381dcf064..8ba3aea07dc2e3 100644 --- a/Documentation/devicetree/bindings/clock/mediatek,syscon.yaml +++ b/Documentation/devicetree/bindings/clock/mediatek,syscon.yaml @@ -28,7 +28,6 @@ properties: - mediatek,mt2712-mfgcfg - mediatek,mt2712-vdecsys - mediatek,mt2712-vencsys - - mediatek,mt6589-dispsys - mediatek,mt6589-imgsys - mediatek,mt6589-mfgsys - mediatek,mt6589-vdecsys diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.yaml index 27ffbccc2a082e..046b2d331c5c8a 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.yaml @@ -24,6 +24,7 @@ properties: oneOf: - enum: - mediatek,mt2701-dsi + - mediatek,mt6589-dsi - mediatek,mt7623-dsi - mediatek,mt8167-dsi - mediatek,mt8173-dsi diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,ovl.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,ovl.yaml index 679f731f0f15f7..ee9599a53e5a94 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,ovl.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,ovl.yaml @@ -23,6 +23,7 @@ properties: oneOf: - enum: - mediatek,mt2701-disp-ovl + - mediatek,mt6589-disp-ovl - mediatek,mt8167-disp-ovl - mediatek,mt8173-disp-ovl - mediatek,mt8183-disp-ovl diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,rdma.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,rdma.yaml index cb187a95c11ea4..5eeedea4ae2d2b 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,rdma.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,rdma.yaml @@ -25,6 +25,7 @@ properties: oneOf: - enum: - mediatek,mt2701-disp-rdma + - mediatek,mt6589-disp-rdma - mediatek,mt8173-disp-rdma - mediatek,mt8183-disp-rdma - mediatek,mt8195-disp-rdma diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index cc8d795df732c3..be4e11b6f49c4b 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -31,8 +31,12 @@ properties: # AU Optronics Corporation 8.0" WUXGA TFT LCD panel - auo,b080uan01 + # BOE Technology 10.1" WXGA panel with Himax HX8896-A01 TCON + - boe,hx8896-a01 # Boe Corporation 8.0" WUXGA TFT LCD panel - boe,tv080wum-nl0 + # CMI 10.1" WXGA panel with Himax HX8896-A01 TCON + - innolux,hx8896-a01 # Innolux P079ZCA 7.85" 768x1024 TFT LCD panel - innolux,p079zca # JDI FHD_R63452 1080x1920 5.2" IPS LCD Panel diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml index 79c573c47b08ea..67107e42358b65 100644 --- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml @@ -72,6 +72,7 @@ properties: - enum: - mediatek,mt2701-m4u # generation one - mediatek,mt2712-m4u # generation two + - mediatek,mt6589-m4u - mediatek,mt6779-m4u # generation two - mediatek,mt6795-m4u # generation two - mediatek,mt6893-iommu-mm # generation two diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.yaml index 0762e0ff66ef02..35d7d4c1ca6b65 100644 --- a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.yaml @@ -31,6 +31,7 @@ properties: - enum: - mediatek,mt2701-smi-common - mediatek,mt2712-smi-common + - mediatek,mt6589-smi-common - mediatek,mt6779-smi-common - mediatek,mt6795-smi-common - mediatek,mt6893-smi-common @@ -51,9 +52,6 @@ properties: - const: mediatek,mt7623-smi-common - const: mediatek,mt2701-smi-common - reg: - maxItems: 1 - power-domains: maxItems: 1 @@ -86,6 +84,24 @@ required: - clock-names allOf: + - if: # Generation 0 HW (mt6589) + properties: + compatible: + contains: + const: mediatek,mt6589-smi-common + then: + properties: + reg: + items: + - description: SMI AO base + - description: SMI base + minItems: 2 + maxItems: 2 + else: + properties: + reg: + maxItems: 1 + - if: # only for gen1 HW properties: compatible: diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.yaml b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.yaml index 2e7fac4b50945d..0a4ea1865c2f64 100644 --- a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.yaml @@ -19,6 +19,7 @@ properties: - enum: - mediatek,mt2701-smi-larb - mediatek,mt2712-smi-larb + - mediatek,mt6589-smi-larb - mediatek,mt6779-smi-larb - mediatek,mt6795-smi-larb - mediatek,mt6893-smi-larb diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml index 5267cfe92572dd..802bc0c7c1203b 100644 --- a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml +++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml @@ -26,6 +26,7 @@ properties: enum: - mediatek,mt2701-disp-mutex - mediatek,mt2712-disp-mutex + - mediatek,mt6589-disp-mutex - mediatek,mt6795-disp-mutex - mediatek,mt8167-disp-mutex - mediatek,mt8173-disp-mutex diff --git a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi index 645267e60fe9b2..1f497944a0222a 100644 --- a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi @@ -13,5 +13,44 @@ height = <800>; stride = <(1280 * 2)>; format = "r5g6b5"; + //status = "disabled"; + }; + + reg_panel: regulator-panel { + compatible = "regulator-fixed"; + regulator-name = "fixed-1.2V"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + regulator-always-on; + }; + +}; + +&dsi { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + panel@0 { + compatible = "boe,hx8896-a01"; + /* + * or innolux,hx8896-a01 + * TODO: panel mux + */ + reg = <0>; + power-supply = <®_panel>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + + port { + dsi_out: endpoint { + remote-endpoint = <&panel_in>; + }; }; }; diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index 899af6af69c86e..6ea8b76af9102b 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -10,6 +10,7 @@ #include #include #include +#include #include / { @@ -384,6 +385,16 @@ clock-names = "system-clk", "rtc-clk"; }; + mipi_tx0: dsi-phy@10012000 { + compatible = "mediatek,mt7623-mipi-tx", + "mediatek,mt2701-mipi-tx"; + reg = <0x10012000 0x90>; + clocks = <&dispsys CLK_DISP1_DSI_DIGITAL_LANE>; // uart_clk + clock-output-names = "mipi_tx0_pll"; + #clock-cells = <0>; + #phy-cells = <0>; + }; + pwrap: pwrap@1000f000 { compatible = "mediatek,mt6589-pwrap"; reg = <0x1000f000 0x1000>, @@ -407,6 +418,33 @@ reg = <0x10200100 0x1c>; }; + smi_common: smi@10202000 { + compatible = "mediatek,mt6589-smi-common"; + reg = <0x10202000 0x1000>, <0x1000e000 0x1000>; + clocks = <&infracfg CLK_INFRA_SMI>, + <&infracfg CLK_INFRA_SMI>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + iommu: iommu@10205000 { + compatible = "mediatek,mt6589-m4u"; + reg = <0x10205000 0x200>, + <0x10205200 0x600>, + <0x10205800 0x600>; + reg-names = "global", "m4u0", "m4u1"; + interrupts = , + , + , + ; + interrupt-names = "m4u0", "m4u1", "m4ul2", "m4ul2-sec"; + clocks = <&infracfg CLK_INFRA_M4U>; + clock-names = "bclk"; + mediatek,larbs = <&larb0>, <&larb1>, <&larb2>, + <&larb3>, <&larb4>; + #iommu-cells = <1>; + }; + gic: interrupt-controller@10211000 { compatible = "arm,cortex-a7-gic"; interrupt-controller; @@ -536,6 +574,119 @@ clock-names = "source", "hclk"; status = "disabled"; }; + + ovl: ovl@14003000 { + compatible = "mediatek,mt6589-disp-ovl"; + reg = <0x14003000 0x1000>; + interrupts = ; + clocks = <&dispsys CLK_DISP0_OVL_ENGINE>, + <&dispsys CLK_DISP0_OVL_SMI>; + iommus = <&iommu MT6589_M4U_PORT_OVL_CH0>, + <&iommu MT6589_M4U_PORT_OVL_CH1>, + <&iommu MT6589_M4U_PORT_OVL_CH2>, + <&iommu MT6589_M4U_PORT_OVL_CH3>; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + rdma0: rdma@14006000 { + compatible = "mediatek,mt6589-disp-rdma"; + reg = <0x14006000 0x1000>; + interrupts = ; + clocks = <&dispsys CLK_DISP0_RDMA0_ENGINE>, + <&dispsys CLK_DISP0_RDMA0_SMI>, + <&dispsys CLK_DISP0_RDMA0_OUTPUT>; + iommus = <&iommu MT6589_M4U_PORT_RDMA0>; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + rdma1: rdma@14007000 { + compatible = "mediatek,mt6589-disp-rdma"; + reg = <0x14007000 0x1000>; + interrupts = ; + clocks = <&dispsys CLK_DISP0_RDMA1_ENGINE>, + <&dispsys CLK_DISP0_RDMA1_SMI>, + <&dispsys CLK_DISP0_RDMA1_OUTPUT>; + iommus = <&iommu MT6589_M4U_PORT_RDMA1>; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + mutex: mutex@14011000 { + compatible = "mediatek,mt6589-disp-mutex"; + reg = <0x14011000 0x1000>; + interrupts = ; + /* not found in clock list */ + clocks = <&clk26m>; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + dsi: dsi@1400d000 { + compatible = "mediatek,mt6589-dsi"; + reg = <0x1400d000 0x1000>; + interrupts = ; + clocks = <&dispsys CLK_DISP1_DSI_ENGINE>, + <&dispsys CLK_DISP1_DSI_DIGITAL>, + <&mipi_tx0>; + clock-names = "engine", "digital", "hs"; + phys = <&mipi_tx0>; + phy-names = "dphy"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + status = "disabled"; + }; + + larb0: larb@17001000 { + compatible = "mediatek,mt6589-smi-larb"; + reg = <0x17001000 0x1000>; + mediatek,smi = <&smi_common>; + mediatek,larb-id = <0>; + clocks = <&vencsys CLK_VENC_VEN>, + <&vencsys CLK_VENC_VEN>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + larb1: larb@16010000 { + compatible = "mediatek,mt6589-smi-larb"; + reg = <0x16010000 0x1000>; + mediatek,smi = <&smi_common>; + mediatek,larb-id = <1>; + clocks = <&vdecsys CLK_VDEC0_VDE>, + <&vdecsys CLK_VDEC1_SMI>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + larb2: larb@14010000 { + compatible = "mediatek,mt6589-smi-larb"; + reg = <0x14010000 0x1000>; + mediatek,smi = <&smi_common>; + mediatek,larb-id = <2>; + clocks = <&dispsys CLK_DISP0_LARB2_SMI>, + <&dispsys CLK_DISP0_LARB2_SMI>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + larb3: larb@15001000 { + compatible = "mediatek,mt6589-smi-larb"; + reg = <0x15001000 0x1000>; + mediatek,smi = <&smi_common>; + mediatek,larb-id = <3>; + clocks = <&imgsys CLK_IMAGE_LARB3_SMI>, + <&imgsys CLK_IMAGE_LARB3_SMI>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; + + larb4: larb@15002000 { + compatible = "mediatek,mt6589-smi-larb"; + reg = <0x15002000 0x1000>; + mediatek,smi = <&smi_common>; + mediatek,larb-id = <4>; + clocks = <&imgsys CLK_IMAGE_LARB4_SMI>, + <&imgsys CLK_IMAGE_LARB4_SMI>; + clock-names = "apb", "smi"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + }; }; }; diff --git a/arch/arm/configs/lenovo-blade_defconfig b/arch/arm/configs/lenovo-blade_defconfig index f0389f5a9aa32c..ea66c838702a5a 100644 --- a/arch/arm/configs/lenovo-blade_defconfig +++ b/arch/arm/configs/lenovo-blade_defconfig @@ -31,14 +31,23 @@ CONFIG_ARM_CPUIDLE=y ## IOMMU CONFIG_IOMMU_SUPPORT=y -# MediaTek M4U -#CONFIG_MTK_IOMMU=y +CONFIG_MTK_IOMMU_V1=y + +## SMI +CONFIG_MEMORY=y +CONFIG_MTK_SMI=y ## Display +CONFIG_DRM=y +CONFIG_DRM_SIMPLEDRM=y +CONFIG_DRM_MEDIATEK=y +CONFIG_DRM_PANEL=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_PHY_MTK_MIPI_DSI=y +CONFIG_MTK_MMSYS=y -## Framebuffer CONFIG_FB=y -CONFIG_FB_SIMPLE=y CONFIG_FRAMEBUFFER_CONSOLE=y ## Clock diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index 97a899e4bd99ca..e92ee0eff27e94 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -20,9 +20,21 @@ #include "mtk_disp_drv.h" #include "mtk_drm_drv.h" -#define DISP_REG_OVL_INTEN 0x0004 -#define OVL_FME_CPL_INT BIT(1) +#define DISP_REG_OVL_INTEN 0x0004 +#define OVL_FME_CPL_INT BIT(1) +#define OVL_FME_UND_INT BIT(2) +#define OVL_RDMA0_EOF_ABNORMAL_INT BIT(5) +#define OVL_RDMA1_EOF_ABNORMAL_INT BIT(6) +#define OVL_RDMA0_FIFO_UND_INT BIT(9) +#define OVL_RDMA1_FIFO_UND_INT BIT(10) + #define DISP_REG_OVL_INTSTA 0x0008 +#define OVL_FME_UND BIT(2) +#define OVL_RDMA0_EOF_ABNORMAL BIT(5) +#define OVL_RDMA1_EOF_ABNORMAL BIT(6) +#define OVL_RDMA0_FIFO_UND BIT(9) +#define OVL_RDMA1_FIFO_UND BIT(10) + #define DISP_REG_OVL_EN 0x000c #define DISP_REG_OVL_RST 0x0014 #define DISP_REG_OVL_ROI_SIZE 0x0020 @@ -170,6 +182,18 @@ struct mtk_disp_ovl { static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id) { struct mtk_disp_ovl *priv = dev_id; + u32 reg = readl(priv->regs + DISP_REG_OVL_INTSTA); + + if (reg & OVL_FME_UND) + pr_err("OVL: OVL frame underflow\n"); + if (reg & OVL_RDMA0_EOF_ABNORMAL) + pr_err("OVL: RDMA0 didn't complete frame\n"); + if (reg & OVL_RDMA1_EOF_ABNORMAL) + pr_err("OVL: RDMA1 didn't complete frame\n"); + if (reg & OVL_RDMA0_FIFO_UND) + pr_err("OVL: RDMA0 FIFO underflow\n"); + if (reg & OVL_RDMA1_FIFO_UND) + pr_err("OVL: RDMA1 FIFO underflow\n"); /* Clear frame completion interrupt */ writel(0x0, priv->regs + DISP_REG_OVL_INTSTA); @@ -205,7 +229,10 @@ void mtk_ovl_enable_vblank(struct device *dev) struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA); - writel_relaxed(OVL_FME_CPL_INT, ovl->regs + DISP_REG_OVL_INTEN); + writel_relaxed(OVL_FME_CPL_INT | OVL_FME_UND_INT | + OVL_RDMA0_EOF_ABNORMAL_INT | OVL_RDMA1_EOF_ABNORMAL_INT | + OVL_RDMA0_FIFO_UND_INT | OVL_RDMA1_FIFO_UND_INT, + ovl->regs + DISP_REG_OVL_INTEN); } void mtk_ovl_disable_vblank(struct device *dev) @@ -681,6 +708,18 @@ static const struct mtk_disp_ovl_data mt8167_ovl_driver_data = { .num_formats = ARRAY_SIZE(mt8173_formats), }; +static const struct mtk_disp_ovl_data mt6589_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT2701, + .gmc_bits = 10, + .layer_nr = 4, + .fmt_rgb565_is_0 = false, + .blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE) | + BIT(DRM_MODE_BLEND_PIXEL_NONE), + .formats = mt8173_formats, + .num_formats = ARRAY_SIZE(mt8173_formats), +}; + static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = { .addr = DISP_REG_OVL_ADDR_MT8173, .gmc_bits = 8, @@ -752,6 +791,8 @@ static const struct mtk_disp_ovl_data mt8195_ovl_driver_data = { static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = { { .compatible = "mediatek,mt2701-disp-ovl", .data = &mt2701_ovl_driver_data}, + { .compatible = "mediatek,mt6589-disp-ovl", + .data = &mt6589_ovl_driver_data}, { .compatible = "mediatek,mt8167-disp-ovl", .data = &mt8167_ovl_driver_data}, { .compatible = "mediatek,mt8173-disp-ovl", diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index c86a3f54f35bcc..02d33d292c2ced 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -334,6 +334,8 @@ static const struct mtk_mmsys_driver_data mt8365_mmsys_driver_data = { static const struct of_device_id mtk_drm_of_ids[] = { { .compatible = "mediatek,mt2701-mmsys", .data = &mt2701_mmsys_driver_data}, + { .compatible = "mediatek,mt6589-dispsys", + .data = &mt2701_mmsys_driver_data}, /* TODO */ { .compatible = "mediatek,mt7623-mmsys", .data = &mt7623_mmsys_driver_data}, { .compatible = "mediatek,mt2712-mmsys", @@ -742,6 +744,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DISP_MUTEX }, { .compatible = "mediatek,mt2712-disp-mutex", .data = (void *)MTK_DISP_MUTEX }, + { .compatible = "mediatek,mt6589-disp-mutex", + .data = (void *)MTK_DISP_MUTEX }, { .compatible = "mediatek,mt8167-disp-mutex", .data = (void *)MTK_DISP_MUTEX }, { .compatible = "mediatek,mt8173-disp-mutex", @@ -762,6 +766,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DISP_OD }, { .compatible = "mediatek,mt2701-disp-ovl", .data = (void *)MTK_DISP_OVL }, + { .compatible = "mediatek,mt6589-disp-ovl", + .data = (void *)MTK_DISP_OVL }, { .compatible = "mediatek,mt8167-disp-ovl", .data = (void *)MTK_DISP_OVL }, { .compatible = "mediatek,mt8173-disp-ovl", @@ -786,6 +792,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DISP_PWM }, { .compatible = "mediatek,mt2701-disp-rdma", .data = (void *)MTK_DISP_RDMA }, + { .compatible = "mediatek,mt6589-disp-rdma", + .data = (void *)MTK_DISP_RDMA }, { .compatible = "mediatek,mt8167-disp-rdma", .data = (void *)MTK_DISP_RDMA }, { .compatible = "mediatek,mt8173-disp-rdma", @@ -818,6 +826,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DPI }, { .compatible = "mediatek,mt2701-dsi", .data = (void *)MTK_DSI }, + { .compatible = "mediatek,mt6589-dsi", + .data = (void *)MTK_DSI }, { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI }, { .compatible = "mediatek,mt8183-dsi", diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 5aa71fcdcfab78..a1e17693d56d96 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -1277,6 +1277,11 @@ static const struct mtk_dsi_driver_data mt2701_dsi_driver_data = { .reg_shadow_dbg_off = 0x190 }; +static const struct mtk_dsi_driver_data mt6589_dsi_driver_data = { + .reg_cmdq_off = 0x180, + .reg_vm_cmd_off = 0x130, +}; + static const struct mtk_dsi_driver_data mt8183_dsi_driver_data = { .reg_cmdq_off = 0x200, .reg_vm_cmd_off = 0x130, @@ -1305,6 +1310,7 @@ static const struct mtk_dsi_driver_data mt8188_dsi_driver_data = { static const struct of_device_id mtk_dsi_of_match[] = { { .compatible = "mediatek,mt2701-dsi", .data = &mt2701_dsi_driver_data }, + { .compatible = "mediatek,mt6589-dsi", .data = &mt6589_dsi_driver_data }, { .compatible = "mediatek,mt8173-dsi", .data = &mt8173_dsi_driver_data }, { .compatible = "mediatek,mt8183-dsi", .data = &mt8183_dsi_driver_data }, { .compatible = "mediatek,mt8186-dsi", .data = &mt8186_dsi_driver_data }, diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 236bd56208ccfc..2d40ab5b794312 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5790,6 +5790,31 @@ static const struct panel_desc_dsi auo_b080uan01 = { .lanes = 4, }; +static const struct drm_display_mode boe_hx8896_a01_mode = { + .clock = 69837, /* 1416 * 822 * 60 / 1000 kHz */ + .hdisplay = 1280, + .hsync_start = 1280 + 100, + .hsync_end = 1280 + 100 + 4, + .htotal = 1280 + 100 + 4 + 32, + .vdisplay = 800, + .vsync_start = 800 + 10, + .vsync_end = 800 + 10 + 2, + .vtotal = 800 + 10 + 2 + 10, +}; + +static const struct panel_desc_dsi boe_hx8896_a01 = { + .desc = { + .modes = &boe_hx8896_a01_mode, + .num_modes = 1, + .bpc = 8, + .size = { .width = 217, .height = 136 }, /* TODO */ + .connector_type = DRM_MODE_CONNECTOR_DSI, + }, + .flags = MIPI_DSI_MODE_VIDEO, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static const struct drm_display_mode boe_tv080wum_nl0_mode = { .clock = 160000, .hdisplay = 1200, @@ -5820,6 +5845,31 @@ static const struct panel_desc_dsi boe_tv080wum_nl0 = { .lanes = 4, }; +static const struct drm_display_mode innolux_hx8896_a01_mode = { + .clock = 76019, /* 1521 * 833 * 60 / 1000 kHz */ + .hdisplay = 1280, + .hsync_start = 1280 + 186, /* + HFP */ + .hsync_end = 1280 + 186 + 5, /* + HSW */ + .htotal = 1280 + 186 + 5 + 50, /* + HBP */ + .vdisplay = 800, + .vsync_start = 800 + 21, /* + VFP */ + .vsync_end = 800 + 21 + 2, /* + VSW */ + .vtotal = 800 + 21 + 2 + 10, /* + VBP */ +}; + +static const struct panel_desc_dsi innolux_hx8896_a01 = { + .desc = { + .modes = &innolux_hx8896_a01_mode, + .num_modes = 1, + .bpc = 8, + .size = { .width = 217, .height = 136 }, /* TODO */ + .connector_type = DRM_MODE_CONNECTOR_DSI, + }, + .flags = MIPI_DSI_MODE_VIDEO, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static const struct drm_display_mode lg_lh500wx1_sd03_mode = { .clock = 67000, .hdisplay = 720, @@ -5940,9 +5990,15 @@ static const struct of_device_id dsi_of_match[] = { { .compatible = "auo,b080uan01", .data = &auo_b080uan01 + }, { + .compatible = "boe,hx8896-a01", + .data = &boe_hx8896_a01 }, { .compatible = "boe,tv080wum-nl0", .data = &boe_tv080wum_nl0 + }, { + .compatible = "innolux,hx8896-a01", + .data = &innolux_hx8896_a01 }, { .compatible = "lg,lh500wx1-sd03", .data = &lg_lh500wx1_sd03 diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index ac97dd2868d4b8..591ffa66a72839 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -146,8 +146,8 @@ static struct mtk_iommu_v1_domain *to_mtk_domain(struct iommu_domain *dom) } static const int mt2701_m4u_in_larb[] = { - LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, - LARB2_PORT_OFFSET, LARB3_PORT_OFFSET + MT2701_LARB0_PORT_OFFSET, MT2701_LARB1_PORT_OFFSET, + MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET }; static inline int mt2701_m4u_to_larb(int id) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index aaeba8ab211e97..ad396d5aea5ff7 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -37,6 +37,7 @@ #define SMI_DUMMY 0x444 /* SMI LARB */ +#define SMI_LARB_STAT 0x0 #define SMI_LARB_SLP_CON 0xc #define SLP_PROT_EN BIT(0) #define SLP_PROT_RDY BIT(16) @@ -48,9 +49,19 @@ #define SMI_LARB_SW_FLAG 0x40 #define SMI_LARB_SW_FLAG_1 0x1 +/* mt6589, mt8135 */ +#define SMI_LARB_BWFILTER_EN_V1 0x2c + +/* mt2601, mt6572, mt6571, mt6582, mt6592, mt8127 */ +#define SMI_LARB_BWFILTER_EN_V2 0x60 + +#define SMI_LARB_OSTD_CTRL_EN 0x64 + #define SMI_LARB_OSTDL_PORT 0x200 #define SMI_LARB_OSTDL_PORTx(id) (SMI_LARB_OSTDL_PORT + (((id) & 0x1f) << 2)) +#define SMI_LARB_FIFO_STAT0 0x600 + /* Below are about mmu enable registers, they are different in SoCs */ /* gen1: mt2701 */ #define REG_SMI_SECUR_CON_BASE 0x5c0 @@ -92,6 +103,8 @@ #define MTK_SMI_FLAG_SW_FLAG BIT(1) #define MTK_SMI_FLAG_SLEEP_CTL BIT(2) #define MTK_SMI_FLAG_CFG_PORT_SEC_CTL BIT(3) +#define MTK_SMI_FLAG_BW_CALIBRATE BIT(4) +#define MTK_SMI_FLAG_BWFILTER_REG_V2 BIT(5) #define MTK_SMI_CAPS(flags, _x) (!!((flags) & (_x))) struct mtk_smi_reg_pair { @@ -100,6 +113,7 @@ struct mtk_smi_reg_pair { }; enum mtk_smi_type { + MTK_SMI_GEN0, MTK_SMI_GEN1, MTK_SMI_GEN2, /* gen2 smi common */ MTK_SMI_GEN2_SUB_COMM, /* gen2 smi sub common */ @@ -141,10 +155,8 @@ struct mtk_smi { unsigned int clk_num; struct clk_bulk_data clks[MTK_SMI_CLK_NR_MAX]; struct clk *clk_async; /*only needed by mt2701*/ - union { - void __iomem *smi_ao_base; /* only for gen1 */ - void __iomem *base; /* only for gen2 */ - }; + void __iomem *smi_ao_base; + void __iomem *base; struct device *smi_common_dev; /* for sub common */ const struct mtk_smi_common_plat *plat; }; @@ -188,6 +200,71 @@ static const struct component_ops mtk_smi_larb_component_ops = { .unbind = mtk_smi_larb_unbind, }; +static int mtk_smi_larb_config_port_gen0(struct device *dev) +{ + struct mtk_smi_larb *larb = dev_get_drvdata(dev); + const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen; + struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); + const u8 *larbostd = larb_gen->ostd ? larb_gen->ostd[larb->larbid] : + NULL; + int i, m4u_port_id, larb_port_num, offset; + u32 sec_con_val, reg_val, tmp; + int ret; + + m4u_port_id = larb_gen->port_in_larb[larb->larbid]; + larb_port_num = larb_gen->port_in_larb[larb->larbid + 1] - m4u_port_id; + + /* gen 1 part */ + for (i = 0; i < larb_port_num; i++, m4u_port_id++) { + if (*larb->mmu & BIT(i)) { + sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id); + } else { + continue; + } + + reg_val = readl(common->smi_ao_base + + REG_SMI_SECUR_CON_ADDR(m4u_port_id)); + reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id); + reg_val |= sec_con_val; + reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id); + writel(reg_val, common->smi_ao_base + + REG_SMI_SECUR_CON_ADDR(m4u_port_id)); + } + + /* gen 2 part */ + for (i = 0; i < SMI_LARB_PORT_NR_MAX && larbostd && !!larbostd[i]; i++) + writel_relaxed(larbostd[i], + larb->base + SMI_LARB_OSTDL_PORTx(i)); + + /* some gen 0 SoCs need BW calibration */ + if (MTK_SMI_CAPS(larb_gen->flags_general, MTK_SMI_FLAG_BW_CALIBRATE)) { + if (MTK_SMI_CAPS(larb_gen->flags_general, + MTK_SMI_FLAG_BWFILTER_REG_V2)) + offset = SMI_LARB_BWFILTER_EN_V2; + else + offset = SMI_LARB_BWFILTER_EN_V1; + + reg_val = readl_relaxed(larb->base + SMI_LARB_STAT); + if (!reg_val) { + writel_relaxed(0xffffffff, + larb->base + offset); + + ret = readl_poll_timeout( + larb->base + SMI_LARB_FIFO_STAT0, tmp, + tmp == 0xaaaa, 500, 64 * 500); + if (ret) + dev_warn(dev, + "BW limiter calibration timeout\n"); + + writel_relaxed(0, larb->base + offset); + writel_relaxed(0xffffffff, + larb->base + offset); + } + } + + return 0; +} + static int mtk_smi_larb_config_port_gen1(struct device *dev) { struct mtk_smi_larb *larb = dev_get_drvdata(dev); @@ -283,6 +360,8 @@ static int mtk_smi_larb_config_port_gen2_general(struct device *dev) return 0; } + + static const u8 mtk_smi_larb_mt6893_ostd[][SMI_LARB_PORT_NR_MAX] = { [0] = {0x2, 0x6, 0x2, 0x2, 0x2, 0x28, 0x18, 0x18, 0x1, 0x1, 0x1, 0x8, 0x8, 0x1, 0x3f}, @@ -480,8 +559,8 @@ static const u8 mtk_smi_larb_mt8195_ostd[][SMI_LARB_PORT_NR_MAX] = { static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { .port_in_larb = { - LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, - LARB2_PORT_OFFSET, LARB3_PORT_OFFSET + MT2701_LARB0_PORT_OFFSET, MT2701_LARB1_PORT_OFFSET, + MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET }, .config_port = mtk_smi_larb_config_port_gen1, }; @@ -491,6 +570,12 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = { .larb_direct_to_common_mask = BIT(8) | BIT(9), /* bdpsys */ }; +static const struct mtk_smi_larb_gen mtk_smi_larb_mt6589 = { + .port_in_larb = { 0, 10, 17, 29, 44 ,56 }, + .config_port = mtk_smi_larb_config_port_gen0, + .flags_general = MTK_SMI_FLAG_BW_CALIBRATE, +}; + static const struct mtk_smi_larb_gen mtk_smi_larb_mt6779 = { .config_port = mtk_smi_larb_config_port_gen2_general, .larb_direct_to_common_mask = @@ -548,6 +633,7 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8195 = { static const struct of_device_id mtk_smi_larb_of_ids[] = { {.compatible = "mediatek,mt2701-smi-larb", .data = &mtk_smi_larb_mt2701}, {.compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712}, + {.compatible = "mediatek,mt6589-smi-larb", .data = &mtk_smi_larb_mt6589}, {.compatible = "mediatek,mt6779-smi-larb", .data = &mtk_smi_larb_mt6779}, {.compatible = "mediatek,mt6795-smi-larb", .data = &mtk_smi_larb_mt8173}, {.compatible = "mediatek,mt6893-smi-larb", .data = &mtk_smi_larb_mt6893}, @@ -739,6 +825,10 @@ static struct platform_driver mtk_smi_larb_driver = { } }; +static const struct mtk_smi_reg_pair mtk_smi_common_mt6589_init[SMI_COMMON_INIT_REGS_NR] = { + {0x200, 0x24}, /* SMI_L1LEN */ +}; + static const struct mtk_smi_reg_pair mtk_smi_common_mt6795_init[SMI_COMMON_INIT_REGS_NR] = { {SMI_L1_ARB, 0x1b}, {SMI_M4U_TH, 0xce810c85}, @@ -763,6 +853,11 @@ static const struct mtk_smi_common_plat mtk_smi_common_gen2 = { .type = MTK_SMI_GEN2, }; +static const struct mtk_smi_common_plat mtk_smi_common_mt6589 = { + .type = MTK_SMI_GEN0, + .init = mtk_smi_common_mt6589_init, +}; + static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = { .type = MTK_SMI_GEN2, .has_gals = true, @@ -843,6 +938,7 @@ static const struct mtk_smi_common_plat mtk_smi_common_mt8365 = { static const struct of_device_id mtk_smi_common_of_ids[] = { {.compatible = "mediatek,mt2701-smi-common", .data = &mtk_smi_common_gen1}, {.compatible = "mediatek,mt2712-smi-common", .data = &mtk_smi_common_gen2}, + {.compatible = "mediatek,mt6589-smi-common", .data = &mtk_smi_common_mt6589}, {.compatible = "mediatek,mt6779-smi-common", .data = &mtk_smi_common_mt6779}, {.compatible = "mediatek,mt6795-smi-common", .data = &mtk_smi_common_mt6795}, {.compatible = "mediatek,mt6893-smi-common", .data = &mtk_smi_common_mt6893}, @@ -883,31 +979,42 @@ static int mtk_smi_common_probe(struct platform_device *pdev) if (ret) return ret; - /* - * for mtk smi gen 1, we need to get the ao(always on) base to config - * m4u port, and we need to enable the aync clock for transform the smi - * clock into emi clock domain, but for mtk smi gen2, there's no smi ao - * base. - */ - if (common->plat->type == MTK_SMI_GEN1) { - common->smi_ao_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(common->smi_ao_base)) - return PTR_ERR(common->smi_ao_base); - - common->clk_async = devm_clk_get_enabled(dev, "async"); - if (IS_ERR(common->clk_async)) - return PTR_ERR(common->clk_async); - } else { - common->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(common->base)) - return PTR_ERR(common->base); - } - - /* link its smi-common if this is smi-sub-common */ - if (common->plat->type == MTK_SMI_GEN2_SUB_COMM) { - ret = mtk_smi_device_link_common(dev, &common->smi_common_dev); - if (ret < 0) - return ret; + switch (common->plat->type) { + case MTK_SMI_GEN0: + /* + * gen 0 uses 2 mmio ranges: ao base for iommu configuration, + * and ext base for ostd, fifo and bw limiter setup + */ + common->smi_ao_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(common->smi_ao_base)) + return PTR_ERR(common->smi_ao_base); + common->base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(common->base)) + return PTR_ERR(common->base); + break; + case MTK_SMI_GEN1: + /* + * gen 1 needs async clock to transform the smi clock to the + * emi clock domain + */ + common->smi_ao_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(common->smi_ao_base)) + return PTR_ERR(common->smi_ao_base); + common->clk_async = devm_clk_get_enabled(dev, "async"); + if (IS_ERR(common->clk_async)) + return PTR_ERR(common->clk_async); + break; + case MTK_SMI_GEN2: + common->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(common->base)) + return PTR_ERR(common->base); + break; + case MTK_SMI_GEN2_SUB_COMM: + /* link its smi-common if this is smi-sub-common */ + ret = mtk_smi_device_link_common(dev, &common->smi_common_dev); + if (ret < 0) + return ret; + break; } pm_runtime_enable(dev); @@ -936,13 +1043,15 @@ static int __maybe_unused mtk_smi_common_resume(struct device *dev) if (ret) return ret; - if (common->plat->type != MTK_SMI_GEN2) + if (common->plat->type != MTK_SMI_GEN0 && common->plat->type != MTK_SMI_GEN2) return 0; for (i = 0; i < SMI_COMMON_INIT_REGS_NR && init && init[i].offset; i++) writel_relaxed(init[i].value, common->base + init[i].offset); - writel(bus_sel, common->base + SMI_BUS_SEL); + if (common->plat->type == MTK_SMI_GEN2) + writel(bus_sel, common->base + SMI_BUS_SEL); + return 0; } diff --git a/drivers/soc/mediatek/mt6589-dispsys.h b/drivers/soc/mediatek/mt6589-dispsys.h new file mode 100644 index 00000000000000..91e8bf5f2ca640 --- /dev/null +++ b/drivers/soc/mediatek/mt6589-dispsys.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2026 Akari Tsuyukusa + */ + +#ifndef __SOC_MEDIATEK_MT6589_DISPSYS_H +#define __SOC_MEDIATEK_MT6589_DISPSYS_H + +#include "mtk-mmsys.h" + +/* + * MT6589 display pipeline overview: + * + * Main path (OVL → LCD): + * OVL → COLOR → BLS → RDMA0 → DSI0 + * OVL → COLOR → BLS → RDMA0 → DBI + * OVL → COLOR → BLS → RDMA0 → DPI0 + * + * Memory-out path (concurrent with main path): + * OVL → WDMA1 + * + * Direct RDMA1 paths (bypass OVL/COLOR/BLS): + * RDMA1 → DPI0 + * RDMA1 → DPI1 + * + * MDP path: + * SCL → WDMA0 + * + * Routing register bit/value summary (from vendor disp_path_config_()): + * + * OVL_MOUT_EN GENMASK(2,0) bit[0]=WDMA1, bit[2]=COLOR + * COLOR_MOUT_EN GENMASK(3,0) bit[3]=BLS + * COLOR_SEL 0x1 0x1=from OVL + * BLS_SEL 0x1 0x1=from COLOR + * RDMA0_OUT_SEL 0x3 0x0=DSI0, 0x1=DBI, 0x2=DPI0 + * RDMA1_OUT_SEL 0x3 0x1=DPI0, 0x2=DPI1 + * DPI0_SEL 0x1 0x0=RDMA0, 0x1=RDMA1 + * DBI_SEL 0x1 0x0=RDMA0 + * SCL_MOUT_EN BIT(0) bit[0]=WDMA0 + * WDMA0_SEL 0x1 0x0=SCL + */ + +#define MT6589_DISP_SCL_MOUT_EN 0x020 +#define MT6589_DISP_OVL_MOUT_EN 0x024 +#define MT6589_DISP_COLOR_MOUT_EN 0x028 +#define MT6589_DISP_RDMA0_OUT_SEL 0x034 +#define MT6589_DISP_RDMA1_OUT_SEL 0x038 +#define MT6589_DISP_WDMA0_SEL_IN 0x040 +#define MT6589_DISP_COLOR_SEL_IN 0x04c +#define MT6589_DISP_BLS_SEL_IN 0x054 +#define MT6589_DISP_DPI0_SEL_IN 0x05c +#define MT6589_DISP_DBI_SEL_IN 0x058 + +/* OVL_MOUT_EN */ +#define MT6589_OVL_MOUT_EN_WDMA1 BIT(0) +#define MT6589_OVL_MOUT_EN_COLOR BIT(2) +#define MT6589_OVL_MOUT_EN_MASK GENMASK(2, 0) + +/* COLOR_MOUT_EN */ +#define MT6589_COLOR_MOUT_EN_BLS BIT(3) +#define MT6589_COLOR_MOUT_EN_MASK GENMASK(3, 0) + +/* COLOR_SEL_IN */ +#define MT6589_COLOR_SEL_IN_OVL 0x1 +#define MT6589_COLOR_SEL_IN_MASK 0x1 + +/* BLS_SEL_IN */ +#define MT6589_BLS_SEL_IN_COLOR 0x1 +#define MT6589_BLS_SEL_IN_MASK 0x1 + +/* RDMA0_SOUT_SEL */ +#define MT6589_RDMA0_SOUT_DBI 0x1 +#define MT6589_RDMA0_SOUT_DPI0 0x2 +#define MT6589_RDMA0_SOUT_MASK 0x3 + +/* RDMA1_SOUT_SEL */ +#define MT6589_RDMA1_SOUT_DPI0 0x1 +#define MT6589_RDMA1_SOUT_DPI1 0x2 +#define MT6589_RDMA1_SOUT_MASK 0x3 + +/* DPI0_SEL_IN */ +#define MT6589_DPI0_SEL_IN_RDMA1 0x1 +#define MT6589_DPI0_SEL_IN_MASK 0x1 + +/* SCL_MOUT_EN */ +#define MT6589_SCL_MOUT_EN_WDMA0 BIT(0) +#define MT6589_SCL_MOUT_EN_MASK BIT(0) + +static const struct mtk_mmsys_routes mt6589_dispsys_routing_table[] = { + /* + * Main path step 1: OVL output → COLOR + * OVL_MOUT_EN selects COLOR as the downstream engine. + * COLOR_SEL_IN confirms OVL as the upstream source. + */ + MMSYS_ROUTE(OVL0, COLOR0, + MT6589_DISP_OVL_MOUT_EN, + MT6589_OVL_MOUT_EN_MASK, MT6589_OVL_MOUT_EN_COLOR), + MMSYS_ROUTE(OVL0, COLOR0, + MT6589_DISP_COLOR_SEL_IN, + MT6589_COLOR_SEL_IN_MASK, MT6589_COLOR_SEL_IN_OVL), + + /* + * Main path step 2: COLOR output → BLS + * COLOR_MOUT_EN selects BLS as the downstream engine. + * BLS_SEL_IN confirms COLOR as the upstream source. + */ + MMSYS_ROUTE(COLOR0, BLS, + MT6589_DISP_COLOR_MOUT_EN, + MT6589_COLOR_MOUT_EN_MASK, MT6589_COLOR_MOUT_EN_BLS), + MMSYS_ROUTE(COLOR0, BLS, + MT6589_DISP_BLS_SEL_IN, + MT6589_BLS_SEL_IN_MASK, MT6589_BLS_SEL_IN_COLOR), + + /* + * Main path step 3: RDMA0 output → DSI0 / DBI / DPI0 + * BLS feeds RDMA0 via direct-link (no SEL register needed). + * RDMA0_SOUT_SEL steers RDMA0's output to the target interface. + * + * DSI0: default output selection (val=0x0); no SOUT entry needed + * as the hardware resets to DSI0. Only the MOUT/SEL entries for + * the OVL→COLOR and COLOR→BLS hops are required for this path. + */ + + /* RDMA0 → DBI */ + MMSYS_ROUTE(RDMA0, DBI0, + MT6589_DISP_RDMA0_OUT_SEL, + MT6589_RDMA0_SOUT_MASK, MT6589_RDMA0_SOUT_DBI), + + /* RDMA0 → DPI0 (OVL-sourced) */ + MMSYS_ROUTE(RDMA0, DPI0, + MT6589_DISP_RDMA0_OUT_SEL, + MT6589_RDMA0_SOUT_MASK, MT6589_RDMA0_SOUT_DPI0), + + /* + * Memory-out path: OVL → WDMA1 + * Allows screen-capture concurrently with the main LCD path. + * Enabled by setting bit[0] of OVL_MOUT_EN alongside bit[2]. + */ + MMSYS_ROUTE(OVL0, WDMA1, + MT6589_DISP_OVL_MOUT_EN, + MT6589_OVL_MOUT_EN_MASK, MT6589_OVL_MOUT_EN_WDMA1), + + /* + * Direct RDMA1 paths: bypass OVL/COLOR/BLS entirely. + * Used for external display (e.g. HDMI via bridge chip). + */ + + /* RDMA1 → DPI0 */ + MMSYS_ROUTE(RDMA1, DPI0, + MT6589_DISP_RDMA1_OUT_SEL, + MT6589_RDMA1_SOUT_MASK, MT6589_RDMA1_SOUT_DPI0), + MMSYS_ROUTE(RDMA1, DPI0, + MT6589_DISP_DPI0_SEL_IN, + MT6589_DPI0_SEL_IN_MASK, MT6589_DPI0_SEL_IN_RDMA1), + + /* RDMA1 → DPI1 */ + MMSYS_ROUTE(RDMA1, DPI1, + MT6589_DISP_RDMA1_OUT_SEL, + MT6589_RDMA1_SOUT_MASK, MT6589_RDMA1_SOUT_DPI1), + + /* + * MDP path: SCL → WDMA0 + * Used for MDP (Media Data Path) scaling + write-back. + * ROT feeds SCL; SCL_MOUT_EN routes the output to WDMA0. + * WDMA0_SEL_IN defaults to SCL (val=0x0); no SEL entry needed. + */ + MMSYS_ROUTE(SCL, WDMA0, + MT6589_DISP_SCL_MOUT_EN, + MT6589_SCL_MOUT_EN_MASK, MT6589_SCL_MOUT_EN_WDMA0), +}; + +#endif /* __SOC_MEDIATEK_MT6589_DISPSYS_H */ diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index bb4639ca0b8cdc..5597fceb26ace1 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -14,6 +14,7 @@ #include #include "mtk-mmsys.h" +#include "mt6589-dispsys.h" #include "mt8167-mmsys.h" #include "mt8173-mmsys.h" #include "mt8183-mmsys.h" @@ -37,6 +38,12 @@ static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = { .num_routes = ARRAY_SIZE(mmsys_default_routing_table), }; +static const struct mtk_mmsys_driver_data mt6572_dispsys_driver_data = { + .clk_driver = "clk-mt6589-disp", + .routes = mt6589_dispsys_routing_table, + .num_routes = ARRAY_SIZE(mt6589_dispsys_routing_table), +}; + static const struct mtk_mmsys_driver_data mt6779_mmsys_driver_data = { .clk_driver = "clk-mt6779-mm", }; @@ -458,6 +465,7 @@ static void mtk_mmsys_remove(struct platform_device *pdev) static const struct of_device_id of_match_mtk_mmsys[] = { { .compatible = "mediatek,mt2701-mmsys", .data = &mt2701_mmsys_driver_data }, { .compatible = "mediatek,mt2712-mmsys", .data = &mt2712_mmsys_driver_data }, + { .compatible = "mediatek,mt6589-dispsys", .data = &mt6572_dispsys_driver_data }, { .compatible = "mediatek,mt6779-mmsys", .data = &mt6779_mmsys_driver_data }, { .compatible = "mediatek,mt6795-mmsys", .data = &mt6795_mmsys_driver_data }, { .compatible = "mediatek,mt6797-mmsys", .data = &mt6797_mmsys_driver_data }, diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index 38179e8cd98f84..c383ff089be6ed 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -397,6 +397,20 @@ static const u8 mt2712_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_WDMA1] = MT2712_MUTEX_MOD_DISP_WDMA1, }; +static const u8 mt6589_mutex_mod[DDP_COMPONENT_ID_MAX] = { + [DDP_COMPONENT_ROT] = 0, + [DDP_COMPONENT_SCL] = 1, + [DDP_COMPONENT_OVL0] = 2, + [DDP_COMPONENT_COLOR0] = 3, + [DDP_COMPONENT_2DSHARP] = 4, + [DDP_COMPONENT_WDMA0] = 5, + [DDP_COMPONENT_WDMA1] = 6, + [DDP_COMPONENT_RDMA0] = 7, + [DDP_COMPONENT_RDMA1] = 8, + [DDP_COMPONENT_BLS] = 9, + [DDP_COMPONENT_GAMMA] = 10, +}; + static const u8 mt8167_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_AAL0] = MT8167_MUTEX_MOD_DISP_AAL, [DDP_COMPONENT_CCORR] = MT8167_MUTEX_MOD_DISP_CCORR, @@ -746,6 +760,13 @@ static const struct mtk_mutex_data mt2712_mutex_driver_data = { .mutex_sof_reg = MT2701_MUTEX0_SOF0, }; +static const struct mtk_mutex_data mt6589_mutex_driver_data = { + .mutex_mod = mt6589_mutex_mod, + .mutex_sof = mt8167_mutex_sof, + .mutex_mod_reg = MT2701_MUTEX0_MOD0, + .mutex_sof_reg = MT2701_MUTEX0_SOF0, +}; + static const struct mtk_mutex_data mt6795_mutex_driver_data = { .mutex_mod = mt8173_mutex_mod, .mutex_sof = mt6795_mutex_sof, @@ -1132,6 +1153,7 @@ static int mtk_mutex_probe(struct platform_device *pdev) static const struct of_device_id mutex_driver_dt_match[] = { { .compatible = "mediatek,mt2701-disp-mutex", .data = &mt2701_mutex_driver_data }, { .compatible = "mediatek,mt2712-disp-mutex", .data = &mt2712_mutex_driver_data }, + { .compatible = "mediatek,mt6589-disp-mutex", .data = &mt6589_mutex_driver_data }, { .compatible = "mediatek,mt6795-disp-mutex", .data = &mt6795_mutex_driver_data }, { .compatible = "mediatek,mt8167-disp-mutex", .data = &mt8167_mutex_driver_data }, { .compatible = "mediatek,mt8173-disp-mutex", .data = &mt8173_mutex_driver_data }, diff --git a/include/dt-bindings/memory/mt2701-larb-port.h b/include/dt-bindings/memory/mt2701-larb-port.h index 25d03526f142a2..dc2c98baf7891d 100644 --- a/include/dt-bindings/memory/mt2701-larb-port.h +++ b/include/dt-bindings/memory/mt2701-larb-port.h @@ -15,14 +15,14 @@ * But m4u generation 2 like mt8173 have different port number, it use fixed * offset for each larb, the first port's id for larb[N] would be (N * 32). */ -#define LARB0_PORT_OFFSET 0 -#define LARB1_PORT_OFFSET 11 -#define LARB2_PORT_OFFSET 21 -#define LARB3_PORT_OFFSET 44 +#define MT2701_LARB0_PORT_OFFSET 0 +#define MT2701_LARB1_PORT_OFFSET 11 +#define MT2701_LARB2_PORT_OFFSET 21 +#define MT2701_LARB3_PORT_OFFSET 44 -#define MT2701_M4U_ID_LARB0(port) ((port) + LARB0_PORT_OFFSET) -#define MT2701_M4U_ID_LARB1(port) ((port) + LARB1_PORT_OFFSET) -#define MT2701_M4U_ID_LARB2(port) ((port) + LARB2_PORT_OFFSET) +#define MT2701_M4U_ID_LARB0(port) ((port) + MT2701_LARB0_PORT_OFFSET) +#define MT2701_M4U_ID_LARB1(port) ((port) + MT2701_LARB1_PORT_OFFSET) +#define MT2701_M4U_ID_LARB2(port) ((port) + MT2701_LARB2_PORT_OFFSET) /* Port define for larb0 */ #define MT2701_M4U_PORT_DISP_OVL_0 MT2701_M4U_ID_LARB0(0) diff --git a/include/dt-bindings/memory/mt6589-larb-port.h b/include/dt-bindings/memory/mt6589-larb-port.h new file mode 100644 index 00000000000000..9dc0183f5402ad --- /dev/null +++ b/include/dt-bindings/memory/mt6589-larb-port.h @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 Akari Tsuyukusa + */ + +#ifndef _DT_BINDINGS_MEMORY_MT6589_LARB_PORT_H_ +#define _DT_BINDINGS_MEMORY_MT6589_LARB_PORT_H_ + +#define MT6589_LARB0_PORT_OFFSET 0 +#define MT6589_LARB1_PORT_OFFSET 10 +#define MT6589_LARB2_PORT_OFFSET 17 +#define MT6589_LARB3_PORT_OFFSET 29 +#define MT6589_LARB4_PORT_OFFSET 43 +#define MT6589_LARB5_PORT_OFFSET 53 + +#define MT6589_M4U_ID_LARB0(n) ((n) + MT6589_LARB0_PORT_OFFSET) +#define MT6589_M4U_ID_LARB1(n) ((n) + MT6589_LARB1_PORT_OFFSET) +#define MT6589_M4U_ID_LARB2(n) ((n) + MT6589_LARB2_PORT_OFFSET) +#define MT6589_M4U_ID_LARB3(n) ((n) + MT6589_LARB3_PORT_OFFSET) +#define MT6589_M4U_ID_LARB4(n) ((n) + MT6589_LARB4_PORT_OFFSET) +#define MT6589_M4U_ID_LARB5(n) ((n) + MT6589_LARB5_PORT_OFFSET) + +/* larb0 */ +#define MT6589_M4U_PORT_VENC_RCPU MT6589_M4U_ID_LARB0(0) +#define MT6589_M4U_PORT_VENC_REF_LUMA MT6589_M4U_ID_LARB0(1) +#define MT6589_M4U_PORT_VENC_REF_CHROMA MT6589_M4U_ID_LARB0(2) +#define MT6589_M4U_PORT_VENC_DB_READ MT6589_M4U_ID_LARB0(3) +#define MT6589_M4U_PORT_VENC_DB_WRITE MT6589_M4U_ID_LARB0(4) +#define MT6589_M4U_PORT_VENC_CUR_LUMA MT6589_M4U_ID_LARB0(5) +#define MT6589_M4U_PORT_VENC_CUR_CHROMA MT6589_M4U_ID_LARB0(6) +#define MT6589_M4U_PORT_VENC_RD_COMV MT6589_M4U_ID_LARB0(7) +#define MT6589_M4U_PORT_VENC_SV_COMV MT6589_M4U_ID_LARB0(8) +#define MT6589_M4U_PORT_VENC_BSDMA MT6589_M4U_ID_LARB0(9) + +/* larb1 */ +#define MT6589_M4U_PORT_HW_VDEC_MC_EXT MT6589_M4U_ID_LARB1(0) +#define MT6589_M4U_PORT_HW_VDEC_PP_EXT MT6589_M4U_ID_LARB1(1) +#define MT6589_M4U_PORT_HW_VDEC_AVC_MV_EXT MT6589_M4U_ID_LARB1(2) +#define MT6589_M4U_PORT_HW_VDEC_PRED_RD_EXT MT6589_M4U_ID_LARB1(3) +#define MT6589_M4U_PORT_HW_VDEC_PRED_WR_EXT MT6589_M4U_ID_LARB1(4) +#define MT6589_M4U_PORT_HW_VDEC_VLD_EXT MT6589_M4U_ID_LARB1(5) +#define MT6589_M4U_PORT_HW_VDEC_VLD2_EXT MT6589_M4U_ID_LARB1(6) + +/* larb2 */ +#define MT6589_M4U_PORT_ROT_EXT MT6589_M4U_ID_LARB2(0) +#define MT6589_M4U_PORT_OVL_CH0 MT6589_M4U_ID_LARB2(1) +#define MT6589_M4U_PORT_OVL_CH1 MT6589_M4U_ID_LARB2(2) +#define MT6589_M4U_PORT_OVL_CH2 MT6589_M4U_ID_LARB2(3) +#define MT6589_M4U_PORT_OVL_CH3 MT6589_M4U_ID_LARB2(4) +#define MT6589_M4U_PORT_WDMA0 MT6589_M4U_ID_LARB2(5) +#define MT6589_M4U_PORT_WDMA1 MT6589_M4U_ID_LARB2(6) +#define MT6589_M4U_PORT_RDMA0 MT6589_M4U_ID_LARB2(7) +#define MT6589_M4U_PORT_RDMA1 MT6589_M4U_ID_LARB2(8) +#define MT6589_M4U_PORT_CMDQ MT6589_M4U_ID_LARB2(9) +#define MT6589_M4U_PORT_DBI MT6589_M4U_ID_LARB2(10) +#define MT6589_M4U_PORT_G2D MT6589_M4U_ID_LARB2(11) + +/* larb3 */ +#define MT6589_M4U_PORT_JPGDEC_WDMA MT6589_M4U_ID_LARB3(0) +#define MT6589_M4U_PORT_JPGENC_RDMA MT6589_M4U_ID_LARB3(1) +#define MT6589_M4U_PORT_VIPI MT6589_M4U_ID_LARB3(2) +#define MT6589_M4U_PORT_IMGI MT6589_M4U_ID_LARB3(3) +#define MT6589_M4U_PORT_DISPO MT6589_M4U_ID_LARB3(4) +#define MT6589_M4U_PORT_DISPCO MT6589_M4U_ID_LARB3(5) +#define MT6589_M4U_PORT_DISPVO MT6589_M4U_ID_LARB3(6) +#define MT6589_M4U_PORT_VIDO MT6589_M4U_ID_LARB3(7) +#define MT6589_M4U_PORT_VIDCO MT6589_M4U_ID_LARB3(8) +#define MT6589_M4U_PORT_VIDVO MT6589_M4U_ID_LARB3(9) +#define MT6589_M4U_PORT_VIP2I MT6589_M4U_ID_LARB3(10) +#define MT6589_M4U_PORT_GDMA_SMI_WR MT6589_M4U_ID_LARB3(11) +#define MT6589_M4U_PORT_JPGDEC_BSDMA MT6589_M4U_ID_LARB3(12) +#define MT6589_M4U_PORT_JPGENC_BSDMA MT6589_M4U_ID_LARB3(13) + +/* larb4 */ +#define MT6589_M4U_PORT_GDMA_SMI_RD MT6589_M4U_ID_LARB4(0) +#define MT6589_M4U_PORT_IMGCI MT6589_M4U_ID_LARB4(1) +#define MT6589_M4U_PORT_IMGO MT6589_M4U_ID_LARB4(2) +#define MT6589_M4U_PORT_IMG2O MT6589_M4U_ID_LARB4(3) +#define MT6589_M4U_PORT_LSCI MT6589_M4U_ID_LARB4(4) +#define MT6589_M4U_PORT_FLKI MT6589_M4U_ID_LARB4(5) +#define MT6589_M4U_PORT_LCEI MT6589_M4U_ID_LARB4(6) +#define MT6589_M4U_PORT_LCSO MT6589_M4U_ID_LARB4(7) +#define MT6589_M4U_PORT_ESFKO MT6589_M4U_ID_LARB4(8) +#define MT6589_M4U_PORT_AAO MT6589_M4U_ID_LARB4(9) + +/* larb5 */ +#define MT6589_M4U_PORT_AUDIO MT6589_M4U_ID_LARB5(0) + +#endif diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h index 4885b065b849fb..d1b258510550b5 100644 --- a/include/linux/soc/mediatek/mtk-mmsys.h +++ b/include/linux/soc/mediatek/mtk-mmsys.h @@ -21,6 +21,7 @@ enum mtk_dpi_out_format_con { }; enum mtk_ddp_comp_id { + DDP_COMPONENT_2DSHARP, DDP_COMPONENT_AAL0, DDP_COMPONENT_AAL1, DDP_COMPONENT_BLS, @@ -78,9 +79,12 @@ enum mtk_ddp_comp_id { DDP_COMPONENT_RDMA1, DDP_COMPONENT_RDMA2, DDP_COMPONENT_RDMA4, + DDP_COMPONENT_ROT, + DDP_COMPONENT_SCL, DDP_COMPONENT_UFOE, DDP_COMPONENT_WDMA0, DDP_COMPONENT_WDMA1, + DDP_COMPONENT_DBI0, DDP_COMPONENT_ID_MAX, }; From 74c981578bf0dc5579f0200ac9e6c11338c5da9e Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sat, 4 Jul 2026 22:23:29 +0900 Subject: [PATCH 02/33] memory: mtk-smi: add bus_sel to MT6589 Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/memory/mtk-smi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index ad396d5aea5ff7..95a71be42bce34 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -856,6 +856,8 @@ static const struct mtk_smi_common_plat mtk_smi_common_gen2 = { static const struct mtk_smi_common_plat mtk_smi_common_mt6589 = { .type = MTK_SMI_GEN0, .init = mtk_smi_common_mt6589_init, + /* LARB0→MMU0, LARB1→MMU1, LARB2→MMU0, LARB3→MMU1, LARB4→MMU1 */ + .bus_sel = 0x110, }; static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = { @@ -1049,7 +1051,7 @@ static int __maybe_unused mtk_smi_common_resume(struct device *dev) for (i = 0; i < SMI_COMMON_INIT_REGS_NR && init && init[i].offset; i++) writel_relaxed(init[i].value, common->base + init[i].offset); - if (common->plat->type == MTK_SMI_GEN2) + if (common->plat->bus_sel) writel(bus_sel, common->base + SMI_BUS_SEL); return 0; From 153dfd5c41c84bf4ee36a183f464474b183ad3a0 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sat, 4 Jul 2026 22:48:27 +0900 Subject: [PATCH 03/33] dt-bindings: iommu: mediatek: complete mt6589 Signed-off-by: Akari Tsuyukusa --- .../bindings/iommu/mediatek,iommu.yaml | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml index 67107e42358b65..94f48005fd6582 100644 --- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml @@ -72,7 +72,7 @@ properties: - enum: - mediatek,mt2701-m4u # generation one - mediatek,mt2712-m4u # generation two - - mediatek,mt6589-m4u + - mediatek,mt6589-m4u # generation one (dual core) - mediatek,mt6779-m4u # generation two - mediatek,mt6795-m4u # generation two - mediatek,mt6893-iommu-mm # generation two @@ -98,10 +98,20 @@ properties: - const: mediatek,mt2701-m4u reg: - maxItems: 1 + minItems: 1 + maxItems: 4 # MT6589 needs 4 regions (global, m4u0, m4u1, l2cache) + + reg-names: + minItems: 1 + maxItems: 4 interrupts: - maxItems: 1 + minItems: 1 + maxItems: 2 # MT6589 has one IRQ per core + + interrupt-names: + minItems: 1 + maxItems: 2 clocks: items: @@ -134,6 +144,7 @@ properties: dt-binding/memory/mediatek,mt8188-memory-port.h for mt8188, dt-binding/memory/mediatek,mt8189-memory-port.h for mt8189, dt-binding/memory/mt2701-larb-port.h for mt2701 and mt7623, + dt-binding/memory/mt6589-larb-port.h for mt6589, dt-binding/memory/mt2712-larb-port.h for mt2712, dt-binding/memory/mt6779-larb-port.h for mt6779, dt-binding/memory/mt6795-larb-port.h for mt6795, @@ -156,6 +167,42 @@ required: - '#iommu-cells' allOf: + - if: + properties: + compatible: + not: + contains: + const: mediatek,mt6589-m4u + then: + properties: + reg: + maxItems: 1 + interrupts: + maxItems: 1 + required: + - reg + else: + properties: + reg: + minItems: 4 + maxItems: 4 + interrupts: + minItems: 2 + maxItems: 2 + reg-names: + items: + - const: global + - const: m4u0 + - const: m4u1 + - const: l2cache + interrupt-names: + items: + - const: m4u0 + - const: m4u1 + required: + - reg-names + - interrupt-names + - if: properties: compatible: @@ -163,6 +210,7 @@ allOf: enum: - mediatek,mt2701-m4u - mediatek,mt2712-m4u + - mediatek,mt6589-m4u - mediatek,mt6795-m4u - mediatek,mt6893-iommu-mm - mediatek,mt8173-m4u @@ -241,3 +289,23 @@ examples: <&larb3>, <&larb4>, <&larb5>; #iommu-cells = <1>; }; + + - | + #include + + iommu@10208000 { + compatible = "mediatek,mt6589-m4u"; + reg = <0x10205000 0x200>, + <0x10205200 0x600>, + <0x10205800 0x600>, + <0x10205100 0x100>; + reg-names = "global", "m4u0", "m4u1", "l2cache"; + interrupts = , + ; + interrupt-names = "m4u0", "m4u1"; + clocks = <&infracfg CLK_INFRA_M4U>; + clock-names = "bclk"; + mediatek,larbs = <&larb0>, <&larb1>, <&larb2>, + <&larb3>, <&larb4>, <&larb5>; + #iommu-cells = <1>; + }; From a1f7b04e7ea2db0b2eedc7229961137cb225a484 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sat, 4 Jul 2026 22:53:34 +0900 Subject: [PATCH 04/33] arm: dts: mediatek: mt6589: fix m4u node Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index 6ea8b76af9102b..f9332d0c999720 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -429,15 +429,17 @@ iommu: iommu@10205000 { compatible = "mediatek,mt6589-m4u"; - reg = <0x10205000 0x200>, + reg = <0x10205000 0x100>, <0x10205200 0x600>, - <0x10205800 0x600>; - reg-names = "global", "m4u0", "m4u1"; + <0x10205800 0x600>, + <0x10205100 0x100>; + reg-names = "global", "m4u0", "m4u1", "l2cache"; interrupts = , , , ; interrupt-names = "m4u0", "m4u1", "m4ul2", "m4ul2-sec"; + /* m4ul2 and m4ul2-sec are not used by driver */ clocks = <&infracfg CLK_INFRA_M4U>; clock-names = "bclk"; mediatek,larbs = <&larb0>, <&larb1>, <&larb2>, From 0c77015b157ecb5f4d5baeac7b0cec807ab1539c Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sat, 4 Jul 2026 23:22:42 +0900 Subject: [PATCH 05/33] memory: mtk-smi: add MT6589 specific init Link: https://github.com/TeamYogaBlade2/linux/pull/63#issuecomment-4882342137 Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/memory/mtk-smi.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 95a71be42bce34..53ac9d77ced51c 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -60,6 +60,10 @@ #define SMI_LARB_OSTDL_PORT 0x200 #define SMI_LARB_OSTDL_PORTx(id) (SMI_LARB_OSTDL_PORT + (((id) & 0x1f) << 2)) +/* mt6589 */ +#define SMI_LARB_SHARE_EN 0x210 +#define SMI_LARB_ROUTE_SEL 0x220 + #define SMI_LARB_FIFO_STAT0 0x600 /* Below are about mmu enable registers, they are different in SoCs */ @@ -265,6 +269,38 @@ static int mtk_smi_larb_config_port_gen0(struct device *dev) return 0; } +static int mtk_smi_larb_config_port_mt6589(struct device *dev) +{ + struct mtk_smi_larb *larb = dev_get_drvdata(dev); + int ret; + + /* Execute the common Gen0 port configuration first */ + ret = mtk_smi_larb_config_port_gen0(dev); + if (ret) + return ret; + + /* + * MT6589 requires additional LARB-local settings: + * - Disable share for all ports + * - Route specific ports to EMI instead of MCI + */ + writel_relaxed(0x0, larb->base + SMI_LARB_SHARE_EN); + + switch (larb->larbid) { + case 2: + writel_relaxed(0x1e, larb->base + SMI_LARB_ROUTE_SEL); + break; + case 4: + writel_relaxed(0xffffffff, larb->base + SMI_LARB_ROUTE_SEL); + break; + default: + writel_relaxed(0x0, larb->base + SMI_LARB_ROUTE_SEL); + break; + } + + return 0; +} + static int mtk_smi_larb_config_port_gen1(struct device *dev) { struct mtk_smi_larb *larb = dev_get_drvdata(dev); @@ -572,7 +608,7 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = { static const struct mtk_smi_larb_gen mtk_smi_larb_mt6589 = { .port_in_larb = { 0, 10, 17, 29, 44 ,56 }, - .config_port = mtk_smi_larb_config_port_gen0, + .config_port = mtk_smi_larb_config_port_mt6589, .flags_general = MTK_SMI_FLAG_BW_CALIBRATE, }; From 32dcd5cf734dfda9c2ee2e0056976eb40ac41d09 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 5 Jul 2026 02:21:49 +0900 Subject: [PATCH 06/33] dt-bindings: memory: mt6589-larb-port: add MT6589_M4U_PORT_NR Signed-off-by: Akari Tsuyukusa --- include/dt-bindings/memory/mt6589-larb-port.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/memory/mt6589-larb-port.h b/include/dt-bindings/memory/mt6589-larb-port.h index 9dc0183f5402ad..ae0acf225e13be 100644 --- a/include/dt-bindings/memory/mt6589-larb-port.h +++ b/include/dt-bindings/memory/mt6589-larb-port.h @@ -84,6 +84,9 @@ #define MT6589_M4U_PORT_AAO MT6589_M4U_ID_LARB4(9) /* larb5 */ +/* xxx: is it really larb? */ #define MT6589_M4U_PORT_AUDIO MT6589_M4U_ID_LARB5(0) +#define MT6589_M4U_PORT_NR 54 + #endif From f3b9a50e194cf0f07aae0cca7d11413f2012febf Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 5 Jul 2026 02:22:55 +0900 Subject: [PATCH 07/33] iommu: mtk-v1: add MT6589 support Assited-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/iommu/mtk_iommu_v1.c | 648 ++++++++++++++++++++++++++++------- 1 file changed, 530 insertions(+), 118 deletions(-) diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index 591ffa66a72839..64da45cec8f33b 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -7,6 +7,7 @@ * * Based on driver/iommu/mtk_iommu.c */ +#include #include #include #include @@ -26,9 +27,11 @@ #include #include #include +#include #include #include #include +#include #include #if defined(CONFIG_ARM) @@ -51,6 +54,7 @@ struct dma_iommu_mapping { #define F_MMU_FAULT_VA_MSK 0xfffff000 #define MTK_PROTECT_PA_ALIGN 128 +/* -------- Common M4U v1 register definitions (MT2701 and MT6589 core) -------- */ #define REG_MMU_CTRL_REG 0x210 #define F_MMU_CTRL_COHERENT_EN BIT(8) #define REG_MMU_IVRP_PADDR 0x214 @@ -71,6 +75,8 @@ struct dma_iommu_mapping { #define REG_MMU_FAULT_VA 0x228 #define REG_MMU_INVLD_PA 0x22C #define REG_MMU_INT_ID 0x388 + +/* MT2701 specific (core space) */ #define REG_MMU_INVALIDATE 0x5c0 #define REG_MMU_INVLD_START_A 0x5c4 #define REG_MMU_INVLD_END_A 0x5c8 @@ -81,14 +87,66 @@ struct dma_iommu_mapping { #define REG_MMU_DCM 0x5f0 #define F_MMU_DCM_ON BIT(1) #define REG_MMU_CPE_DONE 0x60c + +/* MT6589 global space registers */ +#define REG_MMUg_CTRL 0x00 +#define F_MMUg_CTRL_INV_EN0 BIT(0) +#define F_MMUg_CTRL_INV_EN1 BIT(1) +#define F_MMUg_CTRL_INV_EN2 BIT(2) /* L2 */ +#define F_MMUg_CTRL_PRE_LOCK(en) ((en) ? BIT(3) : 0) +#define F_MMUg_CTRL_PRE_EN BIT(4) + +#define REG_MMUg_INVLD 0x04 +#define F_MMUg_INV_ALL 0x2 +#define F_MMUg_INV_RANGE 0x1 + +#define REG_MMUg_INVLD_SA 0x08 +#define REG_MMUg_INVLD_EA 0x0C +#define REG_MMUg_PT_BASE 0x10 +#define F_MMUg_PT_VA_MSK 0xffff0000 + +#define REG_MMUg_L2_SEL 0x18 +#define F_MMUg_L2_SEL_FLUSH_EN(en) ((en) ? BIT(3) : 0) +#define F_MMUg_L2_SEL_L2_ULTRA(en) ((en) ? BIT(2) : 0) +#define F_MMUg_L2_SEL_L2_SHARE(en) ((en) ? BIT(1) : 0) +#define F_MMUg_L2_SEL_L2_BUS_SEL(go_emi) ((go_emi) ? BIT(0) : 0) + +#define REG_MMUg_DCM 0x1C +#define F_MMUg_DCM_ON(on) ((on) ? BIT(0) : 0) + +/* L2 cache registers (MT6589) */ +#define REG_L2_GDC_STATE 0x00 +#define F_L2_GDC_ST_EVENT_MSK GENMASK(7,6) +#define F_L2_GDC_ST_EVENT_VAL(val) (((val) & 0x3) << 6) + +#define REG_L2_GDC_OP 0x04 +#define F_L2_GDC_BYPASS(en) ((en) ? BIT(10) : 0) +#define F_L2_GDC_PERF_MASK(msk) (((msk) & 0x7) << 7) +#define GDC_PERF_MASK_HIT_MISS 0 +#define F_L2_GDC_LOCK_ALERT_DIS(dis) ((dis) ? BIT(6) : 0) +#define F_L2_GDC_PERF_EN(en) ((en) ? BIT(5) : 0) +#define F_L2_GDC_LOCK_TH(th) (((th) & 0x3) << 2) +#define F_L2_GDC_PAUSE_OP(op) ((op) & 0x3) +#define GDC_NO_PAUSE 0 + +#define REG_L2_GPE_STATUS 0x18 +#define F_L2_GPE_ST_RANGE_INV_DONE BIT(1) +#define F_L2_GPE_ST_PREFETCH_DONE BIT(0) + +/* MT6589 core PFH distance / direction registers */ +#define REG_MMU_PFH_DIST(port) (0x80 + (((port) >> 3) << 2)) +#define F_MMU_PFH_DIST_VAL(port, val) (((val) & 0xf) << (((port) & 0x7) << 2)) +#define F_MMU_PFH_DIST_MASK(port) F_MMU_PFH_DIST_VAL(port, 0xf) + +#define REG_MMU_PFH_DIR(port) (((port) < 32) ? 0xF0 : 0xF4) +#define F_MMU_PFH_DIR(port, val) ((!!(val)) << ((port) & 0x1f)) + +/* Common page table descriptor bits */ #define F_DESC_VALID 0x2 #define F_DESC_NONSEC BIT(3) -#define MT2701_M4U_TF_LARB(TF) (6 - (((TF) >> 13) & 0x7)) -#define MT2701_M4U_TF_PORT(TF) (((TF) >> 8) & 0xF) /* MTK generation one iommu HW only support 4K size mapping */ #define MT2701_IOMMU_PAGE_SHIFT 12 #define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT) -#define MT2701_LARB_NR_MAX 3 /* * MTK m4u support 4GB iova address space, and only support 4K page @@ -96,26 +154,72 @@ struct dma_iommu_mapping { */ #define M2701_IOMMU_PGT_SIZE SZ_4M +#define MAX_M4U_CORES 2 + +struct mtk_iommu_v1_data; + +struct mtk_iommu_v1_soc_data { + const char *compatible; + unsigned int num_cores; + bool has_global_base; + bool has_l2_cache; + + void (*tlb_flush_all)(struct mtk_iommu_v1_data *data); + void (*tlb_flush_range)(struct mtk_iommu_v1_data *data, + unsigned long iova, size_t size); + + void (*get_fault_larb_port)(u32 int_id, unsigned int *larb, + unsigned int *port); + + int (*hw_init)(struct mtk_iommu_v1_data *data); + + u32 pt_base_reg_offset; + bool pt_base_in_global; + + const int *larb_port_offsets; + unsigned int num_larb; +}; + +struct mtk_iommu_v1_core { + void __iomem *base; + int irq; + struct mtk_iommu_v1_data *data; + unsigned int id; +}; + struct mtk_iommu_v1_suspend_reg { + /* MT2701 fields */ u32 standard_axi_mode; u32 dcm_dis; u32 ctrl_reg; u32 int_control0; + + /* MT6589 additional fields */ + u32 mmug_ctrl; + u32 mmug_pt_base; + u32 mmug_l2_sel; + u32 mmug_dcm; + u32 l2_gdc_op; }; struct mtk_iommu_v1_data { - void __iomem *base; - int irq; - struct device *dev; - struct clk *bclk; - phys_addr_t protect_base; /* protect memory base */ - struct mtk_iommu_v1_domain *m4u_dom; - - struct iommu_device iommu; - struct dma_iommu_mapping *mapping; - struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX]; - - struct mtk_iommu_v1_suspend_reg reg; + const struct mtk_iommu_v1_soc_data *soc; + struct device *dev; + + struct mtk_iommu_v1_core cores[MAX_M4U_CORES]; + void __iomem *global_base; + void __iomem *l2_base; + + struct clk *bclk; + phys_addr_t protect_base; + struct mtk_iommu_v1_domain *m4u_dom; + + struct iommu_device iommu; + struct dma_iommu_mapping *mapping; + struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX]; + + struct mtk_iommu_v1_suspend_reg reg; + struct page *dummy_page; /* Physical address of guard dummy page */ }; struct mtk_iommu_v1_domain { @@ -150,73 +254,143 @@ static const int mt2701_m4u_in_larb[] = { MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET }; -static inline int mt2701_m4u_to_larb(int id) +static const int mt6589_m4u_in_larb[] = { + MT6589_LARB0_PORT_OFFSET, MT6589_LARB1_PORT_OFFSET, + MT6589_LARB2_PORT_OFFSET, MT6589_LARB3_PORT_OFFSET, + MT6589_LARB4_PORT_OFFSET, MT6589_LARB5_PORT_OFFSET +}; + +static inline int mtk_iommu_v1_to_larb(struct mtk_iommu_v1_data *data, int id) { + const int *offsets = data->soc->larb_port_offsets; + int num = data->soc->num_larb; int i; - for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--) - if ((id) >= mt2701_m4u_in_larb[i]) + for (i = num - 1; i >= 0; i--) + if (id >= offsets[i]) return i; return 0; } -static inline int mt2701_m4u_to_port(int id) +static inline int mtk_iommu_v1_to_port(struct mtk_iommu_v1_data *data, int id) { - int larb = mt2701_m4u_to_larb(id); + int larb = mtk_iommu_v1_to_larb(data, id); + return id - data->soc->larb_port_offsets[larb]; +} - return id - mt2701_m4u_in_larb[larb]; +static inline void m4u_set_field(void __iomem *reg, u32 mask, u32 val) +{ + u32 regval = readl_relaxed(reg); + regval = (regval & ~mask) | val; + writel_relaxed(regval, reg); } -static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data) +/* MT2701 (single core, no global space) */ +static void mt2701_tlb_flush_all(struct mtk_iommu_v1_data *data) { - writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, - data->base + REG_MMU_INV_SEL); - writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); - wmb(); /* Make sure the tlb flush all done */ + void __iomem *base = data->cores[0].base; + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL); + writel_relaxed(F_ALL_INVLD, base + REG_MMU_INVALIDATE); + wmb(); } -static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data, - unsigned long iova, size_t size) +static void mt2701_tlb_flush_range(struct mtk_iommu_v1_data *data, + unsigned long iova, size_t size) { - int ret; + void __iomem *base = data->cores[0].base; u32 tmp; + int ret; - writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, - data->base + REG_MMU_INV_SEL); - writel_relaxed(iova & F_MMU_FAULT_VA_MSK, - data->base + REG_MMU_INVLD_START_A); + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL); + writel_relaxed(iova & F_MMU_FAULT_VA_MSK, base + REG_MMU_INVLD_START_A); writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK, - data->base + REG_MMU_INVLD_END_A); - writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); + base + REG_MMU_INVLD_END_A); + writel_relaxed(F_MMU_INV_RANGE, base + REG_MMU_INVALIDATE); - ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, - tmp, tmp != 0, 10, 100000); + ret = readl_poll_timeout_atomic(base + REG_MMU_CPE_DONE, + tmp, tmp != 0, 10, 100000); if (ret) { dev_warn(data->dev, "Partial TLB flush timed out, falling back to full flush\n"); - mtk_iommu_v1_tlb_flush_all(data); + mt2701_tlb_flush_all(data); + } + writel_relaxed(0, base + REG_MMU_CPE_DONE); +} + +/* MT6589 (global control, L2) */ +static void mt6589_tlb_flush_all(struct mtk_iommu_v1_data *data) +{ + u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1; + if (data->l2_base) + reg |= F_MMUg_CTRL_INV_EN2; + + writel_relaxed(reg, data->global_base + REG_MMUg_CTRL); + writel_relaxed(F_MMUg_INV_ALL, data->global_base + REG_MMUg_INVLD); + + if (data->l2_base) { + u32 event; + readl_poll_timeout_atomic(data->l2_base + REG_L2_GDC_STATE, + event, + event & F_L2_GDC_ST_EVENT_MSK, + 10, 100000); + writel_relaxed(0, data->l2_base + REG_L2_GDC_STATE); } - /* Clear the CPE status */ - writel_relaxed(0, data->base + REG_MMU_CPE_DONE); +} + +static void mt6589_tlb_flush_range(struct mtk_iommu_v1_data *data, + unsigned long iova, size_t size) +{ + u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1; + if (data->l2_base) + reg |= F_MMUg_CTRL_INV_EN2; + + writel_relaxed(reg, data->global_base + REG_MMUg_CTRL); + writel_relaxed(iova & F_MMU_FAULT_VA_MSK, + data->global_base + REG_MMUg_INVLD_SA); + writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK, + data->global_base + REG_MMUg_INVLD_EA); + writel_relaxed(F_MMUg_INV_RANGE, data->global_base + REG_MMUg_INVLD); + + if (data->l2_base) { + u32 status; + readl_poll_timeout_atomic(data->l2_base + REG_L2_GPE_STATUS, + status, + status & F_L2_GPE_ST_RANGE_INV_DONE, + 10, 100000); + writel_relaxed(0, data->l2_base + REG_L2_GPE_STATUS); + } +} + +static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb, + unsigned int *port) +{ + *larb = 6 - ((int_id >> 13) & 0x7); + *port = (int_id >> 8) & 0xF; +} + +static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb, + unsigned int *port) +{ + *larb = 6 - (int_id >> 12) & 0x7; + *port = (int_id >> 8) & 0xF; } static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id) { - struct mtk_iommu_v1_data *data = dev_id; + struct mtk_iommu_v1_core *core = dev_id; + struct mtk_iommu_v1_data *data = core->data; struct mtk_iommu_v1_domain *dom = data->m4u_dom; u32 int_state, regval, fault_iova, fault_pa; unsigned int fault_larb, fault_port; /* Read error information from registers */ - int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST); - fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA); + int_state = readl_relaxed(core->base + REG_MMU_FAULT_ST); + fault_iova = readl_relaxed(core->base + REG_MMU_FAULT_VA) & F_MMU_FAULT_VA_MSK; + fault_pa = readl_relaxed(core->base + REG_MMU_INVLD_PA); + regval = readl_relaxed(core->base + REG_MMU_INT_ID); - fault_iova &= F_MMU_FAULT_VA_MSK; - fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA); - regval = readl_relaxed(data->base + REG_MMU_INT_ID); - fault_larb = MT2701_M4U_TF_LARB(regval); - fault_port = MT2701_M4U_TF_PORT(regval); + data->soc->get_fault_larb_port(regval, &fault_larb, &fault_port); /* * MTK v1 iommu HW could not determine whether the fault is read or @@ -225,16 +399,16 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id) if (report_iommu_fault(&dom->domain, data->dev, fault_iova, IOMMU_FAULT_READ)) dev_err_ratelimited(data->dev, - "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n", + "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d core=%d\n", int_state, fault_iova, fault_pa, - fault_larb, fault_port); + fault_larb, fault_port, core->id); /* Interrupt clear */ - regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL); + regval = readl_relaxed(core->base + REG_MMU_INT_CONTROL); regval |= F_INT_CLR_BIT; - writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); + writel_relaxed(regval, core->base + REG_MMU_INT_CONTROL); - mtk_iommu_v1_tlb_flush_all(data); + data->soc->tlb_flush_all(data); return IRQ_HANDLED; } @@ -242,14 +416,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id) static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data, struct device *dev, bool enable) { - struct mtk_smi_larb_iommu *larb_mmu; - unsigned int larbid, portid; + struct mtk_smi_larb_iommu *larb_mmu; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - int i; + unsigned int larbid, portid, i, mmu_id; + void __iomem *base; for (i = 0; i < fwspec->num_ids; ++i) { - larbid = mt2701_m4u_to_larb(fwspec->ids[i]); - portid = mt2701_m4u_to_port(fwspec->ids[i]); + larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]); + portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]); larb_mmu = &data->larb_imu[larbid]; dev_dbg(dev, "%s iommu port: %d\n", @@ -260,6 +434,26 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data, else larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid); } + + /* MT6589 specific: set default prefetch distance & direction */ + if (data->soc->has_global_base) { + for (i = 0; i < fwspec->num_ids; i++) { + portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]); + larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]); + + /* Determine which M4U core this LARB is connected to */ + mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1; + base = data->cores[mmu_id].base; + + /* Set distance = 1 */ + m4u_set_field(base + REG_MMU_PFH_DIST(portid), + F_MMU_PFH_DIST_MASK(portid), + F_MMU_PFH_DIST_VAL(portid, 1)); + /* Set direction = 0 */ + m4u_set_field(base + REG_MMU_PFH_DIR(portid), + 1 << (portid & 0x1f), 0); + } + } } static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data) @@ -273,7 +467,10 @@ static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data) if (!dom->pgt_va) return -ENOMEM; - writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR); + if (data->soc->pt_base_in_global) + writel(dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset); + else + writel(dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset); dom->data = data; @@ -354,10 +551,19 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova, int prot, gfp_t gfp, size_t *mapped) { struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain); + struct mtk_iommu_v1_data *data = dom->data; unsigned long flags; unsigned int i; - u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT); + u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT); u32 pabase = (u32)paddr; + phys_addr_t dummy_pa = page_to_phys(data->dummy_page); + unsigned int guard_pages = 0; + + if (data->soc->has_global_base) { + /* Align to 4-entry boundary then add 4 more for PFH prefetch */ + unsigned int mod = pgcount & 0x3; + guard_pages = (mod ? (4 - mod) : 0) + 4; + } spin_lock_irqsave(&dom->pgtlock, flags); for (i = 0; i < pgcount; i++) { @@ -367,10 +573,22 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova, pabase += MT2701_IOMMU_PAGE_SIZE; } + if (guard_pages && i == pgcount) { + for (i = 0; i < guard_pages; i++) { + unsigned int idx = pgcount + i; + if ((iova >> MT2701_IOMMU_PAGE_SHIFT) + idx >= + (M2701_IOMMU_PGT_SIZE / sizeof(u32))) + break; + if (!pgt_base_iova[idx]) + pgt_base_iova[idx] = dummy_pa | F_DESC_VALID | F_DESC_NONSEC; + } + i = pgcount; /* for *mapped calculation */ + } + spin_unlock_irqrestore(&dom->pgtlock, flags); *mapped = i * MT2701_IOMMU_PAGE_SIZE; - mtk_iommu_v1_tlb_flush_range(dom->data, iova, *mapped); + data->soc->tlb_flush_range(data, iova, *mapped); return i == pgcount ? 0 : -EEXIST; } @@ -388,7 +606,7 @@ static size_t mtk_iommu_v1_unmap(struct iommu_domain *domain, unsigned long iova memset(pgt_base_iova, 0, pgcount * sizeof(u32)); spin_unlock_irqrestore(&dom->pgtlock, flags); - mtk_iommu_v1_tlb_flush_range(dom->data, iova, size); + dom->data->soc->tlb_flush_range(dom->data, iova, size); return size; } @@ -489,12 +707,12 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev) data = dev_iommu_priv_get(dev); /* Link the consumer device with the smi-larb device(supplier) */ - larbid = mt2701_m4u_to_larb(fwspec->ids[0]); - if (larbid >= MT2701_LARB_NR_MAX) + larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]); + if (larbid >= MTK_LARB_NR_MAX) return ERR_PTR(-EINVAL); for (idx = 1; idx < fwspec->num_ids; idx++) { - larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]); + larbidx = mtk_iommu_v1_to_larb(data, fwspec->ids[idx]); if (larbid != larbidx) { dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n", larbid, larbidx); @@ -532,12 +750,12 @@ static void mtk_iommu_v1_release_device(struct device *dev) unsigned int larbid; data = dev_iommu_priv_get(dev); - larbid = mt2701_m4u_to_larb(fwspec->ids[0]); + larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]); larbdev = data->larb_imu[larbid].dev; device_link_remove(dev, larbdev); } -static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data) +static int mt2701_hw_init(struct mtk_iommu_v1_data *data) { u32 regval; int ret; @@ -549,7 +767,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data) } regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2); - writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); + writel_relaxed(regval, data->cores[0].base + REG_MMU_CTRL_REG); regval = F_INT_TRANSLATION_FAULT | F_INT_MAIN_MULTI_HIT_FAULT | @@ -559,23 +777,66 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data) F_INT_TLB_MISS_FAULT | F_INT_PFH_DMA_FIFO_OVERFLOW | F_INT_MISS_DMA_FIFO_OVERFLOW; - writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); + writel_relaxed(regval, data->cores[0].base + REG_MMU_INT_CONTROL); - /* protect memory,hw will write here while translation fault */ - writel_relaxed(data->protect_base, - data->base + REG_MMU_IVRP_PADDR); + writel_relaxed(data->protect_base, data->cores[0].base + REG_MMU_IVRP_PADDR); + writel_relaxed(F_MMU_DCM_ON, data->cores[0].base + REG_MMU_DCM); - writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM); + return 0; +} + +static int mt6589_hw_init(struct mtk_iommu_v1_data *data) +{ + u32 regval; + int i, ret; - if (devm_request_irq(data->dev, data->irq, mtk_iommu_v1_isr, 0, - dev_name(data->dev), (void *)data)) { - writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR); - clk_disable_unprepare(data->bclk); - dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq); - return -ENODEV; + ret = clk_prepare_enable(data->bclk); + if (ret) { + dev_err(data->dev, "Failed to enable bclk\n"); + goto err_clk; + } + + /* ---- Global registers ---- */ + writel_relaxed(F_MMUg_L2_SEL_FLUSH_EN(1) | F_MMUg_L2_SEL_L2_ULTRA(1) | + F_MMUg_L2_SEL_L2_SHARE(0) | F_MMUg_L2_SEL_L2_BUS_SEL(1), + data->global_base + REG_MMUg_L2_SEL); + writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM); + + /* ---- L2 cache ---- */ + if (data->l2_base) { + regval = F_L2_GDC_BYPASS(0) | + F_L2_GDC_PERF_MASK(GDC_PERF_MASK_HIT_MISS) | + F_L2_GDC_LOCK_ALERT_DIS(0) | + F_L2_GDC_LOCK_TH(3) | + F_L2_GDC_PAUSE_OP(GDC_NO_PAUSE); + writel_relaxed(regval, data->l2_base + REG_L2_GDC_OP); + } + + /* ---- Per-core setup ---- */ + for (i = 0; i < data->soc->num_cores; i++) { + void __iomem *base = data->cores[i].base; + + regval = 0; /* PFH enabled, walk enabled, cohere disabled */ + regval |= F_MMU_TF_PROTECT_SEL(2); + writel_relaxed(regval, base + REG_MMU_CTRL_REG); + + regval = F_INT_TRANSLATION_FAULT | + F_INT_MAIN_MULTI_HIT_FAULT | + F_INT_INVALID_PA_FAULT | + F_INT_ENTRY_REPLACEMENT_FAULT | + F_INT_TABLE_WALK_FAULT | + F_INT_TLB_MISS_FAULT | + F_INT_PFH_DMA_FIFO_OVERFLOW | + F_INT_MISS_DMA_FIFO_OVERFLOW; + writel_relaxed(regval, base + REG_MMU_INT_CONTROL); + + writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); } return 0; + +err_clk: + return ret; } static const struct iommu_ops mtk_iommu_v1_ops = { @@ -595,8 +856,39 @@ static const struct iommu_ops mtk_iommu_v1_ops = { } }; +static const struct mtk_iommu_v1_soc_data mt2701_soc_data = { + .compatible = "mediatek,mt2701-m4u", + .num_cores = 1, + .has_global_base = false, + .has_l2_cache = false, + .tlb_flush_all = mt2701_tlb_flush_all, + .tlb_flush_range = mt2701_tlb_flush_range, + .get_fault_larb_port = mt2701_get_fault_larb_port, + .hw_init = mt2701_hw_init, + .pt_base_reg_offset = REG_MMU_PT_BASE_ADDR, + .pt_base_in_global = false, + .larb_port_offsets = mt2701_m4u_in_larb, + .num_larb = ARRAY_SIZE(mt2701_m4u_in_larb), +}; + +static const struct mtk_iommu_v1_soc_data mt6589_soc_data = { + .compatible = "mediatek,mt6589-m4u", + .num_cores = 2, + .has_global_base = true, + .has_l2_cache = true, + .tlb_flush_all = mt6589_tlb_flush_all, + .tlb_flush_range = mt6589_tlb_flush_range, + .get_fault_larb_port = mt6589_get_fault_larb_port, + .hw_init = mt6589_hw_init, + .pt_base_reg_offset = REG_MMUg_PT_BASE, + .pt_base_in_global = true, + .larb_port_offsets = mt6589_m4u_in_larb, + .num_larb = ARRAY_SIZE(mt6589_m4u_in_larb), +}; + static const struct of_device_id mtk_iommu_v1_of_ids[] = { - { .compatible = "mediatek,mt2701-m4u", }, + { .compatible = "mediatek,mt2701-m4u", .data = &mt2701_soc_data }, + { .compatible = "mediatek,mt6589-m4u", .data = &mt6589_soc_data }, {} }; MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids); @@ -610,16 +902,19 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_iommu_v1_data *data; - struct resource *res; + const struct mtk_iommu_v1_soc_data *soc; struct component_match *match = NULL; void *protect; int larb_nr, ret, i; + struct page *dummy_page; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->dev = dev; + soc = of_device_get_match_data(dev); + data->soc = soc; /* Protect memory. HW will access here while translation fault.*/ protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN, @@ -628,26 +923,69 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev) return -ENOMEM; data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->base = devm_ioremap_resource(dev, res); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) - return data->irq; + if (soc->has_global_base) { + data->global_base = devm_platform_ioremap_resource_byname(pdev, + "global"); + if (IS_ERR(data->global_base)) + return PTR_ERR(data->global_base); + } + for (i = 0; i < soc->num_cores; i++) { + char name[8]; + snprintf(name, sizeof(name), "m4u%d", i); + data->cores[i].base = devm_platform_ioremap_resource_byname(pdev, + name); + if (IS_ERR(data->cores[i].base)) + return PTR_ERR(data->cores[i].base); + data->cores[i].data = data; + data->cores[i].id = i; + } + if (soc->has_l2_cache) { + data->l2_base = devm_platform_ioremap_resource_byname(pdev, + "l2cache"); + if (IS_ERR(data->l2_base)) + return PTR_ERR(data->l2_base); + } data->bclk = devm_clk_get(dev, "bclk"); if (IS_ERR(data->bclk)) return PTR_ERR(data->bclk); + /* Interrupts - request after hw_init to avoid spurious IRQs? but + we need the core IRQs ready before registering ISR. We'll request + them after hw_init for simplicity. */ + ret = soc->hw_init(data); + if (ret) + return ret; + + for (i = 0; i < soc->num_cores; i++) { + struct mtk_iommu_v1_core *core = &data->cores[i]; + char irqname[8]; + snprintf(irqname, sizeof(irqname), "m4u%d", i); + core->irq = platform_get_irq_byname(pdev, irqname); + if (core->irq < 0) { + ret = core->irq; + goto out_clk_unprepare; + } + ret = devm_request_irq(dev, core->irq, mtk_iommu_v1_isr, 0, + dev_name(dev), core); + if (ret) { + dev_err(dev, "Failed to request IRQ %d for core%d\n", + core->irq, i); + goto out_clk_unprepare; + } + } + larb_nr = of_count_phandle_with_args(dev->of_node, "mediatek,larbs", NULL); - if (larb_nr < 0) - return larb_nr; + if (larb_nr < 0) { + ret = larb_nr; + goto out_clk_unprepare; + } - if (larb_nr > MTK_LARB_NR_MAX) - return -EINVAL; + if (larb_nr > MTK_LARB_NR_MAX) { + ret = -EINVAL; + goto out_clk_unprepare; + } for (i = 0; i < larb_nr; i++) { struct device_node *larbnode; @@ -682,17 +1020,20 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev) component_compare_of, larbnode); } + dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!dummy_page) { + ret = -ENOMEM; + goto out_put_larbs; + } + data->dummy_page = dummy_page; + platform_set_drvdata(pdev, data); - ret = mtk_iommu_v1_hw_init(data); + ret = iommu_device_sysfs_add(&data->iommu, dev, NULL, + dev_name(dev)); if (ret) goto out_put_larbs; - ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL, - dev_name(&pdev->dev)); - if (ret) - goto out_clk_unprepare; - ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev); if (ret) goto out_sysfs_remove; @@ -706,12 +1047,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev) iommu_device_unregister(&data->iommu); out_sysfs_remove: iommu_device_sysfs_remove(&data->iommu); -out_clk_unprepare: - clk_disable_unprepare(data->bclk); out_put_larbs: for (i = 0; i < MTK_LARB_NR_MAX; i++) - put_device(data->larb_imu[i].dev); - + if (data->larb_imu[i].dev) + put_device(data->larb_imu[i].dev); + if (data->dummy_page) + __free_page(data->dummy_page); +out_clk_unprepare: + clk_disable_unprepare(data->bclk); return ret; } @@ -724,40 +1067,109 @@ static void mtk_iommu_v1_remove(struct platform_device *pdev) iommu_device_unregister(&data->iommu); clk_disable_unprepare(data->bclk); - devm_free_irq(&pdev->dev, data->irq, data); + for (i = 0; i < data->soc->num_cores; i++) + devm_free_irq(&pdev->dev, data->cores[i].irq, &data->cores[i]); component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops); + __free_page(data->dummy_page); for (i = 0; i < MTK_LARB_NR_MAX; i++) - put_device(data->larb_imu[i].dev); + if (data->larb_imu[i].dev) + put_device(data->larb_imu[i].dev); } static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev) { struct mtk_iommu_v1_data *data = dev_get_drvdata(dev); struct mtk_iommu_v1_suspend_reg *reg = &data->reg; - void __iomem *base = data->base; + void __iomem *base = data->cores[0].base; - reg->standard_axi_mode = readl_relaxed(base + - REG_MMU_STANDARD_AXI_MODE); - reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM); + /* Common core registers */ reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG); reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL); + + if (data->soc->has_global_base) { + reg->mmug_ctrl = readl_relaxed(data->global_base + REG_MMUg_CTRL); + reg->mmug_pt_base = readl_relaxed(data->global_base + REG_MMUg_PT_BASE); + reg->mmug_l2_sel = readl_relaxed(data->global_base + REG_MMUg_L2_SEL); + reg->mmug_dcm = readl_relaxed(data->global_base + REG_MMUg_DCM); + if (data->l2_base) + reg->l2_gdc_op = readl_relaxed(data->l2_base + REG_L2_GDC_OP); + } else { + /* MT2701 specific */ + base = data->cores[0].base; + reg->standard_axi_mode = readl_relaxed(base + REG_MMU_STANDARD_AXI_MODE); + reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM); + } + return 0; } +static void mt6589_restore_pfh_settings(struct mtk_iommu_v1_data *data) +{ + const int *offsets = data->soc->larb_port_offsets; + int num_larb = data->soc->num_larb; + /* LARB to M4U core mapping (from downstream m4u_index_of_larb) */ + static const int larb_to_mmu[] = {0, 0, 1, 0, 1, 0}; + int larb, port; + + for (larb = 0; larb < num_larb; larb++) { + int mmu_id = larb_to_mmu[larb]; + void __iomem *base = data->cores[mmu_id].base; + int first_port = offsets[larb]; + int last_port = (larb == num_larb - 1) ? + MT6589_M4U_PORT_NR - 1 : + offsets[larb + 1] - 1; + + for (port = first_port; port <= last_port; port++) { + m4u_set_field(base + REG_MMU_PFH_DIST(port), + F_MMU_PFH_DIST_MASK(port), + F_MMU_PFH_DIST_VAL(port, 1)); + m4u_set_field(base + REG_MMU_PFH_DIR(port), + 1 << (port & 0x1f), 0); + } + } +} + static int __maybe_unused mtk_iommu_v1_resume(struct device *dev) { struct mtk_iommu_v1_data *data = dev_get_drvdata(dev); struct mtk_iommu_v1_suspend_reg *reg = &data->reg; - void __iomem *base = data->base; - - writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR); - writel_relaxed(reg->standard_axi_mode, - base + REG_MMU_STANDARD_AXI_MODE); - writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM); - writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); - writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL); - writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); + void __iomem *base = data->cores[0].base; + + if (data->soc->has_global_base) { + writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL); + writel_relaxed(reg->mmug_pt_base, data->global_base + REG_MMUg_PT_BASE); + writel_relaxed(reg->mmug_l2_sel, data->global_base + REG_MMUg_L2_SEL); + writel_relaxed(reg->mmug_dcm, data->global_base + REG_MMUg_DCM); + if (data->l2_base) + writel_relaxed(reg->l2_gdc_op, data->l2_base + REG_L2_GDC_OP); + } + + /* Per-core restore (common) */ + for (int i = 0; i < data->soc->num_cores; i++) { + base = data->cores[i].base; + writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); + writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL); + writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); + } + + if (!data->soc->has_global_base) { + /* MT2701 extra */ + base = data->cores[0].base; + writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE); + writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM); + } + + if (data->m4u_dom && data->m4u_dom->pgt_pa) { + if (data->soc->pt_base_in_global) + writel_relaxed(data->m4u_dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset); + else + writel_relaxed(data->m4u_dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset); + } + + if (data->soc->has_global_base) + mt6589_restore_pfh_settings(data); + return 0; } From 286e04bb64ba5a4a372b10e0c4fec7b2c2f64633 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Tue, 7 Jul 2026 23:35:34 +0900 Subject: [PATCH 08/33] drm: mediatek: mtk_drm_drv: add mt6589 routing Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 02d33d292c2ced..3837dc414999ec 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -72,6 +72,14 @@ static const unsigned int mt2701_mtk_ddp_ext[] = { DDP_COMPONENT_DPI0, }; +static const unsigned int mt6589_mtk_ddp_main[] = { + DDP_COMPONENT_OVL0, + DDP_COMPONENT_COLOR0, + DDP_COMPONENT_BLS, + DDP_COMPONENT_RDMA0, + DDP_COMPONENT_DSI0, +}; + static const unsigned int mt7623_mtk_ddp_main[] = { DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0, @@ -240,6 +248,13 @@ static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = { .mmsys_dev_num = 1, }; +static const struct mtk_mmsys_driver_data mt6589_mmsys_driver_data = { + .main_path = mt6589_mtk_ddp_main, + .main_len = ARRAY_SIZE(mt6589_mtk_ddp_main), + .shadow_register = true, + .mmsys_dev_num = 1, +}; + static const struct mtk_mmsys_driver_data mt7623_mmsys_driver_data = { .main_path = mt7623_mtk_ddp_main, .main_len = ARRAY_SIZE(mt7623_mtk_ddp_main), @@ -335,7 +350,7 @@ static const struct of_device_id mtk_drm_of_ids[] = { { .compatible = "mediatek,mt2701-mmsys", .data = &mt2701_mmsys_driver_data}, { .compatible = "mediatek,mt6589-dispsys", - .data = &mt2701_mmsys_driver_data}, /* TODO */ + .data = &mt6589_mmsys_driver_data}, { .compatible = "mediatek,mt7623-mmsys", .data = &mt7623_mmsys_driver_data}, { .compatible = "mediatek,mt2712-mmsys", From b3c07afe4c26e119d1a6da84ee73e8b30c55ff24 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Tue, 7 Jul 2026 23:36:11 +0900 Subject: [PATCH 09/33] soc: mediatek: mmsys: add TDSHP def Signed-off-by: Akari Tsuyukusa --- include/linux/soc/mediatek/mtk-mmsys.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h index d1b258510550b5..2345c8c1f5af05 100644 --- a/include/linux/soc/mediatek/mtk-mmsys.h +++ b/include/linux/soc/mediatek/mtk-mmsys.h @@ -81,6 +81,7 @@ enum mtk_ddp_comp_id { DDP_COMPONENT_RDMA4, DDP_COMPONENT_ROT, DDP_COMPONENT_SCL, + DDP_COMPONENT_TDSHP, DDP_COMPONENT_UFOE, DDP_COMPONENT_WDMA0, DDP_COMPONENT_WDMA1, From ebe6c2c081bb2c72807f1476632a80d9e01e0499 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 16:32:23 +0900 Subject: [PATCH 10/33] panel fixes Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- .../display/panel/panel-simple-dsi.yaml | 4 +- .../dts/mediatek/mt6589-lenovo-b8000.dtsi | 42 +++++++++++++------ drivers/gpu/drm/panel/panel-simple.c | 14 ++++++- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml index be4e11b6f49c4b..c31f5a5ddde31e 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml @@ -32,11 +32,11 @@ properties: # AU Optronics Corporation 8.0" WUXGA TFT LCD panel - auo,b080uan01 # BOE Technology 10.1" WXGA panel with Himax HX8896-A01 TCON - - boe,hx8896-a01 + - boe,hx8896-a01-panel # Boe Corporation 8.0" WUXGA TFT LCD panel - boe,tv080wum-nl0 # CMI 10.1" WXGA panel with Himax HX8896-A01 TCON - - innolux,hx8896-a01 + - innolux,hx8896-a01-panel # Innolux P079ZCA 7.85" 768x1024 TFT LCD panel - innolux,p079zca # JDI FHD_R63452 1080x1920 5.2" IPS LCD Panel diff --git a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi index 1f497944a0222a..6a64ef39ff16d9 100644 --- a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi @@ -15,16 +15,19 @@ format = "r5g6b5"; //status = "disabled"; }; +}; - reg_panel: regulator-panel { - compatible = "regulator-fixed"; - regulator-name = "fixed-1.2V"; - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - regulator-boot-on; - regulator-always-on; +&pio { + panel_pins: panel-pins { + pins_power_en { + pinmux = ; + }; }; +}; +&mt6320_ldo_vrf28_2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; }; &dsi { @@ -33,13 +36,25 @@ status = "okay"; panel@0 { - compatible = "boe,hx8896-a01"; - /* - * or innolux,hx8896-a01 - * TODO: panel mux - */ + compatible = "boe,hx8896-a01-panel"; + reg = <0>; + power-supply = <&mt6320_ldo_vrf28_2>; + enable-gpios = <&pio 137 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&panel_pins>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + + /* + panel@0 { + compatible = "innolux,hx8896-a01"; reg = <0>; - power-supply = <®_panel>; + power-supply = <&mt6320_ldo_vrf28_2>; port { panel_in: endpoint { @@ -47,6 +62,7 @@ }; }; }; + */ port { dsi_out: endpoint { diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 2d40ab5b794312..38ca899a36bcb4 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5809,6 +5809,11 @@ static const struct panel_desc_dsi boe_hx8896_a01 = { .bpc = 8, .size = { .width = 217, .height = 136 }, /* TODO */ .connector_type = DRM_MODE_CONNECTOR_DSI, + .delay = { + .prepare = 120, /* or 120 */ + .enable = 100, /* or 0 */ + .disable = 320, + }, }, .flags = MIPI_DSI_MODE_VIDEO, .format = MIPI_DSI_FMT_RGB888, @@ -5864,6 +5869,11 @@ static const struct panel_desc_dsi innolux_hx8896_a01 = { .bpc = 8, .size = { .width = 217, .height = 136 }, /* TODO */ .connector_type = DRM_MODE_CONNECTOR_DSI, + .delay = { + .prepare = 120, /* or 120 */ + .enable = 100, /* or 0 */ + .disable = 320, + }, }, .flags = MIPI_DSI_MODE_VIDEO, .format = MIPI_DSI_FMT_RGB888, @@ -5991,13 +6001,13 @@ static const struct of_device_id dsi_of_match[] = { .compatible = "auo,b080uan01", .data = &auo_b080uan01 }, { - .compatible = "boe,hx8896-a01", + .compatible = "boe,hx8896-a01-panel", .data = &boe_hx8896_a01 }, { .compatible = "boe,tv080wum-nl0", .data = &boe_tv080wum_nl0 }, { - .compatible = "innolux,hx8896-a01", + .compatible = "innolux,hx8896-a01-panel", .data = &innolux_hx8896_a01 }, { .compatible = "lg,lh500wx1-sd03", From 857ac65b221853d54484cb3e55e57ddb733ca8de Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 17:46:03 +0900 Subject: [PATCH 11/33] dt-bindings: reset: mt6589: add dispsys resets Signed-off-by: Akari Tsuyukusa --- include/dt-bindings/reset/mt6589-resets.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/dt-bindings/reset/mt6589-resets.h b/include/dt-bindings/reset/mt6589-resets.h index 228eee14dd9d13..3292b774b08490 100644 --- a/include/dt-bindings/reset/mt6589-resets.h +++ b/include/dt-bindings/reset/mt6589-resets.h @@ -71,4 +71,27 @@ #define MT6589_MFG_G3D_RESET 0 #define MT6589_MFG_AXI_RESET 1 +/* DISP */ +#define MT6589_DISP_SMI_LARB2_RST 0 +#define MT6589_DISP_ROT_RST 1 +#define MT6589_DISP_SCL_RST 2 +#define MT6589_DISP_OVL_RST 3 +#define MT6589_DISP_COLOR_RST 4 +#define MT6589_DISP_TDSHP_RST 5 +#define MT6589_DISP_BLS_RST 6 +#define MT6589_DISP_WDMA0_RST 7 +#define MT6589_DISP_WDMA1_RST 8 +#define MT6589_DISP_RDMA0_RST 9 +#define MT6589_DISP_RDMA1_RST 10 +#define MT6589_DISP_GAMMA_RST 11 +#define MT6589_DISP_CMDQ_DISPCK_RST 12 +#define MT6589_DISP_CMDQ_SMICK_RST 13 +#define MT6589_DISP_G2D_RST 14 +#define MT6589_DISP_DBI_DISPCK_RST 15 +#define MT6589_DISP_DBI_SMICK_RST 16 +#define MT6589_DISP_DBI_INTERFACECK_RST 17 +#define MT6589_DISP_DSI_RST 18 +#define MT6589_DISP_DPI0_RST 19 +#define MT6589_DISP_DPI1_RST 20 + #endif /* _DT_BINDINGS_RESET_CONTROLLER_MT6589 */ From 8228898c9572c257ab95ddfdff2fc72e5a9ba3e8 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 18:17:36 +0900 Subject: [PATCH 12/33] clk: mediatek: add support for resetbar Signed-off-by: Akari Tsuyukusa --- drivers/clk/mediatek/reset.c | 21 +++++++++++++++++++++ drivers/clk/mediatek/reset.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index 2e3303975096a4..3b63afc4140ad8 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -51,6 +51,17 @@ static int mtk_reset(struct reset_controller_dev *rcdev, unsigned long id) return mtk_reset_deassert(rcdev, id); } +static int mtk_reset_rstb(struct reset_controller_dev *rcdev, unsigned long id) +{ + int ret; + + ret = mtk_reset_deassert(rcdev, id); + if (ret) + return ret; + + return mtk_reset_assert(rcdev, id); +} + static int mtk_reset_update_set_clr(struct reset_controller_dev *rcdev, unsigned long id, bool deassert) { @@ -92,6 +103,13 @@ static const struct reset_control_ops mtk_reset_ops = { .reset = mtk_reset, }; +static const struct reset_control_ops mtk_reset_rstb_ops = { + /* inverted logic for reset bar */ + .assert = mtk_reset_deassert, + .deassert = mtk_reset_assert, + .reset = mtk_reset_rstb, +}; + static const struct reset_control_ops mtk_reset_ops_set_clr = { .assert = mtk_reset_assert_set_clr, .deassert = mtk_reset_deassert_set_clr, @@ -128,6 +146,9 @@ int mtk_register_reset_controller_with_dev(struct device *dev, case MTK_RST_SIMPLE: rcops = &mtk_reset_ops; break; + case MTK_RST_SIMPLE_RSTB: + rcops = &mtk_reset_rstb_ops; + break; case MTK_RST_SET_CLR: rcops = &mtk_reset_ops_set_clr; break; diff --git a/drivers/clk/mediatek/reset.h b/drivers/clk/mediatek/reset.h index 562ffd290a224a..a7cac2be92d0cc 100644 --- a/drivers/clk/mediatek/reset.h +++ b/drivers/clk/mediatek/reset.h @@ -21,11 +21,13 @@ /** * enum mtk_reset_version - Version of MediaTek clock reset controller. * @MTK_RST_SIMPLE: Use the same registers for bit set and clear. + * @MTK_RST_SIMPLE_RSTB: Use the same registers and active low (reset bar). * @MTK_RST_SET_CLR: Use separate registers for bit set and clear. * @MTK_RST_MAX: Total quantity of version for MediaTek clock reset controller. */ enum mtk_reset_version { MTK_RST_SIMPLE = 0, + MTK_RST_SIMPLE_RSTB, MTK_RST_SET_CLR, MTK_RST_MAX, }; From 621c45f9da70247657cf40790517b186e719f12a Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 18:18:00 +0900 Subject: [PATCH 13/33] clk: mediatek: mt6589-disp: add reset support Signed-off-by: Akari Tsuyukusa --- drivers/clk/mediatek/clk-mt6589-disp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/clk/mediatek/clk-mt6589-disp.c b/drivers/clk/mediatek/clk-mt6589-disp.c index ab1648970d200b..0c57146c741eb7 100644 --- a/drivers/clk/mediatek/clk-mt6589-disp.c +++ b/drivers/clk/mediatek/clk-mt6589-disp.c @@ -18,6 +18,8 @@ #define DISP_CG_SET1 0x0114 #define DISP_CG_CLR1 0x0118 +#define DISP_SW_RST_B 0x0140 + static const struct mtk_gate_regs disp0_cg_regs = { .set_ofs = DISP_CG_SET0, .clr_ofs = DISP_CG_CLR0, @@ -75,9 +77,18 @@ static const struct mtk_gate disp_clks[] = { GATE_DISP1(CLK_DISP1_SLCD, "disp1_slcd", "disp_sel", 9), }; +static u16 disp_rst_ofs[] = { DISP_SW_RST_B }; + +static const struct mtk_clk_rst_desc disp_clk_rst_desc = { + .version = MTK_RST_SIMPLE_RSTB, + .rst_bank_ofs = disp_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(disp_rst_ofs), +}; + static const struct mtk_clk_desc disp_desc = { .clks = disp_clks, .num_clks = ARRAY_SIZE(disp_clks), + .rst_desc = &disp_clk_rst_desc, }; static const struct of_device_id of_match_clk_mt6589_disp[] = { From 613ec2cc9f9167645b6c1b71ee9686611c1fed12 Mon Sep 17 00:00:00 2001 From: rva3 Date: Fri, 13 Mar 2026 13:47:01 +0200 Subject: [PATCH 14/33] drm: mediatek: disp_rdma: move size_con0 and size_con1 to platform data The RDMA SIZE_CON0 and SIZE_CON1 values vary across different SoCs. Prepare for supporting new SoCs by moving these values into the mtk_disp_rdma_data and updating existing SoC data to use their respective values. Signed-off-by: rva3 --- drivers/gpu/drm/mediatek/mtk_disp_rdma.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index c9d41d75e7f2a3..d4038842089f62 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -72,6 +72,8 @@ struct mtk_disp_rdma_data { unsigned int fifo_size; const u32 *formats; size_t num_formats; + u32 size_con0; + u32 size_con1; }; /* @@ -191,9 +193,9 @@ void mtk_rdma_config(struct device *dev, unsigned int width, u32 rdma_fifo_size; mtk_ddp_write_mask(cmdq_pkt, width, &rdma->cmdq_reg, rdma->regs, - DISP_REG_RDMA_SIZE_CON_0, 0xfff); + DISP_REG_RDMA_SIZE_CON_0, rdma->data->size_con0); mtk_ddp_write_mask(cmdq_pkt, height, &rdma->cmdq_reg, rdma->regs, - DISP_REG_RDMA_SIZE_CON_1, 0xfffff); + DISP_REG_RDMA_SIZE_CON_1, rdma->data->size_con1); if (rdma->fifo_size) rdma_fifo_size = rdma->fifo_size; @@ -380,24 +382,32 @@ static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = { .fifo_size = SZ_4K, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .size_con0 = 0xfff, + .size_con1 = 0xfffff, }; static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { .fifo_size = SZ_8K, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .size_con0 = 0xfff, + .size_con1 = 0xfffff, }; static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { .fifo_size = 5 * SZ_1K, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .size_con0 = 0xfff, + .size_con1 = 0xfffff, }; static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { .fifo_size = 1920, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .size_con0 = 0xfff, + .size_con1 = 0xfffff, }; static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { From 3dcea01253f53861681db0ea1216d4669dfa944b Mon Sep 17 00:00:00 2001 From: rva3 Date: Sun, 22 Mar 2026 13:29:54 +0200 Subject: [PATCH 15/33] drm: mediatek: disp_rdma: use function pointer for the fmt_convert RDMA uses different values for memory formats. Currently, these are hardcoded using macros. Move the format conversion logic into function pointer instead of raw call. This allows other SoCs to provide their own mapping logic without breaking others. Also drop unused parameter in the rdma_fmt_convert method. Signed-off-by: rva3 --- drivers/gpu/drm/mediatek/mtk_disp_rdma.c | 50 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index d4038842089f62..12460b80c69106 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -36,6 +36,10 @@ #define DISP_REG_RDMA_SIZE_CON_1 0x0018 #define DISP_REG_RDMA_TARGET_LINE 0x001c #define DISP_RDMA_MEM_CON 0x0024 + +#define MEM_MODE_INPUT_FORMAT_RGB565_MT65XX (0x004 << 4) +#define MEM_MODE_INPUT_FORMAT_RGB888_MT65XX (0x008 << 4) + #define MEM_MODE_INPUT_FORMAT_RGB565 (0x000 << 4) #define MEM_MODE_INPUT_FORMAT_RGB888 (0x001 << 4) #define MEM_MODE_INPUT_FORMAT_RGBA8888 (0x002 << 4) @@ -74,6 +78,7 @@ struct mtk_disp_rdma_data { size_t num_formats; u32 size_con0; u32 size_con1; + unsigned int (*fmt_convert)(unsigned int fmt); }; /* @@ -215,8 +220,43 @@ void mtk_rdma_config(struct device *dev, unsigned int width, mtk_ddp_write(cmdq_pkt, reg, &rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_FIFO_CON); } -static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma, - unsigned int fmt) +static unsigned int rdma_fmt_convert_mt65xx(unsigned int fmt) +{ + /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" + * is defined in mediatek HW data sheet. + * The alphabet order in XXX is no relation to data + * arrangement in memory. + */ + switch (fmt) { + default: + case DRM_FORMAT_RGB565: + return MEM_MODE_INPUT_FORMAT_RGB565_MT65XX; + case DRM_FORMAT_BGR565: + return MEM_MODE_INPUT_FORMAT_RGB565_MT65XX | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_RGB888: + return MEM_MODE_INPUT_FORMAT_RGB888_MT65XX; + case DRM_FORMAT_BGR888: + return MEM_MODE_INPUT_FORMAT_RGB888_MT65XX | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + return MEM_MODE_INPUT_FORMAT_ARGB8888; + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + return MEM_MODE_INPUT_FORMAT_ARGB8888 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return MEM_MODE_INPUT_FORMAT_RGBA8888; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return MEM_MODE_INPUT_FORMAT_RGBA8888 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_UYVY: + return MEM_MODE_INPUT_FORMAT_UYVY; + case DRM_FORMAT_YUYV: + return MEM_MODE_INPUT_FORMAT_YUYV; + } +} + +static unsigned int rdma_fmt_convert(unsigned int fmt) { /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" * is defined in mediatek HW data sheet. @@ -268,7 +308,7 @@ void mtk_rdma_layer_config(struct device *dev, unsigned int idx, unsigned int fmt = pending->format; unsigned int con; - con = rdma_fmt_convert(rdma, fmt); + con = rdma->data->fmt_convert(fmt); mtk_ddp_write_relaxed(cmdq_pkt, con, &rdma->cmdq_reg, rdma->regs, DISP_RDMA_MEM_CON); if (fmt == DRM_FORMAT_UYVY || fmt == DRM_FORMAT_YUYV) { @@ -384,6 +424,7 @@ static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = { .num_formats = ARRAY_SIZE(mt8173_formats), .size_con0 = 0xfff, .size_con1 = 0xfffff, + .fmt_convert = rdma_fmt_convert, }; static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { @@ -392,6 +433,7 @@ static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { .num_formats = ARRAY_SIZE(mt8173_formats), .size_con0 = 0xfff, .size_con1 = 0xfffff, + .fmt_convert = rdma_fmt_convert, }; static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { @@ -400,6 +442,7 @@ static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { .num_formats = ARRAY_SIZE(mt8173_formats), .size_con0 = 0xfff, .size_con1 = 0xfffff, + .fmt_convert = rdma_fmt_convert, }; static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { @@ -408,6 +451,7 @@ static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { .num_formats = ARRAY_SIZE(mt8173_formats), .size_con0 = 0xfff, .size_con1 = 0xfffff, + .fmt_convert = rdma_fmt_convert, }; static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { From e8d88c3322a28edc813b92f6a87ac894e81b2a4e Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 22:05:09 +0900 Subject: [PATCH 16/33] arm: dts: mediatek: mt6589: add dispsys resets Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index effad123c35deb..e5167b748aa152 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -752,6 +752,7 @@ compatible = "mediatek,mt6589-dispsys", "syscon"; reg = <0x14000000 0x1000>; #clock-cells = <1>; + #reset-cells = <1>; }; imgsys: syscon@15000000 { From 9df456fed8058e4037a970b7501fb86292cc74a0 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 22:05:17 +0900 Subject: [PATCH 17/33] drm: mediatek: mtk_disp_rdma: add MT6589 support Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_disp_rdma.c | 106 ++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index 12460b80c69106..815393f9b11588 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -49,6 +49,7 @@ #define MEM_MODE_INPUT_SWAP BIT(8) #define DISP_RDMA_MEM_SRC_PITCH 0x002c #define DISP_RDMA_MEM_GMC_SETTING_0 0x0030 +#define DISP_REG_RDMA_MEM_GMC_SETTING_1 0x0038 #define DISP_REG_RDMA_FIFO_CON 0x0040 #define RDMA_FIFO_UNDERFLOW_EN BIT(31) #define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) @@ -58,6 +59,15 @@ #define RDMA_MEM_GMC 0x40402020 +static const u32 mt6589_formats[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, +}; + static const u32 mt8173_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, @@ -72,6 +82,8 @@ static const u32 mt8173_formats[] = { DRM_FORMAT_YUYV, }; +struct mtk_disp_rdma; + struct mtk_disp_rdma_data { unsigned int fifo_size; const u32 *formats; @@ -79,6 +91,9 @@ struct mtk_disp_rdma_data { u32 size_con0; u32 size_con1; unsigned int (*fmt_convert)(unsigned int fmt); + u32 mem_start_addr_reg; + u32 mem_gmc_val; + void (*reset)(struct mtk_disp_rdma *rdma); }; /* @@ -256,6 +271,25 @@ static unsigned int rdma_fmt_convert_mt65xx(unsigned int fmt) } } +static unsigned int rdma_fmt_convert_mt6589(unsigned int fmt) +{ + switch (fmt) { + case DRM_FORMAT_YUYV: + return (0 << 4); + case DRM_FORMAT_UYVY: + return (1 << 4); + case DRM_FORMAT_RGB565: + return (4 << 4); + case DRM_FORMAT_RGB888: + return (8 << 4); + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return (16 << 4); + default: + return (16 << 4); + } +} + static unsigned int rdma_fmt_convert(unsigned int fmt) { /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" @@ -292,6 +326,37 @@ static unsigned int rdma_fmt_convert(unsigned int fmt) } } +static void mtk_rdma_reset_mt6589(struct mtk_disp_rdma *rdma) +{ + void __iomem *regs = rdma->regs; + unsigned int delay_cnt = 0; + + writel(0x10, regs + DISP_REG_RDMA_GLOBAL_CON); + while ((readl(regs + DISP_REG_RDMA_GLOBAL_CON) & 0x700) == 0x100) { + if (++delay_cnt > 10000) { + pr_warn("RDMA reset stage 1 timeout\n"); + break; + } + } + + writel(0x00, regs + DISP_REG_RDMA_GLOBAL_CON); + delay_cnt = 0; + while ((readl(regs + DISP_REG_RDMA_GLOBAL_CON) & 0x700) != 0x100) { + if (++delay_cnt > 10000) { + pr_warn("RDMA reset stage 2 timeout\n"); + break; + } + } + + writel(0x00, regs + DISP_REG_RDMA_SIZE_CON_0); + writel(0x00, regs + DISP_REG_RDMA_SIZE_CON_1); + writel(0x00, regs + DISP_RDMA_MEM_CON); + writel(0x00, regs + rdma->data->mem_start_addr_reg); + writel(0x00, regs + DISP_RDMA_MEM_SRC_PITCH); + writel(0x20, regs + DISP_REG_RDMA_MEM_GMC_SETTING_1); + writel(0x80f00008, regs + DISP_REG_RDMA_FIFO_CON); +} + unsigned int mtk_rdma_layer_nr(struct device *dev) { return 1; @@ -324,10 +389,10 @@ void mtk_rdma_layer_config(struct device *dev, unsigned int idx, RDMA_MATRIX_ENABLE); } mtk_ddp_write_relaxed(cmdq_pkt, addr, &rdma->cmdq_reg, rdma->regs, - DISP_RDMA_MEM_START_ADDR); + rdma->data->mem_start_addr_reg); mtk_ddp_write_relaxed(cmdq_pkt, pitch, &rdma->cmdq_reg, rdma->regs, DISP_RDMA_MEM_SRC_PITCH); - mtk_ddp_write(cmdq_pkt, RDMA_MEM_GMC, &rdma->cmdq_reg, rdma->regs, + mtk_ddp_write(cmdq_pkt, rdma->data->mem_gmc_val, &rdma->cmdq_reg, rdma->regs, DISP_RDMA_MEM_GMC_SETTING_0); mtk_ddp_write_mask(cmdq_pkt, RDMA_MODE_MEMORY, &rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_GLOBAL_CON, RDMA_MODE_MEMORY); @@ -402,6 +467,17 @@ static int mtk_disp_rdma_probe(struct platform_device *pdev) pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_disable(dev); + return dev_err_probe(dev, ret, "Failed to enable power\n"); + } + + if (priv->data->reset) + priv->data->reset(priv); + + pm_runtime_put_sync(dev); + ret = component_add(dev, &mtk_disp_rdma_component_ops); if (ret) { pm_runtime_disable(dev); @@ -425,6 +501,21 @@ static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = { .size_con0 = 0xfff, .size_con1 = 0xfffff, .fmt_convert = rdma_fmt_convert, + .mem_start_addr_reg = DISP_RDMA_MEM_START_ADDR, + .mem_gmc_val = RDMA_MEM_GMC, + .reset = NULL, +}; + +static const struct mtk_disp_rdma_data mt6589_rdma_driver_data = { + .fifo_size = 3840, + .formats = mt6589_formats, + .num_formats = ARRAY_SIZE(mt6589_formats), + .size_con0 = 0xfff, + .size_con1 = 0xfffff, + .fmt_convert = rdma_fmt_convert_mt6589, + .mem_start_addr_reg = 0x0028, /* MT6589 のアドレスレジスタ */ + .mem_gmc_val = 0x00000000, /* GMC は 0 クリア */ + .reset = mtk_rdma_reset_mt6589, }; static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { @@ -434,6 +525,9 @@ static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { .size_con0 = 0xfff, .size_con1 = 0xfffff, .fmt_convert = rdma_fmt_convert, + .mem_start_addr_reg = DISP_RDMA_MEM_START_ADDR, + .mem_gmc_val = RDMA_MEM_GMC, + .reset = NULL, }; static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { @@ -443,6 +537,9 @@ static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { .size_con0 = 0xfff, .size_con1 = 0xfffff, .fmt_convert = rdma_fmt_convert, + .mem_start_addr_reg = DISP_RDMA_MEM_START_ADDR, + .mem_gmc_val = RDMA_MEM_GMC, + .reset = NULL, }; static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { @@ -452,11 +549,16 @@ static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { .size_con0 = 0xfff, .size_con1 = 0xfffff, .fmt_convert = rdma_fmt_convert, + .mem_start_addr_reg = DISP_RDMA_MEM_START_ADDR, + .mem_gmc_val = RDMA_MEM_GMC, + .reset = NULL, }; static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { { .compatible = "mediatek,mt2701-disp-rdma", .data = &mt2701_rdma_driver_data}, + { .compatible = "mediatek,mt6589-disp-rdma", + .data = &mt6589_rdma_driver_data }, { .compatible = "mediatek,mt8173-disp-rdma", .data = &mt8173_rdma_driver_data}, { .compatible = "mediatek,mt8183-disp-rdma", From f3e21457b793f5a7c2d55c652523ae4de931fd9e Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 22:49:23 +0900 Subject: [PATCH 18/33] dt-bindings: phy: mediatek,dsi-phy: add mt6589 compatible Signed-off-by: Akari Tsuyukusa --- Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml b/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml index c6d0bbdbe0e227..dd573714fa4bc9 100644 --- a/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml +++ b/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml @@ -37,6 +37,7 @@ properties: - mediatek,mt8365-mipi-tx - const: mediatek,mt8183-mipi-tx - const: mediatek,mt2701-mipi-tx + - const: mediatek,mt6589-mipi-tx - const: mediatek,mt8173-mipi-tx - const: mediatek,mt8183-mipi-tx From 588ef588c1a6d62e5facd973fadbb59490964b4b Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 22:49:33 +0900 Subject: [PATCH 19/33] arm: dts: mediatek: mt6589: add mipi-tx Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index e5167b748aa152..27294b57d817b2 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -822,6 +822,14 @@ reset-names = "pwrap", "pwrap-bridge"; }; + mipi_tx: dsi-phy@10012000 { + compatible = "mediatek,mt6589-mipi-tx"; + clocks = <&clk26m>; + clock-output-names = "mipi_tx0_pll"; + #clock-cells = <0>; + #phy-cells = <0>; + }; + sysirq: interrupt-controller@10200100 { compatible = "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq"; From b58ad63056d08bab9cfa0372fa9139f8c7507d15 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 23:06:35 +0900 Subject: [PATCH 20/33] phy: mediatek: add MT6589 dsi phy Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/phy/mediatek/Makefile | 1 + .../phy/mediatek/phy-mtk-mipi-dsi-mt6589.c | 332 ++++++++++++++++++ drivers/phy/mediatek/phy-mtk-mipi-dsi.c | 1 + drivers/phy/mediatek/phy-mtk-mipi-dsi.h | 1 + 4 files changed, 335 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-mipi-dsi-mt6589.c diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 1b8088df71e845..73f2614a17ea12 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PHY_MTK_HDMI) += phy-mtk-hdmi-drv.o obj-$(CONFIG_PHY_MTK_MIPI_CSI_0_5) += phy-mtk-mipi-csi-0-5.o phy-mtk-mipi-dsi-drv-y := phy-mtk-mipi-dsi.o +phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt6589.o phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8173.o phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8183.o obj-$(CONFIG_PHY_MTK_MIPI_DSI) += phy-mtk-mipi-dsi-drv.o diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt6589.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt6589.c new file mode 100644 index 00000000000000..1bc56186517f58 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt6589.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek MT6589 MIPI D-PHY driver + * + * Copyright (c) 2026 MediaTek Inc. + * Based on downstream mt6589 dsi_drv.c + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "phy-mtk-io.h" +#include "phy-mtk-mipi-dsi.h" + +/* MT6589 MIPI TX PHY registers (offsets relative to PHY base) */ +#define MIPITX_DSI_CON 0x00 +#define MIPITX_DSI_LANE0 0x04 +#define MIPITX_DSI_LANE1 0x08 +#define MIPITX_DSI_LANE2 0x0c +#define MIPITX_DSI_LANE3 0x10 +#define MIPITX_DSI_CLK_LANE 0x14 +#define MIPITX_DSI_TOP_CON 0x40 +#define MIPITX_DSI_BG_CON 0x44 +#define MIPITX_DSI_PLL_CON0 0x50 +#define MIPITX_DSI_PLL_PWR 0x60 +#define MIPITX_DSI_SW_CTRL 0x80 +#define MIPITX_DSI_SW_CTRL_CON0 0x84 +#define MIPITX_DSI_SW_CTRL_CON1 0x88 + +/* PLL configuration entry: raw register values from downstream pll_config[] */ +struct mt6589_pll_config { + u8 txdiv0; /* TXDIV0 field (2 bits) */ + u8 txdiv1; /* TXDIV1 field (2 bits) */ + u8 fbk_sel; /* FBK_SEL (2 bits) */ + u8 fbk_div; /* FBK_DIV (7 bits) */ + u8 pre_div; /* PRE_DIV (2 bits) */ + u8 rg_br; /* RG_BR (2 bits) */ + u8 rg_bc; /* RG_BC (2 bits) */ + u8 rg_bir; /* RG_BIR (4 bits) */ + u8 rg_bic; /* RG_BIC (4 bits) */ + u8 rg_bp; /* RG_BP (4 bits) */ +}; + +/* + * PLL configuration table (50 entries) taken from MT6589 downstream. + * Each entry corresponds to a pre‑defined output frequency. + */ +static const struct mt6589_pll_config pll_config[] = { + {1,1,1,0x1E,1,2,3,8,4,0xC}, + {1,1,1,0x0F,0,2,2,4,1,0xC}, + {1,1,1,0x20,1,2,3,9,4,0xC}, + {1,1,1,0x10,0,2,2,4,1,0xC}, + {1,1,1,0x22,1,2,3,9,4,0xC}, + {1,1,1,0x11,0,2,2,4,1,0xC}, + {1,1,1,0x24,1,2,3,0xA,5,0xC}, + {1,1,1,0x12,0,2,2,5,2,0xC}, + {1,1,1,0x26,1,2,3,0xA,4,0xC}, + {1,1,1,0x13,0,2,2,5,2,0xC}, + {1,1,1,0x28,1,2,3,0xA,4,0xC}, + {1,1,1,0x14,0,2,2,5,2,0xC}, + {1,1,1,0x2A,1,2,3,0xB,5,0xC}, + {1,1,1,0x15,0,2,2,6,2,0xC}, + {1,1,1,0x2C,1,2,3,0xB,5,0xC}, + {1,1,1,0x16,0,2,2,6,2,0xC}, + {1,1,1,0x2E,1,2,3,0xC,5,0xC}, + {1,1,1,0x17,0,2,2,6,2,0xC}, + {1,1,1,0x30,1,2,3,0xD,5,0xC}, + {1,1,1,0x18,0,2,2,6,2,0xC}, + {1,1,1,0x32,1,2,3,0xE,6,0xC}, + {1,1,1,0x19,0,2,2,6,2,0xC}, + {1,1,1,0x34,1,1,3,0x7,6,0x6}, + {1,1,1,0x1A,0,2,2,7,2,0xC}, + {1,1,1,0x36,1,1,3,0x7,7,0x6}, + {1,0,0,0x1B,0,2,2,5,2,0x8}, + {1,0,0,0x38,1,2,2,0xA,5,0x8}, + {1,0,1,0x1C,1,2,2,0xA,5,0x8}, + {1,0,1,0x3A,3,1,3,5,3,0x8}, + {1,0,1,0x0E,0,2,2,5,2,0x9}, + {1,0,1,0x3C,3,1,3,5,3,0x8}, + {1,0,1,0x1E,1,1,2,5,5,0x4}, + {1,0,1,0x3E,3,1,3,5,3,0x8}, + {1,0,1,0x0F,0,2,2,5,2,0xA}, + {1,0,1,0x40,3,1,3,5,3,0x8}, + {1,0,1,0x20,1,1,2,6,5,0x4}, + {1,0,1,0x42,3,1,3,6,3,0x8}, + {1,0,1,0x10,0,2,2,6,2,0xA}, + {1,0,1,0x44,3,1,3,6,3,0x8}, + {1,0,1,0x22,1,1,2,6,6,0x4}, + {1,0,1,0x46,3,1,3,6,4,0x8}, + {1,0,1,0x11,0,2,2,5,2,0xA}, + {1,0,1,0x48,3,1,3,6,4,0x8}, + {1,0,1,0x24,1,1,2,6,6,0x4}, + {1,0,1,0x4A,3,1,3,7,4,0x8}, + {1,0,1,0x12,0,2,2,5,2,0xC}, + {1,0,1,0x4C,3,1,3,7,4,0x8}, + {1,0,1,0x26,1,1,2,7,6,0x4}, + {1,0,1,0x4E,3,1,3,7,4,0x8}, + {1,0,1,0x13,0,2,2,5,2,0xC}, +}; + +/* Convert raw divider field to actual division factor */ +static u8 div_factor(u8 raw) +{ + switch (raw) { + case 0: return 1; + case 1: return 2; + case 2: + case 3: return 4; + default: return 4; + } +} + +/* Calculate the MIPI lane data rate (Hz) produced by a PLL configuration */ +static unsigned long mt6589_pll_rate(const struct mt6589_pll_config *cfg) +{ + unsigned int fbk = cfg->fbk_div + 1; + unsigned int fbk_sel = div_factor(cfg->fbk_sel); + unsigned int pre = div_factor(cfg->pre_div); + unsigned int div1 = div_factor(cfg->txdiv0); + unsigned int div2 = div_factor(cfg->txdiv1); + unsigned long rate; + + /* Lane data rate (Mbps) = 26 * fbk * fbk_sel * 2 / (8 * div1 * div2 * pre) */ + rate = 26 * fbk * fbk_sel * 2; + rate /= 8 * div1 * div2 * pre; + /* Convert to Hz */ + return rate * 1000000UL; +} + +/* Find the table entry whose output rate is closest to the target */ +static const struct mt6589_pll_config *mt6589_pll_find_closest(unsigned long rate_hz) +{ + unsigned long best_diff = ULONG_MAX; + const struct mt6589_pll_config *best = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(pll_config); i++) { + unsigned long r = mt6589_pll_rate(&pll_config[i]); + long diff = abs((long)(r - rate_hz)); + + if (diff < best_diff) { + best_diff = diff; + best = &pll_config[i]; + } + } + return best; +} + +/* Program the PLL dividers and analog tuning values */ +static void mt6589_mipi_tx_set_pll(struct mtk_mipi_tx *mipi_tx, + const struct mt6589_pll_config *cfg) +{ + void __iomem *base = mipi_tx->regs; + u32 val; + + /* Assemble PLL_CON0 value without PLL_EN (bit0) */ + val = (cfg->rg_br << 30) | + (cfg->rg_bc << 28) | + (cfg->rg_bp << 24) | + (cfg->rg_bir << 20) | + (cfg->rg_bic << 16) | + (cfg->txdiv1 << 14) | + (cfg->txdiv0 << 12) | + (cfg->fbk_sel << 10) | + (cfg->pre_div << 8) | + (cfg->fbk_div << 1); + + writel(val, base + MIPITX_DSI_PLL_CON0); + /* Now enable the PLL */ + val |= BIT(0); + writel(val, base + MIPITX_DSI_PLL_CON0); +} + +/* --- Clock operations for the PLL --- */ + +static int mt6589_mipi_tx_pll_prepare(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + const struct mt6589_pll_config *cfg; + + cfg = mt6589_pll_find_closest(mipi_tx->data_rate); + if (!cfg) + return -EINVAL; + + dev_dbg(mipi_tx->dev, "PLL prepare: target %lu Hz, selected entry rate %lu Hz\n", + (unsigned long)mipi_tx->data_rate, + (unsigned long)mt6589_pll_rate(cfg)); + + /* Step 1: Enable bandgap and LDO core */ + writel(0x3, base + MIPITX_DSI_BG_CON); /* BG_CORE_EN | BG_CKEN */ + writel(0x3, base + MIPITX_DSI_CON); /* LDOCORE_EN | CKG_LDOOUT_EN */ + usleep_range(30, 100); + + /* Step 2: PLL power-up sequence (mimic downstream 0x400 -> PLL setup -> 0x600) */ + writel(0x400, base + MIPITX_DSI_PLL_PWR); + mt6589_mipi_tx_set_pll(mipi_tx, cfg); + usleep_range(20, 100); + writel(0x600, base + MIPITX_DSI_PLL_PWR); + usleep_range(20, 100); + + return 0; +} + +static void mt6589_mipi_tx_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + + dev_dbg(mipi_tx->dev, "PLL unprepare\n"); + + /* Restore default divider settings (mask 0xF0FE, value 0x26) */ + mtk_phy_update_bits(base + MIPITX_DSI_PLL_CON0, 0xF0FE, 0x26); + /* Disable PLL */ + mtk_phy_clear_bits(base + MIPITX_DSI_PLL_CON0, BIT(0)); + + /* Power down analog blocks */ + writel(0, base + MIPITX_DSI_PLL_PWR); + writel(0, base + MIPITX_DSI_CON); + writel(0, base + MIPITX_DSI_BG_CON); +} + +static int mt6589_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + + dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); + mipi_tx->data_rate = rate; + return 0; +} + +static unsigned long mt6589_mipi_tx_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + return mipi_tx->data_rate; +} + +static int mt6589_mipi_tx_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + req->rate = clamp_val(req->rate, 50000000, 1250000000); + return 0; +} + +static const struct clk_ops mt6589_mipi_tx_pll_ops = { + .prepare = mt6589_mipi_tx_pll_prepare, + .unprepare = mt6589_mipi_tx_pll_unprepare, + .set_rate = mt6589_mipi_tx_pll_set_rate, + .recalc_rate = mt6589_mipi_tx_pll_recalc_rate, + .determine_rate = mt6589_mipi_tx_pll_determine_rate, +}; + +/* --- PHY signal enable / disable --- */ + +static void mt6589_mipi_tx_power_on_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* Enable software control over PHY lanes */ + writel(1, base + MIPITX_DSI_SW_CTRL); + + /* Enable LDO output on all data lanes and clock lane */ + writel(1, base + MIPITX_DSI_LANE0); + writel(1, base + MIPITX_DSI_LANE1); + writel(1, base + MIPITX_DSI_LANE2); + writel(1, base + MIPITX_DSI_LANE3); + writel(1, base + MIPITX_DSI_CLK_LANE); + + /* Enable HS bias */ + writel(0x2, base + MIPITX_DSI_TOP_CON); /* BIT(1) */ + usleep_range(20, 100); + + /* Clear an unknown TOP_CON bit (BIT(11) in downstream) */ + mtk_phy_clear_bits(base + MIPITX_DSI_TOP_CON, BIT(11)); + + /* Set DP/DN to mark-1 state: first 0x200, then 0x600 */ + writel(0x200, base + MIPITX_DSI_SW_CTRL_CON0); + usleep_range(20, 100); + writel(0x600, base + MIPITX_DSI_SW_CTRL_CON0); + + /* Return control to DSI host */ + writel(0, base + MIPITX_DSI_SW_CTRL); +} + +static void mt6589_mipi_tx_power_off_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* Take software control */ + writel(1, base + MIPITX_DSI_SW_CTRL); + + /* Disable all lane LDOs */ + writel(0, base + MIPITX_DSI_LANE0); + writel(0, base + MIPITX_DSI_LANE1); + writel(0, base + MIPITX_DSI_LANE2); + writel(0, base + MIPITX_DSI_LANE3); + writel(0, base + MIPITX_DSI_CLK_LANE); + + /* Disable HS bias */ + writel(0, base + MIPITX_DSI_TOP_CON); + + /* Clear SW control registers (including HS TX data ready / data) */ + writel(0, base + MIPITX_DSI_SW_CTRL_CON0); + writel(0, base + MIPITX_DSI_SW_CTRL_CON1); + + /* Power down PLL block */ + writel(0, base + MIPITX_DSI_PLL_PWR); + + /* Release software control */ + writel(0, base + MIPITX_DSI_SW_CTRL); +} + +/* Platform data exported for binding */ +const struct mtk_mipitx_data mt6589_mipitx_data = { + .mppll_preserve = 0, /* not required? */ + .mipi_tx_clk_ops = &mt6589_mipi_tx_pll_ops, + .mipi_tx_enable_signal = mt6589_mipi_tx_power_on_signal, + .mipi_tx_disable_signal = mt6589_mipi_tx_power_off_signal, +}; +EXPORT_SYMBOL_GPL(mt6589_mipitx_data); + +MODULE_DESCRIPTION("MediaTek MT6589 MIPI TX PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c index 065ea626093aaa..d34f46bdec0efd 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -181,6 +181,7 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev) static const struct of_device_id mtk_mipi_tx_match[] = { { .compatible = "mediatek,mt2701-mipi-tx", .data = &mt2701_mipitx_data }, + { .compatible = "mediatek,mt6589-mipi-tx", .data = &mt6589_mipitx_data }, { .compatible = "mediatek,mt8173-mipi-tx", .data = &mt8173_mipitx_data }, { .compatible = "mediatek,mt8183-mipi-tx", .data = &mt8183_mipitx_data }, { /* sentinel */ } diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h index 5d4876f1dc9509..6589557b258f6c 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h @@ -40,6 +40,7 @@ unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate); extern const struct mtk_mipitx_data mt2701_mipitx_data; +extern const struct mtk_mipitx_data mt6589_mipitx_data; extern const struct mtk_mipitx_data mt8173_mipitx_data; extern const struct mtk_mipitx_data mt8183_mipitx_data; From c39f1c4f71841ee2271fccca101e9e5080dbd64a Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 23:07:04 +0900 Subject: [PATCH 21/33] arm: dts: mediatek: mt6589: fix Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index 27294b57d817b2..ccb9a187744f38 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -798,16 +798,6 @@ clock-names = "system-clk", "rtc-clk"; }; - mipi_tx0: dsi-phy@10012000 { - compatible = "mediatek,mt7623-mipi-tx", - "mediatek,mt2701-mipi-tx"; - reg = <0x10012000 0x90>; - clocks = <&dispsys CLK_DISP1_DSI_DIGITAL_LANE>; // uart_clk - clock-output-names = "mipi_tx0_pll"; - #clock-cells = <0>; - #phy-cells = <0>; - }; - pwrap: pwrap@1000f000 { compatible = "mediatek,mt6589-pwrap"; reg = <0x1000f000 0x1000>, @@ -822,9 +812,11 @@ reset-names = "pwrap", "pwrap-bridge"; }; - mipi_tx: dsi-phy@10012000 { + mipi_tx0: dsi-phy@10012000 { compatible = "mediatek,mt6589-mipi-tx"; + reg = <0x10012000 0x90>; clocks = <&clk26m>; + /* <&dispsys CLK_DISP1_DSI_DIGITAL_LANE> ? */ clock-output-names = "mipi_tx0_pll"; #clock-cells = <0>; #phy-cells = <0>; From ee06ee5e9e8d98db63d17bc1bef8e6b085b28c2c Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 23:38:26 +0900 Subject: [PATCH 22/33] dt-bindings: pwm: mediatek,pwm-disp: add mt6589 (wip) Signed-off-by: Akari Tsuyukusa --- Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml b/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml index 68ef30414325a5..48aff8e58e05b5 100644 --- a/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml +++ b/Documentation/devicetree/bindings/pwm/mediatek,pwm-disp.yaml @@ -17,6 +17,7 @@ properties: oneOf: - enum: - mediatek,mt2701-disp-pwm + - mediatek,mt6589-disp-pwm - mediatek,mt6595-disp-pwm - mediatek,mt8173-disp-pwm - mediatek,mt8183-disp-pwm From 5798865d1cb790ae2b1361dd4de35a87ec274773 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 23:38:38 +0900 Subject: [PATCH 23/33] arm: dts: mediatek: mt6589: add bls Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index ccb9a187744f38..686a8cb8f9cff1 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -755,6 +755,16 @@ #reset-cells = <1>; }; + bls: pwm@14008000 { + compatible = "mediatek,mt6589-disp-pwm"; + reg = <0x14008000 0x1000>; + #pwm-cells = <2>; + clocks = <&dispsys CLK_DISP0_BLS>; + clock-names = "main"; + power-domains = <&spm MT6589_POWER_DOMAIN_DIS>; /* maybe */ + status = "disabled"; + }; + imgsys: syscon@15000000 { compatible = "mediatek,mt6589-imgsys", "syscon"; reg = <0x15000000 0x1000>; From dffd339a1483e33e0eee234e234df1b57763d585 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Sun, 26 Jul 2026 23:38:54 +0900 Subject: [PATCH 24/33] pwm: add MT6589 BLS driver Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- arch/arm/configs/lenovo-blade_defconfig | 2 +- drivers/pwm/Kconfig | 5 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-mt6589-disp.c | 326 ++++++++++++++++++++++++ 4 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 drivers/pwm/pwm-mt6589-disp.c diff --git a/arch/arm/configs/lenovo-blade_defconfig b/arch/arm/configs/lenovo-blade_defconfig index f3a947dbb8db29..32fb2ebabdf3ab 100644 --- a/arch/arm/configs/lenovo-blade_defconfig +++ b/arch/arm/configs/lenovo-blade_defconfig @@ -197,7 +197,7 @@ CONFIG_RMI4_F34=y ## PWM CONFIG_PWM=y CONFIG_PWM_MEDIATEK=y -CONFIG_PWM_MTK_DISP=y +CONFIG_PWM_MT6589_DISP=y ## RTC CONFIG_RTC_CLASS=y diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6f3147518376a0..ed234f7000f1a3 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -504,6 +504,11 @@ config PWM_MICROCHIP_CORE To compile this driver as a module, choose M here: the module will be called pwm-microchip-core. +config PWM_MT6589_DISP + tristate "MediaTek MT6589 display PWM driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM + config PWM_MTK_DISP tristate "MediaTek display PWM driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 0dc0d2b69025db..d38e28bdcbae1a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_PWM_MC33XS2410) += pwm-mc33xs2410.o obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o +obj-$(CONFIG_PWM_MT6589_DISP) += pwm-mt6589-disp.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_NTXEC) += pwm-ntxec.o diff --git a/drivers/pwm/pwm-mt6589-disp.c b/drivers/pwm/pwm-mt6589-disp.c new file mode 100644 index 00000000000000..d1fc2ef2ab9bb1 --- /dev/null +++ b/drivers/pwm/pwm-mt6589-disp.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek MT6589 Display BLS PWM Driver + * + * Copyright (c) 2026 MediaTek Inc. + * + * The MT6589 BLS (Backlight Scaler) module provides a PWM generator + * for LCD backlight control, integrated with gamma correction LUTs, + * PWM output LUTs, dithering, and histogram-based auto-brightness. + * This driver exposes only the PWM functionality; the other features + * are set up once during probe and then left in their default states. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets (relative to BLS base address) */ +#define BLS_EN 0x0000 +#define BLS_RST 0x0004 +#define BLS_BLS_SETTING 0x0008 +#define BLS_HIS_SETTING 0x000C +#define BLS_INTEN 0x0010 +#define BLS_INTSTA 0x0014 +#define BLS_SRC_SIZE 0x0018 +#define BLS_HIS_PROT 0x001C +#define BLS_GAIN 0x0020 +#define BLS_DISTORT_POINT 0x0024 +#define BLS_GAIN_SETTING 0x0028 +#define BLS_GAMMA_SETTING 0x0030 +#define BLS_GAMMA_BOUNDARY 0x0034 +#define BLS_LUT_UPDATE 0x0038 +#define BLS_PWM_LUT_SEL 0x003C +#define BLS_MAXCLR_LIMIT 0x0040 +#define BLS_PRE_DIST_THD 0x0044 +#define BLS_DIST_THD_NORM 0x0048 +#define BLS_DIST_THD_DARK 0x004C +#define BLS_DIST_THD_BRIGHT 0x0050 +#define BLS_DIST_THD_TEXT 0x0054 +#define BLS_DS_SETTING 0x0058 +#define BLS_BS_SETTING 0x005C +#define BLS_TS_SETTING0 0x0060 +#define BLS_TS_SETTING1 0x0064 +#define BLS_SC_DIFF_THD 0x0068 +#define BLS_SC_BIN_THD 0x006C +#define BLS_FAST_IIR_XCOEFF 0x0070 +#define BLS_FAST_IIR_YCOEFF 0x0074 +#define BLS_SLOW_IIR_XCOEFF 0x0078 +#define BLS_SLOW_IIR_YCOEFF 0x007C +#define BLS_PWM_DUTY 0x0080 +#define BLS_PWM_DUTY_GAIN 0x0084 +#define BLS_PATTERN 0x0088 +#define BLS_PWM_CON 0x0090 +/* + * PWM period is determined by PWM_H_DURATION, PWM_L_DURATION, + * and PWM_G_DURATION registers. These are left at their reset values + * for now, as downstream does not modify them. If the backlight + * frequency needs to be adjusted, set these registers accordingly. + */ +#define PWM_H_DURATION 0x0094 +#define PWM_L_DURATION 0x0098 +#define PWM_G_DURATION 0x009C +#define PWM_SEND_DATA0 0x00A0 +#define PWM_SEND_DATA1 0x00A4 +#define PWM_WAVE_NUM 0x00A8 +#define PWM_DATA_WIDTH 0x00AC +#define PWM_THRESH 0x00B0 +#define PWM_SEND_WAVENUM 0x00B4 + +/* Gamma LUT / IGAMMA LUT / PWM LUT base offsets */ +#define BLS_GAMMA_LUT(n) (0x0400 + 4 * (n)) /* n=0..255 */ +#define BLS_IGAMMA_LUT(n) (0x0800 + 4 * (n)) /* n=0..255 */ +#define BLS_PWM_LUT(n) (0x0C00 + 4 * (n)) /* n=0..32 */ + +/* Dither registers */ +#define BLS_DITHER(n) (0x0E00 + 4 * (n)) /* n=0..17 */ + +/* BLS_EN bit definitions */ +#define BLS_EN_PWM_ONLY BIT(31) /* Enable PWM, others off */ + +/* BLS_PWM_DUTY format */ +#define PWM_DUTY_MIN_LEVEL BIT(19) /* Lower bound (fixed to 1) */ +#define PWM_MAX_LEVEL 255 /* Maximum duty value */ + +/* Default PWM divider value (from downstream) */ +#define PWM_DEFAULT_DIV 0x24 + +struct mt6589_bls_pwm { + void __iomem *base; + struct clk *clk_main; + unsigned int max_level; +}; + +static inline struct mt6589_bls_pwm *to_mt6589_bls_pwm(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +/* + * Convert PWM duty cycle (in ns) to a brightness level (0..max_level). + * The period is expected to be constant and provided by the consumer + * (e.g., pwm-backlight). We simply scale duty_cycle to the [0, max_level] + * range. + */ +static unsigned int duty_to_level(const struct pwm_state *state, + unsigned int max_level) +{ + u64 level; + + if (!state->enabled || state->duty_cycle == 0) + return 0; + + level = mul_u64_u64_div_u64(state->duty_cycle, max_level, state->period); + if (level > max_level) + level = max_level; + + return (unsigned int)level; +} + +/* Initialize Gamma LUT with identity (1:1) mapping */ +static void mt6589_bls_gamma_init(struct mt6589_bls_pwm *bls) +{ + unsigned int i; + u32 val; + + /* Gamma table: 256 entries, 10-bit per component, packed as + * {R[9:0], G[9:0], B[9:0]} => bits 29..20, 19..10, 9..0. + * Identity mapping: entry[i] = (i << 2) so that 255 -> 1020. + */ + for (i = 0; i < 256; i++) { + val = ((i << 2) & 0x3FF) << 20 | /* Red */ + ((i << 2) & 0x3FF) << 10 | /* Green */ + ((i << 2) & 0x3FF); /* Blue */ + writel(val, bls->base + BLS_GAMMA_LUT(i)); + } + + /* Last point (index 256) boundary register */ + val = ((256 << 2) & 0x3FF) << 20 | + ((256 << 2) & 0x3FF) << 10 | + ((256 << 2) & 0x3FF); + writel(val, bls->base + BLS_GAMMA_BOUNDARY); + + /* Enable gamma table (bit 0 of BLS_GAMMA_SETTING) */ + writel(0x00000001, bls->base + BLS_GAMMA_SETTING); +} + +/* Initialize PWM LUT with a linear ramp */ +static void mt6589_bls_pwm_lut_init(struct mt6589_bls_pwm *bls) +{ + unsigned int i; + u32 val; + + /* PWM LUT has 33 entries (indices 0..32). Program an identity + * where entry[i] = i * (1023 / 32) ~ i * 31. : true table + * from downstream is unknown, this should be sufficient for basic + * operation. + */ + for (i = 0; i <= 32; i++) { + val = (i * 31) & 0x3FF; /* 10-bit value */ + writel(val, bls->base + BLS_PWM_LUT(i)); + } + + /* LUT update: select and commit (bits defined in BLS_LUT_UPDATE) */ + writel(0x4, bls->base + BLS_LUT_UPDATE); /* PWM LUT update start */ + for (i = 0; i <= 32; i++) { + writel(i, bls->base + BLS_PWM_LUT_SEL); /* select row */ + /* dummy read to ensure write? Not needed */ + } + writel(0x0, bls->base + BLS_LUT_UPDATE); /* PWM LUT update end */ +} + +/* Configure dithering registers with downstream magic values */ +static void mt6589_bls_dither_init(struct mt6589_bls_pwm *bls) +{ + /* Values taken from MT6589 downstream ddp_bls.c */ + writel(0x00000001, bls->base + BLS_DITHER(0)); + writel(0x00000000, bls->base + BLS_DITHER(6)); + writel(0x00000222, bls->base + BLS_DITHER(13)); + writel(0x00000000, bls->base + BLS_DITHER(14)); + writel(0x22220001, bls->base + BLS_DITHER(15)); + writel(0x22222222, bls->base + BLS_DITHER(16)); + writel(0x00000000, bls->base + BLS_DITHER(17)); +} + +static int mt6589_bls_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct mt6589_bls_pwm *bls = to_mt6589_bls_pwm(chip); + unsigned int level; + u32 reg; + int ret; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (state->enabled) { + /* Ensure clocks are on */ + ret = clk_prepare_enable(bls->clk_main); + if (ret) + return ret; + + level = duty_to_level(state, bls->max_level); + + /* Set PWM duty: register format is (min_level << 19) | duty_value */ + reg = PWM_DUTY_MIN_LEVEL | (level & 0x3FF); + writel(reg, bls->base + BLS_PWM_DUTY); + + /* Enable PWM (only PWM, keep BLS engine off) */ + writel(BLS_EN_PWM_ONLY, bls->base + BLS_EN); + } else { + /* Disable PWM output */ + writel(0x0, bls->base + BLS_EN); + + clk_disable_unprepare(bls->clk_main); + } + + return 0; +} + +static const struct pwm_ops mt6589_bls_pwm_ops = { + .apply = mt6589_bls_pwm_apply, +}; + +static int mt6589_bls_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pwm_chip *chip; + struct mt6589_bls_pwm *bls; + int ret; + + chip = devm_pwmchip_alloc(dev, 1, sizeof(*bls)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + bls = to_mt6589_bls_pwm(chip); + + bls->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bls->base)) + return PTR_ERR(bls->base); + + bls->clk_main = devm_clk_get(dev, "main"); + if (IS_ERR(bls->clk_main)) + return dev_err_probe(dev, PTR_ERR(bls->clk_main), + "failed to get clock\n"); + + bls->max_level = PWM_MAX_LEVEL; /* hardcoded, matches downstream default */ + + /* Enable clocks for initialization */ + ret = clk_prepare_enable(bls->clk_main); + if (ret) + return ret; + + /* Reset the BLS module (optional, downstream does this) */ + writel(0x0, bls->base + BLS_RST); /* release reset */ + + /* Set source size (unknown, use default 0, ) */ + writel(0x0, bls->base + BLS_SRC_SIZE); + + /* Initialize PWM control: clock divider, idle level high */ + writel(0x00050000 | PWM_DEFAULT_DIV, bls->base + BLS_PWM_CON); + + /* Duty gain = 1.0 (0x100 = 256/256) */ + writel(0x00000100, bls->base + BLS_PWM_DUTY_GAIN); + + /* Disable BLS processing, keep only PWM enabled eventually */ + writel(0x0, bls->base + BLS_BLS_SETTING); + + /* Enable PWM-only mode */ + writel(BLS_EN_PWM_ONLY, bls->base + BLS_EN); + + /* Initialize gamma LUT (identity) */ + mt6589_bls_gamma_init(bls); + + /* Initialize PWM LUT (linear ramp) */ + mt6589_bls_pwm_lut_init(bls); + + /* Initialize dithering */ + mt6589_bls_dither_init(bls); + + /* Disable interrupts (they are enabled by default?) */ + writel(0x0, bls->base + BLS_INTEN); + + /* + * BLS_HIS_SETTING: + * bit1: Histogram_Mode = 1 (histogram of input data, w/o inverse gamma) + * bit0: Histogram_Auto_Clear = 1 (auto-clear histogram at frame start) + * We do not use the histogram engine, but keep downstream default. + */ + writel(0x00000003, bls->base + BLS_HIS_SETTING); + + /* Disable BLS module for now, it will be enabled on first apply */ + writel(0x0, bls->base + BLS_EN); + + clk_disable_unprepare(bls->clk_main); + + chip->ops = &mt6589_bls_pwm_ops; + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add PWM chip\n"); + + return 0; +} + +static const struct of_device_id mt6589_bls_pwm_of_match[] = { + { .compatible = "mediatek,mt6589-disp-pwm" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt6589_bls_pwm_of_match); + +static struct platform_driver mt6589_bls_pwm_driver = { + .probe = mt6589_bls_pwm_probe, + .driver = { + .name = "mt6589-disp-pwm", + .of_match_table = mt6589_bls_pwm_of_match, + }, +}; +module_platform_driver(mt6589_bls_pwm_driver); + +MODULE_AUTHOR("MediaTek"); +MODULE_DESCRIPTION("MediaTek MT6589 Display BLS PWM Driver"); +MODULE_LICENSE("GPL"); From 31fc966283e268728e4173cc9cc8ec59f30a56e0 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 00:02:47 +0900 Subject: [PATCH 25/33] dt-bindings: display: mediatek: color: add mt6589 Signed-off-by: Akari Tsuyukusa --- .../devicetree/bindings/display/mediatek/mediatek,color.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,color.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,color.yaml index 5564f4063317b2..4c410bd427b30a 100644 --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,color.yaml +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,color.yaml @@ -24,6 +24,7 @@ properties: oneOf: - enum: - mediatek,mt2701-disp-color + - mediatek,mt6589-disp-color - mediatek,mt8167-disp-color - mediatek,mt8173-disp-color - mediatek,mt8195-mdp3-color From c07a6240d22c43b8ca06ae563358f1e821710be0 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 00:08:33 +0900 Subject: [PATCH 26/33] drm: mediatek: mtk_disp_color: add MT6589 support Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_disp_color.c | 70 ++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_color.c b/drivers/gpu/drm/mediatek/mtk_disp_color.c index 39c7de4cdcc16b..6a380e69f967d1 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_color.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_color.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017 MediaTek Inc. + * Copyright (c) 2026 MT6589 adaptation */ #include @@ -25,16 +26,22 @@ #define COLOR_BYPASS_ALL BIT(7) #define COLOR_SEQ_SEL BIT(13) +/* MT6589 specific: main control bit to enable the color module */ +#define COLOR_MAIN_EN BIT(29) + +/* Additional registers needed by MT6589 */ +#define DISP_COLOR_R2Y_EN (DISP_COLOR_START_MT2701 + 0x60) /* 0xf60 */ +#define DISP_COLOR_R2Y_MATRIX_BASE (DISP_COLOR_START_MT2701 + 0x64) /* 0xf64 */ +#define DISP_COLOR_CCOR_EN (DISP_COLOR_START_MT2701 + 0xa0) /* 0xfa0 */ + +struct mtk_disp_color *color; struct mtk_disp_color_data { unsigned int color_offset; + bool enable_main_bit29; /* set BIT(29) in CFG_MAIN */ + void (*init)(struct mtk_disp_color *color); /* extra init sequence */ }; -/* - * struct mtk_disp_color - DISP_COLOR driver structure - * @crtc: associated crtc to report irq events to - * @data: platform colour driver data - */ struct mtk_disp_color { struct drm_crtc *crtc; struct clk *clk; @@ -67,12 +74,53 @@ void mtk_color_config(struct device *dev, unsigned int w, mtk_ddp_write(cmdq_pkt, h, &color->cmdq_reg, color->regs, DISP_COLOR_HEIGHT(color)); } +/* + * MT6589 specific initialization: + * - Enable the color module via BIT(29) in CFG_MAIN + * - Setup the color space conversion matrix (BT.601 YUV->RGB) + * - Enable R2Y and CCOR stages + */ +static void mt6589_color_init(struct mtk_disp_color *color) +{ + void __iomem *regs = color->regs; + + /* Color matrix coefficients (downstream values) */ + writel(306, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x00); + writel(601, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x04); + writel(117, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x08); + writel(-173, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x0c); + writel(-339, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x10); + writel(512, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x14); + writel(512, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x18); + writel(-429, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x1c); + writel(-83, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x20); + /* The rest of the matrix (0x00 at 0x24..0x34) are already zero */ + + writel(128, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x34); /* offset R */ + writel(128, regs + DISP_COLOR_R2Y_MATRIX_BASE + 0x38); /* offset G */ + /* 0x00 at 0x3c already zero */ + + /* Enable R2Y (YUV to RGB conversion) */ + writel(1, regs + DISP_COLOR_R2Y_EN); + + /* Enable color correction (CCOR) */ + writel(1, regs + DISP_COLOR_CCOR_EN); +} + void mtk_color_start(struct device *dev) { struct mtk_disp_color *color = dev_get_drvdata(dev); + u32 cfg_main = COLOR_BYPASS_ALL | COLOR_SEQ_SEL; + + if (color->data->enable_main_bit29) + cfg_main |= COLOR_MAIN_EN; + + writel(cfg_main, color->regs + DISP_COLOR_CFG_MAIN); + + /* SoC-specific extra initialization */ + if (color->data->init) + color->data->init(color); - writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, - color->regs + DISP_COLOR_CFG_MAIN); writel(0x1, color->regs + DISP_COLOR_START(color)); } @@ -144,6 +192,12 @@ static const struct mtk_disp_color_data mt8173_color_driver_data = { .color_offset = DISP_COLOR_START_MT8173, }; +static const struct mtk_disp_color_data mt6589_color_driver_data = { + .color_offset = DISP_COLOR_START_MT2701, /* same as MT2701: 0x0f00 */ + .enable_main_bit29 = true, + .init = mt6589_color_init, +}; + static const struct of_device_id mtk_disp_color_driver_dt_match[] = { { .compatible = "mediatek,mt2701-disp-color", .data = &mt2701_color_driver_data}, @@ -151,6 +205,8 @@ static const struct of_device_id mtk_disp_color_driver_dt_match[] = { .data = &mt8167_color_driver_data}, { .compatible = "mediatek,mt8173-disp-color", .data = &mt8173_color_driver_data}, + { .compatible = "mediatek,mt6589-disp-color", + .data = &mt6589_color_driver_data}, {}, }; MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match); From 99393d9ef79a713454919eba20b2b1ce3e639819 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 01:05:09 +0900 Subject: [PATCH 27/33] drm: mediatek: mtk_disp_ovl: add MT6589 support Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 135 +++++++++++++++++++++--- 1 file changed, 121 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c index e92ee0eff27e94..e29e688aa092e3 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -97,6 +97,28 @@ #define OVL_COLOR_ALPHA GENMASK(31, 24) +/* MT6589 specific registers for YUV-to-RGB conversion matrix */ +#define DISP_REG_OVL_Y2R_BASE(n) (0x0134 + 0x28 * (n)) +#define Y2R_R0 0x00 +#define Y2R_R1 0x04 +#define Y2R_G0 0x08 +#define Y2R_G1 0x0C +#define Y2R_B0 0x10 +#define Y2R_B1 0x14 +#define Y2R_YUV_A0 0x18 +#define Y2R_YUV_A1 0x1C +#define Y2R_RGB_A0 0x20 +#define Y2R_RGB_A1 0x24 + +/* BT.601 coefficients: 5x3 matrix, 13-bit signed (as used by MT6589) */ +static const s16 mt6589_yuv2rgb_coef[5][3] = { + { 306, 601, 117 }, + { -173, -339, 512 }, + { 512, -429, -83 }, + { 0, 0, 0 }, + { 0, 0, 0 }, +}; + static inline bool is_10bit_rgb(u32 fmt) { switch (fmt) { @@ -158,6 +180,15 @@ struct mtk_disp_ovl_data { bool fmt_rgb565_is_0; bool smi_id_en; bool supports_afbc; + bool has_const_blend; + bool set_layer_src; + unsigned int vblank_en_mask; + unsigned int fme_und_bit; + unsigned int rdma0_eof_abn_bit; + unsigned int rdma1_eof_abn_bit; + unsigned int rdma0_fifo_und_bit; + unsigned int rdma1_fifo_und_bit; + unsigned int (*fmt_convert)(unsigned int fmt, unsigned int blend_mode); const u32 blend_modes; const u32 *formats; size_t num_formats; @@ -184,15 +215,15 @@ static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id) struct mtk_disp_ovl *priv = dev_id; u32 reg = readl(priv->regs + DISP_REG_OVL_INTSTA); - if (reg & OVL_FME_UND) + if (reg & priv->data->fme_und_bit) pr_err("OVL: OVL frame underflow\n"); - if (reg & OVL_RDMA0_EOF_ABNORMAL) + if (reg & priv->data->rdma0_eof_abn_bit) pr_err("OVL: RDMA0 didn't complete frame\n"); - if (reg & OVL_RDMA1_EOF_ABNORMAL) + if (reg & priv->data->rdma1_eof_abn_bit) pr_err("OVL: RDMA1 didn't complete frame\n"); - if (reg & OVL_RDMA0_FIFO_UND) + if (reg & priv->data->rdma0_fifo_und_bit) pr_err("OVL: RDMA0 FIFO underflow\n"); - if (reg & OVL_RDMA1_FIFO_UND) + if (reg & priv->data->rdma1_fifo_und_bit) pr_err("OVL: RDMA1 FIFO underflow\n"); /* Clear frame completion interrupt */ @@ -229,10 +260,8 @@ void mtk_ovl_enable_vblank(struct device *dev) struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA); - writel_relaxed(OVL_FME_CPL_INT | OVL_FME_UND_INT | - OVL_RDMA0_EOF_ABNORMAL_INT | OVL_RDMA1_EOF_ABNORMAL_INT | - OVL_RDMA0_FIFO_UND_INT | OVL_RDMA1_FIFO_UND_INT, - ovl->regs + DISP_REG_OVL_INTEN); + writel_relaxed(ovl->data->vblank_en_mask, + ovl->regs + DISP_REG_OVL_INTEN); } void mtk_ovl_disable_vblank(struct device *dev) @@ -337,6 +366,37 @@ static void mtk_ovl_set_bit_depth(struct device *dev, int idx, u32 format, OVL_CON_CLRFMT_BIT_DEPTH_MASK(idx)); } +static void mt6589_ovl_write_yuv_matrix(struct mtk_disp_ovl *ovl, + unsigned int idx, struct cmdq_pkt *cmdq_pkt) +{ + void __iomem *base = ovl->regs; + unsigned int reg_base = DISP_REG_OVL_Y2R_BASE(idx); + + mtk_ddp_write(cmdq_pkt, + (mt6589_yuv2rgb_coef[0][1] << 16) | (mt6589_yuv2rgb_coef[0][0] & 0x1FFF), + &ovl->cmdq_reg, base, reg_base + Y2R_R0); + mtk_ddp_write(cmdq_pkt, mt6589_yuv2rgb_coef[0][2] & 0x1FFF, + &ovl->cmdq_reg, base, reg_base + Y2R_R1); + mtk_ddp_write(cmdq_pkt, + (mt6589_yuv2rgb_coef[1][1] << 16) | (mt6589_yuv2rgb_coef[1][0] & 0x1FFF), + &ovl->cmdq_reg, base, reg_base + Y2R_G0); + mtk_ddp_write(cmdq_pkt, mt6589_yuv2rgb_coef[1][2] & 0x1FFF, + &ovl->cmdq_reg, base, reg_base + Y2R_G1); + mtk_ddp_write(cmdq_pkt, + (mt6589_yuv2rgb_coef[2][1] << 16) | (mt6589_yuv2rgb_coef[2][0] & 0x1FFF), + &ovl->cmdq_reg, base, reg_base + Y2R_B0); + mtk_ddp_write(cmdq_pkt, mt6589_yuv2rgb_coef[2][2] & 0x1FFF, + &ovl->cmdq_reg, base, reg_base + Y2R_B1); + /* Offsets and RGB gains (neutral: 0 offset, 1024 gain) */ + mtk_ddp_write(cmdq_pkt, (0 << 16) | 0, &ovl->cmdq_reg, base, reg_base + Y2R_YUV_A0); + mtk_ddp_write(cmdq_pkt, 0, &ovl->cmdq_reg, base, reg_base + Y2R_YUV_A1); + mtk_ddp_write(cmdq_pkt, (1024 << 16) | 0, &ovl->cmdq_reg, base, reg_base + Y2R_RGB_A0); + mtk_ddp_write(cmdq_pkt, 0, &ovl->cmdq_reg, base, reg_base + Y2R_RGB_A1); + /* Enable YUV-to-RGB conversion (bit 0 of some ctrl reg? Not needed on 6589, + * matrix is automatically active when CLRFMT is YUV) + */ +} + void mtk_ovl_config(struct device *dev, unsigned int w, unsigned int h, unsigned int vrefresh, unsigned int bpc, struct cmdq_pkt *cmdq_pkt) @@ -413,6 +473,12 @@ void mtk_ovl_layer_on(struct device *dev, unsigned int idx, gmc_thrshd_h << 16 | gmc_thrshd_h << 24; mtk_ddp_write(cmdq_pkt, gmc_value, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RDMA_GMC(idx)); + + /* For MT6589, explicitly set layer source to memory (0) */ + if (ovl->data->set_layer_src) + mtk_ddp_write_mask(cmdq_pkt, 0, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_CON(idx), GENMASK(29, 28)); + mtk_ddp_write_mask(cmdq_pkt, BIT(idx), &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_SRC_CON, BIT(idx)); } @@ -428,8 +494,7 @@ void mtk_ovl_layer_off(struct device *dev, unsigned int idx, DISP_REG_OVL_RDMA_CTRL(idx)); } -static unsigned int mtk_ovl_fmt_convert(struct mtk_disp_ovl *ovl, - struct mtk_plane_state *state) +static unsigned int mtk_ovl_fmt_convert(struct mtk_disp_ovl *ovl, struct mtk_plane_state *state) { unsigned int fmt = state->pending.format; unsigned int blend_mode = DRM_MODE_BLEND_COVERAGE; @@ -494,6 +559,27 @@ static unsigned int mtk_ovl_fmt_convert(struct mtk_disp_ovl *ovl, } } +static unsigned int mt6589_fmt_convert(unsigned int fmt, unsigned int blend_mode) +{ + switch (fmt) { + case DRM_FORMAT_RGB565: return (1 << 12); + case DRM_FORMAT_BGR565: return (1 << 12) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_RGB888: return (0 << 12); + case DRM_FORMAT_BGR888: return (0 << 12) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: return (3 << 12); + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_BGRX8888: return (3 << 12) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: return (2 << 12); + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XBGR8888: return (2 << 12) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_UYVY: return (9 << 12); + case DRM_FORMAT_YUYV: return (8 << 12); + } + return 0; +} + static void mtk_ovl_afbc_layer_config(struct mtk_disp_ovl *ovl, unsigned int idx, struct mtk_plane_pending_state *pending, @@ -530,7 +616,7 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx, unsigned int offset = (pending->y << 16) | pending->x; unsigned int src_size = (pending->height << 16) | pending->width; unsigned int blend_mode = state->base.pixel_blend_mode; - unsigned int ignore_pixel_alpha = 0; + unsigned int ignore_pixel_alpha = 0, const_blend = 0; unsigned int con; if (!pending->enable) { @@ -538,7 +624,14 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx, return; } - con = mtk_ovl_fmt_convert(ovl, state); + if (ovl->data->fmt_convert) + con = ovl->data->fmt_convert(fmt, blend_mode); + else + con = mtk_ovl_fmt_convert(ovl, state); + + if (ovl->data->has_const_blend) + const_blend = OVL_CONST_BLEND; + if (state->base.fb) { con |= state->base.alpha & OVL_CON_ALPHA; @@ -556,7 +649,7 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx, * affect the result. Therefore we use !has_alpha as the condition. */ if (blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !state->base.fb->format->has_alpha) - ignore_pixel_alpha = OVL_CONST_BLEND; + ignore_pixel_alpha = const_blend; } /* @@ -596,6 +689,11 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx, mtk_ovl_set_bit_depth(dev, idx, fmt, cmdq_pkt); mtk_ovl_layer_on(dev, idx, cmdq_pkt); + + /* For MT6589, write YUV conversion matrix if needed */ + if (ovl->data->fmt_convert == mt6589_fmt_convert && + (fmt == DRM_FORMAT_UYVY || fmt == DRM_FORMAT_YUYV)) + mt6589_ovl_write_yuv_matrix(ovl, idx, cmdq_pkt); } void mtk_ovl_bgclr_in_on(struct device *dev) @@ -716,6 +814,15 @@ static const struct mtk_disp_ovl_data mt6589_ovl_driver_data = { .blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) | BIT(DRM_MODE_BLEND_COVERAGE) | BIT(DRM_MODE_BLEND_PIXEL_NONE), + .has_const_blend = false, + .set_layer_src = true, + .vblank_en_mask = 0xF, /* Reg update, frame done, underflow, sw reset done */ + .fme_und_bit = BIT(2), + .rdma0_eof_abn_bit = BIT(4), + .rdma1_eof_abn_bit = BIT(5), + .rdma0_fifo_und_bit = BIT(8), + .rdma1_fifo_und_bit = BIT(9), + .fmt_convert = mt6589_fmt_convert, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), }; From 160738614e9a341db1225a628e9135419b2f94f4 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 01:23:58 +0900 Subject: [PATCH 28/33] drm: mediatek: mtk_dsi: add DSI_MEM_CONTI control Assisted-by: DeepSeek:deepseek-v4-pro Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_dsi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index a1e17693d56d96..448501ab5c18ab 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -85,6 +85,8 @@ #define DSI_SIZE_CON 0x38 #define DSI_HEIGHT GENMASK(30, 16) #define DSI_WIDTH GENMASK(14, 0) +#define DSI_MEM_CONTI 0x3c +#define DSI_WMEM_CONTI 0x3c #define DSI_HSA_WC 0x50 #define DSI_HBP_WC 0x54 #define DSI_HFP_WC 0x58 @@ -736,6 +738,8 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi) mtk_dsi_reset_engine(dsi); mtk_dsi_phy_timconfig(dsi); + writel(DSI_WMEM_CONTI, dsi->regs + DSI_MEM_CONTI); + mtk_dsi_ps_control(dsi, true); mtk_dsi_set_vm_cmd(dsi); mtk_dsi_config_vdo_timing(dsi); From 79cee894dbc61e4c3cb230c34cbf6d7d61895613 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 01:25:03 +0900 Subject: [PATCH 29/33] drm: mediatek: mtk_drm_drv: add missing mt6589 compatibles Signed-off-by: Akari Tsuyukusa --- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 3837dc414999ec..5cbcc018c2fbb7 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -735,6 +735,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DISP_CCORR }, { .compatible = "mediatek,mt2701-disp-color", .data = (void *)MTK_DISP_COLOR }, + { .compatible = "mediatek,mt6589-disp-color", + .data = (void *)MTK_DISP_COLOR }, { .compatible = "mediatek,mt8167-disp-color", .data = (void *)MTK_DISP_COLOR }, { .compatible = "mediatek,mt8173-disp-color", @@ -801,6 +803,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = { .data = (void *)MTK_DISP_POSTMASK }, { .compatible = "mediatek,mt2701-disp-pwm", .data = (void *)MTK_DISP_BLS }, + { .compatible = "mediatek,mt6589-disp-pwm", + .data = (void *)MTK_DISP_BLS }, { .compatible = "mediatek,mt8167-disp-pwm", .data = (void *)MTK_DISP_PWM }, { .compatible = "mediatek,mt8173-disp-pwm", From 1a32b1b6e0056e3ea52816febb797d578f6f5869 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 01:25:35 +0900 Subject: [PATCH 30/33] arm: dts: mediatek: mt6589: add color engine Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi index 686a8cb8f9cff1..2ac3358c088d98 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -765,6 +765,13 @@ status = "disabled"; }; + color: color@1400a000 { + compatible = "mediatek,mt6589-disp-color"; + reg = <0x1400a000 0x1000>; + interrupts = ; + clocks = <&dispsys CLK_DISP0_COLOR>; + }; + imgsys: syscon@15000000 { compatible = "mediatek,mt6589-imgsys", "syscon"; reg = <0x15000000 0x1000>; From e0a56c91593224e5ff86cf5d3907b64063588d67 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 01:32:50 +0900 Subject: [PATCH 31/33] arm: dts: mediatek: lenovo-blade: enable bls Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589-lenovo-blade.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/mediatek/mt6589-lenovo-blade.dtsi b/arch/arm/boot/dts/mediatek/mt6589-lenovo-blade.dtsi index 6cbac35641857e..849ca5de21baf1 100644 --- a/arch/arm/boot/dts/mediatek/mt6589-lenovo-blade.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589-lenovo-blade.dtsi @@ -457,3 +457,7 @@ &pwm { status = "okay"; }; + +&bls { + status = "okay"; +}; From ddc66d6f711fa1aaf1062fc2fb174c951ef1291e Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 02:33:37 +0900 Subject: [PATCH 32/33] arm: dts: mediatek: lenovo-b8000: re-enable simple-framebuffer Signed-off-by: Akari Tsuyukusa --- arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi index 6a64ef39ff16d9..8837ef1f7ebbe5 100644 --- a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi @@ -13,7 +13,7 @@ height = <800>; stride = <(1280 * 2)>; format = "r5g6b5"; - //status = "disabled"; + // status = "disabled"; }; }; From 2f03f7bb20dfc6e287e85f2a7ebbff69a575aae1 Mon Sep 17 00:00:00 2001 From: Akari Tsuyukusa Date: Mon, 27 Jul 2026 02:33:54 +0900 Subject: [PATCH 33/33] iommu: mtk_iommu_v1: fix mt6589 (wip) Signed-off-by: Akari Tsuyukusa --- drivers/iommu/mtk_iommu_v1.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index 64da45cec8f33b..7002f9fd60d141 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -156,6 +156,9 @@ struct dma_iommu_mapping { #define MAX_M4U_CORES 2 +// static const int mt6589_larb_to_mmu[] = {0, 0, 1, 0, 1, 0}; +static const int mt6589_larb_to_mmu[] = {0, 0, 1, 0, 1}; + struct mtk_iommu_v1_data; struct mtk_iommu_v1_soc_data { @@ -372,7 +375,7 @@ static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb, static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb, unsigned int *port) { - *larb = 6 - (int_id >> 12) & 0x7; + *larb = 6 - ((int_id >> 12) & 0x7); *port = (int_id >> 8) & 0xF; } @@ -440,9 +443,7 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data, for (i = 0; i < fwspec->num_ids; i++) { portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]); larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]); - - /* Determine which M4U core this LARB is connected to */ - mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1; + mmu_id = mt6589_larb_to_mmu[larbid]; base = data->cores[mmu_id].base; /* Set distance = 1 */ @@ -802,6 +803,13 @@ static int mt6589_hw_init(struct mtk_iommu_v1_data *data) data->global_base + REG_MMUg_L2_SEL); writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM); + /* + * Before any TLB operations, set a safe dummy page table base address. + * The real page table will be set later in domain_finalise(). + * Use the protect buffer as a dummy, as downstream does. + */ + writel_relaxed(data->protect_base, data->global_base + REG_MMUg_PT_BASE); + /* ---- L2 cache ---- */ if (data->l2_base) { regval = F_L2_GDC_BYPASS(0) | @@ -829,7 +837,7 @@ static int mt6589_hw_init(struct mtk_iommu_v1_data *data) F_INT_PFH_DMA_FIFO_OVERFLOW | F_INT_MISS_DMA_FIFO_OVERFLOW; writel_relaxed(regval, base + REG_MMU_INT_CONTROL); - + writel_relaxed(0xff, base + REG_MMU_FAULT_ST); writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); } @@ -1108,12 +1116,10 @@ static void mt6589_restore_pfh_settings(struct mtk_iommu_v1_data *data) { const int *offsets = data->soc->larb_port_offsets; int num_larb = data->soc->num_larb; - /* LARB to M4U core mapping (from downstream m4u_index_of_larb) */ - static const int larb_to_mmu[] = {0, 0, 1, 0, 1, 0}; int larb, port; for (larb = 0; larb < num_larb; larb++) { - int mmu_id = larb_to_mmu[larb]; + int mmu_id = mt6589_larb_to_mmu[larb]; void __iomem *base = data->cores[mmu_id].base; int first_port = offsets[larb]; int last_port = (larb == num_larb - 1) ? @@ -1135,6 +1141,7 @@ static int __maybe_unused mtk_iommu_v1_resume(struct device *dev) struct mtk_iommu_v1_data *data = dev_get_drvdata(dev); struct mtk_iommu_v1_suspend_reg *reg = &data->reg; void __iomem *base = data->cores[0].base; + int i; if (data->soc->has_global_base) { writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL); @@ -1146,7 +1153,7 @@ static int __maybe_unused mtk_iommu_v1_resume(struct device *dev) } /* Per-core restore (common) */ - for (int i = 0; i < data->soc->num_cores; i++) { + for (i = 0; i < data->soc->num_cores; i++) { base = data->cores[i].base; writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);