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 |
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 | |
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, crtc: 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(lock: &drm_dev->event_lock, flags); |
59 | } |
60 | } |
61 | |
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, crtc: drm_crtc); |
69 | struct drm_crtc_state *new_state = |
70 | drm_atomic_get_new_crtc_state(state, crtc: 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(map: logicvc->regmap, LOGICVC_HSYNC_FRONT_PORCH_REG, val: hfp - 1); |
92 | regmap_write(map: logicvc->regmap, LOGICVC_HSYNC_REG, val: hsl - 1); |
93 | regmap_write(map: logicvc->regmap, LOGICVC_HSYNC_BACK_PORCH_REG, val: hbp - 1); |
94 | regmap_write(map: logicvc->regmap, LOGICVC_HRES_REG, val: hact - 1); |
95 | |
96 | regmap_write(map: logicvc->regmap, LOGICVC_VSYNC_FRONT_PORCH_REG, val: vfp - 1); |
97 | regmap_write(map: logicvc->regmap, LOGICVC_VSYNC_REG, val: vsl - 1); |
98 | regmap_write(map: logicvc->regmap, LOGICVC_VSYNC_BACK_PORCH_REG, val: vbp - 1); |
99 | regmap_write(map: logicvc->regmap, LOGICVC_VRES_REG, val: 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(map: 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, val: ctrl); |
135 | |
136 | /* Generate internal state reset. */ |
137 | regmap_write(map: logicvc->regmap, LOGICVC_DTYPE_REG, val: 0); |
138 | |
139 | drm_crtc_vblank_on(crtc: 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(lock: &drm_dev->event_lock, flags); |
149 | } |
150 | } |
151 | |
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(crtc: drm_crtc); |
159 | |
160 | /* Disable and clear CRTC bits. */ |
161 | regmap_update_bits(map: 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, val: 0); |
170 | |
171 | /* Generate internal state reset. */ |
172 | regmap_write(map: logicvc->regmap, LOGICVC_DTYPE_REG, val: 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(lock: &drm_dev->event_lock); |
177 | |
178 | drm_crtc_send_vblank_event(crtc: drm_crtc, e: drm_crtc->state->event); |
179 | drm_crtc->state->event = NULL; |
180 | spin_unlock_irq(lock: &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 | |
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(map: 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(map: logicvc->regmap, LOGICVC_INT_MASK_REG, |
201 | LOGICVC_INT_MASK_V_SYNC, val: 0); |
202 | |
203 | return 0; |
204 | } |
205 | |
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(map: 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 | |
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: &crtc->drm_crtc); |
236 | |
237 | if (crtc->event) { |
238 | spin_lock_irqsave(&drm_dev->event_lock, flags); |
239 | drm_crtc_send_vblank_event(crtc: &crtc->drm_crtc, e: crtc->event); |
240 | drm_crtc_vblank_put(crtc: &crtc->drm_crtc); |
241 | crtc->event = NULL; |
242 | spin_unlock_irqrestore(lock: &drm_dev->event_lock, flags); |
243 | } |
244 | } |
245 | |
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, size: 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(dev: drm_dev, crtc: &crtc->drm_crtc, |
266 | primary: &layer_primary->drm_plane, NULL, |
267 | funcs: &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: &crtc->drm_crtc, funcs: &logicvc_crtc_helper_funcs); |
274 | |
275 | crtc->drm_crtc.port = of_graph_get_port_by_id(node: of_node, id: 1); |
276 | |
277 | logicvc->crtc = crtc; |
278 | |
279 | return 0; |
280 | } |
281 | |