xref: /linux/drivers/gpu/drm/sun4i/sun4i_crtc.c (revision 52338415)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2015 Free Electrons
4  * Copyright (C) 2015 NextThing Co
5  *
6  * Maxime Ripard <maxime.ripard@free-electrons.com>
7  */
8 
9 #include <linux/clk-provider.h>
10 #include <linux/ioport.h>
11 #include <linux/of_address.h>
12 #include <linux/of_graph.h>
13 #include <linux/of_irq.h>
14 #include <linux/regmap.h>
15 
16 #include <video/videomode.h>
17 
18 #include <drm/drm_atomic_helper.h>
19 #include <drm/drm_crtc.h>
20 #include <drm/drm_modes.h>
21 #include <drm/drm_print.h>
22 #include <drm/drm_probe_helper.h>
23 #include <drm/drm_vblank.h>
24 
25 #include "sun4i_backend.h"
26 #include "sun4i_crtc.h"
27 #include "sun4i_drv.h"
28 #include "sunxi_engine.h"
29 #include "sun4i_tcon.h"
30 
31 /*
32  * While this isn't really working in the DRM theory, in practice we
33  * can only ever have one encoder per TCON since we have a mux in our
34  * TCON.
35  */
36 static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
37 {
38 	struct drm_encoder *encoder;
39 
40 	drm_for_each_encoder(encoder, crtc->dev)
41 		if (encoder->crtc == crtc)
42 			return encoder;
43 
44 	return NULL;
45 }
46 
47 static int sun4i_crtc_atomic_check(struct drm_crtc *crtc,
48 				    struct drm_crtc_state *state)
49 {
50 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
51 	struct sunxi_engine *engine = scrtc->engine;
52 	int ret = 0;
53 
54 	if (engine && engine->ops && engine->ops->atomic_check)
55 		ret = engine->ops->atomic_check(engine, state);
56 
57 	return ret;
58 }
59 
60 static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
61 				    struct drm_crtc_state *old_state)
62 {
63 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
64 	struct drm_device *dev = crtc->dev;
65 	struct sunxi_engine *engine = scrtc->engine;
66 	unsigned long flags;
67 
68 	if (crtc->state->event) {
69 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
70 
71 		spin_lock_irqsave(&dev->event_lock, flags);
72 		scrtc->event = crtc->state->event;
73 		spin_unlock_irqrestore(&dev->event_lock, flags);
74 		crtc->state->event = NULL;
75 	}
76 
77 	if (engine->ops->atomic_begin)
78 		engine->ops->atomic_begin(engine, old_state);
79 }
80 
81 static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
82 				    struct drm_crtc_state *old_state)
83 {
84 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
85 	struct drm_pending_vblank_event *event = crtc->state->event;
86 
87 	DRM_DEBUG_DRIVER("Committing plane changes\n");
88 
89 	sunxi_engine_commit(scrtc->engine);
90 
91 	if (event) {
92 		crtc->state->event = NULL;
93 
94 		spin_lock_irq(&crtc->dev->event_lock);
95 		if (drm_crtc_vblank_get(crtc) == 0)
96 			drm_crtc_arm_vblank_event(crtc, event);
97 		else
98 			drm_crtc_send_vblank_event(crtc, event);
99 		spin_unlock_irq(&crtc->dev->event_lock);
100 	}
101 }
102 
103 static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
104 				      struct drm_crtc_state *old_state)
105 {
106 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
107 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
108 
109 	DRM_DEBUG_DRIVER("Disabling the CRTC\n");
110 
111 	drm_crtc_vblank_off(crtc);
112 
113 	sun4i_tcon_set_status(scrtc->tcon, encoder, false);
114 
115 	if (crtc->state->event && !crtc->state->active) {
116 		spin_lock_irq(&crtc->dev->event_lock);
117 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
118 		spin_unlock_irq(&crtc->dev->event_lock);
119 
120 		crtc->state->event = NULL;
121 	}
122 }
123 
124 static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
125 				     struct drm_crtc_state *old_state)
126 {
127 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
128 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
129 
130 	DRM_DEBUG_DRIVER("Enabling the CRTC\n");
131 
132 	sun4i_tcon_set_status(scrtc->tcon, encoder, true);
133 
134 	drm_crtc_vblank_on(crtc);
135 }
136 
137 static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
138 {
139 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
140 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
141 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
142 
143 	sun4i_tcon_mode_set(scrtc->tcon, encoder, mode);
144 }
145 
146 static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
147 	.atomic_check	= sun4i_crtc_atomic_check,
148 	.atomic_begin	= sun4i_crtc_atomic_begin,
149 	.atomic_flush	= sun4i_crtc_atomic_flush,
150 	.atomic_enable	= sun4i_crtc_atomic_enable,
151 	.atomic_disable	= sun4i_crtc_atomic_disable,
152 	.mode_set_nofb	= sun4i_crtc_mode_set_nofb,
153 };
154 
155 static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc)
156 {
157 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
158 
159 	DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc);
160 
161 	sun4i_tcon_enable_vblank(scrtc->tcon, true);
162 
163 	return 0;
164 }
165 
166 static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc)
167 {
168 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
169 
170 	DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc);
171 
172 	sun4i_tcon_enable_vblank(scrtc->tcon, false);
173 }
174 
175 static const struct drm_crtc_funcs sun4i_crtc_funcs = {
176 	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
177 	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
178 	.destroy		= drm_crtc_cleanup,
179 	.page_flip		= drm_atomic_helper_page_flip,
180 	.reset			= drm_atomic_helper_crtc_reset,
181 	.set_config		= drm_atomic_helper_set_config,
182 	.enable_vblank		= sun4i_crtc_enable_vblank,
183 	.disable_vblank		= sun4i_crtc_disable_vblank,
184 };
185 
186 struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
187 				   struct sunxi_engine *engine,
188 				   struct sun4i_tcon *tcon)
189 {
190 	struct sun4i_crtc *scrtc;
191 	struct drm_plane **planes;
192 	struct drm_plane *primary = NULL, *cursor = NULL;
193 	int ret, i;
194 
195 	scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
196 	if (!scrtc)
197 		return ERR_PTR(-ENOMEM);
198 	scrtc->engine = engine;
199 	scrtc->tcon = tcon;
200 
201 	/* Create our layers */
202 	planes = sunxi_engine_layers_init(drm, engine);
203 	if (IS_ERR(planes)) {
204 		dev_err(drm->dev, "Couldn't create the planes\n");
205 		return NULL;
206 	}
207 
208 	/* find primary and cursor planes for drm_crtc_init_with_planes */
209 	for (i = 0; planes[i]; i++) {
210 		struct drm_plane *plane = planes[i];
211 
212 		switch (plane->type) {
213 		case DRM_PLANE_TYPE_PRIMARY:
214 			primary = plane;
215 			break;
216 		case DRM_PLANE_TYPE_CURSOR:
217 			cursor = plane;
218 			break;
219 		default:
220 			break;
221 		}
222 	}
223 
224 	ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
225 					primary,
226 					cursor,
227 					&sun4i_crtc_funcs,
228 					NULL);
229 	if (ret) {
230 		dev_err(drm->dev, "Couldn't init DRM CRTC\n");
231 		return ERR_PTR(ret);
232 	}
233 
234 	drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
235 
236 	/* Set crtc.port to output port node of the tcon */
237 	scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node,
238 						   1);
239 
240 	/* Set possible_crtcs to this crtc for overlay planes */
241 	for (i = 0; planes[i]; i++) {
242 		uint32_t possible_crtcs = drm_crtc_mask(&scrtc->crtc);
243 		struct drm_plane *plane = planes[i];
244 
245 		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
246 			plane->possible_crtcs = possible_crtcs;
247 	}
248 
249 	return scrtc;
250 }
251