diff options
| author | Dave Airlie <airlied@redhat.com> | 2016-05-10 01:01:47 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2016-05-10 01:01:47 -0400 |
| commit | 2e726dc4b4e2dd3ae3fe675f9d3af88a2d593ee1 (patch) | |
| tree | bb22392b580c3c58e9973c4a50133d580f861f98 | |
| parent | bafb86f5bc3173479002555dea7f31d943b12332 (diff) | |
| parent | ac4b1280319c3032787ac95bfeff14a425c417bf (diff) | |
Merge tag 'mediatek-drm-2016-05-09' of git://git.pengutronix.de/git/pza/linux into drm-next
MT8173 DRM support
- device tree binding documentation for all MT8173 display
subsystem components
- basic mediatek-drm driver for MT8173 with two optional,
currently fixed output paths:
- DSI encoder support for DSI and (via bridge) eDP panels
- DPI encoder support for output to HDMI bridge
- necessary clock tree changes for the DPI->HDMI path
- export mtk-smi functions used by mediatek-drm
* tag 'mediatek-drm-2016-05-09' of git://git.pengutronix.de/git/pza/linux:
clk: mediatek: remove hdmitx_dig_cts from TOP clocks
clk: mediatek: Add hdmi_ref HDMI PHY PLL reference clock output
clk: mediatek: make dpi0_sel propagate rate changes
drm/mediatek: Add DPI sub driver
drm/mediatek: Add DSI sub driver
drm/mediatek: Add DRM Driver for Mediatek SoC MT8173.
dt-bindings: drm/mediatek: Add Mediatek display subsystem dts binding
memory: mtk-smi: export mtk_smi_larb_get/put
31 files changed, 6098 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt new file mode 100644 index 000000000000..db6e77edbea8 --- /dev/null +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | Mediatek display subsystem | ||
| 2 | ========================== | ||
| 3 | |||
| 4 | The Mediatek display subsystem consists of various DISP function blocks in the | ||
| 5 | MMSYS register space. The connections between them can be configured by output | ||
| 6 | and input selectors in the MMSYS_CONFIG register space. Pixel clock and start | ||
| 7 | of frame signal are distributed to the other function blocks by a DISP_MUTEX | ||
| 8 | function block. | ||
| 9 | |||
| 10 | All DISP device tree nodes must be siblings to the central MMSYS_CONFIG node. | ||
| 11 | For a description of the MMSYS_CONFIG binding, see | ||
| 12 | Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt. | ||
| 13 | |||
| 14 | DISP function blocks | ||
| 15 | ==================== | ||
| 16 | |||
| 17 | A display stream starts at a source function block that reads pixel data from | ||
| 18 | memory and ends with a sink function block that drives pixels on a display | ||
| 19 | interface, or writes pixels back to memory. All DISP function blocks have | ||
| 20 | their own register space, interrupt, and clock gate. The blocks that can | ||
| 21 | access memory additionally have to list the IOMMU and local arbiter they are | ||
| 22 | connected to. | ||
| 23 | |||
| 24 | For a description of the display interface sink function blocks, see | ||
| 25 | Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt and | ||
| 26 | Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt. | ||
| 27 | |||
| 28 | Required properties (all function blocks): | ||
| 29 | - compatible: "mediatek,<chip>-disp-<function>", one of | ||
| 30 | "mediatek,<chip>-disp-ovl" - overlay (4 layers, blending, csc) | ||
| 31 | "mediatek,<chip>-disp-rdma" - read DMA / line buffer | ||
| 32 | "mediatek,<chip>-disp-wdma" - write DMA | ||
| 33 | "mediatek,<chip>-disp-color" - color processor | ||
| 34 | "mediatek,<chip>-disp-aal" - adaptive ambient light controller | ||
| 35 | "mediatek,<chip>-disp-gamma" - gamma correction | ||
| 36 | "mediatek,<chip>-disp-merge" - merge streams from two RDMA sources | ||
| 37 | "mediatek,<chip>-disp-split" - split stream to two encoders | ||
| 38 | "mediatek,<chip>-disp-ufoe" - data compression engine | ||
| 39 | "mediatek,<chip>-dsi" - DSI controller, see mediatek,dsi.txt | ||
| 40 | "mediatek,<chip>-dpi" - DPI controller, see mediatek,dpi.txt | ||
| 41 | "mediatek,<chip>-disp-mutex" - display mutex | ||
| 42 | "mediatek,<chip>-disp-od" - overdrive | ||
| 43 | - reg: Physical base address and length of the function block register space | ||
| 44 | - interrupts: The interrupt signal from the function block (required, except for | ||
| 45 | merge and split function blocks). | ||
| 46 | - clocks: device clocks | ||
| 47 | See Documentation/devicetree/bindings/clock/clock-bindings.txt for details. | ||
| 48 | For most function blocks this is just a single clock input. Only the DSI and | ||
| 49 | DPI controller nodes have multiple clock inputs. These are documented in | ||
| 50 | mediatek,dsi.txt and mediatek,dpi.txt, respectively. | ||
| 51 | |||
| 52 | Required properties (DMA function blocks): | ||
| 53 | - compatible: Should be one of | ||
| 54 | "mediatek,<chip>-disp-ovl" | ||
| 55 | "mediatek,<chip>-disp-rdma" | ||
| 56 | "mediatek,<chip>-disp-wdma" | ||
| 57 | - larb: Should contain a phandle pointing to the local arbiter device as defined | ||
| 58 | in Documentation/devicetree/bindings/soc/mediatek/mediatek,smi-larb.txt | ||
| 59 | - iommus: Should point to the respective IOMMU block with master port as | ||
| 60 | argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.txt | ||
| 61 | for details. | ||
| 62 | |||
| 63 | Examples: | ||
| 64 | |||
| 65 | mmsys: clock-controller@14000000 { | ||
| 66 | compatible = "mediatek,mt8173-mmsys", "syscon"; | ||
| 67 | reg = <0 0x14000000 0 0x1000>; | ||
| 68 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 69 | #clock-cells = <1>; | ||
| 70 | }; | ||
| 71 | |||
| 72 | ovl0: ovl@1400c000 { | ||
| 73 | compatible = "mediatek,mt8173-disp-ovl"; | ||
| 74 | reg = <0 0x1400c000 0 0x1000>; | ||
| 75 | interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>; | ||
| 76 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 77 | clocks = <&mmsys CLK_MM_DISP_OVL0>; | ||
| 78 | iommus = <&iommu M4U_PORT_DISP_OVL0>; | ||
| 79 | mediatek,larb = <&larb0>; | ||
| 80 | }; | ||
| 81 | |||
| 82 | ovl1: ovl@1400d000 { | ||
| 83 | compatible = "mediatek,mt8173-disp-ovl"; | ||
| 84 | reg = <0 0x1400d000 0 0x1000>; | ||
| 85 | interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>; | ||
| 86 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 87 | clocks = <&mmsys CLK_MM_DISP_OVL1>; | ||
| 88 | iommus = <&iommu M4U_PORT_DISP_OVL1>; | ||
| 89 | mediatek,larb = <&larb4>; | ||
| 90 | }; | ||
| 91 | |||
| 92 | rdma0: rdma@1400e000 { | ||
| 93 | compatible = "mediatek,mt8173-disp-rdma"; | ||
| 94 | reg = <0 0x1400e000 0 0x1000>; | ||
| 95 | interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_LOW>; | ||
| 96 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 97 | clocks = <&mmsys CLK_MM_DISP_RDMA0>; | ||
| 98 | iommus = <&iommu M4U_PORT_DISP_RDMA0>; | ||
| 99 | mediatek,larb = <&larb0>; | ||
| 100 | }; | ||
| 101 | |||
| 102 | rdma1: rdma@1400f000 { | ||
| 103 | compatible = "mediatek,mt8173-disp-rdma"; | ||
| 104 | reg = <0 0x1400f000 0 0x1000>; | ||
| 105 | interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_LOW>; | ||
| 106 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 107 | clocks = <&mmsys CLK_MM_DISP_RDMA1>; | ||
| 108 | iommus = <&iommu M4U_PORT_DISP_RDMA1>; | ||
| 109 | mediatek,larb = <&larb4>; | ||
| 110 | }; | ||
| 111 | |||
| 112 | rdma2: rdma@14010000 { | ||
| 113 | compatible = "mediatek,mt8173-disp-rdma"; | ||
| 114 | reg = <0 0x14010000 0 0x1000>; | ||
| 115 | interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_LOW>; | ||
| 116 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 117 | clocks = <&mmsys CLK_MM_DISP_RDMA2>; | ||
| 118 | iommus = <&iommu M4U_PORT_DISP_RDMA2>; | ||
| 119 | mediatek,larb = <&larb4>; | ||
| 120 | }; | ||
| 121 | |||
| 122 | wdma0: wdma@14011000 { | ||
| 123 | compatible = "mediatek,mt8173-disp-wdma"; | ||
| 124 | reg = <0 0x14011000 0 0x1000>; | ||
| 125 | interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_LOW>; | ||
| 126 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 127 | clocks = <&mmsys CLK_MM_DISP_WDMA0>; | ||
| 128 | iommus = <&iommu M4U_PORT_DISP_WDMA0>; | ||
| 129 | mediatek,larb = <&larb0>; | ||
| 130 | }; | ||
| 131 | |||
| 132 | wdma1: wdma@14012000 { | ||
| 133 | compatible = "mediatek,mt8173-disp-wdma"; | ||
| 134 | reg = <0 0x14012000 0 0x1000>; | ||
| 135 | interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_LOW>; | ||
| 136 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 137 | clocks = <&mmsys CLK_MM_DISP_WDMA1>; | ||
| 138 | iommus = <&iommu M4U_PORT_DISP_WDMA1>; | ||
| 139 | mediatek,larb = <&larb4>; | ||
| 140 | }; | ||
| 141 | |||
| 142 | color0: color@14013000 { | ||
| 143 | compatible = "mediatek,mt8173-disp-color"; | ||
| 144 | reg = <0 0x14013000 0 0x1000>; | ||
| 145 | interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_LOW>; | ||
| 146 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 147 | clocks = <&mmsys CLK_MM_DISP_COLOR0>; | ||
| 148 | }; | ||
| 149 | |||
| 150 | color1: color@14014000 { | ||
| 151 | compatible = "mediatek,mt8173-disp-color"; | ||
| 152 | reg = <0 0x14014000 0 0x1000>; | ||
| 153 | interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_LOW>; | ||
| 154 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 155 | clocks = <&mmsys CLK_MM_DISP_COLOR1>; | ||
| 156 | }; | ||
| 157 | |||
| 158 | aal@14015000 { | ||
| 159 | compatible = "mediatek,mt8173-disp-aal"; | ||
| 160 | reg = <0 0x14015000 0 0x1000>; | ||
| 161 | interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_LOW>; | ||
| 162 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 163 | clocks = <&mmsys CLK_MM_DISP_AAL>; | ||
| 164 | }; | ||
| 165 | |||
| 166 | gamma@14016000 { | ||
| 167 | compatible = "mediatek,mt8173-disp-gamma"; | ||
| 168 | reg = <0 0x14016000 0 0x1000>; | ||
| 169 | interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_LOW>; | ||
| 170 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 171 | clocks = <&mmsys CLK_MM_DISP_GAMMA>; | ||
| 172 | }; | ||
| 173 | |||
| 174 | ufoe@1401a000 { | ||
| 175 | compatible = "mediatek,mt8173-disp-ufoe"; | ||
| 176 | reg = <0 0x1401a000 0 0x1000>; | ||
| 177 | interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_LOW>; | ||
| 178 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 179 | clocks = <&mmsys CLK_MM_DISP_UFOE>; | ||
| 180 | }; | ||
| 181 | |||
| 182 | dsi0: dsi@1401b000 { | ||
| 183 | /* See mediatek,dsi.txt for details */ | ||
| 184 | }; | ||
| 185 | |||
| 186 | dpi0: dpi@1401d000 { | ||
| 187 | /* See mediatek,dpi.txt for details */ | ||
| 188 | }; | ||
| 189 | |||
| 190 | mutex: mutex@14020000 { | ||
| 191 | compatible = "mediatek,mt8173-disp-mutex"; | ||
| 192 | reg = <0 0x14020000 0 0x1000>; | ||
| 193 | interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_LOW>; | ||
| 194 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 195 | clocks = <&mmsys CLK_MM_MUTEX_32K>; | ||
| 196 | }; | ||
| 197 | |||
| 198 | od@14023000 { | ||
| 199 | compatible = "mediatek,mt8173-disp-od"; | ||
| 200 | reg = <0 0x14023000 0 0x1000>; | ||
| 201 | power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>; | ||
| 202 | clocks = <&mmsys CLK_MM_DISP_OD>; | ||
| 203 | }; | ||
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt new file mode 100644 index 000000000000..b6a7e7397b8b --- /dev/null +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | Mediatek DPI Device | ||
| 2 | =================== | ||
| 3 | |||
| 4 | The Mediatek DPI function block is a sink of the display subsystem and | ||
| 5 | provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel | ||
| 6 | output bus. | ||
| 7 | |||
| 8 | Required properties: | ||
| 9 | - compatible: "mediatek,<chip>-dpi" | ||
| 10 | - reg: Physical base address and length of the controller's registers | ||
| 11 | - interrupts: The interrupt signal from the function block. | ||
| 12 | - clocks: device clocks | ||
| 13 | See Documentation/devicetree/bindings/clock/clock-bindings.txt for details. | ||
| 14 | - clock-names: must contain "pixel", "engine", and "pll" | ||
| 15 | - port: Output port node with endpoint definitions as described in | ||
| 16 | Documentation/devicetree/bindings/graph.txt. This port should be connected | ||
| 17 | to the input port of an attached HDMI or LVDS encoder chip. | ||
| 18 | |||
| 19 | Example: | ||
| 20 | |||
| 21 | dpi0: dpi@1401d000 { | ||
| 22 | compatible = "mediatek,mt8173-dpi"; | ||
| 23 | reg = <0 0x1401d000 0 0x1000>; | ||
| 24 | interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>; | ||
| 25 | clocks = <&mmsys CLK_MM_DPI_PIXEL>, | ||
| 26 | <&mmsys CLK_MM_DPI_ENGINE>, | ||
| 27 | <&apmixedsys CLK_APMIXED_TVDPLL>; | ||
| 28 | clock-names = "pixel", "engine", "pll"; | ||
| 29 | |||
| 30 | port { | ||
| 31 | dpi0_out: endpoint { | ||
| 32 | remote-endpoint = <&hdmi0_in>; | ||
| 33 | }; | ||
| 34 | }; | ||
| 35 | }; | ||
diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt new file mode 100644 index 000000000000..2b1585a34b85 --- /dev/null +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | Mediatek DSI Device | ||
| 2 | =================== | ||
| 3 | |||
| 4 | The Mediatek DSI function block is a sink of the display subsystem and can | ||
| 5 | drive up to 4-lane MIPI DSI output. Two DSIs can be synchronized for dual- | ||
| 6 | channel output. | ||
| 7 | |||
| 8 | Required properties: | ||
| 9 | - compatible: "mediatek,<chip>-dsi" | ||
| 10 | - reg: Physical base address and length of the controller's registers | ||
| 11 | - interrupts: The interrupt signal from the function block. | ||
| 12 | - clocks: device clocks | ||
| 13 | See Documentation/devicetree/bindings/clock/clock-bindings.txt for details. | ||
| 14 | - clock-names: must contain "engine", "digital", and "hs" | ||
| 15 | - phys: phandle link to the MIPI D-PHY controller. | ||
| 16 | - phy-names: must contain "dphy" | ||
| 17 | - port: Output port node with endpoint definitions as described in | ||
| 18 | Documentation/devicetree/bindings/graph.txt. This port should be connected | ||
| 19 | to the input port of an attached DSI panel or DSI-to-eDP encoder chip. | ||
| 20 | |||
| 21 | MIPI TX Configuration Module | ||
| 22 | ============================ | ||
| 23 | |||
| 24 | The MIPI TX configuration module controls the MIPI D-PHY. | ||
| 25 | |||
| 26 | Required properties: | ||
| 27 | - compatible: "mediatek,<chip>-mipi-tx" | ||
| 28 | - reg: Physical base address and length of the controller's registers | ||
| 29 | - clocks: PLL reference clock | ||
| 30 | - clock-output-names: name of the output clock line to the DSI encoder | ||
| 31 | - #clock-cells: must be <0>; | ||
| 32 | - #phy-cells: must be <0>. | ||
| 33 | |||
| 34 | Example: | ||
| 35 | |||
| 36 | mipi_tx0: mipi-dphy@10215000 { | ||
| 37 | compatible = "mediatek,mt8173-mipi-tx"; | ||
| 38 | reg = <0 0x10215000 0 0x1000>; | ||
| 39 | clocks = <&clk26m>; | ||
| 40 | clock-output-names = "mipi_tx0_pll"; | ||
| 41 | #clock-cells = <0>; | ||
| 42 | #phy-cells = <0>; | ||
| 43 | }; | ||
| 44 | |||
| 45 | dsi0: dsi@1401b000 { | ||
| 46 | compatible = "mediatek,mt8173-dsi"; | ||
| 47 | reg = <0 0x1401b000 0 0x1000>; | ||
| 48 | interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_LOW>; | ||
| 49 | clocks = <&mmsys MM_DSI0_ENGINE>, <&mmsys MM_DSI0_DIGITAL>, | ||
| 50 | <&mipi_tx0>; | ||
| 51 | clock-names = "engine", "digital", "hs"; | ||
| 52 | phys = <&mipi_tx0>; | ||
| 53 | phy-names = "dphy"; | ||
| 54 | |||
| 55 | port { | ||
| 56 | dsi0_out: endpoint { | ||
| 57 | remote-endpoint = <&panel_in>; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | }; | ||
diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c index 227e356403d9..10c986018a08 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c | |||
| @@ -61,7 +61,6 @@ static const struct mtk_fixed_factor top_divs[] __initconst = { | |||
| 61 | FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), | 61 | FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), |
| 62 | FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), | 62 | FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), |
| 63 | 63 | ||
| 64 | FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "tvdpll_445p5m", 1, 3), | ||
| 65 | FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), | 64 | FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), |
| 66 | FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), | 65 | FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), |
| 67 | 66 | ||
| @@ -558,7 +557,11 @@ static const struct mtk_composite top_muxes[] __initconst = { | |||
| 558 | MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), | 557 | MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), |
| 559 | MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), | 558 | MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), |
| 560 | /* CLK_CFG_6 */ | 559 | /* CLK_CFG_6 */ |
| 561 | MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7), | 560 | /* |
| 561 | * The dpi0_sel clock should not propagate rate changes to its parent | ||
| 562 | * clock so the dpi driver can have full control over PLL and divider. | ||
| 563 | */ | ||
| 564 | MUX_GATE_FLAGS(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7, 0), | ||
| 562 | MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), | 565 | MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), |
| 563 | MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), | 566 | MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), |
| 564 | MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), | 567 | MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), |
| @@ -1091,6 +1094,11 @@ static void __init mtk_apmixedsys_init(struct device_node *node) | |||
| 1091 | clk_data->clks[cku->id] = clk; | 1094 | clk_data->clks[cku->id] = clk; |
| 1092 | } | 1095 | } |
| 1093 | 1096 | ||
| 1097 | clk = clk_register_divider(NULL, "hdmi_ref", "tvdpll_594m", 0, | ||
| 1098 | base + 0x40, 16, 3, CLK_DIVIDER_POWER_OF_TWO, | ||
| 1099 | NULL); | ||
| 1100 | clk_data->clks[CLK_APMIXED_HDMI_REF] = clk; | ||
| 1101 | |||
| 1094 | r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | 1102 | r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); |
| 1095 | if (r) | 1103 | if (r) |
| 1096 | pr_err("%s(): could not register clock provider: %d\n", | 1104 | pr_err("%s(): could not register clock provider: %d\n", |
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index 32d2e455eb3f..9f24fcfa304f 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h | |||
| @@ -83,7 +83,11 @@ struct mtk_composite { | |||
| 83 | signed char num_parents; | 83 | signed char num_parents; |
| 84 | }; | 84 | }; |
| 85 | 85 | ||
| 86 | #define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ | 86 | /* |
| 87 | * In case the rate change propagation to parent clocks is undesirable, | ||
| 88 | * this macro allows to specify the clock flags manually. | ||
| 89 | */ | ||
| 90 | #define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) { \ | ||
| 87 | .id = _id, \ | 91 | .id = _id, \ |
| 88 | .name = _name, \ | 92 | .name = _name, \ |
| 89 | .mux_reg = _reg, \ | 93 | .mux_reg = _reg, \ |
| @@ -94,9 +98,16 @@ struct mtk_composite { | |||
| 94 | .divider_shift = -1, \ | 98 | .divider_shift = -1, \ |
| 95 | .parent_names = _parents, \ | 99 | .parent_names = _parents, \ |
| 96 | .num_parents = ARRAY_SIZE(_parents), \ | 100 | .num_parents = ARRAY_SIZE(_parents), \ |
| 97 | .flags = CLK_SET_RATE_PARENT, \ | 101 | .flags = _flags, \ |
| 98 | } | 102 | } |
| 99 | 103 | ||
| 104 | /* | ||
| 105 | * Unless necessary, all MUX_GATE clocks propagate rate changes to their | ||
| 106 | * parent clock by default. | ||
| 107 | */ | ||
| 108 | #define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) \ | ||
| 109 | MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, CLK_SET_RATE_PARENT) | ||
| 110 | |||
| 100 | #define MUX(_id, _name, _parents, _reg, _shift, _width) { \ | 111 | #define MUX(_id, _name, _parents, _reg, _shift, _width) { \ |
| 101 | .id = _id, \ | 112 | .id = _id, \ |
| 102 | .name = _name, \ | 113 | .name = _name, \ |
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 16e4c2135aad..fc357319de35 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
| @@ -288,3 +288,5 @@ source "drivers/gpu/drm/etnaviv/Kconfig" | |||
| 288 | source "drivers/gpu/drm/arc/Kconfig" | 288 | source "drivers/gpu/drm/arc/Kconfig" |
| 289 | 289 | ||
| 290 | source "drivers/gpu/drm/hisilicon/Kconfig" | 290 | source "drivers/gpu/drm/hisilicon/Kconfig" |
| 291 | |||
| 292 | source "drivers/gpu/drm/mediatek/Kconfig" | ||
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 43c2abf425ee..2bd3e5aa43c6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
| @@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_MSM) += msm/ | |||
| 74 | obj-$(CONFIG_DRM_TEGRA) += tegra/ | 74 | obj-$(CONFIG_DRM_TEGRA) += tegra/ |
| 75 | obj-$(CONFIG_DRM_STI) += sti/ | 75 | obj-$(CONFIG_DRM_STI) += sti/ |
| 76 | obj-$(CONFIG_DRM_IMX) += imx/ | 76 | obj-$(CONFIG_DRM_IMX) += imx/ |
| 77 | obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ | ||
| 77 | obj-y += i2c/ | 78 | obj-y += i2c/ |
| 78 | obj-y += panel/ | 79 | obj-y += panel/ |
| 79 | obj-y += bridge/ | 80 | obj-y += bridge/ |
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig new file mode 100644 index 000000000000..eeefc971801a --- /dev/null +++ b/drivers/gpu/drm/mediatek/Kconfig | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | config DRM_MEDIATEK | ||
| 2 | tristate "DRM Support for Mediatek SoCs" | ||
| 3 | depends on DRM | ||
| 4 | depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST) | ||
| 5 | select DRM_GEM_CMA_HELPER | ||
| 6 | select DRM_KMS_HELPER | ||
| 7 | select DRM_MIPI_DSI | ||
| 8 | select DRM_PANEL | ||
| 9 | select IOMMU_DMA | ||
| 10 | select MEMORY | ||
| 11 | select MTK_SMI | ||
| 12 | help | ||
| 13 | Choose this option if you have a Mediatek SoCs. | ||
| 14 | The module will be called mediatek-drm | ||
| 15 | This driver provides kernel mode setting and | ||
| 16 | buffer management to userspace. | ||
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile new file mode 100644 index 000000000000..5fcf58e87786 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Makefile | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | mediatek-drm-y := mtk_disp_ovl.o \ | ||
| 2 | mtk_disp_rdma.o \ | ||
| 3 | mtk_drm_crtc.o \ | ||
| 4 | mtk_drm_ddp.o \ | ||
| 5 | mtk_drm_ddp_comp.o \ | ||
| 6 | mtk_drm_drv.o \ | ||
| 7 | mtk_drm_fb.o \ | ||
| 8 | mtk_drm_gem.o \ | ||
| 9 | mtk_drm_plane.o \ | ||
| 10 | mtk_dsi.o \ | ||
| 11 | mtk_mipi_tx.o \ | ||
| 12 | mtk_dpi.o | ||
| 13 | |||
| 14 | obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c new file mode 100644 index 000000000000..8f62671fcfbf --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c | |||
| @@ -0,0 +1,302 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <linux/clk.h> | ||
| 16 | #include <linux/component.h> | ||
| 17 | #include <linux/of_device.h> | ||
| 18 | #include <linux/of_irq.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | |||
| 21 | #include "mtk_drm_crtc.h" | ||
| 22 | #include "mtk_drm_ddp_comp.h" | ||
| 23 | |||
| 24 | #define DISP_REG_OVL_INTEN 0x0004 | ||
| 25 | #define OVL_FME_CPL_INT BIT(1) | ||
| 26 | #define DISP_REG_OVL_INTSTA 0x0008 | ||
| 27 | #define DISP_REG_OVL_EN 0x000c | ||
| 28 | #define DISP_REG_OVL_RST 0x0014 | ||
| 29 | #define DISP_REG_OVL_ROI_SIZE 0x0020 | ||
| 30 | #define DISP_REG_OVL_ROI_BGCLR 0x0028 | ||
| 31 | #define DISP_REG_OVL_SRC_CON 0x002c | ||
| 32 | #define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n)) | ||
| 33 | #define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n)) | ||
| 34 | #define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n)) | ||
| 35 | #define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n)) | ||
| 36 | #define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n)) | ||
| 37 | #define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n)) | ||
| 38 | #define DISP_REG_OVL_ADDR(n) (0x0f40 + 0x20 * (n)) | ||
| 39 | |||
| 40 | #define OVL_RDMA_MEM_GMC 0x40402020 | ||
| 41 | |||
| 42 | #define OVL_CON_BYTE_SWAP BIT(24) | ||
| 43 | #define OVL_CON_CLRFMT_RGB565 (0 << 12) | ||
| 44 | #define OVL_CON_CLRFMT_RGB888 (1 << 12) | ||
| 45 | #define OVL_CON_CLRFMT_RGBA8888 (2 << 12) | ||
| 46 | #define OVL_CON_CLRFMT_ARGB8888 (3 << 12) | ||
| 47 | #define OVL_CON_AEN BIT(8) | ||
| 48 | #define OVL_CON_ALPHA 0xff | ||
| 49 | |||
| 50 | /** | ||
| 51 | * struct mtk_disp_ovl - DISP_OVL driver structure | ||
| 52 | * @ddp_comp - structure containing type enum and hardware resources | ||
| 53 | * @crtc - associated crtc to report vblank events to | ||
| 54 | */ | ||
| 55 | struct mtk_disp_ovl { | ||
| 56 | struct mtk_ddp_comp ddp_comp; | ||
| 57 | struct drm_crtc *crtc; | ||
| 58 | }; | ||
| 59 | |||
| 60 | static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id) | ||
| 61 | { | ||
| 62 | struct mtk_disp_ovl *priv = dev_id; | ||
| 63 | struct mtk_ddp_comp *ovl = &priv->ddp_comp; | ||
| 64 | |||
| 65 | /* Clear frame completion interrupt */ | ||
| 66 | writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA); | ||
| 67 | |||
| 68 | if (!priv->crtc) | ||
| 69 | return IRQ_NONE; | ||
| 70 | |||
| 71 | mtk_crtc_ddp_irq(priv->crtc, ovl); | ||
| 72 | |||
| 73 | return IRQ_HANDLED; | ||
| 74 | } | ||
| 75 | |||
| 76 | static void mtk_ovl_enable_vblank(struct mtk_ddp_comp *comp, | ||
| 77 | struct drm_crtc *crtc) | ||
| 78 | { | ||
| 79 | struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl, | ||
| 80 | ddp_comp); | ||
| 81 | |||
| 82 | priv->crtc = crtc; | ||
| 83 | writel_relaxed(OVL_FME_CPL_INT, comp->regs + DISP_REG_OVL_INTEN); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void mtk_ovl_disable_vblank(struct mtk_ddp_comp *comp) | ||
| 87 | { | ||
| 88 | struct mtk_disp_ovl *priv = container_of(comp, struct mtk_disp_ovl, | ||
| 89 | ddp_comp); | ||
| 90 | |||
| 91 | priv->crtc = NULL; | ||
| 92 | writel_relaxed(0x0, comp->regs + DISP_REG_OVL_INTEN); | ||
| 93 | } | ||
| 94 | |||
| 95 | static void mtk_ovl_start(struct mtk_ddp_comp *comp) | ||
| 96 | { | ||
| 97 | writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN); | ||
| 98 | } | ||
| 99 | |||
| 100 | static void mtk_ovl_stop(struct mtk_ddp_comp *comp) | ||
| 101 | { | ||
| 102 | writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w, | ||
| 106 | unsigned int h, unsigned int vrefresh) | ||
| 107 | { | ||
| 108 | if (w != 0 && h != 0) | ||
| 109 | writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE); | ||
| 110 | writel_relaxed(0x0, comp->regs + DISP_REG_OVL_ROI_BGCLR); | ||
| 111 | |||
| 112 | writel(0x1, comp->regs + DISP_REG_OVL_RST); | ||
| 113 | writel(0x0, comp->regs + DISP_REG_OVL_RST); | ||
| 114 | } | ||
| 115 | |||
| 116 | static void mtk_ovl_layer_on(struct mtk_ddp_comp *comp, unsigned int idx) | ||
| 117 | { | ||
| 118 | unsigned int reg; | ||
| 119 | |||
| 120 | writel(0x1, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx)); | ||
| 121 | writel(OVL_RDMA_MEM_GMC, comp->regs + DISP_REG_OVL_RDMA_GMC(idx)); | ||
| 122 | |||
| 123 | reg = readl(comp->regs + DISP_REG_OVL_SRC_CON); | ||
| 124 | reg = reg | BIT(idx); | ||
| 125 | writel(reg, comp->regs + DISP_REG_OVL_SRC_CON); | ||
| 126 | } | ||
| 127 | |||
| 128 | static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx) | ||
| 129 | { | ||
| 130 | unsigned int reg; | ||
| 131 | |||
| 132 | reg = readl(comp->regs + DISP_REG_OVL_SRC_CON); | ||
| 133 | reg = reg & ~BIT(idx); | ||
| 134 | writel(reg, comp->regs + DISP_REG_OVL_SRC_CON); | ||
| 135 | |||
| 136 | writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx)); | ||
| 137 | } | ||
| 138 | |||
| 139 | static unsigned int ovl_fmt_convert(unsigned int fmt) | ||
| 140 | { | ||
| 141 | switch (fmt) { | ||
| 142 | default: | ||
| 143 | case DRM_FORMAT_RGB565: | ||
| 144 | return OVL_CON_CLRFMT_RGB565; | ||
| 145 | case DRM_FORMAT_BGR565: | ||
| 146 | return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP; | ||
| 147 | case DRM_FORMAT_RGB888: | ||
| 148 | return OVL_CON_CLRFMT_RGB888; | ||
| 149 | case DRM_FORMAT_BGR888: | ||
| 150 | return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP; | ||
| 151 | case DRM_FORMAT_RGBX8888: | ||
| 152 | case DRM_FORMAT_RGBA8888: | ||
| 153 | return OVL_CON_CLRFMT_ARGB8888; | ||
| 154 | case DRM_FORMAT_BGRX8888: | ||
| 155 | case DRM_FORMAT_BGRA8888: | ||
| 156 | return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP; | ||
| 157 | case DRM_FORMAT_XRGB8888: | ||
| 158 | case DRM_FORMAT_ARGB8888: | ||
| 159 | return OVL_CON_CLRFMT_RGBA8888; | ||
| 160 | case DRM_FORMAT_XBGR8888: | ||
| 161 | case DRM_FORMAT_ABGR8888: | ||
| 162 | return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx, | ||
| 167 | struct mtk_plane_state *state) | ||
| 168 | { | ||
| 169 | struct mtk_plane_pending_state *pending = &state->pending; | ||
| 170 | unsigned int addr = pending->addr; | ||
| 171 | unsigned int pitch = pending->pitch & 0xffff; | ||
| 172 | unsigned int fmt = pending->format; | ||
| 173 | unsigned int offset = (pending->y << 16) | pending->x; | ||
| 174 | unsigned int src_size = (pending->height << 16) | pending->width; | ||
| 175 | unsigned int con; | ||
| 176 | |||
| 177 | if (!pending->enable) | ||
| 178 | mtk_ovl_layer_off(comp, idx); | ||
| 179 | |||
| 180 | con = ovl_fmt_convert(fmt); | ||
| 181 | if (idx != 0) | ||
| 182 | con |= OVL_CON_AEN | OVL_CON_ALPHA; | ||
| 183 | |||
| 184 | writel_relaxed(con, comp->regs + DISP_REG_OVL_CON(idx)); | ||
| 185 | writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx)); | ||
| 186 | writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx)); | ||
| 187 | writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx)); | ||
| 188 | writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx)); | ||
| 189 | |||
| 190 | if (pending->enable) | ||
| 191 | mtk_ovl_layer_on(comp, idx); | ||
| 192 | } | ||
| 193 | |||
| 194 | static const struct mtk_ddp_comp_funcs mtk_disp_ovl_funcs = { | ||
| 195 | .config = mtk_ovl_config, | ||
| 196 | .start = mtk_ovl_start, | ||
| 197 | .stop = mtk_ovl_stop, | ||
| 198 | .enable_vblank = mtk_ovl_enable_vblank, | ||
| 199 | .disable_vblank = mtk_ovl_disable_vblank, | ||
| 200 | .layer_on = mtk_ovl_layer_on, | ||
| 201 | .layer_off = mtk_ovl_layer_off, | ||
| 202 | .layer_config = mtk_ovl_layer_config, | ||
| 203 | }; | ||
| 204 | |||
| 205 | static int mtk_disp_ovl_bind(struct device *dev, struct device *master, | ||
| 206 | void *data) | ||
| 207 | { | ||
| 208 | struct mtk_disp_ovl *priv = dev_get_drvdata(dev); | ||
| 209 | struct drm_device *drm_dev = data; | ||
| 210 | int ret; | ||
| 211 | |||
| 212 | ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp); | ||
| 213 | if (ret < 0) { | ||
| 214 | dev_err(dev, "Failed to register component %s: %d\n", | ||
| 215 | dev->of_node->full_name, ret); | ||
| 216 | return ret; | ||
| 217 | } | ||
| 218 | |||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | static void mtk_disp_ovl_unbind(struct device *dev, struct device *master, | ||
| 223 | void *data) | ||
| 224 | { | ||
| 225 | struct mtk_disp_ovl *priv = dev_get_drvdata(dev); | ||
| 226 | struct drm_device *drm_dev = data; | ||
| 227 | |||
| 228 | mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp); | ||
| 229 | } | ||
| 230 | |||
| 231 | static const struct component_ops mtk_disp_ovl_component_ops = { | ||
| 232 | .bind = mtk_disp_ovl_bind, | ||
| 233 | .unbind = mtk_disp_ovl_unbind, | ||
| 234 | }; | ||
| 235 | |||
| 236 | static int mtk_disp_ovl_probe(struct platform_device *pdev) | ||
| 237 | { | ||
| 238 | struct device *dev = &pdev->dev; | ||
| 239 | struct mtk_disp_ovl *priv; | ||
| 240 | int comp_id; | ||
| 241 | int irq; | ||
| 242 | int ret; | ||
| 243 | |||
| 244 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 245 | if (!priv) | ||
| 246 | return -ENOMEM; | ||
| 247 | |||
| 248 | irq = platform_get_irq(pdev, 0); | ||
| 249 | if (irq < 0) | ||
| 250 | return irq; | ||
| 251 | |||
| 252 | ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler, | ||
| 253 | IRQF_TRIGGER_NONE, dev_name(dev), priv); | ||
| 254 | if (ret < 0) { | ||
| 255 | dev_err(dev, "Failed to request irq %d: %d\n", irq, ret); | ||
| 256 | return ret; | ||
| 257 | } | ||
| 258 | |||
| 259 | comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_OVL); | ||
| 260 | if (comp_id < 0) { | ||
| 261 | dev_err(dev, "Failed to identify by alias: %d\n", comp_id); | ||
| 262 | return comp_id; | ||
| 263 | } | ||
| 264 | |||
| 265 | ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id, | ||
| 266 | &mtk_disp_ovl_funcs); | ||
| 267 | if (ret) { | ||
| 268 | dev_err(dev, "Failed to initialize component: %d\n", ret); | ||
| 269 | return ret; | ||
| 270 | } | ||
| 271 | |||
| 272 | platform_set_drvdata(pdev, priv); | ||
| 273 | |||
| 274 | ret = component_add(dev, &mtk_disp_ovl_component_ops); | ||
| 275 | if (ret) | ||
| 276 | dev_err(dev, "Failed to add component: %d\n", ret); | ||
| 277 | |||
| 278 | return ret; | ||
| 279 | } | ||
| 280 | |||
| 281 | static int mtk_disp_ovl_remove(struct platform_device *pdev) | ||
| 282 | { | ||
| 283 | component_del(&pdev->dev, &mtk_disp_ovl_component_ops); | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = { | ||
| 289 | { .compatible = "mediatek,mt8173-disp-ovl", }, | ||
| 290 | {}, | ||
| 291 | }; | ||
| 292 | MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match); | ||
| 293 | |||
| 294 | struct platform_driver mtk_disp_ovl_driver = { | ||
| 295 | .probe = mtk_disp_ovl_probe, | ||
| 296 | .remove = mtk_disp_ovl_remove, | ||
| 297 | .driver = { | ||
| 298 | .name = "mediatek-disp-ovl", | ||
| 299 | .owner = THIS_MODULE, | ||
| 300 | .of_match_table = mtk_disp_ovl_driver_dt_match, | ||
| 301 | }, | ||
| 302 | }; | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c new file mode 100644 index 000000000000..5fb80cbe4c5b --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <linux/clk.h> | ||
| 16 | #include <linux/component.h> | ||
| 17 | #include <linux/of_device.h> | ||
| 18 | #include <linux/of_irq.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | |||
| 21 | #include "mtk_drm_crtc.h" | ||
| 22 | #include "mtk_drm_ddp_comp.h" | ||
| 23 | |||
| 24 | #define DISP_REG_RDMA_INT_ENABLE 0x0000 | ||
| 25 | #define DISP_REG_RDMA_INT_STATUS 0x0004 | ||
| 26 | #define RDMA_TARGET_LINE_INT BIT(5) | ||
| 27 | #define RDMA_FIFO_UNDERFLOW_INT BIT(4) | ||
| 28 | #define RDMA_EOF_ABNORMAL_INT BIT(3) | ||
| 29 | #define RDMA_FRAME_END_INT BIT(2) | ||
| 30 | #define RDMA_FRAME_START_INT BIT(1) | ||
| 31 | #define RDMA_REG_UPDATE_INT BIT(0) | ||
| 32 | #define DISP_REG_RDMA_GLOBAL_CON 0x0010 | ||
| 33 | #define RDMA_ENGINE_EN BIT(0) | ||
| 34 | #define DISP_REG_RDMA_SIZE_CON_0 0x0014 | ||
| 35 | #define DISP_REG_RDMA_SIZE_CON_1 0x0018 | ||
| 36 | #define DISP_REG_RDMA_TARGET_LINE 0x001c | ||
| 37 | #define DISP_REG_RDMA_FIFO_CON 0x0040 | ||
| 38 | #define RDMA_FIFO_UNDERFLOW_EN BIT(31) | ||
| 39 | #define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) | ||
| 40 | #define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16) | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct mtk_disp_rdma - DISP_RDMA driver structure | ||
| 44 | * @ddp_comp - structure containing type enum and hardware resources | ||
| 45 | * @crtc - associated crtc to report irq events to | ||
| 46 | */ | ||
| 47 | struct mtk_disp_rdma { | ||
| 48 | struct mtk_ddp_comp ddp_comp; | ||
| 49 | struct drm_crtc *crtc; | ||
| 50 | }; | ||
| 51 | |||
| 52 | static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id) | ||
| 53 | { | ||
| 54 | struct mtk_disp_rdma *priv = dev_id; | ||
| 55 | struct mtk_ddp_comp *rdma = &priv->ddp_comp; | ||
| 56 | |||
| 57 | /* Clear frame completion interrupt */ | ||
| 58 | writel(0x0, rdma->regs + DISP_REG_RDMA_INT_STATUS); | ||
| 59 | |||
| 60 | if (!priv->crtc) | ||
| 61 | return IRQ_NONE; | ||
| 62 | |||
| 63 | mtk_crtc_ddp_irq(priv->crtc, rdma); | ||
| 64 | |||
| 65 | return IRQ_HANDLED; | ||
| 66 | } | ||
| 67 | |||
| 68 | static void rdma_update_bits(struct mtk_ddp_comp *comp, unsigned int reg, | ||
| 69 | unsigned int mask, unsigned int val) | ||
| 70 | { | ||
| 71 | unsigned int tmp = readl(comp->regs + reg); | ||
| 72 | |||
| 73 | tmp = (tmp & ~mask) | (val & mask); | ||
| 74 | writel(tmp, comp->regs + reg); | ||
| 75 | } | ||
| 76 | |||
| 77 | static void mtk_rdma_enable_vblank(struct mtk_ddp_comp *comp, | ||
| 78 | struct drm_crtc *crtc) | ||
| 79 | { | ||
| 80 | struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma, | ||
| 81 | ddp_comp); | ||
| 82 | |||
| 83 | priv->crtc = crtc; | ||
| 84 | rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, | ||
| 85 | RDMA_FRAME_END_INT); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void mtk_rdma_disable_vblank(struct mtk_ddp_comp *comp) | ||
| 89 | { | ||
| 90 | struct mtk_disp_rdma *priv = container_of(comp, struct mtk_disp_rdma, | ||
| 91 | ddp_comp); | ||
| 92 | |||
| 93 | priv->crtc = NULL; | ||
| 94 | rdma_update_bits(comp, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0); | ||
| 95 | } | ||
| 96 | |||
| 97 | static void mtk_rdma_start(struct mtk_ddp_comp *comp) | ||
| 98 | { | ||
| 99 | rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, | ||
| 100 | RDMA_ENGINE_EN); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void mtk_rdma_stop(struct mtk_ddp_comp *comp) | ||
| 104 | { | ||
| 105 | rdma_update_bits(comp, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0); | ||
| 106 | } | ||
| 107 | |||
| 108 | static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width, | ||
| 109 | unsigned int height, unsigned int vrefresh) | ||
| 110 | { | ||
| 111 | unsigned int threshold; | ||
| 112 | unsigned int reg; | ||
| 113 | |||
| 114 | rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_0, 0xfff, width); | ||
| 115 | rdma_update_bits(comp, DISP_REG_RDMA_SIZE_CON_1, 0xfffff, height); | ||
| 116 | |||
| 117 | /* | ||
| 118 | * Enable FIFO underflow since DSI and DPI can't be blocked. | ||
| 119 | * Keep the FIFO pseudo size reset default of 8 KiB. Set the | ||
| 120 | * output threshold to 6 microseconds with 7/6 overhead to | ||
| 121 | * account for blanking, and with a pixel depth of 4 bytes: | ||
| 122 | */ | ||
| 123 | threshold = width * height * vrefresh * 4 * 7 / 1000000; | ||
| 124 | reg = RDMA_FIFO_UNDERFLOW_EN | | ||
| 125 | RDMA_FIFO_PSEUDO_SIZE(SZ_8K) | | ||
| 126 | RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold); | ||
| 127 | writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON); | ||
| 128 | } | ||
| 129 | |||
| 130 | static const struct mtk_ddp_comp_funcs mtk_disp_rdma_funcs = { | ||
| 131 | .config = mtk_rdma_config, | ||
| 132 | .start = mtk_rdma_start, | ||
| 133 | .stop = mtk_rdma_stop, | ||
| 134 | .enable_vblank = mtk_rdma_enable_vblank, | ||
| 135 | .disable_vblank = mtk_rdma_disable_vblank, | ||
| 136 | }; | ||
| 137 | |||
| 138 | static int mtk_disp_rdma_bind(struct device *dev, struct device *master, | ||
| 139 | void *data) | ||
| 140 | { | ||
| 141 | struct mtk_disp_rdma *priv = dev_get_drvdata(dev); | ||
| 142 | struct drm_device *drm_dev = data; | ||
| 143 | int ret; | ||
| 144 | |||
| 145 | ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp); | ||
| 146 | if (ret < 0) { | ||
| 147 | dev_err(dev, "Failed to register component %s: %d\n", | ||
| 148 | dev->of_node->full_name, ret); | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | |||
| 154 | } | ||
| 155 | |||
| 156 | static void mtk_disp_rdma_unbind(struct device *dev, struct device *master, | ||
| 157 | void *data) | ||
| 158 | { | ||
| 159 | struct mtk_disp_rdma *priv = dev_get_drvdata(dev); | ||
| 160 | struct drm_device *drm_dev = data; | ||
| 161 | |||
| 162 | mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp); | ||
| 163 | } | ||
| 164 | |||
| 165 | static const struct component_ops mtk_disp_rdma_component_ops = { | ||
| 166 | .bind = mtk_disp_rdma_bind, | ||
| 167 | .unbind = mtk_disp_rdma_unbind, | ||
| 168 | }; | ||
| 169 | |||
| 170 | static int mtk_disp_rdma_probe(struct platform_device *pdev) | ||
| 171 | { | ||
| 172 | struct device *dev = &pdev->dev; | ||
| 173 | struct mtk_disp_rdma *priv; | ||
| 174 | int comp_id; | ||
| 175 | int irq; | ||
| 176 | int ret; | ||
| 177 | |||
| 178 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 179 | if (!priv) | ||
| 180 | return -ENOMEM; | ||
| 181 | |||
| 182 | irq = platform_get_irq(pdev, 0); | ||
| 183 | if (irq < 0) | ||
| 184 | return irq; | ||
| 185 | |||
| 186 | comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_RDMA); | ||
| 187 | if (comp_id < 0) { | ||
| 188 | dev_err(dev, "Failed to identify by alias: %d\n", comp_id); | ||
| 189 | return comp_id; | ||
| 190 | } | ||
| 191 | |||
| 192 | ret = mtk_ddp_comp_init(dev, dev->of_node, &priv->ddp_comp, comp_id, | ||
| 193 | &mtk_disp_rdma_funcs); | ||
| 194 | if (ret) { | ||
| 195 | dev_err(dev, "Failed to initialize component: %d\n", ret); | ||
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Disable and clear pending interrupts */ | ||
| 200 | writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_ENABLE); | ||
| 201 | writel(0x0, priv->ddp_comp.regs + DISP_REG_RDMA_INT_STATUS); | ||
| 202 | |||
| 203 | ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler, | ||
| 204 | IRQF_TRIGGER_NONE, dev_name(dev), priv); | ||
| 205 | if (ret < 0) { | ||
| 206 | dev_err(dev, "Failed to request irq %d: %d\n", irq, ret); | ||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | |||
| 210 | platform_set_drvdata(pdev, priv); | ||
| 211 | |||
| 212 | ret = component_add(dev, &mtk_disp_rdma_component_ops); | ||
| 213 | if (ret) | ||
| 214 | dev_err(dev, "Failed to add component: %d\n", ret); | ||
| 215 | |||
| 216 | return ret; | ||
| 217 | } | ||
| 218 | |||
| 219 | static int mtk_disp_rdma_remove(struct platform_device *pdev) | ||
| 220 | { | ||
| 221 | component_del(&pdev->dev, &mtk_disp_rdma_component_ops); | ||
| 222 | |||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { | ||
| 227 | { .compatible = "mediatek,mt8173-disp-rdma", }, | ||
| 228 | {}, | ||
| 229 | }; | ||
| 230 | MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match); | ||
| 231 | |||
| 232 | struct platform_driver mtk_disp_rdma_driver = { | ||
| 233 | .probe = mtk_disp_rdma_probe, | ||
| 234 | .remove = mtk_disp_rdma_remove, | ||
| 235 | .driver = { | ||
| 236 | .name = "mediatek-disp-rdma", | ||
| 237 | .owner = THIS_MODULE, | ||
| 238 | .of_match_table = mtk_disp_rdma_driver_dt_match, | ||
| 239 | }, | ||
| 240 | }; | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c new file mode 100644 index 000000000000..d05ca7901315 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c | |||
| @@ -0,0 +1,769 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2014 MediaTek Inc. | ||
| 3 | * Author: Jie Qiu <jie.qiu@mediatek.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | ||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <drm/drm_crtc.h> | ||
| 16 | #include <drm/drm_crtc_helper.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/component.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/of.h> | ||
| 21 | #include <linux/of_graph.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | #include <linux/types.h> | ||
| 24 | #include <linux/clk.h> | ||
| 25 | |||
| 26 | #include "mtk_dpi_regs.h" | ||
| 27 | #include "mtk_drm_ddp_comp.h" | ||
| 28 | |||
| 29 | enum mtk_dpi_out_bit_num { | ||
| 30 | MTK_DPI_OUT_BIT_NUM_8BITS, | ||
| 31 | MTK_DPI_OUT_BIT_NUM_10BITS, | ||
| 32 | MTK_DPI_OUT_BIT_NUM_12BITS, | ||
| 33 | MTK_DPI_OUT_BIT_NUM_16BITS | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum mtk_dpi_out_yc_map { | ||
| 37 | MTK_DPI_OUT_YC_MAP_RGB, | ||
| 38 | MTK_DPI_OUT_YC_MAP_CYCY, | ||
| 39 | MTK_DPI_OUT_YC_MAP_YCYC, | ||
| 40 | MTK_DPI_OUT_YC_MAP_CY, | ||
| 41 | MTK_DPI_OUT_YC_MAP_YC | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum mtk_dpi_out_channel_swap { | ||
| 45 | MTK_DPI_OUT_CHANNEL_SWAP_RGB, | ||
| 46 | MTK_DPI_OUT_CHANNEL_SWAP_GBR, | ||
| 47 | MTK_DPI_OUT_CHANNEL_SWAP_BRG, | ||
| 48 | MTK_DPI_OUT_CHANNEL_SWAP_RBG, | ||
| 49 | MTK_DPI_OUT_CHANNEL_SWAP_GRB, | ||
| 50 | MTK_DPI_OUT_CHANNEL_SWAP_BGR | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum mtk_dpi_out_color_format { | ||
| 54 | MTK_DPI_COLOR_FORMAT_RGB, | ||
| 55 | MTK_DPI_COLOR_FORMAT_RGB_FULL, | ||
| 56 | MTK_DPI_COLOR_FORMAT_YCBCR_444, | ||
| 57 | MTK_DPI_COLOR_FORMAT_YCBCR_422, | ||
| 58 | MTK_DPI_COLOR_FORMAT_XV_YCC, | ||
| 59 | MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL, | ||
| 60 | MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct mtk_dpi { | ||
| 64 | struct mtk_ddp_comp ddp_comp; | ||
| 65 | struct drm_encoder encoder; | ||
| 66 | void __iomem *regs; | ||
| 67 | struct device *dev; | ||
| 68 | struct clk *engine_clk; | ||
| 69 | struct clk *pixel_clk; | ||
| 70 | struct clk *tvd_clk; | ||
| 71 | int irq; | ||
| 72 | struct drm_display_mode mode; | ||
| 73 | enum mtk_dpi_out_color_format color_format; | ||
| 74 | enum mtk_dpi_out_yc_map yc_map; | ||
| 75 | enum mtk_dpi_out_bit_num bit_num; | ||
| 76 | enum mtk_dpi_out_channel_swap channel_swap; | ||
| 77 | bool power_sta; | ||
| 78 | u8 power_ctl; | ||
| 79 | }; | ||
| 80 | |||
| 81 | static inline struct mtk_dpi *mtk_dpi_from_encoder(struct drm_encoder *e) | ||
| 82 | { | ||
| 83 | return container_of(e, struct mtk_dpi, encoder); | ||
| 84 | } | ||
| 85 | |||
| 86 | enum mtk_dpi_polarity { | ||
| 87 | MTK_DPI_POLARITY_RISING, | ||
| 88 | MTK_DPI_POLARITY_FALLING, | ||
| 89 | }; | ||
| 90 | |||
| 91 | enum mtk_dpi_power_ctl { | ||
| 92 | DPI_POWER_START = BIT(0), | ||
| 93 | DPI_POWER_ENABLE = BIT(1), | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct mtk_dpi_polarities { | ||
| 97 | enum mtk_dpi_polarity de_pol; | ||
| 98 | enum mtk_dpi_polarity ck_pol; | ||
| 99 | enum mtk_dpi_polarity hsync_pol; | ||
| 100 | enum mtk_dpi_polarity vsync_pol; | ||
| 101 | }; | ||
| 102 | |||
| 103 | struct mtk_dpi_sync_param { | ||
| 104 | u32 sync_width; | ||
| 105 | u32 front_porch; | ||
| 106 | u32 back_porch; | ||
| 107 | bool shift_half_line; | ||
| 108 | }; | ||
| 109 | |||
| 110 | struct mtk_dpi_yc_limit { | ||
| 111 | u16 y_top; | ||
| 112 | u16 y_bottom; | ||
| 113 | u16 c_top; | ||
| 114 | u16 c_bottom; | ||
| 115 | }; | ||
| 116 | |||
| 117 | static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask) | ||
| 118 | { | ||
| 119 | u32 tmp = readl(dpi->regs + offset) & ~mask; | ||
| 120 | |||
| 121 | tmp |= (val & mask); | ||
| 122 | writel(tmp, dpi->regs + offset); | ||
| 123 | } | ||
| 124 | |||
| 125 | static void mtk_dpi_sw_reset(struct mtk_dpi *dpi, bool reset) | ||
| 126 | { | ||
| 127 | mtk_dpi_mask(dpi, DPI_RET, reset ? RST : 0, RST); | ||
| 128 | } | ||
| 129 | |||
| 130 | static void mtk_dpi_enable(struct mtk_dpi *dpi) | ||
| 131 | { | ||
| 132 | mtk_dpi_mask(dpi, DPI_EN, EN, EN); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void mtk_dpi_disable(struct mtk_dpi *dpi) | ||
| 136 | { | ||
| 137 | mtk_dpi_mask(dpi, DPI_EN, 0, EN); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void mtk_dpi_config_hsync(struct mtk_dpi *dpi, | ||
| 141 | struct mtk_dpi_sync_param *sync) | ||
| 142 | { | ||
| 143 | mtk_dpi_mask(dpi, DPI_TGEN_HWIDTH, | ||
| 144 | sync->sync_width << HPW, HPW_MASK); | ||
| 145 | mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, | ||
| 146 | sync->back_porch << HBP, HBP_MASK); | ||
| 147 | mtk_dpi_mask(dpi, DPI_TGEN_HPORCH, sync->front_porch << HFP, | ||
| 148 | HFP_MASK); | ||
| 149 | } | ||
| 150 | |||
| 151 | static void mtk_dpi_config_vsync(struct mtk_dpi *dpi, | ||
| 152 | struct mtk_dpi_sync_param *sync, | ||
| 153 | u32 width_addr, u32 porch_addr) | ||
| 154 | { | ||
| 155 | mtk_dpi_mask(dpi, width_addr, | ||
| 156 | sync->sync_width << VSYNC_WIDTH_SHIFT, | ||
| 157 | VSYNC_WIDTH_MASK); | ||
| 158 | mtk_dpi_mask(dpi, width_addr, | ||
| 159 | sync->shift_half_line << VSYNC_HALF_LINE_SHIFT, | ||
| 160 | VSYNC_HALF_LINE_MASK); | ||
| 161 | mtk_dpi_mask(dpi, porch_addr, | ||
| 162 | sync->back_porch << VSYNC_BACK_PORCH_SHIFT, | ||
| 163 | VSYNC_BACK_PORCH_MASK); | ||
| 164 | mtk_dpi_mask(dpi, porch_addr, | ||
| 165 | sync->front_porch << VSYNC_FRONT_PORCH_SHIFT, | ||
| 166 | VSYNC_FRONT_PORCH_MASK); | ||
| 167 | } | ||
| 168 | |||
| 169 | static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi, | ||
| 170 | struct mtk_dpi_sync_param *sync) | ||
| 171 | { | ||
| 172 | mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH, DPI_TGEN_VPORCH); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void mtk_dpi_config_vsync_leven(struct mtk_dpi *dpi, | ||
| 176 | struct mtk_dpi_sync_param *sync) | ||
| 177 | { | ||
| 178 | mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_LEVEN, | ||
| 179 | DPI_TGEN_VPORCH_LEVEN); | ||
| 180 | } | ||
| 181 | |||
| 182 | static void mtk_dpi_config_vsync_rodd(struct mtk_dpi *dpi, | ||
| 183 | struct mtk_dpi_sync_param *sync) | ||
| 184 | { | ||
| 185 | mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_RODD, | ||
| 186 | DPI_TGEN_VPORCH_RODD); | ||
| 187 | } | ||
| 188 | |||
| 189 | static void mtk_dpi_config_vsync_reven(struct mtk_dpi *dpi, | ||
| 190 | struct mtk_dpi_sync_param *sync) | ||
| 191 | { | ||
| 192 | mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_REVEN, | ||
| 193 | DPI_TGEN_VPORCH_REVEN); | ||
| 194 | } | ||
| 195 | |||
| 196 | static void mtk_dpi_config_pol(struct mtk_dpi *dpi, | ||
| 197 | struct mtk_dpi_polarities *dpi_pol) | ||
| 198 | { | ||
| 199 | unsigned int pol; | ||
| 200 | |||
| 201 | pol = (dpi_pol->ck_pol == MTK_DPI_POLARITY_RISING ? 0 : CK_POL) | | ||
| 202 | (dpi_pol->de_pol == MTK_DPI_POLARITY_RISING ? 0 : DE_POL) | | ||
| 203 | (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HSYNC_POL) | | ||
| 204 | (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VSYNC_POL); | ||
| 205 | mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, pol, | ||
| 206 | CK_POL | DE_POL | HSYNC_POL | VSYNC_POL); | ||
| 207 | } | ||
| 208 | |||
| 209 | static void mtk_dpi_config_3d(struct mtk_dpi *dpi, bool en_3d) | ||
| 210 | { | ||
| 211 | mtk_dpi_mask(dpi, DPI_CON, en_3d ? TDFP_EN : 0, TDFP_EN); | ||
| 212 | } | ||
| 213 | |||
| 214 | static void mtk_dpi_config_interface(struct mtk_dpi *dpi, bool inter) | ||
| 215 | { | ||
| 216 | mtk_dpi_mask(dpi, DPI_CON, inter ? INTL_EN : 0, INTL_EN); | ||
| 217 | } | ||
| 218 | |||
| 219 | static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height) | ||
| 220 | { | ||
| 221 | mtk_dpi_mask(dpi, DPI_SIZE, width << HSIZE, HSIZE_MASK); | ||
| 222 | mtk_dpi_mask(dpi, DPI_SIZE, height << VSIZE, VSIZE_MASK); | ||
| 223 | } | ||
| 224 | |||
| 225 | static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi, | ||
| 226 | struct mtk_dpi_yc_limit *limit) | ||
| 227 | { | ||
| 228 | mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT, | ||
| 229 | Y_LIMINT_BOT_MASK); | ||
| 230 | mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP, | ||
| 231 | Y_LIMINT_TOP_MASK); | ||
| 232 | mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_bottom << C_LIMIT_BOT, | ||
| 233 | C_LIMIT_BOT_MASK); | ||
| 234 | mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_top << C_LIMIT_TOP, | ||
| 235 | C_LIMIT_TOP_MASK); | ||
| 236 | } | ||
| 237 | |||
| 238 | static void mtk_dpi_config_bit_num(struct mtk_dpi *dpi, | ||
| 239 | enum mtk_dpi_out_bit_num num) | ||
| 240 | { | ||
| 241 | u32 val; | ||
| 242 | |||
| 243 | switch (num) { | ||
| 244 | case MTK_DPI_OUT_BIT_NUM_8BITS: | ||
| 245 | val = OUT_BIT_8; | ||
| 246 | break; | ||
| 247 | case MTK_DPI_OUT_BIT_NUM_10BITS: | ||
| 248 | val = OUT_BIT_10; | ||
| 249 | break; | ||
| 250 | case MTK_DPI_OUT_BIT_NUM_12BITS: | ||
| 251 | val = OUT_BIT_12; | ||
| 252 | break; | ||
| 253 | case MTK_DPI_OUT_BIT_NUM_16BITS: | ||
| 254 | val = OUT_BIT_16; | ||
| 255 | break; | ||
| 256 | default: | ||
| 257 | val = OUT_BIT_8; | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << OUT_BIT, | ||
| 261 | OUT_BIT_MASK); | ||
| 262 | } | ||
| 263 | |||
| 264 | static void mtk_dpi_config_yc_map(struct mtk_dpi *dpi, | ||
| 265 | enum mtk_dpi_out_yc_map map) | ||
| 266 | { | ||
| 267 | u32 val; | ||
| 268 | |||
| 269 | switch (map) { | ||
| 270 | case MTK_DPI_OUT_YC_MAP_RGB: | ||
| 271 | val = YC_MAP_RGB; | ||
| 272 | break; | ||
| 273 | case MTK_DPI_OUT_YC_MAP_CYCY: | ||
| 274 | val = YC_MAP_CYCY; | ||
| 275 | break; | ||
| 276 | case MTK_DPI_OUT_YC_MAP_YCYC: | ||
| 277 | val = YC_MAP_YCYC; | ||
| 278 | break; | ||
| 279 | case MTK_DPI_OUT_YC_MAP_CY: | ||
| 280 | val = YC_MAP_CY; | ||
| 281 | break; | ||
| 282 | case MTK_DPI_OUT_YC_MAP_YC: | ||
| 283 | val = YC_MAP_YC; | ||
| 284 | break; | ||
| 285 | default: | ||
| 286 | val = YC_MAP_RGB; | ||
| 287 | break; | ||
| 288 | } | ||
| 289 | |||
| 290 | mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << YC_MAP, YC_MAP_MASK); | ||
| 291 | } | ||
| 292 | |||
| 293 | static void mtk_dpi_config_channel_swap(struct mtk_dpi *dpi, | ||
| 294 | enum mtk_dpi_out_channel_swap swap) | ||
| 295 | { | ||
| 296 | u32 val; | ||
| 297 | |||
| 298 | switch (swap) { | ||
| 299 | case MTK_DPI_OUT_CHANNEL_SWAP_RGB: | ||
| 300 | val = SWAP_RGB; | ||
| 301 | break; | ||
| 302 | case MTK_DPI_OUT_CHANNEL_SWAP_GBR: | ||
| 303 | val = SWAP_GBR; | ||
| 304 | break; | ||
| 305 | case MTK_DPI_OUT_CHANNEL_SWAP_BRG: | ||
| 306 | val = SWAP_BRG; | ||
| 307 | break; | ||
| 308 | case MTK_DPI_OUT_CHANNEL_SWAP_RBG: | ||
| 309 | val = SWAP_RBG; | ||
| 310 | break; | ||
| 311 | case MTK_DPI_OUT_CHANNEL_SWAP_GRB: | ||
| 312 | val = SWAP_GRB; | ||
| 313 | break; | ||
| 314 | case MTK_DPI_OUT_CHANNEL_SWAP_BGR: | ||
| 315 | val = SWAP_BGR; | ||
| 316 | break; | ||
| 317 | default: | ||
| 318 | val = SWAP_RGB; | ||
| 319 | break; | ||
| 320 | } | ||
| 321 | |||
| 322 | mtk_dpi_mask(dpi, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK); | ||
| 323 | } | ||
| 324 | |||
| 325 | static void mtk_dpi_config_yuv422_enable(struct mtk_dpi *dpi, bool enable) | ||
| 326 | { | ||
| 327 | mtk_dpi_mask(dpi, DPI_CON, enable ? YUV422_EN : 0, YUV422_EN); | ||
| 328 | } | ||
| 329 | |||
| 330 | static void mtk_dpi_config_csc_enable(struct mtk_dpi *dpi, bool enable) | ||
| 331 | { | ||
| 332 | mtk_dpi_mask(dpi, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE); | ||
| 333 | } | ||
| 334 | |||
| 335 | static void mtk_dpi_config_swap_input(struct mtk_dpi *dpi, bool enable) | ||
| 336 | { | ||
| 337 | mtk_dpi_mask(dpi, DPI_CON, enable ? IN_RB_SWAP : 0, IN_RB_SWAP); | ||
| 338 | } | ||
| 339 | |||
| 340 | static void mtk_dpi_config_2n_h_fre(struct mtk_dpi *dpi) | ||
| 341 | { | ||
| 342 | mtk_dpi_mask(dpi, DPI_H_FRE_CON, H_FRE_2N, H_FRE_2N); | ||
| 343 | } | ||
| 344 | |||
| 345 | static void mtk_dpi_config_color_format(struct mtk_dpi *dpi, | ||
| 346 | enum mtk_dpi_out_color_format format) | ||
| 347 | { | ||
| 348 | if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_444) || | ||
| 349 | (format == MTK_DPI_COLOR_FORMAT_YCBCR_444_FULL)) { | ||
| 350 | mtk_dpi_config_yuv422_enable(dpi, false); | ||
| 351 | mtk_dpi_config_csc_enable(dpi, true); | ||
| 352 | mtk_dpi_config_swap_input(dpi, false); | ||
| 353 | mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_BGR); | ||
| 354 | } else if ((format == MTK_DPI_COLOR_FORMAT_YCBCR_422) || | ||
| 355 | (format == MTK_DPI_COLOR_FORMAT_YCBCR_422_FULL)) { | ||
| 356 | mtk_dpi_config_yuv422_enable(dpi, true); | ||
| 357 | mtk_dpi_config_csc_enable(dpi, true); | ||
| 358 | mtk_dpi_config_swap_input(dpi, true); | ||
| 359 | mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB); | ||
| 360 | } else { | ||
| 361 | mtk_dpi_config_yuv422_enable(dpi, false); | ||
| 362 | mtk_dpi_config_csc_enable(dpi, false); | ||
| 363 | mtk_dpi_config_swap_input(dpi, false); | ||
| 364 | mtk_dpi_config_channel_swap(dpi, MTK_DPI_OUT_CHANNEL_SWAP_RGB); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | static void mtk_dpi_power_off(struct mtk_dpi *dpi, enum mtk_dpi_power_ctl pctl) | ||
| 369 | { | ||
| 370 | dpi->power_ctl &= ~pctl; | ||
| 371 | |||
| 372 | if ((dpi->power_ctl & DPI_POWER_START) || | ||
| 373 | (dpi->power_ctl & DPI_POWER_ENABLE)) | ||
| 374 | return; | ||
| 375 | |||
| 376 | if (!dpi->power_sta) | ||
| 377 | return; | ||
| 378 | |||
| 379 | mtk_dpi_disable(dpi); | ||
| 380 | clk_disable_unprepare(dpi->pixel_clk); | ||
| 381 | clk_disable_unprepare(dpi->engine_clk); | ||
| 382 | dpi->power_sta = false; | ||
| 383 | } | ||
| 384 | |||
| 385 | static int mtk_dpi_power_on(struct mtk_dpi *dpi, enum mtk_dpi_power_ctl pctl) | ||
| 386 | { | ||
| 387 | int ret; | ||
| 388 | |||
| 389 | dpi->power_ctl |= pctl; | ||
| 390 | |||
| 391 | if (!(dpi->power_ctl & DPI_POWER_START) && | ||
| 392 | !(dpi->power_ctl & DPI_POWER_ENABLE)) | ||
| 393 | return 0; | ||
| 394 | |||
| 395 | if (dpi->power_sta) | ||
| 396 | return 0; | ||
| 397 | |||
| 398 | ret = clk_prepare_enable(dpi->engine_clk); | ||
| 399 | if (ret) { | ||
| 400 | dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret); | ||
| 401 | goto err_eng; | ||
| 402 | } | ||
| 403 | |||
| 404 | ret = clk_prepare_enable(dpi->pixel_clk); | ||
| 405 | if (ret) { | ||
| 406 | dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret); | ||
| 407 | goto err_pixel; | ||
| 408 | } | ||
| 409 | |||
| 410 | mtk_dpi_enable(dpi); | ||
| 411 | dpi->power_sta = true; | ||
| 412 | return 0; | ||
| 413 | |||
| 414 | err_pixel: | ||
| 415 | clk_disable_unprepare(dpi->engine_clk); | ||
| 416 | err_eng: | ||
| 417 | dpi->power_ctl &= ~pctl; | ||
| 418 | return ret; | ||
| 419 | } | ||
| 420 | |||
| 421 | static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, | ||
| 422 | struct drm_display_mode *mode) | ||
| 423 | { | ||
| 424 | struct mtk_dpi_yc_limit limit; | ||
| 425 | struct mtk_dpi_polarities dpi_pol; | ||
| 426 | struct mtk_dpi_sync_param hsync; | ||
| 427 | struct mtk_dpi_sync_param vsync_lodd = { 0 }; | ||
| 428 | struct mtk_dpi_sync_param vsync_leven = { 0 }; | ||
| 429 | struct mtk_dpi_sync_param vsync_rodd = { 0 }; | ||
| 430 | struct mtk_dpi_sync_param vsync_reven = { 0 }; | ||
| 431 | unsigned long pix_rate; | ||
| 432 | unsigned long pll_rate; | ||
| 433 | unsigned int factor; | ||
| 434 | |||
| 435 | if (!dpi) { | ||
| 436 | dev_err(dpi->dev, "invalid argument\n"); | ||
| 437 | return -EINVAL; | ||
| 438 | } | ||
| 439 | |||
| 440 | pix_rate = 1000UL * mode->clock; | ||
| 441 | if (mode->clock <= 74000) | ||
| 442 | factor = 8 * 3; | ||
| 443 | else | ||
| 444 | factor = 4 * 3; | ||
| 445 | pll_rate = pix_rate * factor; | ||
| 446 | |||
| 447 | dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n", | ||
| 448 | pll_rate, pix_rate); | ||
| 449 | |||
| 450 | clk_set_rate(dpi->tvd_clk, pll_rate); | ||
| 451 | pll_rate = clk_get_rate(dpi->tvd_clk); | ||
| 452 | |||
| 453 | pix_rate = pll_rate / factor; | ||
| 454 | clk_set_rate(dpi->pixel_clk, pix_rate); | ||
| 455 | pix_rate = clk_get_rate(dpi->pixel_clk); | ||
| 456 | |||
| 457 | dev_dbg(dpi->dev, "Got PLL %lu Hz, pixel clock %lu Hz\n", | ||
| 458 | pll_rate, pix_rate); | ||
| 459 | |||
| 460 | limit.c_bottom = 0x0010; | ||
| 461 | limit.c_top = 0x0FE0; | ||
| 462 | limit.y_bottom = 0x0010; | ||
| 463 | limit.y_top = 0x0FE0; | ||
| 464 | |||
| 465 | dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING; | ||
| 466 | dpi_pol.de_pol = MTK_DPI_POLARITY_RISING; | ||
| 467 | dpi_pol.hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ? | ||
| 468 | MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; | ||
| 469 | dpi_pol.vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ? | ||
| 470 | MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING; | ||
| 471 | |||
| 472 | hsync.sync_width = mode->hsync_end - mode->hsync_start; | ||
| 473 | hsync.back_porch = mode->htotal - mode->hsync_end; | ||
| 474 | hsync.front_porch = mode->hsync_start - mode->hdisplay; | ||
| 475 | hsync.shift_half_line = false; | ||
| 476 | |||
| 477 | vsync_lodd.sync_width = mode->vsync_end - mode->vsync_start; | ||
| 478 | vsync_lodd.back_porch = mode->vtotal - mode->vsync_end; | ||
| 479 | vsync_lodd.front_porch = mode->vsync_start - mode->vdisplay; | ||
| 480 | vsync_lodd.shift_half_line = false; | ||
| 481 | |||
| 482 | if (mode->flags & DRM_MODE_FLAG_INTERLACE && | ||
| 483 | mode->flags & DRM_MODE_FLAG_3D_MASK) { | ||
| 484 | vsync_leven = vsync_lodd; | ||
| 485 | vsync_rodd = vsync_lodd; | ||
| 486 | vsync_reven = vsync_lodd; | ||
| 487 | vsync_leven.shift_half_line = true; | ||
| 488 | vsync_reven.shift_half_line = true; | ||
| 489 | } else if (mode->flags & DRM_MODE_FLAG_INTERLACE && | ||
| 490 | !(mode->flags & DRM_MODE_FLAG_3D_MASK)) { | ||
| 491 | vsync_leven = vsync_lodd; | ||
| 492 | vsync_leven.shift_half_line = true; | ||
| 493 | } else if (!(mode->flags & DRM_MODE_FLAG_INTERLACE) && | ||
| 494 | mode->flags & DRM_MODE_FLAG_3D_MASK) { | ||
| 495 | vsync_rodd = vsync_lodd; | ||
| 496 | } | ||
| 497 | mtk_dpi_sw_reset(dpi, true); | ||
| 498 | mtk_dpi_config_pol(dpi, &dpi_pol); | ||
| 499 | |||
| 500 | mtk_dpi_config_hsync(dpi, &hsync); | ||
| 501 | mtk_dpi_config_vsync_lodd(dpi, &vsync_lodd); | ||
| 502 | mtk_dpi_config_vsync_rodd(dpi, &vsync_rodd); | ||
| 503 | mtk_dpi_config_vsync_leven(dpi, &vsync_leven); | ||
| 504 | mtk_dpi_config_vsync_reven(dpi, &vsync_reven); | ||
| 505 | |||
| 506 | mtk_dpi_config_3d(dpi, !!(mode->flags & DRM_MODE_FLAG_3D_MASK)); | ||
| 507 | mtk_dpi_config_interface(dpi, !!(mode->flags & | ||
| 508 | DRM_MODE_FLAG_INTERLACE)); | ||
| 509 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
| 510 | mtk_dpi_config_fb_size(dpi, mode->hdisplay, mode->vdisplay / 2); | ||
| 511 | else | ||
| 512 | mtk_dpi_config_fb_size(dpi, mode->hdisplay, mode->vdisplay); | ||
| 513 | |||
| 514 | mtk_dpi_config_channel_limit(dpi, &limit); | ||
| 515 | mtk_dpi_config_bit_num(dpi, dpi->bit_num); | ||
| 516 | mtk_dpi_config_channel_swap(dpi, dpi->channel_swap); | ||
| 517 | mtk_dpi_config_yc_map(dpi, dpi->yc_map); | ||
| 518 | mtk_dpi_config_color_format(dpi, dpi->color_format); | ||
| 519 | mtk_dpi_config_2n_h_fre(dpi); | ||
| 520 | mtk_dpi_sw_reset(dpi, false); | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | static void mtk_dpi_encoder_destroy(struct drm_encoder *encoder) | ||
| 526 | { | ||
| 527 | drm_encoder_cleanup(encoder); | ||
| 528 | } | ||
| 529 | |||
| 530 | static const struct drm_encoder_funcs mtk_dpi_encoder_funcs = { | ||
| 531 | .destroy = mtk_dpi_encoder_destroy, | ||
| 532 | }; | ||
| 533 | |||
| 534 | static bool mtk_dpi_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 535 | const struct drm_display_mode *mode, | ||
| 536 | struct drm_display_mode *adjusted_mode) | ||
| 537 | { | ||
| 538 | return true; | ||
| 539 | } | ||
| 540 | |||
| 541 | static void mtk_dpi_encoder_mode_set(struct drm_encoder *encoder, | ||
| 542 | struct drm_display_mode *mode, | ||
| 543 | struct drm_display_mode *adjusted_mode) | ||
| 544 | { | ||
| 545 | struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder); | ||
| 546 | |||
| 547 | drm_mode_copy(&dpi->mode, adjusted_mode); | ||
| 548 | } | ||
| 549 | |||
| 550 | static void mtk_dpi_encoder_disable(struct drm_encoder *encoder) | ||
| 551 | { | ||
| 552 | struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder); | ||
| 553 | |||
| 554 | mtk_dpi_power_off(dpi, DPI_POWER_ENABLE); | ||
| 555 | } | ||
| 556 | |||
| 557 | static void mtk_dpi_encoder_enable(struct drm_encoder *encoder) | ||
| 558 | { | ||
| 559 | struct mtk_dpi *dpi = mtk_dpi_from_encoder(encoder); | ||
| 560 | |||
| 561 | mtk_dpi_power_on(dpi, DPI_POWER_ENABLE); | ||
| 562 | mtk_dpi_set_display_mode(dpi, &dpi->mode); | ||
| 563 | } | ||
| 564 | |||
| 565 | static int mtk_dpi_atomic_check(struct drm_encoder *encoder, | ||
| 566 | struct drm_crtc_state *crtc_state, | ||
| 567 | struct drm_connector_state *conn_state) | ||
| 568 | { | ||
| 569 | return 0; | ||
| 570 | } | ||
| 571 | |||
| 572 | static const struct drm_encoder_helper_funcs mtk_dpi_encoder_helper_funcs = { | ||
| 573 | .mode_fixup = mtk_dpi_encoder_mode_fixup, | ||
| 574 | .mode_set = mtk_dpi_encoder_mode_set, | ||
| 575 | .disable = mtk_dpi_encoder_disable, | ||
| 576 | .enable = mtk_dpi_encoder_enable, | ||
| 577 | .atomic_check = mtk_dpi_atomic_check, | ||
| 578 | }; | ||
| 579 | |||
| 580 | static void mtk_dpi_start(struct mtk_ddp_comp *comp) | ||
| 581 | { | ||
| 582 | struct mtk_dpi *dpi = container_of(comp, struct mtk_dpi, ddp_comp); | ||
| 583 | |||
| 584 | mtk_dpi_power_on(dpi, DPI_POWER_START); | ||
| 585 | } | ||
| 586 | |||
| 587 | static void mtk_dpi_stop(struct mtk_ddp_comp *comp) | ||
| 588 | { | ||
| 589 | struct mtk_dpi *dpi = container_of(comp, struct mtk_dpi, ddp_comp); | ||
| 590 | |||
| 591 | mtk_dpi_power_off(dpi, DPI_POWER_START); | ||
| 592 | } | ||
| 593 | |||
| 594 | static const struct mtk_ddp_comp_funcs mtk_dpi_funcs = { | ||
| 595 | .start = mtk_dpi_start, | ||
| 596 | .stop = mtk_dpi_stop, | ||
| 597 | }; | ||
| 598 | |||
| 599 | static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) | ||
| 600 | { | ||
| 601 | struct mtk_dpi *dpi = dev_get_drvdata(dev); | ||
| 602 | struct drm_device *drm_dev = data; | ||
| 603 | int ret; | ||
| 604 | |||
| 605 | ret = mtk_ddp_comp_register(drm_dev, &dpi->ddp_comp); | ||
| 606 | if (ret < 0) { | ||
| 607 | dev_err(dev, "Failed to register component %s: %d\n", | ||
| 608 | dev->of_node->full_name, ret); | ||
| 609 | return ret; | ||
| 610 | } | ||
| 611 | |||
| 612 | ret = drm_encoder_init(drm_dev, &dpi->encoder, &mtk_dpi_encoder_funcs, | ||
| 613 | DRM_MODE_ENCODER_TMDS, NULL); | ||
| 614 | if (ret) { | ||
| 615 | dev_err(dev, "Failed to initialize decoder: %d\n", ret); | ||
| 616 | goto err_unregister; | ||
| 617 | } | ||
| 618 | drm_encoder_helper_add(&dpi->encoder, &mtk_dpi_encoder_helper_funcs); | ||
| 619 | |||
| 620 | /* Currently DPI0 is fixed to be driven by OVL1 */ | ||
| 621 | dpi->encoder.possible_crtcs = BIT(1); | ||
| 622 | |||
| 623 | dpi->encoder.bridge->encoder = &dpi->encoder; | ||
| 624 | ret = drm_bridge_attach(dpi->encoder.dev, dpi->encoder.bridge); | ||
| 625 | if (ret) { | ||
| 626 | dev_err(dev, "Failed to attach bridge: %d\n", ret); | ||
| 627 | goto err_cleanup; | ||
| 628 | } | ||
| 629 | |||
| 630 | dpi->bit_num = MTK_DPI_OUT_BIT_NUM_8BITS; | ||
| 631 | dpi->channel_swap = MTK_DPI_OUT_CHANNEL_SWAP_RGB; | ||
| 632 | dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB; | ||
| 633 | dpi->color_format = MTK_DPI_COLOR_FORMAT_RGB; | ||
| 634 | |||
| 635 | return 0; | ||
| 636 | |||
| 637 | err_cleanup: | ||
| 638 | drm_encoder_cleanup(&dpi->encoder); | ||
| 639 | err_unregister: | ||
| 640 | mtk_ddp_comp_unregister(drm_dev, &dpi->ddp_comp); | ||
| 641 | return ret; | ||
| 642 | } | ||
| 643 | |||
| 644 | static void mtk_dpi_unbind(struct device *dev, struct device *master, | ||
| 645 | void *data) | ||
| 646 | { | ||
| 647 | struct mtk_dpi *dpi = dev_get_drvdata(dev); | ||
| 648 | struct drm_device *drm_dev = data; | ||
| 649 | |||
| 650 | drm_encoder_cleanup(&dpi->encoder); | ||
| 651 | mtk_ddp_comp_unregister(drm_dev, &dpi->ddp_comp); | ||
| 652 | } | ||
| 653 | |||
| 654 | static const struct component_ops mtk_dpi_component_ops = { | ||
| 655 | .bind = mtk_dpi_bind, | ||
| 656 | .unbind = mtk_dpi_unbind, | ||
| 657 | }; | ||
| 658 | |||
| 659 | static int mtk_dpi_probe(struct platform_device *pdev) | ||
| 660 | { | ||
| 661 | struct device *dev = &pdev->dev; | ||
| 662 | struct mtk_dpi *dpi; | ||
| 663 | struct resource *mem; | ||
| 664 | struct device_node *ep, *bridge_node = NULL; | ||
| 665 | int comp_id; | ||
| 666 | int ret; | ||
| 667 | |||
| 668 | dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); | ||
| 669 | if (!dpi) | ||
| 670 | return -ENOMEM; | ||
| 671 | |||
| 672 | dpi->dev = dev; | ||
| 673 | |||
| 674 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 675 | dpi->regs = devm_ioremap_resource(dev, mem); | ||
| 676 | if (IS_ERR(dpi->regs)) { | ||
| 677 | ret = PTR_ERR(dpi->regs); | ||
| 678 | dev_err(dev, "Failed to ioremap mem resource: %d\n", ret); | ||
| 679 | return ret; | ||
| 680 | } | ||
| 681 | |||
| 682 | dpi->engine_clk = devm_clk_get(dev, "engine"); | ||
| 683 | if (IS_ERR(dpi->engine_clk)) { | ||
| 684 | ret = PTR_ERR(dpi->engine_clk); | ||
| 685 | dev_err(dev, "Failed to get engine clock: %d\n", ret); | ||
| 686 | return ret; | ||
| 687 | } | ||
| 688 | |||
| 689 | dpi->pixel_clk = devm_clk_get(dev, "pixel"); | ||
| 690 | if (IS_ERR(dpi->pixel_clk)) { | ||
| 691 | ret = PTR_ERR(dpi->pixel_clk); | ||
| 692 | dev_err(dev, "Failed to get pixel clock: %d\n", ret); | ||
| 693 | return ret; | ||
| 694 | } | ||
| 695 | |||
| 696 | dpi->tvd_clk = devm_clk_get(dev, "pll"); | ||
| 697 | if (IS_ERR(dpi->tvd_clk)) { | ||
| 698 | ret = PTR_ERR(dpi->tvd_clk); | ||
| 699 | dev_err(dev, "Failed to get tvdpll clock: %d\n", ret); | ||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | |||
| 703 | dpi->irq = platform_get_irq(pdev, 0); | ||
| 704 | if (dpi->irq <= 0) { | ||
| 705 | dev_err(dev, "Failed to get irq: %d\n", dpi->irq); | ||
| 706 | return -EINVAL; | ||
| 707 | } | ||
| 708 | |||
| 709 | ep = of_graph_get_next_endpoint(dev->of_node, NULL); | ||
| 710 | if (ep) { | ||
| 711 | bridge_node = of_graph_get_remote_port_parent(ep); | ||
| 712 | of_node_put(ep); | ||
| 713 | } | ||
| 714 | if (!bridge_node) { | ||
| 715 | dev_err(dev, "Failed to find bridge node\n"); | ||
| 716 | return -ENODEV; | ||
| 717 | } | ||
| 718 | |||
| 719 | dev_info(dev, "Found bridge node: %s\n", bridge_node->full_name); | ||
| 720 | |||
| 721 | dpi->encoder.bridge = of_drm_find_bridge(bridge_node); | ||
| 722 | of_node_put(bridge_node); | ||
| 723 | if (!dpi->encoder.bridge) | ||
| 724 | return -EPROBE_DEFER; | ||
| 725 | |||
| 726 | comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DPI); | ||
| 727 | if (comp_id < 0) { | ||
| 728 | dev_err(dev, "Failed to identify by alias: %d\n", comp_id); | ||
| 729 | return comp_id; | ||
| 730 | } | ||
| 731 | |||
| 732 | ret = mtk_ddp_comp_init(dev, dev->of_node, &dpi->ddp_comp, comp_id, | ||
| 733 | &mtk_dpi_funcs); | ||
| 734 | if (ret) { | ||
| 735 | dev_err(dev, "Failed to initialize component: %d\n", ret); | ||
| 736 | return ret; | ||
| 737 | } | ||
| 738 | |||
| 739 | platform_set_drvdata(pdev, dpi); | ||
| 740 | |||
| 741 | ret = component_add(dev, &mtk_dpi_component_ops); | ||
| 742 | if (ret) { | ||
| 743 | dev_err(dev, "Failed to add component: %d\n", ret); | ||
| 744 | return ret; | ||
| 745 | } | ||
| 746 | |||
| 747 | return 0; | ||
| 748 | } | ||
| 749 | |||
| 750 | static int mtk_dpi_remove(struct platform_device *pdev) | ||
| 751 | { | ||
| 752 | component_del(&pdev->dev, &mtk_dpi_component_ops); | ||
| 753 | |||
| 754 | return 0; | ||
| 755 | } | ||
| 756 | |||
| 757 | static const struct of_device_id mtk_dpi_of_ids[] = { | ||
| 758 | { .compatible = "mediatek,mt8173-dpi", }, | ||
| 759 | {} | ||
| 760 | }; | ||
| 761 | |||
| 762 | struct platform_driver mtk_dpi_driver = { | ||
| 763 | .probe = mtk_dpi_probe, | ||
| 764 | .remove = mtk_dpi_remove, | ||
| 765 | .driver = { | ||
| 766 | .name = "mediatek-dpi", | ||
| 767 | .of_match_table = mtk_dpi_of_ids, | ||
| 768 | }, | ||
| 769 | }; | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_regs.h b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h new file mode 100644 index 000000000000..4b6ad4751a31 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dpi_regs.h | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2014 MediaTek Inc. | ||
| 3 | * Author: Jie Qiu <jie.qiu@mediatek.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | ||
| 14 | #ifndef __MTK_DPI_REGS_H | ||
| 15 | #define __MTK_DPI_REGS_H | ||
| 16 | |||
| 17 | #define DPI_EN 0x00 | ||
| 18 | #define EN BIT(0) | ||
| 19 | |||
| 20 | #define DPI_RET 0x04 | ||
| 21 | #define RST BIT(0) | ||
| 22 | |||
| 23 | #define DPI_INTEN 0x08 | ||
| 24 | #define INT_VSYNC_EN BIT(0) | ||
| 25 | #define INT_VDE_EN BIT(1) | ||
| 26 | #define INT_UNDERFLOW_EN BIT(2) | ||
| 27 | |||
| 28 | #define DPI_INTSTA 0x0C | ||
| 29 | #define INT_VSYNC_STA BIT(0) | ||
| 30 | #define INT_VDE_STA BIT(1) | ||
| 31 | #define INT_UNDERFLOW_STA BIT(2) | ||
| 32 | |||
| 33 | #define DPI_CON 0x10 | ||
| 34 | #define BG_ENABLE BIT(0) | ||
| 35 | #define IN_RB_SWAP BIT(1) | ||
| 36 | #define INTL_EN BIT(2) | ||
| 37 | #define TDFP_EN BIT(3) | ||
| 38 | #define CLPF_EN BIT(4) | ||
| 39 | #define YUV422_EN BIT(5) | ||
| 40 | #define CSC_ENABLE BIT(6) | ||
| 41 | #define R601_SEL BIT(7) | ||
| 42 | #define EMBSYNC_EN BIT(8) | ||
| 43 | #define VS_LODD_EN BIT(16) | ||
| 44 | #define VS_LEVEN_EN BIT(17) | ||
| 45 | #define VS_RODD_EN BIT(18) | ||
| 46 | #define VS_REVEN BIT(19) | ||
| 47 | #define FAKE_DE_LODD BIT(20) | ||
| 48 | #define FAKE_DE_LEVEN BIT(21) | ||
| 49 | #define FAKE_DE_RODD BIT(22) | ||
| 50 | #define FAKE_DE_REVEN BIT(23) | ||
| 51 | |||
| 52 | #define DPI_OUTPUT_SETTING 0x14 | ||
| 53 | #define CH_SWAP 0 | ||
| 54 | #define CH_SWAP_MASK (0x7 << 0) | ||
| 55 | #define SWAP_RGB 0x00 | ||
| 56 | #define SWAP_GBR 0x01 | ||
| 57 | #define SWAP_BRG 0x02 | ||
| 58 | #define SWAP_RBG 0x03 | ||
| 59 | #define SWAP_GRB 0x04 | ||
| 60 | #define SWAP_BGR 0x05 | ||
| 61 | #define BIT_SWAP BIT(3) | ||
| 62 | #define B_MASK BIT(4) | ||
| 63 | #define G_MASK BIT(5) | ||
| 64 | #define R_MASK BIT(6) | ||
| 65 | #define DE_MASK BIT(8) | ||
| 66 | #define HS_MASK BIT(9) | ||
| 67 | #define VS_MASK BIT(10) | ||
| 68 | #define DE_POL BIT(12) | ||
| 69 | #define HSYNC_POL BIT(13) | ||
| 70 | #define VSYNC_POL BIT(14) | ||
| 71 | #define CK_POL BIT(15) | ||
| 72 | #define OEN_OFF BIT(16) | ||
| 73 | #define EDGE_SEL BIT(17) | ||
| 74 | #define OUT_BIT 18 | ||
| 75 | #define OUT_BIT_MASK (0x3 << 18) | ||
| 76 | #define OUT_BIT_8 0x00 | ||
| 77 | #define OUT_BIT_10 0x01 | ||
| 78 | #define OUT_BIT_12 0x02 | ||
| 79 | #define OUT_BIT_16 0x03 | ||
| 80 | #define YC_MAP 20 | ||
| 81 | #define YC_MAP_MASK (0x7 << 20) | ||
| 82 | #define YC_MAP_RGB 0x00 | ||
| 83 | #define YC_MAP_CYCY 0x04 | ||
| 84 | #define YC_MAP_YCYC 0x05 | ||
| 85 | #define YC_MAP_CY 0x06 | ||
| 86 | #define YC_MAP_YC 0x07 | ||
| 87 | |||
| 88 | #define DPI_SIZE 0x18 | ||
| 89 | #define HSIZE 0 | ||
| 90 | #define HSIZE_MASK (0x1FFF << 0) | ||
| 91 | #define VSIZE 16 | ||
| 92 | #define VSIZE_MASK (0x1FFF << 16) | ||
| 93 | |||
| 94 | #define DPI_DDR_SETTING 0x1C | ||
| 95 | #define DDR_EN BIT(0) | ||
| 96 | #define DDDR_SEL BIT(1) | ||
| 97 | #define DDR_4PHASE BIT(2) | ||
| 98 | #define DDR_WIDTH (0x3 << 4) | ||
| 99 | #define DDR_PAD_MODE (0x1 << 8) | ||
| 100 | |||
| 101 | #define DPI_TGEN_HWIDTH 0x20 | ||
| 102 | #define HPW 0 | ||
| 103 | #define HPW_MASK (0xFFF << 0) | ||
| 104 | |||
| 105 | #define DPI_TGEN_HPORCH 0x24 | ||
| 106 | #define HBP 0 | ||
| 107 | #define HBP_MASK (0xFFF << 0) | ||
| 108 | #define HFP 16 | ||
| 109 | #define HFP_MASK (0xFFF << 16) | ||
| 110 | |||
| 111 | #define DPI_TGEN_VWIDTH 0x28 | ||
| 112 | #define DPI_TGEN_VPORCH 0x2C | ||
| 113 | |||
| 114 | #define VSYNC_WIDTH_SHIFT 0 | ||
| 115 | #define VSYNC_WIDTH_MASK (0xFFF << 0) | ||
| 116 | #define VSYNC_HALF_LINE_SHIFT 16 | ||
| 117 | #define VSYNC_HALF_LINE_MASK BIT(16) | ||
| 118 | #define VSYNC_BACK_PORCH_SHIFT 0 | ||
| 119 | #define VSYNC_BACK_PORCH_MASK (0xFFF << 0) | ||
| 120 | #define VSYNC_FRONT_PORCH_SHIFT 16 | ||
| 121 | #define VSYNC_FRONT_PORCH_MASK (0xFFF << 16) | ||
| 122 | |||
| 123 | #define DPI_BG_HCNTL 0x30 | ||
| 124 | #define BG_RIGHT (0x1FFF << 0) | ||
| 125 | #define BG_LEFT (0x1FFF << 16) | ||
| 126 | |||
| 127 | #define DPI_BG_VCNTL 0x34 | ||
| 128 | #define BG_BOT (0x1FFF << 0) | ||
| 129 | #define BG_TOP (0x1FFF << 16) | ||
| 130 | |||
| 131 | #define DPI_BG_COLOR 0x38 | ||
| 132 | #define BG_B (0xF << 0) | ||
| 133 | #define BG_G (0xF << 8) | ||
| 134 | #define BG_R (0xF << 16) | ||
| 135 | |||
| 136 | #define DPI_FIFO_CTL 0x3C | ||
| 137 | #define FIFO_VALID_SET (0x1F << 0) | ||
| 138 | #define FIFO_RST_SEL (0x1 << 8) | ||
| 139 | |||
| 140 | #define DPI_STATUS 0x40 | ||
| 141 | #define VCOUNTER (0x1FFF << 0) | ||
| 142 | #define DPI_BUSY BIT(16) | ||
| 143 | #define OUTEN BIT(17) | ||
| 144 | #define FIELD BIT(20) | ||
| 145 | #define TDLR BIT(21) | ||
| 146 | |||
| 147 | #define DPI_TMODE 0x44 | ||
| 148 | #define DPI_OEN_ON BIT(0) | ||
| 149 | |||
| 150 | #define DPI_CHECKSUM 0x48 | ||
| 151 | #define DPI_CHECKSUM_MASK (0xFFFFFF << 0) | ||
| 152 | #define DPI_CHECKSUM_READY BIT(30) | ||
| 153 | #define DPI_CHECKSUM_EN BIT(31) | ||
| 154 | |||
| 155 | #define DPI_DUMMY 0x50 | ||
| 156 | #define DPI_DUMMY_MASK (0xFFFFFFFF << 0) | ||
| 157 | |||
| 158 | #define DPI_TGEN_VWIDTH_LEVEN 0x68 | ||
| 159 | #define DPI_TGEN_VPORCH_LEVEN 0x6C | ||
| 160 | #define DPI_TGEN_VWIDTH_RODD 0x70 | ||
| 161 | #define DPI_TGEN_VPORCH_RODD 0x74 | ||
| 162 | #define DPI_TGEN_VWIDTH_REVEN 0x78 | ||
| 163 | #define DPI_TGEN_VPORCH_REVEN 0x7C | ||
| 164 | |||
| 165 | #define DPI_ESAV_VTIMING_LODD 0x80 | ||
| 166 | #define ESAV_VOFST_LODD (0xFFF << 0) | ||
| 167 | #define ESAV_VWID_LODD (0xFFF << 16) | ||
| 168 | |||
| 169 | #define DPI_ESAV_VTIMING_LEVEN 0x84 | ||
| 170 | #define ESAV_VOFST_LEVEN (0xFFF << 0) | ||
| 171 | #define ESAV_VWID_LEVEN (0xFFF << 16) | ||
| 172 | |||
| 173 | #define DPI_ESAV_VTIMING_RODD 0x88 | ||
| 174 | #define ESAV_VOFST_RODD (0xFFF << 0) | ||
| 175 | #define ESAV_VWID_RODD (0xFFF << 16) | ||
| 176 | |||
| 177 | #define DPI_ESAV_VTIMING_REVEN 0x8C | ||
| 178 | #define ESAV_VOFST_REVEN (0xFFF << 0) | ||
| 179 | #define ESAV_VWID_REVEN (0xFFF << 16) | ||
| 180 | |||
| 181 | #define DPI_ESAV_FTIMING 0x90 | ||
| 182 | #define ESAV_FOFST_ODD (0xFFF << 0) | ||
| 183 | #define ESAV_FOFST_EVEN (0xFFF << 16) | ||
| 184 | |||
| 185 | #define DPI_CLPF_SETTING 0x94 | ||
| 186 | #define CLPF_TYPE (0x3 << 0) | ||
| 187 | #define ROUND_EN BIT(4) | ||
| 188 | |||
| 189 | #define DPI_Y_LIMIT 0x98 | ||
| 190 | #define Y_LIMINT_BOT 0 | ||
| 191 | #define Y_LIMINT_BOT_MASK (0xFFF << 0) | ||
| 192 | #define Y_LIMINT_TOP 16 | ||
| 193 | #define Y_LIMINT_TOP_MASK (0xFFF << 16) | ||
| 194 | |||
| 195 | #define DPI_C_LIMIT 0x9C | ||
| 196 | #define C_LIMIT_BOT 0 | ||
| 197 | #define C_LIMIT_BOT_MASK (0xFFF << 0) | ||
| 198 | #define C_LIMIT_TOP 16 | ||
| 199 | #define C_LIMIT_TOP_MASK (0xFFF << 16) | ||
| 200 | |||
| 201 | #define DPI_YUV422_SETTING 0xA0 | ||
| 202 | #define UV_SWAP BIT(0) | ||
| 203 | #define CR_DELSEL BIT(4) | ||
| 204 | #define CB_DELSEL BIT(5) | ||
| 205 | #define Y_DELSEL BIT(6) | ||
| 206 | #define DE_DELSEL BIT(7) | ||
| 207 | |||
| 208 | #define DPI_EMBSYNC_SETTING 0xA4 | ||
| 209 | #define EMBSYNC_R_CR_EN BIT(0) | ||
| 210 | #define EMPSYNC_G_Y_EN BIT(1) | ||
| 211 | #define EMPSYNC_B_CB_EN BIT(2) | ||
| 212 | #define ESAV_F_INV BIT(4) | ||
| 213 | #define ESAV_V_INV BIT(5) | ||
| 214 | #define ESAV_H_INV BIT(6) | ||
| 215 | #define ESAV_CODE_MAN BIT(8) | ||
| 216 | #define VS_OUT_SEL (0x7 << 12) | ||
| 217 | |||
| 218 | #define DPI_ESAV_CODE_SET0 0xA8 | ||
| 219 | #define ESAV_CODE0 (0xFFF << 0) | ||
| 220 | #define ESAV_CODE1 (0xFFF << 16) | ||
| 221 | |||
| 222 | #define DPI_ESAV_CODE_SET1 0xAC | ||
| 223 | #define ESAV_CODE2 (0xFFF << 0) | ||
| 224 | #define ESAV_CODE3_MSB BIT(16) | ||
| 225 | |||
| 226 | #define DPI_H_FRE_CON 0xE0 | ||
| 227 | #define H_FRE_2N BIT(25) | ||
| 228 | #endif /* __MTK_DPI_REGS_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c new file mode 100644 index 000000000000..3095fc182f07 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c | |||
| @@ -0,0 +1,582 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <asm/barrier.h> | ||
| 15 | #include <drm/drmP.h> | ||
| 16 | #include <drm/drm_atomic_helper.h> | ||
| 17 | #include <drm/drm_crtc_helper.h> | ||
| 18 | #include <drm/drm_plane_helper.h> | ||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/pm_runtime.h> | ||
| 21 | #include <soc/mediatek/smi.h> | ||
| 22 | |||
| 23 | #include "mtk_drm_drv.h" | ||
| 24 | #include "mtk_drm_crtc.h" | ||
| 25 | #include "mtk_drm_ddp.h" | ||
| 26 | #include "mtk_drm_ddp_comp.h" | ||
| 27 | #include "mtk_drm_gem.h" | ||
| 28 | #include "mtk_drm_plane.h" | ||
| 29 | |||
| 30 | /** | ||
| 31 | * struct mtk_drm_crtc - MediaTek specific crtc structure. | ||
| 32 | * @base: crtc object. | ||
| 33 | * @enabled: records whether crtc_enable succeeded | ||
| 34 | * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane | ||
| 35 | * @pending_planes: whether any plane has pending changes to be applied | ||
| 36 | * @config_regs: memory mapped mmsys configuration register space | ||
| 37 | * @mutex: handle to one of the ten disp_mutex streams | ||
| 38 | * @ddp_comp_nr: number of components in ddp_comp | ||
| 39 | * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc | ||
| 40 | */ | ||
| 41 | struct mtk_drm_crtc { | ||
| 42 | struct drm_crtc base; | ||
| 43 | bool enabled; | ||
| 44 | |||
| 45 | bool pending_needs_vblank; | ||
| 46 | struct drm_pending_vblank_event *event; | ||
| 47 | |||
| 48 | struct mtk_drm_plane planes[OVL_LAYER_NR]; | ||
| 49 | bool pending_planes; | ||
| 50 | |||
| 51 | void __iomem *config_regs; | ||
| 52 | struct mtk_disp_mutex *mutex; | ||
| 53 | unsigned int ddp_comp_nr; | ||
| 54 | struct mtk_ddp_comp **ddp_comp; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct mtk_crtc_state { | ||
| 58 | struct drm_crtc_state base; | ||
| 59 | |||
| 60 | bool pending_config; | ||
| 61 | unsigned int pending_width; | ||
| 62 | unsigned int pending_height; | ||
| 63 | unsigned int pending_vrefresh; | ||
| 64 | }; | ||
| 65 | |||
| 66 | static inline struct mtk_drm_crtc *to_mtk_crtc(struct drm_crtc *c) | ||
| 67 | { | ||
| 68 | return container_of(c, struct mtk_drm_crtc, base); | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline struct mtk_crtc_state *to_mtk_crtc_state(struct drm_crtc_state *s) | ||
| 72 | { | ||
| 73 | return container_of(s, struct mtk_crtc_state, base); | ||
| 74 | } | ||
| 75 | |||
| 76 | static void mtk_drm_crtc_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) | ||
| 77 | { | ||
| 78 | struct drm_crtc *crtc = &mtk_crtc->base; | ||
| 79 | unsigned long flags; | ||
| 80 | |||
| 81 | spin_lock_irqsave(&crtc->dev->event_lock, flags); | ||
| 82 | drm_crtc_send_vblank_event(crtc, mtk_crtc->event); | ||
| 83 | drm_crtc_vblank_put(crtc); | ||
| 84 | mtk_crtc->event = NULL; | ||
| 85 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | ||
| 86 | } | ||
| 87 | |||
| 88 | static void mtk_drm_finish_page_flip(struct mtk_drm_crtc *mtk_crtc) | ||
| 89 | { | ||
| 90 | drm_crtc_handle_vblank(&mtk_crtc->base); | ||
| 91 | if (mtk_crtc->pending_needs_vblank) { | ||
| 92 | mtk_drm_crtc_finish_page_flip(mtk_crtc); | ||
| 93 | mtk_crtc->pending_needs_vblank = false; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | static void mtk_drm_crtc_destroy(struct drm_crtc *crtc) | ||
| 98 | { | ||
| 99 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 100 | int i; | ||
| 101 | |||
| 102 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) | ||
| 103 | clk_unprepare(mtk_crtc->ddp_comp[i]->clk); | ||
| 104 | |||
| 105 | mtk_disp_mutex_put(mtk_crtc->mutex); | ||
| 106 | |||
| 107 | drm_crtc_cleanup(crtc); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void mtk_drm_crtc_reset(struct drm_crtc *crtc) | ||
| 111 | { | ||
| 112 | struct mtk_crtc_state *state; | ||
| 113 | |||
| 114 | if (crtc->state) { | ||
| 115 | if (crtc->state->mode_blob) | ||
| 116 | drm_property_unreference_blob(crtc->state->mode_blob); | ||
| 117 | |||
| 118 | state = to_mtk_crtc_state(crtc->state); | ||
| 119 | memset(state, 0, sizeof(*state)); | ||
| 120 | } else { | ||
| 121 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
| 122 | if (!state) | ||
| 123 | return; | ||
| 124 | crtc->state = &state->base; | ||
| 125 | } | ||
| 126 | |||
| 127 | state->base.crtc = crtc; | ||
| 128 | } | ||
| 129 | |||
| 130 | static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc) | ||
| 131 | { | ||
| 132 | struct mtk_crtc_state *state; | ||
| 133 | |||
| 134 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
| 135 | if (!state) | ||
| 136 | return NULL; | ||
| 137 | |||
| 138 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | ||
| 139 | |||
| 140 | WARN_ON(state->base.crtc != crtc); | ||
| 141 | state->base.crtc = crtc; | ||
| 142 | |||
| 143 | return &state->base; | ||
| 144 | } | ||
| 145 | |||
| 146 | static void mtk_drm_crtc_destroy_state(struct drm_crtc *crtc, | ||
| 147 | struct drm_crtc_state *state) | ||
| 148 | { | ||
| 149 | __drm_atomic_helper_crtc_destroy_state(crtc, state); | ||
| 150 | kfree(to_mtk_crtc_state(state)); | ||
| 151 | } | ||
| 152 | |||
| 153 | static bool mtk_drm_crtc_mode_fixup(struct drm_crtc *crtc, | ||
| 154 | const struct drm_display_mode *mode, | ||
| 155 | struct drm_display_mode *adjusted_mode) | ||
| 156 | { | ||
| 157 | /* Nothing to do here, but this callback is mandatory. */ | ||
| 158 | return true; | ||
| 159 | } | ||
| 160 | |||
| 161 | static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) | ||
| 162 | { | ||
| 163 | struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); | ||
| 164 | |||
| 165 | state->pending_width = crtc->mode.hdisplay; | ||
| 166 | state->pending_height = crtc->mode.vdisplay; | ||
| 167 | state->pending_vrefresh = crtc->mode.vrefresh; | ||
| 168 | wmb(); /* Make sure the above parameters are set before update */ | ||
| 169 | state->pending_config = true; | ||
| 170 | } | ||
| 171 | |||
| 172 | int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe) | ||
| 173 | { | ||
| 174 | struct mtk_drm_private *priv = drm->dev_private; | ||
| 175 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); | ||
| 176 | struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; | ||
| 177 | |||
| 178 | mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base); | ||
| 179 | |||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe) | ||
| 184 | { | ||
| 185 | struct mtk_drm_private *priv = drm->dev_private; | ||
| 186 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); | ||
| 187 | struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; | ||
| 188 | |||
| 189 | mtk_ddp_comp_disable_vblank(ovl); | ||
| 190 | } | ||
| 191 | |||
| 192 | static int mtk_crtc_ddp_clk_enable(struct mtk_drm_crtc *mtk_crtc) | ||
| 193 | { | ||
| 194 | int ret; | ||
| 195 | int i; | ||
| 196 | |||
| 197 | DRM_DEBUG_DRIVER("%s\n", __func__); | ||
| 198 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) { | ||
| 199 | ret = clk_enable(mtk_crtc->ddp_comp[i]->clk); | ||
| 200 | if (ret) { | ||
| 201 | DRM_ERROR("Failed to enable clock %d: %d\n", i, ret); | ||
| 202 | goto err; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | return 0; | ||
| 207 | err: | ||
| 208 | while (--i >= 0) | ||
| 209 | clk_disable(mtk_crtc->ddp_comp[i]->clk); | ||
| 210 | return ret; | ||
| 211 | } | ||
| 212 | |||
| 213 | static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc) | ||
| 214 | { | ||
| 215 | int i; | ||
| 216 | |||
| 217 | DRM_DEBUG_DRIVER("%s\n", __func__); | ||
| 218 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) | ||
| 219 | clk_disable(mtk_crtc->ddp_comp[i]->clk); | ||
| 220 | } | ||
| 221 | |||
| 222 | static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) | ||
| 223 | { | ||
| 224 | struct drm_crtc *crtc = &mtk_crtc->base; | ||
| 225 | unsigned int width, height, vrefresh; | ||
| 226 | int ret; | ||
| 227 | int i; | ||
| 228 | |||
| 229 | DRM_DEBUG_DRIVER("%s\n", __func__); | ||
| 230 | if (WARN_ON(!crtc->state)) | ||
| 231 | return -EINVAL; | ||
| 232 | |||
| 233 | width = crtc->state->adjusted_mode.hdisplay; | ||
| 234 | height = crtc->state->adjusted_mode.vdisplay; | ||
| 235 | vrefresh = crtc->state->adjusted_mode.vrefresh; | ||
| 236 | |||
| 237 | ret = pm_runtime_get_sync(crtc->dev->dev); | ||
| 238 | if (ret < 0) { | ||
| 239 | DRM_ERROR("Failed to enable power domain: %d\n", ret); | ||
| 240 | return ret; | ||
| 241 | } | ||
| 242 | |||
| 243 | ret = mtk_disp_mutex_prepare(mtk_crtc->mutex); | ||
| 244 | if (ret < 0) { | ||
| 245 | DRM_ERROR("Failed to enable mutex clock: %d\n", ret); | ||
| 246 | goto err_pm_runtime_put; | ||
| 247 | } | ||
| 248 | |||
| 249 | ret = mtk_crtc_ddp_clk_enable(mtk_crtc); | ||
| 250 | if (ret < 0) { | ||
| 251 | DRM_ERROR("Failed to enable component clocks: %d\n", ret); | ||
| 252 | goto err_mutex_unprepare; | ||
| 253 | } | ||
| 254 | |||
| 255 | DRM_DEBUG_DRIVER("mediatek_ddp_ddp_path_setup\n"); | ||
| 256 | for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) { | ||
| 257 | mtk_ddp_add_comp_to_path(mtk_crtc->config_regs, | ||
| 258 | mtk_crtc->ddp_comp[i]->id, | ||
| 259 | mtk_crtc->ddp_comp[i + 1]->id); | ||
| 260 | mtk_disp_mutex_add_comp(mtk_crtc->mutex, | ||
| 261 | mtk_crtc->ddp_comp[i]->id); | ||
| 262 | } | ||
| 263 | mtk_disp_mutex_add_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id); | ||
| 264 | mtk_disp_mutex_enable(mtk_crtc->mutex); | ||
| 265 | |||
| 266 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) { | ||
| 267 | struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i]; | ||
| 268 | |||
| 269 | mtk_ddp_comp_config(comp, width, height, vrefresh); | ||
| 270 | mtk_ddp_comp_start(comp); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Initially configure all planes */ | ||
| 274 | for (i = 0; i < OVL_LAYER_NR; i++) { | ||
| 275 | struct drm_plane *plane = &mtk_crtc->planes[i].base; | ||
| 276 | struct mtk_plane_state *plane_state; | ||
| 277 | |||
| 278 | plane_state = to_mtk_plane_state(plane->state); | ||
| 279 | mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[0], i, | ||
| 280 | plane_state); | ||
| 281 | } | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | |||
| 285 | err_mutex_unprepare: | ||
| 286 | mtk_disp_mutex_unprepare(mtk_crtc->mutex); | ||
| 287 | err_pm_runtime_put: | ||
| 288 | pm_runtime_put(crtc->dev->dev); | ||
| 289 | return ret; | ||
| 290 | } | ||
| 291 | |||
| 292 | static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc) | ||
| 293 | { | ||
| 294 | struct drm_device *drm = mtk_crtc->base.dev; | ||
| 295 | int i; | ||
| 296 | |||
| 297 | DRM_DEBUG_DRIVER("%s\n", __func__); | ||
| 298 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) | ||
| 299 | mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]); | ||
| 300 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) | ||
| 301 | mtk_disp_mutex_remove_comp(mtk_crtc->mutex, | ||
| 302 | mtk_crtc->ddp_comp[i]->id); | ||
| 303 | mtk_disp_mutex_disable(mtk_crtc->mutex); | ||
| 304 | for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) { | ||
| 305 | mtk_ddp_remove_comp_from_path(mtk_crtc->config_regs, | ||
| 306 | mtk_crtc->ddp_comp[i]->id, | ||
| 307 | mtk_crtc->ddp_comp[i + 1]->id); | ||
| 308 | mtk_disp_mutex_remove_comp(mtk_crtc->mutex, | ||
| 309 | mtk_crtc->ddp_comp[i]->id); | ||
| 310 | } | ||
| 311 | mtk_disp_mutex_remove_comp(mtk_crtc->mutex, mtk_crtc->ddp_comp[i]->id); | ||
| 312 | mtk_crtc_ddp_clk_disable(mtk_crtc); | ||
| 313 | mtk_disp_mutex_unprepare(mtk_crtc->mutex); | ||
| 314 | |||
| 315 | pm_runtime_put(drm->dev); | ||
| 316 | } | ||
| 317 | |||
| 318 | static void mtk_drm_crtc_enable(struct drm_crtc *crtc) | ||
| 319 | { | ||
| 320 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 321 | struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; | ||
| 322 | int ret; | ||
| 323 | |||
| 324 | DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id); | ||
| 325 | |||
| 326 | ret = mtk_smi_larb_get(ovl->larb_dev); | ||
| 327 | if (ret) { | ||
| 328 | DRM_ERROR("Failed to get larb: %d\n", ret); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | |||
| 332 | ret = mtk_crtc_ddp_hw_init(mtk_crtc); | ||
| 333 | if (ret) { | ||
| 334 | mtk_smi_larb_put(ovl->larb_dev); | ||
| 335 | return; | ||
| 336 | } | ||
| 337 | |||
| 338 | drm_crtc_vblank_on(crtc); | ||
| 339 | mtk_crtc->enabled = true; | ||
| 340 | } | ||
| 341 | |||
| 342 | static void mtk_drm_crtc_disable(struct drm_crtc *crtc) | ||
| 343 | { | ||
| 344 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 345 | struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; | ||
| 346 | int i; | ||
| 347 | |||
| 348 | DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id); | ||
| 349 | if (!mtk_crtc->enabled) | ||
| 350 | return; | ||
| 351 | |||
| 352 | /* Set all pending plane state to disabled */ | ||
| 353 | for (i = 0; i < OVL_LAYER_NR; i++) { | ||
| 354 | struct drm_plane *plane = &mtk_crtc->planes[i].base; | ||
| 355 | struct mtk_plane_state *plane_state; | ||
| 356 | |||
| 357 | plane_state = to_mtk_plane_state(plane->state); | ||
| 358 | plane_state->pending.enable = false; | ||
| 359 | plane_state->pending.config = true; | ||
| 360 | } | ||
| 361 | mtk_crtc->pending_planes = true; | ||
| 362 | |||
| 363 | /* Wait for planes to be disabled */ | ||
| 364 | drm_crtc_wait_one_vblank(crtc); | ||
| 365 | |||
| 366 | drm_crtc_vblank_off(crtc); | ||
| 367 | mtk_crtc_ddp_hw_fini(mtk_crtc); | ||
| 368 | mtk_smi_larb_put(ovl->larb_dev); | ||
| 369 | |||
| 370 | mtk_crtc->enabled = false; | ||
| 371 | } | ||
| 372 | |||
| 373 | static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc, | ||
| 374 | struct drm_crtc_state *old_crtc_state) | ||
| 375 | { | ||
| 376 | struct mtk_crtc_state *state = to_mtk_crtc_state(crtc->state); | ||
| 377 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 378 | |||
| 379 | if (mtk_crtc->event && state->base.event) | ||
| 380 | DRM_ERROR("new event while there is still a pending event\n"); | ||
| 381 | |||
| 382 | if (state->base.event) { | ||
| 383 | state->base.event->pipe = drm_crtc_index(crtc); | ||
| 384 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | ||
| 385 | mtk_crtc->event = state->base.event; | ||
| 386 | state->base.event = NULL; | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc, | ||
| 391 | struct drm_crtc_state *old_crtc_state) | ||
| 392 | { | ||
| 393 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 394 | unsigned int pending_planes = 0; | ||
| 395 | int i; | ||
| 396 | |||
| 397 | if (mtk_crtc->event) | ||
| 398 | mtk_crtc->pending_needs_vblank = true; | ||
| 399 | for (i = 0; i < OVL_LAYER_NR; i++) { | ||
| 400 | struct drm_plane *plane = &mtk_crtc->planes[i].base; | ||
| 401 | struct mtk_plane_state *plane_state; | ||
| 402 | |||
| 403 | plane_state = to_mtk_plane_state(plane->state); | ||
| 404 | if (plane_state->pending.dirty) { | ||
| 405 | plane_state->pending.config = true; | ||
| 406 | plane_state->pending.dirty = false; | ||
| 407 | pending_planes |= BIT(i); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | if (pending_planes) | ||
| 411 | mtk_crtc->pending_planes = true; | ||
| 412 | } | ||
| 413 | |||
| 414 | static const struct drm_crtc_funcs mtk_crtc_funcs = { | ||
| 415 | .set_config = drm_atomic_helper_set_config, | ||
| 416 | .page_flip = drm_atomic_helper_page_flip, | ||
| 417 | .destroy = mtk_drm_crtc_destroy, | ||
| 418 | .reset = mtk_drm_crtc_reset, | ||
| 419 | .atomic_duplicate_state = mtk_drm_crtc_duplicate_state, | ||
| 420 | .atomic_destroy_state = mtk_drm_crtc_destroy_state, | ||
| 421 | }; | ||
| 422 | |||
| 423 | static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = { | ||
| 424 | .mode_fixup = mtk_drm_crtc_mode_fixup, | ||
| 425 | .mode_set_nofb = mtk_drm_crtc_mode_set_nofb, | ||
| 426 | .enable = mtk_drm_crtc_enable, | ||
| 427 | .disable = mtk_drm_crtc_disable, | ||
| 428 | .atomic_begin = mtk_drm_crtc_atomic_begin, | ||
| 429 | .atomic_flush = mtk_drm_crtc_atomic_flush, | ||
| 430 | }; | ||
| 431 | |||
| 432 | static int mtk_drm_crtc_init(struct drm_device *drm, | ||
| 433 | struct mtk_drm_crtc *mtk_crtc, | ||
| 434 | struct drm_plane *primary, | ||
| 435 | struct drm_plane *cursor, unsigned int pipe) | ||
| 436 | { | ||
| 437 | int ret; | ||
| 438 | |||
| 439 | ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor, | ||
| 440 | &mtk_crtc_funcs, NULL); | ||
| 441 | if (ret) | ||
| 442 | goto err_cleanup_crtc; | ||
| 443 | |||
| 444 | drm_crtc_helper_add(&mtk_crtc->base, &mtk_crtc_helper_funcs); | ||
| 445 | |||
| 446 | return 0; | ||
| 447 | |||
| 448 | err_cleanup_crtc: | ||
| 449 | drm_crtc_cleanup(&mtk_crtc->base); | ||
| 450 | return ret; | ||
| 451 | } | ||
| 452 | |||
| 453 | void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl) | ||
| 454 | { | ||
| 455 | struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc); | ||
| 456 | struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state); | ||
| 457 | unsigned int i; | ||
| 458 | |||
| 459 | /* | ||
| 460 | * TODO: instead of updating the registers here, we should prepare | ||
| 461 | * working registers in atomic_commit and let the hardware command | ||
| 462 | * queue update module registers on vblank. | ||
| 463 | */ | ||
| 464 | if (state->pending_config) { | ||
| 465 | mtk_ddp_comp_config(ovl, state->pending_width, | ||
| 466 | state->pending_height, | ||
| 467 | state->pending_vrefresh); | ||
| 468 | |||
| 469 | state->pending_config = false; | ||
| 470 | } | ||
| 471 | |||
| 472 | if (mtk_crtc->pending_planes) { | ||
| 473 | for (i = 0; i < OVL_LAYER_NR; i++) { | ||
| 474 | struct drm_plane *plane = &mtk_crtc->planes[i].base; | ||
| 475 | struct mtk_plane_state *plane_state; | ||
| 476 | |||
| 477 | plane_state = to_mtk_plane_state(plane->state); | ||
| 478 | |||
| 479 | if (plane_state->pending.config) { | ||
| 480 | mtk_ddp_comp_layer_config(ovl, i, plane_state); | ||
| 481 | plane_state->pending.config = false; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | mtk_crtc->pending_planes = false; | ||
| 485 | } | ||
| 486 | |||
| 487 | mtk_drm_finish_page_flip(mtk_crtc); | ||
| 488 | } | ||
| 489 | |||
| 490 | int mtk_drm_crtc_create(struct drm_device *drm_dev, | ||
| 491 | const enum mtk_ddp_comp_id *path, unsigned int path_len) | ||
| 492 | { | ||
| 493 | struct mtk_drm_private *priv = drm_dev->dev_private; | ||
| 494 | struct device *dev = drm_dev->dev; | ||
| 495 | struct mtk_drm_crtc *mtk_crtc; | ||
| 496 | enum drm_plane_type type; | ||
| 497 | unsigned int zpos; | ||
| 498 | int pipe = priv->num_pipes; | ||
| 499 | int ret; | ||
| 500 | int i; | ||
| 501 | |||
| 502 | for (i = 0; i < path_len; i++) { | ||
| 503 | enum mtk_ddp_comp_id comp_id = path[i]; | ||
| 504 | struct device_node *node; | ||
| 505 | |||
| 506 | node = priv->comp_node[comp_id]; | ||
| 507 | if (!node) { | ||
| 508 | dev_info(dev, | ||
| 509 | "Not creating crtc %d because component %d is disabled or missing\n", | ||
| 510 | pipe, comp_id); | ||
| 511 | return 0; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL); | ||
| 516 | if (!mtk_crtc) | ||
| 517 | return -ENOMEM; | ||
| 518 | |||
| 519 | mtk_crtc->config_regs = priv->config_regs; | ||
| 520 | mtk_crtc->ddp_comp_nr = path_len; | ||
| 521 | mtk_crtc->ddp_comp = devm_kmalloc_array(dev, mtk_crtc->ddp_comp_nr, | ||
| 522 | sizeof(*mtk_crtc->ddp_comp), | ||
| 523 | GFP_KERNEL); | ||
| 524 | |||
| 525 | mtk_crtc->mutex = mtk_disp_mutex_get(priv->mutex_dev, pipe); | ||
| 526 | if (IS_ERR(mtk_crtc->mutex)) { | ||
| 527 | ret = PTR_ERR(mtk_crtc->mutex); | ||
| 528 | dev_err(dev, "Failed to get mutex: %d\n", ret); | ||
| 529 | return ret; | ||
| 530 | } | ||
| 531 | |||
| 532 | for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) { | ||
| 533 | enum mtk_ddp_comp_id comp_id = path[i]; | ||
| 534 | struct mtk_ddp_comp *comp; | ||
| 535 | struct device_node *node; | ||
| 536 | |||
| 537 | node = priv->comp_node[comp_id]; | ||
| 538 | comp = priv->ddp_comp[comp_id]; | ||
| 539 | if (!comp) { | ||
| 540 | dev_err(dev, "Component %s not initialized\n", | ||
| 541 | node->full_name); | ||
| 542 | ret = -ENODEV; | ||
| 543 | goto unprepare; | ||
| 544 | } | ||
| 545 | |||
| 546 | ret = clk_prepare(comp->clk); | ||
| 547 | if (ret) { | ||
| 548 | dev_err(dev, | ||
| 549 | "Failed to prepare clock for component %s: %d\n", | ||
| 550 | node->full_name, ret); | ||
| 551 | goto unprepare; | ||
| 552 | } | ||
| 553 | |||
| 554 | mtk_crtc->ddp_comp[i] = comp; | ||
| 555 | } | ||
| 556 | |||
| 557 | for (zpos = 0; zpos < OVL_LAYER_NR; zpos++) { | ||
| 558 | type = (zpos == 0) ? DRM_PLANE_TYPE_PRIMARY : | ||
| 559 | (zpos == 1) ? DRM_PLANE_TYPE_CURSOR : | ||
| 560 | DRM_PLANE_TYPE_OVERLAY; | ||
| 561 | ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos], | ||
| 562 | BIT(pipe), type, zpos); | ||
| 563 | if (ret) | ||
| 564 | goto unprepare; | ||
| 565 | } | ||
| 566 | |||
| 567 | ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base, | ||
| 568 | &mtk_crtc->planes[1].base, pipe); | ||
| 569 | if (ret < 0) | ||
| 570 | goto unprepare; | ||
| 571 | |||
| 572 | priv->crtc[pipe] = &mtk_crtc->base; | ||
| 573 | priv->num_pipes++; | ||
| 574 | |||
| 575 | return 0; | ||
| 576 | |||
| 577 | unprepare: | ||
| 578 | while (--i >= 0) | ||
| 579 | clk_unprepare(mtk_crtc->ddp_comp[i]->clk); | ||
| 580 | |||
| 581 | return ret; | ||
| 582 | } | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h new file mode 100644 index 000000000000..81e5566ec82f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef MTK_DRM_CRTC_H | ||
| 15 | #define MTK_DRM_CRTC_H | ||
| 16 | |||
| 17 | #include <drm/drm_crtc.h> | ||
| 18 | #include "mtk_drm_ddp_comp.h" | ||
| 19 | #include "mtk_drm_plane.h" | ||
| 20 | |||
| 21 | #define OVL_LAYER_NR 4 | ||
| 22 | |||
| 23 | int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe); | ||
| 24 | void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe); | ||
| 25 | void mtk_drm_crtc_check_flush(struct drm_crtc *crtc); | ||
| 26 | void mtk_drm_crtc_commit(struct drm_crtc *crtc); | ||
| 27 | void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl); | ||
| 28 | int mtk_drm_crtc_create(struct drm_device *drm_dev, | ||
| 29 | const enum mtk_ddp_comp_id *path, | ||
| 30 | unsigned int path_len); | ||
| 31 | |||
| 32 | #endif /* MTK_DRM_CRTC_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c new file mode 100644 index 000000000000..17ba9355a49c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c | |||
| @@ -0,0 +1,353 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/clk.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/of_device.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/regmap.h> | ||
| 19 | |||
| 20 | #include "mtk_drm_ddp.h" | ||
| 21 | #include "mtk_drm_ddp_comp.h" | ||
| 22 | |||
| 23 | #define DISP_REG_CONFIG_DISP_OVL0_MOUT_EN 0x040 | ||
| 24 | #define DISP_REG_CONFIG_DISP_OVL1_MOUT_EN 0x044 | ||
| 25 | #define DISP_REG_CONFIG_DISP_OD_MOUT_EN 0x048 | ||
| 26 | #define DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN 0x04c | ||
| 27 | #define DISP_REG_CONFIG_DISP_UFOE_MOUT_EN 0x050 | ||
| 28 | #define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084 | ||
| 29 | #define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088 | ||
| 30 | #define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac | ||
| 31 | #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8 | ||
| 32 | #define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100 | ||
| 33 | |||
| 34 | #define DISP_REG_MUTEX_EN(n) (0x20 + 0x20 * (n)) | ||
| 35 | #define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n)) | ||
| 36 | #define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n)) | ||
| 37 | #define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n)) | ||
| 38 | |||
| 39 | #define MUTEX_MOD_DISP_OVL0 BIT(11) | ||
| 40 | #define MUTEX_MOD_DISP_OVL1 BIT(12) | ||
| 41 | #define MUTEX_MOD_DISP_RDMA0 BIT(13) | ||
| 42 | #define MUTEX_MOD_DISP_RDMA1 BIT(14) | ||
| 43 | #define MUTEX_MOD_DISP_RDMA2 BIT(15) | ||
| 44 | #define MUTEX_MOD_DISP_WDMA0 BIT(16) | ||
| 45 | #define MUTEX_MOD_DISP_WDMA1 BIT(17) | ||
| 46 | #define MUTEX_MOD_DISP_COLOR0 BIT(18) | ||
| 47 | #define MUTEX_MOD_DISP_COLOR1 BIT(19) | ||
| 48 | #define MUTEX_MOD_DISP_AAL BIT(20) | ||
| 49 | #define MUTEX_MOD_DISP_GAMMA BIT(21) | ||
| 50 | #define MUTEX_MOD_DISP_UFOE BIT(22) | ||
| 51 | #define MUTEX_MOD_DISP_PWM0 BIT(23) | ||
| 52 | #define MUTEX_MOD_DISP_PWM1 BIT(24) | ||
| 53 | #define MUTEX_MOD_DISP_OD BIT(25) | ||
| 54 | |||
| 55 | #define MUTEX_SOF_SINGLE_MODE 0 | ||
| 56 | #define MUTEX_SOF_DSI0 1 | ||
| 57 | #define MUTEX_SOF_DSI1 2 | ||
| 58 | #define MUTEX_SOF_DPI0 3 | ||
| 59 | |||
| 60 | #define OVL0_MOUT_EN_COLOR0 0x1 | ||
| 61 | #define OD_MOUT_EN_RDMA0 0x1 | ||
| 62 | #define UFOE_MOUT_EN_DSI0 0x1 | ||
| 63 | #define COLOR0_SEL_IN_OVL0 0x1 | ||
| 64 | #define OVL1_MOUT_EN_COLOR1 0x1 | ||
| 65 | #define GAMMA_MOUT_EN_RDMA1 0x1 | ||
| 66 | #define RDMA1_MOUT_DPI0 0x2 | ||
| 67 | #define DPI0_SEL_IN_RDMA1 0x1 | ||
| 68 | #define COLOR1_SEL_IN_OVL1 0x1 | ||
| 69 | |||
| 70 | struct mtk_disp_mutex { | ||
| 71 | int id; | ||
| 72 | bool claimed; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct mtk_ddp { | ||
| 76 | struct device *dev; | ||
| 77 | struct clk *clk; | ||
| 78 | void __iomem *regs; | ||
| 79 | struct mtk_disp_mutex mutex[10]; | ||
| 80 | }; | ||
| 81 | |||
| 82 | static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = { | ||
| 83 | [DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL, | ||
| 84 | [DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0, | ||
| 85 | [DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1, | ||
| 86 | [DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA, | ||
| 87 | [DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD, | ||
| 88 | [DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0, | ||
| 89 | [DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1, | ||
| 90 | [DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0, | ||
| 91 | [DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1, | ||
| 92 | [DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0, | ||
| 93 | [DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1, | ||
| 94 | [DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2, | ||
| 95 | [DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE, | ||
| 96 | [DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0, | ||
| 97 | [DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1, | ||
| 98 | }; | ||
| 99 | |||
| 100 | static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur, | ||
| 101 | enum mtk_ddp_comp_id next, | ||
| 102 | unsigned int *addr) | ||
| 103 | { | ||
| 104 | unsigned int value; | ||
| 105 | |||
| 106 | if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { | ||
| 107 | *addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN; | ||
| 108 | value = OVL0_MOUT_EN_COLOR0; | ||
| 109 | } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) { | ||
| 110 | *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN; | ||
| 111 | value = OD_MOUT_EN_RDMA0; | ||
| 112 | } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) { | ||
| 113 | *addr = DISP_REG_CONFIG_DISP_UFOE_MOUT_EN; | ||
| 114 | value = UFOE_MOUT_EN_DSI0; | ||
| 115 | } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { | ||
| 116 | *addr = DISP_REG_CONFIG_DISP_OVL1_MOUT_EN; | ||
| 117 | value = OVL1_MOUT_EN_COLOR1; | ||
| 118 | } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) { | ||
| 119 | *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN; | ||
| 120 | value = GAMMA_MOUT_EN_RDMA1; | ||
| 121 | } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { | ||
| 122 | *addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN; | ||
| 123 | value = RDMA1_MOUT_DPI0; | ||
| 124 | } else { | ||
| 125 | value = 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | return value; | ||
| 129 | } | ||
| 130 | |||
| 131 | static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur, | ||
| 132 | enum mtk_ddp_comp_id next, | ||
| 133 | unsigned int *addr) | ||
| 134 | { | ||
| 135 | unsigned int value; | ||
| 136 | |||
| 137 | if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) { | ||
| 138 | *addr = DISP_REG_CONFIG_DISP_COLOR0_SEL_IN; | ||
| 139 | value = COLOR0_SEL_IN_OVL0; | ||
| 140 | } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) { | ||
| 141 | *addr = DISP_REG_CONFIG_DPI_SEL_IN; | ||
| 142 | value = DPI0_SEL_IN_RDMA1; | ||
| 143 | } else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) { | ||
| 144 | *addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN; | ||
| 145 | value = COLOR1_SEL_IN_OVL1; | ||
| 146 | } else { | ||
| 147 | value = 0; | ||
| 148 | } | ||
| 149 | |||
| 150 | return value; | ||
| 151 | } | ||
| 152 | |||
| 153 | void mtk_ddp_add_comp_to_path(void __iomem *config_regs, | ||
| 154 | enum mtk_ddp_comp_id cur, | ||
| 155 | enum mtk_ddp_comp_id next) | ||
| 156 | { | ||
| 157 | unsigned int addr, value, reg; | ||
| 158 | |||
| 159 | value = mtk_ddp_mout_en(cur, next, &addr); | ||
| 160 | if (value) { | ||
| 161 | reg = readl_relaxed(config_regs + addr) | value; | ||
| 162 | writel_relaxed(reg, config_regs + addr); | ||
| 163 | } | ||
| 164 | |||
| 165 | value = mtk_ddp_sel_in(cur, next, &addr); | ||
| 166 | if (value) { | ||
| 167 | reg = readl_relaxed(config_regs + addr) | value; | ||
| 168 | writel_relaxed(reg, config_regs + addr); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | void mtk_ddp_remove_comp_from_path(void __iomem *config_regs, | ||
| 173 | enum mtk_ddp_comp_id cur, | ||
| 174 | enum mtk_ddp_comp_id next) | ||
| 175 | { | ||
| 176 | unsigned int addr, value, reg; | ||
| 177 | |||
| 178 | value = mtk_ddp_mout_en(cur, next, &addr); | ||
| 179 | if (value) { | ||
| 180 | reg = readl_relaxed(config_regs + addr) & ~value; | ||
| 181 | writel_relaxed(reg, config_regs + addr); | ||
| 182 | } | ||
| 183 | |||
| 184 | value = mtk_ddp_sel_in(cur, next, &addr); | ||
| 185 | if (value) { | ||
| 186 | reg = readl_relaxed(config_regs + addr) & ~value; | ||
| 187 | writel_relaxed(reg, config_regs + addr); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id) | ||
| 192 | { | ||
| 193 | struct mtk_ddp *ddp = dev_get_drvdata(dev); | ||
| 194 | |||
| 195 | if (id >= 10) | ||
| 196 | return ERR_PTR(-EINVAL); | ||
| 197 | if (ddp->mutex[id].claimed) | ||
| 198 | return ERR_PTR(-EBUSY); | ||
| 199 | |||
| 200 | ddp->mutex[id].claimed = true; | ||
| 201 | |||
| 202 | return &ddp->mutex[id]; | ||
| 203 | } | ||
| 204 | |||
| 205 | void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex) | ||
| 206 | { | ||
| 207 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 208 | mutex[mutex->id]); | ||
| 209 | |||
| 210 | WARN_ON(&ddp->mutex[mutex->id] != mutex); | ||
| 211 | |||
| 212 | mutex->claimed = false; | ||
| 213 | } | ||
| 214 | |||
| 215 | int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex) | ||
| 216 | { | ||
| 217 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 218 | mutex[mutex->id]); | ||
| 219 | return clk_prepare_enable(ddp->clk); | ||
| 220 | } | ||
| 221 | |||
| 222 | void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex) | ||
| 223 | { | ||
| 224 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 225 | mutex[mutex->id]); | ||
| 226 | clk_disable_unprepare(ddp->clk); | ||
| 227 | } | ||
| 228 | |||
| 229 | void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex, | ||
| 230 | enum mtk_ddp_comp_id id) | ||
| 231 | { | ||
| 232 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 233 | mutex[mutex->id]); | ||
| 234 | unsigned int reg; | ||
| 235 | |||
| 236 | WARN_ON(&ddp->mutex[mutex->id] != mutex); | ||
| 237 | |||
| 238 | switch (id) { | ||
| 239 | case DDP_COMPONENT_DSI0: | ||
| 240 | reg = MUTEX_SOF_DSI0; | ||
| 241 | break; | ||
| 242 | case DDP_COMPONENT_DSI1: | ||
| 243 | reg = MUTEX_SOF_DSI0; | ||
| 244 | break; | ||
| 245 | case DDP_COMPONENT_DPI0: | ||
| 246 | reg = MUTEX_SOF_DPI0; | ||
| 247 | break; | ||
| 248 | default: | ||
| 249 | reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); | ||
| 250 | reg |= mutex_mod[id]; | ||
| 251 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); | ||
| 252 | return; | ||
| 253 | } | ||
| 254 | |||
| 255 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_SOF(mutex->id)); | ||
| 256 | } | ||
| 257 | |||
| 258 | void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex, | ||
| 259 | enum mtk_ddp_comp_id id) | ||
| 260 | { | ||
| 261 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 262 | mutex[mutex->id]); | ||
| 263 | unsigned int reg; | ||
| 264 | |||
| 265 | WARN_ON(&ddp->mutex[mutex->id] != mutex); | ||
| 266 | |||
| 267 | switch (id) { | ||
| 268 | case DDP_COMPONENT_DSI0: | ||
| 269 | case DDP_COMPONENT_DSI1: | ||
| 270 | case DDP_COMPONENT_DPI0: | ||
| 271 | writel_relaxed(MUTEX_SOF_SINGLE_MODE, | ||
| 272 | ddp->regs + DISP_REG_MUTEX_SOF(mutex->id)); | ||
| 273 | break; | ||
| 274 | default: | ||
| 275 | reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); | ||
| 276 | reg &= ~mutex_mod[id]; | ||
| 277 | writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id)); | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex) | ||
| 283 | { | ||
| 284 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 285 | mutex[mutex->id]); | ||
| 286 | |||
| 287 | WARN_ON(&ddp->mutex[mutex->id] != mutex); | ||
| 288 | |||
| 289 | writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id)); | ||
| 290 | } | ||
| 291 | |||
| 292 | void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex) | ||
| 293 | { | ||
| 294 | struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp, | ||
| 295 | mutex[mutex->id]); | ||
| 296 | |||
| 297 | WARN_ON(&ddp->mutex[mutex->id] != mutex); | ||
| 298 | |||
| 299 | writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id)); | ||
| 300 | } | ||
| 301 | |||
| 302 | static int mtk_ddp_probe(struct platform_device *pdev) | ||
| 303 | { | ||
| 304 | struct device *dev = &pdev->dev; | ||
| 305 | struct mtk_ddp *ddp; | ||
| 306 | struct resource *regs; | ||
| 307 | int i; | ||
| 308 | |||
| 309 | ddp = devm_kzalloc(dev, sizeof(*ddp), GFP_KERNEL); | ||
| 310 | if (!ddp) | ||
| 311 | return -ENOMEM; | ||
| 312 | |||
| 313 | for (i = 0; i < 10; i++) | ||
| 314 | ddp->mutex[i].id = i; | ||
| 315 | |||
| 316 | ddp->clk = devm_clk_get(dev, NULL); | ||
| 317 | if (IS_ERR(ddp->clk)) { | ||
| 318 | dev_err(dev, "Failed to get clock\n"); | ||
| 319 | return PTR_ERR(ddp->clk); | ||
| 320 | } | ||
| 321 | |||
| 322 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 323 | ddp->regs = devm_ioremap_resource(dev, regs); | ||
| 324 | if (IS_ERR(ddp->regs)) { | ||
| 325 | dev_err(dev, "Failed to map mutex registers\n"); | ||
| 326 | return PTR_ERR(ddp->regs); | ||
| 327 | } | ||
| 328 | |||
| 329 | platform_set_drvdata(pdev, ddp); | ||
| 330 | |||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | static int mtk_ddp_remove(struct platform_device *pdev) | ||
| 335 | { | ||
| 336 | return 0; | ||
| 337 | } | ||
| 338 | |||
| 339 | static const struct of_device_id ddp_driver_dt_match[] = { | ||
| 340 | { .compatible = "mediatek,mt8173-disp-mutex" }, | ||
| 341 | {}, | ||
| 342 | }; | ||
| 343 | MODULE_DEVICE_TABLE(of, ddp_driver_dt_match); | ||
| 344 | |||
| 345 | struct platform_driver mtk_ddp_driver = { | ||
| 346 | .probe = mtk_ddp_probe, | ||
| 347 | .remove = mtk_ddp_remove, | ||
| 348 | .driver = { | ||
| 349 | .name = "mediatek-ddp", | ||
| 350 | .owner = THIS_MODULE, | ||
| 351 | .of_match_table = ddp_driver_dt_match, | ||
| 352 | }, | ||
| 353 | }; | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h new file mode 100644 index 000000000000..92c11752ff65 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef MTK_DRM_DDP_H | ||
| 15 | #define MTK_DRM_DDP_H | ||
| 16 | |||
| 17 | #include "mtk_drm_ddp_comp.h" | ||
| 18 | |||
| 19 | struct regmap; | ||
| 20 | struct device; | ||
| 21 | struct mtk_disp_mutex; | ||
| 22 | |||
| 23 | void mtk_ddp_add_comp_to_path(void __iomem *config_regs, | ||
| 24 | enum mtk_ddp_comp_id cur, | ||
| 25 | enum mtk_ddp_comp_id next); | ||
| 26 | void mtk_ddp_remove_comp_from_path(void __iomem *config_regs, | ||
| 27 | enum mtk_ddp_comp_id cur, | ||
| 28 | enum mtk_ddp_comp_id next); | ||
| 29 | |||
| 30 | struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id); | ||
| 31 | int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex); | ||
| 32 | void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex, | ||
| 33 | enum mtk_ddp_comp_id id); | ||
| 34 | void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex); | ||
| 35 | void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex); | ||
| 36 | void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex, | ||
| 37 | enum mtk_ddp_comp_id id); | ||
| 38 | void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex); | ||
| 39 | void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex); | ||
| 40 | |||
| 41 | #endif /* MTK_DRM_DDP_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c new file mode 100644 index 000000000000..3970fcf0f05f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * Authors: | ||
| 4 | * YT Shen <yt.shen@mediatek.com> | ||
| 5 | * CK Hu <ck.hu@mediatek.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/clk.h> | ||
| 18 | #include <linux/of.h> | ||
| 19 | #include <linux/of_address.h> | ||
| 20 | #include <linux/of_irq.h> | ||
| 21 | #include <linux/of_platform.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <drm/drmP.h> | ||
| 24 | #include "mtk_drm_drv.h" | ||
| 25 | #include "mtk_drm_plane.h" | ||
| 26 | #include "mtk_drm_ddp_comp.h" | ||
| 27 | |||
| 28 | #define DISP_OD_EN 0x0000 | ||
| 29 | #define DISP_OD_INTEN 0x0008 | ||
| 30 | #define DISP_OD_INTSTA 0x000c | ||
| 31 | #define DISP_OD_CFG 0x0020 | ||
| 32 | #define DISP_OD_SIZE 0x0030 | ||
| 33 | |||
| 34 | #define DISP_REG_UFO_START 0x0000 | ||
| 35 | |||
| 36 | #define DISP_COLOR_CFG_MAIN 0x0400 | ||
| 37 | #define DISP_COLOR_START 0x0c00 | ||
| 38 | #define DISP_COLOR_WIDTH 0x0c50 | ||
| 39 | #define DISP_COLOR_HEIGHT 0x0c54 | ||
| 40 | |||
| 41 | #define OD_RELAY_MODE BIT(0) | ||
| 42 | |||
| 43 | #define UFO_BYPASS BIT(2) | ||
| 44 | |||
| 45 | #define COLOR_BYPASS_ALL BIT(7) | ||
| 46 | #define COLOR_SEQ_SEL BIT(13) | ||
| 47 | |||
| 48 | static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w, | ||
| 49 | unsigned int h, unsigned int vrefresh) | ||
| 50 | { | ||
| 51 | writel(w, comp->regs + DISP_COLOR_WIDTH); | ||
| 52 | writel(h, comp->regs + DISP_COLOR_HEIGHT); | ||
| 53 | } | ||
| 54 | |||
| 55 | static void mtk_color_start(struct mtk_ddp_comp *comp) | ||
| 56 | { | ||
| 57 | writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, | ||
| 58 | comp->regs + DISP_COLOR_CFG_MAIN); | ||
| 59 | writel(0x1, comp->regs + DISP_COLOR_START); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w, | ||
| 63 | unsigned int h, unsigned int vrefresh) | ||
| 64 | { | ||
| 65 | writel(w << 16 | h, comp->regs + DISP_OD_SIZE); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void mtk_od_start(struct mtk_ddp_comp *comp) | ||
| 69 | { | ||
| 70 | writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG); | ||
| 71 | writel(1, comp->regs + DISP_OD_EN); | ||
| 72 | } | ||
| 73 | |||
| 74 | static void mtk_ufoe_start(struct mtk_ddp_comp *comp) | ||
| 75 | { | ||
| 76 | writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START); | ||
| 77 | } | ||
| 78 | |||
| 79 | static const struct mtk_ddp_comp_funcs ddp_color = { | ||
| 80 | .config = mtk_color_config, | ||
| 81 | .start = mtk_color_start, | ||
| 82 | }; | ||
| 83 | |||
| 84 | static const struct mtk_ddp_comp_funcs ddp_od = { | ||
| 85 | .config = mtk_od_config, | ||
| 86 | .start = mtk_od_start, | ||
| 87 | }; | ||
| 88 | |||
| 89 | static const struct mtk_ddp_comp_funcs ddp_ufoe = { | ||
| 90 | .start = mtk_ufoe_start, | ||
| 91 | }; | ||
| 92 | |||
| 93 | static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = { | ||
| 94 | [MTK_DISP_OVL] = "ovl", | ||
| 95 | [MTK_DISP_RDMA] = "rdma", | ||
| 96 | [MTK_DISP_WDMA] = "wdma", | ||
| 97 | [MTK_DISP_COLOR] = "color", | ||
| 98 | [MTK_DISP_AAL] = "aal", | ||
| 99 | [MTK_DISP_GAMMA] = "gamma", | ||
| 100 | [MTK_DISP_UFOE] = "ufoe", | ||
| 101 | [MTK_DSI] = "dsi", | ||
| 102 | [MTK_DPI] = "dpi", | ||
| 103 | [MTK_DISP_PWM] = "pwm", | ||
| 104 | [MTK_DISP_MUTEX] = "mutex", | ||
| 105 | [MTK_DISP_OD] = "od", | ||
| 106 | }; | ||
| 107 | |||
| 108 | struct mtk_ddp_comp_match { | ||
| 109 | enum mtk_ddp_comp_type type; | ||
| 110 | int alias_id; | ||
| 111 | const struct mtk_ddp_comp_funcs *funcs; | ||
| 112 | }; | ||
| 113 | |||
| 114 | static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = { | ||
| 115 | [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, NULL }, | ||
| 116 | [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, &ddp_color }, | ||
| 117 | [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, &ddp_color }, | ||
| 118 | [DDP_COMPONENT_DPI0] = { MTK_DPI, 0, NULL }, | ||
| 119 | [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL }, | ||
| 120 | [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL }, | ||
| 121 | [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, NULL }, | ||
| 122 | [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od }, | ||
| 123 | [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL }, | ||
| 124 | [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL }, | ||
| 125 | [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL }, | ||
| 126 | [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL }, | ||
| 127 | [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL }, | ||
| 128 | [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL }, | ||
| 129 | [DDP_COMPONENT_UFOE] = { MTK_DISP_UFOE, 0, &ddp_ufoe }, | ||
| 130 | [DDP_COMPONENT_WDMA0] = { MTK_DISP_WDMA, 0, NULL }, | ||
| 131 | [DDP_COMPONENT_WDMA1] = { MTK_DISP_WDMA, 1, NULL }, | ||
| 132 | }; | ||
| 133 | |||
| 134 | int mtk_ddp_comp_get_id(struct device_node *node, | ||
| 135 | enum mtk_ddp_comp_type comp_type) | ||
| 136 | { | ||
| 137 | int id = of_alias_get_id(node, mtk_ddp_comp_stem[comp_type]); | ||
| 138 | int i; | ||
| 139 | |||
| 140 | for (i = 0; i < ARRAY_SIZE(mtk_ddp_matches); i++) { | ||
| 141 | if (comp_type == mtk_ddp_matches[i].type && | ||
| 142 | (id < 0 || id == mtk_ddp_matches[i].alias_id)) | ||
| 143 | return i; | ||
| 144 | } | ||
| 145 | |||
| 146 | return -EINVAL; | ||
| 147 | } | ||
| 148 | |||
| 149 | int mtk_ddp_comp_init(struct device *dev, struct device_node *node, | ||
| 150 | struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id, | ||
| 151 | const struct mtk_ddp_comp_funcs *funcs) | ||
| 152 | { | ||
| 153 | enum mtk_ddp_comp_type type; | ||
| 154 | struct device_node *larb_node; | ||
| 155 | struct platform_device *larb_pdev; | ||
| 156 | |||
| 157 | if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX) | ||
| 158 | return -EINVAL; | ||
| 159 | |||
| 160 | comp->id = comp_id; | ||
| 161 | comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs; | ||
| 162 | |||
| 163 | if (comp_id == DDP_COMPONENT_DPI0 || | ||
| 164 | comp_id == DDP_COMPONENT_DSI0 || | ||
| 165 | comp_id == DDP_COMPONENT_PWM0) { | ||
| 166 | comp->regs = NULL; | ||
| 167 | comp->clk = NULL; | ||
| 168 | comp->irq = 0; | ||
| 169 | return 0; | ||
| 170 | } | ||
| 171 | |||
| 172 | comp->regs = of_iomap(node, 0); | ||
| 173 | comp->irq = of_irq_get(node, 0); | ||
| 174 | comp->clk = of_clk_get(node, 0); | ||
| 175 | if (IS_ERR(comp->clk)) | ||
| 176 | comp->clk = NULL; | ||
| 177 | |||
| 178 | type = mtk_ddp_matches[comp_id].type; | ||
| 179 | |||
| 180 | /* Only DMA capable components need the LARB property */ | ||
| 181 | comp->larb_dev = NULL; | ||
| 182 | if (type != MTK_DISP_OVL && | ||
| 183 | type != MTK_DISP_RDMA && | ||
| 184 | type != MTK_DISP_WDMA) | ||
| 185 | return 0; | ||
| 186 | |||
| 187 | larb_node = of_parse_phandle(node, "mediatek,larb", 0); | ||
| 188 | if (!larb_node) { | ||
| 189 | dev_err(dev, | ||
| 190 | "Missing mediadek,larb phandle in %s node\n", | ||
| 191 | node->full_name); | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 194 | |||
| 195 | larb_pdev = of_find_device_by_node(larb_node); | ||
| 196 | if (!larb_pdev) { | ||
| 197 | dev_warn(dev, "Waiting for larb device %s\n", | ||
| 198 | larb_node->full_name); | ||
| 199 | of_node_put(larb_node); | ||
| 200 | return -EPROBE_DEFER; | ||
| 201 | } | ||
| 202 | of_node_put(larb_node); | ||
| 203 | |||
| 204 | comp->larb_dev = &larb_pdev->dev; | ||
| 205 | |||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp) | ||
| 210 | { | ||
| 211 | struct mtk_drm_private *private = drm->dev_private; | ||
| 212 | |||
| 213 | if (private->ddp_comp[comp->id]) | ||
| 214 | return -EBUSY; | ||
| 215 | |||
| 216 | private->ddp_comp[comp->id] = comp; | ||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp) | ||
| 221 | { | ||
| 222 | struct mtk_drm_private *private = drm->dev_private; | ||
| 223 | |||
| 224 | private->ddp_comp[comp->id] = NULL; | ||
| 225 | } | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h new file mode 100644 index 000000000000..6b13ba97094d --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | |||
| @@ -0,0 +1,150 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef MTK_DRM_DDP_COMP_H | ||
| 15 | #define MTK_DRM_DDP_COMP_H | ||
| 16 | |||
| 17 | #include <linux/io.h> | ||
| 18 | |||
| 19 | struct device; | ||
| 20 | struct device_node; | ||
| 21 | struct drm_crtc; | ||
| 22 | struct drm_device; | ||
| 23 | struct mtk_plane_state; | ||
| 24 | |||
| 25 | enum mtk_ddp_comp_type { | ||
| 26 | MTK_DISP_OVL, | ||
| 27 | MTK_DISP_RDMA, | ||
| 28 | MTK_DISP_WDMA, | ||
| 29 | MTK_DISP_COLOR, | ||
| 30 | MTK_DISP_AAL, | ||
| 31 | MTK_DISP_GAMMA, | ||
| 32 | MTK_DISP_UFOE, | ||
| 33 | MTK_DSI, | ||
| 34 | MTK_DPI, | ||
| 35 | MTK_DISP_PWM, | ||
| 36 | MTK_DISP_MUTEX, | ||
| 37 | MTK_DISP_OD, | ||
| 38 | MTK_DDP_COMP_TYPE_MAX, | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum mtk_ddp_comp_id { | ||
| 42 | DDP_COMPONENT_AAL, | ||
| 43 | DDP_COMPONENT_COLOR0, | ||
| 44 | DDP_COMPONENT_COLOR1, | ||
| 45 | DDP_COMPONENT_DPI0, | ||
| 46 | DDP_COMPONENT_DSI0, | ||
| 47 | DDP_COMPONENT_DSI1, | ||
| 48 | DDP_COMPONENT_GAMMA, | ||
| 49 | DDP_COMPONENT_OD, | ||
| 50 | DDP_COMPONENT_OVL0, | ||
| 51 | DDP_COMPONENT_OVL1, | ||
| 52 | DDP_COMPONENT_PWM0, | ||
| 53 | DDP_COMPONENT_PWM1, | ||
| 54 | DDP_COMPONENT_RDMA0, | ||
| 55 | DDP_COMPONENT_RDMA1, | ||
| 56 | DDP_COMPONENT_RDMA2, | ||
| 57 | DDP_COMPONENT_UFOE, | ||
| 58 | DDP_COMPONENT_WDMA0, | ||
| 59 | DDP_COMPONENT_WDMA1, | ||
| 60 | DDP_COMPONENT_ID_MAX, | ||
| 61 | }; | ||
| 62 | |||
| 63 | struct mtk_ddp_comp; | ||
| 64 | |||
| 65 | struct mtk_ddp_comp_funcs { | ||
| 66 | void (*config)(struct mtk_ddp_comp *comp, unsigned int w, | ||
| 67 | unsigned int h, unsigned int vrefresh); | ||
| 68 | void (*start)(struct mtk_ddp_comp *comp); | ||
| 69 | void (*stop)(struct mtk_ddp_comp *comp); | ||
| 70 | void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc); | ||
| 71 | void (*disable_vblank)(struct mtk_ddp_comp *comp); | ||
| 72 | void (*layer_on)(struct mtk_ddp_comp *comp, unsigned int idx); | ||
| 73 | void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx); | ||
| 74 | void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx, | ||
| 75 | struct mtk_plane_state *state); | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct mtk_ddp_comp { | ||
| 79 | struct clk *clk; | ||
| 80 | void __iomem *regs; | ||
| 81 | int irq; | ||
| 82 | struct device *larb_dev; | ||
| 83 | enum mtk_ddp_comp_id id; | ||
| 84 | const struct mtk_ddp_comp_funcs *funcs; | ||
| 85 | }; | ||
| 86 | |||
| 87 | static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp, | ||
| 88 | unsigned int w, unsigned int h, | ||
| 89 | unsigned int vrefresh) | ||
| 90 | { | ||
| 91 | if (comp->funcs && comp->funcs->config) | ||
| 92 | comp->funcs->config(comp, w, h, vrefresh); | ||
| 93 | } | ||
| 94 | |||
| 95 | static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp) | ||
| 96 | { | ||
| 97 | if (comp->funcs && comp->funcs->start) | ||
| 98 | comp->funcs->start(comp); | ||
| 99 | } | ||
| 100 | |||
| 101 | static inline void mtk_ddp_comp_stop(struct mtk_ddp_comp *comp) | ||
| 102 | { | ||
| 103 | if (comp->funcs && comp->funcs->stop) | ||
| 104 | comp->funcs->stop(comp); | ||
| 105 | } | ||
| 106 | |||
| 107 | static inline void mtk_ddp_comp_enable_vblank(struct mtk_ddp_comp *comp, | ||
| 108 | struct drm_crtc *crtc) | ||
| 109 | { | ||
| 110 | if (comp->funcs && comp->funcs->enable_vblank) | ||
| 111 | comp->funcs->enable_vblank(comp, crtc); | ||
| 112 | } | ||
| 113 | |||
| 114 | static inline void mtk_ddp_comp_disable_vblank(struct mtk_ddp_comp *comp) | ||
| 115 | { | ||
| 116 | if (comp->funcs && comp->funcs->disable_vblank) | ||
| 117 | comp->funcs->disable_vblank(comp); | ||
| 118 | } | ||
| 119 | |||
| 120 | static inline void mtk_ddp_comp_layer_on(struct mtk_ddp_comp *comp, | ||
| 121 | unsigned int idx) | ||
| 122 | { | ||
| 123 | if (comp->funcs && comp->funcs->layer_on) | ||
| 124 | comp->funcs->layer_on(comp, idx); | ||
| 125 | } | ||
| 126 | |||
| 127 | static inline void mtk_ddp_comp_layer_off(struct mtk_ddp_comp *comp, | ||
| 128 | unsigned int idx) | ||
| 129 | { | ||
| 130 | if (comp->funcs && comp->funcs->layer_off) | ||
| 131 | comp->funcs->layer_off(comp, idx); | ||
| 132 | } | ||
| 133 | |||
| 134 | static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp, | ||
| 135 | unsigned int idx, | ||
| 136 | struct mtk_plane_state *state) | ||
| 137 | { | ||
| 138 | if (comp->funcs && comp->funcs->layer_config) | ||
| 139 | comp->funcs->layer_config(comp, idx, state); | ||
| 140 | } | ||
| 141 | |||
| 142 | int mtk_ddp_comp_get_id(struct device_node *node, | ||
| 143 | enum mtk_ddp_comp_type comp_type); | ||
| 144 | int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node, | ||
| 145 | struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id, | ||
| 146 | const struct mtk_ddp_comp_funcs *funcs); | ||
| 147 | int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp); | ||
| 148 | void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp); | ||
| 149 | |||
| 150 | #endif /* MTK_DRM_DDP_COMP_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c new file mode 100644 index 000000000000..b1223d54d0ab --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c | |||
| @@ -0,0 +1,567 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * Author: YT SHEN <yt.shen@mediatek.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <drm/drmP.h> | ||
| 16 | #include <drm/drm_atomic.h> | ||
| 17 | #include <drm/drm_atomic_helper.h> | ||
| 18 | #include <drm/drm_crtc_helper.h> | ||
| 19 | #include <drm/drm_gem.h> | ||
| 20 | #include <drm/drm_gem_cma_helper.h> | ||
| 21 | #include <linux/component.h> | ||
| 22 | #include <linux/iommu.h> | ||
| 23 | #include <linux/of_address.h> | ||
| 24 | #include <linux/of_platform.h> | ||
| 25 | #include <linux/pm_runtime.h> | ||
| 26 | |||
| 27 | #include "mtk_drm_crtc.h" | ||
| 28 | #include "mtk_drm_ddp.h" | ||
| 29 | #include "mtk_drm_ddp_comp.h" | ||
| 30 | #include "mtk_drm_drv.h" | ||
| 31 | #include "mtk_drm_fb.h" | ||
| 32 | #include "mtk_drm_gem.h" | ||
| 33 | |||
| 34 | #define DRIVER_NAME "mediatek" | ||
| 35 | #define DRIVER_DESC "Mediatek SoC DRM" | ||
| 36 | #define DRIVER_DATE "20150513" | ||
| 37 | #define DRIVER_MAJOR 1 | ||
| 38 | #define DRIVER_MINOR 0 | ||
| 39 | |||
| 40 | static void mtk_atomic_schedule(struct mtk_drm_private *private, | ||
| 41 | struct drm_atomic_state *state) | ||
| 42 | { | ||
| 43 | private->commit.state = state; | ||
| 44 | schedule_work(&private->commit.work); | ||
| 45 | } | ||
| 46 | |||
| 47 | static void mtk_atomic_wait_for_fences(struct drm_atomic_state *state) | ||
| 48 | { | ||
| 49 | struct drm_plane *plane; | ||
| 50 | struct drm_plane_state *plane_state; | ||
| 51 | int i; | ||
| 52 | |||
| 53 | for_each_plane_in_state(state, plane, plane_state, i) | ||
| 54 | mtk_fb_wait(plane->state->fb); | ||
| 55 | } | ||
| 56 | |||
| 57 | static void mtk_atomic_complete(struct mtk_drm_private *private, | ||
| 58 | struct drm_atomic_state *state) | ||
| 59 | { | ||
| 60 | struct drm_device *drm = private->drm; | ||
| 61 | |||
| 62 | mtk_atomic_wait_for_fences(state); | ||
| 63 | |||
| 64 | drm_atomic_helper_commit_modeset_disables(drm, state); | ||
| 65 | drm_atomic_helper_commit_planes(drm, state, false); | ||
| 66 | drm_atomic_helper_commit_modeset_enables(drm, state); | ||
| 67 | drm_atomic_helper_wait_for_vblanks(drm, state); | ||
| 68 | drm_atomic_helper_cleanup_planes(drm, state); | ||
| 69 | drm_atomic_state_free(state); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void mtk_atomic_work(struct work_struct *work) | ||
| 73 | { | ||
| 74 | struct mtk_drm_private *private = container_of(work, | ||
| 75 | struct mtk_drm_private, commit.work); | ||
| 76 | |||
| 77 | mtk_atomic_complete(private, private->commit.state); | ||
| 78 | } | ||
| 79 | |||
| 80 | static int mtk_atomic_commit(struct drm_device *drm, | ||
| 81 | struct drm_atomic_state *state, | ||
| 82 | bool async) | ||
| 83 | { | ||
| 84 | struct mtk_drm_private *private = drm->dev_private; | ||
| 85 | int ret; | ||
| 86 | |||
| 87 | ret = drm_atomic_helper_prepare_planes(drm, state); | ||
| 88 | if (ret) | ||
| 89 | return ret; | ||
| 90 | |||
| 91 | mutex_lock(&private->commit.lock); | ||
| 92 | flush_work(&private->commit.work); | ||
| 93 | |||
| 94 | drm_atomic_helper_swap_state(drm, state); | ||
| 95 | |||
| 96 | if (async) | ||
| 97 | mtk_atomic_schedule(private, state); | ||
| 98 | else | ||
| 99 | mtk_atomic_complete(private, state); | ||
| 100 | |||
| 101 | mutex_unlock(&private->commit.lock); | ||
| 102 | |||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = { | ||
| 107 | .fb_create = mtk_drm_mode_fb_create, | ||
| 108 | .atomic_check = drm_atomic_helper_check, | ||
| 109 | .atomic_commit = mtk_atomic_commit, | ||
| 110 | }; | ||
| 111 | |||
| 112 | static const enum mtk_ddp_comp_id mtk_ddp_main[] = { | ||
| 113 | DDP_COMPONENT_OVL0, | ||
| 114 | DDP_COMPONENT_COLOR0, | ||
| 115 | DDP_COMPONENT_AAL, | ||
| 116 | DDP_COMPONENT_OD, | ||
| 117 | DDP_COMPONENT_RDMA0, | ||
| 118 | DDP_COMPONENT_UFOE, | ||
| 119 | DDP_COMPONENT_DSI0, | ||
| 120 | DDP_COMPONENT_PWM0, | ||
| 121 | }; | ||
| 122 | |||
| 123 | static const enum mtk_ddp_comp_id mtk_ddp_ext[] = { | ||
| 124 | DDP_COMPONENT_OVL1, | ||
| 125 | DDP_COMPONENT_COLOR1, | ||
| 126 | DDP_COMPONENT_GAMMA, | ||
| 127 | DDP_COMPONENT_RDMA1, | ||
| 128 | DDP_COMPONENT_DPI0, | ||
| 129 | }; | ||
| 130 | |||
| 131 | static int mtk_drm_kms_init(struct drm_device *drm) | ||
| 132 | { | ||
| 133 | struct mtk_drm_private *private = drm->dev_private; | ||
| 134 | struct platform_device *pdev; | ||
| 135 | struct device_node *np; | ||
| 136 | int ret; | ||
| 137 | |||
| 138 | if (!iommu_present(&platform_bus_type)) | ||
| 139 | return -EPROBE_DEFER; | ||
| 140 | |||
| 141 | pdev = of_find_device_by_node(private->mutex_node); | ||
| 142 | if (!pdev) { | ||
| 143 | dev_err(drm->dev, "Waiting for disp-mutex device %s\n", | ||
| 144 | private->mutex_node->full_name); | ||
| 145 | of_node_put(private->mutex_node); | ||
| 146 | return -EPROBE_DEFER; | ||
| 147 | } | ||
| 148 | private->mutex_dev = &pdev->dev; | ||
| 149 | |||
| 150 | drm_mode_config_init(drm); | ||
| 151 | |||
| 152 | drm->mode_config.min_width = 64; | ||
| 153 | drm->mode_config.min_height = 64; | ||
| 154 | |||
| 155 | /* | ||
| 156 | * set max width and height as default value(4096x4096). | ||
| 157 | * this value would be used to check framebuffer size limitation | ||
| 158 | * at drm_mode_addfb(). | ||
| 159 | */ | ||
| 160 | drm->mode_config.max_width = 4096; | ||
| 161 | drm->mode_config.max_height = 4096; | ||
| 162 | drm->mode_config.funcs = &mtk_drm_mode_config_funcs; | ||
| 163 | |||
| 164 | ret = component_bind_all(drm->dev, drm); | ||
| 165 | if (ret) | ||
| 166 | goto err_config_cleanup; | ||
| 167 | |||
| 168 | /* | ||
| 169 | * We currently support two fixed data streams, each optional, | ||
| 170 | * and each statically assigned to a crtc: | ||
| 171 | * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ... | ||
| 172 | */ | ||
| 173 | ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main)); | ||
| 174 | if (ret < 0) | ||
| 175 | goto err_component_unbind; | ||
| 176 | /* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */ | ||
| 177 | ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext)); | ||
| 178 | if (ret < 0) | ||
| 179 | goto err_component_unbind; | ||
| 180 | |||
| 181 | /* Use OVL device for all DMA memory allocations */ | ||
| 182 | np = private->comp_node[mtk_ddp_main[0]] ?: | ||
| 183 | private->comp_node[mtk_ddp_ext[0]]; | ||
| 184 | pdev = of_find_device_by_node(np); | ||
| 185 | if (!pdev) { | ||
| 186 | ret = -ENODEV; | ||
| 187 | dev_err(drm->dev, "Need at least one OVL device\n"); | ||
| 188 | goto err_component_unbind; | ||
| 189 | } | ||
| 190 | |||
| 191 | private->dma_dev = &pdev->dev; | ||
| 192 | |||
| 193 | /* | ||
| 194 | * We don't use the drm_irq_install() helpers provided by the DRM | ||
| 195 | * core, so we need to set this manually in order to allow the | ||
| 196 | * DRM_IOCTL_WAIT_VBLANK to operate correctly. | ||
| 197 | */ | ||
| 198 | drm->irq_enabled = true; | ||
| 199 | ret = drm_vblank_init(drm, MAX_CRTC); | ||
| 200 | if (ret < 0) | ||
| 201 | goto err_component_unbind; | ||
| 202 | |||
| 203 | drm_kms_helper_poll_init(drm); | ||
| 204 | drm_mode_config_reset(drm); | ||
| 205 | |||
| 206 | return 0; | ||
| 207 | |||
| 208 | err_component_unbind: | ||
| 209 | component_unbind_all(drm->dev, drm); | ||
| 210 | err_config_cleanup: | ||
| 211 | drm_mode_config_cleanup(drm); | ||
| 212 | |||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | |||
| 216 | static void mtk_drm_kms_deinit(struct drm_device *drm) | ||
| 217 | { | ||
| 218 | drm_kms_helper_poll_fini(drm); | ||
| 219 | |||
| 220 | drm_vblank_cleanup(drm); | ||
| 221 | component_unbind_all(drm->dev, drm); | ||
| 222 | drm_mode_config_cleanup(drm); | ||
| 223 | } | ||
| 224 | |||
| 225 | static const struct file_operations mtk_drm_fops = { | ||
| 226 | .owner = THIS_MODULE, | ||
| 227 | .open = drm_open, | ||
| 228 | .release = drm_release, | ||
| 229 | .unlocked_ioctl = drm_ioctl, | ||
| 230 | .mmap = mtk_drm_gem_mmap, | ||
| 231 | .poll = drm_poll, | ||
| 232 | .read = drm_read, | ||
| 233 | #ifdef CONFIG_COMPAT | ||
| 234 | .compat_ioctl = drm_compat_ioctl, | ||
| 235 | #endif | ||
| 236 | }; | ||
| 237 | |||
| 238 | static struct drm_driver mtk_drm_driver = { | ||
| 239 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | | ||
| 240 | DRIVER_ATOMIC, | ||
| 241 | |||
| 242 | .get_vblank_counter = drm_vblank_count, | ||
| 243 | .enable_vblank = mtk_drm_crtc_enable_vblank, | ||
| 244 | .disable_vblank = mtk_drm_crtc_disable_vblank, | ||
| 245 | |||
| 246 | .gem_free_object = mtk_drm_gem_free_object, | ||
| 247 | .gem_vm_ops = &drm_gem_cma_vm_ops, | ||
| 248 | .dumb_create = mtk_drm_gem_dumb_create, | ||
| 249 | .dumb_map_offset = mtk_drm_gem_dumb_map_offset, | ||
| 250 | .dumb_destroy = drm_gem_dumb_destroy, | ||
| 251 | |||
| 252 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
| 253 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
| 254 | .gem_prime_export = drm_gem_prime_export, | ||
| 255 | .gem_prime_import = drm_gem_prime_import, | ||
| 256 | .gem_prime_get_sg_table = mtk_gem_prime_get_sg_table, | ||
| 257 | .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table, | ||
| 258 | .gem_prime_mmap = mtk_drm_gem_mmap_buf, | ||
| 259 | .fops = &mtk_drm_fops, | ||
| 260 | |||
| 261 | .name = DRIVER_NAME, | ||
| 262 | .desc = DRIVER_DESC, | ||
| 263 | .date = DRIVER_DATE, | ||
| 264 | .major = DRIVER_MAJOR, | ||
| 265 | .minor = DRIVER_MINOR, | ||
| 266 | }; | ||
| 267 | |||
| 268 | static int compare_of(struct device *dev, void *data) | ||
| 269 | { | ||
| 270 | return dev->of_node == data; | ||
| 271 | } | ||
| 272 | |||
| 273 | static int mtk_drm_bind(struct device *dev) | ||
| 274 | { | ||
| 275 | struct mtk_drm_private *private = dev_get_drvdata(dev); | ||
| 276 | struct drm_device *drm; | ||
| 277 | int ret; | ||
| 278 | |||
| 279 | drm = drm_dev_alloc(&mtk_drm_driver, dev); | ||
| 280 | if (!drm) | ||
| 281 | return -ENOMEM; | ||
| 282 | |||
| 283 | drm_dev_set_unique(drm, dev_name(dev)); | ||
| 284 | |||
| 285 | drm->dev_private = private; | ||
| 286 | private->drm = drm; | ||
| 287 | |||
| 288 | ret = mtk_drm_kms_init(drm); | ||
| 289 | if (ret < 0) | ||
| 290 | goto err_free; | ||
| 291 | |||
| 292 | ret = drm_dev_register(drm, 0); | ||
| 293 | if (ret < 0) | ||
| 294 | goto err_deinit; | ||
| 295 | |||
| 296 | ret = drm_connector_register_all(drm); | ||
| 297 | if (ret < 0) | ||
| 298 | goto err_unregister; | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | |||
| 302 | err_unregister: | ||
| 303 | drm_dev_unregister(drm); | ||
| 304 | err_deinit: | ||
| 305 | mtk_drm_kms_deinit(drm); | ||
| 306 | err_free: | ||
| 307 | drm_dev_unref(drm); | ||
| 308 | return ret; | ||
| 309 | } | ||
| 310 | |||
| 311 | static void mtk_drm_unbind(struct device *dev) | ||
| 312 | { | ||
| 313 | struct mtk_drm_private *private = dev_get_drvdata(dev); | ||
| 314 | |||
| 315 | drm_put_dev(private->drm); | ||
| 316 | private->drm = NULL; | ||
| 317 | } | ||
| 318 | |||
| 319 | static const struct component_master_ops mtk_drm_ops = { | ||
| 320 | .bind = mtk_drm_bind, | ||
| 321 | .unbind = mtk_drm_unbind, | ||
| 322 | }; | ||
| 323 | |||
| 324 | static const struct of_device_id mtk_ddp_comp_dt_ids[] = { | ||
| 325 | { .compatible = "mediatek,mt8173-disp-ovl", .data = (void *)MTK_DISP_OVL }, | ||
| 326 | { .compatible = "mediatek,mt8173-disp-rdma", .data = (void *)MTK_DISP_RDMA }, | ||
| 327 | { .compatible = "mediatek,mt8173-disp-wdma", .data = (void *)MTK_DISP_WDMA }, | ||
| 328 | { .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR }, | ||
| 329 | { .compatible = "mediatek,mt8173-disp-aal", .data = (void *)MTK_DISP_AAL}, | ||
| 330 | { .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, }, | ||
| 331 | { .compatible = "mediatek,mt8173-disp-ufoe", .data = (void *)MTK_DISP_UFOE }, | ||
| 332 | { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI }, | ||
| 333 | { .compatible = "mediatek,mt8173-dpi", .data = (void *)MTK_DPI }, | ||
| 334 | { .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX }, | ||
| 335 | { .compatible = "mediatek,mt8173-disp-pwm", .data = (void *)MTK_DISP_PWM }, | ||
| 336 | { .compatible = "mediatek,mt8173-disp-od", .data = (void *)MTK_DISP_OD }, | ||
| 337 | { } | ||
| 338 | }; | ||
| 339 | |||
| 340 | static int mtk_drm_probe(struct platform_device *pdev) | ||
| 341 | { | ||
| 342 | struct device *dev = &pdev->dev; | ||
| 343 | struct mtk_drm_private *private; | ||
| 344 | struct resource *mem; | ||
| 345 | struct device_node *node; | ||
| 346 | struct component_match *match = NULL; | ||
| 347 | int ret; | ||
| 348 | int i; | ||
| 349 | |||
| 350 | private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL); | ||
| 351 | if (!private) | ||
| 352 | return -ENOMEM; | ||
| 353 | |||
| 354 | mutex_init(&private->commit.lock); | ||
| 355 | INIT_WORK(&private->commit.work, mtk_atomic_work); | ||
| 356 | |||
| 357 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 358 | private->config_regs = devm_ioremap_resource(dev, mem); | ||
| 359 | if (IS_ERR(private->config_regs)) { | ||
| 360 | ret = PTR_ERR(private->config_regs); | ||
| 361 | dev_err(dev, "Failed to ioremap mmsys-config resource: %d\n", | ||
| 362 | ret); | ||
| 363 | return ret; | ||
| 364 | } | ||
| 365 | |||
| 366 | /* Iterate over sibling DISP function blocks */ | ||
| 367 | for_each_child_of_node(dev->of_node->parent, node) { | ||
| 368 | const struct of_device_id *of_id; | ||
| 369 | enum mtk_ddp_comp_type comp_type; | ||
| 370 | int comp_id; | ||
| 371 | |||
| 372 | of_id = of_match_node(mtk_ddp_comp_dt_ids, node); | ||
| 373 | if (!of_id) | ||
| 374 | continue; | ||
| 375 | |||
| 376 | if (!of_device_is_available(node)) { | ||
| 377 | dev_dbg(dev, "Skipping disabled component %s\n", | ||
| 378 | node->full_name); | ||
| 379 | continue; | ||
| 380 | } | ||
| 381 | |||
| 382 | comp_type = (enum mtk_ddp_comp_type)of_id->data; | ||
| 383 | |||
| 384 | if (comp_type == MTK_DISP_MUTEX) { | ||
| 385 | private->mutex_node = of_node_get(node); | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | |||
| 389 | comp_id = mtk_ddp_comp_get_id(node, comp_type); | ||
| 390 | if (comp_id < 0) { | ||
| 391 | dev_warn(dev, "Skipping unknown component %s\n", | ||
| 392 | node->full_name); | ||
| 393 | continue; | ||
| 394 | } | ||
| 395 | |||
| 396 | private->comp_node[comp_id] = of_node_get(node); | ||
| 397 | |||
| 398 | /* | ||
| 399 | * Currently only the OVL, RDMA, DSI, and DPI blocks have | ||
| 400 | * separate component platform drivers and initialize their own | ||
| 401 | * DDP component structure. The others are initialized here. | ||
| 402 | */ | ||
| 403 | if (comp_type == MTK_DISP_OVL || | ||
| 404 | comp_type == MTK_DISP_RDMA || | ||
| 405 | comp_type == MTK_DSI || | ||
| 406 | comp_type == MTK_DPI) { | ||
| 407 | dev_info(dev, "Adding component match for %s\n", | ||
| 408 | node->full_name); | ||
| 409 | component_match_add(dev, &match, compare_of, node); | ||
| 410 | } else { | ||
| 411 | struct mtk_ddp_comp *comp; | ||
| 412 | |||
| 413 | comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); | ||
| 414 | if (!comp) { | ||
| 415 | ret = -ENOMEM; | ||
| 416 | goto err_node; | ||
| 417 | } | ||
| 418 | |||
| 419 | ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL); | ||
| 420 | if (ret) | ||
| 421 | goto err_node; | ||
| 422 | |||
| 423 | private->ddp_comp[comp_id] = comp; | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | if (!private->mutex_node) { | ||
| 428 | dev_err(dev, "Failed to find disp-mutex node\n"); | ||
| 429 | ret = -ENODEV; | ||
| 430 | goto err_node; | ||
| 431 | } | ||
| 432 | |||
| 433 | pm_runtime_enable(dev); | ||
| 434 | |||
| 435 | platform_set_drvdata(pdev, private); | ||
| 436 | |||
| 437 | ret = component_master_add_with_match(dev, &mtk_drm_ops, match); | ||
| 438 | if (ret) | ||
| 439 | goto err_pm; | ||
| 440 | |||
| 441 | return 0; | ||
| 442 | |||
| 443 | err_pm: | ||
| 444 | pm_runtime_disable(dev); | ||
| 445 | err_node: | ||
| 446 | of_node_put(private->mutex_node); | ||
| 447 | for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) | ||
| 448 | of_node_put(private->comp_node[i]); | ||
| 449 | return ret; | ||
| 450 | } | ||
| 451 | |||
| 452 | static int mtk_drm_remove(struct platform_device *pdev) | ||
| 453 | { | ||
| 454 | struct mtk_drm_private *private = platform_get_drvdata(pdev); | ||
| 455 | struct drm_device *drm = private->drm; | ||
| 456 | int i; | ||
| 457 | |||
| 458 | drm_connector_unregister_all(drm); | ||
| 459 | drm_dev_unregister(drm); | ||
| 460 | mtk_drm_kms_deinit(drm); | ||
| 461 | drm_dev_unref(drm); | ||
| 462 | |||
| 463 | component_master_del(&pdev->dev, &mtk_drm_ops); | ||
| 464 | pm_runtime_disable(&pdev->dev); | ||
| 465 | of_node_put(private->mutex_node); | ||
| 466 | for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) | ||
| 467 | of_node_put(private->comp_node[i]); | ||
| 468 | |||
| 469 | return 0; | ||
| 470 | } | ||
| 471 | |||
| 472 | #ifdef CONFIG_PM_SLEEP | ||
| 473 | static int mtk_drm_sys_suspend(struct device *dev) | ||
| 474 | { | ||
| 475 | struct mtk_drm_private *private = dev_get_drvdata(dev); | ||
| 476 | struct drm_device *drm = private->drm; | ||
| 477 | |||
| 478 | drm_kms_helper_poll_disable(drm); | ||
| 479 | |||
| 480 | private->suspend_state = drm_atomic_helper_suspend(drm); | ||
| 481 | if (IS_ERR(private->suspend_state)) { | ||
| 482 | drm_kms_helper_poll_enable(drm); | ||
| 483 | return PTR_ERR(private->suspend_state); | ||
| 484 | } | ||
| 485 | |||
| 486 | DRM_DEBUG_DRIVER("mtk_drm_sys_suspend\n"); | ||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | |||
| 490 | static int mtk_drm_sys_resume(struct device *dev) | ||
| 491 | { | ||
| 492 | struct mtk_drm_private *private = dev_get_drvdata(dev); | ||
| 493 | struct drm_device *drm = private->drm; | ||
| 494 | |||
| 495 | drm_atomic_helper_resume(drm, private->suspend_state); | ||
| 496 | drm_kms_helper_poll_enable(drm); | ||
| 497 | |||
| 498 | DRM_DEBUG_DRIVER("mtk_drm_sys_resume\n"); | ||
| 499 | return 0; | ||
| 500 | } | ||
| 501 | #endif | ||
| 502 | |||
| 503 | static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend, | ||
| 504 | mtk_drm_sys_resume); | ||
| 505 | |||
| 506 | static const struct of_device_id mtk_drm_of_ids[] = { | ||
| 507 | { .compatible = "mediatek,mt8173-mmsys", }, | ||
| 508 | { } | ||
| 509 | }; | ||
| 510 | |||
| 511 | static struct platform_driver mtk_drm_platform_driver = { | ||
| 512 | .probe = mtk_drm_probe, | ||
| 513 | .remove = mtk_drm_remove, | ||
| 514 | .driver = { | ||
| 515 | .name = "mediatek-drm", | ||
| 516 | .of_match_table = mtk_drm_of_ids, | ||
| 517 | .pm = &mtk_drm_pm_ops, | ||
| 518 | }, | ||
| 519 | }; | ||
| 520 | |||
| 521 | static struct platform_driver * const mtk_drm_drivers[] = { | ||
| 522 | &mtk_ddp_driver, | ||
| 523 | &mtk_disp_ovl_driver, | ||
| 524 | &mtk_disp_rdma_driver, | ||
| 525 | &mtk_dpi_driver, | ||
| 526 | &mtk_drm_platform_driver, | ||
| 527 | &mtk_dsi_driver, | ||
| 528 | &mtk_mipi_tx_driver, | ||
| 529 | }; | ||
| 530 | |||
| 531 | static int __init mtk_drm_init(void) | ||
| 532 | { | ||
| 533 | int ret; | ||
| 534 | int i; | ||
| 535 | |||
| 536 | for (i = 0; i < ARRAY_SIZE(mtk_drm_drivers); i++) { | ||
| 537 | ret = platform_driver_register(mtk_drm_drivers[i]); | ||
| 538 | if (ret < 0) { | ||
| 539 | pr_err("Failed to register %s driver: %d\n", | ||
| 540 | mtk_drm_drivers[i]->driver.name, ret); | ||
| 541 | goto err; | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | return 0; | ||
| 546 | |||
| 547 | err: | ||
| 548 | while (--i >= 0) | ||
| 549 | platform_driver_unregister(mtk_drm_drivers[i]); | ||
| 550 | |||
| 551 | return ret; | ||
| 552 | } | ||
| 553 | |||
| 554 | static void __exit mtk_drm_exit(void) | ||
| 555 | { | ||
| 556 | int i; | ||
| 557 | |||
| 558 | for (i = ARRAY_SIZE(mtk_drm_drivers) - 1; i >= 0; i--) | ||
| 559 | platform_driver_unregister(mtk_drm_drivers[i]); | ||
| 560 | } | ||
| 561 | |||
| 562 | module_init(mtk_drm_init); | ||
| 563 | module_exit(mtk_drm_exit); | ||
| 564 | |||
| 565 | MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>"); | ||
| 566 | MODULE_DESCRIPTION("Mediatek SoC DRM driver"); | ||
| 567 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h new file mode 100644 index 000000000000..aa9389446785 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef MTK_DRM_DRV_H | ||
| 15 | #define MTK_DRM_DRV_H | ||
| 16 | |||
| 17 | #include <linux/io.h> | ||
| 18 | #include "mtk_drm_ddp_comp.h" | ||
| 19 | |||
| 20 | #define MAX_CRTC 2 | ||
| 21 | #define MAX_CONNECTOR 2 | ||
| 22 | |||
| 23 | struct device; | ||
| 24 | struct device_node; | ||
| 25 | struct drm_crtc; | ||
| 26 | struct drm_device; | ||
| 27 | struct drm_fb_helper; | ||
| 28 | struct drm_property; | ||
| 29 | struct regmap; | ||
| 30 | |||
| 31 | struct mtk_drm_private { | ||
| 32 | struct drm_device *drm; | ||
| 33 | struct device *dma_dev; | ||
| 34 | |||
| 35 | struct drm_crtc *crtc[MAX_CRTC]; | ||
| 36 | unsigned int num_pipes; | ||
| 37 | |||
| 38 | struct device_node *mutex_node; | ||
| 39 | struct device *mutex_dev; | ||
| 40 | void __iomem *config_regs; | ||
| 41 | struct device_node *comp_node[DDP_COMPONENT_ID_MAX]; | ||
| 42 | struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX]; | ||
| 43 | |||
| 44 | struct { | ||
| 45 | struct drm_atomic_state *state; | ||
| 46 | struct work_struct work; | ||
| 47 | struct mutex lock; | ||
| 48 | } commit; | ||
| 49 | |||
| 50 | struct drm_atomic_state *suspend_state; | ||
| 51 | }; | ||
| 52 | |||
| 53 | extern struct platform_driver mtk_ddp_driver; | ||
| 54 | extern struct platform_driver mtk_disp_ovl_driver; | ||
| 55 | extern struct platform_driver mtk_disp_rdma_driver; | ||
| 56 | extern struct platform_driver mtk_dpi_driver; | ||
| 57 | extern struct platform_driver mtk_dsi_driver; | ||
| 58 | extern struct platform_driver mtk_mipi_tx_driver; | ||
| 59 | |||
| 60 | #endif /* MTK_DRM_DRV_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.c b/drivers/gpu/drm/mediatek/mtk_drm_fb.c new file mode 100644 index 000000000000..33d30c19f35f --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.c | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <drm/drm_crtc_helper.h> | ||
| 16 | #include <drm/drm_fb_helper.h> | ||
| 17 | #include <drm/drm_gem.h> | ||
| 18 | #include <linux/dma-buf.h> | ||
| 19 | #include <linux/reservation.h> | ||
| 20 | |||
| 21 | #include "mtk_drm_drv.h" | ||
| 22 | #include "mtk_drm_fb.h" | ||
| 23 | #include "mtk_drm_gem.h" | ||
| 24 | |||
| 25 | /* | ||
| 26 | * mtk specific framebuffer structure. | ||
| 27 | * | ||
| 28 | * @fb: drm framebuffer object. | ||
| 29 | * @gem_obj: array of gem objects. | ||
| 30 | */ | ||
| 31 | struct mtk_drm_fb { | ||
| 32 | struct drm_framebuffer base; | ||
| 33 | /* For now we only support a single plane */ | ||
| 34 | struct drm_gem_object *gem_obj; | ||
| 35 | }; | ||
| 36 | |||
| 37 | #define to_mtk_fb(x) container_of(x, struct mtk_drm_fb, base) | ||
| 38 | |||
| 39 | struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb) | ||
| 40 | { | ||
| 41 | struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); | ||
| 42 | |||
| 43 | return mtk_fb->gem_obj; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int mtk_drm_fb_create_handle(struct drm_framebuffer *fb, | ||
| 47 | struct drm_file *file_priv, | ||
| 48 | unsigned int *handle) | ||
| 49 | { | ||
| 50 | struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); | ||
| 51 | |||
| 52 | return drm_gem_handle_create(file_priv, mtk_fb->gem_obj, handle); | ||
| 53 | } | ||
| 54 | |||
| 55 | static void mtk_drm_fb_destroy(struct drm_framebuffer *fb) | ||
| 56 | { | ||
| 57 | struct mtk_drm_fb *mtk_fb = to_mtk_fb(fb); | ||
| 58 | |||
| 59 | drm_framebuffer_cleanup(fb); | ||
| 60 | |||
| 61 | drm_gem_object_unreference_unlocked(mtk_fb->gem_obj); | ||
| 62 | |||
| 63 | kfree(mtk_fb); | ||
| 64 | } | ||
| 65 | |||
| 66 | static const struct drm_framebuffer_funcs mtk_drm_fb_funcs = { | ||
| 67 | .create_handle = mtk_drm_fb_create_handle, | ||
| 68 | .destroy = mtk_drm_fb_destroy, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static struct mtk_drm_fb *mtk_drm_framebuffer_init(struct drm_device *dev, | ||
| 72 | const struct drm_mode_fb_cmd2 *mode, | ||
| 73 | struct drm_gem_object *obj) | ||
| 74 | { | ||
| 75 | struct mtk_drm_fb *mtk_fb; | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | if (drm_format_num_planes(mode->pixel_format) != 1) | ||
| 79 | return ERR_PTR(-EINVAL); | ||
| 80 | |||
| 81 | mtk_fb = kzalloc(sizeof(*mtk_fb), GFP_KERNEL); | ||
| 82 | if (!mtk_fb) | ||
| 83 | return ERR_PTR(-ENOMEM); | ||
| 84 | |||
| 85 | drm_helper_mode_fill_fb_struct(&mtk_fb->base, mode); | ||
| 86 | |||
| 87 | mtk_fb->gem_obj = obj; | ||
| 88 | |||
| 89 | ret = drm_framebuffer_init(dev, &mtk_fb->base, &mtk_drm_fb_funcs); | ||
| 90 | if (ret) { | ||
| 91 | DRM_ERROR("failed to initialize framebuffer\n"); | ||
| 92 | kfree(mtk_fb); | ||
| 93 | return ERR_PTR(ret); | ||
| 94 | } | ||
| 95 | |||
| 96 | return mtk_fb; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Wait for any exclusive fence in fb's gem object's reservation object. | ||
| 101 | * | ||
| 102 | * Returns -ERESTARTSYS if interrupted, else 0. | ||
| 103 | */ | ||
| 104 | int mtk_fb_wait(struct drm_framebuffer *fb) | ||
| 105 | { | ||
| 106 | struct drm_gem_object *gem; | ||
| 107 | struct reservation_object *resv; | ||
| 108 | long ret; | ||
| 109 | |||
| 110 | if (!fb) | ||
| 111 | return 0; | ||
| 112 | |||
| 113 | gem = mtk_fb_get_gem_obj(fb); | ||
| 114 | if (!gem || !gem->dma_buf || !gem->dma_buf->resv) | ||
| 115 | return 0; | ||
| 116 | |||
| 117 | resv = gem->dma_buf->resv; | ||
| 118 | ret = reservation_object_wait_timeout_rcu(resv, false, true, | ||
| 119 | MAX_SCHEDULE_TIMEOUT); | ||
| 120 | /* MAX_SCHEDULE_TIMEOUT on success, -ERESTARTSYS if interrupted */ | ||
| 121 | if (WARN_ON(ret < 0)) | ||
| 122 | return ret; | ||
| 123 | |||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | |||
| 127 | struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, | ||
| 128 | struct drm_file *file, | ||
| 129 | const struct drm_mode_fb_cmd2 *cmd) | ||
| 130 | { | ||
| 131 | struct mtk_drm_fb *mtk_fb; | ||
| 132 | struct drm_gem_object *gem; | ||
| 133 | unsigned int width = cmd->width; | ||
| 134 | unsigned int height = cmd->height; | ||
| 135 | unsigned int size, bpp; | ||
| 136 | int ret; | ||
| 137 | |||
| 138 | if (drm_format_num_planes(cmd->pixel_format) != 1) | ||
| 139 | return ERR_PTR(-EINVAL); | ||
| 140 | |||
| 141 | gem = drm_gem_object_lookup(dev, file, cmd->handles[0]); | ||
| 142 | if (!gem) | ||
| 143 | return ERR_PTR(-ENOENT); | ||
| 144 | |||
| 145 | bpp = drm_format_plane_cpp(cmd->pixel_format, 0); | ||
| 146 | size = (height - 1) * cmd->pitches[0] + width * bpp; | ||
| 147 | size += cmd->offsets[0]; | ||
| 148 | |||
| 149 | if (gem->size < size) { | ||
| 150 | ret = -EINVAL; | ||
| 151 | goto unreference; | ||
| 152 | } | ||
| 153 | |||
| 154 | mtk_fb = mtk_drm_framebuffer_init(dev, cmd, gem); | ||
| 155 | if (IS_ERR(mtk_fb)) { | ||
| 156 | ret = PTR_ERR(mtk_fb); | ||
| 157 | goto unreference; | ||
| 158 | } | ||
| 159 | |||
| 160 | return &mtk_fb->base; | ||
| 161 | |||
| 162 | unreference: | ||
| 163 | drm_gem_object_unreference_unlocked(gem); | ||
| 164 | return ERR_PTR(ret); | ||
| 165 | } | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_fb.h b/drivers/gpu/drm/mediatek/mtk_drm_fb.h new file mode 100644 index 000000000000..9b2ae345a4e9 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_fb.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef MTK_DRM_FB_H | ||
| 15 | #define MTK_DRM_FB_H | ||
| 16 | |||
| 17 | struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb); | ||
| 18 | int mtk_fb_wait(struct drm_framebuffer *fb); | ||
| 19 | struct drm_framebuffer *mtk_drm_mode_fb_create(struct drm_device *dev, | ||
| 20 | struct drm_file *file, | ||
| 21 | const struct drm_mode_fb_cmd2 *cmd); | ||
| 22 | |||
| 23 | #endif /* MTK_DRM_FB_H */ | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c new file mode 100644 index 000000000000..a773bfaea913 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <drm/drm_gem.h> | ||
| 16 | #include <linux/dma-buf.h> | ||
| 17 | |||
| 18 | #include "mtk_drm_drv.h" | ||
| 19 | #include "mtk_drm_gem.h" | ||
| 20 | |||
| 21 | static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev, | ||
| 22 | unsigned long size) | ||
| 23 | { | ||
| 24 | struct mtk_drm_gem_obj *mtk_gem_obj; | ||
| 25 | int ret; | ||
| 26 | |||
| 27 | size = round_up(size, PAGE_SIZE); | ||
| 28 | |||
| 29 | mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL); | ||
| 30 | if (!mtk_gem_obj) | ||
| 31 | return ERR_PTR(-ENOMEM); | ||
| 32 | |||
| 33 | ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size); | ||
| 34 | if (ret < 0) { | ||
| 35 | DRM_ERROR("failed to initialize gem object\n"); | ||
| 36 | kfree(mtk_gem_obj); | ||
| 37 | return ERR_PTR(ret); | ||
| 38 | } | ||
| 39 | |||
| 40 | return mtk_gem_obj; | ||
| 41 | } | ||
| 42 | |||
| 43 | struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, | ||
| 44 | size_t size, bool alloc_kmap) | ||
| 45 | { | ||
| 46 | struct mtk_drm_private *priv = dev->dev_private; | ||
| 47 | struct mtk_drm_gem_obj *mtk_gem; | ||
| 48 | struct drm_gem_object *obj; | ||
| 49 | int ret; | ||
| 50 | |||
| 51 | mtk_gem = mtk_drm_gem_init(dev, size); | ||
| 52 | if (IS_ERR(mtk_gem)) | ||
| 53 | return ERR_CAST(mtk_gem); | ||
| 54 | |||
| 55 | obj = &mtk_gem->base; | ||
| 56 | |||
| 57 | init_dma_attrs(&mtk_gem->dma_attrs); | ||
| 58 | dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs); | ||
| 59 | |||
| 60 | if (!alloc_kmap) | ||
| 61 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs); | ||
| 62 | |||
| 63 | mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size, | ||
| 64 | &mtk_gem->dma_addr, GFP_KERNEL, | ||
| 65 | &mtk_gem->dma_attrs); | ||
| 66 | if (!mtk_gem->cookie) { | ||
| 67 | DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size); | ||
| 68 | ret = -ENOMEM; | ||
| 69 | goto err_gem_free; | ||
| 70 | } | ||
| 71 | |||
| 72 | if (alloc_kmap) | ||
| 73 | mtk_gem->kvaddr = mtk_gem->cookie; | ||
| 74 | |||
| 75 | DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n", | ||
| 76 | mtk_gem->cookie, &mtk_gem->dma_addr, | ||
| 77 | size); | ||
| 78 | |||
| 79 | return mtk_gem; | ||
| 80 | |||
| 81 | err_gem_free: | ||
| 82 | drm_gem_object_release(obj); | ||
| 83 | kfree(mtk_gem); | ||
| 84 | return ERR_PTR(ret); | ||
| 85 | } | ||
| 86 | |||
| 87 | void mtk_drm_gem_free_object(struct drm_gem_object *obj) | ||
| 88 | { | ||
| 89 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | ||
| 90 | struct mtk_drm_private *priv = obj->dev->dev_private; | ||
| 91 | |||
| 92 | if (mtk_gem->sg) | ||
| 93 | drm_prime_gem_destroy(obj, mtk_gem->sg); | ||
| 94 | else | ||
| 95 | dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie, | ||
| 96 | mtk_gem->dma_addr, &mtk_gem->dma_attrs); | ||
| 97 | |||
| 98 | /* release file pointer to gem object. */ | ||
| 99 | drm_gem_object_release(obj); | ||
| 100 | |||
| 101 | kfree(mtk_gem); | ||
| 102 | } | ||
| 103 | |||
| 104 | int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, | ||
| 105 | struct drm_mode_create_dumb *args) | ||
| 106 | { | ||
| 107 | struct mtk_drm_gem_obj *mtk_gem; | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); | ||
| 111 | args->size = args->pitch * args->height; | ||
| 112 | |||
| 113 | mtk_gem = mtk_drm_gem_create(dev, args->size, false); | ||
| 114 | if (IS_ERR(mtk_gem)) | ||
| 115 | return PTR_ERR(mtk_gem); | ||
| 116 | |||
| 117 | /* | ||
| 118 | * allocate a id of idr table where the obj is registered | ||
| 119 | * and handle has the id what user can see. | ||
| 120 | */ | ||
| 121 | ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle); | ||
| 122 | if (ret) | ||
| 123 | goto err_handle_create; | ||
| 124 | |||
| 125 | /* drop reference from allocate - handle holds it now. */ | ||
| 126 | drm_gem_object_unreference_unlocked(&mtk_gem->base); | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | |||
| 130 | err_handle_create: | ||
| 131 | mtk_drm_gem_free_object(&mtk_gem->base); | ||
| 132 | return ret; | ||
| 133 | } | ||
| 134 | |||
| 135 | int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv, | ||
| 136 | struct drm_device *dev, uint32_t handle, | ||
| 137 | uint64_t *offset) | ||
| 138 | { | ||
| 139 | struct drm_gem_object *obj; | ||
| 140 | int ret; | ||
| 141 | |||
| 142 | obj = drm_gem_object_lookup(dev, file_priv, handle); | ||
| 143 | if (!obj) { | ||
| 144 | DRM_ERROR("failed to lookup gem object.\n"); | ||
| 145 | return -EINVAL; | ||
| 146 | } | ||
| 147 | |||
| 148 | ret = drm_gem_create_mmap_offset(obj); | ||
| 149 | if (ret) | ||
| 150 | goto out; | ||
| 151 | |||
| 152 | *offset = drm_vma_node_offset_addr(&obj->vma_node); | ||
| 153 | DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); | ||
| 154 | |||
| 155 | out: | ||
| 156 | drm_gem_object_unreference_unlocked(obj); | ||
| 157 | return ret; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj, | ||
| 161 | struct vm_area_struct *vma) | ||
| 162 | |||
| 163 | { | ||
| 164 | int ret; | ||
| 165 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | ||
| 166 | struct mtk_drm_private *priv = obj->dev->dev_private; | ||
| 167 | |||
| 168 | /* | ||
| 169 | * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear | ||
| 170 | * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). | ||
| 171 | */ | ||
| 172 | vma->vm_flags &= ~VM_PFNMAP; | ||
| 173 | vma->vm_pgoff = 0; | ||
| 174 | |||
| 175 | ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie, | ||
| 176 | mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs); | ||
| 177 | if (ret) | ||
| 178 | drm_gem_vm_close(vma); | ||
| 179 | |||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma) | ||
| 184 | { | ||
| 185 | int ret; | ||
| 186 | |||
| 187 | ret = drm_gem_mmap_obj(obj, obj->size, vma); | ||
| 188 | if (ret) | ||
| 189 | return ret; | ||
| 190 | |||
| 191 | return mtk_drm_gem_object_mmap(obj, vma); | ||
| 192 | } | ||
| 193 | |||
| 194 | int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | ||
| 195 | { | ||
| 196 | struct drm_gem_object *obj; | ||
| 197 | int ret; | ||
| 198 | |||
| 199 | ret = drm_gem_mmap(filp, vma); | ||
| 200 | if (ret) | ||
| 201 | return ret; | ||
| 202 | |||
| 203 | obj = vma->vm_private_data; | ||
| 204 | |||
| 205 | return mtk_drm_gem_object_mmap(obj, vma); | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 209 | * Allocate a sg_table for this GEM object. | ||
| 210 | * Note: Both the table's contents, and the sg_table itself must be freed by | ||
| 211 | * the caller. | ||
| 212 | * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. | ||
| 213 | */ | ||
| 214 | struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj) | ||
| 215 | { | ||
| 216 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | ||
| 217 | struct mtk_drm_private *priv = obj->dev->dev_private; | ||
| 218 | struct sg_table *sgt; | ||
| 219 | int ret; | ||
| 220 | |||
| 221 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | ||
| 222 | if (!sgt) | ||
| 223 | return ERR_PTR(-ENOMEM); | ||
| 224 | |||
| 225 | ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie, | ||
| 226 | mtk_gem->dma_addr, obj->size, | ||
| 227 | &mtk_gem->dma_attrs); | ||
| 228 | if (ret) { | ||
| 229 | DRM_ERROR("failed to allocate sgt, %d\n", ret); | ||
| 230 | kfree(sgt); | ||
| 231 | return ERR_PTR(ret); | ||
| 232 | } | ||
| 233 | |||
| 234 | return sgt; | ||
| 235 | } | ||
| 236 | |||
| 237 | struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev, | ||
| 238 | struct dma_buf_attachment *attach, struct sg_table *sg) | ||
| 239 | { | ||
| 240 | struct mtk_drm_gem_obj *mtk_gem; | ||
| 241 | int ret; | ||
| 242 | struct scatterlist *s; | ||
| 243 | unsigned int i; | ||
| 244 | dma_addr_t expected; | ||
| 245 | |||
| 246 | mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size); | ||
| 247 | |||
| 248 | if (IS_ERR(mtk_gem)) | ||
| 249 | return ERR_PTR(PTR_ERR(mtk_gem)); | ||
| 250 | |||
| 251 | expected = sg_dma_address(sg->sgl); | ||
| 252 | for_each_sg(sg->sgl, s, sg->nents, i) { | ||
| 253 | if (sg_dma_address(s) != expected) { | ||
| 254 | DRM_ERROR("sg_table is not contiguous"); | ||
| 255 | ret = -EINVAL; | ||
| 256 | goto err_gem_free; | ||
| 257 | } | ||
| 258 | expected = sg_dma_address(s) + sg_dma_len(s); | ||
| 259 | } | ||
| 260 | |||
| 261 | mtk_gem->dma_addr = sg_dma_address(sg->sgl); | ||
| 262 | mtk_gem->sg = sg; | ||
| 263 | |||
| 264 | return &mtk_gem->base; | ||
| 265 | |||
| 266 | err_gem_free: | ||
| 267 | kfree(mtk_gem); | ||
| 268 | return ERR_PTR(ret); | ||
| 269 | } | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h new file mode 100644 index 000000000000..3a2a5624a1cb --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef _MTK_DRM_GEM_H_ | ||
| 15 | #define _MTK_DRM_GEM_H_ | ||
| 16 | |||
| 17 | #include <drm/drm_gem.h> | ||
| 18 | |||
| 19 | /* | ||
| 20 | * mtk drm buffer structure. | ||
| 21 | * | ||
| 22 | * @base: a gem object. | ||
| 23 | * - a new handle to this gem object would be created | ||
| 24 | * by drm_gem_handle_create(). | ||
| 25 | * @cookie: the return value of dma_alloc_attrs(), keep it for dma_free_attrs() | ||
| 26 | * @kvaddr: kernel virtual address of gem buffer. | ||
| 27 | * @dma_addr: dma address of gem buffer. | ||
| 28 | * @dma_attrs: dma attributes of gem buffer. | ||
| 29 | * | ||
| 30 | * P.S. this object would be transferred to user as kms_bo.handle so | ||
| 31 | * user can access the buffer through kms_bo.handle. | ||
| 32 | */ | ||
| 33 | struct mtk_drm_gem_obj { | ||
| 34 | struct drm_gem_object base; | ||
| 35 | void *cookie; | ||
| 36 | void *kvaddr; | ||
| 37 | dma_addr_t dma_addr; | ||
| 38 | struct dma_attrs dma_attrs; | ||
| 39 | struct sg_table *sg; | ||
| 40 | }; | ||
| 41 | |||
| 42 | #define to_mtk_gem_obj(x) container_of(x, struct mtk_drm_gem_obj, base) | ||
| 43 | |||
| 44 | void mtk_drm_gem_free_object(struct drm_gem_object *gem); | ||
| 45 | struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size, | ||
| 46 | bool alloc_kmap); | ||
| 47 | int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, | ||
| 48 | struct drm_mode_create_dumb *args); | ||
| 49 | int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv, | ||
| 50 | struct drm_device *dev, uint32_t handle, | ||
| 51 | uint64_t *offset); | ||
| 52 | int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); | ||
| 53 | int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, | ||
| 54 | struct vm_area_struct *vma); | ||
| 55 | struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj); | ||
| 56 | struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev, | ||
| 57 | struct dma_buf_attachment *attach, struct sg_table *sg); | ||
| 58 | |||
| 59 | #endif | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.c b/drivers/gpu/drm/mediatek/mtk_drm_plane.c new file mode 100644 index 000000000000..c898788f3dd3 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.c | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * Author: CK Hu <ck.hu@mediatek.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <drm/drmP.h> | ||
| 16 | #include <drm/drm_atomic.h> | ||
| 17 | #include <drm/drm_atomic_helper.h> | ||
| 18 | #include <drm/drm_plane_helper.h> | ||
| 19 | |||
| 20 | #include "mtk_drm_crtc.h" | ||
| 21 | #include "mtk_drm_ddp_comp.h" | ||
| 22 | #include "mtk_drm_drv.h" | ||
| 23 | #include "mtk_drm_fb.h" | ||
| 24 | #include "mtk_drm_gem.h" | ||
| 25 | #include "mtk_drm_plane.h" | ||
| 26 | |||
| 27 | static const u32 formats[] = { | ||
| 28 | DRM_FORMAT_XRGB8888, | ||
| 29 | DRM_FORMAT_ARGB8888, | ||
| 30 | DRM_FORMAT_RGB565, | ||
| 31 | }; | ||
| 32 | |||
| 33 | static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable, | ||
| 34 | dma_addr_t addr, struct drm_rect *dest) | ||
| 35 | { | ||
| 36 | struct drm_plane *plane = &mtk_plane->base; | ||
| 37 | struct mtk_plane_state *state = to_mtk_plane_state(plane->state); | ||
| 38 | unsigned int pitch, format; | ||
| 39 | int x, y; | ||
| 40 | |||
| 41 | if (WARN_ON(!plane->state || (enable && !plane->state->fb))) | ||
| 42 | return; | ||
| 43 | |||
| 44 | if (plane->state->fb) { | ||
| 45 | pitch = plane->state->fb->pitches[0]; | ||
| 46 | format = plane->state->fb->pixel_format; | ||
| 47 | } else { | ||
| 48 | pitch = 0; | ||
| 49 | format = DRM_FORMAT_RGBA8888; | ||
| 50 | } | ||
| 51 | |||
| 52 | x = plane->state->crtc_x; | ||
| 53 | y = plane->state->crtc_y; | ||
| 54 | |||
| 55 | if (x < 0) { | ||
| 56 | addr -= x * 4; | ||
| 57 | x = 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (y < 0) { | ||
| 61 | addr -= y * pitch; | ||
| 62 | y = 0; | ||
| 63 | } | ||
| 64 | |||
| 65 | state->pending.enable = enable; | ||
| 66 | state->pending.pitch = pitch; | ||
| 67 | state->pending.format = format; | ||
| 68 | state->pending.addr = addr; | ||
| 69 | state->pending.x = x; | ||
| 70 | state->pending.y = y; | ||
| 71 | state->pending.width = dest->x2 - dest->x1; | ||
| 72 | state->pending.height = dest->y2 - dest->y1; | ||
| 73 | wmb(); /* Make sure the above parameters are set before update */ | ||
| 74 | state->pending.dirty = true; | ||
| 75 | } | ||
| 76 | |||
| 77 | static void mtk_plane_reset(struct drm_plane *plane) | ||
| 78 | { | ||
| 79 | struct mtk_plane_state *state; | ||
| 80 | |||
| 81 | if (plane->state) { | ||
| 82 | if (plane->state->fb) | ||
| 83 | drm_framebuffer_unreference(plane->state->fb); | ||
| 84 | |||
| 85 | state = to_mtk_plane_state(plane->state); | ||
| 86 | memset(state, 0, sizeof(*state)); | ||
| 87 | } else { | ||
| 88 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
| 89 | if (!state) | ||
| 90 | return; | ||
| 91 | plane->state = &state->base; | ||
| 92 | } | ||
| 93 | |||
| 94 | state->base.plane = plane; | ||
| 95 | state->pending.format = DRM_FORMAT_RGB565; | ||
| 96 | } | ||
| 97 | |||
| 98 | static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane) | ||
| 99 | { | ||
| 100 | struct mtk_plane_state *old_state = to_mtk_plane_state(plane->state); | ||
| 101 | struct mtk_plane_state *state; | ||
| 102 | |||
| 103 | state = kzalloc(sizeof(*state), GFP_KERNEL); | ||
| 104 | if (!state) | ||
| 105 | return NULL; | ||
| 106 | |||
| 107 | __drm_atomic_helper_plane_duplicate_state(plane, &state->base); | ||
| 108 | |||
| 109 | WARN_ON(state->base.plane != plane); | ||
| 110 | |||
| 111 | state->pending = old_state->pending; | ||
| 112 | |||
| 113 | return &state->base; | ||
| 114 | } | ||
| 115 | |||
| 116 | static void mtk_drm_plane_destroy_state(struct drm_plane *plane, | ||
| 117 | struct drm_plane_state *state) | ||
| 118 | { | ||
| 119 | __drm_atomic_helper_plane_destroy_state(plane, state); | ||
| 120 | kfree(to_mtk_plane_state(state)); | ||
| 121 | } | ||
| 122 | |||
| 123 | static const struct drm_plane_funcs mtk_plane_funcs = { | ||
| 124 | .update_plane = drm_atomic_helper_update_plane, | ||
| 125 | .disable_plane = drm_atomic_helper_disable_plane, | ||
| 126 | .destroy = drm_plane_cleanup, | ||
| 127 | .reset = mtk_plane_reset, | ||
| 128 | .atomic_duplicate_state = mtk_plane_duplicate_state, | ||
| 129 | .atomic_destroy_state = mtk_drm_plane_destroy_state, | ||
| 130 | }; | ||
| 131 | |||
| 132 | static int mtk_plane_atomic_check(struct drm_plane *plane, | ||
| 133 | struct drm_plane_state *state) | ||
| 134 | { | ||
| 135 | struct drm_framebuffer *fb = state->fb; | ||
| 136 | struct drm_crtc_state *crtc_state; | ||
| 137 | bool visible; | ||
| 138 | struct drm_rect dest = { | ||
| 139 | .x1 = state->crtc_x, | ||
| 140 | .y1 = state->crtc_y, | ||
| 141 | .x2 = state->crtc_x + state->crtc_w, | ||
| 142 | .y2 = state->crtc_y + state->crtc_h, | ||
| 143 | }; | ||
| 144 | struct drm_rect src = { | ||
| 145 | /* 16.16 fixed point */ | ||
| 146 | .x1 = state->src_x, | ||
| 147 | .y1 = state->src_y, | ||
| 148 | .x2 = state->src_x + state->src_w, | ||
| 149 | .y2 = state->src_y + state->src_h, | ||
| 150 | }; | ||
| 151 | struct drm_rect clip = { 0, }; | ||
| 152 | |||
| 153 | if (!fb) | ||
| 154 | return 0; | ||
| 155 | |||
| 156 | if (!mtk_fb_get_gem_obj(fb)) { | ||
| 157 | DRM_DEBUG_KMS("buffer is null\n"); | ||
| 158 | return -EFAULT; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (!state->crtc) | ||
| 162 | return 0; | ||
| 163 | |||
| 164 | crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); | ||
| 165 | if (IS_ERR(crtc_state)) | ||
| 166 | return PTR_ERR(crtc_state); | ||
| 167 | |||
| 168 | clip.x2 = crtc_state->mode.hdisplay; | ||
| 169 | clip.y2 = crtc_state->mode.vdisplay; | ||
| 170 | |||
| 171 | return drm_plane_helper_check_update(plane, state->crtc, fb, | ||
| 172 | &src, &dest, &clip, | ||
| 173 | DRM_PLANE_HELPER_NO_SCALING, | ||
| 174 | DRM_PLANE_HELPER_NO_SCALING, | ||
| 175 | true, true, &visible); | ||
| 176 | } | ||
| 177 | |||
| 178 | static void mtk_plane_atomic_update(struct drm_plane *plane, | ||
| 179 | struct drm_plane_state *old_state) | ||
| 180 | { | ||
| 181 | struct mtk_plane_state *state = to_mtk_plane_state(plane->state); | ||
| 182 | struct drm_crtc *crtc = state->base.crtc; | ||
| 183 | struct drm_gem_object *gem; | ||
| 184 | struct mtk_drm_gem_obj *mtk_gem; | ||
| 185 | struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane); | ||
| 186 | struct drm_rect dest = { | ||
| 187 | .x1 = state->base.crtc_x, | ||
| 188 | .y1 = state->base.crtc_y, | ||
| 189 | .x2 = state->base.crtc_x + state->base.crtc_w, | ||
| 190 | .y2 = state->base.crtc_y + state->base.crtc_h, | ||
| 191 | }; | ||
| 192 | struct drm_rect clip = { 0, }; | ||
| 193 | |||
| 194 | if (!crtc) | ||
| 195 | return; | ||
| 196 | |||
| 197 | clip.x2 = state->base.crtc->state->mode.hdisplay; | ||
| 198 | clip.y2 = state->base.crtc->state->mode.vdisplay; | ||
| 199 | drm_rect_intersect(&dest, &clip); | ||
| 200 | |||
| 201 | gem = mtk_fb_get_gem_obj(state->base.fb); | ||
| 202 | mtk_gem = to_mtk_gem_obj(gem); | ||
| 203 | mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest); | ||
| 204 | } | ||
| 205 | |||
| 206 | static void mtk_plane_atomic_disable(struct drm_plane *plane, | ||
| 207 | struct drm_plane_state *old_state) | ||
| 208 | { | ||
| 209 | struct mtk_plane_state *state = to_mtk_plane_state(plane->state); | ||
| 210 | |||
| 211 | state->pending.enable = false; | ||
| 212 | wmb(); /* Make sure the above parameter is set before update */ | ||
| 213 | state->pending.dirty = true; | ||
| 214 | } | ||
| 215 | |||
| 216 | static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = { | ||
| 217 | .atomic_check = mtk_plane_atomic_check, | ||
| 218 | .atomic_update = mtk_plane_atomic_update, | ||
| 219 | .atomic_disable = mtk_plane_atomic_disable, | ||
| 220 | }; | ||
| 221 | |||
| 222 | int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, | ||
| 223 | unsigned long possible_crtcs, enum drm_plane_type type, | ||
| 224 | unsigned int zpos) | ||
| 225 | { | ||
| 226 | int err; | ||
| 227 | |||
| 228 | err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs, | ||
| 229 | &mtk_plane_funcs, formats, | ||
| 230 | ARRAY_SIZE(formats), type, NULL); | ||
| 231 | if (err) { | ||
| 232 | DRM_ERROR("failed to initialize plane\n"); | ||
| 233 | return err; | ||
| 234 | } | ||
| 235 | |||
| 236 | drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs); | ||
| 237 | mtk_plane->idx = zpos; | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_plane.h b/drivers/gpu/drm/mediatek/mtk_drm_plane.h new file mode 100644 index 000000000000..72a7b3e4c126 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_drm_plane.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * Author: CK Hu <ck.hu@mediatek.com> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #ifndef _MTK_DRM_PLANE_H_ | ||
| 16 | #define _MTK_DRM_PLANE_H_ | ||
| 17 | |||
| 18 | #include <drm/drm_crtc.h> | ||
| 19 | #include <linux/types.h> | ||
| 20 | |||
| 21 | struct mtk_drm_plane { | ||
| 22 | struct drm_plane base; | ||
| 23 | unsigned int idx; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct mtk_plane_pending_state { | ||
| 27 | bool config; | ||
| 28 | bool enable; | ||
| 29 | dma_addr_t addr; | ||
| 30 | unsigned int pitch; | ||
| 31 | unsigned int format; | ||
| 32 | unsigned int x; | ||
| 33 | unsigned int y; | ||
| 34 | unsigned int width; | ||
| 35 | unsigned int height; | ||
| 36 | bool dirty; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct mtk_plane_state { | ||
| 40 | struct drm_plane_state base; | ||
| 41 | struct mtk_plane_pending_state pending; | ||
| 42 | }; | ||
| 43 | |||
| 44 | static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane) | ||
| 45 | { | ||
| 46 | return container_of(plane, struct mtk_drm_plane, base); | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline struct mtk_plane_state * | ||
| 50 | to_mtk_plane_state(struct drm_plane_state *state) | ||
| 51 | { | ||
| 52 | return container_of(state, struct mtk_plane_state, base); | ||
| 53 | } | ||
| 54 | |||
| 55 | int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane, | ||
| 56 | unsigned long possible_crtcs, enum drm_plane_type type, | ||
| 57 | unsigned int zpos); | ||
| 58 | |||
| 59 | #endif | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c new file mode 100644 index 000000000000..2d808e59fefd --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c | |||
| @@ -0,0 +1,913 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <drm/drm_atomic_helper.h> | ||
| 16 | #include <drm/drm_crtc_helper.h> | ||
| 17 | #include <drm/drm_mipi_dsi.h> | ||
| 18 | #include <drm/drm_panel.h> | ||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/component.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/of_platform.h> | ||
| 23 | #include <linux/of_graph.h> | ||
| 24 | #include <linux/phy/phy.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <video/videomode.h> | ||
| 27 | |||
| 28 | #include "mtk_drm_ddp_comp.h" | ||
| 29 | |||
| 30 | #define DSI_VIDEO_FIFO_DEPTH (1920 / 4) | ||
| 31 | #define DSI_HOST_FIFO_DEPTH 64 | ||
| 32 | |||
| 33 | #define DSI_START 0x00 | ||
| 34 | |||
| 35 | #define DSI_CON_CTRL 0x10 | ||
| 36 | #define DSI_RESET BIT(0) | ||
| 37 | #define DSI_EN BIT(1) | ||
| 38 | |||
| 39 | #define DSI_MODE_CTRL 0x14 | ||
| 40 | #define MODE (3) | ||
| 41 | #define CMD_MODE 0 | ||
| 42 | #define SYNC_PULSE_MODE 1 | ||
| 43 | #define SYNC_EVENT_MODE 2 | ||
| 44 | #define BURST_MODE 3 | ||
| 45 | #define FRM_MODE BIT(16) | ||
| 46 | #define MIX_MODE BIT(17) | ||
| 47 | |||
| 48 | #define DSI_TXRX_CTRL 0x18 | ||
| 49 | #define VC_NUM (2 << 0) | ||
| 50 | #define LANE_NUM (0xf << 2) | ||
| 51 | #define DIS_EOT BIT(6) | ||
| 52 | #define NULL_EN BIT(7) | ||
| 53 | #define TE_FREERUN BIT(8) | ||
| 54 | #define EXT_TE_EN BIT(9) | ||
| 55 | #define EXT_TE_EDGE BIT(10) | ||
| 56 | #define MAX_RTN_SIZE (0xf << 12) | ||
| 57 | #define HSTX_CKLP_EN BIT(16) | ||
| 58 | |||
| 59 | #define DSI_PSCTRL 0x1c | ||
| 60 | #define DSI_PS_WC 0x3fff | ||
| 61 | #define DSI_PS_SEL (3 << 16) | ||
| 62 | #define PACKED_PS_16BIT_RGB565 (0 << 16) | ||
| 63 | #define LOOSELY_PS_18BIT_RGB666 (1 << 16) | ||
| 64 | #define PACKED_PS_18BIT_RGB666 (2 << 16) | ||
| 65 | #define PACKED_PS_24BIT_RGB888 (3 << 16) | ||
| 66 | |||
| 67 | #define DSI_VSA_NL 0x20 | ||
| 68 | #define DSI_VBP_NL 0x24 | ||
| 69 | #define DSI_VFP_NL 0x28 | ||
| 70 | #define DSI_VACT_NL 0x2C | ||
| 71 | #define DSI_HSA_WC 0x50 | ||
| 72 | #define DSI_HBP_WC 0x54 | ||
| 73 | #define DSI_HFP_WC 0x58 | ||
| 74 | |||
| 75 | #define DSI_HSTX_CKL_WC 0x64 | ||
| 76 | |||
| 77 | #define DSI_PHY_LCCON 0x104 | ||
| 78 | #define LC_HS_TX_EN BIT(0) | ||
| 79 | #define LC_ULPM_EN BIT(1) | ||
| 80 | #define LC_WAKEUP_EN BIT(2) | ||
| 81 | |||
| 82 | #define DSI_PHY_LD0CON 0x108 | ||
| 83 | #define LD0_HS_TX_EN BIT(0) | ||
| 84 | #define LD0_ULPM_EN BIT(1) | ||
| 85 | #define LD0_WAKEUP_EN BIT(2) | ||
| 86 | |||
| 87 | #define DSI_PHY_TIMECON0 0x110 | ||
| 88 | #define LPX (0xff << 0) | ||
| 89 | #define HS_PRPR (0xff << 8) | ||
| 90 | #define HS_ZERO (0xff << 16) | ||
| 91 | #define HS_TRAIL (0xff << 24) | ||
| 92 | |||
| 93 | #define DSI_PHY_TIMECON1 0x114 | ||
| 94 | #define TA_GO (0xff << 0) | ||
| 95 | #define TA_SURE (0xff << 8) | ||
| 96 | #define TA_GET (0xff << 16) | ||
| 97 | #define DA_HS_EXIT (0xff << 24) | ||
| 98 | |||
| 99 | #define DSI_PHY_TIMECON2 0x118 | ||
| 100 | #define CONT_DET (0xff << 0) | ||
| 101 | #define CLK_ZERO (0xff << 16) | ||
| 102 | #define CLK_TRAIL (0xff << 24) | ||
| 103 | |||
| 104 | #define DSI_PHY_TIMECON3 0x11c | ||
| 105 | #define CLK_HS_PRPR (0xff << 0) | ||
| 106 | #define CLK_HS_POST (0xff << 8) | ||
| 107 | #define CLK_HS_EXIT (0xff << 16) | ||
| 108 | |||
| 109 | #define NS_TO_CYCLE(n, c) ((n) / (c) + (((n) % (c)) ? 1 : 0)) | ||
| 110 | |||
| 111 | struct phy; | ||
| 112 | |||
| 113 | struct mtk_dsi { | ||
| 114 | struct mtk_ddp_comp ddp_comp; | ||
| 115 | struct device *dev; | ||
| 116 | struct mipi_dsi_host host; | ||
| 117 | struct drm_encoder encoder; | ||
| 118 | struct drm_connector conn; | ||
| 119 | struct drm_panel *panel; | ||
| 120 | struct drm_bridge *bridge; | ||
| 121 | struct phy *phy; | ||
| 122 | |||
| 123 | void __iomem *regs; | ||
| 124 | |||
| 125 | struct clk *engine_clk; | ||
| 126 | struct clk *digital_clk; | ||
| 127 | struct clk *hs_clk; | ||
| 128 | |||
| 129 | u32 data_rate; | ||
| 130 | |||
| 131 | unsigned long mode_flags; | ||
| 132 | enum mipi_dsi_pixel_format format; | ||
| 133 | unsigned int lanes; | ||
| 134 | struct videomode vm; | ||
| 135 | int refcount; | ||
| 136 | bool enabled; | ||
| 137 | }; | ||
| 138 | |||
| 139 | static inline struct mtk_dsi *encoder_to_dsi(struct drm_encoder *e) | ||
| 140 | { | ||
| 141 | return container_of(e, struct mtk_dsi, encoder); | ||
| 142 | } | ||
| 143 | |||
| 144 | static inline struct mtk_dsi *connector_to_dsi(struct drm_connector *c) | ||
| 145 | { | ||
| 146 | return container_of(c, struct mtk_dsi, conn); | ||
| 147 | } | ||
| 148 | |||
| 149 | static inline struct mtk_dsi *host_to_dsi(struct mipi_dsi_host *h) | ||
| 150 | { | ||
| 151 | return container_of(h, struct mtk_dsi, host); | ||
| 152 | } | ||
| 153 | |||
| 154 | static void mtk_dsi_mask(struct mtk_dsi *dsi, u32 offset, u32 mask, u32 data) | ||
| 155 | { | ||
| 156 | u32 temp = readl(dsi->regs + offset); | ||
| 157 | |||
| 158 | writel((temp & ~mask) | (data & mask), dsi->regs + offset); | ||
| 159 | } | ||
| 160 | |||
| 161 | static void dsi_phy_timconfig(struct mtk_dsi *dsi) | ||
| 162 | { | ||
| 163 | u32 timcon0, timcon1, timcon2, timcon3; | ||
| 164 | unsigned int ui, cycle_time; | ||
| 165 | unsigned int lpx; | ||
| 166 | |||
| 167 | ui = 1000 / dsi->data_rate + 0x01; | ||
| 168 | cycle_time = 8000 / dsi->data_rate + 0x01; | ||
| 169 | lpx = 5; | ||
| 170 | |||
| 171 | timcon0 = (8 << 24) | (0xa << 16) | (0x6 << 8) | lpx; | ||
| 172 | timcon1 = (7 << 24) | (5 * lpx << 16) | ((3 * lpx) / 2) << 8 | | ||
| 173 | (4 * lpx); | ||
| 174 | timcon2 = ((NS_TO_CYCLE(0x64, cycle_time) + 0xa) << 24) | | ||
| 175 | (NS_TO_CYCLE(0x150, cycle_time) << 16); | ||
| 176 | timcon3 = (2 * lpx) << 16 | NS_TO_CYCLE(80 + 52 * ui, cycle_time) << 8 | | ||
| 177 | NS_TO_CYCLE(0x40, cycle_time); | ||
| 178 | |||
| 179 | writel(timcon0, dsi->regs + DSI_PHY_TIMECON0); | ||
| 180 | writel(timcon1, dsi->regs + DSI_PHY_TIMECON1); | ||
| 181 | writel(timcon2, dsi->regs + DSI_PHY_TIMECON2); | ||
| 182 | writel(timcon3, dsi->regs + DSI_PHY_TIMECON3); | ||
| 183 | } | ||
| 184 | |||
| 185 | static void mtk_dsi_enable(struct mtk_dsi *dsi) | ||
| 186 | { | ||
| 187 | mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, DSI_EN); | ||
| 188 | } | ||
| 189 | |||
| 190 | static void mtk_dsi_disable(struct mtk_dsi *dsi) | ||
| 191 | { | ||
| 192 | mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, 0); | ||
| 193 | } | ||
| 194 | |||
| 195 | static void mtk_dsi_reset(struct mtk_dsi *dsi) | ||
| 196 | { | ||
| 197 | mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_RESET, DSI_RESET); | ||
| 198 | mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_RESET, 0); | ||
| 199 | } | ||
| 200 | |||
| 201 | static int mtk_dsi_poweron(struct mtk_dsi *dsi) | ||
| 202 | { | ||
| 203 | struct device *dev = dsi->dev; | ||
| 204 | int ret; | ||
| 205 | |||
| 206 | if (++dsi->refcount != 1) | ||
| 207 | return 0; | ||
| 208 | |||
| 209 | /** | ||
| 210 | * data_rate = (pixel_clock / 1000) * pixel_dipth * mipi_ratio; | ||
| 211 | * pixel_clock unit is Khz, data_rata unit is MHz, so need divide 1000. | ||
| 212 | * mipi_ratio is mipi clk coefficient for balance the pixel clk in mipi. | ||
| 213 | * we set mipi_ratio is 1.05. | ||
| 214 | */ | ||
| 215 | dsi->data_rate = dsi->vm.pixelclock * 3 * 21 / (1 * 1000 * 10); | ||
| 216 | |||
| 217 | ret = clk_set_rate(dsi->hs_clk, dsi->data_rate * 1000000); | ||
| 218 | if (ret < 0) { | ||
| 219 | dev_err(dev, "Failed to set data rate: %d\n", ret); | ||
| 220 | goto err_refcount; | ||
| 221 | } | ||
| 222 | |||
| 223 | phy_power_on(dsi->phy); | ||
| 224 | |||
| 225 | ret = clk_prepare_enable(dsi->engine_clk); | ||
| 226 | if (ret < 0) { | ||
| 227 | dev_err(dev, "Failed to enable engine clock: %d\n", ret); | ||
| 228 | goto err_phy_power_off; | ||
| 229 | } | ||
| 230 | |||
| 231 | ret = clk_prepare_enable(dsi->digital_clk); | ||
| 232 | if (ret < 0) { | ||
| 233 | dev_err(dev, "Failed to enable digital clock: %d\n", ret); | ||
| 234 | goto err_disable_engine_clk; | ||
| 235 | } | ||
| 236 | |||
| 237 | mtk_dsi_enable(dsi); | ||
| 238 | mtk_dsi_reset(dsi); | ||
| 239 | dsi_phy_timconfig(dsi); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | |||
| 243 | err_disable_engine_clk: | ||
| 244 | clk_disable_unprepare(dsi->engine_clk); | ||
| 245 | err_phy_power_off: | ||
| 246 | phy_power_off(dsi->phy); | ||
| 247 | err_refcount: | ||
| 248 | dsi->refcount--; | ||
| 249 | return ret; | ||
| 250 | } | ||
| 251 | |||
| 252 | static void dsi_clk_ulp_mode_enter(struct mtk_dsi *dsi) | ||
| 253 | { | ||
| 254 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, 0); | ||
| 255 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_ULPM_EN, 0); | ||
| 256 | } | ||
| 257 | |||
| 258 | static void dsi_clk_ulp_mode_leave(struct mtk_dsi *dsi) | ||
| 259 | { | ||
| 260 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_ULPM_EN, 0); | ||
| 261 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_WAKEUP_EN, LC_WAKEUP_EN); | ||
| 262 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_WAKEUP_EN, 0); | ||
| 263 | } | ||
| 264 | |||
| 265 | static void dsi_lane0_ulp_mode_enter(struct mtk_dsi *dsi) | ||
| 266 | { | ||
| 267 | mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_HS_TX_EN, 0); | ||
| 268 | mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_ULPM_EN, 0); | ||
| 269 | } | ||
| 270 | |||
| 271 | static void dsi_lane0_ulp_mode_leave(struct mtk_dsi *dsi) | ||
| 272 | { | ||
| 273 | mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_ULPM_EN, 0); | ||
| 274 | mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_WAKEUP_EN, LD0_WAKEUP_EN); | ||
| 275 | mtk_dsi_mask(dsi, DSI_PHY_LD0CON, LD0_WAKEUP_EN, 0); | ||
| 276 | } | ||
| 277 | |||
| 278 | static bool dsi_clk_hs_state(struct mtk_dsi *dsi) | ||
| 279 | { | ||
| 280 | u32 tmp_reg1; | ||
| 281 | |||
| 282 | tmp_reg1 = readl(dsi->regs + DSI_PHY_LCCON); | ||
| 283 | return ((tmp_reg1 & LC_HS_TX_EN) == 1) ? true : false; | ||
| 284 | } | ||
| 285 | |||
| 286 | static void dsi_clk_hs_mode(struct mtk_dsi *dsi, bool enter) | ||
| 287 | { | ||
| 288 | if (enter && !dsi_clk_hs_state(dsi)) | ||
| 289 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, LC_HS_TX_EN); | ||
| 290 | else if (!enter && dsi_clk_hs_state(dsi)) | ||
| 291 | mtk_dsi_mask(dsi, DSI_PHY_LCCON, LC_HS_TX_EN, 0); | ||
| 292 | } | ||
| 293 | |||
| 294 | static void dsi_set_mode(struct mtk_dsi *dsi) | ||
| 295 | { | ||
| 296 | u32 vid_mode = CMD_MODE; | ||
| 297 | |||
| 298 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { | ||
| 299 | vid_mode = SYNC_PULSE_MODE; | ||
| 300 | |||
| 301 | if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) && | ||
| 302 | !(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)) | ||
| 303 | vid_mode = BURST_MODE; | ||
| 304 | } | ||
| 305 | |||
| 306 | writel(vid_mode, dsi->regs + DSI_MODE_CTRL); | ||
| 307 | } | ||
| 308 | |||
| 309 | static void dsi_ps_control_vact(struct mtk_dsi *dsi) | ||
| 310 | { | ||
| 311 | struct videomode *vm = &dsi->vm; | ||
| 312 | u32 dsi_buf_bpp, ps_wc; | ||
| 313 | u32 ps_bpp_mode; | ||
| 314 | |||
| 315 | if (dsi->format == MIPI_DSI_FMT_RGB565) | ||
| 316 | dsi_buf_bpp = 2; | ||
| 317 | else | ||
| 318 | dsi_buf_bpp = 3; | ||
| 319 | |||
| 320 | ps_wc = vm->hactive * dsi_buf_bpp; | ||
| 321 | ps_bpp_mode = ps_wc; | ||
| 322 | |||
| 323 | switch (dsi->format) { | ||
| 324 | case MIPI_DSI_FMT_RGB888: | ||
| 325 | ps_bpp_mode |= PACKED_PS_24BIT_RGB888; | ||
| 326 | break; | ||
| 327 | case MIPI_DSI_FMT_RGB666: | ||
| 328 | ps_bpp_mode |= PACKED_PS_18BIT_RGB666; | ||
| 329 | break; | ||
| 330 | case MIPI_DSI_FMT_RGB666_PACKED: | ||
| 331 | ps_bpp_mode |= LOOSELY_PS_18BIT_RGB666; | ||
| 332 | break; | ||
| 333 | case MIPI_DSI_FMT_RGB565: | ||
| 334 | ps_bpp_mode |= PACKED_PS_16BIT_RGB565; | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | |||
| 338 | writel(vm->vactive, dsi->regs + DSI_VACT_NL); | ||
| 339 | writel(ps_bpp_mode, dsi->regs + DSI_PSCTRL); | ||
| 340 | writel(ps_wc, dsi->regs + DSI_HSTX_CKL_WC); | ||
| 341 | } | ||
| 342 | |||
| 343 | static void dsi_rxtx_control(struct mtk_dsi *dsi) | ||
| 344 | { | ||
| 345 | u32 tmp_reg; | ||
| 346 | |||
| 347 | switch (dsi->lanes) { | ||
| 348 | case 1: | ||
| 349 | tmp_reg = 1 << 2; | ||
| 350 | break; | ||
| 351 | case 2: | ||
| 352 | tmp_reg = 3 << 2; | ||
| 353 | break; | ||
| 354 | case 3: | ||
| 355 | tmp_reg = 7 << 2; | ||
| 356 | break; | ||
| 357 | case 4: | ||
| 358 | tmp_reg = 0xf << 2; | ||
| 359 | break; | ||
| 360 | default: | ||
| 361 | tmp_reg = 0xf << 2; | ||
| 362 | break; | ||
| 363 | } | ||
| 364 | |||
| 365 | writel(tmp_reg, dsi->regs + DSI_TXRX_CTRL); | ||
| 366 | } | ||
| 367 | |||
| 368 | static void dsi_ps_control(struct mtk_dsi *dsi) | ||
| 369 | { | ||
| 370 | unsigned int dsi_tmp_buf_bpp; | ||
| 371 | u32 tmp_reg; | ||
| 372 | |||
| 373 | switch (dsi->format) { | ||
| 374 | case MIPI_DSI_FMT_RGB888: | ||
| 375 | tmp_reg = PACKED_PS_24BIT_RGB888; | ||
| 376 | dsi_tmp_buf_bpp = 3; | ||
| 377 | break; | ||
| 378 | case MIPI_DSI_FMT_RGB666: | ||
| 379 | tmp_reg = LOOSELY_PS_18BIT_RGB666; | ||
| 380 | dsi_tmp_buf_bpp = 3; | ||
| 381 | break; | ||
| 382 | case MIPI_DSI_FMT_RGB666_PACKED: | ||
| 383 | tmp_reg = PACKED_PS_18BIT_RGB666; | ||
| 384 | dsi_tmp_buf_bpp = 3; | ||
| 385 | break; | ||
| 386 | case MIPI_DSI_FMT_RGB565: | ||
| 387 | tmp_reg = PACKED_PS_16BIT_RGB565; | ||
| 388 | dsi_tmp_buf_bpp = 2; | ||
| 389 | break; | ||
| 390 | default: | ||
| 391 | tmp_reg = PACKED_PS_24BIT_RGB888; | ||
| 392 | dsi_tmp_buf_bpp = 3; | ||
| 393 | break; | ||
| 394 | } | ||
| 395 | |||
| 396 | tmp_reg += dsi->vm.hactive * dsi_tmp_buf_bpp & DSI_PS_WC; | ||
| 397 | writel(tmp_reg, dsi->regs + DSI_PSCTRL); | ||
| 398 | } | ||
| 399 | |||
| 400 | static void dsi_config_vdo_timing(struct mtk_dsi *dsi) | ||
| 401 | { | ||
| 402 | unsigned int horizontal_sync_active_byte; | ||
| 403 | unsigned int horizontal_backporch_byte; | ||
| 404 | unsigned int horizontal_frontporch_byte; | ||
| 405 | unsigned int dsi_tmp_buf_bpp; | ||
| 406 | |||
| 407 | struct videomode *vm = &dsi->vm; | ||
| 408 | |||
| 409 | if (dsi->format == MIPI_DSI_FMT_RGB565) | ||
| 410 | dsi_tmp_buf_bpp = 2; | ||
| 411 | else | ||
| 412 | dsi_tmp_buf_bpp = 3; | ||
| 413 | |||
| 414 | writel(vm->vsync_len, dsi->regs + DSI_VSA_NL); | ||
| 415 | writel(vm->vback_porch, dsi->regs + DSI_VBP_NL); | ||
| 416 | writel(vm->vfront_porch, dsi->regs + DSI_VFP_NL); | ||
| 417 | writel(vm->vactive, dsi->regs + DSI_VACT_NL); | ||
| 418 | |||
| 419 | horizontal_sync_active_byte = (vm->hsync_len * dsi_tmp_buf_bpp - 10); | ||
| 420 | |||
| 421 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) | ||
| 422 | horizontal_backporch_byte = | ||
| 423 | (vm->hback_porch * dsi_tmp_buf_bpp - 10); | ||
| 424 | else | ||
| 425 | horizontal_backporch_byte = ((vm->hback_porch + vm->hsync_len) * | ||
| 426 | dsi_tmp_buf_bpp - 10); | ||
| 427 | |||
| 428 | horizontal_frontporch_byte = (vm->hfront_porch * dsi_tmp_buf_bpp - 12); | ||
| 429 | |||
| 430 | writel(horizontal_sync_active_byte, dsi->regs + DSI_HSA_WC); | ||
| 431 | writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC); | ||
| 432 | writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC); | ||
| 433 | |||
| 434 | dsi_ps_control(dsi); | ||
| 435 | } | ||
| 436 | |||
| 437 | static void mtk_dsi_start(struct mtk_dsi *dsi) | ||
| 438 | { | ||
| 439 | writel(0, dsi->regs + DSI_START); | ||
| 440 | writel(1, dsi->regs + DSI_START); | ||
| 441 | } | ||
| 442 | |||
| 443 | static void mtk_dsi_poweroff(struct mtk_dsi *dsi) | ||
| 444 | { | ||
| 445 | if (WARN_ON(dsi->refcount == 0)) | ||
| 446 | return; | ||
| 447 | |||
| 448 | if (--dsi->refcount != 0) | ||
| 449 | return; | ||
| 450 | |||
| 451 | dsi_lane0_ulp_mode_enter(dsi); | ||
| 452 | dsi_clk_ulp_mode_enter(dsi); | ||
| 453 | |||
| 454 | mtk_dsi_disable(dsi); | ||
| 455 | |||
| 456 | clk_disable_unprepare(dsi->engine_clk); | ||
| 457 | clk_disable_unprepare(dsi->digital_clk); | ||
| 458 | |||
| 459 | phy_power_off(dsi->phy); | ||
| 460 | } | ||
| 461 | |||
| 462 | static void mtk_output_dsi_enable(struct mtk_dsi *dsi) | ||
| 463 | { | ||
| 464 | int ret; | ||
| 465 | |||
| 466 | if (dsi->enabled) | ||
| 467 | return; | ||
| 468 | |||
| 469 | if (dsi->panel) { | ||
| 470 | if (drm_panel_prepare(dsi->panel)) { | ||
| 471 | DRM_ERROR("failed to setup the panel\n"); | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | ret = mtk_dsi_poweron(dsi); | ||
| 477 | if (ret < 0) { | ||
| 478 | DRM_ERROR("failed to power on dsi\n"); | ||
| 479 | return; | ||
| 480 | } | ||
| 481 | |||
| 482 | dsi_rxtx_control(dsi); | ||
| 483 | |||
| 484 | dsi_clk_ulp_mode_leave(dsi); | ||
| 485 | dsi_lane0_ulp_mode_leave(dsi); | ||
| 486 | dsi_clk_hs_mode(dsi, 0); | ||
| 487 | dsi_set_mode(dsi); | ||
| 488 | |||
| 489 | dsi_ps_control_vact(dsi); | ||
| 490 | dsi_config_vdo_timing(dsi); | ||
| 491 | |||
| 492 | dsi_set_mode(dsi); | ||
| 493 | dsi_clk_hs_mode(dsi, 1); | ||
| 494 | |||
| 495 | mtk_dsi_start(dsi); | ||
| 496 | |||
| 497 | dsi->enabled = true; | ||
| 498 | } | ||
| 499 | |||
| 500 | static void mtk_output_dsi_disable(struct mtk_dsi *dsi) | ||
| 501 | { | ||
| 502 | if (!dsi->enabled) | ||
| 503 | return; | ||
| 504 | |||
| 505 | if (dsi->panel) { | ||
| 506 | if (drm_panel_disable(dsi->panel)) { | ||
| 507 | DRM_ERROR("failed to disable the panel\n"); | ||
| 508 | return; | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | mtk_dsi_poweroff(dsi); | ||
| 513 | |||
| 514 | dsi->enabled = false; | ||
| 515 | } | ||
| 516 | |||
| 517 | static void mtk_dsi_encoder_destroy(struct drm_encoder *encoder) | ||
| 518 | { | ||
| 519 | drm_encoder_cleanup(encoder); | ||
| 520 | } | ||
| 521 | |||
| 522 | static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = { | ||
| 523 | .destroy = mtk_dsi_encoder_destroy, | ||
| 524 | }; | ||
| 525 | |||
| 526 | static bool mtk_dsi_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 527 | const struct drm_display_mode *mode, | ||
| 528 | struct drm_display_mode *adjusted_mode) | ||
| 529 | { | ||
| 530 | return true; | ||
| 531 | } | ||
| 532 | |||
| 533 | static void mtk_dsi_encoder_mode_set(struct drm_encoder *encoder, | ||
| 534 | struct drm_display_mode *mode, | ||
| 535 | struct drm_display_mode *adjusted) | ||
| 536 | { | ||
| 537 | struct mtk_dsi *dsi = encoder_to_dsi(encoder); | ||
| 538 | |||
| 539 | dsi->vm.pixelclock = adjusted->clock; | ||
| 540 | dsi->vm.hactive = adjusted->hdisplay; | ||
| 541 | dsi->vm.hback_porch = adjusted->htotal - adjusted->hsync_end; | ||
| 542 | dsi->vm.hfront_porch = adjusted->hsync_start - adjusted->hdisplay; | ||
| 543 | dsi->vm.hsync_len = adjusted->hsync_end - adjusted->hsync_start; | ||
| 544 | |||
| 545 | dsi->vm.vactive = adjusted->vdisplay; | ||
| 546 | dsi->vm.vback_porch = adjusted->vtotal - adjusted->vsync_end; | ||
| 547 | dsi->vm.vfront_porch = adjusted->vsync_start - adjusted->vdisplay; | ||
| 548 | dsi->vm.vsync_len = adjusted->vsync_end - adjusted->vsync_start; | ||
| 549 | } | ||
| 550 | |||
| 551 | static void mtk_dsi_encoder_disable(struct drm_encoder *encoder) | ||
| 552 | { | ||
| 553 | struct mtk_dsi *dsi = encoder_to_dsi(encoder); | ||
| 554 | |||
| 555 | mtk_output_dsi_disable(dsi); | ||
| 556 | } | ||
| 557 | |||
| 558 | static void mtk_dsi_encoder_enable(struct drm_encoder *encoder) | ||
| 559 | { | ||
| 560 | struct mtk_dsi *dsi = encoder_to_dsi(encoder); | ||
| 561 | |||
| 562 | mtk_output_dsi_enable(dsi); | ||
| 563 | } | ||
| 564 | |||
| 565 | static enum drm_connector_status mtk_dsi_connector_detect( | ||
| 566 | struct drm_connector *connector, bool force) | ||
| 567 | { | ||
| 568 | return connector_status_connected; | ||
| 569 | } | ||
| 570 | |||
| 571 | static int mtk_dsi_connector_get_modes(struct drm_connector *connector) | ||
| 572 | { | ||
| 573 | struct mtk_dsi *dsi = connector_to_dsi(connector); | ||
| 574 | |||
| 575 | return drm_panel_get_modes(dsi->panel); | ||
| 576 | } | ||
| 577 | |||
| 578 | static struct drm_encoder *mtk_dsi_connector_best_encoder( | ||
| 579 | struct drm_connector *connector) | ||
| 580 | { | ||
| 581 | struct mtk_dsi *dsi = connector_to_dsi(connector); | ||
| 582 | |||
| 583 | return &dsi->encoder; | ||
| 584 | } | ||
| 585 | |||
| 586 | static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = { | ||
| 587 | .mode_fixup = mtk_dsi_encoder_mode_fixup, | ||
| 588 | .mode_set = mtk_dsi_encoder_mode_set, | ||
| 589 | .disable = mtk_dsi_encoder_disable, | ||
| 590 | .enable = mtk_dsi_encoder_enable, | ||
| 591 | }; | ||
| 592 | |||
| 593 | static const struct drm_connector_funcs mtk_dsi_connector_funcs = { | ||
| 594 | .dpms = drm_atomic_helper_connector_dpms, | ||
| 595 | .detect = mtk_dsi_connector_detect, | ||
| 596 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 597 | .destroy = drm_connector_cleanup, | ||
| 598 | .reset = drm_atomic_helper_connector_reset, | ||
| 599 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | ||
| 600 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | ||
| 601 | }; | ||
| 602 | |||
| 603 | static const struct drm_connector_helper_funcs | ||
| 604 | mtk_dsi_connector_helper_funcs = { | ||
| 605 | .get_modes = mtk_dsi_connector_get_modes, | ||
| 606 | .best_encoder = mtk_dsi_connector_best_encoder, | ||
| 607 | }; | ||
| 608 | |||
| 609 | static int mtk_drm_attach_bridge(struct drm_bridge *bridge, | ||
| 610 | struct drm_encoder *encoder) | ||
| 611 | { | ||
| 612 | int ret; | ||
| 613 | |||
| 614 | if (!bridge) | ||
| 615 | return -ENOENT; | ||
| 616 | |||
| 617 | encoder->bridge = bridge; | ||
| 618 | bridge->encoder = encoder; | ||
| 619 | ret = drm_bridge_attach(encoder->dev, bridge); | ||
| 620 | if (ret) { | ||
| 621 | DRM_ERROR("Failed to attach bridge to drm\n"); | ||
| 622 | encoder->bridge = NULL; | ||
| 623 | bridge->encoder = NULL; | ||
| 624 | } | ||
| 625 | |||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | |||
| 629 | static int mtk_dsi_create_connector(struct drm_device *drm, struct mtk_dsi *dsi) | ||
| 630 | { | ||
| 631 | int ret; | ||
| 632 | |||
| 633 | ret = drm_connector_init(drm, &dsi->conn, &mtk_dsi_connector_funcs, | ||
| 634 | DRM_MODE_CONNECTOR_DSI); | ||
| 635 | if (ret) { | ||
| 636 | DRM_ERROR("Failed to connector init to drm\n"); | ||
| 637 | return ret; | ||
| 638 | } | ||
| 639 | |||
| 640 | drm_connector_helper_add(&dsi->conn, &mtk_dsi_connector_helper_funcs); | ||
| 641 | |||
| 642 | dsi->conn.dpms = DRM_MODE_DPMS_OFF; | ||
| 643 | drm_mode_connector_attach_encoder(&dsi->conn, &dsi->encoder); | ||
| 644 | |||
| 645 | if (dsi->panel) { | ||
| 646 | ret = drm_panel_attach(dsi->panel, &dsi->conn); | ||
| 647 | if (ret) { | ||
| 648 | DRM_ERROR("Failed to attach panel to drm\n"); | ||
| 649 | goto err_connector_cleanup; | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | return 0; | ||
| 654 | |||
| 655 | err_connector_cleanup: | ||
| 656 | drm_connector_cleanup(&dsi->conn); | ||
| 657 | return ret; | ||
| 658 | } | ||
| 659 | |||
| 660 | static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi) | ||
| 661 | { | ||
| 662 | int ret; | ||
| 663 | |||
| 664 | ret = drm_encoder_init(drm, &dsi->encoder, &mtk_dsi_encoder_funcs, | ||
| 665 | DRM_MODE_ENCODER_DSI, NULL); | ||
| 666 | if (ret) { | ||
| 667 | DRM_ERROR("Failed to encoder init to drm\n"); | ||
| 668 | return ret; | ||
| 669 | } | ||
| 670 | drm_encoder_helper_add(&dsi->encoder, &mtk_dsi_encoder_helper_funcs); | ||
| 671 | |||
| 672 | /* | ||
| 673 | * Currently display data paths are statically assigned to a crtc each. | ||
| 674 | * crtc 0 is OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 | ||
| 675 | */ | ||
| 676 | dsi->encoder.possible_crtcs = 1; | ||
| 677 | |||
| 678 | /* If there's a bridge, attach to it and let it create the connector */ | ||
| 679 | ret = mtk_drm_attach_bridge(dsi->bridge, &dsi->encoder); | ||
| 680 | if (ret) { | ||
| 681 | /* Otherwise create our own connector and attach to a panel */ | ||
| 682 | ret = mtk_dsi_create_connector(drm, dsi); | ||
| 683 | if (ret) | ||
| 684 | goto err_encoder_cleanup; | ||
| 685 | } | ||
| 686 | |||
| 687 | return 0; | ||
| 688 | |||
| 689 | err_encoder_cleanup: | ||
| 690 | drm_encoder_cleanup(&dsi->encoder); | ||
| 691 | return ret; | ||
| 692 | } | ||
| 693 | |||
| 694 | static void mtk_dsi_destroy_conn_enc(struct mtk_dsi *dsi) | ||
| 695 | { | ||
| 696 | drm_encoder_cleanup(&dsi->encoder); | ||
| 697 | /* Skip connector cleanup if creation was delegated to the bridge */ | ||
| 698 | if (dsi->conn.dev) { | ||
| 699 | drm_connector_unregister(&dsi->conn); | ||
| 700 | drm_connector_cleanup(&dsi->conn); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | static void mtk_dsi_ddp_start(struct mtk_ddp_comp *comp) | ||
| 705 | { | ||
| 706 | struct mtk_dsi *dsi = container_of(comp, struct mtk_dsi, ddp_comp); | ||
| 707 | |||
| 708 | mtk_dsi_poweron(dsi); | ||
| 709 | } | ||
| 710 | |||
| 711 | static void mtk_dsi_ddp_stop(struct mtk_ddp_comp *comp) | ||
| 712 | { | ||
| 713 | struct mtk_dsi *dsi = container_of(comp, struct mtk_dsi, ddp_comp); | ||
| 714 | |||
| 715 | mtk_dsi_poweroff(dsi); | ||
| 716 | } | ||
| 717 | |||
| 718 | static const struct mtk_ddp_comp_funcs mtk_dsi_funcs = { | ||
| 719 | .start = mtk_dsi_ddp_start, | ||
| 720 | .stop = mtk_dsi_ddp_stop, | ||
| 721 | }; | ||
| 722 | |||
| 723 | static int mtk_dsi_host_attach(struct mipi_dsi_host *host, | ||
| 724 | struct mipi_dsi_device *device) | ||
| 725 | { | ||
| 726 | struct mtk_dsi *dsi = host_to_dsi(host); | ||
| 727 | |||
| 728 | dsi->lanes = device->lanes; | ||
| 729 | dsi->format = device->format; | ||
| 730 | dsi->mode_flags = device->mode_flags; | ||
| 731 | |||
| 732 | if (dsi->conn.dev) | ||
| 733 | drm_helper_hpd_irq_event(dsi->conn.dev); | ||
| 734 | |||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | |||
| 738 | static int mtk_dsi_host_detach(struct mipi_dsi_host *host, | ||
| 739 | struct mipi_dsi_device *device) | ||
| 740 | { | ||
| 741 | struct mtk_dsi *dsi = host_to_dsi(host); | ||
| 742 | |||
| 743 | if (dsi->conn.dev) | ||
| 744 | drm_helper_hpd_irq_event(dsi->conn.dev); | ||
| 745 | |||
| 746 | return 0; | ||
| 747 | } | ||
| 748 | |||
| 749 | static const struct mipi_dsi_host_ops mtk_dsi_ops = { | ||
| 750 | .attach = mtk_dsi_host_attach, | ||
| 751 | .detach = mtk_dsi_host_detach, | ||
| 752 | }; | ||
| 753 | |||
| 754 | static int mtk_dsi_bind(struct device *dev, struct device *master, void *data) | ||
| 755 | { | ||
| 756 | int ret; | ||
| 757 | struct drm_device *drm = data; | ||
| 758 | struct mtk_dsi *dsi = dev_get_drvdata(dev); | ||
| 759 | |||
| 760 | ret = mtk_ddp_comp_register(drm, &dsi->ddp_comp); | ||
| 761 | if (ret < 0) { | ||
| 762 | dev_err(dev, "Failed to register component %s: %d\n", | ||
| 763 | dev->of_node->full_name, ret); | ||
| 764 | return ret; | ||
| 765 | } | ||
| 766 | |||
| 767 | ret = mipi_dsi_host_register(&dsi->host); | ||
| 768 | if (ret < 0) { | ||
| 769 | dev_err(dev, "failed to register DSI host: %d\n", ret); | ||
| 770 | goto err_ddp_comp_unregister; | ||
| 771 | } | ||
| 772 | |||
| 773 | ret = mtk_dsi_create_conn_enc(drm, dsi); | ||
| 774 | if (ret) { | ||
| 775 | DRM_ERROR("Encoder create failed with %d\n", ret); | ||
| 776 | goto err_unregister; | ||
| 777 | } | ||
| 778 | |||
| 779 | return 0; | ||
| 780 | |||
| 781 | err_unregister: | ||
| 782 | mipi_dsi_host_unregister(&dsi->host); | ||
| 783 | err_ddp_comp_unregister: | ||
| 784 | mtk_ddp_comp_unregister(drm, &dsi->ddp_comp); | ||
| 785 | return ret; | ||
| 786 | } | ||
| 787 | |||
| 788 | static void mtk_dsi_unbind(struct device *dev, struct device *master, | ||
| 789 | void *data) | ||
| 790 | { | ||
| 791 | struct drm_device *drm = data; | ||
| 792 | struct mtk_dsi *dsi = dev_get_drvdata(dev); | ||
| 793 | |||
| 794 | mtk_dsi_destroy_conn_enc(dsi); | ||
| 795 | mipi_dsi_host_unregister(&dsi->host); | ||
| 796 | mtk_ddp_comp_unregister(drm, &dsi->ddp_comp); | ||
| 797 | } | ||
| 798 | |||
| 799 | static const struct component_ops mtk_dsi_component_ops = { | ||
| 800 | .bind = mtk_dsi_bind, | ||
| 801 | .unbind = mtk_dsi_unbind, | ||
| 802 | }; | ||
| 803 | |||
| 804 | static int mtk_dsi_probe(struct platform_device *pdev) | ||
| 805 | { | ||
| 806 | struct mtk_dsi *dsi; | ||
| 807 | struct device *dev = &pdev->dev; | ||
| 808 | struct device_node *remote_node, *endpoint; | ||
| 809 | struct resource *regs; | ||
| 810 | int comp_id; | ||
| 811 | int ret; | ||
| 812 | |||
| 813 | dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); | ||
| 814 | if (!dsi) | ||
| 815 | return -ENOMEM; | ||
| 816 | |||
| 817 | dsi->host.ops = &mtk_dsi_ops; | ||
| 818 | dsi->host.dev = dev; | ||
| 819 | |||
| 820 | endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); | ||
| 821 | if (endpoint) { | ||
| 822 | remote_node = of_graph_get_remote_port_parent(endpoint); | ||
| 823 | if (!remote_node) { | ||
| 824 | dev_err(dev, "No panel connected\n"); | ||
| 825 | return -ENODEV; | ||
| 826 | } | ||
| 827 | |||
| 828 | dsi->bridge = of_drm_find_bridge(remote_node); | ||
| 829 | dsi->panel = of_drm_find_panel(remote_node); | ||
| 830 | of_node_put(remote_node); | ||
| 831 | if (!dsi->bridge && !dsi->panel) { | ||
| 832 | dev_info(dev, "Waiting for bridge or panel driver\n"); | ||
| 833 | return -EPROBE_DEFER; | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | dsi->engine_clk = devm_clk_get(dev, "engine"); | ||
| 838 | if (IS_ERR(dsi->engine_clk)) { | ||
| 839 | ret = PTR_ERR(dsi->engine_clk); | ||
| 840 | dev_err(dev, "Failed to get engine clock: %d\n", ret); | ||
| 841 | return ret; | ||
| 842 | } | ||
| 843 | |||
| 844 | dsi->digital_clk = devm_clk_get(dev, "digital"); | ||
| 845 | if (IS_ERR(dsi->digital_clk)) { | ||
| 846 | ret = PTR_ERR(dsi->digital_clk); | ||
| 847 | dev_err(dev, "Failed to get digital clock: %d\n", ret); | ||
| 848 | return ret; | ||
| 849 | } | ||
| 850 | |||
| 851 | dsi->hs_clk = devm_clk_get(dev, "hs"); | ||
| 852 | if (IS_ERR(dsi->hs_clk)) { | ||
| 853 | ret = PTR_ERR(dsi->hs_clk); | ||
| 854 | dev_err(dev, "Failed to get hs clock: %d\n", ret); | ||
| 855 | return ret; | ||
| 856 | } | ||
| 857 | |||
| 858 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 859 | dsi->regs = devm_ioremap_resource(dev, regs); | ||
| 860 | if (IS_ERR(dsi->regs)) { | ||
| 861 | ret = PTR_ERR(dsi->regs); | ||
| 862 | dev_err(dev, "Failed to ioremap memory: %d\n", ret); | ||
| 863 | return ret; | ||
| 864 | } | ||
| 865 | |||
| 866 | dsi->phy = devm_phy_get(dev, "dphy"); | ||
| 867 | if (IS_ERR(dsi->phy)) { | ||
| 868 | ret = PTR_ERR(dsi->phy); | ||
| 869 | dev_err(dev, "Failed to get MIPI-DPHY: %d\n", ret); | ||
| 870 | return ret; | ||
| 871 | } | ||
| 872 | |||
| 873 | comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DSI); | ||
| 874 | if (comp_id < 0) { | ||
| 875 | dev_err(dev, "Failed to identify by alias: %d\n", comp_id); | ||
| 876 | return comp_id; | ||
| 877 | } | ||
| 878 | |||
| 879 | ret = mtk_ddp_comp_init(dev, dev->of_node, &dsi->ddp_comp, comp_id, | ||
| 880 | &mtk_dsi_funcs); | ||
| 881 | if (ret) { | ||
| 882 | dev_err(dev, "Failed to initialize component: %d\n", ret); | ||
| 883 | return ret; | ||
| 884 | } | ||
| 885 | |||
| 886 | platform_set_drvdata(pdev, dsi); | ||
| 887 | |||
| 888 | return component_add(&pdev->dev, &mtk_dsi_component_ops); | ||
| 889 | } | ||
| 890 | |||
| 891 | static int mtk_dsi_remove(struct platform_device *pdev) | ||
| 892 | { | ||
| 893 | struct mtk_dsi *dsi = platform_get_drvdata(pdev); | ||
| 894 | |||
| 895 | mtk_output_dsi_disable(dsi); | ||
| 896 | component_del(&pdev->dev, &mtk_dsi_component_ops); | ||
| 897 | |||
| 898 | return 0; | ||
| 899 | } | ||
| 900 | |||
| 901 | static const struct of_device_id mtk_dsi_of_match[] = { | ||
| 902 | { .compatible = "mediatek,mt8173-dsi" }, | ||
| 903 | { }, | ||
| 904 | }; | ||
| 905 | |||
| 906 | struct platform_driver mtk_dsi_driver = { | ||
| 907 | .probe = mtk_dsi_probe, | ||
| 908 | .remove = mtk_dsi_remove, | ||
| 909 | .driver = { | ||
| 910 | .name = "mtk-dsi", | ||
| 911 | .of_match_table = mtk_dsi_of_match, | ||
| 912 | }, | ||
| 913 | }; | ||
diff --git a/drivers/gpu/drm/mediatek/mtk_mipi_tx.c b/drivers/gpu/drm/mediatek/mtk_mipi_tx.c new file mode 100644 index 000000000000..cf8f38d39e10 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_mipi_tx.c | |||
| @@ -0,0 +1,463 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 MediaTek Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/clk.h> | ||
| 15 | #include <linux/clk-provider.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/phy/phy.h> | ||
| 21 | |||
| 22 | #define MIPITX_DSI_CON 0x00 | ||
| 23 | #define RG_DSI_LDOCORE_EN BIT(0) | ||
| 24 | #define RG_DSI_CKG_LDOOUT_EN BIT(1) | ||
| 25 | #define RG_DSI_BCLK_SEL (3 << 2) | ||
| 26 | #define RG_DSI_LD_IDX_SEL (7 << 4) | ||
| 27 | #define RG_DSI_PHYCLK_SEL (2 << 8) | ||
| 28 | #define RG_DSI_DSICLK_FREQ_SEL BIT(10) | ||
| 29 | #define RG_DSI_LPTX_CLMP_EN BIT(11) | ||
| 30 | |||
| 31 | #define MIPITX_DSI_CLOCK_LANE 0x04 | ||
| 32 | #define MIPITX_DSI_DATA_LANE0 0x08 | ||
| 33 | #define MIPITX_DSI_DATA_LANE1 0x0c | ||
| 34 | #define MIPITX_DSI_DATA_LANE2 0x10 | ||
| 35 | #define MIPITX_DSI_DATA_LANE3 0x14 | ||
| 36 | #define RG_DSI_LNTx_LDOOUT_EN BIT(0) | ||
| 37 | #define RG_DSI_LNTx_CKLANE_EN BIT(1) | ||
| 38 | #define RG_DSI_LNTx_LPTX_IPLUS1 BIT(2) | ||
| 39 | #define RG_DSI_LNTx_LPTX_IPLUS2 BIT(3) | ||
| 40 | #define RG_DSI_LNTx_LPTX_IMINUS BIT(4) | ||
| 41 | #define RG_DSI_LNTx_LPCD_IPLUS BIT(5) | ||
| 42 | #define RG_DSI_LNTx_LPCD_IMINUS BIT(6) | ||
| 43 | #define RG_DSI_LNTx_RT_CODE (0xf << 8) | ||
| 44 | |||
| 45 | #define MIPITX_DSI_TOP_CON 0x40 | ||
| 46 | #define RG_DSI_LNT_INTR_EN BIT(0) | ||
| 47 | #define RG_DSI_LNT_HS_BIAS_EN BIT(1) | ||
| 48 | #define RG_DSI_LNT_IMP_CAL_EN BIT(2) | ||
| 49 | #define RG_DSI_LNT_TESTMODE_EN BIT(3) | ||
| 50 | #define RG_DSI_LNT_IMP_CAL_CODE (0xf << 4) | ||
| 51 | #define RG_DSI_LNT_AIO_SEL (7 << 8) | ||
| 52 | #define RG_DSI_PAD_TIE_LOW_EN BIT(11) | ||
| 53 | #define RG_DSI_DEBUG_INPUT_EN BIT(12) | ||
| 54 | #define RG_DSI_PRESERVE (7 << 13) | ||
| 55 | |||
| 56 | #define MIPITX_DSI_BG_CON 0x44 | ||
| 57 | #define RG_DSI_BG_CORE_EN BIT(0) | ||
| 58 | #define RG_DSI_BG_CKEN BIT(1) | ||
| 59 | #define RG_DSI_BG_DIV (0x3 << 2) | ||
| 60 | #define RG_DSI_BG_FAST_CHARGE BIT(4) | ||
| 61 | #define RG_DSI_VOUT_MSK (0x3ffff << 5) | ||
| 62 | #define RG_DSI_V12_SEL (7 << 5) | ||
| 63 | #define RG_DSI_V10_SEL (7 << 8) | ||
| 64 | #define RG_DSI_V072_SEL (7 << 11) | ||
| 65 | #define RG_DSI_V04_SEL (7 << 14) | ||
| 66 | #define RG_DSI_V032_SEL (7 << 17) | ||
| 67 | #define RG_DSI_V02_SEL (7 << 20) | ||
| 68 | #define RG_DSI_BG_R1_TRIM (0xf << 24) | ||
| 69 | #define RG_DSI_BG_R2_TRIM (0xf << 28) | ||
| 70 | |||
| 71 | #define MIPITX_DSI_PLL_CON0 0x50 | ||
| 72 | #define RG_DSI_MPPLL_PLL_EN BIT(0) | ||
| 73 | #define RG_DSI_MPPLL_DIV_MSK (0x1ff << 1) | ||
| 74 | #define RG_DSI_MPPLL_PREDIV (3 << 1) | ||
| 75 | #define RG_DSI_MPPLL_TXDIV0 (3 << 3) | ||
| 76 | #define RG_DSI_MPPLL_TXDIV1 (3 << 5) | ||
| 77 | #define RG_DSI_MPPLL_POSDIV (7 << 7) | ||
| 78 | #define RG_DSI_MPPLL_MONVC_EN BIT(10) | ||
| 79 | #define RG_DSI_MPPLL_MONREF_EN BIT(11) | ||
| 80 | #define RG_DSI_MPPLL_VOD_EN BIT(12) | ||
| 81 | |||
| 82 | #define MIPITX_DSI_PLL_CON1 0x54 | ||
| 83 | #define RG_DSI_MPPLL_SDM_FRA_EN BIT(0) | ||
| 84 | #define RG_DSI_MPPLL_SDM_SSC_PH_INIT BIT(1) | ||
| 85 | #define RG_DSI_MPPLL_SDM_SSC_EN BIT(2) | ||
| 86 | #define RG_DSI_MPPLL_SDM_SSC_PRD (0xffff << 16) | ||
| 87 | |||
| 88 | #define MIPITX_DSI_PLL_CON2 0x58 | ||
| 89 | |||
| 90 | #define MIPITX_DSI_PLL_PWR 0x68 | ||
| 91 | #define RG_DSI_MPPLL_SDM_PWR_ON BIT(0) | ||
| 92 | #define RG_DSI_MPPLL_SDM_ISO_EN BIT(1) | ||
| 93 | #define RG_DSI_MPPLL_SDM_PWR_ACK BIT(8) | ||
| 94 | |||
| 95 | #define MIPITX_DSI_SW_CTRL 0x80 | ||
| 96 | #define SW_CTRL_EN BIT(0) | ||
| 97 | |||
| 98 | #define MIPITX_DSI_SW_CTRL_CON0 0x84 | ||
| 99 | #define SW_LNTC_LPTX_PRE_OE BIT(0) | ||
| 100 | #define SW_LNTC_LPTX_OE BIT(1) | ||
| 101 | #define SW_LNTC_LPTX_P BIT(2) | ||
| 102 | #define SW_LNTC_LPTX_N BIT(3) | ||
| 103 | #define SW_LNTC_HSTX_PRE_OE BIT(4) | ||
| 104 | #define SW_LNTC_HSTX_OE BIT(5) | ||
| 105 | #define SW_LNTC_HSTX_ZEROCLK BIT(6) | ||
| 106 | #define SW_LNT0_LPTX_PRE_OE BIT(7) | ||
| 107 | #define SW_LNT0_LPTX_OE BIT(8) | ||
| 108 | #define SW_LNT0_LPTX_P BIT(9) | ||
| 109 | #define SW_LNT0_LPTX_N BIT(10) | ||
| 110 | #define SW_LNT0_HSTX_PRE_OE BIT(11) | ||
| 111 | #define SW_LNT0_HSTX_OE BIT(12) | ||
| 112 | #define SW_LNT0_LPRX_EN BIT(13) | ||
| 113 | #define SW_LNT1_LPTX_PRE_OE BIT(14) | ||
| 114 | #define SW_LNT1_LPTX_OE BIT(15) | ||
| 115 | #define SW_LNT1_LPTX_P BIT(16) | ||
| 116 | #define SW_LNT1_LPTX_N BIT(17) | ||
| 117 | #define SW_LNT1_HSTX_PRE_OE BIT(18) | ||
| 118 | #define SW_LNT1_HSTX_OE BIT(19) | ||
| 119 | #define SW_LNT2_LPTX_PRE_OE BIT(20) | ||
| 120 | #define SW_LNT2_LPTX_OE BIT(21) | ||
| 121 | #define SW_LNT2_LPTX_P BIT(22) | ||
| 122 | #define SW_LNT2_LPTX_N BIT(23) | ||
| 123 | #define SW_LNT2_HSTX_PRE_OE BIT(24) | ||
| 124 | #define SW_LNT2_HSTX_OE BIT(25) | ||
| 125 | |||
| 126 | struct mtk_mipi_tx { | ||
| 127 | struct device *dev; | ||
| 128 | void __iomem *regs; | ||
| 129 | unsigned int data_rate; | ||
| 130 | struct clk_hw pll_hw; | ||
| 131 | struct clk *pll; | ||
| 132 | }; | ||
| 133 | |||
| 134 | static inline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw) | ||
| 135 | { | ||
| 136 | return container_of(hw, struct mtk_mipi_tx, pll_hw); | ||
| 137 | } | ||
| 138 | |||
| 139 | static void mtk_mipi_tx_clear_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, | ||
| 140 | u32 bits) | ||
| 141 | { | ||
| 142 | u32 temp = readl(mipi_tx->regs + offset); | ||
| 143 | |||
| 144 | writel(temp & ~bits, mipi_tx->regs + offset); | ||
| 145 | } | ||
| 146 | |||
| 147 | static void mtk_mipi_tx_set_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, | ||
| 148 | u32 bits) | ||
| 149 | { | ||
| 150 | u32 temp = readl(mipi_tx->regs + offset); | ||
| 151 | |||
| 152 | writel(temp | bits, mipi_tx->regs + offset); | ||
| 153 | } | ||
| 154 | |||
| 155 | static void mtk_mipi_tx_update_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, | ||
| 156 | u32 mask, u32 data) | ||
| 157 | { | ||
| 158 | u32 temp = readl(mipi_tx->regs + offset); | ||
| 159 | |||
| 160 | writel((temp & ~mask) | (data & mask), mipi_tx->regs + offset); | ||
| 161 | } | ||
| 162 | |||
| 163 | static int mtk_mipi_tx_pll_prepare(struct clk_hw *hw) | ||
| 164 | { | ||
| 165 | struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); | ||
| 166 | unsigned int txdiv, txdiv0, txdiv1; | ||
| 167 | u64 pcw; | ||
| 168 | |||
| 169 | dev_dbg(mipi_tx->dev, "prepare: %u Hz\n", mipi_tx->data_rate); | ||
| 170 | |||
| 171 | if (mipi_tx->data_rate >= 500000000) { | ||
| 172 | txdiv = 1; | ||
| 173 | txdiv0 = 0; | ||
| 174 | txdiv1 = 0; | ||
| 175 | } else if (mipi_tx->data_rate >= 250000000) { | ||
| 176 | txdiv = 2; | ||
| 177 | txdiv0 = 1; | ||
| 178 | txdiv1 = 0; | ||
| 179 | } else if (mipi_tx->data_rate >= 125000000) { | ||
| 180 | txdiv = 4; | ||
| 181 | txdiv0 = 2; | ||
| 182 | txdiv1 = 0; | ||
| 183 | } else if (mipi_tx->data_rate > 62000000) { | ||
| 184 | txdiv = 8; | ||
| 185 | txdiv0 = 2; | ||
| 186 | txdiv1 = 1; | ||
| 187 | } else if (mipi_tx->data_rate >= 50000000) { | ||
| 188 | txdiv = 16; | ||
| 189 | txdiv0 = 2; | ||
| 190 | txdiv1 = 2; | ||
| 191 | } else { | ||
| 192 | return -EINVAL; | ||
| 193 | } | ||
| 194 | |||
| 195 | mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_BG_CON, | ||
| 196 | RG_DSI_VOUT_MSK | | ||
| 197 | RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN, | ||
| 198 | (4 << 20) | (4 << 17) | (4 << 14) | | ||
| 199 | (4 << 11) | (4 << 8) | (4 << 5) | | ||
| 200 | RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN); | ||
| 201 | |||
| 202 | usleep_range(30, 100); | ||
| 203 | |||
| 204 | mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_TOP_CON, | ||
| 205 | RG_DSI_LNT_IMP_CAL_CODE | RG_DSI_LNT_HS_BIAS_EN, | ||
| 206 | (8 << 4) | RG_DSI_LNT_HS_BIAS_EN); | ||
| 207 | |||
| 208 | mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_CON, | ||
| 209 | RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN); | ||
| 210 | |||
| 211 | mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_PWR, | ||
| 212 | RG_DSI_MPPLL_SDM_PWR_ON | | ||
| 213 | RG_DSI_MPPLL_SDM_ISO_EN, | ||
| 214 | RG_DSI_MPPLL_SDM_PWR_ON); | ||
| 215 | |||
| 216 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0, | ||
| 217 | RG_DSI_MPPLL_PLL_EN); | ||
| 218 | |||
| 219 | mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_CON0, | ||
| 220 | RG_DSI_MPPLL_TXDIV0 | RG_DSI_MPPLL_TXDIV1 | | ||
| 221 | RG_DSI_MPPLL_PREDIV, | ||
| 222 | (txdiv0 << 3) | (txdiv1 << 5)); | ||
| 223 | |||
| 224 | /* | ||
| 225 | * PLL PCW config | ||
| 226 | * PCW bit 24~30 = integer part of pcw | ||
| 227 | * PCW bit 0~23 = fractional part of pcw | ||
| 228 | * pcw = data_Rate*4*txdiv/(Ref_clk*2); | ||
| 229 | * Post DIV =4, so need data_Rate*4 | ||
| 230 | * Ref_clk is 26MHz | ||
| 231 | */ | ||
| 232 | pcw = div_u64(((u64)mipi_tx->data_rate * 2 * txdiv) << 24, | ||
| 233 | 26000000); | ||
| 234 | writel(pcw, mipi_tx->regs + MIPITX_DSI_PLL_CON2); | ||
| 235 | |||
| 236 | mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_PLL_CON1, | ||
| 237 | RG_DSI_MPPLL_SDM_FRA_EN); | ||
| 238 | |||
| 239 | mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_PLL_CON0, RG_DSI_MPPLL_PLL_EN); | ||
| 240 | |||
| 241 | usleep_range(20, 100); | ||
| 242 | |||
| 243 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON1, | ||
| 244 | RG_DSI_MPPLL_SDM_SSC_EN); | ||
| 245 | |||
| 246 | return 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | static void mtk_mipi_tx_pll_unprepare(struct clk_hw *hw) | ||
| 250 | { | ||
| 251 | struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); | ||
| 252 | |||
| 253 | dev_dbg(mipi_tx->dev, "unprepare\n"); | ||
| 254 | |||
| 255 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0, | ||
| 256 | RG_DSI_MPPLL_PLL_EN); | ||
| 257 | |||
| 258 | mtk_mipi_tx_update_bits(mipi_tx, MIPITX_DSI_PLL_PWR, | ||
| 259 | RG_DSI_MPPLL_SDM_ISO_EN | | ||
| 260 | RG_DSI_MPPLL_SDM_PWR_ON, | ||
| 261 | RG_DSI_MPPLL_SDM_ISO_EN); | ||
| 262 | |||
| 263 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_TOP_CON, | ||
| 264 | RG_DSI_LNT_HS_BIAS_EN); | ||
| 265 | |||
| 266 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_CON, | ||
| 267 | RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN); | ||
| 268 | |||
| 269 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_BG_CON, | ||
| 270 | RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN); | ||
| 271 | |||
| 272 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_PLL_CON0, | ||
| 273 | RG_DSI_MPPLL_DIV_MSK); | ||
| 274 | } | ||
| 275 | |||
| 276 | static long mtk_mipi_tx_pll_round_rate(struct clk_hw *hw, unsigned long rate, | ||
| 277 | unsigned long *prate) | ||
| 278 | { | ||
| 279 | return clamp_val(rate, 50000000, 1250000000); | ||
| 280 | } | ||
| 281 | |||
| 282 | static int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, | ||
| 283 | unsigned long parent_rate) | ||
| 284 | { | ||
| 285 | struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); | ||
| 286 | |||
| 287 | dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); | ||
| 288 | |||
| 289 | mipi_tx->data_rate = rate; | ||
| 290 | |||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | static unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, | ||
| 295 | unsigned long parent_rate) | ||
| 296 | { | ||
| 297 | struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); | ||
| 298 | |||
| 299 | return mipi_tx->data_rate; | ||
| 300 | } | ||
| 301 | |||
| 302 | static const struct clk_ops mtk_mipi_tx_pll_ops = { | ||
| 303 | .prepare = mtk_mipi_tx_pll_prepare, | ||
| 304 | .unprepare = mtk_mipi_tx_pll_unprepare, | ||
| 305 | .round_rate = mtk_mipi_tx_pll_round_rate, | ||
| 306 | .set_rate = mtk_mipi_tx_pll_set_rate, | ||
| 307 | .recalc_rate = mtk_mipi_tx_pll_recalc_rate, | ||
| 308 | }; | ||
| 309 | |||
| 310 | static int mtk_mipi_tx_power_on_signal(struct phy *phy) | ||
| 311 | { | ||
| 312 | struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); | ||
| 313 | unsigned int reg; | ||
| 314 | |||
| 315 | for (reg = MIPITX_DSI_CLOCK_LANE; | ||
| 316 | reg <= MIPITX_DSI_DATA_LANE3; reg += 4) | ||
| 317 | mtk_mipi_tx_set_bits(mipi_tx, reg, RG_DSI_LNTx_LDOOUT_EN); | ||
| 318 | |||
| 319 | mtk_mipi_tx_clear_bits(mipi_tx, MIPITX_DSI_TOP_CON, | ||
| 320 | RG_DSI_PAD_TIE_LOW_EN); | ||
| 321 | |||
| 322 | return 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | static int mtk_mipi_tx_power_on(struct phy *phy) | ||
| 326 | { | ||
| 327 | struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); | ||
| 328 | int ret; | ||
| 329 | |||
| 330 | /* Power up core and enable PLL */ | ||
| 331 | ret = clk_prepare_enable(mipi_tx->pll); | ||
| 332 | if (ret < 0) | ||
| 333 | return ret; | ||
| 334 | |||
| 335 | /* Enable DSI Lane LDO outputs, disable pad tie low */ | ||
| 336 | mtk_mipi_tx_power_on_signal(phy); | ||
| 337 | |||
| 338 | return 0; | ||
| 339 | } | ||
| 340 | |||
| 341 | static void mtk_mipi_tx_power_off_signal(struct phy *phy) | ||
| 342 | { | ||
| 343 | struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); | ||
| 344 | unsigned int reg; | ||
| 345 | |||
| 346 | mtk_mipi_tx_set_bits(mipi_tx, MIPITX_DSI_TOP_CON, | ||
| 347 | RG_DSI_PAD_TIE_LOW_EN); | ||
| 348 | |||
| 349 | for (reg = MIPITX_DSI_CLOCK_LANE; | ||
| 350 | reg <= MIPITX_DSI_DATA_LANE3; reg += 4) | ||
| 351 | mtk_mipi_tx_clear_bits(mipi_tx, reg, RG_DSI_LNTx_LDOOUT_EN); | ||
| 352 | } | ||
| 353 | |||
| 354 | static int mtk_mipi_tx_power_off(struct phy *phy) | ||
| 355 | { | ||
| 356 | struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); | ||
| 357 | |||
| 358 | /* Enable pad tie low, disable DSI Lane LDO outputs */ | ||
| 359 | mtk_mipi_tx_power_off_signal(phy); | ||
| 360 | |||
| 361 | /* Disable PLL and power down core */ | ||
| 362 | clk_disable_unprepare(mipi_tx->pll); | ||
| 363 | |||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | |||
| 367 | static const struct phy_ops mtk_mipi_tx_ops = { | ||
| 368 | .power_on = mtk_mipi_tx_power_on, | ||
| 369 | .power_off = mtk_mipi_tx_power_off, | ||
| 370 | .owner = THIS_MODULE, | ||
| 371 | }; | ||
| 372 | |||
| 373 | static int mtk_mipi_tx_probe(struct platform_device *pdev) | ||
| 374 | { | ||
| 375 | struct device *dev = &pdev->dev; | ||
| 376 | struct mtk_mipi_tx *mipi_tx; | ||
| 377 | struct resource *mem; | ||
| 378 | struct clk *ref_clk; | ||
| 379 | const char *ref_clk_name; | ||
| 380 | struct clk_init_data clk_init = { | ||
| 381 | .ops = &mtk_mipi_tx_pll_ops, | ||
| 382 | .num_parents = 1, | ||
| 383 | .parent_names = (const char * const *)&ref_clk_name, | ||
| 384 | .flags = CLK_SET_RATE_GATE, | ||
| 385 | }; | ||
| 386 | struct phy *phy; | ||
| 387 | struct phy_provider *phy_provider; | ||
| 388 | int ret; | ||
| 389 | |||
| 390 | mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL); | ||
| 391 | if (!mipi_tx) | ||
| 392 | return -ENOMEM; | ||
| 393 | |||
| 394 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 395 | mipi_tx->regs = devm_ioremap_resource(dev, mem); | ||
| 396 | if (IS_ERR(mipi_tx->regs)) { | ||
| 397 | ret = PTR_ERR(mipi_tx->regs); | ||
| 398 | dev_err(dev, "Failed to get memory resource: %d\n", ret); | ||
| 399 | return ret; | ||
| 400 | } | ||
| 401 | |||
| 402 | ref_clk = devm_clk_get(dev, NULL); | ||
| 403 | if (IS_ERR(ref_clk)) { | ||
| 404 | ret = PTR_ERR(ref_clk); | ||
| 405 | dev_err(dev, "Failed to get reference clock: %d\n", ret); | ||
| 406 | return ret; | ||
| 407 | } | ||
| 408 | ref_clk_name = __clk_get_name(ref_clk); | ||
| 409 | |||
| 410 | ret = of_property_read_string(dev->of_node, "clock-output-names", | ||
| 411 | &clk_init.name); | ||
| 412 | if (ret < 0) { | ||
| 413 | dev_err(dev, "Failed to read clock-output-names: %d\n", ret); | ||
| 414 | return ret; | ||
| 415 | } | ||
| 416 | |||
| 417 | mipi_tx->pll_hw.init = &clk_init; | ||
| 418 | mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw); | ||
| 419 | if (IS_ERR(mipi_tx->pll)) { | ||
| 420 | ret = PTR_ERR(mipi_tx->pll); | ||
| 421 | dev_err(dev, "Failed to register PLL: %d\n", ret); | ||
| 422 | return ret; | ||
| 423 | } | ||
| 424 | |||
| 425 | phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); | ||
| 426 | if (IS_ERR(phy)) { | ||
| 427 | ret = PTR_ERR(phy); | ||
| 428 | dev_err(dev, "Failed to create MIPI D-PHY: %d\n", ret); | ||
| 429 | return ret; | ||
| 430 | } | ||
| 431 | phy_set_drvdata(phy, mipi_tx); | ||
| 432 | |||
| 433 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
| 434 | if (IS_ERR(phy)) { | ||
| 435 | ret = PTR_ERR(phy_provider); | ||
| 436 | return ret; | ||
| 437 | } | ||
| 438 | |||
| 439 | mipi_tx->dev = dev; | ||
| 440 | |||
| 441 | return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, | ||
| 442 | mipi_tx->pll); | ||
| 443 | } | ||
| 444 | |||
| 445 | static int mtk_mipi_tx_remove(struct platform_device *pdev) | ||
| 446 | { | ||
| 447 | of_clk_del_provider(pdev->dev.of_node); | ||
| 448 | return 0; | ||
| 449 | } | ||
| 450 | |||
| 451 | static const struct of_device_id mtk_mipi_tx_match[] = { | ||
| 452 | { .compatible = "mediatek,mt8173-mipi-tx", }, | ||
| 453 | {}, | ||
| 454 | }; | ||
| 455 | |||
| 456 | struct platform_driver mtk_mipi_tx_driver = { | ||
| 457 | .probe = mtk_mipi_tx_probe, | ||
| 458 | .remove = mtk_mipi_tx_remove, | ||
| 459 | .driver = { | ||
| 460 | .name = "mediatek-mipi-tx", | ||
| 461 | .of_match_table = mtk_mipi_tx_match, | ||
| 462 | }, | ||
| 463 | }; | ||
diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 089091f5f890..f6b57579185a 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c | |||
| @@ -91,6 +91,7 @@ int mtk_smi_larb_get(struct device *larbdev) | |||
| 91 | 91 | ||
| 92 | return 0; | 92 | return 0; |
| 93 | } | 93 | } |
| 94 | EXPORT_SYMBOL_GPL(mtk_smi_larb_get); | ||
| 94 | 95 | ||
| 95 | void mtk_smi_larb_put(struct device *larbdev) | 96 | void mtk_smi_larb_put(struct device *larbdev) |
| 96 | { | 97 | { |
| @@ -106,6 +107,7 @@ void mtk_smi_larb_put(struct device *larbdev) | |||
| 106 | mtk_smi_disable(&larb->smi); | 107 | mtk_smi_disable(&larb->smi); |
| 107 | mtk_smi_disable(common); | 108 | mtk_smi_disable(common); |
| 108 | } | 109 | } |
| 110 | EXPORT_SYMBOL_GPL(mtk_smi_larb_put); | ||
| 109 | 111 | ||
| 110 | static int | 112 | static int |
| 111 | mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) | 113 | mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) |
diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h index 7956ba1bc974..6094bf7e50ab 100644 --- a/include/dt-bindings/clock/mt8173-clk.h +++ b/include/dt-bindings/clock/mt8173-clk.h | |||
| @@ -176,7 +176,8 @@ | |||
| 176 | #define CLK_APMIXED_LVDSPLL 13 | 176 | #define CLK_APMIXED_LVDSPLL 13 |
| 177 | #define CLK_APMIXED_MSDCPLL2 14 | 177 | #define CLK_APMIXED_MSDCPLL2 14 |
| 178 | #define CLK_APMIXED_REF2USB_TX 15 | 178 | #define CLK_APMIXED_REF2USB_TX 15 |
| 179 | #define CLK_APMIXED_NR_CLK 16 | 179 | #define CLK_APMIXED_HDMI_REF 16 |
| 180 | #define CLK_APMIXED_NR_CLK 17 | ||
| 180 | 181 | ||
| 181 | /* INFRA_SYS */ | 182 | /* INFRA_SYS */ |
| 182 | 183 | ||
