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
28static enum drm_mode_status
29logicvc_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
38static 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
62static 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
152static 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
184static 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
191static 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
206static 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
215static 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
226void 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
246int 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

source code of linux/drivers/gpu/drm/logicvc/logicvc_crtc.c