xref: /linux/drivers/gpu/drm/logicvc/logicvc_crtc.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019-2022 Bootlin
4  * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
5  */
6 
7 #include <linux/of.h>
8 #include <linux/of_graph.h>
9 #include <linux/types.h>
10 #include <linux/workqueue.h>
11 
12 #include <drm/drm_atomic_helper.h>
13 #include <drm/drm_crtc.h>
14 #include <drm/drm_drv.h>
15 #include <drm/drm_gem_dma_helper.h>
16 #include <drm/drm_print.h>
17 #include <drm/drm_vblank.h>
18 
19 #include "logicvc_crtc.h"
20 #include "logicvc_drm.h"
21 #include "logicvc_interface.h"
22 #include "logicvc_layer.h"
23 #include "logicvc_regs.h"
24 
25 #define logicvc_crtc(c) \
26 	container_of(c, struct logicvc_crtc, drm_crtc)
27 
28 static enum drm_mode_status
logicvc_crtc_mode_valid(struct drm_crtc * drm_crtc,const struct drm_display_mode * mode)29 logicvc_crtc_mode_valid(struct drm_crtc *drm_crtc,
30 			const struct drm_display_mode *mode)
31 {
32 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
33 		return -EINVAL;
34 
35 	return 0;
36 }
37 
logicvc_crtc_atomic_begin(struct drm_crtc * drm_crtc,struct drm_atomic_state * state)38 static void logicvc_crtc_atomic_begin(struct drm_crtc *drm_crtc,
39 				      struct drm_atomic_state *state)
40 {
41 	struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
42 	struct drm_crtc_state *old_state =
43 		drm_atomic_get_old_crtc_state(state, drm_crtc);
44 	struct drm_device *drm_dev = drm_crtc->dev;
45 	unsigned long flags;
46 
47 	/*
48 	 * We need to grab the pending event here if vblank was already enabled
49 	 * since we won't get a call to atomic_enable to grab it.
50 	 */
51 	if (drm_crtc->state->event && old_state->active) {
52 		spin_lock_irqsave(&drm_dev->event_lock, flags);
53 		WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
54 
55 		crtc->event = drm_crtc->state->event;
56 		drm_crtc->state->event = NULL;
57 
58 		spin_unlock_irqrestore(&drm_dev->event_lock, flags);
59 	}
60 }
61 
logicvc_crtc_atomic_enable(struct drm_crtc * drm_crtc,struct drm_atomic_state * state)62 static void logicvc_crtc_atomic_enable(struct drm_crtc *drm_crtc,
63 				       struct drm_atomic_state *state)
64 {
65 	struct logicvc_crtc *crtc = logicvc_crtc(drm_crtc);
66 	struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
67 	struct drm_crtc_state *old_state =
68 		drm_atomic_get_old_crtc_state(state, drm_crtc);
69 	struct drm_crtc_state *new_state =
70 		drm_atomic_get_new_crtc_state(state, drm_crtc);
71 	struct drm_display_mode *mode = &new_state->adjusted_mode;
72 
73 	struct drm_device *drm_dev = drm_crtc->dev;
74 	unsigned int hact, hfp, hsl, hbp;
75 	unsigned int vact, vfp, vsl, vbp;
76 	unsigned long flags;
77 	u32 ctrl;
78 
79 	/* Timings */
80 
81 	hact = mode->hdisplay;
82 	hfp = mode->hsync_start - mode->hdisplay;
83 	hsl = mode->hsync_end - mode->hsync_start;
84 	hbp = mode->htotal - mode->hsync_end;
85 
86 	vact = mode->vdisplay;
87 	vfp = mode->vsync_start - mode->vdisplay;
88 	vsl = mode->vsync_end - mode->vsync_start;
89 	vbp = mode->vtotal - mode->vsync_end;
90 
91 	regmap_write(logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, hfp - 1);
92 	regmap_write(logicvc->regmap, LOGICVC_HSYNC_REG, hsl - 1);
93 	regmap_write(logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, hbp - 1);
94 	regmap_write(logicvc->regmap, LOGICVC_HRES_REG, hact - 1);
95 
96 	regmap_write(logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, vfp - 1);
97 	regmap_write(logicvc->regmap, LOGICVC_VSYNC_REG, vsl - 1);
98 	regmap_write(logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, vbp - 1);
99 	regmap_write(logicvc->regmap, LOGICVC_VRES_REG, vact - 1);
100 
101 	/* Signals */
102 
103 	ctrl = LOGICVC_CTRL_HSYNC_ENABLE | LOGICVC_CTRL_VSYNC_ENABLE |
104 	       LOGICVC_CTRL_DE_ENABLE;
105 
106 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
107 		ctrl |= LOGICVC_CTRL_HSYNC_INVERT;
108 
109 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
110 		ctrl |= LOGICVC_CTRL_VSYNC_INVERT;
111 
112 	if (logicvc->interface) {
113 		struct drm_connector *connector =
114 			&logicvc->interface->drm_connector;
115 		struct drm_display_info *display_info =
116 			&connector->display_info;
117 
118 		if (display_info->bus_flags & DRM_BUS_FLAG_DE_LOW)
119 			ctrl |= LOGICVC_CTRL_DE_INVERT;
120 
121 		if (display_info->bus_flags &
122 		    DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
123 			ctrl |= LOGICVC_CTRL_CLOCK_INVERT;
124 	}
125 
126 	regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
127 			   LOGICVC_CTRL_HSYNC_ENABLE |
128 			   LOGICVC_CTRL_HSYNC_INVERT |
129 			   LOGICVC_CTRL_VSYNC_ENABLE |
130 			   LOGICVC_CTRL_VSYNC_INVERT |
131 			   LOGICVC_CTRL_DE_ENABLE |
132 			   LOGICVC_CTRL_DE_INVERT |
133 			   LOGICVC_CTRL_PIXEL_INVERT |
134 			   LOGICVC_CTRL_CLOCK_INVERT, ctrl);
135 
136 	/* Generate internal state reset. */
137 	regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
138 
139 	drm_crtc_vblank_on(drm_crtc);
140 
141 	/* Register our event after vblank is enabled. */
142 	if (drm_crtc->state->event && !old_state->active) {
143 		spin_lock_irqsave(&drm_dev->event_lock, flags);
144 		WARN_ON(drm_crtc_vblank_get(drm_crtc) != 0);
145 
146 		crtc->event = drm_crtc->state->event;
147 		drm_crtc->state->event = NULL;
148 		spin_unlock_irqrestore(&drm_dev->event_lock, flags);
149 	}
150 }
151 
logicvc_crtc_atomic_disable(struct drm_crtc * drm_crtc,struct drm_atomic_state * state)152 static void logicvc_crtc_atomic_disable(struct drm_crtc *drm_crtc,
153 					struct drm_atomic_state *state)
154 {
155 	struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
156 	struct drm_device *drm_dev = drm_crtc->dev;
157 
158 	drm_crtc_vblank_off(drm_crtc);
159 
160 	/* Disable and clear CRTC bits. */
161 	regmap_update_bits(logicvc->regmap, LOGICVC_CTRL_REG,
162 			   LOGICVC_CTRL_HSYNC_ENABLE |
163 			   LOGICVC_CTRL_HSYNC_INVERT |
164 			   LOGICVC_CTRL_VSYNC_ENABLE |
165 			   LOGICVC_CTRL_VSYNC_INVERT |
166 			   LOGICVC_CTRL_DE_ENABLE |
167 			   LOGICVC_CTRL_DE_INVERT |
168 			   LOGICVC_CTRL_PIXEL_INVERT |
169 			   LOGICVC_CTRL_CLOCK_INVERT, 0);
170 
171 	/* Generate internal state reset. */
172 	regmap_write(logicvc->regmap, LOGICVC_DTYPE_REG, 0);
173 
174 	/* Consume any leftover event since vblank is now disabled. */
175 	if (drm_crtc->state->event && !drm_crtc->state->active) {
176 		spin_lock_irq(&drm_dev->event_lock);
177 
178 		drm_crtc_send_vblank_event(drm_crtc, drm_crtc->state->event);
179 		drm_crtc->state->event = NULL;
180 		spin_unlock_irq(&drm_dev->event_lock);
181 	}
182 }
183 
184 static const struct drm_crtc_helper_funcs logicvc_crtc_helper_funcs = {
185 	.mode_valid		= logicvc_crtc_mode_valid,
186 	.atomic_begin		= logicvc_crtc_atomic_begin,
187 	.atomic_enable		= logicvc_crtc_atomic_enable,
188 	.atomic_disable		= logicvc_crtc_atomic_disable,
189 };
190 
logicvc_crtc_enable_vblank(struct drm_crtc * drm_crtc)191 static int logicvc_crtc_enable_vblank(struct drm_crtc *drm_crtc)
192 {
193 	struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
194 
195 	/* Clear any pending V_SYNC interrupt. */
196 	regmap_write_bits(logicvc->regmap, LOGICVC_INT_STAT_REG,
197 			  LOGICVC_INT_STAT_V_SYNC, LOGICVC_INT_STAT_V_SYNC);
198 
199 	/* Unmask V_SYNC interrupt. */
200 	regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
201 			  LOGICVC_INT_MASK_V_SYNC, 0);
202 
203 	return 0;
204 }
205 
logicvc_crtc_disable_vblank(struct drm_crtc * drm_crtc)206 static void logicvc_crtc_disable_vblank(struct drm_crtc *drm_crtc)
207 {
208 	struct logicvc_drm *logicvc = logicvc_drm(drm_crtc->dev);
209 
210 	/* Mask V_SYNC interrupt. */
211 	regmap_write_bits(logicvc->regmap, LOGICVC_INT_MASK_REG,
212 			  LOGICVC_INT_MASK_V_SYNC, LOGICVC_INT_MASK_V_SYNC);
213 }
214 
215 static const struct drm_crtc_funcs logicvc_crtc_funcs = {
216 	.reset			= drm_atomic_helper_crtc_reset,
217 	.destroy		= drm_crtc_cleanup,
218 	.set_config		= drm_atomic_helper_set_config,
219 	.page_flip		= drm_atomic_helper_page_flip,
220 	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
221 	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
222 	.enable_vblank		= logicvc_crtc_enable_vblank,
223 	.disable_vblank		= logicvc_crtc_disable_vblank,
224 };
225 
logicvc_crtc_vblank_handler(struct logicvc_drm * logicvc)226 void logicvc_crtc_vblank_handler(struct logicvc_drm *logicvc)
227 {
228 	struct drm_device *drm_dev = &logicvc->drm_dev;
229 	struct logicvc_crtc *crtc = logicvc->crtc;
230 	unsigned long flags;
231 
232 	if (!crtc)
233 		return;
234 
235 	drm_crtc_handle_vblank(&crtc->drm_crtc);
236 
237 	if (crtc->event) {
238 		spin_lock_irqsave(&drm_dev->event_lock, flags);
239 		drm_crtc_send_vblank_event(&crtc->drm_crtc, crtc->event);
240 		drm_crtc_vblank_put(&crtc->drm_crtc);
241 		crtc->event = NULL;
242 		spin_unlock_irqrestore(&drm_dev->event_lock, flags);
243 	}
244 }
245 
logicvc_crtc_init(struct logicvc_drm * logicvc)246 int logicvc_crtc_init(struct logicvc_drm *logicvc)
247 {
248 	struct drm_device *drm_dev = &logicvc->drm_dev;
249 	struct device *dev = drm_dev->dev;
250 	struct device_node *of_node = dev->of_node;
251 	struct logicvc_crtc *crtc;
252 	struct logicvc_layer *layer_primary;
253 	int ret;
254 
255 	crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
256 	if (!crtc)
257 		return -ENOMEM;
258 
259 	layer_primary = logicvc_layer_get_primary(logicvc);
260 	if (!layer_primary) {
261 		drm_err(drm_dev, "Failed to get primary layer\n");
262 		return -EINVAL;
263 	}
264 
265 	ret = drm_crtc_init_with_planes(drm_dev, &crtc->drm_crtc,
266 					&layer_primary->drm_plane, NULL,
267 					&logicvc_crtc_funcs, NULL);
268 	if (ret) {
269 		drm_err(drm_dev, "Failed to initialize CRTC\n");
270 		return ret;
271 	}
272 
273 	drm_crtc_helper_add(&crtc->drm_crtc, &logicvc_crtc_helper_funcs);
274 
275 	crtc->drm_crtc.port = of_graph_get_port_by_id(of_node, 1);
276 
277 	logicvc->crtc = crtc;
278 
279 	return 0;
280 }
281