Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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, 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
 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, 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
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(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
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(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
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(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->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
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, 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}