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