aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2016-05-10 01:01:47 -0400
committerDave Airlie <airlied@redhat.com>2016-05-10 01:01:47 -0400
commit2e726dc4b4e2dd3ae3fe675f9d3af88a2d593ee1 (patch)
treebb22392b580c3c58e9973c4a50133d580f861f98
parentbafb86f5bc3173479002555dea7f31d943b12332 (diff)
parentac4b1280319c3032787ac95bfeff14a425c417bf (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
-rw-r--r--Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt203
-rw-r--r--Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt35
-rw-r--r--Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt60
-rw-r--r--drivers/clk/mediatek/clk-mt8173.c12
-rw-r--r--drivers/clk/mediatek/clk-mtk.h15
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig16
-rw-r--r--drivers/gpu/drm/mediatek/Makefile14
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl.c302
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_rdma.c240
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi.c769
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dpi_regs.h228
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.c582
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_crtc.h32
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp.c353
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp.h41
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c225
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h150
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c567
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.h60
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_fb.c165
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_fb.h23
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.c269
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_gem.h59
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.c240
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_plane.h59
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c913
-rw-r--r--drivers/gpu/drm/mediatek/mtk_mipi_tx.c463
-rw-r--r--drivers/memory/mtk-smi.c2
-rw-r--r--include/dt-bindings/clock/mt8173-clk.h3
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 @@
1Mediatek display subsystem
2==========================
3
4The Mediatek display subsystem consists of various DISP function blocks in the
5MMSYS register space. The connections between them can be configured by output
6and input selectors in the MMSYS_CONFIG register space. Pixel clock and start
7of frame signal are distributed to the other function blocks by a DISP_MUTEX
8function block.
9
10All DISP device tree nodes must be siblings to the central MMSYS_CONFIG node.
11For a description of the MMSYS_CONFIG binding, see
12Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt.
13
14DISP function blocks
15====================
16
17A display stream starts at a source function block that reads pixel data from
18memory and ends with a sink function block that drives pixels on a display
19interface, or writes pixels back to memory. All DISP function blocks have
20their own register space, interrupt, and clock gate. The blocks that can
21access memory additionally have to list the IOMMU and local arbiter they are
22connected to.
23
24For a description of the display interface sink function blocks, see
25Documentation/devicetree/bindings/display/mediatek/mediatek,dsi.txt and
26Documentation/devicetree/bindings/display/mediatek/mediatek,dpi.txt.
27
28Required 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
52Required 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
63Examples:
64
65mmsys: 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
72ovl0: 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
82ovl1: 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
92rdma0: 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
102rdma1: 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
112rdma2: 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
122wdma0: 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
132wdma1: 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
142color0: 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
150color1: 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
158aal@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
166gamma@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
174ufoe@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
182dsi0: dsi@1401b000 {
183 /* See mediatek,dsi.txt for details */
184};
185
186dpi0: dpi@1401d000 {
187 /* See mediatek,dpi.txt for details */
188};
189
190mutex: 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
198od@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 @@
1Mediatek DPI Device
2===================
3
4The Mediatek DPI function block is a sink of the display subsystem and
5provides 8-bit RGB/YUV444 or 8/10/10-bit YUV422 pixel data on a parallel
6output bus.
7
8Required 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
19Example:
20
21dpi0: 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 @@
1Mediatek DSI Device
2===================
3
4The Mediatek DSI function block is a sink of the display subsystem and can
5drive up to 4-lane MIPI DSI output. Two DSIs can be synchronized for dual-
6channel output.
7
8Required 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
21MIPI TX Configuration Module
22============================
23
24The MIPI TX configuration module controls the MIPI D-PHY.
25
26Required 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
34Example:
35
36mipi_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
45dsi0: 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"
288source "drivers/gpu/drm/arc/Kconfig" 288source "drivers/gpu/drm/arc/Kconfig"
289 289
290source "drivers/gpu/drm/hisilicon/Kconfig" 290source "drivers/gpu/drm/hisilicon/Kconfig"
291
292source "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/
74obj-$(CONFIG_DRM_TEGRA) += tegra/ 74obj-$(CONFIG_DRM_TEGRA) += tegra/
75obj-$(CONFIG_DRM_STI) += sti/ 75obj-$(CONFIG_DRM_STI) += sti/
76obj-$(CONFIG_DRM_IMX) += imx/ 76obj-$(CONFIG_DRM_IMX) += imx/
77obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
77obj-y += i2c/ 78obj-y += i2c/
78obj-y += panel/ 79obj-y += panel/
79obj-y += bridge/ 80obj-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 @@
1config 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 @@
1mediatek-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
14obj-$(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 */
55struct mtk_disp_ovl {
56 struct mtk_ddp_comp ddp_comp;
57 struct drm_crtc *crtc;
58};
59
60static 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
76static 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
86static 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
95static void mtk_ovl_start(struct mtk_ddp_comp *comp)
96{
97 writel_relaxed(0x1, comp->regs + DISP_REG_OVL_EN);
98}
99
100static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
101{
102 writel_relaxed(0x0, comp->regs + DISP_REG_OVL_EN);
103}
104
105static 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
116static 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
128static 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
139static 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
166static 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
194static 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
205static 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
222static 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
231static const struct component_ops mtk_disp_ovl_component_ops = {
232 .bind = mtk_disp_ovl_bind,
233 .unbind = mtk_disp_ovl_unbind,
234};
235
236static 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
281static 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
288static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
289 { .compatible = "mediatek,mt8173-disp-ovl", },
290 {},
291};
292MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
293
294struct 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 */
47struct mtk_disp_rdma {
48 struct mtk_ddp_comp ddp_comp;
49 struct drm_crtc *crtc;
50};
51
52static 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
68static 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
77static 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
88static 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
97static 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
103static 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
108static 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
130static 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
138static 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
156static 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
165static const struct component_ops mtk_disp_rdma_component_ops = {
166 .bind = mtk_disp_rdma_bind,
167 .unbind = mtk_disp_rdma_unbind,
168};
169
170static 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
219static 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
226static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
227 { .compatible = "mediatek,mt8173-disp-rdma", },
228 {},
229};
230MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
231
232struct 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
29enum 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
36enum 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
44enum 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
53enum 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
63struct 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
81static inline struct mtk_dpi *mtk_dpi_from_encoder(struct drm_encoder *e)
82{
83 return container_of(e, struct mtk_dpi, encoder);
84}
85
86enum mtk_dpi_polarity {
87 MTK_DPI_POLARITY_RISING,
88 MTK_DPI_POLARITY_FALLING,
89};
90
91enum mtk_dpi_power_ctl {
92 DPI_POWER_START = BIT(0),
93 DPI_POWER_ENABLE = BIT(1),
94};
95
96struct 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
103struct mtk_dpi_sync_param {
104 u32 sync_width;
105 u32 front_porch;
106 u32 back_porch;
107 bool shift_half_line;
108};
109
110struct mtk_dpi_yc_limit {
111 u16 y_top;
112 u16 y_bottom;
113 u16 c_top;
114 u16 c_bottom;
115};
116
117static 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
125static 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
130static void mtk_dpi_enable(struct mtk_dpi *dpi)
131{
132 mtk_dpi_mask(dpi, DPI_EN, EN, EN);
133}
134
135static void mtk_dpi_disable(struct mtk_dpi *dpi)
136{
137 mtk_dpi_mask(dpi, DPI_EN, 0, EN);
138}
139
140static 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
151static 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
169static 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
175static 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
182static 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
189static 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
196static 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
209static 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
214static 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
219static 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
225static 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
238static 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
264static 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
293static 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
325static 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
330static 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
335static 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
340static 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
345static 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
368static 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
385static 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
414err_pixel:
415 clk_disable_unprepare(dpi->engine_clk);
416err_eng:
417 dpi->power_ctl &= ~pctl;
418 return ret;
419}
420
421static 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
525static void mtk_dpi_encoder_destroy(struct drm_encoder *encoder)
526{
527 drm_encoder_cleanup(encoder);
528}
529
530static const struct drm_encoder_funcs mtk_dpi_encoder_funcs = {
531 .destroy = mtk_dpi_encoder_destroy,
532};
533
534static 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
541static 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
550static 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
557static 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
565static 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
572static 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
580static 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
587static 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
594static const struct mtk_ddp_comp_funcs mtk_dpi_funcs = {
595 .start = mtk_dpi_start,
596 .stop = mtk_dpi_stop,
597};
598
599static 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
637err_cleanup:
638 drm_encoder_cleanup(&dpi->encoder);
639err_unregister:
640 mtk_ddp_comp_unregister(drm_dev, &dpi->ddp_comp);
641 return ret;
642}
643
644static 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
654static const struct component_ops mtk_dpi_component_ops = {
655 .bind = mtk_dpi_bind,
656 .unbind = mtk_dpi_unbind,
657};
658
659static 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
750static int mtk_dpi_remove(struct platform_device *pdev)
751{
752 component_del(&pdev->dev, &mtk_dpi_component_ops);
753
754 return 0;
755}
756
757static const struct of_device_id mtk_dpi_of_ids[] = {
758 { .compatible = "mediatek,mt8173-dpi", },
759 {}
760};
761
762struct 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 */
41struct 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
57struct 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
66static 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
71static 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
76static 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
88static 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
97static 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
110static 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
130static 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
146static 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
153static 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
161static 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
172int 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
183void 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
192static 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;
207err:
208 while (--i >= 0)
209 clk_disable(mtk_crtc->ddp_comp[i]->clk);
210 return ret;
211}
212
213static 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
222static 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
285err_mutex_unprepare:
286 mtk_disp_mutex_unprepare(mtk_crtc->mutex);
287err_pm_runtime_put:
288 pm_runtime_put(crtc->dev->dev);
289 return ret;
290}
291
292static 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
318static 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
342static 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
373static 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
390static 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
414static 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
423static 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
432static 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
448err_cleanup_crtc:
449 drm_crtc_cleanup(&mtk_crtc->base);
450 return ret;
451}
452
453void 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
490int 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
577unprepare:
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
23int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
24void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
25void mtk_drm_crtc_check_flush(struct drm_crtc *crtc);
26void mtk_drm_crtc_commit(struct drm_crtc *crtc);
27void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl);
28int 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
70struct mtk_disp_mutex {
71 int id;
72 bool claimed;
73};
74
75struct mtk_ddp {
76 struct device *dev;
77 struct clk *clk;
78 void __iomem *regs;
79 struct mtk_disp_mutex mutex[10];
80};
81
82static 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
100static 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
131static 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
153void 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
172void 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
191struct 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
205void 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
215int 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
222void 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
229void 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
258void 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
282void 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
292void 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
302static 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
334static int mtk_ddp_remove(struct platform_device *pdev)
335{
336 return 0;
337}
338
339static const struct of_device_id ddp_driver_dt_match[] = {
340 { .compatible = "mediatek,mt8173-disp-mutex" },
341 {},
342};
343MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
344
345struct 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
19struct regmap;
20struct device;
21struct mtk_disp_mutex;
22
23void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
24 enum mtk_ddp_comp_id cur,
25 enum mtk_ddp_comp_id next);
26void 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
30struct mtk_disp_mutex *mtk_disp_mutex_get(struct device *dev, unsigned int id);
31int mtk_disp_mutex_prepare(struct mtk_disp_mutex *mutex);
32void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
33 enum mtk_ddp_comp_id id);
34void mtk_disp_mutex_enable(struct mtk_disp_mutex *mutex);
35void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex);
36void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
37 enum mtk_ddp_comp_id id);
38void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
39void 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
48static 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
55static 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
62static 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
68static 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
74static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
75{
76 writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
77}
78
79static const struct mtk_ddp_comp_funcs ddp_color = {
80 .config = mtk_color_config,
81 .start = mtk_color_start,
82};
83
84static const struct mtk_ddp_comp_funcs ddp_od = {
85 .config = mtk_od_config,
86 .start = mtk_od_start,
87};
88
89static const struct mtk_ddp_comp_funcs ddp_ufoe = {
90 .start = mtk_ufoe_start,
91};
92
93static 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
108struct 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
114static 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
134int 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
149int 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
209int 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
220void 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
19struct device;
20struct device_node;
21struct drm_crtc;
22struct drm_device;
23struct mtk_plane_state;
24
25enum 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
41enum 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
63struct mtk_ddp_comp;
64
65struct 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
78struct 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
87static 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
95static 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
101static 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
107static 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
114static 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
120static 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
127static 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
134static 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
142int mtk_ddp_comp_get_id(struct device_node *node,
143 enum mtk_ddp_comp_type comp_type);
144int 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);
147int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp);
148void 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
40static 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
47static 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
57static 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
72static 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
80static 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
106static 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
112static 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
123static 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
131static 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
208err_component_unbind:
209 component_unbind_all(drm->dev, drm);
210err_config_cleanup:
211 drm_mode_config_cleanup(drm);
212
213 return ret;
214}
215
216static 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
225static 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
238static 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
268static int compare_of(struct device *dev, void *data)
269{
270 return dev->of_node == data;
271}
272
273static 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
302err_unregister:
303 drm_dev_unregister(drm);
304err_deinit:
305 mtk_drm_kms_deinit(drm);
306err_free:
307 drm_dev_unref(drm);
308 return ret;
309}
310
311static 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
319static const struct component_master_ops mtk_drm_ops = {
320 .bind = mtk_drm_bind,
321 .unbind = mtk_drm_unbind,
322};
323
324static 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
340static 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
443err_pm:
444 pm_runtime_disable(dev);
445err_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
452static 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
473static 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
490static 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
503static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
504 mtk_drm_sys_resume);
505
506static const struct of_device_id mtk_drm_of_ids[] = {
507 { .compatible = "mediatek,mt8173-mmsys", },
508 { }
509};
510
511static 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
521static 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
531static 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
547err:
548 while (--i >= 0)
549 platform_driver_unregister(mtk_drm_drivers[i]);
550
551 return ret;
552}
553
554static 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
562module_init(mtk_drm_init);
563module_exit(mtk_drm_exit);
564
565MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
566MODULE_DESCRIPTION("Mediatek SoC DRM driver");
567MODULE_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
23struct device;
24struct device_node;
25struct drm_crtc;
26struct drm_device;
27struct drm_fb_helper;
28struct drm_property;
29struct regmap;
30
31struct 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
53extern struct platform_driver mtk_ddp_driver;
54extern struct platform_driver mtk_disp_ovl_driver;
55extern struct platform_driver mtk_disp_rdma_driver;
56extern struct platform_driver mtk_dpi_driver;
57extern struct platform_driver mtk_dsi_driver;
58extern 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 */
31struct 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
39struct 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
46static 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
55static 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
66static 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
71static 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 */
104int 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
127struct 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
162unreference:
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
17struct drm_gem_object *mtk_fb_get_gem_obj(struct drm_framebuffer *fb);
18int mtk_fb_wait(struct drm_framebuffer *fb);
19struct 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
21static 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
43struct 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
81err_gem_free:
82 drm_gem_object_release(obj);
83 kfree(mtk_gem);
84 return ERR_PTR(ret);
85}
86
87void 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
104int 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
130err_handle_create:
131 mtk_drm_gem_free_object(&mtk_gem->base);
132 return ret;
133}
134
135int 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
155out:
156 drm_gem_object_unreference_unlocked(obj);
157 return ret;
158}
159
160static 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
183int 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
194int 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 */
214struct 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
237struct 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
266err_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 */
33struct 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
44void mtk_drm_gem_free_object(struct drm_gem_object *gem);
45struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, size_t size,
46 bool alloc_kmap);
47int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
48 struct drm_mode_create_dumb *args);
49int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv,
50 struct drm_device *dev, uint32_t handle,
51 uint64_t *offset);
52int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
53int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj,
54 struct vm_area_struct *vma);
55struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj);
56struct 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
27static const u32 formats[] = {
28 DRM_FORMAT_XRGB8888,
29 DRM_FORMAT_ARGB8888,
30 DRM_FORMAT_RGB565,
31};
32
33static 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
77static 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
98static 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
116static 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
123static 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
132static 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
178static 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
206static 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
216static 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
222int 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
21struct mtk_drm_plane {
22 struct drm_plane base;
23 unsigned int idx;
24};
25
26struct 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
39struct mtk_plane_state {
40 struct drm_plane_state base;
41 struct mtk_plane_pending_state pending;
42};
43
44static 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
49static inline struct mtk_plane_state *
50to_mtk_plane_state(struct drm_plane_state *state)
51{
52 return container_of(state, struct mtk_plane_state, base);
53}
54
55int 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
111struct phy;
112
113struct 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
139static inline struct mtk_dsi *encoder_to_dsi(struct drm_encoder *e)
140{
141 return container_of(e, struct mtk_dsi, encoder);
142}
143
144static inline struct mtk_dsi *connector_to_dsi(struct drm_connector *c)
145{
146 return container_of(c, struct mtk_dsi, conn);
147}
148
149static inline struct mtk_dsi *host_to_dsi(struct mipi_dsi_host *h)
150{
151 return container_of(h, struct mtk_dsi, host);
152}
153
154static 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
161static 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
185static void mtk_dsi_enable(struct mtk_dsi *dsi)
186{
187 mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, DSI_EN);
188}
189
190static void mtk_dsi_disable(struct mtk_dsi *dsi)
191{
192 mtk_dsi_mask(dsi, DSI_CON_CTRL, DSI_EN, 0);
193}
194
195static 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
201static 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
243err_disable_engine_clk:
244 clk_disable_unprepare(dsi->engine_clk);
245err_phy_power_off:
246 phy_power_off(dsi->phy);
247err_refcount:
248 dsi->refcount--;
249 return ret;
250}
251
252static 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
258static 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
265static 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
271static 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
278static 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
286static 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
294static 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
309static 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
343static 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
368static 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
400static 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
437static 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
443static 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
462static 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
500static 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
517static void mtk_dsi_encoder_destroy(struct drm_encoder *encoder)
518{
519 drm_encoder_cleanup(encoder);
520}
521
522static const struct drm_encoder_funcs mtk_dsi_encoder_funcs = {
523 .destroy = mtk_dsi_encoder_destroy,
524};
525
526static 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
533static 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
551static 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
558static 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
565static enum drm_connector_status mtk_dsi_connector_detect(
566 struct drm_connector *connector, bool force)
567{
568 return connector_status_connected;
569}
570
571static 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
578static 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
586static 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
593static 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
603static 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
609static 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
629static 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
655err_connector_cleanup:
656 drm_connector_cleanup(&dsi->conn);
657 return ret;
658}
659
660static 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
689err_encoder_cleanup:
690 drm_encoder_cleanup(&dsi->encoder);
691 return ret;
692}
693
694static 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
704static 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
711static 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
718static const struct mtk_ddp_comp_funcs mtk_dsi_funcs = {
719 .start = mtk_dsi_ddp_start,
720 .stop = mtk_dsi_ddp_stop,
721};
722
723static 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
738static 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
749static const struct mipi_dsi_host_ops mtk_dsi_ops = {
750 .attach = mtk_dsi_host_attach,
751 .detach = mtk_dsi_host_detach,
752};
753
754static 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
781err_unregister:
782 mipi_dsi_host_unregister(&dsi->host);
783err_ddp_comp_unregister:
784 mtk_ddp_comp_unregister(drm, &dsi->ddp_comp);
785 return ret;
786}
787
788static 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
799static const struct component_ops mtk_dsi_component_ops = {
800 .bind = mtk_dsi_bind,
801 .unbind = mtk_dsi_unbind,
802};
803
804static 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
891static 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
901static const struct of_device_id mtk_dsi_of_match[] = {
902 { .compatible = "mediatek,mt8173-dsi" },
903 { },
904};
905
906struct 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
126struct 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
134static 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
139static 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
147static 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
155static 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
163static 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
249static 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
276static 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
282static 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
294static 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
302static 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
310static 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
325static 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
341static 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
354static 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
367static 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
373static 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
445static int mtk_mipi_tx_remove(struct platform_device *pdev)
446{
447 of_clk_del_provider(pdev->dev.of_node);
448 return 0;
449}
450
451static const struct of_device_id mtk_mipi_tx_match[] = {
452 { .compatible = "mediatek,mt8173-mipi-tx", },
453 {},
454};
455
456struct 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}
94EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
94 95
95void mtk_smi_larb_put(struct device *larbdev) 96void 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}
110EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
109 111
110static int 112static int
111mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) 113mtk_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