162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci * Author: Rob Clark <rob@ti.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/math64.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1062306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1162306a36Sopenharmony_ci#include <drm/drm_crtc.h> 1262306a36Sopenharmony_ci#include <drm/drm_mode.h> 1362306a36Sopenharmony_ci#include <drm/drm_vblank.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "omap_drv.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct omap_crtc_state { 2062306a36Sopenharmony_ci /* Must be first. */ 2162306a36Sopenharmony_ci struct drm_crtc_state base; 2262306a36Sopenharmony_ci /* Shadow values for legacy userspace support. */ 2362306a36Sopenharmony_ci unsigned int rotation; 2462306a36Sopenharmony_ci unsigned int zpos; 2562306a36Sopenharmony_ci bool manually_updated; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define to_omap_crtc(x) container_of(x, struct omap_crtc, base) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct omap_crtc { 3162306a36Sopenharmony_ci struct drm_crtc base; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci const char *name; 3462306a36Sopenharmony_ci struct omap_drm_pipeline *pipe; 3562306a36Sopenharmony_ci enum omap_channel channel; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci struct videomode vm; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci bool ignore_digit_sync_lost; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci bool enabled; 4262306a36Sopenharmony_ci bool pending; 4362306a36Sopenharmony_ci wait_queue_head_t pending_wait; 4462306a36Sopenharmony_ci struct drm_pending_vblank_event *event; 4562306a36Sopenharmony_ci struct delayed_work update_work; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci void (*framedone_handler)(void *); 4862306a36Sopenharmony_ci void *framedone_handler_data; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 5262306a36Sopenharmony_ci * Helper Functions 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct videomode *omap_crtc_timings(struct drm_crtc *crtc) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 5862306a36Sopenharmony_ci return &omap_crtc->vm; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cienum omap_channel omap_crtc_channel(struct drm_crtc *crtc) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 6462306a36Sopenharmony_ci return omap_crtc->channel; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic bool omap_crtc_is_pending(struct drm_crtc *crtc) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 7062306a36Sopenharmony_ci unsigned long flags; 7162306a36Sopenharmony_ci bool pending; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 7462306a36Sopenharmony_ci pending = omap_crtc->pending; 7562306a36Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return pending; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint omap_crtc_wait_pending(struct drm_crtc *crtc) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Timeout is set to a "sufficiently" high value, which should cover 8662306a36Sopenharmony_ci * a single frame refresh even on slower displays. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci return wait_event_timeout(omap_crtc->pending_wait, 8962306a36Sopenharmony_ci !omap_crtc_is_pending(crtc), 9062306a36Sopenharmony_ci msecs_to_jiffies(250)); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 9462306a36Sopenharmony_ci * DSS Manager Functions 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * Manager-ops, callbacks from output when they need to configure 9962306a36Sopenharmony_ci * the upstream part of the video pipe. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid omap_crtc_dss_start_update(struct omap_drm_private *priv, 10362306a36Sopenharmony_ci enum omap_channel channel) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci dispc_mgr_enable(priv->dispc, channel, true); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Called only from the encoder enable/disable and suspend/resume handlers. */ 10962306a36Sopenharmony_civoid omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); 11262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 11362306a36Sopenharmony_ci struct omap_drm_private *priv = dev->dev_private; 11462306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 11562306a36Sopenharmony_ci enum omap_channel channel = omap_crtc->channel; 11662306a36Sopenharmony_ci struct omap_irq_wait *wait; 11762306a36Sopenharmony_ci u32 framedone_irq, vsync_irq; 11862306a36Sopenharmony_ci int ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (WARN_ON(omap_crtc->enabled == enable)) 12162306a36Sopenharmony_ci return; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (omap_state->manually_updated) { 12462306a36Sopenharmony_ci omap_irq_enable_framedone(crtc, enable); 12562306a36Sopenharmony_ci omap_crtc->enabled = enable; 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) { 13062306a36Sopenharmony_ci dispc_mgr_enable(priv->dispc, channel, enable); 13162306a36Sopenharmony_ci omap_crtc->enabled = enable; 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Digit output produces some sync lost interrupts during the 13862306a36Sopenharmony_ci * first frame when enabling, so we need to ignore those. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci omap_crtc->ignore_digit_sync_lost = true; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci framedone_irq = dispc_mgr_get_framedone_irq(priv->dispc, 14462306a36Sopenharmony_ci channel); 14562306a36Sopenharmony_ci vsync_irq = dispc_mgr_get_vsync_irq(priv->dispc, channel); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (enable) { 14862306a36Sopenharmony_ci wait = omap_irq_wait_init(dev, vsync_irq, 1); 14962306a36Sopenharmony_ci } else { 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * When we disable the digit output, we need to wait for 15262306a36Sopenharmony_ci * FRAMEDONE to know that DISPC has finished with the output. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * OMAP2/3 does not have FRAMEDONE irq for digit output, and in 15562306a36Sopenharmony_ci * that case we need to use vsync interrupt, and wait for both 15662306a36Sopenharmony_ci * even and odd frames. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (framedone_irq) 16062306a36Sopenharmony_ci wait = omap_irq_wait_init(dev, framedone_irq, 1); 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci wait = omap_irq_wait_init(dev, vsync_irq, 2); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dispc_mgr_enable(priv->dispc, channel, enable); 16662306a36Sopenharmony_ci omap_crtc->enabled = enable; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100)); 16962306a36Sopenharmony_ci if (ret) { 17062306a36Sopenharmony_ci dev_err(dev->dev, "%s: timeout waiting for %s\n", 17162306a36Sopenharmony_ci omap_crtc->name, enable ? "enable" : "disable"); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { 17562306a36Sopenharmony_ci omap_crtc->ignore_digit_sync_lost = false; 17662306a36Sopenharmony_ci /* make sure the irq handler sees the value above */ 17762306a36Sopenharmony_ci mb(); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint omap_crtc_dss_enable(struct omap_drm_private *priv, enum omap_channel channel) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 18562306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dispc_mgr_set_timings(priv->dispc, omap_crtc->channel, 18862306a36Sopenharmony_ci &omap_crtc->vm); 18962306a36Sopenharmony_ci omap_crtc_set_enabled(&omap_crtc->base, true); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_civoid omap_crtc_dss_disable(struct omap_drm_private *priv, enum omap_channel channel) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 19762306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci omap_crtc_set_enabled(&omap_crtc->base, false); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_civoid omap_crtc_dss_set_timings(struct omap_drm_private *priv, 20362306a36Sopenharmony_ci enum omap_channel channel, 20462306a36Sopenharmony_ci const struct videomode *vm) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 20762306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci DBG("%s", omap_crtc->name); 21062306a36Sopenharmony_ci omap_crtc->vm = *vm; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid omap_crtc_dss_set_lcd_config(struct omap_drm_private *priv, 21462306a36Sopenharmony_ci enum omap_channel channel, 21562306a36Sopenharmony_ci const struct dss_lcd_mgr_config *config) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 21862306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci DBG("%s", omap_crtc->name); 22162306a36Sopenharmony_ci dispc_mgr_set_lcd_config(priv->dispc, omap_crtc->channel, 22262306a36Sopenharmony_ci config); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciint omap_crtc_dss_register_framedone( 22662306a36Sopenharmony_ci struct omap_drm_private *priv, enum omap_channel channel, 22762306a36Sopenharmony_ci void (*handler)(void *), void *data) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 23062306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 23162306a36Sopenharmony_ci struct drm_device *dev = omap_crtc->base.dev; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (omap_crtc->framedone_handler) 23462306a36Sopenharmony_ci return -EBUSY; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci dev_dbg(dev->dev, "register framedone %s", omap_crtc->name); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci omap_crtc->framedone_handler = handler; 23962306a36Sopenharmony_ci omap_crtc->framedone_handler_data = data; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_civoid omap_crtc_dss_unregister_framedone( 24562306a36Sopenharmony_ci struct omap_drm_private *priv, enum omap_channel channel, 24662306a36Sopenharmony_ci void (*handler)(void *), void *data) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct drm_crtc *crtc = priv->channels[channel]->crtc; 24962306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 25062306a36Sopenharmony_ci struct drm_device *dev = omap_crtc->base.dev; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dev_dbg(dev->dev, "unregister framedone %s", omap_crtc->name); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci WARN_ON(omap_crtc->framedone_handler != handler); 25562306a36Sopenharmony_ci WARN_ON(omap_crtc->framedone_handler_data != data); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci omap_crtc->framedone_handler = NULL; 25862306a36Sopenharmony_ci omap_crtc->framedone_handler_data = NULL; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 26262306a36Sopenharmony_ci * Setup, Flush and Page Flip 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_civoid omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (omap_crtc->ignore_digit_sync_lost) { 27062306a36Sopenharmony_ci irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; 27162306a36Sopenharmony_ci if (!irqstatus) 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_civoid omap_crtc_vblank_irq(struct drm_crtc *crtc) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 28162306a36Sopenharmony_ci struct drm_device *dev = omap_crtc->base.dev; 28262306a36Sopenharmony_ci struct omap_drm_private *priv = dev->dev_private; 28362306a36Sopenharmony_ci bool pending; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci spin_lock(&crtc->dev->event_lock); 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * If the dispc is busy we're racing the flush operation. Try again on 28862306a36Sopenharmony_ci * the next vblank interrupt. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if (dispc_mgr_go_busy(priv->dispc, omap_crtc->channel)) { 29162306a36Sopenharmony_ci spin_unlock(&crtc->dev->event_lock); 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Send the vblank event if one has been requested. */ 29662306a36Sopenharmony_ci if (omap_crtc->event) { 29762306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, omap_crtc->event); 29862306a36Sopenharmony_ci omap_crtc->event = NULL; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pending = omap_crtc->pending; 30262306a36Sopenharmony_ci omap_crtc->pending = false; 30362306a36Sopenharmony_ci spin_unlock(&crtc->dev->event_lock); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (pending) 30662306a36Sopenharmony_ci drm_crtc_vblank_put(crtc); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Wake up omap_atomic_complete. */ 30962306a36Sopenharmony_ci wake_up(&omap_crtc->pending_wait); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci DBG("%s: apply done", omap_crtc->name); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_civoid omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!omap_crtc->framedone_handler) 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci omap_crtc->framedone_handler(omap_crtc->framedone_handler_data); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci spin_lock(&crtc->dev->event_lock); 32462306a36Sopenharmony_ci /* Send the vblank event if one has been requested. */ 32562306a36Sopenharmony_ci if (omap_crtc->event) { 32662306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, omap_crtc->event); 32762306a36Sopenharmony_ci omap_crtc->event = NULL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci omap_crtc->pending = false; 33062306a36Sopenharmony_ci spin_unlock(&crtc->dev->event_lock); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Wake up omap_atomic_complete. */ 33362306a36Sopenharmony_ci wake_up(&omap_crtc->pending_wait); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_civoid omap_crtc_flush(struct drm_crtc *crtc) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 33962306a36Sopenharmony_ci struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!omap_state->manually_updated) 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!delayed_work_pending(&omap_crtc->update_work)) 34562306a36Sopenharmony_ci schedule_delayed_work(&omap_crtc->update_work, 0); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void omap_crtc_manual_display_update(struct work_struct *data) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct omap_crtc *omap_crtc = 35162306a36Sopenharmony_ci container_of(data, struct omap_crtc, update_work.work); 35262306a36Sopenharmony_ci struct omap_dss_device *dssdev = omap_crtc->pipe->output; 35362306a36Sopenharmony_ci struct drm_device *dev = omap_crtc->base.dev; 35462306a36Sopenharmony_ci int ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!dssdev || !dssdev->dsi_ops || !dssdev->dsi_ops->update) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = dssdev->dsi_ops->update(dssdev); 36062306a36Sopenharmony_ci if (ret < 0) { 36162306a36Sopenharmony_ci spin_lock_irq(&dev->event_lock); 36262306a36Sopenharmony_ci omap_crtc->pending = false; 36362306a36Sopenharmony_ci spin_unlock_irq(&dev->event_lock); 36462306a36Sopenharmony_ci wake_up(&omap_crtc->pending_wait); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic s16 omap_crtc_s31_32_to_s2_8(s64 coef) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci u64 sign_bit = 1ULL << 63; 37162306a36Sopenharmony_ci u64 cbits = (u64)coef; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci s16 ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x1ff); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (cbits & sign_bit) 37662306a36Sopenharmony_ci ret = -ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void omap_crtc_cpr_coefs_from_ctm(const struct drm_color_ctm *ctm, 38262306a36Sopenharmony_ci struct omap_dss_cpr_coefs *cpr) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci cpr->rr = omap_crtc_s31_32_to_s2_8(ctm->matrix[0]); 38562306a36Sopenharmony_ci cpr->rg = omap_crtc_s31_32_to_s2_8(ctm->matrix[1]); 38662306a36Sopenharmony_ci cpr->rb = omap_crtc_s31_32_to_s2_8(ctm->matrix[2]); 38762306a36Sopenharmony_ci cpr->gr = omap_crtc_s31_32_to_s2_8(ctm->matrix[3]); 38862306a36Sopenharmony_ci cpr->gg = omap_crtc_s31_32_to_s2_8(ctm->matrix[4]); 38962306a36Sopenharmony_ci cpr->gb = omap_crtc_s31_32_to_s2_8(ctm->matrix[5]); 39062306a36Sopenharmony_ci cpr->br = omap_crtc_s31_32_to_s2_8(ctm->matrix[6]); 39162306a36Sopenharmony_ci cpr->bg = omap_crtc_s31_32_to_s2_8(ctm->matrix[7]); 39262306a36Sopenharmony_ci cpr->bb = omap_crtc_s31_32_to_s2_8(ctm->matrix[8]); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void omap_crtc_write_crtc_properties(struct drm_crtc *crtc) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 39862306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 39962306a36Sopenharmony_ci struct omap_overlay_manager_info info; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci info.default_color = 0x000000; 40462306a36Sopenharmony_ci info.trans_enabled = false; 40562306a36Sopenharmony_ci info.partial_alpha_enabled = false; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (crtc->state->ctm) { 40862306a36Sopenharmony_ci struct drm_color_ctm *ctm = crtc->state->ctm->data; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci info.cpr_enable = true; 41162306a36Sopenharmony_ci omap_crtc_cpr_coefs_from_ctm(ctm, &info.cpr_coefs); 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci info.cpr_enable = false; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci dispc_mgr_setup(priv->dispc, omap_crtc->channel, &info); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 42062306a36Sopenharmony_ci * CRTC Functions 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void omap_crtc_destroy(struct drm_crtc *crtc) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci DBG("%s", omap_crtc->name); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci kfree(omap_crtc); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void omap_crtc_arm_event(struct drm_crtc *crtc) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci WARN_ON(omap_crtc->pending); 43962306a36Sopenharmony_ci omap_crtc->pending = true; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (crtc->state->event) { 44262306a36Sopenharmony_ci omap_crtc->event = crtc->state->event; 44362306a36Sopenharmony_ci crtc->state->event = NULL; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void omap_crtc_atomic_enable(struct drm_crtc *crtc, 44862306a36Sopenharmony_ci struct drm_atomic_state *state) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 45162306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 45262306a36Sopenharmony_ci struct omap_crtc_state *omap_state = to_omap_crtc_state(crtc->state); 45362306a36Sopenharmony_ci int ret; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci DBG("%s", omap_crtc->name); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci dispc_runtime_get(priv->dispc); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* manual updated display will not trigger vsync irq */ 46062306a36Sopenharmony_ci if (omap_state->manually_updated) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = drm_crtc_vblank_get(crtc); 46662306a36Sopenharmony_ci WARN_ON(ret != 0); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 46962306a36Sopenharmony_ci omap_crtc_arm_event(crtc); 47062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void omap_crtc_atomic_disable(struct drm_crtc *crtc, 47462306a36Sopenharmony_ci struct drm_atomic_state *state) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 47762306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 47862306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci DBG("%s", omap_crtc->name); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 48362306a36Sopenharmony_ci if (crtc->state->event) { 48462306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, crtc->state->event); 48562306a36Sopenharmony_ci crtc->state->event = NULL; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci cancel_delayed_work(&omap_crtc->update_work); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (!omap_crtc_wait_pending(crtc)) 49262306a36Sopenharmony_ci dev_warn(dev->dev, "manual display update did not finish!"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci dispc_runtime_put(priv->dispc); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc, 50062306a36Sopenharmony_ci const struct drm_display_mode *mode) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 50362306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 50462306a36Sopenharmony_ci struct videomode vm = {0}; 50562306a36Sopenharmony_ci int r; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci drm_display_mode_to_videomode(mode, &vm); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * DSI might not call this, since the supplied mode is not a 51162306a36Sopenharmony_ci * valid DISPC mode. DSI will calculate and configure the 51262306a36Sopenharmony_ci * proper DISPC mode later. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if (omap_crtc->pipe->output->type != OMAP_DISPLAY_TYPE_DSI) { 51562306a36Sopenharmony_ci r = dispc_mgr_check_timings(priv->dispc, 51662306a36Sopenharmony_ci omap_crtc->channel, 51762306a36Sopenharmony_ci &vm); 51862306a36Sopenharmony_ci if (r) 51962306a36Sopenharmony_ci return r; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Check for bandwidth limit */ 52362306a36Sopenharmony_ci if (priv->max_bandwidth) { 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * Estimation for the bandwidth need of a given mode with one 52662306a36Sopenharmony_ci * full screen plane: 52762306a36Sopenharmony_ci * bandwidth = resolution * 32bpp * (pclk / (vtotal * htotal)) 52862306a36Sopenharmony_ci * ^^ Refresh rate ^^ 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * The interlaced mode is taken into account by using the 53162306a36Sopenharmony_ci * pixelclock in the calculation. 53262306a36Sopenharmony_ci * 53362306a36Sopenharmony_ci * The equation is rearranged for 64bit arithmetic. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci uint64_t bandwidth = mode->clock * 1000; 53662306a36Sopenharmony_ci unsigned int bpp = 4; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; 53962306a36Sopenharmony_ci bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Reject modes which would need more bandwidth if used with one 54362306a36Sopenharmony_ci * full resolution plane (most common use case). 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci if (priv->max_bandwidth < bandwidth) 54662306a36Sopenharmony_ci return MODE_BAD; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return MODE_OK; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 55562306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci DBG("%s: set mode: " DRM_MODE_FMT, 55862306a36Sopenharmony_ci omap_crtc->name, DRM_MODE_ARG(mode)); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci drm_display_mode_to_videomode(mode, &omap_crtc->vm); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic bool omap_crtc_is_manually_updated(struct drm_crtc *crtc) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 56662306a36Sopenharmony_ci struct omap_dss_device *dssdev = omap_crtc->pipe->output; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (!dssdev || !dssdev->dsi_ops || !dssdev->dsi_ops->is_video_mode) 56962306a36Sopenharmony_ci return false; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (dssdev->dsi_ops->is_video_mode(dssdev)) 57262306a36Sopenharmony_ci return false; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci DBG("detected manually updated display!"); 57562306a36Sopenharmony_ci return true; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int omap_crtc_atomic_check(struct drm_crtc *crtc, 57962306a36Sopenharmony_ci struct drm_atomic_state *state) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, 58262306a36Sopenharmony_ci crtc); 58362306a36Sopenharmony_ci struct drm_plane_state *pri_state; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (crtc_state->color_mgmt_changed && crtc_state->degamma_lut) { 58662306a36Sopenharmony_ci unsigned int length = crtc_state->degamma_lut->length / 58762306a36Sopenharmony_ci sizeof(struct drm_color_lut); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (length < 2) 59062306a36Sopenharmony_ci return -EINVAL; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci pri_state = drm_atomic_get_new_plane_state(state, 59462306a36Sopenharmony_ci crtc->primary); 59562306a36Sopenharmony_ci if (pri_state) { 59662306a36Sopenharmony_ci struct omap_crtc_state *omap_crtc_state = 59762306a36Sopenharmony_ci to_omap_crtc_state(crtc_state); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Mirror new values for zpos and rotation in omap_crtc_state */ 60062306a36Sopenharmony_ci omap_crtc_state->zpos = pri_state->zpos; 60162306a36Sopenharmony_ci omap_crtc_state->rotation = pri_state->rotation; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Check if this CRTC is for a manually updated display */ 60462306a36Sopenharmony_ci omap_crtc_state->manually_updated = omap_crtc_is_manually_updated(crtc); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic void omap_crtc_atomic_begin(struct drm_crtc *crtc, 61162306a36Sopenharmony_ci struct drm_atomic_state *state) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic void omap_crtc_atomic_flush(struct drm_crtc *crtc, 61662306a36Sopenharmony_ci struct drm_atomic_state *state) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 61962306a36Sopenharmony_ci struct omap_crtc *omap_crtc = to_omap_crtc(crtc); 62062306a36Sopenharmony_ci struct omap_crtc_state *omap_crtc_state = to_omap_crtc_state(crtc->state); 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (crtc->state->color_mgmt_changed) { 62462306a36Sopenharmony_ci struct drm_color_lut *lut = NULL; 62562306a36Sopenharmony_ci unsigned int length = 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (crtc->state->degamma_lut) { 62862306a36Sopenharmony_ci lut = (struct drm_color_lut *) 62962306a36Sopenharmony_ci crtc->state->degamma_lut->data; 63062306a36Sopenharmony_ci length = crtc->state->degamma_lut->length / 63162306a36Sopenharmony_ci sizeof(*lut); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci dispc_mgr_set_gamma(priv->dispc, omap_crtc->channel, 63462306a36Sopenharmony_ci lut, length); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci omap_crtc_write_crtc_properties(crtc); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Only flush the CRTC if it is currently enabled. */ 64062306a36Sopenharmony_ci if (!omap_crtc->enabled) 64162306a36Sopenharmony_ci return; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci DBG("%s: GO", omap_crtc->name); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (omap_crtc_state->manually_updated) { 64662306a36Sopenharmony_ci /* send new image for page flips and modeset changes */ 64762306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 64862306a36Sopenharmony_ci omap_crtc_flush(crtc); 64962306a36Sopenharmony_ci omap_crtc_arm_event(crtc); 65062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci ret = drm_crtc_vblank_get(crtc); 65562306a36Sopenharmony_ci WARN_ON(ret != 0); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 65862306a36Sopenharmony_ci dispc_mgr_go(priv->dispc, omap_crtc->channel); 65962306a36Sopenharmony_ci omap_crtc_arm_event(crtc); 66062306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int omap_crtc_atomic_set_property(struct drm_crtc *crtc, 66462306a36Sopenharmony_ci struct drm_crtc_state *state, 66562306a36Sopenharmony_ci struct drm_property *property, 66662306a36Sopenharmony_ci u64 val) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 66962306a36Sopenharmony_ci struct drm_plane_state *plane_state; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * Delegate property set to the primary plane. Get the plane state and 67362306a36Sopenharmony_ci * set the property directly, the shadow copy will be assigned in the 67462306a36Sopenharmony_ci * omap_crtc_atomic_check callback. This way updates to plane state will 67562306a36Sopenharmony_ci * always be mirrored in the crtc state correctly. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_ci plane_state = drm_atomic_get_plane_state(state->state, crtc->primary); 67862306a36Sopenharmony_ci if (IS_ERR(plane_state)) 67962306a36Sopenharmony_ci return PTR_ERR(plane_state); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (property == crtc->primary->rotation_property) 68262306a36Sopenharmony_ci plane_state->rotation = val; 68362306a36Sopenharmony_ci else if (property == priv->zorder_prop) 68462306a36Sopenharmony_ci plane_state->zpos = val; 68562306a36Sopenharmony_ci else 68662306a36Sopenharmony_ci return -EINVAL; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int omap_crtc_atomic_get_property(struct drm_crtc *crtc, 69262306a36Sopenharmony_ci const struct drm_crtc_state *state, 69362306a36Sopenharmony_ci struct drm_property *property, 69462306a36Sopenharmony_ci u64 *val) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct omap_drm_private *priv = crtc->dev->dev_private; 69762306a36Sopenharmony_ci struct omap_crtc_state *omap_state = to_omap_crtc_state(state); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (property == crtc->primary->rotation_property) 70062306a36Sopenharmony_ci *val = omap_state->rotation; 70162306a36Sopenharmony_ci else if (property == priv->zorder_prop) 70262306a36Sopenharmony_ci *val = omap_state->zpos; 70362306a36Sopenharmony_ci else 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return 0; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void omap_crtc_reset(struct drm_crtc *crtc) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct omap_crtc_state *state; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (crtc->state) 71462306a36Sopenharmony_ci __drm_atomic_helper_crtc_destroy_state(crtc->state); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci kfree(crtc->state); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 71962306a36Sopenharmony_ci if (state) 72062306a36Sopenharmony_ci __drm_atomic_helper_crtc_reset(crtc, &state->base); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic struct drm_crtc_state * 72462306a36Sopenharmony_ciomap_crtc_duplicate_state(struct drm_crtc *crtc) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct omap_crtc_state *state, *current_state; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (WARN_ON(!crtc->state)) 72962306a36Sopenharmony_ci return NULL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci current_state = to_omap_crtc_state(crtc->state); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci state = kmalloc(sizeof(*state), GFP_KERNEL); 73462306a36Sopenharmony_ci if (!state) 73562306a36Sopenharmony_ci return NULL; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci state->zpos = current_state->zpos; 74062306a36Sopenharmony_ci state->rotation = current_state->rotation; 74162306a36Sopenharmony_ci state->manually_updated = current_state->manually_updated; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return &state->base; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct drm_crtc_funcs omap_crtc_funcs = { 74762306a36Sopenharmony_ci .reset = omap_crtc_reset, 74862306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 74962306a36Sopenharmony_ci .destroy = omap_crtc_destroy, 75062306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 75162306a36Sopenharmony_ci .atomic_duplicate_state = omap_crtc_duplicate_state, 75262306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 75362306a36Sopenharmony_ci .atomic_set_property = omap_crtc_atomic_set_property, 75462306a36Sopenharmony_ci .atomic_get_property = omap_crtc_atomic_get_property, 75562306a36Sopenharmony_ci .enable_vblank = omap_irq_enable_vblank, 75662306a36Sopenharmony_ci .disable_vblank = omap_irq_disable_vblank, 75762306a36Sopenharmony_ci}; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { 76062306a36Sopenharmony_ci .mode_set_nofb = omap_crtc_mode_set_nofb, 76162306a36Sopenharmony_ci .atomic_check = omap_crtc_atomic_check, 76262306a36Sopenharmony_ci .atomic_begin = omap_crtc_atomic_begin, 76362306a36Sopenharmony_ci .atomic_flush = omap_crtc_atomic_flush, 76462306a36Sopenharmony_ci .atomic_enable = omap_crtc_atomic_enable, 76562306a36Sopenharmony_ci .atomic_disable = omap_crtc_atomic_disable, 76662306a36Sopenharmony_ci .mode_valid = omap_crtc_mode_valid, 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 77062306a36Sopenharmony_ci * Init and Cleanup 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const char *channel_names[] = { 77462306a36Sopenharmony_ci [OMAP_DSS_CHANNEL_LCD] = "lcd", 77562306a36Sopenharmony_ci [OMAP_DSS_CHANNEL_DIGIT] = "tv", 77662306a36Sopenharmony_ci [OMAP_DSS_CHANNEL_LCD2] = "lcd2", 77762306a36Sopenharmony_ci [OMAP_DSS_CHANNEL_LCD3] = "lcd3", 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* initialize crtc */ 78162306a36Sopenharmony_cistruct drm_crtc *omap_crtc_init(struct drm_device *dev, 78262306a36Sopenharmony_ci struct omap_drm_pipeline *pipe, 78362306a36Sopenharmony_ci struct drm_plane *plane) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct omap_drm_private *priv = dev->dev_private; 78662306a36Sopenharmony_ci struct drm_crtc *crtc = NULL; 78762306a36Sopenharmony_ci struct omap_crtc *omap_crtc; 78862306a36Sopenharmony_ci enum omap_channel channel; 78962306a36Sopenharmony_ci int ret; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci channel = pipe->output->dispc_channel; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci DBG("%s", channel_names[channel]); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL); 79662306a36Sopenharmony_ci if (!omap_crtc) 79762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci crtc = &omap_crtc->base; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci init_waitqueue_head(&omap_crtc->pending_wait); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci omap_crtc->pipe = pipe; 80462306a36Sopenharmony_ci omap_crtc->channel = channel; 80562306a36Sopenharmony_ci omap_crtc->name = channel_names[channel]; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * We want to refresh manually updated displays from dirty callback, 80962306a36Sopenharmony_ci * which is called quite often (e.g. for each drawn line). This will 81062306a36Sopenharmony_ci * be used to do the display update asynchronously to avoid blocking 81162306a36Sopenharmony_ci * the rendering process and merges multiple dirty calls into one 81262306a36Sopenharmony_ci * update if they arrive very fast. We also call this function for 81362306a36Sopenharmony_ci * atomic display updates (e.g. for page flips), which means we do 81462306a36Sopenharmony_ci * not need extra locking. Atomic updates should be synchronous, but 81562306a36Sopenharmony_ci * need to wait for the framedone interrupt anyways. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci INIT_DELAYED_WORK(&omap_crtc->update_work, 81862306a36Sopenharmony_ci omap_crtc_manual_display_update); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, 82162306a36Sopenharmony_ci &omap_crtc_funcs, NULL); 82262306a36Sopenharmony_ci if (ret < 0) { 82362306a36Sopenharmony_ci dev_err(dev->dev, "%s(): could not init crtc for: %s\n", 82462306a36Sopenharmony_ci __func__, pipe->output->name); 82562306a36Sopenharmony_ci kfree(omap_crtc); 82662306a36Sopenharmony_ci return ERR_PTR(ret); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* The dispc API adapts to what ever size, but the HW supports 83262306a36Sopenharmony_ci * 256 element gamma table for LCDs and 1024 element table for 83362306a36Sopenharmony_ci * OMAP_DSS_CHANNEL_DIGIT. X server assumes 256 element gamma 83462306a36Sopenharmony_ci * tables so lets use that. Size of HW gamma table can be 83562306a36Sopenharmony_ci * extracted with dispc_mgr_gamma_size(). If it returns 0 83662306a36Sopenharmony_ci * gamma table is not supported. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci if (dispc_mgr_gamma_size(priv->dispc, channel)) { 83962306a36Sopenharmony_ci unsigned int gamma_lut_size = 256; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci drm_crtc_enable_color_mgmt(crtc, gamma_lut_size, true, 0); 84262306a36Sopenharmony_ci drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci omap_plane_install_properties(crtc->primary, &crtc->base); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return crtc; 84862306a36Sopenharmony_ci} 849