xref: /linux/drivers/gpu/drm/sun4i/sun4i_crtc.c (revision 45e88f994add84d8cd3864d36064ea1ed1b08cdf)
19026e0d1SMaxime Ripard /*
29026e0d1SMaxime Ripard  * Copyright (C) 2015 Free Electrons
39026e0d1SMaxime Ripard  * Copyright (C) 2015 NextThing Co
49026e0d1SMaxime Ripard  *
59026e0d1SMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
69026e0d1SMaxime Ripard  *
79026e0d1SMaxime Ripard  * This program is free software; you can redistribute it and/or
89026e0d1SMaxime Ripard  * modify it under the terms of the GNU General Public License as
99026e0d1SMaxime Ripard  * published by the Free Software Foundation; either version 2 of
109026e0d1SMaxime Ripard  * the License, or (at your option) any later version.
119026e0d1SMaxime Ripard  */
129026e0d1SMaxime Ripard 
139026e0d1SMaxime Ripard #include <drm/drmP.h>
149026e0d1SMaxime Ripard #include <drm/drm_atomic_helper.h>
159026e0d1SMaxime Ripard #include <drm/drm_crtc.h>
169026e0d1SMaxime Ripard #include <drm/drm_crtc_helper.h>
179026e0d1SMaxime Ripard #include <drm/drm_modes.h>
189026e0d1SMaxime Ripard 
199026e0d1SMaxime Ripard #include <linux/clk-provider.h>
209026e0d1SMaxime Ripard #include <linux/ioport.h>
219026e0d1SMaxime Ripard #include <linux/of_address.h>
2275448607SChen-Yu Tsai #include <linux/of_graph.h>
239026e0d1SMaxime Ripard #include <linux/of_irq.h>
249026e0d1SMaxime Ripard #include <linux/regmap.h>
259026e0d1SMaxime Ripard 
269026e0d1SMaxime Ripard #include <video/videomode.h>
279026e0d1SMaxime Ripard 
289026e0d1SMaxime Ripard #include "sun4i_crtc.h"
299026e0d1SMaxime Ripard #include "sun4i_drv.h"
3087969338SIcenowy Zheng #include "sunxi_engine.h"
319026e0d1SMaxime Ripard #include "sun4i_tcon.h"
329026e0d1SMaxime Ripard 
33*45e88f99SMaxime Ripard /*
34*45e88f99SMaxime Ripard  * While this isn't really working in the DRM theory, in practice we
35*45e88f99SMaxime Ripard  * can only ever have one encoder per TCON since we have a mux in our
36*45e88f99SMaxime Ripard  * TCON.
37*45e88f99SMaxime Ripard  */
38*45e88f99SMaxime Ripard static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
39*45e88f99SMaxime Ripard {
40*45e88f99SMaxime Ripard 	struct drm_encoder *encoder;
41*45e88f99SMaxime Ripard 
42*45e88f99SMaxime Ripard 	drm_for_each_encoder(encoder, crtc->dev)
43*45e88f99SMaxime Ripard 		if (encoder->crtc == crtc)
44*45e88f99SMaxime Ripard 			return encoder;
45*45e88f99SMaxime Ripard 
46*45e88f99SMaxime Ripard 	return NULL;
47*45e88f99SMaxime Ripard }
48*45e88f99SMaxime Ripard 
499026e0d1SMaxime Ripard static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
509026e0d1SMaxime Ripard 				    struct drm_crtc_state *old_state)
519026e0d1SMaxime Ripard {
529026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
539026e0d1SMaxime Ripard 	struct drm_device *dev = crtc->dev;
549026e0d1SMaxime Ripard 	unsigned long flags;
559026e0d1SMaxime Ripard 
569026e0d1SMaxime Ripard 	if (crtc->state->event) {
579026e0d1SMaxime Ripard 		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
589026e0d1SMaxime Ripard 
599026e0d1SMaxime Ripard 		spin_lock_irqsave(&dev->event_lock, flags);
609026e0d1SMaxime Ripard 		scrtc->event = crtc->state->event;
619026e0d1SMaxime Ripard 		spin_unlock_irqrestore(&dev->event_lock, flags);
629026e0d1SMaxime Ripard 		crtc->state->event = NULL;
639026e0d1SMaxime Ripard 	 }
649026e0d1SMaxime Ripard }
659026e0d1SMaxime Ripard 
669026e0d1SMaxime Ripard static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
679026e0d1SMaxime Ripard 				    struct drm_crtc_state *old_state)
689026e0d1SMaxime Ripard {
699026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
70a33e93dbSDaniel Vetter 	struct drm_pending_vblank_event *event = crtc->state->event;
719026e0d1SMaxime Ripard 
729026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Committing plane changes\n");
739026e0d1SMaxime Ripard 
7487969338SIcenowy Zheng 	sunxi_engine_commit(scrtc->engine);
75a33e93dbSDaniel Vetter 
76a33e93dbSDaniel Vetter 	if (event) {
77a33e93dbSDaniel Vetter 		crtc->state->event = NULL;
78a33e93dbSDaniel Vetter 
79a33e93dbSDaniel Vetter 		spin_lock_irq(&crtc->dev->event_lock);
80a33e93dbSDaniel Vetter 		if (drm_crtc_vblank_get(crtc) == 0)
81a33e93dbSDaniel Vetter 			drm_crtc_arm_vblank_event(crtc, event);
82a33e93dbSDaniel Vetter 		else
83a33e93dbSDaniel Vetter 			drm_crtc_send_vblank_event(crtc, event);
84a33e93dbSDaniel Vetter 		spin_unlock_irq(&crtc->dev->event_lock);
85a33e93dbSDaniel Vetter 	}
869026e0d1SMaxime Ripard }
879026e0d1SMaxime Ripard 
8864581714SLaurent Pinchart static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
8964581714SLaurent Pinchart 				      struct drm_crtc_state *old_state)
909026e0d1SMaxime Ripard {
91*45e88f99SMaxime Ripard 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
929026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
939026e0d1SMaxime Ripard 
949026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Disabling the CRTC\n");
959026e0d1SMaxime Ripard 
96*45e88f99SMaxime Ripard 	sun4i_tcon_set_status(scrtc->tcon, encoder, false);
972cd36830SMaxime Ripard 
982cd36830SMaxime Ripard 	if (crtc->state->event && !crtc->state->active) {
992cd36830SMaxime Ripard 		spin_lock_irq(&crtc->dev->event_lock);
1002cd36830SMaxime Ripard 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
1012cd36830SMaxime Ripard 		spin_unlock_irq(&crtc->dev->event_lock);
1022cd36830SMaxime Ripard 
1032cd36830SMaxime Ripard 		crtc->state->event = NULL;
1042cd36830SMaxime Ripard 	}
1059026e0d1SMaxime Ripard }
1069026e0d1SMaxime Ripard 
1070b20a0f8SLaurent Pinchart static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
1080b20a0f8SLaurent Pinchart 				     struct drm_crtc_state *old_state)
1099026e0d1SMaxime Ripard {
110*45e88f99SMaxime Ripard 	struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
1119026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
1129026e0d1SMaxime Ripard 
1139026e0d1SMaxime Ripard 	DRM_DEBUG_DRIVER("Enabling the CRTC\n");
1149026e0d1SMaxime Ripard 
115*45e88f99SMaxime Ripard 	sun4i_tcon_set_status(scrtc->tcon, encoder, true);
1169026e0d1SMaxime Ripard }
1179026e0d1SMaxime Ripard 
1189026e0d1SMaxime Ripard static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
1199026e0d1SMaxime Ripard 	.atomic_begin	= sun4i_crtc_atomic_begin,
1209026e0d1SMaxime Ripard 	.atomic_flush	= sun4i_crtc_atomic_flush,
1210b20a0f8SLaurent Pinchart 	.atomic_enable	= sun4i_crtc_atomic_enable,
12264581714SLaurent Pinchart 	.atomic_disable	= sun4i_crtc_atomic_disable,
1239026e0d1SMaxime Ripard };
1249026e0d1SMaxime Ripard 
12550480a78SShawn Guo static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc)
12650480a78SShawn Guo {
12750480a78SShawn Guo 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
12850480a78SShawn Guo 
12950480a78SShawn Guo 	DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc);
13050480a78SShawn Guo 
1313c64fb37SChen-Yu Tsai 	sun4i_tcon_enable_vblank(scrtc->tcon, true);
13250480a78SShawn Guo 
13350480a78SShawn Guo 	return 0;
13450480a78SShawn Guo }
13550480a78SShawn Guo 
13650480a78SShawn Guo static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc)
13750480a78SShawn Guo {
13850480a78SShawn Guo 	struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
13950480a78SShawn Guo 
14050480a78SShawn Guo 	DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc);
14150480a78SShawn Guo 
1423c64fb37SChen-Yu Tsai 	sun4i_tcon_enable_vblank(scrtc->tcon, false);
14350480a78SShawn Guo }
14450480a78SShawn Guo 
1459026e0d1SMaxime Ripard static const struct drm_crtc_funcs sun4i_crtc_funcs = {
1469026e0d1SMaxime Ripard 	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
1479026e0d1SMaxime Ripard 	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
1489026e0d1SMaxime Ripard 	.destroy		= drm_crtc_cleanup,
1499026e0d1SMaxime Ripard 	.page_flip		= drm_atomic_helper_page_flip,
1509026e0d1SMaxime Ripard 	.reset			= drm_atomic_helper_crtc_reset,
1519026e0d1SMaxime Ripard 	.set_config		= drm_atomic_helper_set_config,
15250480a78SShawn Guo 	.enable_vblank		= sun4i_crtc_enable_vblank,
15350480a78SShawn Guo 	.disable_vblank		= sun4i_crtc_disable_vblank,
1549026e0d1SMaxime Ripard };
1559026e0d1SMaxime Ripard 
15618c3b300SChen-Yu Tsai struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
15787969338SIcenowy Zheng 				   struct sunxi_engine *engine,
15818c3b300SChen-Yu Tsai 				   struct sun4i_tcon *tcon)
1599026e0d1SMaxime Ripard {
1609026e0d1SMaxime Ripard 	struct sun4i_crtc *scrtc;
1617921e147SIcenowy Zheng 	struct drm_plane **planes;
162dcd21580SChen-Yu Tsai 	struct drm_plane *primary = NULL, *cursor = NULL;
163dcd21580SChen-Yu Tsai 	int ret, i;
1649026e0d1SMaxime Ripard 
1659026e0d1SMaxime Ripard 	scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
1669026e0d1SMaxime Ripard 	if (!scrtc)
167ea411fd2SChen-Yu Tsai 		return ERR_PTR(-ENOMEM);
16887969338SIcenowy Zheng 	scrtc->engine = engine;
16918c3b300SChen-Yu Tsai 	scrtc->tcon = tcon;
1709026e0d1SMaxime Ripard 
171b3f266e4SChen-Yu Tsai 	/* Create our layers */
17287969338SIcenowy Zheng 	planes = sunxi_engine_layers_init(drm, engine);
1737921e147SIcenowy Zheng 	if (IS_ERR(planes)) {
174b3f266e4SChen-Yu Tsai 		dev_err(drm->dev, "Couldn't create the planes\n");
175dcd21580SChen-Yu Tsai 		return NULL;
176dcd21580SChen-Yu Tsai 	}
177dcd21580SChen-Yu Tsai 
178dcd21580SChen-Yu Tsai 	/* find primary and cursor planes for drm_crtc_init_with_planes */
1797921e147SIcenowy Zheng 	for (i = 0; planes[i]; i++) {
1807921e147SIcenowy Zheng 		struct drm_plane *plane = planes[i];
181dcd21580SChen-Yu Tsai 
1827921e147SIcenowy Zheng 		switch (plane->type) {
183dcd21580SChen-Yu Tsai 		case DRM_PLANE_TYPE_PRIMARY:
1847921e147SIcenowy Zheng 			primary = plane;
185dcd21580SChen-Yu Tsai 			break;
186dcd21580SChen-Yu Tsai 		case DRM_PLANE_TYPE_CURSOR:
1877921e147SIcenowy Zheng 			cursor = plane;
188dcd21580SChen-Yu Tsai 			break;
189dcd21580SChen-Yu Tsai 		default:
190dcd21580SChen-Yu Tsai 			break;
191dcd21580SChen-Yu Tsai 		}
192b3f266e4SChen-Yu Tsai 	}
193b3f266e4SChen-Yu Tsai 
1949026e0d1SMaxime Ripard 	ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
195dcd21580SChen-Yu Tsai 					primary,
196dcd21580SChen-Yu Tsai 					cursor,
1979026e0d1SMaxime Ripard 					&sun4i_crtc_funcs,
1989026e0d1SMaxime Ripard 					NULL);
1999026e0d1SMaxime Ripard 	if (ret) {
2009026e0d1SMaxime Ripard 		dev_err(drm->dev, "Couldn't init DRM CRTC\n");
201ea411fd2SChen-Yu Tsai 		return ERR_PTR(ret);
2029026e0d1SMaxime Ripard 	}
2039026e0d1SMaxime Ripard 
2049026e0d1SMaxime Ripard 	drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
2059026e0d1SMaxime Ripard 
20675448607SChen-Yu Tsai 	/* Set crtc.port to output port node of the tcon */
207e4cdcb7cSChen-Yu Tsai 	scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node,
20875448607SChen-Yu Tsai 						   1);
20975448607SChen-Yu Tsai 
210a5154a4dSChen-Yu Tsai 	/* Set possible_crtcs to this crtc for overlay planes */
2117921e147SIcenowy Zheng 	for (i = 0; planes[i]; i++) {
212a5154a4dSChen-Yu Tsai 		uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc));
2137921e147SIcenowy Zheng 		struct drm_plane *plane = planes[i];
214a5154a4dSChen-Yu Tsai 
2157921e147SIcenowy Zheng 		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
2167921e147SIcenowy Zheng 			plane->possible_crtcs = possible_crtcs;
217a5154a4dSChen-Yu Tsai 	}
218a5154a4dSChen-Yu Tsai 
2199026e0d1SMaxime Ripard 	return scrtc;
2209026e0d1SMaxime Ripard }
221