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..c31f5a5ddde31e 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-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-panel # 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..94f48005fd6582 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 # generation one (dual core) - mediatek,mt6779-m4u # generation two - mediatek,mt6795-m4u # generation two - mediatek,mt6893-iommu-mm # generation two @@ -97,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: @@ -133,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, @@ -155,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: @@ -162,6 +210,7 @@ allOf: enum: - mediatek,mt2701-m4u - mediatek,mt2712-m4u + - mediatek,mt6589-m4u - mediatek,mt6795-m4u - mediatek,mt6893-iommu-mm - mediatek,mt8173-m4u @@ -240,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>; + }; 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/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 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 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..6a64ef39ff16d9 100644 --- a/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589-lenovo-b8000.dtsi @@ -13,5 +13,60 @@ height = <800>; stride = <(1280 * 2)>; format = "r5g6b5"; + //status = "disabled"; + }; +}; + +&pio { + panel_pins: panel-pins { + pins_power_en { + pinmux = ; + }; + }; +}; + +&mt6320_ldo_vrf28_2 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; +}; + +&dsi { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + panel@0 { + 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 = <&mt6320_ldo_vrf28_2>; + + 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 4731ecbdc40f14..686a8cb8f9cff1 100644 --- a/arch/arm/boot/dts/mediatek/mt6589.dtsi +++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include / { @@ -752,6 +752,17 @@ compatible = "mediatek,mt6589-dispsys", "syscon"; reg = <0x14000000 0x1000>; #clock-cells = <1>; + #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 { @@ -811,6 +822,16 @@ reset-names = "pwrap", "pwrap-bridge"; }; + 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>; + }; + sysirq: interrupt-controller@10200100 { compatible = "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq"; @@ -820,6 +841,35 @@ 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 0x100>, + <0x10205200 0x600>, + <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>, + <&larb3>, <&larb4>; + #iommu-cells = <1>; + }; + gic: interrupt-controller@10211000 { compatible = "arm,cortex-a7-gic"; interrupt-controller; @@ -998,6 +1048,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 0f11752f7483df..32fb2ebabdf3ab 100644 --- a/arch/arm/configs/lenovo-blade_defconfig +++ b/arch/arm/configs/lenovo-blade_defconfig @@ -30,14 +30,22 @@ CONFIG_ARM_MEDIATEK_CPUFREQ=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_FRAMEBUFFER_CONSOLE=y @@ -189,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/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[] = { 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, }; 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_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c index c9d41d75e7f2a3..815393f9b11588 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) @@ -45,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) @@ -54,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, @@ -68,10 +82,18 @@ static const u32 mt8173_formats[] = { DRM_FORMAT_YUYV, }; +struct mtk_disp_rdma; + struct mtk_disp_rdma_data { unsigned int fifo_size; const u32 *formats; size_t num_formats; + 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); }; /* @@ -191,9 +213,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; @@ -213,8 +235,62 @@ 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_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" * is defined in mediatek HW data sheet. @@ -250,6 +326,37 @@ static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma, } } +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; @@ -266,7 +373,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) { @@ -282,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); @@ -360,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); @@ -380,29 +498,67 @@ 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, + .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 = { .fifo_size = SZ_8K, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .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 = { .fifo_size = 5 * SZ_1K, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .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 = { .fifo_size = 1920, .formats = mt8173_formats, .num_formats = ARRAY_SIZE(mt8173_formats), + .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", diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index c86a3f54f35bcc..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), @@ -334,6 +349,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 = &mt6589_mmsys_driver_data}, { .compatible = "mediatek,mt7623-mmsys", .data = &mt7623_mmsys_driver_data}, { .compatible = "mediatek,mt2712-mmsys", @@ -742,6 +759,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 +781,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 +807,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 +841,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..38ca899a36bcb4 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -5790,6 +5790,36 @@ 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, + .delay = { + .prepare = 120, /* or 120 */ + .enable = 100, /* or 0 */ + .disable = 320, + }, + }, + .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 +5850,36 @@ 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, + .delay = { + .prepare = 120, /* or 120 */ + .enable = 100, /* or 0 */ + .disable = 320, + }, + }, + .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 +6000,15 @@ static const struct of_device_id dsi_of_match[] = { { .compatible = "auo,b080uan01", .data = &auo_b080uan01 + }, { + .compatible = "boe,hx8896-a01-panel", + .data = &boe_hx8896_a01 }, { .compatible = "boe,tv080wum-nl0", .data = &boe_tv080wum_nl0 + }, { + .compatible = "innolux,hx8896-a01-panel", + .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..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 { @@ -146,77 +250,147 @@ 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) +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; } diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index aaeba8ab211e97..53ac9d77ced51c 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,23 @@ #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)) +/* 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 */ /* gen1: mt2701 */ #define REG_SMI_SECUR_CON_BASE 0x5c0 @@ -92,6 +107,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 +117,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 +159,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 +204,103 @@ 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_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); @@ -283,6 +396,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 +595,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 +606,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_mt6589, + .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 +669,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 +861,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 +889,13 @@ 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, + /* LARB0→MMU0, LARB1→MMU1, LARB2→MMU0, LARB3→MMU1, LARB4→MMU1 */ + .bus_sel = 0x110, +}; + static const struct mtk_smi_common_plat mtk_smi_common_mt6779 = { .type = MTK_SMI_GEN2, .has_gals = true, @@ -843,6 +976,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 +1017,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 +1081,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->bus_sel) + writel(bus_sel, common->base + SMI_BUS_SEL); + return 0; } 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; 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"); 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..ae0acf225e13be --- /dev/null +++ b/include/dt-bindings/memory/mt6589-larb-port.h @@ -0,0 +1,92 @@ +// 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 */ +/* xxx: is it really larb? */ +#define MT6589_M4U_PORT_AUDIO MT6589_M4U_ID_LARB5(0) + +#define MT6589_M4U_PORT_NR 54 + +#endif 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 */ diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h index 4885b065b849fb..2345c8c1f5af05 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,13 @@ enum mtk_ddp_comp_id { DDP_COMPONENT_RDMA1, DDP_COMPONENT_RDMA2, DDP_COMPONENT_RDMA4, + DDP_COMPONENT_ROT, + DDP_COMPONENT_SCL, + DDP_COMPONENT_TDSHP, DDP_COMPONENT_UFOE, DDP_COMPONENT_WDMA0, DDP_COMPONENT_WDMA1, + DDP_COMPONENT_DBI0, DDP_COMPONENT_ID_MAX, };