xref: /linux/drivers/gpu/drm/sun4i/sun4i_crtc.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29026e0d1SMaxime Ripard /*
39026e0d1SMaxime Ripard  * Copyright (C) 2015 Free Electrons
49026e0d1SMaxime Ripard  * Copyright (C) 2015 NextThing Co
59026e0d1SMaxime Ripard  *
69026e0d1SMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
79026e0d1SMaxime Ripard  */
89026e0d1SMaxime Ripard 
99026e0d1SMaxime Ripard #include <linux/clk-provider.h>
109026e0d1SMaxime Ripard #include <linux/ioport.h>
119026e0d1SMaxime Ripard #include <linux/of_address.h>
1275448607SChen-Yu Tsai #include <linux/of_graph.h>
139026e0d1SMaxime Ripard #include <linux/of_irq.h>
149026e0d1SMaxime Ripard #include <linux/regmap.h>
159026e0d1SMaxime Ripard 
169026e0d1SMaxime Ripard #include <video/videomode.h>
179026e0d1SMaxime Ripard 
1829b77ad7SMaxime Ripard #include <drm/drm_atomic.h>
199c25a297SSam Ravnborg #include <drm/drm_atomic_helper.h>
209c25a297SSam Ravnborg #include <drm/drm_crtc.h>
219c25a297SSam Ravnborg #include <drm/drm_modes.h>
229c25a297SSam Ravnborg #include <drm/drm_print.h>
239c25a297SSam Ravnborg #include <drm/drm_probe_helper.h>
249c25a297SSam Ravnborg #include <drm/drm_vblank.h>
259c25a297SSam Ravnborg 
26ca07b210SMaxime Ripard #include "sun4i_backend.h"
279026e0d1SMaxime Ripard #include "sun4i_crtc.h"
289026e0d1SMaxime Ripard #include "sun4i_drv.h"
2987969338SIcenowy Zheng #include "sunxi_engine.h"
309026e0d1SMaxime Ripard #include "sun4i_tcon.h"
319026e0d1SMaxime Ripard 
3245e88f99SMaxime Ripard /*
3345e88f99SMaxime Ripard  * While this isn't really working in the DRM theory, in practice we
3445e88f99SMaxime Ripard  * can only ever have one encoder per TCON since we have a mux in our
3545e88f99SMaxime Ripard  * TCON.
3645e88f99SMaxime Ripard  */
sun4i_crtc_get_encoder(struct drm_crtc * crtc)3745e88f99SMaxime Ripard static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
3845e88f99SMaxime Ripard {
3945e88f99SMaxime Ripard 	struct drm_encoder *encoder;
4045e88f99SMaxime Ripard 
4145e88f99SMaxime Ripard 	drm_for_each_encoder(encoder, crtc->dev)
4245e88f99SMaxime Ripard 		if (encoder->crtc == crtc)
4345e88f99SMaxime Ripard 			return encoder;
4445e88f99SMaxime Ripard 
4545e88f99SMaxime Ripard 	return NULL;
4645e88f99SMaxime Ripard }
4745e88f99SMaxime Ripard 
sun4i_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)48656e5f65SMaxime Ripard static int sun4i_crtc_atomic_check(struct drm_crtc *crtc,
4929b77ad7SMaxime Ripard 				    struct drm_atomic_state *state)
50656e5f65SMaxime Ripard {
5129b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
5229b77ad7SMaxime Ripard 									  crtc);
53656e5f65SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
54656e5f65SMaxime Ripard 	struct sunxi_engine *engine = scrtc->engine;
55656e5f65SMaxime Ripard 	int ret = 0;
56656e5f65SMaxime Ripard 
57656e5f65SMaxime Ripard 	if (engine && engine->ops && engine->ops->atomic_check)
5829b77ad7SMaxime Ripard 		ret = engine->ops->atomic_check(engine, crtc_state);
59656e5f65SMaxime Ripard 
60656e5f65SMaxime Ripard 	return ret;
61656e5f65SMaxime Ripard }
62656e5f65SMaxime Ripard 
sun4i_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)639026e0d1SMaxime Ripard static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
64f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
659026e0d1SMaxime Ripard {
66f6ebe9f9SMaxime Ripard 	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
67f6ebe9f9SMaxime Ripard 									 crtc);
689026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
699026e0d1SMaxime Ripard 	struct drm_device *dev = crtc->dev;
706b8562c8SMaxime Ripard 	struct sunxi_engine *engine = scrtc->engine;
719026e0d1SMaxime Ripard 	unsigned long flags;
729026e0d1SMaxime Ripard 
739026e0d1SMaxime Ripard 	if (crtc->state->event) {
749026e0d1SMaxime Ripard 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
759026e0d1SMaxime Ripard 
769026e0d1SMaxime Ripard 		spin_lock_irqsave(&dev->event_lock, flags);
779026e0d1SMaxime Ripard 		scrtc->event = crtc->state->event;
789026e0d1SMaxime Ripard 		spin_unlock_irqrestore(&dev->event_lock, flags);
799026e0d1SMaxime Ripard 		crtc->state->event = NULL;
809026e0d1SMaxime Ripard 	}
816b8562c8SMaxime Ripard 
826b8562c8SMaxime Ripard 	if (engine->ops->atomic_begin)
836b8562c8SMaxime Ripard 		engine->ops->atomic_begin(engine, old_state);
849026e0d1SMaxime Ripard }
859026e0d1SMaxime Ripard 
sun4i_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)869026e0d1SMaxime Ripard static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
87f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
889026e0d1SMaxime Ripard {
899026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
90a33e93dbSDaniel Vetter 	struct drm_pending_vblank_event *event = crtc->state->event;
919026e0d1SMaxime Ripard 
929026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Committing plane changes\n");
939026e0d1SMaxime Ripard 
94*aa0b4a69SOndrej Jirman 	sunxi_engine_commit(scrtc->engine, crtc, state);
95a33e93dbSDaniel Vetter 
96a33e93dbSDaniel Vetter 	if (event) {
97a33e93dbSDaniel Vetter 		crtc->state->event = NULL;
98a33e93dbSDaniel Vetter 
99a33e93dbSDaniel Vetter 		spin_lock_irq(&crtc->dev->event_lock);
100a33e93dbSDaniel Vetter 		if (drm_crtc_vblank_get(crtc) == 0)
101a33e93dbSDaniel Vetter 			drm_crtc_arm_vblank_event(crtc, event);
102a33e93dbSDaniel Vetter 		else
103a33e93dbSDaniel Vetter 			drm_crtc_send_vblank_event(crtc, event);
104a33e93dbSDaniel Vetter 		spin_unlock_irq(&crtc->dev->event_lock);
105a33e93dbSDaniel Vetter 	}
1069026e0d1SMaxime Ripard }
1079026e0d1SMaxime Ripard 
sun4i_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)10864581714SLaurent Pinchart static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
109351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
1109026e0d1SMaxime Ripard {
11145e88f99SMaxime Ripard 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
1129026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
1139026e0d1SMaxime Ripard 
1149026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Disabling the CRTC\n");
1159026e0d1SMaxime Ripard 
116fd00c4eeSMaxime Ripard 	drm_crtc_vblank_off(crtc);
117fd00c4eeSMaxime Ripard 
11845e88f99SMaxime Ripard 	sun4i_tcon_set_status(scrtc->tcon, encoder, false);
1192cd36830SMaxime Ripard 
1202cd36830SMaxime Ripard 	if (crtc->state->event && !crtc->state->active) {
1212cd36830SMaxime Ripard 		spin_lock_irq(&crtc->dev->event_lock);
1222cd36830SMaxime Ripard 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
1232cd36830SMaxime Ripard 		spin_unlock_irq(&crtc->dev->event_lock);
1242cd36830SMaxime Ripard 
1252cd36830SMaxime Ripard 		crtc->state->event = NULL;
1262cd36830SMaxime Ripard 	}
1279026e0d1SMaxime Ripard }
1289026e0d1SMaxime Ripard 
sun4i_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)1290b20a0f8SLaurent Pinchart static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
130351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
1319026e0d1SMaxime Ripard {
13245e88f99SMaxime Ripard 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
1339026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
1349026e0d1SMaxime Ripard 
1359026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Enabling the CRTC\n");
1369026e0d1SMaxime Ripard 
13745e88f99SMaxime Ripard 	sun4i_tcon_set_status(scrtc->tcon, encoder, true);
138fd00c4eeSMaxime Ripard 
139fd00c4eeSMaxime Ripard 	drm_crtc_vblank_on(crtc);
1409026e0d1SMaxime Ripard }
1419026e0d1SMaxime Ripard 
sun4i_crtc_mode_set_nofb(struct drm_crtc * crtc)1425b8f0910SMaxime Ripard static void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc)
1435b8f0910SMaxime Ripard {
1445b8f0910SMaxime Ripard 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
1455b8f0910SMaxime Ripard 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
1465b8f0910SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
1475b8f0910SMaxime Ripard 
1485b8f0910SMaxime Ripard 	sun4i_tcon_mode_set(scrtc->tcon, encoder, mode);
149f7e974a3SJernej Skrabec 	sunxi_engine_mode_set(scrtc->engine, mode);
1505b8f0910SMaxime Ripard }
1515b8f0910SMaxime Ripard 
1529026e0d1SMaxime Ripard static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
153656e5f65SMaxime Ripard 	.atomic_check	= sun4i_crtc_atomic_check,
1549026e0d1SMaxime Ripard 	.atomic_begin	= sun4i_crtc_atomic_begin,
1559026e0d1SMaxime Ripard 	.atomic_flush	= sun4i_crtc_atomic_flush,
1560b20a0f8SLaurent Pinchart 	.atomic_enable	= sun4i_crtc_atomic_enable,
15764581714SLaurent Pinchart 	.atomic_disable	= sun4i_crtc_atomic_disable,
1585b8f0910SMaxime Ripard 	.mode_set_nofb	= sun4i_crtc_mode_set_nofb,
1599026e0d1SMaxime Ripard };
1609026e0d1SMaxime Ripard 
sun4i_crtc_enable_vblank(struct drm_crtc * crtc)16150480a78SShawn Guo static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc)
16250480a78SShawn Guo {
16350480a78SShawn Guo 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
16450480a78SShawn Guo 
16550480a78SShawn Guo 	DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc);
16650480a78SShawn Guo 
1673c64fb37SChen-Yu Tsai 	sun4i_tcon_enable_vblank(scrtc->tcon, true);
16850480a78SShawn Guo 
16950480a78SShawn Guo 	return 0;
17050480a78SShawn Guo }
17150480a78SShawn Guo 
sun4i_crtc_disable_vblank(struct drm_crtc * crtc)17250480a78SShawn Guo static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc)
17350480a78SShawn Guo {
17450480a78SShawn Guo 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
17550480a78SShawn Guo 
17650480a78SShawn Guo 	DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc);
17750480a78SShawn Guo 
1783c64fb37SChen-Yu Tsai 	sun4i_tcon_enable_vblank(scrtc->tcon, false);
17950480a78SShawn Guo }
18050480a78SShawn Guo 
1819026e0d1SMaxime Ripard static const struct drm_crtc_funcs sun4i_crtc_funcs = {
1829026e0d1SMaxime Ripard 	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
1839026e0d1SMaxime Ripard 	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
1849026e0d1SMaxime Ripard 	.destroy		= drm_crtc_cleanup,
1859026e0d1SMaxime Ripard 	.page_flip		= drm_atomic_helper_page_flip,
1869026e0d1SMaxime Ripard 	.reset			= drm_atomic_helper_crtc_reset,
1879026e0d1SMaxime Ripard 	.set_config		= drm_atomic_helper_set_config,
18850480a78SShawn Guo 	.enable_vblank		= sun4i_crtc_enable_vblank,
18950480a78SShawn Guo 	.disable_vblank		= sun4i_crtc_disable_vblank,
1909026e0d1SMaxime Ripard };
1919026e0d1SMaxime Ripard 
sun4i_crtc_init(struct drm_device * drm,struct sunxi_engine * engine,struct sun4i_tcon * tcon)19218c3b300SChen-Yu Tsai struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
19387969338SIcenowy Zheng 				   struct sunxi_engine *engine,
19418c3b300SChen-Yu Tsai 				   struct sun4i_tcon *tcon)
1959026e0d1SMaxime Ripard {
1969026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc;
1977921e147SIcenowy Zheng 	struct drm_plane **planes;
198dcd21580SChen-Yu Tsai 	struct drm_plane *primary = NULL, *cursor = NULL;
199dcd21580SChen-Yu Tsai 	int ret, i;
2009026e0d1SMaxime Ripard 
2019026e0d1SMaxime Ripard 	scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
2029026e0d1SMaxime Ripard 	if (!scrtc)
203ea411fd2SChen-Yu Tsai 		return ERR_PTR(-ENOMEM);
20487969338SIcenowy Zheng 	scrtc->engine = engine;
20518c3b300SChen-Yu Tsai 	scrtc->tcon = tcon;
2069026e0d1SMaxime Ripard 
207b3f266e4SChen-Yu Tsai 	/* Create our layers */
20887969338SIcenowy Zheng 	planes = sunxi_engine_layers_init(drm, engine);
2097921e147SIcenowy Zheng 	if (IS_ERR(planes)) {
210b3f266e4SChen-Yu Tsai 		dev_err(drm->dev, "Couldn't create the planes\n");
211dcd21580SChen-Yu Tsai 		return NULL;
212dcd21580SChen-Yu Tsai 	}
213dcd21580SChen-Yu Tsai 
214dcd21580SChen-Yu Tsai 	/* find primary and cursor planes for drm_crtc_init_with_planes */
2157921e147SIcenowy Zheng 	for (i = 0; planes[i]; i++) {
2167921e147SIcenowy Zheng 		struct drm_plane *plane = planes[i];
217dcd21580SChen-Yu Tsai 
2187921e147SIcenowy Zheng 		switch (plane->type) {
219dcd21580SChen-Yu Tsai 		case DRM_PLANE_TYPE_PRIMARY:
2207921e147SIcenowy Zheng 			primary = plane;
221dcd21580SChen-Yu Tsai 			break;
222dcd21580SChen-Yu Tsai 		case DRM_PLANE_TYPE_CURSOR:
2237921e147SIcenowy Zheng 			cursor = plane;
224dcd21580SChen-Yu Tsai 			break;
225dcd21580SChen-Yu Tsai 		default:
226dcd21580SChen-Yu Tsai 			break;
227dcd21580SChen-Yu Tsai 		}
228b3f266e4SChen-Yu Tsai 	}
229b3f266e4SChen-Yu Tsai 
2309026e0d1SMaxime Ripard 	ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
231dcd21580SChen-Yu Tsai 					primary,
232dcd21580SChen-Yu Tsai 					cursor,
2339026e0d1SMaxime Ripard 					&sun4i_crtc_funcs,
2349026e0d1SMaxime Ripard 					NULL);
2359026e0d1SMaxime Ripard 	if (ret) {
2369026e0d1SMaxime Ripard 		dev_err(drm->dev, "Couldn't init DRM CRTC\n");
237ea411fd2SChen-Yu Tsai 		return ERR_PTR(ret);
2389026e0d1SMaxime Ripard 	}
2399026e0d1SMaxime Ripard 
2409026e0d1SMaxime Ripard 	drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
2419026e0d1SMaxime Ripard 
24275448607SChen-Yu Tsai 	/* Set crtc.port to output port node of the tcon */
243e4cdcb7cSChen-Yu Tsai 	scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node,
24475448607SChen-Yu Tsai 						   1);
24575448607SChen-Yu Tsai 
246a5154a4dSChen-Yu Tsai 	/* Set possible_crtcs to this crtc for overlay planes */
2477921e147SIcenowy Zheng 	for (i = 0; planes[i]; i++) {
248dbf8f9e4SVille Syrjälä 		uint32_t possible_crtcs = drm_crtc_mask(&scrtc->crtc);
2497921e147SIcenowy Zheng 		struct drm_plane *plane = planes[i];
250a5154a4dSChen-Yu Tsai 
2517921e147SIcenowy Zheng 		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
2527921e147SIcenowy Zheng 			plane->possible_crtcs = possible_crtcs;
253a5154a4dSChen-Yu Tsai 	}
254a5154a4dSChen-Yu Tsai 
2559026e0d1SMaxime Ripard 	return scrtc;
2569026e0d1SMaxime Ripard }
257