File Index
Files and directories
Sign in
FILE

dc-crtc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/imx/dc/dc-crtc.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright 2024 NXP
  4 */
  5
  6#include <linux/completion.h>
  7#include <linux/container_of.h>
  8#include <linux/interrupt.h>
  9#include <linux/irqreturn.h>
 10#include <linux/pm_runtime.h>
 11#include <linux/spinlock.h>
 12
 13#include <drm/drm_atomic.h>
 14#include <drm/drm_atomic_helper.h>
 15#include <drm/drm_atomic_state_helper.h>
 16#include <drm/drm_crtc.h>
 17#include <drm/drm_device.h>
 18#include <drm/drm_drv.h>
 19#include <drm/drm_modes.h>
 20#include <drm/drm_modeset_helper_vtables.h>
 21#include <drm/drm_plane.h>
 22#include <drm/drm_print.h>
 23#include <drm/drm_vblank.h>
 24
 25#include "dc-de.h"
 26#include "dc-drv.h"
 27#include "dc-kms.h"
 28#include "dc-pe.h"
 29
 30#define dc_crtc_dbg(crtc, fmt, ...)					\
 31do {									\
 32	struct drm_crtc *_crtc = (crtc);				\
 33	drm_dbg_kms(_crtc->dev, "[CRTC:%d:%s] " fmt,			\
 34		    _crtc->base.id, _crtc->name, ##__VA_ARGS__);	\
 35} while (0)
 36
 37#define dc_crtc_err(crtc, fmt, ...)					\
 38do {									\
 39	struct drm_crtc *_crtc = (crtc);				\
 40	drm_err(_crtc->dev, "[CRTC:%d:%s] " fmt,			\
 41		_crtc->base.id, _crtc->name, ##__VA_ARGS__);		\
 42} while (0)
 43
 44#define DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(c)				\
 45do {									\
 46	unsigned long ret;						\
 47	ret = wait_for_completion_timeout(&dc_crtc->c, HZ);		\
 48	if (ret == 0)							\
 49		dc_crtc_err(crtc, "%s: wait for " #c " timeout\n",	\
 50							__func__);	\
 51} while (0)
 52
 53#define DC_CRTC_CHECK_FRAMEGEN_FIFO(fg)					\
 54do {									\
 55	struct dc_fg *_fg = (fg);					\
 56	if (dc_fg_secondary_requests_to_read_empty_fifo(_fg)) {		\
 57		dc_fg_secondary_clear_channel_status(_fg);		\
 58		dc_crtc_err(crtc, "%s: FrameGen FIFO empty\n",		\
 59							__func__);	\
 60	}								\
 61} while (0)
 62
 63#define DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(fg)			\
 64do {									\
 65	if (dc_fg_wait_for_secondary_syncup(fg))			\
 66		dc_crtc_err(crtc,					\
 67			"%s: FrameGen secondary channel isn't syncup\n",\
 68							__func__);	\
 69} while (0)
 70
 71static inline struct dc_crtc *to_dc_crtc(struct drm_crtc *crtc)
 72{
 73	return container_of(crtc, struct dc_crtc, base);
 74}
 75
 76static u32 dc_crtc_get_vblank_counter(struct drm_crtc *crtc)
 77{
 78	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
 79
 80	return dc_fg_get_frame_index(dc_crtc->fg);
 81}
 82
 83static int dc_crtc_enable_vblank(struct drm_crtc *crtc)
 84{
 85	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
 86
 87	enable_irq(dc_crtc->irq_dec_framecomplete);
 88
 89	return 0;
 90}
 91
 92static void dc_crtc_disable_vblank(struct drm_crtc *crtc)
 93{
 94	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
 95
 96	/* nosync due to atomic context */
 97	disable_irq_nosync(dc_crtc->irq_dec_framecomplete);
 98}
 99
100static const struct drm_crtc_funcs dc_crtc_funcs = {
101	.reset			= drm_atomic_helper_crtc_reset,
102	.destroy		= drm_crtc_cleanup,
103	.set_config		= drm_atomic_helper_set_config,
104	.page_flip		= drm_atomic_helper_page_flip,
105	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
106	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
107	.get_vblank_counter	= dc_crtc_get_vblank_counter,
108	.enable_vblank		= dc_crtc_enable_vblank,
109	.disable_vblank		= dc_crtc_disable_vblank,
110	.get_vblank_timestamp	= drm_crtc_vblank_helper_get_vblank_timestamp,
111};
112
113static void dc_crtc_queue_state_event(struct drm_crtc_state *crtc_state)
114{
115	struct drm_crtc *crtc = crtc_state->crtc;
116	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
117
118	spin_lock_irq(&crtc->dev->event_lock);
119	if (crtc_state->event) {
120		WARN_ON(drm_crtc_vblank_get(crtc));
121		WARN_ON(dc_crtc->event);
122		dc_crtc->event = crtc_state->event;
123		crtc_state->event = NULL;
124	}
125	spin_unlock_irq(&crtc->dev->event_lock);
126}
127
128static inline enum drm_mode_status
129dc_crtc_check_clock(struct dc_crtc *dc_crtc, int clk_khz)
130{
131	return dc_fg_check_clock(dc_crtc->fg, clk_khz);
132}
133
134static enum drm_mode_status
135dc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
136{
137	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
138	enum drm_mode_status status;
139
140	status = dc_crtc_check_clock(dc_crtc, mode->clock);
141	if (status != MODE_OK)
142		return status;
143
144	if (mode->crtc_clock > DC_FRAMEGEN_MAX_CLOCK_KHZ)
145		return MODE_CLOCK_HIGH;
146
147	return MODE_OK;
148}
149
150static int
151dc_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
152{
153	struct drm_crtc_state *new_crtc_state =
154				drm_atomic_get_new_crtc_state(state, crtc);
155	struct drm_display_mode *adj = &new_crtc_state->adjusted_mode;
156	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
157	enum drm_mode_status status;
158
159	status = dc_crtc_check_clock(dc_crtc, adj->clock);
160	if (status != MODE_OK)
161		return -EINVAL;
162
163	return 0;
164}
165
166static void
167dc_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state)
168{
169	struct drm_crtc_state *new_crtc_state =
170				drm_atomic_get_new_crtc_state(state, crtc);
171	struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
172	int idx, ret;
173
174	if (!drm_atomic_crtc_needs_modeset(new_crtc_state) ||
175	    !new_crtc_state->active)
176		return;
177
178	if (!drm_dev_enter(crtc->dev, &idx))
179		return;
180
181	/* request pixel engine power-on when CRTC starts to be active */
182	ret = pm_runtime_resume_and_get(dc_drm->pe->dev);
183	if (ret)
184		dc_crtc_err(crtc, "failed to get DC pixel engine RPM: %d\n",
185			    ret);
186
187	drm_dev_exit(idx);
188}
189
190static void
191dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state)
192{
193	struct drm_crtc_state *old_crtc_state =
194				drm_atomic_get_old_crtc_state(state, crtc);
195	struct drm_crtc_state *new_crtc_state =
196				drm_atomic_get_new_crtc_state(state, crtc);
197	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
198	int idx;
199
200	if (drm_atomic_crtc_needs_modeset(new_crtc_state) ||
201	    (!old_crtc_state->active && !new_crtc_state->active))
202		return;
203
204	if (!drm_dev_enter(crtc->dev, &idx))
205		goto out;
206
207	enable_irq(dc_crtc->irq_ed_cont_shdload);
208
209	/* flush plane update out to display */
210	dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
211
212	DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done);
213
214	disable_irq(dc_crtc->irq_ed_cont_shdload);
215
216	DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
217
218	drm_dev_exit(idx);
219
220out:
221	dc_crtc_queue_state_event(new_crtc_state);
222}
223
224static void
225dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state)
226{
227	struct drm_crtc_state *new_crtc_state =
228				drm_atomic_get_new_crtc_state(state, crtc);
229	struct drm_display_mode *adj = &new_crtc_state->adjusted_mode;
230	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
231	enum dc_link_id cf_link;
232	int idx, ret;
233
234	dc_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj));
235
236	drm_crtc_vblank_on(crtc);
237
238	if (!drm_dev_enter(crtc->dev, &idx))
239		goto out;
240
241	/* request display engine power-on when CRTC is enabled */
242	ret = pm_runtime_resume_and_get(dc_crtc->de->dev);
243	if (ret < 0)
244		dc_crtc_err(crtc, "failed to get DC display engine RPM: %d\n",
245			    ret);
246
247	enable_irq(dc_crtc->irq_dec_shdload);
248	enable_irq(dc_crtc->irq_ed_cont_shdload);
249	enable_irq(dc_crtc->irq_ed_safe_shdload);
250
251	dc_fg_cfg_videomode(dc_crtc->fg, adj);
252
253	dc_cf_framedimensions(dc_crtc->cf_cont,
254			      adj->crtc_hdisplay, adj->crtc_vdisplay);
255	dc_cf_framedimensions(dc_crtc->cf_safe,
256			      adj->crtc_hdisplay, adj->crtc_vdisplay);
257
258	/* constframe in safety stream shows blue frame */
259	dc_cf_constantcolor_blue(dc_crtc->cf_safe);
260	cf_link = dc_cf_get_link_id(dc_crtc->cf_safe);
261	dc_ed_pec_src_sel(dc_crtc->ed_safe, cf_link);
262
263	/* show CRTC background if no plane is enabled */
264	if (new_crtc_state->plane_mask == 0) {
265		/* constframe in content stream shows black frame */
266		dc_cf_constantcolor_black(dc_crtc->cf_cont);
267
268		cf_link = dc_cf_get_link_id(dc_crtc->cf_cont);
269		dc_ed_pec_src_sel(dc_crtc->ed_cont, cf_link);
270	}
271
272	dc_fg_enable_clock(dc_crtc->fg);
273	dc_ed_pec_sync_trigger(dc_crtc->ed_cont);
274	dc_ed_pec_sync_trigger(dc_crtc->ed_safe);
275	dc_fg_shdtokgen(dc_crtc->fg);
276	dc_fg_enable(dc_crtc->fg);
277
278	DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_safe_shdload_done);
279	DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done);
280	DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_shdload_done);
281
282	disable_irq(dc_crtc->irq_ed_safe_shdload);
283	disable_irq(dc_crtc->irq_ed_cont_shdload);
284	disable_irq(dc_crtc->irq_dec_shdload);
285
286	DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(dc_crtc->fg);
287
288	DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg);
289
290	drm_dev_exit(idx);
291
292out:
293	dc_crtc_queue_state_event(new_crtc_state);
294}
295
296static void
297dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state)
298{
299	struct drm_crtc_state *new_crtc_state =
300				drm_atomic_get_new_crtc_state(state, crtc);
301	struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev);
302	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
303	int idx, ret;
304
305	if (!drm_dev_enter(crtc->dev, &idx))
306		goto out;
307
308	enable_irq(dc_crtc->irq_dec_seqcomplete);
309	dc_fg_disable(dc_crtc->fg);
310	DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done);
311	disable_irq(dc_crtc->irq_dec_seqcomplete);
312
313	dc_fg_disable_clock(dc_crtc->fg);
314
315	/* request pixel engine power-off as plane is off too */
316	ret = pm_runtime_put(dc_drm->pe->dev);
317	if (ret)
318		dc_crtc_err(crtc, "failed to put DC pixel engine RPM: %d\n",
319			    ret);
320
321	/* request display engine power-off when CRTC is disabled */
322	ret = pm_runtime_put(dc_crtc->de->dev);
323	if (ret < 0)
324		dc_crtc_err(crtc, "failed to put DC display engine RPM: %d\n",
325			    ret);
326
327	drm_dev_exit(idx);
328
329out:
330	drm_crtc_vblank_off(crtc);
331
332	spin_lock_irq(&crtc->dev->event_lock);
333	if (new_crtc_state->event && !new_crtc_state->active) {
334		drm_crtc_send_vblank_event(crtc, new_crtc_state->event);
335		new_crtc_state->event = NULL;
336	}
337	spin_unlock_irq(&crtc->dev->event_lock);
338}
339
340static bool dc_crtc_get_scanout_position(struct drm_crtc *crtc,
341					 bool in_vblank_irq,
342					 int *vpos, int *hpos,
343					 ktime_t *stime, ktime_t *etime,
344					 const struct drm_display_mode *mode)
345{
346	struct dc_crtc *dc_crtc = to_dc_crtc(crtc);
347	int vdisplay = mode->crtc_vdisplay;
348	int vtotal = mode->crtc_vtotal;
349	bool reliable;
350	int line;
351	int idx;
352
353	if (stime)
354		*stime = ktime_get();
355
356	if (!drm_dev_enter(crtc->dev, &idx)) {
357		reliable = false;
358		*vpos = 0;
359		*hpos = 0;
360		goto out;
361	}
362
363	/* line index starts with 0 for the first active output line */
364	line = dc_fg_get_line_index(dc_crtc->fg);
365
366	if (line < vdisplay)
367		/* active scanout area - positive */
368		*vpos = line + 1;
369	else
370		/* inside vblank - negative */
371		*vpos = line - (vtotal - 1);
372
373	*hpos = 0;
374
375	reliable = true;
376
377	drm_dev_exit(idx);
378out:
379	if (etime)
380		*etime = ktime_get();
381
382	return reliable;
383}
384
385static const struct drm_crtc_helper_funcs dc_helper_funcs = {
386	.mode_valid		= dc_crtc_mode_valid,
387	.atomic_check		= dc_crtc_atomic_check,
388	.atomic_begin		= dc_crtc_atomic_begin,
389	.atomic_flush		= dc_crtc_atomic_flush,
390	.atomic_enable		= dc_crtc_atomic_enable,
391	.atomic_disable		= dc_crtc_atomic_disable,
392	.get_scanout_position	= dc_crtc_get_scanout_position,
393};
394
395static irqreturn_t dc_crtc_irq_handler_dec_framecomplete(int irq, void *dev_id)
396{
397	struct dc_crtc *dc_crtc = dev_id;
398	struct drm_crtc *crtc = &dc_crtc->base;
399	unsigned long flags;
400
401	drm_crtc_handle_vblank(crtc);
402
403	spin_lock_irqsave(&crtc->dev->event_lock, flags);
404	if (dc_crtc->event) {
405		drm_crtc_send_vblank_event(crtc, dc_crtc->event);
406		dc_crtc->event = NULL;
407		drm_crtc_vblank_put(crtc);
408	}
409	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
410
411	return IRQ_HANDLED;
412}
413
414static irqreturn_t
415dc_crtc_irq_handler_dec_seqcomplete_done(int irq, void *dev_id)
416{
417	struct dc_crtc *dc_crtc = dev_id;
418
419	complete(&dc_crtc->dec_seqcomplete_done);
420
421	return IRQ_HANDLED;
422}
423
424static irqreturn_t dc_crtc_irq_handler_dec_shdload_done(int irq, void *dev_id)
425{
426	struct dc_crtc *dc_crtc = dev_id;
427
428	complete(&dc_crtc->dec_shdload_done);
429
430	return IRQ_HANDLED;
431}
432
433static irqreturn_t
434dc_crtc_irq_handler_ed_cont_shdload_done(int irq, void *dev_id)
435{
436	struct dc_crtc *dc_crtc = dev_id;
437
438	complete(&dc_crtc->ed_cont_shdload_done);
439
440	return IRQ_HANDLED;
441}
442
443static irqreturn_t
444dc_crtc_irq_handler_ed_safe_shdload_done(int irq, void *dev_id)
445{
446	struct dc_crtc *dc_crtc = dev_id;
447
448	complete(&dc_crtc->ed_safe_shdload_done);
449
450	return IRQ_HANDLED;
451}
452
453static int dc_crtc_request_irqs(struct drm_device *drm, struct dc_crtc *dc_crtc)
454{
455	struct {
456		struct device *dev;
457		unsigned int irq;
458		irqreturn_t (*irq_handler)(int irq, void *dev_id);
459	} irqs[DC_CRTC_IRQS] = {
460		{
461			dc_crtc->de->dev,
462			dc_crtc->irq_dec_framecomplete,
463			dc_crtc_irq_handler_dec_framecomplete,
464		}, {
465			dc_crtc->de->dev,
466			dc_crtc->irq_dec_seqcomplete,
467			dc_crtc_irq_handler_dec_seqcomplete_done,
468		}, {
469			dc_crtc->de->dev,
470			dc_crtc->irq_dec_shdload,
471			dc_crtc_irq_handler_dec_shdload_done,
472		}, {
473			dc_crtc->ed_cont->dev,
474			dc_crtc->irq_ed_cont_shdload,
475			dc_crtc_irq_handler_ed_cont_shdload_done,
476		}, {
477			dc_crtc->ed_safe->dev,
478			dc_crtc->irq_ed_safe_shdload,
479			dc_crtc_irq_handler_ed_safe_shdload_done,
480		},
481	};
482	int i, ret;
483
484	for (i = 0; i < DC_CRTC_IRQS; i++) {
485		struct dc_crtc_irq *irq = &dc_crtc->irqs[i];
486
487		ret = devm_request_irq(irqs[i].dev, irqs[i].irq,
488				       irqs[i].irq_handler, IRQF_NO_AUTOEN,
489				       dev_name(irqs[i].dev), dc_crtc);
490		if (ret) {
491			dev_err(irqs[i].dev, "failed to request irq(%u): %d\n",
492				irqs[i].irq, ret);
493			return ret;
494		}
495
496		irq->dc_crtc = dc_crtc;
497		irq->irq = irqs[i].irq;
498	}
499
500	return 0;
501}
502
503int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index)
504{
505	struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index];
506	struct drm_device *drm = &dc_drm->base;
507	struct dc_de *de = dc_drm->de[crtc_index];
508	struct dc_pe *pe = dc_drm->pe;
509	struct dc_plane *dc_primary;
510	int ret;
511
512	dc_crtc->de = de;
513
514	init_completion(&dc_crtc->dec_seqcomplete_done);
515	init_completion(&dc_crtc->dec_shdload_done);
516	init_completion(&dc_crtc->ed_cont_shdload_done);
517	init_completion(&dc_crtc->ed_safe_shdload_done);
518
519	dc_crtc->cf_cont = pe->cf_cont[crtc_index];
520	dc_crtc->cf_safe = pe->cf_safe[crtc_index];
521	dc_crtc->ed_cont = pe->ed_cont[crtc_index];
522	dc_crtc->ed_safe = pe->ed_safe[crtc_index];
523	dc_crtc->fg = de->fg;
524
525	dc_crtc->irq_dec_framecomplete = de->irq_framecomplete;
526	dc_crtc->irq_dec_seqcomplete = de->irq_seqcomplete;
527	dc_crtc->irq_dec_shdload = de->irq_shdload;
528	dc_crtc->irq_ed_safe_shdload = dc_crtc->ed_safe->irq_shdload;
529	dc_crtc->irq_ed_cont_shdload = dc_crtc->ed_cont->irq_shdload;
530
531	dc_primary = &dc_drm->dc_primary[crtc_index];
532	ret = dc_plane_init(dc_drm, dc_primary);
533	if (ret) {
534		dev_err(de->dev, "failed to initialize primary plane: %d\n",
535			ret);
536		return ret;
537	}
538
539	drm_crtc_helper_add(&dc_crtc->base, &dc_helper_funcs);
540
541	ret = drm_crtc_init_with_planes(drm, &dc_crtc->base, &dc_primary->base,
542					NULL, &dc_crtc_funcs, NULL);
543	if (ret)
544		dev_err(de->dev, "failed to add CRTC: %d\n", ret);
545
546	return ret;
547}
548
549int dc_crtc_post_init(struct dc_drm_device *dc_drm, int crtc_index)
550{
551	struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index];
552	struct drm_device *drm = &dc_drm->base;
553
554	return dc_crtc_request_irqs(drm, dc_crtc);
555}
~/ Read-only file preview.