162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Free Electrons 462306a36Sopenharmony_ci * Copyright (C) 2015 NextThing Co 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/ioport.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of_graph.h> 1362306a36Sopenharmony_ci#include <linux/of_irq.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <video/videomode.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2062306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2162306a36Sopenharmony_ci#include <drm/drm_modes.h> 2262306a36Sopenharmony_ci#include <drm/drm_print.h> 2362306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2462306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "sun4i_backend.h" 2762306a36Sopenharmony_ci#include "sun4i_crtc.h" 2862306a36Sopenharmony_ci#include "sun4i_drv.h" 2962306a36Sopenharmony_ci#include "sunxi_engine.h" 3062306a36Sopenharmony_ci#include "sun4i_tcon.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * While this isn't really working in the DRM theory, in practice we 3462306a36Sopenharmony_ci * can only ever have one encoder per TCON since we have a mux in our 3562306a36Sopenharmony_ci * TCON. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct drm_encoder *encoder; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci drm_for_each_encoder(encoder, crtc->dev) 4262306a36Sopenharmony_ci if (encoder->crtc == crtc) 4362306a36Sopenharmony_ci return encoder; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int sun4i_crtc_atomic_check(struct drm_crtc *crtc, 4962306a36Sopenharmony_ci struct drm_atomic_state *state) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 5262306a36Sopenharmony_ci crtc); 5362306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 5462306a36Sopenharmony_ci struct sunxi_engine *engine = scrtc->engine; 5562306a36Sopenharmony_ci int ret = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (engine && engine->ops && engine->ops->atomic_check) 5862306a36Sopenharmony_ci ret = engine->ops->atomic_check(engine, crtc_state); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void sun4i_crtc_atomic_begin(struct drm_crtc *crtc, 6462306a36Sopenharmony_ci struct drm_atomic_state *state) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, 6762306a36Sopenharmony_ci crtc); 6862306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 6962306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 7062306a36Sopenharmony_ci struct sunxi_engine *engine = scrtc->engine; 7162306a36Sopenharmony_ci unsigned long flags; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (crtc->state->event) { 7462306a36Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 7762306a36Sopenharmony_ci scrtc->event = crtc->state->event; 7862306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 7962306a36Sopenharmony_ci crtc->state->event = NULL; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (engine->ops->atomic_begin) 8362306a36Sopenharmony_ci engine->ops->atomic_begin(engine, old_state); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, 8762306a36Sopenharmony_ci struct drm_atomic_state *state) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 9062306a36Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Committing plane changes\n"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sunxi_engine_commit(scrtc->engine); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (event) { 9762306a36Sopenharmony_ci crtc->state->event = NULL; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 10062306a36Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 10162306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 10262306a36Sopenharmony_ci else 10362306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 10462306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, 10962306a36Sopenharmony_ci struct drm_atomic_state *state) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); 11262306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Disabling the CRTC\n"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci sun4i_tcon_set_status(scrtc->tcon, encoder, false); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (crtc->state->event && !crtc->state->active) { 12162306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 12262306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 12362306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci crtc->state->event = NULL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, 13062306a36Sopenharmony_ci struct drm_atomic_state *state) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); 13362306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Enabling the CRTC\n"); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci sun4i_tcon_set_status(scrtc->tcon, encoder, true); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void sun4i_crtc_mode_set_nofb(struct drm_crtc *crtc) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 14562306a36Sopenharmony_ci struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc); 14662306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci sun4i_tcon_mode_set(scrtc->tcon, encoder, mode); 14962306a36Sopenharmony_ci sunxi_engine_mode_set(scrtc->engine, mode); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = { 15362306a36Sopenharmony_ci .atomic_check = sun4i_crtc_atomic_check, 15462306a36Sopenharmony_ci .atomic_begin = sun4i_crtc_atomic_begin, 15562306a36Sopenharmony_ci .atomic_flush = sun4i_crtc_atomic_flush, 15662306a36Sopenharmony_ci .atomic_enable = sun4i_crtc_atomic_enable, 15762306a36Sopenharmony_ci .atomic_disable = sun4i_crtc_atomic_disable, 15862306a36Sopenharmony_ci .mode_set_nofb = sun4i_crtc_mode_set_nofb, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int sun4i_crtc_enable_vblank(struct drm_crtc *crtc) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci sun4i_tcon_enable_vblank(scrtc->tcon, true); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void sun4i_crtc_disable_vblank(struct drm_crtc *crtc) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci sun4i_tcon_enable_vblank(scrtc->tcon, false); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct drm_crtc_funcs sun4i_crtc_funcs = { 18262306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 18362306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 18462306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 18562306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 18662306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 18762306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 18862306a36Sopenharmony_ci .enable_vblank = sun4i_crtc_enable_vblank, 18962306a36Sopenharmony_ci .disable_vblank = sun4i_crtc_disable_vblank, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistruct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm, 19362306a36Sopenharmony_ci struct sunxi_engine *engine, 19462306a36Sopenharmony_ci struct sun4i_tcon *tcon) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct sun4i_crtc *scrtc; 19762306a36Sopenharmony_ci struct drm_plane **planes; 19862306a36Sopenharmony_ci struct drm_plane *primary = NULL, *cursor = NULL; 19962306a36Sopenharmony_ci int ret, i; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL); 20262306a36Sopenharmony_ci if (!scrtc) 20362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 20462306a36Sopenharmony_ci scrtc->engine = engine; 20562306a36Sopenharmony_ci scrtc->tcon = tcon; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Create our layers */ 20862306a36Sopenharmony_ci planes = sunxi_engine_layers_init(drm, engine); 20962306a36Sopenharmony_ci if (IS_ERR(planes)) { 21062306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't create the planes\n"); 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* find primary and cursor planes for drm_crtc_init_with_planes */ 21562306a36Sopenharmony_ci for (i = 0; planes[i]; i++) { 21662306a36Sopenharmony_ci struct drm_plane *plane = planes[i]; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (plane->type) { 21962306a36Sopenharmony_ci case DRM_PLANE_TYPE_PRIMARY: 22062306a36Sopenharmony_ci primary = plane; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case DRM_PLANE_TYPE_CURSOR: 22362306a36Sopenharmony_ci cursor = plane; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci default: 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &scrtc->crtc, 23162306a36Sopenharmony_ci primary, 23262306a36Sopenharmony_ci cursor, 23362306a36Sopenharmony_ci &sun4i_crtc_funcs, 23462306a36Sopenharmony_ci NULL); 23562306a36Sopenharmony_ci if (ret) { 23662306a36Sopenharmony_ci dev_err(drm->dev, "Couldn't init DRM CRTC\n"); 23762306a36Sopenharmony_ci return ERR_PTR(ret); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Set crtc.port to output port node of the tcon */ 24362306a36Sopenharmony_ci scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node, 24462306a36Sopenharmony_ci 1); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Set possible_crtcs to this crtc for overlay planes */ 24762306a36Sopenharmony_ci for (i = 0; planes[i]; i++) { 24862306a36Sopenharmony_ci uint32_t possible_crtcs = drm_crtc_mask(&scrtc->crtc); 24962306a36Sopenharmony_ci struct drm_plane *plane = planes[i]; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_OVERLAY) 25262306a36Sopenharmony_ci plane->possible_crtcs = possible_crtcs; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return scrtc; 25662306a36Sopenharmony_ci} 257