xref: /linux/drivers/gpu/drm/logicvc/logicvc_crtc.c (revision e5f596bc25925e33bb4da8bc7976ecd808e9f6d0)
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, 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  
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  
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  
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  
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  
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  
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