162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Parts of this file were based on sources as follows: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2006-2008 Intel Corporation 862306a36Sopenharmony_ci * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 962306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/dma-buf.h> 1562306a36Sopenharmony_ci#include <linux/media-bus-format.h> 1662306a36Sopenharmony_ci#include <linux/of_graph.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1962306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2062306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 2162306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "pl111_drm.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciirqreturn_t pl111_irq(int irq, void *data) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = data; 3062306a36Sopenharmony_ci u32 irq_stat; 3162306a36Sopenharmony_ci irqreturn_t status = IRQ_NONE; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci irq_stat = readl(priv->regs + CLCD_PL111_MIS); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (!irq_stat) 3662306a36Sopenharmony_ci return IRQ_NONE; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) { 3962306a36Sopenharmony_ci drm_crtc_handle_vblank(&priv->pipe.crtc); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci status = IRQ_HANDLED; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* Clear the interrupt once done */ 4562306a36Sopenharmony_ci writel(irq_stat, priv->regs + CLCD_PL111_ICR); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return status; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic enum drm_mode_status 5162306a36Sopenharmony_cipl111_mode_valid(struct drm_simple_display_pipe *pipe, 5262306a36Sopenharmony_ci const struct drm_display_mode *mode) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct drm_device *drm = pipe->crtc.dev; 5562306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 5662306a36Sopenharmony_ci u32 cpp = DIV_ROUND_UP(priv->variant->fb_depth, 8); 5762306a36Sopenharmony_ci u64 bw; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * We use the pixelclock to also account for interlaced modes, the 6162306a36Sopenharmony_ci * resulting bandwidth is in bytes per second. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci bw = mode->clock * 1000ULL; /* In Hz */ 6462306a36Sopenharmony_ci bw = bw * mode->hdisplay * mode->vdisplay * cpp; 6562306a36Sopenharmony_ci bw = div_u64(bw, mode->htotal * mode->vtotal); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * If no bandwidth constraints, anything goes, else 6962306a36Sopenharmony_ci * check if we are too fast. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci if (priv->memory_bw && (bw > priv->memory_bw)) { 7262306a36Sopenharmony_ci DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu too fast\n", 7362306a36Sopenharmony_ci mode->hdisplay, mode->vdisplay, 7462306a36Sopenharmony_ci mode->clock * 1000, cpp, bw); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return MODE_BAD; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu bytes/s OK\n", 7962306a36Sopenharmony_ci mode->hdisplay, mode->vdisplay, 8062306a36Sopenharmony_ci mode->clock * 1000, cpp, bw); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return MODE_OK; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int pl111_display_check(struct drm_simple_display_pipe *pipe, 8662306a36Sopenharmony_ci struct drm_plane_state *pstate, 8762306a36Sopenharmony_ci struct drm_crtc_state *cstate) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci const struct drm_display_mode *mode = &cstate->mode; 9062306a36Sopenharmony_ci struct drm_framebuffer *old_fb = pipe->plane.state->fb; 9162306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (mode->hdisplay % 16) 9462306a36Sopenharmony_ci return -EINVAL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (fb) { 9762306a36Sopenharmony_ci u32 offset = drm_fb_dma_get_gem_addr(fb, pstate, 0); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* FB base address must be dword aligned. */ 10062306a36Sopenharmony_ci if (offset & 3) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* There's no pitch register -- the mode's hdisplay 10462306a36Sopenharmony_ci * controls it. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* We can't change the FB format in a flicker-free 11062306a36Sopenharmony_ci * manner (and only update it during CRTC enable). 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (old_fb && old_fb->format != fb->format) 11362306a36Sopenharmony_ci cstate->mode_changed = true; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void pl111_display_enable(struct drm_simple_display_pipe *pipe, 12062306a36Sopenharmony_ci struct drm_crtc_state *cstate, 12162306a36Sopenharmony_ci struct drm_plane_state *plane_state) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 12462306a36Sopenharmony_ci struct drm_plane *plane = &pipe->plane; 12562306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 12662306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 12762306a36Sopenharmony_ci const struct drm_display_mode *mode = &cstate->mode; 12862306a36Sopenharmony_ci struct drm_framebuffer *fb = plane->state->fb; 12962306a36Sopenharmony_ci struct drm_connector *connector = priv->connector; 13062306a36Sopenharmony_ci struct drm_bridge *bridge = priv->bridge; 13162306a36Sopenharmony_ci bool grayscale = false; 13262306a36Sopenharmony_ci u32 cntl; 13362306a36Sopenharmony_ci u32 ppl, hsw, hfp, hbp; 13462306a36Sopenharmony_ci u32 lpp, vsw, vfp, vbp; 13562306a36Sopenharmony_ci u32 cpl, tim2; 13662306a36Sopenharmony_ci int ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = clk_set_rate(priv->clk, mode->clock * 1000); 13962306a36Sopenharmony_ci if (ret) { 14062306a36Sopenharmony_ci dev_err(drm->dev, 14162306a36Sopenharmony_ci "Failed to set pixel clock rate to %d: %d\n", 14262306a36Sopenharmony_ci mode->clock * 1000, ret); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci clk_prepare_enable(priv->clk); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ppl = (mode->hdisplay / 16) - 1; 14862306a36Sopenharmony_ci hsw = mode->hsync_end - mode->hsync_start - 1; 14962306a36Sopenharmony_ci hfp = mode->hsync_start - mode->hdisplay - 1; 15062306a36Sopenharmony_ci hbp = mode->htotal - mode->hsync_end - 1; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci lpp = mode->vdisplay - 1; 15362306a36Sopenharmony_ci vsw = mode->vsync_end - mode->vsync_start - 1; 15462306a36Sopenharmony_ci vfp = mode->vsync_start - mode->vdisplay; 15562306a36Sopenharmony_ci vbp = mode->vtotal - mode->vsync_end; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci cpl = mode->hdisplay - 1; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci writel((ppl << 2) | 16062306a36Sopenharmony_ci (hsw << 8) | 16162306a36Sopenharmony_ci (hfp << 16) | 16262306a36Sopenharmony_ci (hbp << 24), 16362306a36Sopenharmony_ci priv->regs + CLCD_TIM0); 16462306a36Sopenharmony_ci writel(lpp | 16562306a36Sopenharmony_ci (vsw << 10) | 16662306a36Sopenharmony_ci (vfp << 16) | 16762306a36Sopenharmony_ci (vbp << 24), 16862306a36Sopenharmony_ci priv->regs + CLCD_TIM1); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci spin_lock(&priv->tim2_lock); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci tim2 = readl(priv->regs + CLCD_TIM2); 17362306a36Sopenharmony_ci tim2 &= (TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (priv->variant->broken_clockdivider) 17662306a36Sopenharmony_ci tim2 |= TIM2_BCD; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 17962306a36Sopenharmony_ci tim2 |= TIM2_IHS; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 18262306a36Sopenharmony_ci tim2 |= TIM2_IVS; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (connector) { 18562306a36Sopenharmony_ci if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) 18662306a36Sopenharmony_ci tim2 |= TIM2_IOE; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (connector->display_info.bus_flags & 18962306a36Sopenharmony_ci DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) 19062306a36Sopenharmony_ci tim2 |= TIM2_IPC; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (connector->display_info.num_bus_formats == 1 && 19362306a36Sopenharmony_ci connector->display_info.bus_formats[0] == 19462306a36Sopenharmony_ci MEDIA_BUS_FMT_Y8_1X8) 19562306a36Sopenharmony_ci grayscale = true; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * The AC pin bias frequency is set to max count when using 19962306a36Sopenharmony_ci * grayscale so at least once in a while we will reverse 20062306a36Sopenharmony_ci * polarity and get rid of any DC built up that could 20162306a36Sopenharmony_ci * damage the display. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci if (grayscale) 20462306a36Sopenharmony_ci tim2 |= TIM2_ACB_MASK; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (bridge) { 20862306a36Sopenharmony_ci const struct drm_bridge_timings *btimings = bridge->timings; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Here is when things get really fun. Sometimes the bridge 21262306a36Sopenharmony_ci * timings are such that the signal out from PL11x is not 21362306a36Sopenharmony_ci * stable before the receiving bridge (such as a dumb VGA DAC 21462306a36Sopenharmony_ci * or similar) samples it. If that happens, we compensate by 21562306a36Sopenharmony_ci * the only method we have: output the data on the opposite 21662306a36Sopenharmony_ci * edge of the clock so it is for sure stable when it gets 21762306a36Sopenharmony_ci * sampled. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * The PL111 manual does not contain proper timining diagrams 22062306a36Sopenharmony_ci * or data for these details, but we know from experiments 22162306a36Sopenharmony_ci * that the setup time is more than 3000 picoseconds (3 ns). 22262306a36Sopenharmony_ci * If we have a bridge that requires the signal to be stable 22362306a36Sopenharmony_ci * earlier than 3000 ps before the clock pulse, we have to 22462306a36Sopenharmony_ci * output the data on the opposite edge to avoid flicker. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci if (btimings && btimings->setup_time_ps >= 3000) 22762306a36Sopenharmony_ci tim2 ^= TIM2_IPC; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci tim2 |= cpl << 16; 23162306a36Sopenharmony_ci writel(tim2, priv->regs + CLCD_TIM2); 23262306a36Sopenharmony_ci spin_unlock(&priv->tim2_lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci writel(0, priv->regs + CLCD_TIM3); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* 23762306a36Sopenharmony_ci * Detect grayscale bus format. We do not support a grayscale mode 23862306a36Sopenharmony_ci * toward userspace, instead we expose an RGB24 buffer and then the 23962306a36Sopenharmony_ci * hardware will activate its grayscaler to convert to the grayscale 24062306a36Sopenharmony_ci * format. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci if (grayscale) 24362306a36Sopenharmony_ci cntl = CNTL_LCDEN | CNTL_LCDMONO8; 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci /* Else we assume TFT display */ 24662306a36Sopenharmony_ci cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* On the ST Micro variant, assume all 24 bits are connected */ 24962306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 25062306a36Sopenharmony_ci cntl |= CNTL_ST_CDWID_24; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * Note that the ARM hardware's format reader takes 'r' from 25462306a36Sopenharmony_ci * the low bit, while DRM formats list channels from high bit 25562306a36Sopenharmony_ci * to low bit as you read left to right. The ST Micro version of 25662306a36Sopenharmony_ci * the PL110 (LCDC) however uses the standard DRM format. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci switch (fb->format->format) { 25962306a36Sopenharmony_ci case DRM_FORMAT_BGR888: 26062306a36Sopenharmony_ci /* Only supported on the ST Micro variant */ 26162306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 26262306a36Sopenharmony_ci cntl |= CNTL_ST_LCDBPP24_PACKED | CNTL_BGR; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 26562306a36Sopenharmony_ci /* Only supported on the ST Micro variant */ 26662306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 26762306a36Sopenharmony_ci cntl |= CNTL_ST_LCDBPP24_PACKED; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 27062306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 27162306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 27262306a36Sopenharmony_ci cntl |= CNTL_LCDBPP24 | CNTL_BGR; 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci cntl |= CNTL_LCDBPP24; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 27762306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 27862306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 27962306a36Sopenharmony_ci cntl |= CNTL_LCDBPP24; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci cntl |= CNTL_LCDBPP24 | CNTL_BGR; 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case DRM_FORMAT_BGR565: 28462306a36Sopenharmony_ci if (priv->variant->is_pl110) 28562306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16; 28662306a36Sopenharmony_ci else if (priv->variant->st_bitmux_control) 28762306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565 | CNTL_BGR; 28862306a36Sopenharmony_ci else 28962306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16_565; 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 29262306a36Sopenharmony_ci if (priv->variant->is_pl110) 29362306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_BGR; 29462306a36Sopenharmony_ci else if (priv->variant->st_bitmux_control) 29562306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565; 29662306a36Sopenharmony_ci else 29762306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16_565 | CNTL_BGR; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci case DRM_FORMAT_ABGR1555: 30062306a36Sopenharmony_ci case DRM_FORMAT_XBGR1555: 30162306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16; 30262306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 30362306a36Sopenharmony_ci cntl |= CNTL_ST_1XBPP_5551 | CNTL_BGR; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case DRM_FORMAT_ARGB1555: 30662306a36Sopenharmony_ci case DRM_FORMAT_XRGB1555: 30762306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16; 30862306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 30962306a36Sopenharmony_ci cntl |= CNTL_ST_1XBPP_5551; 31062306a36Sopenharmony_ci else 31162306a36Sopenharmony_ci cntl |= CNTL_BGR; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case DRM_FORMAT_ABGR4444: 31462306a36Sopenharmony_ci case DRM_FORMAT_XBGR4444: 31562306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16_444; 31662306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 31762306a36Sopenharmony_ci cntl |= CNTL_ST_1XBPP_444 | CNTL_BGR; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case DRM_FORMAT_ARGB4444: 32062306a36Sopenharmony_ci case DRM_FORMAT_XRGB4444: 32162306a36Sopenharmony_ci cntl |= CNTL_LCDBPP16_444; 32262306a36Sopenharmony_ci if (priv->variant->st_bitmux_control) 32362306a36Sopenharmony_ci cntl |= CNTL_ST_1XBPP_444; 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci cntl |= CNTL_BGR; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci WARN_ONCE(true, "Unknown FB format 0x%08x\n", 32962306a36Sopenharmony_ci fb->format->format); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* The PL110 in Integrator/Versatile does the BGR routing externally */ 33462306a36Sopenharmony_ci if (priv->variant->external_bgr) 33562306a36Sopenharmony_ci cntl &= ~CNTL_BGR; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Power sequence: first enable and chill */ 33862306a36Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * We expect this delay to stabilize the contrast 34262306a36Sopenharmony_ci * voltage Vee as stipulated by the manual 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci msleep(20); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (priv->variant_display_enable) 34762306a36Sopenharmony_ci priv->variant_display_enable(drm, fb->format->format); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Power Up */ 35062306a36Sopenharmony_ci cntl |= CNTL_LCDPWR; 35162306a36Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!priv->variant->broken_vblank) 35462306a36Sopenharmony_ci drm_crtc_vblank_on(crtc); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void pl111_display_disable(struct drm_simple_display_pipe *pipe) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 36062306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 36162306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 36262306a36Sopenharmony_ci u32 cntl; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!priv->variant->broken_vblank) 36562306a36Sopenharmony_ci drm_crtc_vblank_off(crtc); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Power Down */ 36862306a36Sopenharmony_ci cntl = readl(priv->regs + priv->ctrl); 36962306a36Sopenharmony_ci if (cntl & CNTL_LCDPWR) { 37062306a36Sopenharmony_ci cntl &= ~CNTL_LCDPWR; 37162306a36Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * We expect this delay to stabilize the contrast voltage Vee as 37662306a36Sopenharmony_ci * stipulated by the manual 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci msleep(20); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (priv->variant_display_disable) 38162306a36Sopenharmony_ci priv->variant_display_disable(drm); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Disable */ 38462306a36Sopenharmony_ci writel(0, priv->regs + priv->ctrl); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void pl111_display_update(struct drm_simple_display_pipe *pipe, 39062306a36Sopenharmony_ci struct drm_plane_state *old_pstate) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 39362306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 39462306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 39562306a36Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 39662306a36Sopenharmony_ci struct drm_plane *plane = &pipe->plane; 39762306a36Sopenharmony_ci struct drm_plane_state *pstate = plane->state; 39862306a36Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (fb) { 40162306a36Sopenharmony_ci u32 addr = drm_fb_dma_get_gem_addr(fb, pstate, 0); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci writel(addr, priv->regs + CLCD_UBAS); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (event) { 40762306a36Sopenharmony_ci crtc->state->event = NULL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 41062306a36Sopenharmony_ci if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) 41162306a36Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 41262306a36Sopenharmony_ci else 41362306a36Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 41462306a36Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int pl111_display_enable_vblank(struct drm_simple_display_pipe *pipe) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 42162306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 42262306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + priv->ienb); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic void pl111_display_disable_vblank(struct drm_simple_display_pipe *pipe) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 43262306a36Sopenharmony_ci struct drm_device *drm = crtc->dev; 43362306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci writel(0, priv->regs + priv->ienb); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic struct drm_simple_display_pipe_funcs pl111_display_funcs = { 43962306a36Sopenharmony_ci .mode_valid = pl111_mode_valid, 44062306a36Sopenharmony_ci .check = pl111_display_check, 44162306a36Sopenharmony_ci .enable = pl111_display_enable, 44262306a36Sopenharmony_ci .disable = pl111_display_disable, 44362306a36Sopenharmony_ci .update = pl111_display_update, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, 44762306a36Sopenharmony_ci unsigned long *prate, bool set_parent) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci int best_div = 1, div; 45062306a36Sopenharmony_ci struct clk_hw *parent = clk_hw_get_parent(hw); 45162306a36Sopenharmony_ci unsigned long best_prate = 0; 45262306a36Sopenharmony_ci unsigned long best_diff = ~0ul; 45362306a36Sopenharmony_ci int max_div = (1 << (TIM2_PCD_LO_BITS + TIM2_PCD_HI_BITS)) - 1; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci for (div = 1; div < max_div; div++) { 45662306a36Sopenharmony_ci unsigned long this_prate, div_rate, diff; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (set_parent) 45962306a36Sopenharmony_ci this_prate = clk_hw_round_rate(parent, rate * div); 46062306a36Sopenharmony_ci else 46162306a36Sopenharmony_ci this_prate = *prate; 46262306a36Sopenharmony_ci div_rate = DIV_ROUND_UP_ULL(this_prate, div); 46362306a36Sopenharmony_ci diff = abs(rate - div_rate); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (diff < best_diff) { 46662306a36Sopenharmony_ci best_div = div; 46762306a36Sopenharmony_ci best_diff = diff; 46862306a36Sopenharmony_ci best_prate = this_prate; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci *prate = best_prate; 47362306a36Sopenharmony_ci return best_div; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, 47762306a36Sopenharmony_ci unsigned long *prate) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci int div = pl111_clk_div_choose_div(hw, rate, prate, true); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return DIV_ROUND_UP_ULL(*prate, div); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw, 48562306a36Sopenharmony_ci unsigned long prate) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = 48862306a36Sopenharmony_ci container_of(hw, struct pl111_drm_dev_private, clk_div); 48962306a36Sopenharmony_ci u32 tim2 = readl(priv->regs + CLCD_TIM2); 49062306a36Sopenharmony_ci int div; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (tim2 & TIM2_BCD) 49362306a36Sopenharmony_ci return prate; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci div = tim2 & TIM2_PCD_LO_MASK; 49662306a36Sopenharmony_ci div |= (tim2 & TIM2_PCD_HI_MASK) >> 49762306a36Sopenharmony_ci (TIM2_PCD_HI_SHIFT - TIM2_PCD_LO_BITS); 49862306a36Sopenharmony_ci div += 2; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return DIV_ROUND_UP_ULL(prate, div); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, 50462306a36Sopenharmony_ci unsigned long prate) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = 50762306a36Sopenharmony_ci container_of(hw, struct pl111_drm_dev_private, clk_div); 50862306a36Sopenharmony_ci int div = pl111_clk_div_choose_div(hw, rate, &prate, false); 50962306a36Sopenharmony_ci u32 tim2; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci spin_lock(&priv->tim2_lock); 51262306a36Sopenharmony_ci tim2 = readl(priv->regs + CLCD_TIM2); 51362306a36Sopenharmony_ci tim2 &= ~(TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (div == 1) { 51662306a36Sopenharmony_ci tim2 |= TIM2_BCD; 51762306a36Sopenharmony_ci } else { 51862306a36Sopenharmony_ci div -= 2; 51962306a36Sopenharmony_ci tim2 |= div & TIM2_PCD_LO_MASK; 52062306a36Sopenharmony_ci tim2 |= (div >> TIM2_PCD_LO_BITS) << TIM2_PCD_HI_SHIFT; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci writel(tim2, priv->regs + CLCD_TIM2); 52462306a36Sopenharmony_ci spin_unlock(&priv->tim2_lock); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic const struct clk_ops pl111_clk_div_ops = { 53062306a36Sopenharmony_ci .recalc_rate = pl111_clk_div_recalc_rate, 53162306a36Sopenharmony_ci .round_rate = pl111_clk_div_round_rate, 53262306a36Sopenharmony_ci .set_rate = pl111_clk_div_set_rate, 53362306a36Sopenharmony_ci}; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int 53662306a36Sopenharmony_cipl111_init_clock_divider(struct drm_device *drm) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 53962306a36Sopenharmony_ci struct clk *parent = devm_clk_get(drm->dev, "clcdclk"); 54062306a36Sopenharmony_ci struct clk_hw *div = &priv->clk_div; 54162306a36Sopenharmony_ci const char *parent_name; 54262306a36Sopenharmony_ci struct clk_init_data init = { 54362306a36Sopenharmony_ci .name = "pl111_div", 54462306a36Sopenharmony_ci .ops = &pl111_clk_div_ops, 54562306a36Sopenharmony_ci .parent_names = &parent_name, 54662306a36Sopenharmony_ci .num_parents = 1, 54762306a36Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 54862306a36Sopenharmony_ci }; 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (IS_ERR(parent)) { 55262306a36Sopenharmony_ci dev_err(drm->dev, "CLCD: unable to get clcdclk.\n"); 55362306a36Sopenharmony_ci return PTR_ERR(parent); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci spin_lock_init(&priv->tim2_lock); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* If the clock divider is broken, use the parent directly */ 55962306a36Sopenharmony_ci if (priv->variant->broken_clockdivider) { 56062306a36Sopenharmony_ci priv->clk = parent; 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci parent_name = __clk_get_name(parent); 56462306a36Sopenharmony_ci div->init = &init; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci ret = devm_clk_hw_register(drm->dev, div); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci priv->clk = div->clk; 56962306a36Sopenharmony_ci return ret; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciint pl111_display_init(struct drm_device *drm) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ret = pl111_init_clock_divider(drm); 57862306a36Sopenharmony_ci if (ret) 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!priv->variant->broken_vblank) { 58262306a36Sopenharmony_ci pl111_display_funcs.enable_vblank = pl111_display_enable_vblank; 58362306a36Sopenharmony_ci pl111_display_funcs.disable_vblank = pl111_display_disable_vblank; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci ret = drm_simple_display_pipe_init(drm, &priv->pipe, 58762306a36Sopenharmony_ci &pl111_display_funcs, 58862306a36Sopenharmony_ci priv->variant->formats, 58962306a36Sopenharmony_ci priv->variant->nformats, 59062306a36Sopenharmony_ci NULL, 59162306a36Sopenharmony_ci priv->connector); 59262306a36Sopenharmony_ci if (ret) 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 597