aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-06-15 20:02:35 -0400
committerDave Airlie <airlied@redhat.com>2017-06-15 20:02:35 -0400
commit7249e3d64ee512521087de802d3f3ad504258535 (patch)
tree4ca6053e519e6be3d7139d3f92a972009807eab5
parent04d4fb5fa63876d8e7cf67f2788aecfafc6a28a7 (diff)
parent110d33dd428ea49b9482bcb780fb096dfb4dcd3e (diff)
Merge tag 'sunxi-drm-for-4.13' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into drm-next
sun4i-drm changes for 4.13 An unusually big pull request for this merge window, with three notable features: - V3s display engine support. This is especially notable because it uses a different display engine used on the newer Allwinner SoCs (H3, A64 and the likes) that will be quite easily supported now. - HDMI support for the old Allwinner SoCs. This is enabled only on the A10s for now, but should be really easy to extend to deal with A10, A20 and A31 - Preliminary work to deal with dual-pipeline SoCs (A10, A20, A31, H3, etc.). It currently ignores the second pipeline, but we can use the dual-pipelines bindings. This will be useful to enable the display pipeline while we work on the dual-pipeline. * tag 'sunxi-drm-for-4.13' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux: (27 commits) drm/sun4i: Add compatible for the A10s pipeline drm/sun4i: Add HDMI support dt-bindings: display: sun4i: Add allwinner,tcon-channel property dt-bindings: display: sun4i: Add HDMI display bindings drm/sun4i: Ignore the generic connectors for components drm/sun4i: tcon: multiply the vtotal when not in interlace drm/sun4i: tcon: Change vertical total size computation inconsistency drm/sun4i: tcon: Fix tcon channel 1 backporch calculation drm/sun4i: tcon: Switch mux on only for composite drm/sun4i: tcon: Move the muxing out of the mode set function drm/sun4i: tcon: Add channel debug drm/sun4i: tcon: add support for V3s TCON drm/sun4i: Add compatible string for V3s display engine drm/sun4i: add support for Allwinner DE2 mixers drm/sun4i: add a Kconfig option for sun4i-backend drm/sun4i: abstract a engine type drm/sun4i: return only planes for layers created dt-bindings: add bindings for DE2 on V3s SoC drm/sun4i: backend: Clarify sun4i_backend_layer_enable debug message drm/sun4i: Set TCON clock inside sun4i_tconX_mode_set ...
-rw-r--r--Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt127
-rw-r--r--drivers/gpu/drm/sun4i/Kconfig28
-rw-r--r--drivers/gpu/drm/sun4i/Makefile14
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c122
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.h15
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.c32
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_crtc.h5
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c19
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.h5
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi.h157
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c127
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c501
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c225
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_layer.c21
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_layer.h6
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_rgb.c3
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c142
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.h10
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tv.c12
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_layer.c134
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_layer.h36
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.c414
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.h137
-rw-r--r--drivers/gpu/drm/sun4i/sunxi_engine.h98
24 files changed, 2285 insertions, 105 deletions
diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
index 57a8d0610062..b83e6018041d 100644
--- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
@@ -4,6 +4,44 @@ Allwinner A10 Display Pipeline
4The Allwinner A10 Display pipeline is composed of several components 4The Allwinner A10 Display pipeline is composed of several components
5that are going to be documented below: 5that are going to be documented below:
6 6
7For the input port of all components up to the TCON in the display
8pipeline, if there are multiple components, the local endpoint IDs
9must correspond to the index of the upstream block. For example, if
10the remote endpoint is Frontend 1, then the local endpoint ID must
11be 1.
12
13Conversely, for the output ports of the same group, the remote endpoint
14ID must be the index of the local hardware block. If the local backend
15is backend 1, then the remote endpoint ID must be 1.
16
17HDMI Encoder
18------------
19
20The HDMI Encoder supports the HDMI video and audio outputs, and does
21CEC. It is one end of the pipeline.
22
23Required properties:
24 - compatible: value must be one of:
25 * allwinner,sun5i-a10s-hdmi
26 - reg: base address and size of memory-mapped region
27 - interrupts: interrupt associated to this IP
28 - clocks: phandles to the clocks feeding the HDMI encoder
29 * ahb: the HDMI interface clock
30 * mod: the HDMI module clock
31 * pll-0: the first video PLL
32 * pll-1: the second video PLL
33 - clock-names: the clock names mentioned above
34 - dmas: phandles to the DMA channels used by the HDMI encoder
35 * ddc-tx: The channel for DDC transmission
36 * ddc-rx: The channel for DDC reception
37 * audio-tx: The channel used for audio transmission
38 - dma-names: the channel names mentioned above
39
40 - ports: A ports node with endpoint definitions as defined in
41 Documentation/devicetree/bindings/media/video-interfaces.txt. The
42 first port should be the input endpoint. The second should be the
43 output, usually to an HDMI connector.
44
7TV Encoder 45TV Encoder
8---------- 46----------
9 47
@@ -31,6 +69,7 @@ Required properties:
31 * allwinner,sun6i-a31-tcon 69 * allwinner,sun6i-a31-tcon
32 * allwinner,sun6i-a31s-tcon 70 * allwinner,sun6i-a31s-tcon
33 * allwinner,sun8i-a33-tcon 71 * allwinner,sun8i-a33-tcon
72 * allwinner,sun8i-v3s-tcon
34 - reg: base address and size of memory-mapped region 73 - reg: base address and size of memory-mapped region
35 - interrupts: interrupt associated to this IP 74 - interrupts: interrupt associated to this IP
36 - clocks: phandles to the clocks feeding the TCON. Three are needed: 75 - clocks: phandles to the clocks feeding the TCON. Three are needed:
@@ -47,12 +86,15 @@ Required properties:
47 Documentation/devicetree/bindings/media/video-interfaces.txt. The 86 Documentation/devicetree/bindings/media/video-interfaces.txt. The
48 first port should be the input endpoint, the second one the output 87 first port should be the input endpoint, the second one the output
49 88
50 The output should have two endpoints. The first is the block 89 The output may have multiple endpoints. The TCON has two channels,
51 connected to the TCON channel 0 (usually a panel or a bridge), the 90 usually with the first channel being used for the panels interfaces
52 second the block connected to the TCON channel 1 (usually the TV 91 (RGB, LVDS, etc.), and the second being used for the outputs that
53 encoder) 92 require another controller (TV Encoder, HDMI, etc.). The endpoints
93 will take an extra property, allwinner,tcon-channel, to specify the
94 channel the endpoint is associated to. If that property is not
95 present, the endpoint number will be used as the channel number.
54 96
55On SoCs other than the A33, there is one more clock required: 97On SoCs other than the A33 and V3s, there is one more clock required:
56 - 'tcon-ch1': The clock driving the TCON channel 1 98 - 'tcon-ch1': The clock driving the TCON channel 1
57 99
58DRC 100DRC
@@ -138,6 +180,26 @@ Required properties:
138 Documentation/devicetree/bindings/media/video-interfaces.txt. The 180 Documentation/devicetree/bindings/media/video-interfaces.txt. The
139 first port should be the input endpoints, the second one the outputs 181 first port should be the input endpoints, the second one the outputs
140 182
183Display Engine 2.0 Mixer
184------------------------
185
186The DE2 mixer have many functionalities, currently only layer blending is
187supported.
188
189Required properties:
190 - compatible: value must be one of:
191 * allwinner,sun8i-v3s-de2-mixer
192 - reg: base address and size of the memory-mapped region.
193 - clocks: phandles to the clocks feeding the mixer
194 * bus: the mixer interface clock
195 * mod: the mixer module clock
196 - clock-names: the clock names mentioned above
197 - resets: phandles to the reset controllers driving the mixer
198
199- ports: A ports node with endpoint definitions as defined in
200 Documentation/devicetree/bindings/media/video-interfaces.txt. The
201 first port should be the input endpoints, the second one the output
202
141 203
142Display Engine Pipeline 204Display Engine Pipeline
143----------------------- 205-----------------------
@@ -148,13 +210,15 @@ extra node.
148 210
149Required properties: 211Required properties:
150 - compatible: value must be one of: 212 - compatible: value must be one of:
213 * allwinner,sun5i-a10s-display-engine
151 * allwinner,sun5i-a13-display-engine 214 * allwinner,sun5i-a13-display-engine
152 * allwinner,sun6i-a31-display-engine 215 * allwinner,sun6i-a31-display-engine
153 * allwinner,sun6i-a31s-display-engine 216 * allwinner,sun6i-a31s-display-engine
154 * allwinner,sun8i-a33-display-engine 217 * allwinner,sun8i-a33-display-engine
218 * allwinner,sun8i-v3s-display-engine
155 219
156 - allwinner,pipelines: list of phandle to the display engine 220 - allwinner,pipelines: list of phandle to the display engine
157 frontends available. 221 frontends (DE 1.0) or mixers (DE 2.0) available.
158 222
159Example: 223Example:
160 224
@@ -173,6 +237,57 @@ panel: panel {
173 }; 237 };
174}; 238};
175 239
240connector {
241 compatible = "hdmi-connector";
242 type = "a";
243
244 port {
245 hdmi_con_in: endpoint {
246 remote-endpoint = <&hdmi_out_con>;
247 };
248 };
249};
250
251hdmi: hdmi@01c16000 {
252 compatible = "allwinner,sun5i-a10s-hdmi";
253 reg = <0x01c16000 0x1000>;
254 interrupts = <58>;
255 clocks = <&ccu CLK_AHB_HDMI>, <&ccu CLK_HDMI>,
256 <&ccu CLK_PLL_VIDEO0_2X>,
257 <&ccu CLK_PLL_VIDEO1_2X>;
258 clock-names = "ahb", "mod", "pll-0", "pll-1";
259 dmas = <&dma SUN4I_DMA_NORMAL 16>,
260 <&dma SUN4I_DMA_NORMAL 16>,
261 <&dma SUN4I_DMA_DEDICATED 24>;
262 dma-names = "ddc-tx", "ddc-rx", "audio-tx";
263 status = "disabled";
264
265 ports {
266 #address-cells = <1>;
267 #size-cells = <0>;
268
269 port@0 {
270 #address-cells = <1>;
271 #size-cells = <0>;
272 reg = <0>;
273
274 hdmi_in_tcon0: endpoint {
275 remote-endpoint = <&tcon0_out_hdmi>;
276 };
277 };
278
279 port@1 {
280 #address-cells = <1>;
281 #size-cells = <0>;
282 reg = <1>;
283
284 hdmi_out_con: endpoint {
285 remote-endpoint = <&hdmi_con_in>;
286 };
287 };
288 };
289};
290
176tve0: tv-encoder@01c0a000 { 291tve0: tv-encoder@01c0a000 {
177 compatible = "allwinner,sun4i-a10-tv-encoder"; 292 compatible = "allwinner,sun4i-a10-tv-encoder";
178 reg = <0x01c0a000 0x1000>; 293 reg = <0x01c0a000 0x1000>;
diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index a4b357db8856..5bcad8f5fb4f 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -12,3 +12,31 @@ config DRM_SUN4I
12 Choose this option if you have an Allwinner SoC with a 12 Choose this option if you have an Allwinner SoC with a
13 Display Engine. If M is selected the module will be called 13 Display Engine. If M is selected the module will be called
14 sun4i-drm. 14 sun4i-drm.
15
16config DRM_SUN4I_HDMI
17 tristate "Allwinner A10 HDMI Controller Support"
18 depends on DRM_SUN4I
19 default DRM_SUN4I
20 help
21 Choose this option if you have an Allwinner SoC with an HDMI
22 controller.
23
24config DRM_SUN4I_BACKEND
25 tristate "Support for Allwinner A10 Display Engine Backend"
26 depends on DRM_SUN4I
27 default DRM_SUN4I
28 help
29 Choose this option if you have an Allwinner SoC with the
30 original Allwinner Display Engine, which has a backend to
31 do some alpha blending and feed graphics to TCON. If M is
32 selected the module will be called sun4i-backend.
33
34config DRM_SUN8I_MIXER
35 tristate "Support for Allwinner Display Engine 2.0 Mixer"
36 depends on DRM_SUN4I
37 default MACH_SUN8I
38 help
39 Choose this option if you have an Allwinner SoC with the
40 Allwinner Display Engine 2.0, which has a mixer to do some
41 graphics mixture and feed graphics to TCON, If M is
42 selected the module will be called sun8i-mixer.
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 59b757350a1f..e29fd3a2ba9c 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -1,13 +1,23 @@
1sun4i-drm-y += sun4i_drv.o 1sun4i-drm-y += sun4i_drv.o
2sun4i-drm-y += sun4i_framebuffer.o 2sun4i-drm-y += sun4i_framebuffer.o
3 3
4sun4i-drm-hdmi-y += sun4i_hdmi_enc.o
5sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o
6sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o
7
4sun4i-tcon-y += sun4i_tcon.o 8sun4i-tcon-y += sun4i_tcon.o
5sun4i-tcon-y += sun4i_rgb.o 9sun4i-tcon-y += sun4i_rgb.o
6sun4i-tcon-y += sun4i_dotclock.o 10sun4i-tcon-y += sun4i_dotclock.o
7sun4i-tcon-y += sun4i_crtc.o 11sun4i-tcon-y += sun4i_crtc.o
8sun4i-tcon-y += sun4i_layer.o 12
13sun4i-backend-y += sun4i_backend.o sun4i_layer.o
14
15sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o
9 16
10obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o 17obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o sun4i-tcon.o
11obj-$(CONFIG_DRM_SUN4I) += sun4i_backend.o
12obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o 18obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o
13obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o 19obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
20
21obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o
22obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
23obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index d660741ba475..cf480218daa5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -19,10 +19,14 @@
19#include <drm/drm_plane_helper.h> 19#include <drm/drm_plane_helper.h>
20 20
21#include <linux/component.h> 21#include <linux/component.h>
22#include <linux/list.h>
23#include <linux/of_graph.h>
22#include <linux/reset.h> 24#include <linux/reset.h>
23 25
24#include "sun4i_backend.h" 26#include "sun4i_backend.h"
25#include "sun4i_drv.h" 27#include "sun4i_drv.h"
28#include "sun4i_layer.h"
29#include "sunxi_engine.h"
26 30
27static const u32 sunxi_rgb2yuv_coef[12] = { 31static const u32 sunxi_rgb2yuv_coef[12] = {
28 0x00000107, 0x00000204, 0x00000064, 0x00000108, 32 0x00000107, 0x00000204, 0x00000064, 0x00000108,
@@ -30,58 +34,55 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
30 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808 34 0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
31}; 35};
32 36
33void sun4i_backend_apply_color_correction(struct sun4i_backend *backend) 37static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)
34{ 38{
35 int i; 39 int i;
36 40
37 DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n"); 41 DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
38 42
39 /* Set color correction */ 43 /* Set color correction */
40 regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG, 44 regmap_write(engine->regs, SUN4I_BACKEND_OCCTL_REG,
41 SUN4I_BACKEND_OCCTL_ENABLE); 45 SUN4I_BACKEND_OCCTL_ENABLE);
42 46
43 for (i = 0; i < 12; i++) 47 for (i = 0; i < 12; i++)
44 regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i), 48 regmap_write(engine->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
45 sunxi_rgb2yuv_coef[i]); 49 sunxi_rgb2yuv_coef[i]);
46} 50}
47EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
48 51
49void sun4i_backend_disable_color_correction(struct sun4i_backend *backend) 52static void sun4i_backend_disable_color_correction(struct sunxi_engine *engine)
50{ 53{
51 DRM_DEBUG_DRIVER("Disabling color correction\n"); 54 DRM_DEBUG_DRIVER("Disabling color correction\n");
52 55
53 /* Disable color correction */ 56 /* Disable color correction */
54 regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG, 57 regmap_update_bits(engine->regs, SUN4I_BACKEND_OCCTL_REG,
55 SUN4I_BACKEND_OCCTL_ENABLE, 0); 58 SUN4I_BACKEND_OCCTL_ENABLE, 0);
56} 59}
57EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
58 60
59void sun4i_backend_commit(struct sun4i_backend *backend) 61static void sun4i_backend_commit(struct sunxi_engine *engine)
60{ 62{
61 DRM_DEBUG_DRIVER("Committing changes\n"); 63 DRM_DEBUG_DRIVER("Committing changes\n");
62 64
63 regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG, 65 regmap_write(engine->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
64 SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS | 66 SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |
65 SUN4I_BACKEND_REGBUFFCTL_LOADCTL); 67 SUN4I_BACKEND_REGBUFFCTL_LOADCTL);
66} 68}
67EXPORT_SYMBOL(sun4i_backend_commit);
68 69
69void sun4i_backend_layer_enable(struct sun4i_backend *backend, 70void sun4i_backend_layer_enable(struct sun4i_backend *backend,
70 int layer, bool enable) 71 int layer, bool enable)
71{ 72{
72 u32 val; 73 u32 val;
73 74
74 DRM_DEBUG_DRIVER("Enabling layer %d\n", layer); 75 DRM_DEBUG_DRIVER("%sabling layer %d\n", enable ? "En" : "Dis",
76 layer);
75 77
76 if (enable) 78 if (enable)
77 val = SUN4I_BACKEND_MODCTL_LAY_EN(layer); 79 val = SUN4I_BACKEND_MODCTL_LAY_EN(layer);
78 else 80 else
79 val = 0; 81 val = 0;
80 82
81 regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG, 83 regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
82 SUN4I_BACKEND_MODCTL_LAY_EN(layer), val); 84 SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);
83} 85}
84EXPORT_SYMBOL(sun4i_backend_layer_enable);
85 86
86static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, 87static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
87 u32 format, u32 *mode) 88 u32 format, u32 *mode)
@@ -141,33 +142,33 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
141 if (plane->type == DRM_PLANE_TYPE_PRIMARY) { 142 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
142 DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", 143 DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
143 state->crtc_w, state->crtc_h); 144 state->crtc_w, state->crtc_h);
144 regmap_write(backend->regs, SUN4I_BACKEND_DISSIZE_REG, 145 regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG,
145 SUN4I_BACKEND_DISSIZE(state->crtc_w, 146 SUN4I_BACKEND_DISSIZE(state->crtc_w,
146 state->crtc_h)); 147 state->crtc_h));
147 } 148 }
148 149
149 /* Set the line width */ 150 /* Set the line width */
150 DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); 151 DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
151 regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), 152 regmap_write(backend->engine.regs,
153 SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
152 fb->pitches[0] * 8); 154 fb->pitches[0] * 8);
153 155
154 /* Set height and width */ 156 /* Set height and width */
155 DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", 157 DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
156 state->crtc_w, state->crtc_h); 158 state->crtc_w, state->crtc_h);
157 regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer), 159 regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
158 SUN4I_BACKEND_LAYSIZE(state->crtc_w, 160 SUN4I_BACKEND_LAYSIZE(state->crtc_w,
159 state->crtc_h)); 161 state->crtc_h));
160 162
161 /* Set base coordinates */ 163 /* Set base coordinates */
162 DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n", 164 DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
163 state->crtc_x, state->crtc_y); 165 state->crtc_x, state->crtc_y);
164 regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer), 166 regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
165 SUN4I_BACKEND_LAYCOOR(state->crtc_x, 167 SUN4I_BACKEND_LAYCOOR(state->crtc_x,
166 state->crtc_y)); 168 state->crtc_y));
167 169
168 return 0; 170 return 0;
169} 171}
170EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
171 172
172int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, 173int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
173 int layer, struct drm_plane *plane) 174 int layer, struct drm_plane *plane)
@@ -182,7 +183,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
182 interlaced = plane->state->crtc->state->adjusted_mode.flags 183 interlaced = plane->state->crtc->state->adjusted_mode.flags
183 & DRM_MODE_FLAG_INTERLACE; 184 & DRM_MODE_FLAG_INTERLACE;
184 185
185 regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG, 186 regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
186 SUN4I_BACKEND_MODCTL_ITLMOD_EN, 187 SUN4I_BACKEND_MODCTL_ITLMOD_EN,
187 interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0); 188 interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
188 189
@@ -196,12 +197,12 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
196 return ret; 197 return ret;
197 } 198 }
198 199
199 regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer), 200 regmap_update_bits(backend->engine.regs,
201 SUN4I_BACKEND_ATTCTL_REG1(layer),
200 SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val); 202 SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
201 203
202 return 0; 204 return 0;
203} 205}
204EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
205 206
206int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, 207int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
207 int layer, struct drm_plane *plane) 208 int layer, struct drm_plane *plane)
@@ -229,19 +230,19 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
229 /* Write the 32 lower bits of the address (in bits) */ 230 /* Write the 32 lower bits of the address (in bits) */
230 lo_paddr = paddr << 3; 231 lo_paddr = paddr << 3;
231 DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); 232 DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);
232 regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer), 233 regmap_write(backend->engine.regs,
234 SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
233 lo_paddr); 235 lo_paddr);
234 236
235 /* And the upper bits */ 237 /* And the upper bits */
236 hi_paddr = paddr >> 29; 238 hi_paddr = paddr >> 29;
237 DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr); 239 DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);
238 regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG, 240 regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
239 SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer), 241 SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),
240 SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr)); 242 SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));
241 243
242 return 0; 244 return 0;
243} 245}
244EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
245 246
246static int sun4i_backend_init_sat(struct device *dev) { 247static int sun4i_backend_init_sat(struct device *dev) {
247 struct sun4i_backend *backend = dev_get_drvdata(dev); 248 struct sun4i_backend *backend = dev_get_drvdata(dev);
@@ -288,6 +289,52 @@ static int sun4i_backend_free_sat(struct device *dev) {
288 return 0; 289 return 0;
289} 290}
290 291
292/*
293 * The display backend can take video output from the display frontend, or
294 * the display enhancement unit on the A80, as input for one it its layers.
295 * This relationship within the display pipeline is encoded in the device
296 * tree with of_graph, and we use it here to figure out which backend, if
297 * there are 2 or more, we are currently probing. The number would be in
298 * the "reg" property of the upstream output port endpoint.
299 */
300static int sun4i_backend_of_get_id(struct device_node *node)
301{
302 struct device_node *port, *ep;
303 int ret = -EINVAL;
304
305 /* input is port 0 */
306 port = of_graph_get_port_by_id(node, 0);
307 if (!port)
308 return -EINVAL;
309
310 /* try finding an upstream endpoint */
311 for_each_available_child_of_node(port, ep) {
312 struct device_node *remote;
313 u32 reg;
314
315 remote = of_parse_phandle(ep, "remote-endpoint", 0);
316 if (!remote)
317 continue;
318
319 ret = of_property_read_u32(remote, "reg", &reg);
320 if (ret)
321 continue;
322
323 ret = reg;
324 }
325
326 of_node_put(port);
327
328 return ret;
329}
330
331static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
332 .commit = sun4i_backend_commit,
333 .layers_init = sun4i_layers_init,
334 .apply_color_correction = sun4i_backend_apply_color_correction,
335 .disable_color_correction = sun4i_backend_disable_color_correction,
336};
337
291static struct regmap_config sun4i_backend_regmap_config = { 338static struct regmap_config sun4i_backend_regmap_config = {
292 .reg_bits = 32, 339 .reg_bits = 32,
293 .val_bits = 32, 340 .val_bits = 32,
@@ -310,18 +357,23 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
310 if (!backend) 357 if (!backend)
311 return -ENOMEM; 358 return -ENOMEM;
312 dev_set_drvdata(dev, backend); 359 dev_set_drvdata(dev, backend);
313 drv->backend = backend; 360
361 backend->engine.node = dev->of_node;
362 backend->engine.ops = &sun4i_backend_engine_ops;
363 backend->engine.id = sun4i_backend_of_get_id(dev->of_node);
364 if (backend->engine.id < 0)
365 return backend->engine.id;
314 366
315 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 367 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
316 regs = devm_ioremap_resource(dev, res); 368 regs = devm_ioremap_resource(dev, res);
317 if (IS_ERR(regs)) 369 if (IS_ERR(regs))
318 return PTR_ERR(regs); 370 return PTR_ERR(regs);
319 371
320 backend->regs = devm_regmap_init_mmio(dev, regs, 372 backend->engine.regs = devm_regmap_init_mmio(dev, regs,
321 &sun4i_backend_regmap_config); 373 &sun4i_backend_regmap_config);
322 if (IS_ERR(backend->regs)) { 374 if (IS_ERR(backend->engine.regs)) {
323 dev_err(dev, "Couldn't create the backend0 regmap\n"); 375 dev_err(dev, "Couldn't create the backend regmap\n");
324 return PTR_ERR(backend->regs); 376 return PTR_ERR(backend->engine.regs);
325 } 377 }
326 378
327 backend->reset = devm_reset_control_get(dev, NULL); 379 backend->reset = devm_reset_control_get(dev, NULL);
@@ -369,16 +421,18 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
369 } 421 }
370 } 422 }
371 423
424 list_add_tail(&backend->engine.list, &drv->engine_list);
425
372 /* Reset the registers */ 426 /* Reset the registers */
373 for (i = 0x800; i < 0x1000; i += 4) 427 for (i = 0x800; i < 0x1000; i += 4)
374 regmap_write(backend->regs, i, 0); 428 regmap_write(backend->engine.regs, i, 0);
375 429
376 /* Disable registers autoloading */ 430 /* Disable registers autoloading */
377 regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG, 431 regmap_write(backend->engine.regs, SUN4I_BACKEND_REGBUFFCTL_REG,
378 SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS); 432 SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);
379 433
380 /* Enable the backend */ 434 /* Enable the backend */
381 regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG, 435 regmap_write(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
382 SUN4I_BACKEND_MODCTL_DEBE_EN | 436 SUN4I_BACKEND_MODCTL_DEBE_EN |
383 SUN4I_BACKEND_MODCTL_START_CTL); 437 SUN4I_BACKEND_MODCTL_START_CTL);
384 438
@@ -400,6 +454,8 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master,
400{ 454{
401 struct sun4i_backend *backend = dev_get_drvdata(dev); 455 struct sun4i_backend *backend = dev_get_drvdata(dev);
402 456
457 list_del(&backend->engine.list);
458
403 if (of_device_is_compatible(dev->of_node, 459 if (of_device_is_compatible(dev->of_node,
404 "allwinner,sun8i-a33-display-backend")) 460 "allwinner,sun8i-a33-display-backend"))
405 sun4i_backend_free_sat(dev); 461 sun4i_backend_free_sat(dev);
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h
index 83e63cc702b4..21945af67a9d 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.h
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.h
@@ -14,9 +14,13 @@
14#define _SUN4I_BACKEND_H_ 14#define _SUN4I_BACKEND_H_
15 15
16#include <linux/clk.h> 16#include <linux/clk.h>
17#include <linux/list.h>
18#include <linux/of.h>
17#include <linux/regmap.h> 19#include <linux/regmap.h>
18#include <linux/reset.h> 20#include <linux/reset.h>
19 21
22#include "sunxi_engine.h"
23
20#define SUN4I_BACKEND_MODCTL_REG 0x800 24#define SUN4I_BACKEND_MODCTL_REG 0x800
21#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29) 25#define SUN4I_BACKEND_MODCTL_LINE_SEL BIT(29)
22#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28) 26#define SUN4I_BACKEND_MODCTL_ITLMOD_EN BIT(28)
@@ -139,7 +143,7 @@
139#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p))) 143#define SUN4I_BACKEND_PIPE_OFF(p) (0x5000 + (0x400 * (p)))
140 144
141struct sun4i_backend { 145struct sun4i_backend {
142 struct regmap *regs; 146 struct sunxi_engine engine;
143 147
144 struct reset_control *reset; 148 struct reset_control *reset;
145 149
@@ -151,10 +155,11 @@ struct sun4i_backend {
151 struct reset_control *sat_reset; 155 struct reset_control *sat_reset;
152}; 156};
153 157
154void sun4i_backend_apply_color_correction(struct sun4i_backend *backend); 158static inline struct sun4i_backend *
155void sun4i_backend_disable_color_correction(struct sun4i_backend *backend); 159engine_to_sun4i_backend(struct sunxi_engine *engine)
156 160{
157void sun4i_backend_commit(struct sun4i_backend *backend); 161 return container_of(engine, struct sun4i_backend, engine);
162}
158 163
159void sun4i_backend_layer_enable(struct sun4i_backend *backend, 164void sun4i_backend_layer_enable(struct sun4i_backend *backend,
160 int layer, bool enable); 165 int layer, bool enable);
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c
index 3c876c3a356a..f8c70439d1e2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c
@@ -25,10 +25,9 @@
25 25
26#include <video/videomode.h> 26#include <video/videomode.h>
27 27
28#include "sun4i_backend.h"
29#include "sun4i_crtc.h" 28#include "sun4i_crtc.h"
30#include "sun4i_drv.h" 29#include "sun4i_drv.h"
31#include "sun4i_layer.h" 30#include "sunxi_engine.h"
32#include "sun4i_tcon.h" 31#include "sun4i_tcon.h"
33 32
34static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, 33static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
@@ -56,7 +55,7 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
56 55
57 DRM_DEBUG_DRIVER("Committing plane changes\n"); 56 DRM_DEBUG_DRIVER("Committing plane changes\n");
58 57
59 sun4i_backend_commit(scrtc->backend); 58 sunxi_engine_commit(scrtc->engine);
60 59
61 if (event) { 60 if (event) {
62 crtc->state->event = NULL; 61 crtc->state->event = NULL;
@@ -135,36 +134,37 @@ static const struct drm_crtc_funcs sun4i_crtc_funcs = {
135}; 134};
136 135
137struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm, 136struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
138 struct sun4i_backend *backend, 137 struct sunxi_engine *engine,
139 struct sun4i_tcon *tcon) 138 struct sun4i_tcon *tcon)
140{ 139{
141 struct sun4i_crtc *scrtc; 140 struct sun4i_crtc *scrtc;
141 struct drm_plane **planes;
142 struct drm_plane *primary = NULL, *cursor = NULL; 142 struct drm_plane *primary = NULL, *cursor = NULL;
143 int ret, i; 143 int ret, i;
144 144
145 scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL); 145 scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
146 if (!scrtc) 146 if (!scrtc)
147 return ERR_PTR(-ENOMEM); 147 return ERR_PTR(-ENOMEM);
148 scrtc->backend = backend; 148 scrtc->engine = engine;
149 scrtc->tcon = tcon; 149 scrtc->tcon = tcon;
150 150
151 /* Create our layers */ 151 /* Create our layers */
152 scrtc->layers = sun4i_layers_init(drm, scrtc->backend); 152 planes = sunxi_engine_layers_init(drm, engine);
153 if (IS_ERR(scrtc->layers)) { 153 if (IS_ERR(planes)) {
154 dev_err(drm->dev, "Couldn't create the planes\n"); 154 dev_err(drm->dev, "Couldn't create the planes\n");
155 return NULL; 155 return NULL;
156 } 156 }
157 157
158 /* find primary and cursor planes for drm_crtc_init_with_planes */ 158 /* find primary and cursor planes for drm_crtc_init_with_planes */
159 for (i = 0; scrtc->layers[i]; i++) { 159 for (i = 0; planes[i]; i++) {
160 struct sun4i_layer *layer = scrtc->layers[i]; 160 struct drm_plane *plane = planes[i];
161 161
162 switch (layer->plane.type) { 162 switch (plane->type) {
163 case DRM_PLANE_TYPE_PRIMARY: 163 case DRM_PLANE_TYPE_PRIMARY:
164 primary = &layer->plane; 164 primary = plane;
165 break; 165 break;
166 case DRM_PLANE_TYPE_CURSOR: 166 case DRM_PLANE_TYPE_CURSOR:
167 cursor = &layer->plane; 167 cursor = plane;
168 break; 168 break;
169 default: 169 default:
170 break; 170 break;
@@ -188,12 +188,12 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
188 1); 188 1);
189 189
190 /* Set possible_crtcs to this crtc for overlay planes */ 190 /* Set possible_crtcs to this crtc for overlay planes */
191 for (i = 0; scrtc->layers[i]; i++) { 191 for (i = 0; planes[i]; i++) {
192 uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc)); 192 uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc));
193 struct sun4i_layer *layer = scrtc->layers[i]; 193 struct drm_plane *plane = planes[i];
194 194
195 if (layer->plane.type == DRM_PLANE_TYPE_OVERLAY) 195 if (plane->type == DRM_PLANE_TYPE_OVERLAY)
196 layer->plane.possible_crtcs = possible_crtcs; 196 plane->possible_crtcs = possible_crtcs;
197 } 197 }
198 198
199 return scrtc; 199 return scrtc;
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.h b/drivers/gpu/drm/sun4i/sun4i_crtc.h
index 230cb8f0d601..bf0ce36eb518 100644
--- a/drivers/gpu/drm/sun4i/sun4i_crtc.h
+++ b/drivers/gpu/drm/sun4i/sun4i_crtc.h
@@ -17,9 +17,8 @@ struct sun4i_crtc {
17 struct drm_crtc crtc; 17 struct drm_crtc crtc;
18 struct drm_pending_vblank_event *event; 18 struct drm_pending_vblank_event *event;
19 19
20 struct sun4i_backend *backend; 20 struct sunxi_engine *engine;
21 struct sun4i_tcon *tcon; 21 struct sun4i_tcon *tcon;
22 struct sun4i_layer **layers;
23}; 22};
24 23
25static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc) 24static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc)
@@ -28,7 +27,7 @@ static inline struct sun4i_crtc *drm_crtc_to_sun4i_crtc(struct drm_crtc *crtc)
28} 27}
29 28
30struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm, 29struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
31 struct sun4i_backend *backend, 30 struct sunxi_engine *engine,
32 struct sun4i_tcon *tcon); 31 struct sun4i_tcon *tcon);
33 32
34#endif /* _SUN4I_CRTC_H_ */ 33#endif /* _SUN4I_CRTC_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index c26d5888f8e1..abc7d8fe06b4 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -91,6 +91,8 @@ static int sun4i_drv_bind(struct device *dev)
91 goto free_drm; 91 goto free_drm;
92 } 92 }
93 drm->dev_private = drv; 93 drm->dev_private = drv;
94 INIT_LIST_HEAD(&drv->engine_list);
95 INIT_LIST_HEAD(&drv->tcon_list);
94 96
95 ret = of_reserved_mem_device_init(dev); 97 ret = of_reserved_mem_device_init(dev);
96 if (ret && ret != -ENODEV) { 98 if (ret && ret != -ENODEV) {
@@ -162,6 +164,11 @@ static const struct component_master_ops sun4i_drv_master_ops = {
162 .unbind = sun4i_drv_unbind, 164 .unbind = sun4i_drv_unbind,
163}; 165};
164 166
167static bool sun4i_drv_node_is_connector(struct device_node *node)
168{
169 return of_device_is_compatible(node, "hdmi-connector");
170}
171
165static bool sun4i_drv_node_is_frontend(struct device_node *node) 172static bool sun4i_drv_node_is_frontend(struct device_node *node)
166{ 173{
167 return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") || 174 return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
@@ -174,7 +181,8 @@ static bool sun4i_drv_node_is_tcon(struct device_node *node)
174 return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") || 181 return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
175 of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") || 182 of_device_is_compatible(node, "allwinner,sun6i-a31-tcon") ||
176 of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") || 183 of_device_is_compatible(node, "allwinner,sun6i-a31s-tcon") ||
177 of_device_is_compatible(node, "allwinner,sun8i-a33-tcon"); 184 of_device_is_compatible(node, "allwinner,sun8i-a33-tcon") ||
185 of_device_is_compatible(node, "allwinner,sun8i-v3s-tcon");
178} 186}
179 187
180static int compare_of(struct device *dev, void *data) 188static int compare_of(struct device *dev, void *data)
@@ -202,6 +210,13 @@ static int sun4i_drv_add_endpoints(struct device *dev,
202 !of_device_is_available(node)) 210 !of_device_is_available(node))
203 return 0; 211 return 0;
204 212
213 /*
214 * The connectors will be the last nodes in our pipeline, we
215 * can just bail out.
216 */
217 if (sun4i_drv_node_is_connector(node))
218 return 0;
219
205 if (!sun4i_drv_node_is_frontend(node)) { 220 if (!sun4i_drv_node_is_frontend(node)) {
206 /* Add current component */ 221 /* Add current component */
207 DRM_DEBUG_DRIVER("Adding component %s\n", 222 DRM_DEBUG_DRIVER("Adding component %s\n",
@@ -288,10 +303,12 @@ static int sun4i_drv_remove(struct platform_device *pdev)
288} 303}
289 304
290static const struct of_device_id sun4i_drv_of_table[] = { 305static const struct of_device_id sun4i_drv_of_table[] = {
306 { .compatible = "allwinner,sun5i-a10s-display-engine" },
291 { .compatible = "allwinner,sun5i-a13-display-engine" }, 307 { .compatible = "allwinner,sun5i-a13-display-engine" },
292 { .compatible = "allwinner,sun6i-a31-display-engine" }, 308 { .compatible = "allwinner,sun6i-a31-display-engine" },
293 { .compatible = "allwinner,sun6i-a31s-display-engine" }, 309 { .compatible = "allwinner,sun6i-a31s-display-engine" },
294 { .compatible = "allwinner,sun8i-a33-display-engine" }, 310 { .compatible = "allwinner,sun8i-a33-display-engine" },
311 { .compatible = "allwinner,sun8i-v3s-display-engine" },
295 { } 312 { }
296}; 313};
297MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); 314MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.h b/drivers/gpu/drm/sun4i/sun4i_drv.h
index 5df50126ff52..a960c89270cc 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.h
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.h
@@ -14,11 +14,12 @@
14#define _SUN4I_DRV_H_ 14#define _SUN4I_DRV_H_
15 15
16#include <linux/clk.h> 16#include <linux/clk.h>
17#include <linux/list.h>
17#include <linux/regmap.h> 18#include <linux/regmap.h>
18 19
19struct sun4i_drv { 20struct sun4i_drv {
20 struct sun4i_backend *backend; 21 struct list_head engine_list;
21 struct sun4i_tcon *tcon; 22 struct list_head tcon_list;
22 23
23 struct drm_fbdev_cma *fbdev; 24 struct drm_fbdev_cma *fbdev;
24}; 25};
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
new file mode 100644
index 000000000000..2f2f2ff1ea63
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h
@@ -0,0 +1,157 @@
1/*
2 * Copyright (C) 2016 Maxime Ripard
3 *
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 */
11
12#ifndef _SUN4I_HDMI_H_
13#define _SUN4I_HDMI_H_
14
15#include <drm/drm_connector.h>
16#include <drm/drm_encoder.h>
17
18#define SUN4I_HDMI_CTRL_REG 0x004
19#define SUN4I_HDMI_CTRL_ENABLE BIT(31)
20
21#define SUN4I_HDMI_IRQ_REG 0x008
22#define SUN4I_HDMI_IRQ_STA_MASK 0x73
23#define SUN4I_HDMI_IRQ_STA_FIFO_OF BIT(1)
24#define SUN4I_HDMI_IRQ_STA_FIFO_UF BIT(0)
25
26#define SUN4I_HDMI_HPD_REG 0x00c
27#define SUN4I_HDMI_HPD_HIGH BIT(0)
28
29#define SUN4I_HDMI_VID_CTRL_REG 0x010
30#define SUN4I_HDMI_VID_CTRL_ENABLE BIT(31)
31#define SUN4I_HDMI_VID_CTRL_HDMI_MODE BIT(30)
32
33#define SUN4I_HDMI_VID_TIMING_ACT_REG 0x014
34#define SUN4I_HDMI_VID_TIMING_BP_REG 0x018
35#define SUN4I_HDMI_VID_TIMING_FP_REG 0x01c
36#define SUN4I_HDMI_VID_TIMING_SPW_REG 0x020
37
38#define SUN4I_HDMI_VID_TIMING_X(x) ((((x) - 1) & GENMASK(11, 0)))
39#define SUN4I_HDMI_VID_TIMING_Y(y) ((((y) - 1) & GENMASK(11, 0)) << 16)
40
41#define SUN4I_HDMI_VID_TIMING_POL_REG 0x024
42#define SUN4I_HDMI_VID_TIMING_POL_TX_CLK (0x3e0 << 16)
43#define SUN4I_HDMI_VID_TIMING_POL_VSYNC BIT(1)
44#define SUN4I_HDMI_VID_TIMING_POL_HSYNC BIT(0)
45
46#define SUN4I_HDMI_AVI_INFOFRAME_REG(n) (0x080 + (n))
47
48#define SUN4I_HDMI_PAD_CTRL0_REG 0x200
49#define SUN4I_HDMI_PAD_CTRL0_BIASEN BIT(31)
50#define SUN4I_HDMI_PAD_CTRL0_LDOCEN BIT(30)
51#define SUN4I_HDMI_PAD_CTRL0_LDODEN BIT(29)
52#define SUN4I_HDMI_PAD_CTRL0_PWENC BIT(28)
53#define SUN4I_HDMI_PAD_CTRL0_PWEND BIT(27)
54#define SUN4I_HDMI_PAD_CTRL0_PWENG BIT(26)
55#define SUN4I_HDMI_PAD_CTRL0_CKEN BIT(25)
56#define SUN4I_HDMI_PAD_CTRL0_TXEN BIT(23)
57
58#define SUN4I_HDMI_PAD_CTRL1_REG 0x204
59#define SUN4I_HDMI_PAD_CTRL1_AMP_OPT BIT(23)
60#define SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT BIT(22)
61#define SUN4I_HDMI_PAD_CTRL1_EMP_OPT BIT(20)
62#define SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT BIT(19)
63#define SUN4I_HDMI_PAD_CTRL1_REG_DEN BIT(15)
64#define SUN4I_HDMI_PAD_CTRL1_REG_DENCK BIT(14)
65#define SUN4I_HDMI_PAD_CTRL1_REG_EMP(n) (((n) & 7) << 10)
66#define SUN4I_HDMI_PAD_CTRL1_HALVE_CLK BIT(6)
67#define SUN4I_HDMI_PAD_CTRL1_REG_AMP(n) (((n) & 7) << 3)
68
69#define SUN4I_HDMI_PLL_CTRL_REG 0x208
70#define SUN4I_HDMI_PLL_CTRL_PLL_EN BIT(31)
71#define SUN4I_HDMI_PLL_CTRL_BWS BIT(30)
72#define SUN4I_HDMI_PLL_CTRL_HV_IS_33 BIT(29)
73#define SUN4I_HDMI_PLL_CTRL_LDO1_EN BIT(28)
74#define SUN4I_HDMI_PLL_CTRL_LDO2_EN BIT(27)
75#define SUN4I_HDMI_PLL_CTRL_SDIV2 BIT(25)
76#define SUN4I_HDMI_PLL_CTRL_VCO_GAIN(n) (((n) & 7) << 20)
77#define SUN4I_HDMI_PLL_CTRL_S(n) (((n) & 7) << 17)
78#define SUN4I_HDMI_PLL_CTRL_CP_S(n) (((n) & 0x1f) << 12)
79#define SUN4I_HDMI_PLL_CTRL_CS(n) (((n) & 0xf) << 8)
80#define SUN4I_HDMI_PLL_CTRL_DIV(n) (((n) & 0xf) << 4)
81#define SUN4I_HDMI_PLL_CTRL_DIV_MASK GENMASK(7, 4)
82#define SUN4I_HDMI_PLL_CTRL_VCO_S(n) ((n) & 0xf)
83
84#define SUN4I_HDMI_PLL_DBG0_REG 0x20c
85#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT(n) (((n) & 1) << 21)
86#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21)
87#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21
88
89#define SUN4I_HDMI_PKT_CTRL_REG(n) (0x2f0 + (4 * (n)))
90#define SUN4I_HDMI_PKT_CTRL_TYPE(n, t) ((t) << (((n) % 4) * 4))
91
92#define SUN4I_HDMI_UNKNOWN_REG 0x300
93#define SUN4I_HDMI_UNKNOWN_INPUT_SYNC BIT(27)
94
95#define SUN4I_HDMI_DDC_CTRL_REG 0x500
96#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31)
97#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30)
98#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8)
99#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8)
100#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0)
101
102#define SUN4I_HDMI_DDC_ADDR_REG 0x504
103#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24)
104#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16)
105#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8)
106#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff)
107
108#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510
109#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31)
110
111#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518
112#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c
113
114#define SUN4I_HDMI_DDC_CMD_REG 0x520
115#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6
116
117#define SUN4I_HDMI_DDC_CLK_REG 0x528
118#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3)
119#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7)
120
121#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540
122#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9)
123#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8)
124
125#define SUN4I_HDMI_DDC_FIFO_SIZE 16
126
127enum sun4i_hdmi_pkt_type {
128 SUN4I_HDMI_PKT_AVI = 2,
129 SUN4I_HDMI_PKT_END = 15,
130};
131
132struct sun4i_hdmi {
133 struct drm_connector connector;
134 struct drm_encoder encoder;
135 struct device *dev;
136
137 void __iomem *base;
138
139 /* Parent clocks */
140 struct clk *bus_clk;
141 struct clk *mod_clk;
142 struct clk *pll0_clk;
143 struct clk *pll1_clk;
144
145 /* And the clocks we create */
146 struct clk *ddc_clk;
147 struct clk *tmds_clk;
148
149 struct sun4i_drv *drv;
150
151 bool hdmi_monitor;
152};
153
154int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
155int sun4i_tmds_create(struct sun4i_hdmi *hdmi);
156
157#endif /* _SUN4I_HDMI_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
new file mode 100644
index 000000000000..4692e8c345ed
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
@@ -0,0 +1,127 @@
1/*
2 * Copyright (C) 2016 Free Electrons
3 * Copyright (C) 2016 NextThing Co
4 *
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 */
12
13#include <linux/clk-provider.h>
14
15#include "sun4i_tcon.h"
16#include "sun4i_hdmi.h"
17
18struct sun4i_ddc {
19 struct clk_hw hw;
20 struct sun4i_hdmi *hdmi;
21};
22
23static inline struct sun4i_ddc *hw_to_ddc(struct clk_hw *hw)
24{
25 return container_of(hw, struct sun4i_ddc, hw);
26}
27
28static unsigned long sun4i_ddc_calc_divider(unsigned long rate,
29 unsigned long parent_rate,
30 u8 *m, u8 *n)
31{
32 unsigned long best_rate = 0;
33 u8 best_m = 0, best_n = 0, _m, _n;
34
35 for (_m = 0; _m < 8; _m++) {
36 for (_n = 0; _n < 8; _n++) {
37 unsigned long tmp_rate;
38
39 tmp_rate = (((parent_rate / 2) / 10) >> _n) / (_m + 1);
40
41 if (tmp_rate > rate)
42 continue;
43
44 if (abs(rate - tmp_rate) < abs(rate - best_rate)) {
45 best_rate = tmp_rate;
46 best_m = _m;
47 best_n = _n;
48 }
49 }
50 }
51
52 if (m && n) {
53 *m = best_m;
54 *n = best_n;
55 }
56
57 return best_rate;
58}
59
60static long sun4i_ddc_round_rate(struct clk_hw *hw, unsigned long rate,
61 unsigned long *prate)
62{
63 return sun4i_ddc_calc_divider(rate, *prate, NULL, NULL);
64}
65
66static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw,
67 unsigned long parent_rate)
68{
69 struct sun4i_ddc *ddc = hw_to_ddc(hw);
70 u32 reg;
71 u8 m, n;
72
73 reg = readl(ddc->hdmi->base + SUN4I_HDMI_DDC_CLK_REG);
74 m = (reg >> 3) & 0x7;
75 n = reg & 0x7;
76
77 return (((parent_rate / 2) / 10) >> n) / (m + 1);
78}
79
80static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate,
81 unsigned long parent_rate)
82{
83 struct sun4i_ddc *ddc = hw_to_ddc(hw);
84 u8 div_m, div_n;
85
86 sun4i_ddc_calc_divider(rate, parent_rate, &div_m, &div_n);
87
88 writel(SUN4I_HDMI_DDC_CLK_M(div_m) | SUN4I_HDMI_DDC_CLK_N(div_n),
89 ddc->hdmi->base + SUN4I_HDMI_DDC_CLK_REG);
90
91 return 0;
92}
93
94static const struct clk_ops sun4i_ddc_ops = {
95 .recalc_rate = sun4i_ddc_recalc_rate,
96 .round_rate = sun4i_ddc_round_rate,
97 .set_rate = sun4i_ddc_set_rate,
98};
99
100int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent)
101{
102 struct clk_init_data init;
103 struct sun4i_ddc *ddc;
104 const char *parent_name;
105
106 parent_name = __clk_get_name(parent);
107 if (!parent_name)
108 return -ENODEV;
109
110 ddc = devm_kzalloc(hdmi->dev, sizeof(*ddc), GFP_KERNEL);
111 if (!ddc)
112 return -ENOMEM;
113
114 init.name = "hdmi-ddc";
115 init.ops = &sun4i_ddc_ops;
116 init.parent_names = &parent_name;
117 init.num_parents = 1;
118
119 ddc->hdmi = hdmi;
120 ddc->hw.init = &init;
121
122 hdmi->ddc_clk = devm_clk_register(hdmi->dev, &ddc->hw);
123 if (IS_ERR(hdmi->ddc_clk))
124 return PTR_ERR(hdmi->ddc_clk);
125
126 return 0;
127}
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
new file mode 100644
index 000000000000..d3398f6250ef
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -0,0 +1,501 @@
1/*
2 * Copyright (C) 2016 Maxime Ripard
3 *
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 */
11
12#include <drm/drmP.h>
13#include <drm/drm_atomic_helper.h>
14#include <drm/drm_crtc_helper.h>
15#include <drm/drm_edid.h>
16#include <drm/drm_encoder.h>
17#include <drm/drm_of.h>
18#include <drm/drm_panel.h>
19
20#include <linux/clk.h>
21#include <linux/component.h>
22#include <linux/iopoll.h>
23#include <linux/platform_device.h>
24#include <linux/pm_runtime.h>
25
26#include "sun4i_backend.h"
27#include "sun4i_crtc.h"
28#include "sun4i_drv.h"
29#include "sun4i_hdmi.h"
30#include "sun4i_tcon.h"
31
32#define DDC_SEGMENT_ADDR 0x30
33
34static inline struct sun4i_hdmi *
35drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
36{
37 return container_of(encoder, struct sun4i_hdmi,
38 encoder);
39}
40
41static inline struct sun4i_hdmi *
42drm_connector_to_sun4i_hdmi(struct drm_connector *connector)
43{
44 return container_of(connector, struct sun4i_hdmi,
45 connector);
46}
47
48static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
49 struct drm_display_mode *mode)
50{
51 struct hdmi_avi_infoframe frame;
52 u8 buffer[17];
53 int i, ret;
54
55 ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
56 if (ret < 0) {
57 DRM_ERROR("Failed to get infoframes from mode\n");
58 return ret;
59 }
60
61 ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
62 if (ret < 0) {
63 DRM_ERROR("Failed to pack infoframes\n");
64 return ret;
65 }
66
67 for (i = 0; i < sizeof(buffer); i++)
68 writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i));
69
70 return 0;
71}
72
73static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
74 struct drm_crtc_state *crtc_state,
75 struct drm_connector_state *conn_state)
76{
77 struct drm_display_mode *mode = &crtc_state->mode;
78
79 if (mode->flags & DRM_MODE_FLAG_DBLCLK)
80 return -EINVAL;
81
82 return 0;
83}
84
85static void sun4i_hdmi_disable(struct drm_encoder *encoder)
86{
87 struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
88 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
89 struct sun4i_tcon *tcon = crtc->tcon;
90 u32 val;
91
92 DRM_DEBUG_DRIVER("Disabling the HDMI Output\n");
93
94 val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
95 val &= ~SUN4I_HDMI_VID_CTRL_ENABLE;
96 writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
97
98 sun4i_tcon_channel_disable(tcon, 1);
99}
100
101static void sun4i_hdmi_enable(struct drm_encoder *encoder)
102{
103 struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
104 struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
105 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
106 struct sun4i_tcon *tcon = crtc->tcon;
107 u32 val = 0;
108
109 DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
110
111 sun4i_tcon_channel_enable(tcon, 1);
112
113 sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
114 val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
115 val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
116 writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
117
118 val = SUN4I_HDMI_VID_CTRL_ENABLE;
119 if (hdmi->hdmi_monitor)
120 val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
121
122 writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
123}
124
125static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
126 struct drm_display_mode *mode,
127 struct drm_display_mode *adjusted_mode)
128{
129 struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
130 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
131 struct sun4i_tcon *tcon = crtc->tcon;
132 unsigned int x, y;
133 u32 val;
134
135 sun4i_tcon1_mode_set(tcon, mode);
136 sun4i_tcon_set_mux(tcon, 1, encoder);
137
138 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
139 clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
140 clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
141
142 /* Set input sync enable */
143 writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC,
144 hdmi->base + SUN4I_HDMI_UNKNOWN_REG);
145
146 /* Setup timing registers */
147 writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay) |
148 SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay),
149 hdmi->base + SUN4I_HDMI_VID_TIMING_ACT_REG);
150
151 x = mode->htotal - mode->hsync_start;
152 y = mode->vtotal - mode->vsync_start;
153 writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
154 hdmi->base + SUN4I_HDMI_VID_TIMING_BP_REG);
155
156 x = mode->hsync_start - mode->hdisplay;
157 y = mode->vsync_start - mode->vdisplay;
158 writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
159 hdmi->base + SUN4I_HDMI_VID_TIMING_FP_REG);
160
161 x = mode->hsync_end - mode->hsync_start;
162 y = mode->vsync_end - mode->vsync_start;
163 writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y),
164 hdmi->base + SUN4I_HDMI_VID_TIMING_SPW_REG);
165
166 val = SUN4I_HDMI_VID_TIMING_POL_TX_CLK;
167 if (mode->flags & DRM_MODE_FLAG_PHSYNC)
168 val |= SUN4I_HDMI_VID_TIMING_POL_HSYNC;
169
170 if (mode->flags & DRM_MODE_FLAG_PVSYNC)
171 val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
172
173 writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
174}
175
176static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
177 .atomic_check = sun4i_hdmi_atomic_check,
178 .disable = sun4i_hdmi_disable,
179 .enable = sun4i_hdmi_enable,
180 .mode_set = sun4i_hdmi_mode_set,
181};
182
183static const struct drm_encoder_funcs sun4i_hdmi_funcs = {
184 .destroy = drm_encoder_cleanup,
185};
186
187static int sun4i_hdmi_read_sub_block(struct sun4i_hdmi *hdmi,
188 unsigned int blk, unsigned int offset,
189 u8 *buf, unsigned int count)
190{
191 unsigned long reg;
192 int i;
193
194 reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
195 reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;
196 writel(reg | SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ,
197 hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
198
199 writel(SUN4I_HDMI_DDC_ADDR_SEGMENT(offset >> 8) |
200 SUN4I_HDMI_DDC_ADDR_EDDC(DDC_SEGMENT_ADDR << 1) |
201 SUN4I_HDMI_DDC_ADDR_OFFSET(offset) |
202 SUN4I_HDMI_DDC_ADDR_SLAVE(DDC_ADDR),
203 hdmi->base + SUN4I_HDMI_DDC_ADDR_REG);
204
205 reg = readl(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
206 writel(reg | SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR,
207 hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
208
209 writel(count, hdmi->base + SUN4I_HDMI_DDC_BYTE_COUNT_REG);
210 writel(SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ,
211 hdmi->base + SUN4I_HDMI_DDC_CMD_REG);
212
213 reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
214 writel(reg | SUN4I_HDMI_DDC_CTRL_START_CMD,
215 hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
216
217 if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
218 !(reg & SUN4I_HDMI_DDC_CTRL_START_CMD),
219 100, 100000))
220 return -EIO;
221
222 for (i = 0; i < count; i++)
223 buf[i] = readb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG);
224
225 return 0;
226}
227
228static int sun4i_hdmi_read_edid_block(void *data, u8 *buf, unsigned int blk,
229 size_t length)
230{
231 struct sun4i_hdmi *hdmi = data;
232 int retry = 2, i;
233
234 do {
235 for (i = 0; i < length; i += SUN4I_HDMI_DDC_FIFO_SIZE) {
236 unsigned char offset = blk * EDID_LENGTH + i;
237 unsigned int count = min((unsigned int)SUN4I_HDMI_DDC_FIFO_SIZE,
238 length - i);
239 int ret;
240
241 ret = sun4i_hdmi_read_sub_block(hdmi, blk, offset,
242 buf + i, count);
243 if (ret)
244 return ret;
245 }
246 } while (!drm_edid_block_valid(buf, blk, true, NULL) && (retry--));
247
248 return 0;
249}
250
251static int sun4i_hdmi_get_modes(struct drm_connector *connector)
252{
253 struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
254 unsigned long reg;
255 struct edid *edid;
256 int ret;
257
258 /* Reset i2c controller */
259 writel(SUN4I_HDMI_DDC_CTRL_ENABLE | SUN4I_HDMI_DDC_CTRL_RESET,
260 hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
261 if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
262 !(reg & SUN4I_HDMI_DDC_CTRL_RESET),
263 100, 2000))
264 return -EIO;
265
266 writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE |
267 SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,
268 hdmi->base + SUN4I_HDMI_DDC_LINE_CTRL_REG);
269
270 clk_prepare_enable(hdmi->ddc_clk);
271 clk_set_rate(hdmi->ddc_clk, 100000);
272
273 edid = drm_do_get_edid(connector, sun4i_hdmi_read_edid_block, hdmi);
274 if (!edid)
275 return 0;
276
277 hdmi->hdmi_monitor = drm_detect_hdmi_monitor(edid);
278 DRM_DEBUG_DRIVER("Monitor is %s monitor\n",
279 hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
280
281 drm_mode_connector_update_edid_property(connector, edid);
282 ret = drm_add_edid_modes(connector, edid);
283 kfree(edid);
284
285 clk_disable_unprepare(hdmi->ddc_clk);
286
287 return ret;
288}
289
290static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
291 .get_modes = sun4i_hdmi_get_modes,
292};
293
294static enum drm_connector_status
295sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
296{
297 struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
298 unsigned long reg;
299
300 if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
301 reg & SUN4I_HDMI_HPD_HIGH,
302 0, 500000))
303 return connector_status_disconnected;
304
305 return connector_status_connected;
306}
307
308static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
309 .dpms = drm_atomic_helper_connector_dpms,
310 .detect = sun4i_hdmi_connector_detect,
311 .fill_modes = drm_helper_probe_single_connector_modes,
312 .destroy = drm_connector_cleanup,
313 .reset = drm_atomic_helper_connector_reset,
314 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
315 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
316};
317
318static int sun4i_hdmi_bind(struct device *dev, struct device *master,
319 void *data)
320{
321 struct platform_device *pdev = to_platform_device(dev);
322 struct drm_device *drm = data;
323 struct sun4i_drv *drv = drm->dev_private;
324 struct sun4i_hdmi *hdmi;
325 struct resource *res;
326 u32 reg;
327 int ret;
328
329 hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
330 if (!hdmi)
331 return -ENOMEM;
332 dev_set_drvdata(dev, hdmi);
333 hdmi->dev = dev;
334 hdmi->drv = drv;
335
336 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
337 hdmi->base = devm_ioremap_resource(dev, res);
338 if (IS_ERR(hdmi->base)) {
339 dev_err(dev, "Couldn't map the HDMI encoder registers\n");
340 return PTR_ERR(hdmi->base);
341 }
342
343 hdmi->bus_clk = devm_clk_get(dev, "ahb");
344 if (IS_ERR(hdmi->bus_clk)) {
345 dev_err(dev, "Couldn't get the HDMI bus clock\n");
346 return PTR_ERR(hdmi->bus_clk);
347 }
348 clk_prepare_enable(hdmi->bus_clk);
349
350 hdmi->mod_clk = devm_clk_get(dev, "mod");
351 if (IS_ERR(hdmi->mod_clk)) {
352 dev_err(dev, "Couldn't get the HDMI mod clock\n");
353 return PTR_ERR(hdmi->mod_clk);
354 }
355 clk_prepare_enable(hdmi->mod_clk);
356
357 hdmi->pll0_clk = devm_clk_get(dev, "pll-0");
358 if (IS_ERR(hdmi->pll0_clk)) {
359 dev_err(dev, "Couldn't get the HDMI PLL 0 clock\n");
360 return PTR_ERR(hdmi->pll0_clk);
361 }
362
363 hdmi->pll1_clk = devm_clk_get(dev, "pll-1");
364 if (IS_ERR(hdmi->pll1_clk)) {
365 dev_err(dev, "Couldn't get the HDMI PLL 1 clock\n");
366 return PTR_ERR(hdmi->pll1_clk);
367 }
368
369 ret = sun4i_tmds_create(hdmi);
370 if (ret) {
371 dev_err(dev, "Couldn't create the TMDS clock\n");
372 return ret;
373 }
374
375 writel(SUN4I_HDMI_CTRL_ENABLE, hdmi->base + SUN4I_HDMI_CTRL_REG);
376
377 writel(SUN4I_HDMI_PAD_CTRL0_TXEN | SUN4I_HDMI_PAD_CTRL0_CKEN |
378 SUN4I_HDMI_PAD_CTRL0_PWENG | SUN4I_HDMI_PAD_CTRL0_PWEND |
379 SUN4I_HDMI_PAD_CTRL0_PWENC | SUN4I_HDMI_PAD_CTRL0_LDODEN |
380 SUN4I_HDMI_PAD_CTRL0_LDOCEN | SUN4I_HDMI_PAD_CTRL0_BIASEN,
381 hdmi->base + SUN4I_HDMI_PAD_CTRL0_REG);
382
383 /*
384 * We can't just initialize the register there, we need to
385 * protect the clock bits that have already been read out and
386 * cached by the clock framework.
387 */
388 reg = readl(hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
389 reg &= SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;
390 reg |= SUN4I_HDMI_PAD_CTRL1_REG_AMP(6) |
391 SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) |
392 SUN4I_HDMI_PAD_CTRL1_REG_DENCK |
393 SUN4I_HDMI_PAD_CTRL1_REG_DEN |
394 SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT |
395 SUN4I_HDMI_PAD_CTRL1_EMP_OPT |
396 SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT |
397 SUN4I_HDMI_PAD_CTRL1_AMP_OPT;
398 writel(reg, hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
399
400 reg = readl(hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
401 reg &= SUN4I_HDMI_PLL_CTRL_DIV_MASK;
402 reg |= SUN4I_HDMI_PLL_CTRL_VCO_S(8) | SUN4I_HDMI_PLL_CTRL_CS(7) |
403 SUN4I_HDMI_PLL_CTRL_CP_S(15) | SUN4I_HDMI_PLL_CTRL_S(7) |
404 SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) | SUN4I_HDMI_PLL_CTRL_SDIV2 |
405 SUN4I_HDMI_PLL_CTRL_LDO2_EN | SUN4I_HDMI_PLL_CTRL_LDO1_EN |
406 SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_BWS |
407 SUN4I_HDMI_PLL_CTRL_PLL_EN;
408 writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
409
410 ret = sun4i_ddc_create(hdmi, hdmi->tmds_clk);
411 if (ret) {
412 dev_err(dev, "Couldn't create the DDC clock\n");
413 return ret;
414 }
415
416 drm_encoder_helper_add(&hdmi->encoder,
417 &sun4i_hdmi_helper_funcs);
418 ret = drm_encoder_init(drm,
419 &hdmi->encoder,
420 &sun4i_hdmi_funcs,
421 DRM_MODE_ENCODER_TMDS,
422 NULL);
423 if (ret) {
424 dev_err(dev, "Couldn't initialise the HDMI encoder\n");
425 return ret;
426 }
427
428 hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm,
429 dev->of_node);
430 if (!hdmi->encoder.possible_crtcs)
431 return -EPROBE_DEFER;
432
433 drm_connector_helper_add(&hdmi->connector,
434 &sun4i_hdmi_connector_helper_funcs);
435 ret = drm_connector_init(drm, &hdmi->connector,
436 &sun4i_hdmi_connector_funcs,
437 DRM_MODE_CONNECTOR_HDMIA);
438 if (ret) {
439 dev_err(dev,
440 "Couldn't initialise the HDMI connector\n");
441 goto err_cleanup_connector;
442 }
443
444 /* There is no HPD interrupt, so we need to poll the controller */
445 hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
446 DRM_CONNECTOR_POLL_DISCONNECT;
447
448 drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
449
450 return 0;
451
452err_cleanup_connector:
453 drm_encoder_cleanup(&hdmi->encoder);
454 return ret;
455}
456
457static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
458 void *data)
459{
460 struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
461
462 drm_connector_cleanup(&hdmi->connector);
463 drm_encoder_cleanup(&hdmi->encoder);
464}
465
466static const struct component_ops sun4i_hdmi_ops = {
467 .bind = sun4i_hdmi_bind,
468 .unbind = sun4i_hdmi_unbind,
469};
470
471static int sun4i_hdmi_probe(struct platform_device *pdev)
472{
473 return component_add(&pdev->dev, &sun4i_hdmi_ops);
474}
475
476static int sun4i_hdmi_remove(struct platform_device *pdev)
477{
478 component_del(&pdev->dev, &sun4i_hdmi_ops);
479
480 return 0;
481}
482
483static const struct of_device_id sun4i_hdmi_of_table[] = {
484 { .compatible = "allwinner,sun5i-a10s-hdmi" },
485 { }
486};
487MODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table);
488
489static struct platform_driver sun4i_hdmi_driver = {
490 .probe = sun4i_hdmi_probe,
491 .remove = sun4i_hdmi_remove,
492 .driver = {
493 .name = "sun4i-hdmi",
494 .of_match_table = sun4i_hdmi_of_table,
495 },
496};
497module_platform_driver(sun4i_hdmi_driver);
498
499MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
500MODULE_DESCRIPTION("Allwinner A10 HDMI Driver");
501MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
new file mode 100644
index 000000000000..5cf2527bffc8
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -0,0 +1,225 @@
1/*
2 * Copyright (C) 2016 Free Electrons
3 * Copyright (C) 2016 NextThing Co
4 *
5 * Maxime Ripard <maxime.ripard@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 */
12
13#include <linux/clk-provider.h>
14
15#include "sun4i_tcon.h"
16#include "sun4i_hdmi.h"
17
18struct sun4i_tmds {
19 struct clk_hw hw;
20 struct sun4i_hdmi *hdmi;
21};
22
23static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw)
24{
25 return container_of(hw, struct sun4i_tmds, hw);
26}
27
28
29static unsigned long sun4i_tmds_calc_divider(unsigned long rate,
30 unsigned long parent_rate,
31 u8 *div,
32 bool *half)
33{
34 unsigned long best_rate = 0;
35 u8 best_m = 0, m;
36 bool is_double;
37
38 for (m = 1; m < 16; m++) {
39 u8 d;
40
41 for (d = 1; d < 3; d++) {
42 unsigned long tmp_rate;
43
44 tmp_rate = parent_rate / m / d;
45
46 if (tmp_rate > rate)
47 continue;
48
49 if (!best_rate ||
50 (rate - tmp_rate) < (rate - best_rate)) {
51 best_rate = tmp_rate;
52 best_m = m;
53 is_double = d;
54 }
55 }
56 }
57
58 if (div && half) {
59 *div = best_m;
60 *half = is_double;
61 }
62
63 return best_rate;
64}
65
66
67static int sun4i_tmds_determine_rate(struct clk_hw *hw,
68 struct clk_rate_request *req)
69{
70 struct clk_hw *parent;
71 unsigned long best_parent = 0;
72 unsigned long rate = req->rate;
73 int best_div = 1, best_half = 1;
74 int i, j;
75
76 /*
77 * We only consider PLL3, since the TCON is very likely to be
78 * clocked from it, and to have the same rate than our HDMI
79 * clock, so we should not need to do anything.
80 */
81
82 parent = clk_hw_get_parent_by_index(hw, 0);
83 if (!parent)
84 return -EINVAL;
85
86 for (i = 1; i < 3; i++) {
87 for (j = 1; j < 16; j++) {
88 unsigned long ideal = rate * i * j;
89 unsigned long rounded;
90
91 rounded = clk_hw_round_rate(parent, ideal);
92
93 if (rounded == ideal) {
94 best_parent = rounded;
95 best_half = i;
96 best_div = j;
97 goto out;
98 }
99
100 if (abs(rate - rounded / i) <
101 abs(rate - best_parent / best_div)) {
102 best_parent = rounded;
103 best_div = i;
104 }
105 }
106 }
107
108out:
109 req->rate = best_parent / best_half / best_div;
110 req->best_parent_rate = best_parent;
111 req->best_parent_hw = parent;
112
113 return 0;
114}
115
116static unsigned long sun4i_tmds_recalc_rate(struct clk_hw *hw,
117 unsigned long parent_rate)
118{
119 struct sun4i_tmds *tmds = hw_to_tmds(hw);
120 u32 reg;
121
122 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
123 if (reg & SUN4I_HDMI_PAD_CTRL1_HALVE_CLK)
124 parent_rate /= 2;
125
126 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
127 reg = (reg >> 4) & 0xf;
128 if (!reg)
129 reg = 1;
130
131 return parent_rate / reg;
132}
133
134static int sun4i_tmds_set_rate(struct clk_hw *hw, unsigned long rate,
135 unsigned long parent_rate)
136{
137 struct sun4i_tmds *tmds = hw_to_tmds(hw);
138 bool half;
139 u32 reg;
140 u8 div;
141
142 sun4i_tmds_calc_divider(rate, parent_rate, &div, &half);
143
144 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
145 reg &= ~SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;
146 if (half)
147 reg |= SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;
148 writel(reg, tmds->hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
149
150 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
151 reg &= ~SUN4I_HDMI_PLL_CTRL_DIV_MASK;
152 writel(reg | SUN4I_HDMI_PLL_CTRL_DIV(div),
153 tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
154
155 return 0;
156}
157
158static u8 sun4i_tmds_get_parent(struct clk_hw *hw)
159{
160 struct sun4i_tmds *tmds = hw_to_tmds(hw);
161 u32 reg;
162
163 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_DBG0_REG);
164 return ((reg & SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK) >>
165 SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT);
166}
167
168static int sun4i_tmds_set_parent(struct clk_hw *hw, u8 index)
169{
170 struct sun4i_tmds *tmds = hw_to_tmds(hw);
171 u32 reg;
172
173 if (index > 1)
174 return -EINVAL;
175
176 reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_DBG0_REG);
177 reg &= ~SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK;
178 writel(reg | SUN4I_HDMI_PLL_DBG0_TMDS_PARENT(index),
179 tmds->hdmi->base + SUN4I_HDMI_PLL_DBG0_REG);
180
181 return 0;
182}
183
184static const struct clk_ops sun4i_tmds_ops = {
185 .determine_rate = sun4i_tmds_determine_rate,
186 .recalc_rate = sun4i_tmds_recalc_rate,
187 .set_rate = sun4i_tmds_set_rate,
188
189 .get_parent = sun4i_tmds_get_parent,
190 .set_parent = sun4i_tmds_set_parent,
191};
192
193int sun4i_tmds_create(struct sun4i_hdmi *hdmi)
194{
195 struct clk_init_data init;
196 struct sun4i_tmds *tmds;
197 const char *parents[2];
198
199 parents[0] = __clk_get_name(hdmi->pll0_clk);
200 if (!parents[0])
201 return -ENODEV;
202
203 parents[1] = __clk_get_name(hdmi->pll1_clk);
204 if (!parents[1])
205 return -ENODEV;
206
207 tmds = devm_kzalloc(hdmi->dev, sizeof(*tmds), GFP_KERNEL);
208 if (!tmds)
209 return -ENOMEM;
210
211 init.name = "hdmi-tmds";
212 init.ops = &sun4i_tmds_ops;
213 init.parent_names = parents;
214 init.num_parents = 2;
215 init.flags = CLK_SET_RATE_PARENT;
216
217 tmds->hdmi = hdmi;
218 tmds->hw.init = &init;
219
220 hdmi->tmds_clk = devm_clk_register(hdmi->dev, &tmds->hw);
221 if (IS_ERR(hdmi->tmds_clk))
222 return PTR_ERR(hdmi->tmds_clk);
223
224 return 0;
225}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
index f26bde5b9117..ead4f9d4c1ee 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.c
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
@@ -11,12 +11,12 @@
11 */ 11 */
12 12
13#include <drm/drm_atomic_helper.h> 13#include <drm/drm_atomic_helper.h>
14#include <drm/drm_crtc.h>
15#include <drm/drm_plane_helper.h> 14#include <drm/drm_plane_helper.h>
16#include <drm/drmP.h> 15#include <drm/drmP.h>
17 16
18#include "sun4i_backend.h" 17#include "sun4i_backend.h"
19#include "sun4i_layer.h" 18#include "sun4i_layer.h"
19#include "sunxi_engine.h"
20 20
21struct sun4i_plane_desc { 21struct sun4i_plane_desc {
22 enum drm_plane_type type; 22 enum drm_plane_type type;
@@ -128,15 +128,16 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
128 return layer; 128 return layer;
129} 129}
130 130
131struct sun4i_layer **sun4i_layers_init(struct drm_device *drm, 131struct drm_plane **sun4i_layers_init(struct drm_device *drm,
132 struct sun4i_backend *backend) 132 struct sunxi_engine *engine)
133{ 133{
134 struct sun4i_layer **layers; 134 struct drm_plane **planes;
135 struct sun4i_backend *backend = engine_to_sun4i_backend(engine);
135 int i; 136 int i;
136 137
137 layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1, 138 planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes) + 1,
138 sizeof(*layers), GFP_KERNEL); 139 sizeof(*planes), GFP_KERNEL);
139 if (!layers) 140 if (!planes)
140 return ERR_PTR(-ENOMEM); 141 return ERR_PTR(-ENOMEM);
141 142
142 /* 143 /*
@@ -173,13 +174,13 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm,
173 174
174 DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n", 175 DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
175 i ? "overlay" : "primary", plane->pipe); 176 i ? "overlay" : "primary", plane->pipe);
176 regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i), 177 regmap_update_bits(engine->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
177 SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK, 178 SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
178 SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe)); 179 SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
179 180
180 layer->id = i; 181 layer->id = i;
181 layers[i] = layer; 182 planes[i] = &layer->plane;
182 }; 183 };
183 184
184 return layers; 185 return planes;
185} 186}
diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.h b/drivers/gpu/drm/sun4i/sun4i_layer.h
index 4be1f0919df2..4e84f438b346 100644
--- a/drivers/gpu/drm/sun4i/sun4i_layer.h
+++ b/drivers/gpu/drm/sun4i/sun4i_layer.h
@@ -13,6 +13,8 @@
13#ifndef _SUN4I_LAYER_H_ 13#ifndef _SUN4I_LAYER_H_
14#define _SUN4I_LAYER_H_ 14#define _SUN4I_LAYER_H_
15 15
16struct sunxi_engine;
17
16struct sun4i_layer { 18struct sun4i_layer {
17 struct drm_plane plane; 19 struct drm_plane plane;
18 struct sun4i_drv *drv; 20 struct sun4i_drv *drv;
@@ -26,7 +28,7 @@ plane_to_sun4i_layer(struct drm_plane *plane)
26 return container_of(plane, struct sun4i_layer, plane); 28 return container_of(plane, struct sun4i_layer, plane);
27} 29}
28 30
29struct sun4i_layer **sun4i_layers_init(struct drm_device *drm, 31struct drm_plane **sun4i_layers_init(struct drm_device *drm,
30 struct sun4i_backend *backend); 32 struct sunxi_engine *engine);
31 33
32#endif /* _SUN4I_LAYER_H_ */ 34#endif /* _SUN4I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index 67f0b91a99de..422b191faa77 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -175,8 +175,7 @@ static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
175 struct sun4i_tcon *tcon = rgb->tcon; 175 struct sun4i_tcon *tcon = rgb->tcon;
176 176
177 sun4i_tcon0_mode_set(tcon, mode); 177 sun4i_tcon0_mode_set(tcon, mode);
178 178 sun4i_tcon_set_mux(tcon, 0, encoder);
179 clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
180 179
181 /* FIXME: This seems to be board specific */ 180 /* FIXME: This seems to be board specific */
182 clk_set_phase(tcon->dclk, 120); 181 clk_set_phase(tcon->dclk, 120);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 9a83a85529ac..d9791292553e 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -30,6 +30,7 @@
30#include "sun4i_drv.h" 30#include "sun4i_drv.h"
31#include "sun4i_rgb.h" 31#include "sun4i_rgb.h"
32#include "sun4i_tcon.h" 32#include "sun4i_tcon.h"
33#include "sunxi_engine.h"
33 34
34void sun4i_tcon_disable(struct sun4i_tcon *tcon) 35void sun4i_tcon_disable(struct sun4i_tcon *tcon)
35{ 36{
@@ -54,6 +55,8 @@ EXPORT_SYMBOL(sun4i_tcon_enable);
54 55
55void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel) 56void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
56{ 57{
58 DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel);
59
57 /* Disable the TCON's channel */ 60 /* Disable the TCON's channel */
58 if (channel == 0) { 61 if (channel == 0) {
59 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 62 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
@@ -71,6 +74,8 @@ EXPORT_SYMBOL(sun4i_tcon_channel_disable);
71 74
72void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel) 75void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
73{ 76{
77 DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel);
78
74 /* Enable the TCON's channel */ 79 /* Enable the TCON's channel */
75 if (channel == 0) { 80 if (channel == 0) {
76 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 81 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
@@ -104,6 +109,29 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
104} 109}
105EXPORT_SYMBOL(sun4i_tcon_enable_vblank); 110EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
106 111
112void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
113 struct drm_encoder *encoder)
114{
115 u32 val;
116
117 if (!tcon->quirks->has_unknown_mux)
118 return;
119
120 if (channel != 1)
121 return;
122
123 if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
124 val = 1;
125 else
126 val = 0;
127
128 /*
129 * FIXME: Undocumented bits
130 */
131 regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
132}
133EXPORT_SYMBOL(sun4i_tcon_set_mux);
134
107static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode, 135static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode,
108 int channel) 136 int channel)
109{ 137{
@@ -129,6 +157,9 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
129 u8 clk_delay; 157 u8 clk_delay;
130 u32 val = 0; 158 u32 val = 0;
131 159
160 /* Configure the dot clock */
161 clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
162
132 /* Adjust clock delay */ 163 /* Adjust clock delay */
133 clk_delay = sun4i_tcon_get_clk_delay(mode, 0); 164 clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
134 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, 165 regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
@@ -163,7 +194,7 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
163 194
164 /* Set vertical display timings */ 195 /* Set vertical display timings */
165 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG, 196 regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
166 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) | 197 SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
167 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp)); 198 SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
168 199
169 /* Set Hsync and Vsync length */ 200 /* Set Hsync and Vsync length */
@@ -198,12 +229,15 @@ EXPORT_SYMBOL(sun4i_tcon0_mode_set);
198void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, 229void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
199 struct drm_display_mode *mode) 230 struct drm_display_mode *mode)
200{ 231{
201 unsigned int bp, hsync, vsync; 232 unsigned int bp, hsync, vsync, vtotal;
202 u8 clk_delay; 233 u8 clk_delay;
203 u32 val; 234 u32 val;
204 235
205 WARN_ON(!tcon->quirks->has_channel_1); 236 WARN_ON(!tcon->quirks->has_channel_1);
206 237
238 /* Configure the dot clock */
239 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
240
207 /* Adjust clock delay */ 241 /* Adjust clock delay */
208 clk_delay = sun4i_tcon_get_clk_delay(mode, 1); 242 clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
209 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, 243 regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
@@ -235,19 +269,37 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
235 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay)); 269 SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
236 270
237 /* Set horizontal display timings */ 271 /* Set horizontal display timings */
238 bp = mode->crtc_htotal - mode->crtc_hsync_end; 272 bp = mode->crtc_htotal - mode->crtc_hsync_start;
239 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n", 273 DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
240 mode->htotal, bp); 274 mode->htotal, bp);
241 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG, 275 regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
242 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) | 276 SUN4I_TCON1_BASIC3_H_TOTAL(mode->crtc_htotal) |
243 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp)); 277 SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
244 278
245 /* Set vertical display timings */ 279 bp = mode->crtc_vtotal - mode->crtc_vsync_start;
246 bp = mode->crtc_vtotal - mode->crtc_vsync_end;
247 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n", 280 DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
248 mode->vtotal, bp); 281 mode->crtc_vtotal, bp);
282
283 /*
284 * The vertical resolution needs to be doubled in all
285 * cases. We could use crtc_vtotal and always multiply by two,
286 * but that leads to a rounding error in interlace when vtotal
287 * is odd.
288 *
289 * This happens with TV's PAL for example, where vtotal will
290 * be 625, crtc_vtotal 312, and thus crtc_vtotal * 2 will be
291 * 624, which apparently confuses the hardware.
292 *
293 * To work around this, we will always use vtotal, and
294 * multiply by two only if we're not in interlace.
295 */
296 vtotal = mode->vtotal;
297 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
298 vtotal = vtotal * 2;
299
300 /* Set vertical display timings */
249 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG, 301 regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
250 SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) | 302 SUN4I_TCON1_BASIC4_V_TOTAL(vtotal) |
251 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp)); 303 SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
252 304
253 /* Set Hsync and Vsync length */ 305 /* Set Hsync and Vsync length */
@@ -262,12 +314,6 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
262 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, 314 regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
263 SUN4I_TCON_GCTL_IOMAP_MASK, 315 SUN4I_TCON_GCTL_IOMAP_MASK,
264 SUN4I_TCON_GCTL_IOMAP_TCON1); 316 SUN4I_TCON_GCTL_IOMAP_TCON1);
265
266 /*
267 * FIXME: Undocumented bits
268 */
269 if (tcon->quirks->has_unknown_mux)
270 regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
271} 317}
272EXPORT_SYMBOL(sun4i_tcon1_mode_set); 318EXPORT_SYMBOL(sun4i_tcon1_mode_set);
273 319
@@ -402,21 +448,79 @@ static int sun4i_tcon_init_regmap(struct device *dev,
402 return 0; 448 return 0;
403} 449}
404 450
451/*
452 * On SoCs with the old display pipeline design (Display Engine 1.0),
453 * the TCON is always tied to just one backend. Hence we can traverse
454 * the of_graph upwards to find the backend our tcon is connected to,
455 * and take its ID as our own.
456 *
457 * We can either identify backends from their compatible strings, which
458 * means maintaining a large list of them. Or, since the backend is
459 * registered and binded before the TCON, we can just go through the
460 * list of registered backends and compare the device node.
461 *
462 * As the structures now store engines instead of backends, here this
463 * function in fact searches the corresponding engine, and the ID is
464 * requested via the get_id function of the engine.
465 */
466static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv,
467 struct device_node *node)
468{
469 struct device_node *port, *ep, *remote;
470 struct sunxi_engine *engine;
471
472 port = of_graph_get_port_by_id(node, 0);
473 if (!port)
474 return ERR_PTR(-EINVAL);
475
476 for_each_available_child_of_node(port, ep) {
477 remote = of_graph_get_remote_port_parent(ep);
478 if (!remote)
479 continue;
480
481 /* does this node match any registered engines? */
482 list_for_each_entry(engine, &drv->engine_list, list) {
483 if (remote == engine->node) {
484 of_node_put(remote);
485 of_node_put(port);
486 return engine;
487 }
488 }
489
490 /* keep looking through upstream ports */
491 engine = sun4i_tcon_find_engine(drv, remote);
492 if (!IS_ERR(engine)) {
493 of_node_put(remote);
494 of_node_put(port);
495 return engine;
496 }
497 }
498
499 return ERR_PTR(-EINVAL);
500}
501
405static int sun4i_tcon_bind(struct device *dev, struct device *master, 502static int sun4i_tcon_bind(struct device *dev, struct device *master,
406 void *data) 503 void *data)
407{ 504{
408 struct drm_device *drm = data; 505 struct drm_device *drm = data;
409 struct sun4i_drv *drv = drm->dev_private; 506 struct sun4i_drv *drv = drm->dev_private;
507 struct sunxi_engine *engine;
410 struct sun4i_tcon *tcon; 508 struct sun4i_tcon *tcon;
411 int ret; 509 int ret;
412 510
511 engine = sun4i_tcon_find_engine(drv, dev->of_node);
512 if (IS_ERR(engine)) {
513 dev_err(dev, "Couldn't find matching engine\n");
514 return -EPROBE_DEFER;
515 }
516
413 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL); 517 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
414 if (!tcon) 518 if (!tcon)
415 return -ENOMEM; 519 return -ENOMEM;
416 dev_set_drvdata(dev, tcon); 520 dev_set_drvdata(dev, tcon);
417 drv->tcon = tcon;
418 tcon->drm = drm; 521 tcon->drm = drm;
419 tcon->dev = dev; 522 tcon->dev = dev;
523 tcon->id = engine->id;
420 tcon->quirks = of_device_get_match_data(dev); 524 tcon->quirks = of_device_get_match_data(dev);
421 525
422 tcon->lcd_rst = devm_reset_control_get(dev, "lcd"); 526 tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
@@ -459,7 +563,7 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
459 goto err_free_dotclock; 563 goto err_free_dotclock;
460 } 564 }
461 565
462 tcon->crtc = sun4i_crtc_init(drm, drv->backend, tcon); 566 tcon->crtc = sun4i_crtc_init(drm, engine, tcon);
463 if (IS_ERR(tcon->crtc)) { 567 if (IS_ERR(tcon->crtc)) {
464 dev_err(dev, "Couldn't create our CRTC\n"); 568 dev_err(dev, "Couldn't create our CRTC\n");
465 ret = PTR_ERR(tcon->crtc); 569 ret = PTR_ERR(tcon->crtc);
@@ -470,6 +574,8 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
470 if (ret < 0) 574 if (ret < 0)
471 goto err_free_clocks; 575 goto err_free_clocks;
472 576
577 list_add_tail(&tcon->list, &drv->tcon_list);
578
473 return 0; 579 return 0;
474 580
475err_free_dotclock: 581err_free_dotclock:
@@ -486,6 +592,7 @@ static void sun4i_tcon_unbind(struct device *dev, struct device *master,
486{ 592{
487 struct sun4i_tcon *tcon = dev_get_drvdata(dev); 593 struct sun4i_tcon *tcon = dev_get_drvdata(dev);
488 594
595 list_del(&tcon->list);
489 sun4i_dclk_free(tcon); 596 sun4i_dclk_free(tcon);
490 sun4i_tcon_free_clocks(tcon); 597 sun4i_tcon_free_clocks(tcon);
491} 598}
@@ -533,11 +640,16 @@ static const struct sun4i_tcon_quirks sun8i_a33_quirks = {
533 /* nothing is supported */ 640 /* nothing is supported */
534}; 641};
535 642
643static const struct sun4i_tcon_quirks sun8i_v3s_quirks = {
644 /* nothing is supported */
645};
646
536static const struct of_device_id sun4i_tcon_of_table[] = { 647static const struct of_device_id sun4i_tcon_of_table[] = {
537 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks }, 648 { .compatible = "allwinner,sun5i-a13-tcon", .data = &sun5i_a13_quirks },
538 { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks }, 649 { .compatible = "allwinner,sun6i-a31-tcon", .data = &sun6i_a31_quirks },
539 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks }, 650 { .compatible = "allwinner,sun6i-a31s-tcon", .data = &sun6i_a31s_quirks },
540 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks }, 651 { .compatible = "allwinner,sun8i-a33-tcon", .data = &sun8i_a33_quirks },
652 { .compatible = "allwinner,sun8i-v3s-tcon", .data = &sun8i_v3s_quirks },
541 { } 653 { }
542}; 654};
543MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table); 655MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index f636343a935d..e3c50ecdcd04 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -17,6 +17,7 @@
17#include <drm/drm_crtc.h> 17#include <drm/drm_crtc.h>
18 18
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <linux/list.h>
20#include <linux/reset.h> 21#include <linux/reset.h>
21 22
22#define SUN4I_TCON_GCTL_REG 0x0 23#define SUN4I_TCON_GCTL_REG 0x0
@@ -51,7 +52,7 @@
51#define SUN4I_TCON0_BASIC1_H_BACKPORCH(bp) (((bp) - 1) & 0xfff) 52#define SUN4I_TCON0_BASIC1_H_BACKPORCH(bp) (((bp) - 1) & 0xfff)
52 53
53#define SUN4I_TCON0_BASIC2_REG 0x50 54#define SUN4I_TCON0_BASIC2_REG 0x50
54#define SUN4I_TCON0_BASIC2_V_TOTAL(total) ((((total) * 2) & 0x1fff) << 16) 55#define SUN4I_TCON0_BASIC2_V_TOTAL(total) (((total) & 0x1fff) << 16)
55#define SUN4I_TCON0_BASIC2_V_BACKPORCH(bp) (((bp) - 1) & 0xfff) 56#define SUN4I_TCON0_BASIC2_V_BACKPORCH(bp) (((bp) - 1) & 0xfff)
56 57
57#define SUN4I_TCON0_BASIC3_REG 0x54 58#define SUN4I_TCON0_BASIC3_REG 0x54
@@ -172,6 +173,11 @@ struct sun4i_tcon {
172 173
173 /* Associated crtc */ 174 /* Associated crtc */
174 struct sun4i_crtc *crtc; 175 struct sun4i_crtc *crtc;
176
177 int id;
178
179 /* TCON list management */
180 struct list_head list;
175}; 181};
176 182
177struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); 183struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
@@ -190,6 +196,8 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
190/* Mode Related Controls */ 196/* Mode Related Controls */
191void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon, 197void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon,
192 bool enable); 198 bool enable);
199void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
200 struct drm_encoder *encoder);
193void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon, 201void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
194 struct drm_display_mode *mode); 202 struct drm_display_mode *mode);
195void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon, 203void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 49c49431a053..338b9e5bb2a3 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -22,10 +22,10 @@
22#include <drm/drm_of.h> 22#include <drm/drm_of.h>
23#include <drm/drm_panel.h> 23#include <drm/drm_panel.h>
24 24
25#include "sun4i_backend.h"
26#include "sun4i_crtc.h" 25#include "sun4i_crtc.h"
27#include "sun4i_drv.h" 26#include "sun4i_drv.h"
28#include "sun4i_tcon.h" 27#include "sun4i_tcon.h"
28#include "sunxi_engine.h"
29 29
30#define SUN4I_TVE_EN_REG 0x000 30#define SUN4I_TVE_EN_REG 0x000
31#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4) 31#define SUN4I_TVE_EN_DAC_MAP_MASK GENMASK(19, 4)
@@ -353,7 +353,6 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
353 struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); 353 struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
354 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 354 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
355 struct sun4i_tcon *tcon = crtc->tcon; 355 struct sun4i_tcon *tcon = crtc->tcon;
356 struct sun4i_backend *backend = crtc->backend;
357 356
358 DRM_DEBUG_DRIVER("Disabling the TV Output\n"); 357 DRM_DEBUG_DRIVER("Disabling the TV Output\n");
359 358
@@ -362,7 +361,8 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
362 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 361 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
363 SUN4I_TVE_EN_ENABLE, 362 SUN4I_TVE_EN_ENABLE,
364 0); 363 0);
365 sun4i_backend_disable_color_correction(backend); 364
365 sunxi_engine_disable_color_correction(crtc->engine);
366} 366}
367 367
368static void sun4i_tv_enable(struct drm_encoder *encoder) 368static void sun4i_tv_enable(struct drm_encoder *encoder)
@@ -370,11 +370,10 @@ static void sun4i_tv_enable(struct drm_encoder *encoder)
370 struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); 370 struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
371 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 371 struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
372 struct sun4i_tcon *tcon = crtc->tcon; 372 struct sun4i_tcon *tcon = crtc->tcon;
373 struct sun4i_backend *backend = crtc->backend;
374 373
375 DRM_DEBUG_DRIVER("Enabling the TV Output\n"); 374 DRM_DEBUG_DRIVER("Enabling the TV Output\n");
376 375
377 sun4i_backend_apply_color_correction(backend); 376 sunxi_engine_apply_color_correction(crtc->engine);
378 377
379 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 378 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
380 SUN4I_TVE_EN_ENABLE, 379 SUN4I_TVE_EN_ENABLE,
@@ -393,6 +392,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
393 const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); 392 const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
394 393
395 sun4i_tcon1_mode_set(tcon, mode); 394 sun4i_tcon1_mode_set(tcon, mode);
395 sun4i_tcon_set_mux(tcon, 1, encoder);
396 396
397 /* Enable and map the DAC to the output */ 397 /* Enable and map the DAC to the output */
398 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, 398 regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
@@ -486,8 +486,6 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
486 SUN4I_TVE_RESYNC_FIELD : 0)); 486 SUN4I_TVE_RESYNC_FIELD : 0));
487 487
488 regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0); 488 regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0);
489
490 clk_set_rate(tcon->sclk1, mode->crtc_clock * 1000);
491} 489}
492 490
493static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = { 491static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.c b/drivers/gpu/drm/sun4i/sun8i_layer.c
new file mode 100644
index 000000000000..e627eeece658
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.c
@@ -0,0 +1,134 @@
1/*
2 * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
3 *
4 * Based on sun4i_layer.h, which is:
5 * Copyright (C) 2015 Free Electrons
6 * Copyright (C) 2015 NextThing Co
7 *
8 * Maxime Ripard <maxime.ripard@free-electrons.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 */
15
16#include <drm/drm_atomic_helper.h>
17#include <drm/drm_plane_helper.h>
18#include <drm/drmP.h>
19
20#include "sun8i_layer.h"
21#include "sun8i_mixer.h"
22
23struct sun8i_plane_desc {
24 enum drm_plane_type type;
25 const uint32_t *formats;
26 uint32_t nformats;
27};
28
29static void sun8i_mixer_layer_atomic_disable(struct drm_plane *plane,
30 struct drm_plane_state *old_state)
31{
32 struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
33 struct sun8i_mixer *mixer = layer->mixer;
34
35 sun8i_mixer_layer_enable(mixer, layer->id, false);
36}
37
38static void sun8i_mixer_layer_atomic_update(struct drm_plane *plane,
39 struct drm_plane_state *old_state)
40{
41 struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
42 struct sun8i_mixer *mixer = layer->mixer;
43
44 sun8i_mixer_update_layer_coord(mixer, layer->id, plane);
45 sun8i_mixer_update_layer_formats(mixer, layer->id, plane);
46 sun8i_mixer_update_layer_buffer(mixer, layer->id, plane);
47 sun8i_mixer_layer_enable(mixer, layer->id, true);
48}
49
50static struct drm_plane_helper_funcs sun8i_mixer_layer_helper_funcs = {
51 .atomic_disable = sun8i_mixer_layer_atomic_disable,
52 .atomic_update = sun8i_mixer_layer_atomic_update,
53};
54
55static const struct drm_plane_funcs sun8i_mixer_layer_funcs = {
56 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
57 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
58 .destroy = drm_plane_cleanup,
59 .disable_plane = drm_atomic_helper_disable_plane,
60 .reset = drm_atomic_helper_plane_reset,
61 .update_plane = drm_atomic_helper_update_plane,
62};
63
64static const uint32_t sun8i_mixer_layer_formats[] = {
65 DRM_FORMAT_RGB888,
66 DRM_FORMAT_ARGB8888,
67 DRM_FORMAT_XRGB8888,
68};
69
70static const struct sun8i_plane_desc sun8i_mixer_planes[] = {
71 {
72 .type = DRM_PLANE_TYPE_PRIMARY,
73 .formats = sun8i_mixer_layer_formats,
74 .nformats = ARRAY_SIZE(sun8i_mixer_layer_formats),
75 },
76};
77
78static struct sun8i_layer *sun8i_layer_init_one(struct drm_device *drm,
79 struct sun8i_mixer *mixer,
80 const struct sun8i_plane_desc *plane)
81{
82 struct sun8i_layer *layer;
83 int ret;
84
85 layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
86 if (!layer)
87 return ERR_PTR(-ENOMEM);
88
89 /* possible crtcs are set later */
90 ret = drm_universal_plane_init(drm, &layer->plane, 0,
91 &sun8i_mixer_layer_funcs,
92 plane->formats, plane->nformats,
93 plane->type, NULL);
94 if (ret) {
95 dev_err(drm->dev, "Couldn't initialize layer\n");
96 return ERR_PTR(ret);
97 }
98
99 drm_plane_helper_add(&layer->plane,
100 &sun8i_mixer_layer_helper_funcs);
101 layer->mixer = mixer;
102
103 return layer;
104}
105
106struct drm_plane **sun8i_layers_init(struct drm_device *drm,
107 struct sunxi_engine *engine)
108{
109 struct drm_plane **planes;
110 struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
111 int i;
112
113 planes = devm_kcalloc(drm->dev, ARRAY_SIZE(sun8i_mixer_planes) + 1,
114 sizeof(*planes), GFP_KERNEL);
115 if (!planes)
116 return ERR_PTR(-ENOMEM);
117
118 for (i = 0; i < ARRAY_SIZE(sun8i_mixer_planes); i++) {
119 const struct sun8i_plane_desc *plane = &sun8i_mixer_planes[i];
120 struct sun8i_layer *layer;
121
122 layer = sun8i_layer_init_one(drm, mixer, plane);
123 if (IS_ERR(layer)) {
124 dev_err(drm->dev, "Couldn't initialize %s plane\n",
125 i ? "overlay" : "primary");
126 return ERR_CAST(layer);
127 };
128
129 layer->id = i;
130 planes[i] = &layer->plane;
131 };
132
133 return planes;
134}
diff --git a/drivers/gpu/drm/sun4i/sun8i_layer.h b/drivers/gpu/drm/sun4i/sun8i_layer.h
new file mode 100644
index 000000000000..e5eccd27cff0
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_layer.h
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
3 *
4 * Based on sun4i_layer.h, which is:
5 * Copyright (C) 2015 Free Electrons
6 * Copyright (C) 2015 NextThing Co
7 *
8 * Maxime Ripard <maxime.ripard@free-electrons.com>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 */
15
16#ifndef _SUN8I_LAYER_H_
17#define _SUN8I_LAYER_H_
18
19struct sunxi_engine;
20
21struct sun8i_layer {
22 struct drm_plane plane;
23 struct sun4i_drv *drv;
24 struct sun8i_mixer *mixer;
25 int id;
26};
27
28static inline struct sun8i_layer *
29plane_to_sun8i_layer(struct drm_plane *plane)
30{
31 return container_of(plane, struct sun8i_layer, plane);
32}
33
34struct drm_plane **sun8i_layers_init(struct drm_device *drm,
35 struct sunxi_engine *engine);
36#endif /* _SUN8I_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
new file mode 100644
index 000000000000..cb193c5f1686
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -0,0 +1,414 @@
1/*
2 * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
3 *
4 * Based on sun4i_backend.c, which is:
5 * Copyright (C) 2015 Free Electrons
6 * Copyright (C) 2015 NextThing Co
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_atomic_helper.h>
16#include <drm/drm_crtc.h>
17#include <drm/drm_crtc_helper.h>
18#include <drm/drm_fb_cma_helper.h>
19#include <drm/drm_gem_cma_helper.h>
20#include <drm/drm_plane_helper.h>
21
22#include <linux/component.h>
23#include <linux/dma-mapping.h>
24#include <linux/reset.h>
25#include <linux/of_device.h>
26
27#include "sun4i_drv.h"
28#include "sun8i_mixer.h"
29#include "sun8i_layer.h"
30#include "sunxi_engine.h"
31
32static void sun8i_mixer_commit(struct sunxi_engine *engine)
33{
34 DRM_DEBUG_DRIVER("Committing changes\n");
35
36 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
37 SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
38}
39
40void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
41 int layer, bool enable)
42{
43 u32 val;
44 /* Currently the first UI channel is used */
45 int chan = mixer->cfg->vi_num;
46
47 DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan);
48
49 if (enable)
50 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
51 else
52 val = 0;
53
54 regmap_update_bits(mixer->engine.regs,
55 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
56 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
57
58 /* Set the alpha configuration */
59 regmap_update_bits(mixer->engine.regs,
60 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
61 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK,
62 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF);
63 regmap_update_bits(mixer->engine.regs,
64 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
65 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK,
66 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF);
67}
68
69static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane,
70 u32 format, u32 *mode)
71{
72 switch (format) {
73 case DRM_FORMAT_ARGB8888:
74 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888;
75 break;
76
77 case DRM_FORMAT_XRGB8888:
78 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888;
79 break;
80
81 case DRM_FORMAT_RGB888:
82 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888;
83 break;
84
85 default:
86 return -EINVAL;
87 }
88
89 return 0;
90}
91
92int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
93 int layer, struct drm_plane *plane)
94{
95 struct drm_plane_state *state = plane->state;
96 struct drm_framebuffer *fb = state->fb;
97 /* Currently the first UI channel is used */
98 int chan = mixer->cfg->vi_num;
99
100 DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
101
102 if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
103 DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
104 state->crtc_w, state->crtc_h);
105 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE,
106 SUN8I_MIXER_SIZE(state->crtc_w,
107 state->crtc_h));
108 DRM_DEBUG_DRIVER("Updating blender size\n");
109 regmap_write(mixer->engine.regs,
110 SUN8I_MIXER_BLEND_ATTR_INSIZE(0),
111 SUN8I_MIXER_SIZE(state->crtc_w,
112 state->crtc_h));
113 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE,
114 SUN8I_MIXER_SIZE(state->crtc_w,
115 state->crtc_h));
116 DRM_DEBUG_DRIVER("Updating channel size\n");
117 regmap_write(mixer->engine.regs,
118 SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan),
119 SUN8I_MIXER_SIZE(state->crtc_w,
120 state->crtc_h));
121 }
122
123 /* Set the line width */
124 DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
125 regmap_write(mixer->engine.regs,
126 SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer),
127 fb->pitches[0]);
128
129 /* Set height and width */
130 DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
131 state->crtc_w, state->crtc_h);
132 regmap_write(mixer->engine.regs,
133 SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer),
134 SUN8I_MIXER_SIZE(state->crtc_w, state->crtc_h));
135
136 /* Set base coordinates */
137 DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
138 state->crtc_x, state->crtc_y);
139 regmap_write(mixer->engine.regs,
140 SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer),
141 SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y));
142
143 return 0;
144}
145
146int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
147 int layer, struct drm_plane *plane)
148{
149 struct drm_plane_state *state = plane->state;
150 struct drm_framebuffer *fb = state->fb;
151 bool interlaced = false;
152 u32 val;
153 /* Currently the first UI channel is used */
154 int chan = mixer->cfg->vi_num;
155 int ret;
156
157 if (plane->state->crtc)
158 interlaced = plane->state->crtc->state->adjusted_mode.flags
159 & DRM_MODE_FLAG_INTERLACE;
160
161 regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTCTL,
162 SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
163 interlaced ?
164 SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0);
165
166 DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
167 interlaced ? "on" : "off");
168
169 ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format,
170 &val);
171 if (ret) {
172 DRM_DEBUG_DRIVER("Invalid format\n");
173 return ret;
174 }
175
176 regmap_update_bits(mixer->engine.regs,
177 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer),
178 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
179
180 return 0;
181}
182
183int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
184 int layer, struct drm_plane *plane)
185{
186 struct drm_plane_state *state = plane->state;
187 struct drm_framebuffer *fb = state->fb;
188 struct drm_gem_cma_object *gem;
189 dma_addr_t paddr;
190 /* Currently the first UI channel is used */
191 int chan = mixer->cfg->vi_num;
192 int bpp;
193
194 /* Get the physical address of the buffer in memory */
195 gem = drm_fb_cma_get_gem_obj(fb, 0);
196
197 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
198
199 /* Compute the start of the displayed memory */
200 bpp = fb->format->cpp[0];
201 paddr = gem->paddr + fb->offsets[0];
202
203 /* Fixup framebuffer address for src coordinates */
204 paddr += (state->src_x >> 16) * bpp;
205 paddr += (state->src_y >> 16) * fb->pitches[0];
206
207 /*
208 * The hardware cannot correctly deal with negative crtc
209 * coordinates, the display is cropped to the requested size,
210 * but the display content is not moved.
211 * Manually move the display content by fixup the framebuffer
212 * address when crtc_x or crtc_y is negative, like what we
213 * have did for src_x and src_y.
214 */
215 if (state->crtc_x < 0)
216 paddr += -state->crtc_x * bpp;
217 if (state->crtc_y < 0)
218 paddr += -state->crtc_y * fb->pitches[0];
219
220 DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
221
222 regmap_write(mixer->engine.regs,
223 SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer),
224 lower_32_bits(paddr));
225
226 return 0;
227}
228
229static const struct sunxi_engine_ops sun8i_engine_ops = {
230 .commit = sun8i_mixer_commit,
231 .layers_init = sun8i_layers_init,
232};
233
234static struct regmap_config sun8i_mixer_regmap_config = {
235 .reg_bits = 32,
236 .val_bits = 32,
237 .reg_stride = 4,
238 .max_register = 0xbfffc, /* guessed */
239};
240
241static int sun8i_mixer_bind(struct device *dev, struct device *master,
242 void *data)
243{
244 struct platform_device *pdev = to_platform_device(dev);
245 struct drm_device *drm = data;
246 struct sun4i_drv *drv = drm->dev_private;
247 struct sun8i_mixer *mixer;
248 struct resource *res;
249 void __iomem *regs;
250 int i, ret;
251
252 /*
253 * The mixer uses single 32-bit register to store memory
254 * addresses, so that it cannot deal with 64-bit memory
255 * addresses.
256 * Restrict the DMA mask so that the mixer won't be
257 * allocated some memory that is too high.
258 */
259 ret = dma_set_mask(dev, DMA_BIT_MASK(32));
260 if (ret) {
261 dev_err(dev, "Cannot do 32-bit DMA.\n");
262 return ret;
263 }
264
265 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
266 if (!mixer)
267 return -ENOMEM;
268 dev_set_drvdata(dev, mixer);
269 mixer->engine.ops = &sun8i_engine_ops;
270 mixer->engine.node = dev->of_node;
271 /* The ID of the mixer currently doesn't matter */
272 mixer->engine.id = -1;
273
274 mixer->cfg = of_device_get_match_data(dev);
275 if (!mixer->cfg)
276 return -EINVAL;
277
278 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
279 regs = devm_ioremap_resource(dev, res);
280 if (IS_ERR(regs))
281 return PTR_ERR(regs);
282
283 mixer->engine.regs = devm_regmap_init_mmio(dev, regs,
284 &sun8i_mixer_regmap_config);
285 if (IS_ERR(mixer->engine.regs)) {
286 dev_err(dev, "Couldn't create the mixer regmap\n");
287 return PTR_ERR(mixer->engine.regs);
288 }
289
290 mixer->reset = devm_reset_control_get(dev, NULL);
291 if (IS_ERR(mixer->reset)) {
292 dev_err(dev, "Couldn't get our reset line\n");
293 return PTR_ERR(mixer->reset);
294 }
295
296 ret = reset_control_deassert(mixer->reset);
297 if (ret) {
298 dev_err(dev, "Couldn't deassert our reset line\n");
299 return ret;
300 }
301
302 mixer->bus_clk = devm_clk_get(dev, "bus");
303 if (IS_ERR(mixer->bus_clk)) {
304 dev_err(dev, "Couldn't get the mixer bus clock\n");
305 ret = PTR_ERR(mixer->bus_clk);
306 goto err_assert_reset;
307 }
308 clk_prepare_enable(mixer->bus_clk);
309
310 mixer->mod_clk = devm_clk_get(dev, "mod");
311 if (IS_ERR(mixer->mod_clk)) {
312 dev_err(dev, "Couldn't get the mixer module clock\n");
313 ret = PTR_ERR(mixer->mod_clk);
314 goto err_disable_bus_clk;
315 }
316 clk_prepare_enable(mixer->mod_clk);
317
318 list_add_tail(&mixer->engine.list, &drv->engine_list);
319
320 /* Reset the registers */
321 for (i = 0x0; i < 0x20000; i += 4)
322 regmap_write(mixer->engine.regs, i, 0);
323
324 /* Enable the mixer */
325 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
326 SUN8I_MIXER_GLOBAL_CTL_RT_EN);
327
328 /* Initialize blender */
329 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL,
330 SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF);
331 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY,
332 SUN8I_MIXER_BLEND_PREMULTIPLY_DEF);
333 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR,
334 SUN8I_MIXER_BLEND_BKCOLOR_DEF);
335 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0),
336 SUN8I_MIXER_BLEND_MODE_DEF);
337 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL,
338 SUN8I_MIXER_BLEND_CK_CTL_DEF);
339
340 regmap_write(mixer->engine.regs,
341 SUN8I_MIXER_BLEND_ATTR_FCOLOR(0),
342 SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF);
343
344 /* Select the first UI channel */
345 DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n",
346 mixer->cfg->vi_num);
347 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE,
348 mixer->cfg->vi_num);
349
350 return 0;
351
352err_disable_bus_clk:
353 clk_disable_unprepare(mixer->bus_clk);
354err_assert_reset:
355 reset_control_assert(mixer->reset);
356 return ret;
357}
358
359static void sun8i_mixer_unbind(struct device *dev, struct device *master,
360 void *data)
361{
362 struct sun8i_mixer *mixer = dev_get_drvdata(dev);
363
364 list_del(&mixer->engine.list);
365
366 clk_disable_unprepare(mixer->mod_clk);
367 clk_disable_unprepare(mixer->bus_clk);
368 reset_control_assert(mixer->reset);
369}
370
371static const struct component_ops sun8i_mixer_ops = {
372 .bind = sun8i_mixer_bind,
373 .unbind = sun8i_mixer_unbind,
374};
375
376static int sun8i_mixer_probe(struct platform_device *pdev)
377{
378 return component_add(&pdev->dev, &sun8i_mixer_ops);
379}
380
381static int sun8i_mixer_remove(struct platform_device *pdev)
382{
383 component_del(&pdev->dev, &sun8i_mixer_ops);
384
385 return 0;
386}
387
388static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
389 .vi_num = 2,
390 .ui_num = 1,
391};
392
393static const struct of_device_id sun8i_mixer_of_table[] = {
394 {
395 .compatible = "allwinner,sun8i-v3s-de2-mixer",
396 .data = &sun8i_v3s_mixer_cfg,
397 },
398 { }
399};
400MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table);
401
402static struct platform_driver sun8i_mixer_platform_driver = {
403 .probe = sun8i_mixer_probe,
404 .remove = sun8i_mixer_remove,
405 .driver = {
406 .name = "sun8i-mixer",
407 .of_match_table = sun8i_mixer_of_table,
408 },
409};
410module_platform_driver(sun8i_mixer_platform_driver);
411
412MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
413MODULE_DESCRIPTION("Allwinner DE2 Mixer driver");
414MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
new file mode 100644
index 000000000000..4785ac090b8c
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -0,0 +1,137 @@
1/*
2 * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 */
9
10#ifndef _SUN8I_MIXER_H_
11#define _SUN8I_MIXER_H_
12
13#include <linux/clk.h>
14#include <linux/regmap.h>
15#include <linux/reset.h>
16
17#include "sunxi_engine.h"
18
19#define SUN8I_MIXER_MAX_CHAN_COUNT 4
20
21#define SUN8I_MIXER_SIZE(w, h) (((h) - 1) << 16 | ((w) - 1))
22#define SUN8I_MIXER_COORD(x, y) ((y) << 16 | (x))
23
24#define SUN8I_MIXER_GLOBAL_CTL 0x0
25#define SUN8I_MIXER_GLOBAL_STATUS 0x4
26#define SUN8I_MIXER_GLOBAL_DBUFF 0x8
27#define SUN8I_MIXER_GLOBAL_SIZE 0xc
28
29#define SUN8I_MIXER_GLOBAL_CTL_RT_EN 0x1
30
31#define SUN8I_MIXER_GLOBAL_DBUFF_ENABLE 0x1
32
33#define SUN8I_MIXER_BLEND_FCOLOR_CTL 0x1000
34#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(x) (0x1004 + 0x10 * (x) + 0x0)
35#define SUN8I_MIXER_BLEND_ATTR_INSIZE(x) (0x1004 + 0x10 * (x) + 0x4)
36#define SUN8I_MIXER_BLEND_ATTR_OFFSET(x) (0x1004 + 0x10 * (x) + 0x8)
37#define SUN8I_MIXER_BLEND_ROUTE 0x1080
38#define SUN8I_MIXER_BLEND_PREMULTIPLY 0x1084
39#define SUN8I_MIXER_BLEND_BKCOLOR 0x1088
40#define SUN8I_MIXER_BLEND_OUTSIZE 0x108c
41#define SUN8I_MIXER_BLEND_MODE(x) (0x1090 + 0x04 * (x))
42#define SUN8I_MIXER_BLEND_CK_CTL 0x10b0
43#define SUN8I_MIXER_BLEND_CK_CFG 0x10b4
44#define SUN8I_MIXER_BLEND_CK_MAX(x) (0x10c0 + 0x04 * (x))
45#define SUN8I_MIXER_BLEND_CK_MIN(x) (0x10e0 + 0x04 * (x))
46#define SUN8I_MIXER_BLEND_OUTCTL 0x10fc
47
48/* The following numbers are some still unknown magic numbers */
49#define SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF 0xff000000
50#define SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF 0x00000101
51#define SUN8I_MIXER_BLEND_PREMULTIPLY_DEF 0x0
52#define SUN8I_MIXER_BLEND_BKCOLOR_DEF 0xff000000
53#define SUN8I_MIXER_BLEND_MODE_DEF 0x03010301
54#define SUN8I_MIXER_BLEND_CK_CTL_DEF 0x0
55
56#define SUN8I_MIXER_BLEND_OUTCTL_INTERLACED BIT(1)
57
58/*
59 * VI channels are not used now, but the support of them may be introduced in
60 * the future.
61 */
62
63#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch, layer) \
64 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x0)
65#define SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch, layer) \
66 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x4)
67#define SUN8I_MIXER_CHAN_UI_LAYER_COORD(ch, layer) \
68 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x8)
69#define SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch, layer) \
70 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0xc)
71#define SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch, layer) \
72 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x10)
73#define SUN8I_MIXER_CHAN_UI_LAYER_BOT_LADDR(ch, layer) \
74 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x14)
75#define SUN8I_MIXER_CHAN_UI_LAYER_FCOLOR(ch, layer) \
76 (0x2000 + 0x1000 * (ch) + 0x20 * (layer) + 0x18)
77#define SUN8I_MIXER_CHAN_UI_TOP_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x80)
78#define SUN8I_MIXER_CHAN_UI_BOT_HADDR(ch) (0x2000 + 0x1000 * (ch) + 0x84)
79#define SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch) (0x2000 + 0x1000 * (ch) + 0x88)
80
81#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN BIT(0)
82#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK GENMASK(2, 1)
83#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK GENMASK(11, 8)
84#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
85#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF (1 << 1)
86#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888 (0 << 8)
87#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888 (4 << 8)
88#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888 (8 << 8)
89#define SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF (0xff << 24)
90
91/*
92 * These sub-engines are still unknown now, the EN registers are here only to
93 * be used to disable these sub-engines.
94 */
95#define SUN8I_MIXER_VSU_EN 0x20000
96#define SUN8I_MIXER_GSU1_EN 0x30000
97#define SUN8I_MIXER_GSU2_EN 0x40000
98#define SUN8I_MIXER_GSU3_EN 0x50000
99#define SUN8I_MIXER_FCE_EN 0xa0000
100#define SUN8I_MIXER_BWS_EN 0xa2000
101#define SUN8I_MIXER_LTI_EN 0xa4000
102#define SUN8I_MIXER_PEAK_EN 0xa6000
103#define SUN8I_MIXER_ASE_EN 0xa8000
104#define SUN8I_MIXER_FCC_EN 0xaa000
105#define SUN8I_MIXER_DCSC_EN 0xb0000
106
107struct sun8i_mixer_cfg {
108 int vi_num;
109 int ui_num;
110};
111
112struct sun8i_mixer {
113 struct sunxi_engine engine;
114
115 const struct sun8i_mixer_cfg *cfg;
116
117 struct reset_control *reset;
118
119 struct clk *bus_clk;
120 struct clk *mod_clk;
121};
122
123static inline struct sun8i_mixer *
124engine_to_sun8i_mixer(struct sunxi_engine *engine)
125{
126 return container_of(engine, struct sun8i_mixer, engine);
127}
128
129void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer,
130 int layer, bool enable);
131int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer,
132 int layer, struct drm_plane *plane);
133int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer,
134 int layer, struct drm_plane *plane);
135int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer,
136 int layer, struct drm_plane *plane);
137#endif /* _SUN8I_MIXER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sunxi_engine.h b/drivers/gpu/drm/sun4i/sunxi_engine.h
new file mode 100644
index 000000000000..4cb70ae65c79
--- /dev/null
+++ b/drivers/gpu/drm/sun4i/sunxi_engine.h
@@ -0,0 +1,98 @@
1/*
2 * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 */
9
10#ifndef _SUNXI_ENGINE_H_
11#define _SUNXI_ENGINE_H_
12
13struct drm_plane;
14struct drm_device;
15
16struct sunxi_engine;
17
18struct sunxi_engine_ops {
19 void (*commit)(struct sunxi_engine *engine);
20 struct drm_plane **(*layers_init)(struct drm_device *drm,
21 struct sunxi_engine *engine);
22
23 void (*apply_color_correction)(struct sunxi_engine *engine);
24 void (*disable_color_correction)(struct sunxi_engine *engine);
25};
26
27/**
28 * struct sunxi_engine - the common parts of an engine for sun4i-drm driver
29 * @ops: the operations of the engine
30 * @node: the of device node of the engine
31 * @regs: the regmap of the engine
32 * @id: the id of the engine (-1 if not used)
33 */
34struct sunxi_engine {
35 const struct sunxi_engine_ops *ops;
36
37 struct device_node *node;
38 struct regmap *regs;
39
40 int id;
41
42 /* Engine list management */
43 struct list_head list;
44};
45
46/**
47 * sunxi_engine_commit() - commit all changes of the engine
48 * @engine: pointer to the engine
49 */
50static inline void
51sunxi_engine_commit(struct sunxi_engine *engine)
52{
53 if (engine->ops && engine->ops->commit)
54 engine->ops->commit(engine);
55}
56
57/**
58 * sunxi_engine_layers_init() - Create planes (layers) for the engine
59 * @drm: pointer to the drm_device for which planes will be created
60 * @engine: pointer to the engine
61 */
62static inline struct drm_plane **
63sunxi_engine_layers_init(struct drm_device *drm, struct sunxi_engine *engine)
64{
65 if (engine->ops && engine->ops->layers_init)
66 return engine->ops->layers_init(drm, engine);
67 return ERR_PTR(-ENOSYS);
68}
69
70/**
71 * sunxi_engine_apply_color_correction - Apply the RGB2YUV color correction
72 * @engine: pointer to the engine
73 *
74 * This functionality is optional for an engine, however, if the engine is
75 * intended to be used with TV Encoder, the output will be incorrect
76 * without the color correction, due to TV Encoder expects the engine to
77 * output directly YUV signal.
78 */
79static inline void
80sunxi_engine_apply_color_correction(struct sunxi_engine *engine)
81{
82 if (engine->ops && engine->ops->apply_color_correction)
83 engine->ops->apply_color_correction(engine);
84}
85
86/**
87 * sunxi_engine_disable_color_correction - Disable the color space correction
88 * @engine: pointer to the engine
89 *
90 * This function is paired with apply_color_correction().
91 */
92static inline void
93sunxi_engine_disable_color_correction(struct sunxi_engine *engine)
94{
95 if (engine->ops && engine->ops->disable_color_correction)
96 engine->ops->disable_color_correction(engine);
97}
98#endif /* _SUNXI_ENGINE_H_ */