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